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