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