1 /*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.nashorn.internal.codegen;
27
28 import static jdk.nashorn.internal.codegen.Compiler.SCRIPTS_PACKAGE;
29 import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
30 import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_ARGUMENTS;
31 import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_MAP;
32 import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_SCOPE;
33 import static jdk.nashorn.internal.codegen.CompilerConstants.JAVA_THIS;
34 import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_DUAL_FIELD_PREFIX;
35 import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_SINGLE_FIELD_PREFIX;
36 import static jdk.nashorn.internal.codegen.CompilerConstants.className;
37 import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
38 import static jdk.nashorn.internal.lookup.Lookup.MH;
39 import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT;
40 import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT_OPTIMISTIC;
41 import static jdk.nashorn.internal.runtime.JSType.GET_UNDEFINED;
42 import static jdk.nashorn.internal.runtime.JSType.TYPE_DOUBLE_INDEX;
43 import static jdk.nashorn.internal.runtime.JSType.TYPE_INT_INDEX;
44 import static jdk.nashorn.internal.runtime.JSType.TYPE_OBJECT_INDEX;
45 import static jdk.nashorn.internal.runtime.JSType.TYPE_UNDEFINED_INDEX;
46 import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
47 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
48
49 import java.lang.invoke.MethodHandle;
50 import java.lang.invoke.MethodHandles;
51 import java.lang.invoke.MethodType;
52 import java.util.EnumSet;
53 import java.util.Iterator;
54 import java.util.LinkedList;
55 import java.util.List;
56 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
57 import jdk.nashorn.internal.codegen.types.Type;
58 import jdk.nashorn.internal.runtime.AccessorProperty;
59 import jdk.nashorn.internal.runtime.AllocationStrategy;
60 import jdk.nashorn.internal.runtime.Context;
61 import jdk.nashorn.internal.runtime.FunctionScope;
62 import jdk.nashorn.internal.runtime.JSType;
63 import jdk.nashorn.internal.runtime.PropertyMap;
64 import jdk.nashorn.internal.runtime.ScriptEnvironment;
65 import jdk.nashorn.internal.runtime.ScriptObject;
66 import jdk.nashorn.internal.runtime.Undefined;
67 import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
68 import jdk.nashorn.internal.runtime.logging.DebugLogger;
69 import jdk.nashorn.internal.runtime.logging.Loggable;
70 import jdk.nashorn.internal.runtime.logging.Logger;
71
72 /**
73 * Generates the ScriptObject subclass structure with fields for a user objects.
74 */
75 @Logger(name="fields")
76 public final class ObjectClassGenerator implements Loggable {
77
78 /**
79 * Type guard to make sure we don't unnecessarily explode field storages. Rather unbox e.g.
80 * a java.lang.Number than blow up the field. Gradually, optimistic types should create almost
81 * no boxed types
82 */
83 private static final MethodHandle IS_TYPE_GUARD = findOwnMH("isType", boolean.class, Class.class, Object.class);
84
85 /**
86 * Marker for scope parameters
87 */
88 private static final String SCOPE_MARKER = "P";
89
90 /**
91 * Minimum number of extra fields in an object.
92 */
93 static final int FIELD_PADDING = 4;
94
95 /**
96 * Debug field logger
97 * Should we print debugging information for fields when they are generated and getters/setters are called?
98 */
99 private final DebugLogger log;
100
101 /** Field types for object-only fields */
102 private static final Type[] FIELD_TYPES_OBJECT = new Type[] { Type.OBJECT };
103 /** Field types for dual primitive/object fields */
104 private static final Type[] FIELD_TYPES_DUAL = new Type[] { Type.LONG, Type.OBJECT };
105
106 /** What type is the primitive type in dual representation */
107 public static final Type PRIMITIVE_FIELD_TYPE = Type.LONG;
108
109 private static final MethodHandle GET_DIFFERENT = findOwnMH("getDifferent", Object.class, Object.class, Class.class, MethodHandle.class, MethodHandle.class, int.class);
110 private static final MethodHandle GET_DIFFERENT_UNDEFINED = findOwnMH("getDifferentUndefined", Object.class, int.class);
111
112 private static boolean initialized = false;
113
114 /** The context */
115 private final Context context;
116
117 private final boolean dualFields;
118
119 /**
120 * Constructor
121 *
122 * @param context a context
123 * @param dualFields whether to use dual fields representation
124 */
125 public ObjectClassGenerator(final Context context, final boolean dualFields) {
126 this.context = context;
127 this.dualFields = dualFields;
128 assert context != null;
129 this.log = initLogger(context);
130 if (!initialized) {
131 initialized = true;
132 if (!dualFields) {
133 log.warning("Running with object fields only - this is a deprecated configuration.");
134 }
135 }
136 }
137
138 @Override
139 public DebugLogger getLogger() {
140 return log;
141 }
142
143 @Override
144 public DebugLogger initLogger(final Context ctxt) {
145 return ctxt.getLogger(this.getClass());
146 }
147
148 /**
149 * Pack a number into a primitive long field
150 * @param n number object
151 * @return primitive long value with all the bits in the number
152 */
153 public static long pack(final Number n) {
154 if (n instanceof Integer) {
155 return n.intValue();
156 } else if (n instanceof Long) {
157 return n.longValue();
158 } else if (n instanceof Double) {
159 return Double.doubleToRawLongBits(n.doubleValue());
160 }
161 throw new AssertionError("cannot pack" + n);
162 }
163
164 private static String getPrefixName(final boolean dualFields) {
165 return dualFields ? JS_OBJECT_DUAL_FIELD_PREFIX.symbolName() : JS_OBJECT_SINGLE_FIELD_PREFIX.symbolName();
166 }
167
168 private static String getPrefixName(final String className) {
169 if (className.startsWith(JS_OBJECT_DUAL_FIELD_PREFIX.symbolName())) {
170 return getPrefixName(true);
171 } else if (className.startsWith(JS_OBJECT_SINGLE_FIELD_PREFIX.symbolName())) {
172 return getPrefixName(false);
173 }
174 throw new AssertionError("Not a structure class: " + className);
175 }
176
177 /**
178 * Returns the class name for JavaScript objects with fieldCount fields.
179 *
180 * @param fieldCount Number of fields to allocate.
181 * @param dualFields whether to use dual fields representation
182 * @return The class name.
183 */
184 public static String getClassName(final int fieldCount, final boolean dualFields) {
185 final String prefix = getPrefixName(dualFields);
186 return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + prefix + fieldCount :
187 SCRIPTS_PACKAGE + '/' + prefix;
188 }
189
190 /**
191 * Returns the class name for JavaScript scope with fieldCount fields and
192 * paramCount parameters.
193 *
194 * @param fieldCount Number of fields to allocate.
195 * @param paramCount Number of parameters to allocate
196 * @param dualFields whether to use dual fields representation
197 * @return The class name.
198 */
199 public static String getClassName(final int fieldCount, final int paramCount, final boolean dualFields) {
200 return SCRIPTS_PACKAGE + '/' + getPrefixName(dualFields) + fieldCount + SCOPE_MARKER + paramCount;
201 }
202
203 /**
204 * Returns the number of fields in the JavaScript scope class. Its name had to be generated using either
205 * {@link #getClassName(int, boolean)} or {@link #getClassName(int, int, boolean)}.
206 * @param clazz the JavaScript scope class.
207 * @return the number of fields in the scope class.
208 */
209 public static int getFieldCount(final Class<?> clazz) {
210 final String name = clazz.getSimpleName();
211 final String prefix = getPrefixName(name);
212
213 if (prefix.equals(name)) {
214 return 0;
215 }
216 final int scopeMarker = name.indexOf(SCOPE_MARKER);
217 return Integer.parseInt(scopeMarker == -1 ? name.substring(prefix.length()) : name.substring(prefix.length(), scopeMarker));
218 }
219
220 /**
221 * Returns the name of a field based on number and type.
222 *
223 * @param fieldIndex Ordinal of field.
224 * @param type Type of field.
225 *
226 * @return The field name.
227 */
228 public static String getFieldName(final int fieldIndex, final Type type) {
229 return type.getDescriptor().substring(0, 1) + fieldIndex;
230 }
231
232 /**
233 * In the world of Object fields, we also have no undefined SwitchPoint, to reduce as much potential
234 * MethodHandle overhead as possible. In that case, we explicitly need to assign undefined to fields
235 * when we initialize them.
236 *
237 * @param init constructor to generate code in
238 * @param className name of class
239 * @param fieldNames fields to initialize to undefined, where applicable
240 */
241 private void initializeToUndefined(final MethodEmitter init, final String className, final List<String> fieldNames) {
242 if (dualFields) {
243 // no need to initialize anything to undefined in the dual field world
244 // - then we have a constant getter for undefined for any unknown type
245 return;
246 }
247
248 if (fieldNames.isEmpty()) {
249 return;
250 }
251
252 init.load(Type.OBJECT, JAVA_THIS.slot());
253 init.loadUndefined(Type.OBJECT);
254
255 final Iterator<String> iter = fieldNames.iterator();
256 while (iter.hasNext()) {
257 final String fieldName = iter.next();
258 if (iter.hasNext()) {
259 init.dup2();
260 }
261 init.putField(className, fieldName, Type.OBJECT.getDescriptor());
262 }
263 }
264
265 /**
266 * Generate the byte codes for a JavaScript object class or scope.
267 * Class name is a function of number of fields and number of param
268 * fields
269 *
270 * @param descriptor Descriptor pulled from class name.
271 *
272 * @return Byte codes for generated class.
273 */
274 public byte[] generate(final String descriptor) {
275 final String[] counts = descriptor.split(SCOPE_MARKER);
276 final int fieldCount = Integer.valueOf(counts[0]);
277
278 if (counts.length == 1) {
279 return generate(fieldCount);
280 }
281
282 final int paramCount = Integer.valueOf(counts[1]);
283
284 return generate(fieldCount, paramCount);
285 }
286
287 /**
288 * Generate the byte codes for a JavaScript object class with fieldCount fields.
289 *
290 * @param fieldCount Number of fields in the JavaScript object.
291 *
292 * @return Byte codes for generated class.
293 */
294 public byte[] generate(final int fieldCount) {
295 final String className = getClassName(fieldCount, dualFields);
296 final String superName = className(ScriptObject.class);
297 final ClassEmitter classEmitter = newClassEmitter(className, superName);
298
299 addFields(classEmitter, fieldCount);
300
301 final MethodEmitter init = newInitMethod(classEmitter);
302 init.returnVoid();
303 init.end();
304
305 final MethodEmitter initWithSpillArrays = newInitWithSpillArraysMethod(classEmitter, ScriptObject.class);
306 initWithSpillArrays.returnVoid();
307 initWithSpillArrays.end();
308
309 newEmptyInit(className, classEmitter);
310 newAllocate(className, classEmitter);
311
312 return toByteArray(className, classEmitter);
313 }
314
315 /**
316 * Generate the byte codes for a JavaScript scope class with fieldCount fields
317 * and paramCount parameters.
318 *
319 * @param fieldCount Number of fields in the JavaScript scope.
320 * @param paramCount Number of parameters in the JavaScript scope
321 * .
322 * @return Byte codes for generated class.
323 */
324 public byte[] generate(final int fieldCount, final int paramCount) {
325 final String className = getClassName(fieldCount, paramCount, dualFields);
326 final String superName = className(FunctionScope.class);
327 final ClassEmitter classEmitter = newClassEmitter(className, superName);
328 final List<String> initFields = addFields(classEmitter, fieldCount);
329
330 final MethodEmitter init = newInitScopeMethod(classEmitter);
331 initializeToUndefined(init, className, initFields);
332 init.returnVoid();
333 init.end();
334
335 final MethodEmitter initWithSpillArrays = newInitWithSpillArraysMethod(classEmitter, FunctionScope.class);
336 initializeToUndefined(initWithSpillArrays, className, initFields);
337 initWithSpillArrays.returnVoid();
338 initWithSpillArrays.end();
339
340 final MethodEmitter initWithArguments = newInitScopeWithArgumentsMethod(classEmitter);
341 initializeToUndefined(initWithArguments, className, initFields);
342 initWithArguments.returnVoid();
343 initWithArguments.end();
344
345 return toByteArray(className, classEmitter);
346 }
347
348 /**
349 * Generates the needed fields.
350 *
351 * @param classEmitter Open class emitter.
352 * @param fieldCount Number of fields.
353 *
354 * @return List fields that need to be initialized.
355 */
356 private List<String> addFields(final ClassEmitter classEmitter, final int fieldCount) {
357 final List<String> initFields = new LinkedList<>();
358 final Type[] fieldTypes = dualFields ? FIELD_TYPES_DUAL : FIELD_TYPES_OBJECT;
359 for (int i = 0; i < fieldCount; i++) {
360 for (final Type type : fieldTypes) {
361 final String fieldName = getFieldName(i, type);
362 classEmitter.field(fieldName, type.getTypeClass());
363
364 if (type == Type.OBJECT) {
365 initFields.add(fieldName);
366 }
367 }
368 }
369
370 return initFields;
371 }
372
373 /**
374 * Allocate and initialize a new class emitter.
375 *
376 * @param className Name of JavaScript class.
377 *
378 * @return Open class emitter.
379 */
380 private ClassEmitter newClassEmitter(final String className, final String superName) {
381 final ClassEmitter classEmitter = new ClassEmitter(context, className, superName);
382 classEmitter.begin();
383
384 return classEmitter;
385 }
386
387 /**
388 * Allocate and initialize a new <init> method.
389 *
390 * @param classEmitter Open class emitter.
391 *
392 * @return Open method emitter.
393 */
394 private static MethodEmitter newInitMethod(final ClassEmitter classEmitter) {
395 final MethodEmitter init = classEmitter.init(PropertyMap.class);
396 init.begin();
397 init.load(Type.OBJECT, JAVA_THIS.slot());
398 init.load(Type.OBJECT, INIT_MAP.slot());
399 init.invoke(constructorNoLookup(ScriptObject.class, PropertyMap.class));
400
401 return init;
402 }
403
404 private static MethodEmitter newInitWithSpillArraysMethod(final ClassEmitter classEmitter, final Class<?> superClass) {
405 final MethodEmitter init = classEmitter.init(PropertyMap.class, long[].class, Object[].class);
406 init.begin();
407 init.load(Type.OBJECT, JAVA_THIS.slot());
408 init.load(Type.OBJECT, INIT_MAP.slot());
409 init.load(Type.LONG_ARRAY, 2);
410 init.load(Type.OBJECT_ARRAY, 3);
411 init.invoke(constructorNoLookup(superClass, PropertyMap.class, long[].class, Object[].class));
412
413 return init;
414 }
415
416 /**
417 * Allocate and initialize a new <init> method for scopes.
418 * @param classEmitter Open class emitter.
419 * @return Open method emitter.
420 */
421 private static MethodEmitter newInitScopeMethod(final ClassEmitter classEmitter) {
422 final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class);
423 init.begin();
424 init.load(Type.OBJECT, JAVA_THIS.slot());
425 init.load(Type.OBJECT, INIT_MAP.slot());
426 init.load(Type.OBJECT, INIT_SCOPE.slot());
427 init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class));
428
429 return init;
430 }
431
432 /**
433 * Allocate and initialize a new <init> method for scopes with arguments.
434 * @param classEmitter Open class emitter.
435 * @return Open method emitter.
436 */
437 private static MethodEmitter newInitScopeWithArgumentsMethod(final ClassEmitter classEmitter) {
438 final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class, ScriptObject.class);
439 init.begin();
440 init.load(Type.OBJECT, JAVA_THIS.slot());
441 init.load(Type.OBJECT, INIT_MAP.slot());
442 init.load(Type.OBJECT, INIT_SCOPE.slot());
443 init.load(Type.OBJECT, INIT_ARGUMENTS.slot());
444 init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class, ScriptObject.class));
445
446 return init;
447 }
448
449 /**
450 * Add an empty <init> method to the JavaScript class.
451 *
452 * @param classEmitter Open class emitter.
453 * @param className Name of JavaScript class.
454 */
455 private static void newEmptyInit(final String className, final ClassEmitter classEmitter) {
456 final MethodEmitter emptyInit = classEmitter.init();
457 emptyInit.begin();
458 emptyInit.load(Type.OBJECT, JAVA_THIS.slot());
459 emptyInit.loadNull();
460 emptyInit.invoke(constructorNoLookup(className, PropertyMap.class));
461 emptyInit.returnVoid();
462 emptyInit.end();
463 }
464
465 /**
466 * Add an empty <init> method to the JavaScript class.
467 *
468 * @param classEmitter Open class emitter.
469 * @param className Name of JavaScript class.
470 */
471 private static void newAllocate(final String className, final ClassEmitter classEmitter) {
472 final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.symbolName(), ScriptObject.class, PropertyMap.class);
473 allocate.begin();
474 allocate._new(className, Type.typeFor(ScriptObject.class));
475 allocate.dup();
476 allocate.load(Type.typeFor(PropertyMap.class), 0);
477 allocate.invoke(constructorNoLookup(className, PropertyMap.class));
478 allocate._return();
479 allocate.end();
480 }
481
482 /**
483 * Collects the byte codes for a generated JavaScript class.
484 *
485 * @param classEmitter Open class emitter.
486 * @return Byte codes for the class.
487 */
488 private byte[] toByteArray(final String className, final ClassEmitter classEmitter) {
489 classEmitter.end();
490
491 final byte[] code = classEmitter.toByteArray();
492 final ScriptEnvironment env = context.getEnv();
493
494 DumpBytecode.dumpBytecode(env, log, code, className);
495
496 if (env._verify_code) {
497 context.verify(code);
498 }
499
500 return code;
501 }
502
503 /** Double to long bits, used with --dual-fields for primitive double values */
504 public static final MethodHandle PACK_DOUBLE =
505 MH.explicitCastArguments(MH.findStatic(MethodHandles.publicLookup(), Double.class, "doubleToRawLongBits", MH.type(long.class, double.class)), MH.type(long.class, double.class));
506
507 /** double bits to long, used with --dual-fields for primitive double values */
508 public static final MethodHandle UNPACK_DOUBLE =
509 MH.findStatic(MethodHandles.publicLookup(), Double.class, "longBitsToDouble", MH.type(double.class, long.class));
510
511 //type != forType, so use the correct getter for forType, box it and throw
512 @SuppressWarnings("unused")
513 private static Object getDifferent(final Object receiver, final Class<?> forType, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final int programPoint) {
514 //create the sametype getter, and upcast to value. no matter what the store format is,
515 //
516 final MethodHandle sameTypeGetter = getterForType(forType, primitiveGetter, objectGetter);
517 final MethodHandle mh = MH.asType(sameTypeGetter, sameTypeGetter.type().changeReturnType(Object.class));
518 try {
519 final Object value = mh.invokeExact(receiver);
520 throw new UnwarrantedOptimismException(value, programPoint);
521 } catch (final Error | RuntimeException e) {
522 throw e;
523 } catch (final Throwable e) {
524 throw new RuntimeException(e);
525 }
526 }
527
528 @SuppressWarnings("unused")
529 private static Object getDifferentUndefined(final int programPoint) {
530 throw new UnwarrantedOptimismException(Undefined.getUndefined(), programPoint);
531 }
532
533 private static MethodHandle getterForType(final Class<?> forType, final MethodHandle primitiveGetter, final MethodHandle objectGetter) {
534 switch (getAccessorTypeIndex(forType)) {
535 case TYPE_INT_INDEX:
536 return MH.explicitCastArguments(primitiveGetter, primitiveGetter.type().changeReturnType(int.class));
537 case TYPE_DOUBLE_INDEX:
538 return MH.filterReturnValue(primitiveGetter, UNPACK_DOUBLE);
539 case TYPE_OBJECT_INDEX:
540 return objectGetter;
541 default:
542 throw new AssertionError(forType);
543 }
544 }
545
546 //no optimism here. we do unconditional conversion to types
547 private static MethodHandle createGetterInner(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final List<MethodHandle> converters, final int programPoint) {
548 final int fti = forType == null ? TYPE_UNDEFINED_INDEX : getAccessorTypeIndex(forType);
549 final int ti = getAccessorTypeIndex(type);
550 //this means fail if forType != type
551 final boolean isOptimistic = converters == CONVERT_OBJECT_OPTIMISTIC;
552 final boolean isPrimitiveStorage = forType != null && forType.isPrimitive();
553
554 //which is the primordial getter
555 final MethodHandle getter = primitiveGetter == null ? objectGetter : isPrimitiveStorage ? primitiveGetter : objectGetter;
556
557 if (forType == null) {
558 if (isOptimistic) {
559 //return undefined if asking for object. otherwise throw UnwarrantedOptimismException
560 if (ti == TYPE_OBJECT_INDEX) {
561 return MH.dropArguments(GET_UNDEFINED.get(TYPE_OBJECT_INDEX), 0, Object.class);
562 }
563 //throw exception
564 return MH.asType(
565 MH.dropArguments(
566 MH.insertArguments(
567 GET_DIFFERENT_UNDEFINED,
568 0,
569 programPoint),
570 0,
571 Object.class),
572 getter.type().changeReturnType(type));
573 }
574 //return an undefined and coerce it to the appropriate type
575 return MH.dropArguments(GET_UNDEFINED.get(ti), 0, Object.class);
576 }
577
578 assert primitiveGetter != null || forType == Object.class : forType;
579
580 if (isOptimistic) {
581 if (fti < ti) {
582 //asking for a wider type than currently stored. then it's OK to coerce.
583 //e.g. stored as int, ask for long or double
584 //e.g. stored as long, ask for double
585 assert fti != TYPE_UNDEFINED_INDEX;
586 final MethodHandle tgetter = getterForType(forType, primitiveGetter, objectGetter);
587 return MH.asType(tgetter, tgetter.type().changeReturnType(type));
588 } else if (fti == ti) {
589 //Fast path, never throw exception - exact getter, just unpack if needed
590 return getterForType(forType, primitiveGetter, objectGetter);
591 } else {
592 assert fti > ti;
593 //if asking for a narrower type than the storage - throw exception
594 //unless FTI is object, in that case we have to go through the converters
595 //there is no
596 if (fti == TYPE_OBJECT_INDEX) {
597 return MH.filterReturnValue(
598 objectGetter,
599 MH.insertArguments(
600 converters.get(ti),
601 1,
602 programPoint));
603 }
604
605 //asking for narrower primitive than we have stored, that is an
606 //UnwarrantedOptimismException
607 return MH.asType(
608 MH.filterArguments(
609 objectGetter,
610 0,
611 MH.insertArguments(
612 GET_DIFFERENT,
613 1,
614 forType,
615 primitiveGetter,
616 objectGetter,
617 programPoint)),
618 objectGetter.type().changeReturnType(type));
619 }
620 }
621
622 assert !isOptimistic;
623 // freely coerce the result to whatever you asked for, this is e.g. Object->int for a & b
624 final MethodHandle tgetter = getterForType(forType, primitiveGetter, objectGetter);
625 if (fti == TYPE_OBJECT_INDEX) {
626 if (fti != ti) {
627 return MH.filterReturnValue(tgetter, CONVERT_OBJECT.get(ti));
628 }
629 return tgetter;
630 }
631
632 assert primitiveGetter != null;
633 final MethodType tgetterType = tgetter.type();
634 switch (fti) {
635 case TYPE_INT_INDEX: {
636 return MH.asType(tgetter, tgetterType.changeReturnType(type));
637 }
638 case TYPE_DOUBLE_INDEX:
639 switch (ti) {
640 case TYPE_INT_INDEX:
641 return MH.filterReturnValue(tgetter, JSType.TO_INT32_D.methodHandle);
642 case TYPE_DOUBLE_INDEX:
643 assert tgetterType.returnType() == double.class;
644 return tgetter;
645 default:
646 return MH.asType(tgetter, tgetterType.changeReturnType(Object.class));
647 }
648 default:
649 throw new UnsupportedOperationException(forType + "=>" + type);
650 }
651 }
652
653 /**
654 * Given a primitiveGetter (optional for non dual fields) and an objectSetter that retrieve
655 * the primitive and object version of a field respectively, return one with the correct
656 * method type and the correct filters. For example, if the value is stored as a double
657 * and we want an Object getter, in the dual fields world we'd pick the primitiveGetter,
658 * which reads a long, use longBitsToDouble on the result to unpack it, and then change the
659 * return type to Object, boxing it. In the objects only world there are only object fields,
660 * primitives are boxed when asked for them and we don't need to bother with primitive encoding
661 * (or even undefined, which if forType==null) representation, so we just return whatever is
662 * in the object field. The object field is always initiated to Undefined, so here, where we have
663 * the representation for Undefined in all our bits, this is not a problem.
664 * <p>
665 * Representing undefined in a primitive is hard, for an int there aren't enough bits, for a long
666 * we could limit the width of a representation, and for a double (as long as it is stored as long,
667 * as all NaNs will turn into QNaN on ia32, which is one bit pattern, we should use a special NaN).
668 * Naturally we could have special undefined values for all types which mean "go look in a wider field",
669 * but the guards needed on every getter took too much time.
670 * <p>
671 * To see how this is used, look for example in {@link AccessorProperty#getGetter}
672 * <p>
673 * @param forType representation of the underlying type in the field, null if undefined
674 * @param type type to retrieve it as
675 * @param primitiveGetter getter to read the primitive version of this field (null if Objects Only)
676 * @param objectGetter getter to read the object version of this field
677 * @param programPoint program point for getter, if program point is INVALID_PROGRAM_POINT, then this is not an optimistic getter
678 *
679 * @return getter for the given representation that returns the given type
680 */
681 public static MethodHandle createGetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final int programPoint) {
682 return createGetterInner(
683 forType,
684 type,
685 primitiveGetter,
686 objectGetter,
687 isValid(programPoint) ? CONVERT_OBJECT_OPTIMISTIC : CONVERT_OBJECT,
688 programPoint);
689 }
690
691 /**
692 * This is similar to the {@link ObjectClassGenerator#createGetter} function. Performs
693 * the necessary operations to massage a setter operand of type {@code type} to
694 * fit into the primitive field (if primitive and dual fields is enabled) or into
695 * the object field (box if primitive and dual fields is disabled)
696 *
697 * @param forType representation of the underlying object
698 * @param type representation of field to write, and setter signature
699 * @param primitiveSetter setter that writes to the primitive field (null if Objects Only)
700 * @param objectSetter setter that writes to the object field
701 *
702 * @return the setter for the given representation that takes a {@code type}
703 */
704 public static MethodHandle createSetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveSetter, final MethodHandle objectSetter) {
705 assert forType != null;
706
707 final int fti = getAccessorTypeIndex(forType);
708 final int ti = getAccessorTypeIndex(type);
709
710 if (fti == TYPE_OBJECT_INDEX || primitiveSetter == null) {
711 if (ti == TYPE_OBJECT_INDEX) {
712 return objectSetter;
713 }
714
715 return MH.asType(objectSetter, objectSetter.type().changeParameterType(1, type));
716 }
717
718 final MethodType pmt = primitiveSetter.type();
719
720 switch (fti) {
721 case TYPE_INT_INDEX:
722 switch (ti) {
723 case TYPE_INT_INDEX:
724 return MH.asType(primitiveSetter, pmt.changeParameterType(1, int.class));
725 case TYPE_DOUBLE_INDEX:
726 return MH.filterArguments(primitiveSetter, 1, PACK_DOUBLE);
727 default:
728 return objectSetter;
729 }
730 case TYPE_DOUBLE_INDEX:
731 if (ti == TYPE_OBJECT_INDEX) {
732 return objectSetter;
733 }
734 return MH.asType(MH.filterArguments(primitiveSetter, 1, PACK_DOUBLE), pmt.changeParameterType(1, type));
735 default:
736 throw new UnsupportedOperationException(forType + "=>" + type);
737 }
738 }
739
740 @SuppressWarnings("unused")
741 private static boolean isType(final Class<?> boxedForType, final Object x) {
742 return x != null && x.getClass() == boxedForType;
743 }
744
745 private static Class<? extends Number> getBoxedType(final Class<?> forType) {
746 if (forType == int.class) {
747 return Integer.class;
748 }
749
750 if (forType == long.class) {
751 return Long.class;
752 }
753
754 if (forType == double.class) {
755 return Double.class;
756 }
757
758 assert false;
759 return null;
760 }
761
762 /**
763 * If we are setting boxed types (because the compiler couldn't determine which they were) to
764 * a primitive field, we can reuse the primitive field getter, as long as we are setting an element
765 * of the same boxed type as the primitive type representation
766 *
767 * @param forType the current type
768 * @param primitiveSetter primitive setter for the current type with an element of the current type
769 * @param objectSetter the object setter
770 *
771 * @return method handle that checks if the element to be set is of the current type, even though it's boxed
772 * and instead of using the generic object setter, that would blow up the type and invalidate the map,
773 * unbox it and call the primitive setter instead
774 */
775 public static MethodHandle createGuardBoxedPrimitiveSetter(final Class<?> forType, final MethodHandle primitiveSetter, final MethodHandle objectSetter) {
776 final Class<? extends Number> boxedForType = getBoxedType(forType);
777 //object setter that checks for primitive if current type is primitive
778 return MH.guardWithTest(
779 MH.insertArguments(
780 MH.dropArguments(
781 IS_TYPE_GUARD,
782 1,
783 Object.class),
784 0,
785 boxedForType),
786 MH.asType(
787 primitiveSetter,
788 objectSetter.type()),
789 objectSetter);
790 }
791 /**
792 * Add padding to field count to avoid creating too many classes and have some spare fields
793 * @param count the field count
794 * @return the padded field count
795 */
796 static int getPaddedFieldCount(final int count) {
797 return count / FIELD_PADDING * FIELD_PADDING + FIELD_PADDING;
798 }
799
800 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
801 return MH.findStatic(MethodHandles.lookup(), ObjectClassGenerator.class, name, MH.type(rtype, types));
802 }
803
804 /**
805 * Creates the allocator class name and property map for a constructor function with the specified
806 * number of "this" properties that it initializes.
807 * @param thisProperties number of properties assigned to "this"
808 * @return the allocation strategy
809 */
810 static AllocationStrategy createAllocationStrategy(final int thisProperties, final boolean dualFields) {
811 final int paddedFieldCount = getPaddedFieldCount(thisProperties);
812 return new AllocationStrategy(paddedFieldCount, dualFields);
813 }
814 }
--- EOF ---