ObjectClassGenerator.java revision 1245:c55ce3738888
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;
72import 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")
78public 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}
834