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