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