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_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.AllocationStrategy;
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;
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    /** Field types for object-only fields */
102    private static final Type[] FIELD_TYPES_OBJECT = new Type[] { Type.OBJECT };
103    /** Field types for dual primitive/object fields */
104    private static final Type[] FIELD_TYPES_DUAL   = new Type[] { Type.LONG, Type.OBJECT };
105
106    /** What type is the primitive type in dual representation */
107    public static final Type PRIMITIVE_FIELD_TYPE = Type.LONG;
108
109    private static final MethodHandle GET_DIFFERENT           = findOwnMH("getDifferent", Object.class, Object.class, Class.class, MethodHandle.class, MethodHandle.class, int.class);
110    private static final MethodHandle GET_DIFFERENT_UNDEFINED = findOwnMH("getDifferentUndefined", Object.class, int.class);
111
112    private static boolean initialized = false;
113
114    /** The context */
115    private final Context context;
116
117    private final boolean dualFields;
118
119    /**
120     * Constructor
121     *
122     * @param context a context
123     * @param dualFields whether to use dual fields representation
124     */
125    public ObjectClassGenerator(final Context context, final boolean dualFields) {
126        this.context = context;
127        this.dualFields = dualFields;
128        assert context != null;
129        this.log = initLogger(context);
130        if (!initialized) {
131            initialized = true;
132            if (!dualFields) {
133                log.warning("Running with object fields only - this is a deprecated configuration.");
134            }
135        }
136    }
137
138    @Override
139    public DebugLogger getLogger() {
140        return log;
141    }
142
143    @Override
144    public DebugLogger initLogger(final Context ctxt) {
145        return ctxt.getLogger(this.getClass());
146    }
147
148    /**
149     * Pack a number into a primitive long field
150     * @param n number object
151     * @return primitive long value with all the bits in the number
152     */
153    public static long pack(final Number n) {
154        if (n instanceof Integer) {
155            return n.intValue();
156        } else if (n instanceof Long) {
157            return n.longValue();
158        } else if (n instanceof Double) {
159            return Double.doubleToRawLongBits(n.doubleValue());
160        }
161        throw new AssertionError("cannot pack" + n);
162    }
163
164    private static String getPrefixName(final boolean dualFields) {
165        return dualFields ? JS_OBJECT_DUAL_FIELD_PREFIX.symbolName() : JS_OBJECT_SINGLE_FIELD_PREFIX.symbolName();
166    }
167
168    private static String getPrefixName(final String className) {
169        if (className.startsWith(JS_OBJECT_DUAL_FIELD_PREFIX.symbolName())) {
170            return getPrefixName(true);
171        } else if (className.startsWith(JS_OBJECT_SINGLE_FIELD_PREFIX.symbolName())) {
172            return getPrefixName(false);
173        }
174        throw new AssertionError("Not a structure class: " + className);
175    }
176
177    /**
178     * Returns the class name for JavaScript objects with fieldCount fields.
179     *
180     * @param fieldCount Number of fields to allocate.
181     * @param dualFields whether to use dual fields representation
182     * @return The class name.
183     */
184    public static String getClassName(final int fieldCount, final boolean dualFields) {
185        final String prefix = getPrefixName(dualFields);
186        return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + prefix + fieldCount :
187                                 SCRIPTS_PACKAGE + '/' + prefix;
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     * @param dualFields whether to use dual fields representation
197     * @return The class name.
198     */
199    public static String getClassName(final int fieldCount, final int paramCount, final boolean dualFields) {
200        return SCRIPTS_PACKAGE + '/' + getPrefixName(dualFields) + 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, boolean)} or {@link #getClassName(int, int, boolean)}.
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 = getPrefixName(name);
212
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 void initializeToUndefined(final MethodEmitter init, final String className, final List<String> fieldNames) {
242        if (dualFields) {
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, dualFields);
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, dualFields);
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 List<String> addFields(final ClassEmitter classEmitter, final int fieldCount) {
357        final List<String> initFields = new LinkedList<>();
358        final Type[] fieldTypes = dualFields ? FIELD_TYPES_DUAL : FIELD_TYPES_OBJECT;
359        for (int i = 0; i < fieldCount; i++) {
360            for (final Type type : fieldTypes) {
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            return MH.explicitCastArguments(primitiveGetter, primitiveGetter.type().changeReturnType(int.class));
537        case TYPE_DOUBLE_INDEX:
538            return MH.filterReturnValue(primitiveGetter, UNPACK_DOUBLE);
539        case TYPE_OBJECT_INDEX:
540            return objectGetter;
541        default:
542            throw new AssertionError(forType);
543        }
544    }
545
546    //no optimism here. we do unconditional conversion to types
547    private static MethodHandle createGetterInner(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final List<MethodHandle> converters, final int programPoint) {
548        final int fti = forType == null ? TYPE_UNDEFINED_INDEX : getAccessorTypeIndex(forType);
549        final int ti  = getAccessorTypeIndex(type);
550        //this means fail if forType != type
551        final boolean isOptimistic = converters == CONVERT_OBJECT_OPTIMISTIC;
552        final boolean isPrimitiveStorage = forType != null && forType.isPrimitive();
553
554        //which is the primordial getter
555        final MethodHandle getter = primitiveGetter == null ? objectGetter : isPrimitiveStorage ? primitiveGetter : objectGetter;
556
557        if (forType == null) {
558            if (isOptimistic) {
559                //return undefined if asking for object. otherwise throw UnwarrantedOptimismException
560                if (ti == TYPE_OBJECT_INDEX) {
561                    return MH.dropArguments(GET_UNDEFINED.get(TYPE_OBJECT_INDEX), 0, Object.class);
562                }
563                //throw exception
564                return MH.asType(
565                    MH.dropArguments(
566                            MH.insertArguments(
567                                    GET_DIFFERENT_UNDEFINED,
568                                    0,
569                                    programPoint),
570                            0,
571                            Object.class),
572                    getter.type().changeReturnType(type));
573            }
574            //return an undefined and coerce it to the appropriate type
575            return MH.dropArguments(GET_UNDEFINED.get(ti), 0, Object.class);
576        }
577
578        assert primitiveGetter != null || forType == Object.class : forType;
579
580        if (isOptimistic) {
581            if (fti < ti) {
582                //asking for a wider type than currently stored. then it's OK to coerce.
583                //e.g. stored as int,  ask for long or double
584                //e.g. stored as long, ask for double
585                assert fti != TYPE_UNDEFINED_INDEX;
586                final MethodHandle tgetter = getterForType(forType, primitiveGetter, objectGetter);
587                return MH.asType(tgetter, tgetter.type().changeReturnType(type));
588            } else if (fti == ti) {
589                //Fast path, never throw exception - exact getter, just unpack if needed
590                return getterForType(forType, primitiveGetter, objectGetter);
591            } else {
592                assert fti > ti;
593                //if asking for a narrower type than the storage - throw exception
594                //unless FTI is object, in that case we have to go through the converters
595                //there is no
596                if (fti == TYPE_OBJECT_INDEX) {
597                    return MH.filterReturnValue(
598                            objectGetter,
599                            MH.insertArguments(
600                                    converters.get(ti),
601                                    1,
602                                    programPoint));
603                }
604
605                //asking for narrower primitive than we have stored, that is an
606                //UnwarrantedOptimismException
607                return MH.asType(
608                        MH.filterArguments(
609                            objectGetter,
610                            0,
611                            MH.insertArguments(
612                                    GET_DIFFERENT,
613                                    1,
614                                    forType,
615                                    primitiveGetter,
616                                    objectGetter,
617                                    programPoint)),
618                        objectGetter.type().changeReturnType(type));
619            }
620        }
621
622        assert !isOptimistic;
623        // freely coerce the result to whatever you asked for, this is e.g. Object->int for a & b
624        final MethodHandle tgetter = getterForType(forType, primitiveGetter, objectGetter);
625        if (fti == TYPE_OBJECT_INDEX) {
626            if (fti != ti) {
627                return MH.filterReturnValue(tgetter, CONVERT_OBJECT.get(ti));
628            }
629            return tgetter;
630        }
631
632        assert primitiveGetter != null;
633        final MethodType tgetterType = tgetter.type();
634        switch (fti) {
635        case TYPE_INT_INDEX: {
636            return MH.asType(tgetter, tgetterType.changeReturnType(type));
637        }
638        case TYPE_DOUBLE_INDEX:
639            switch (ti) {
640            case TYPE_INT_INDEX:
641                return MH.filterReturnValue(tgetter, JSType.TO_INT32_D.methodHandle);
642            case TYPE_DOUBLE_INDEX:
643                assert tgetterType.returnType() == double.class;
644                return tgetter;
645            default:
646                return MH.asType(tgetter, tgetterType.changeReturnType(Object.class));
647            }
648        default:
649            throw new UnsupportedOperationException(forType + "=>" + type);
650        }
651    }
652
653    /**
654     * Given a primitiveGetter (optional for non dual fields) and an objectSetter that retrieve
655     * the primitive and object version of a field respectively, return one with the correct
656     * method type and the correct filters. For example, if the value is stored as a double
657     * and we want an Object getter, in the dual fields world we'd pick the primitiveGetter,
658     * which reads a long, use longBitsToDouble on the result to unpack it, and then change the
659     * return type to Object, boxing it. In the objects only world there are only object fields,
660     * primitives are boxed when asked for them and we don't need to bother with primitive encoding
661     * (or even undefined, which if forType==null) representation, so we just return whatever is
662     * in the object field. The object field is always initiated to Undefined, so here, where we have
663     * the representation for Undefined in all our bits, this is not a problem.
664     * <p>
665     * Representing undefined in a primitive is hard, for an int there aren't enough bits, for a long
666     * we could limit the width of a representation, and for a double (as long as it is stored as long,
667     * as all NaNs will turn into QNaN on ia32, which is one bit pattern, we should use a special NaN).
668     * Naturally we could have special undefined values for all types which mean "go look in a wider field",
669     * but the guards needed on every getter took too much time.
670     * <p>
671     * To see how this is used, look for example in {@link AccessorProperty#getGetter}
672     * <p>
673     * @param forType         representation of the underlying type in the field, null if undefined
674     * @param type            type to retrieve it as
675     * @param primitiveGetter getter to read the primitive version of this field (null if Objects Only)
676     * @param objectGetter    getter to read the object version of this field
677     * @param programPoint    program point for getter, if program point is INVALID_PROGRAM_POINT, then this is not an optimistic getter
678     *
679     * @return getter for the given representation that returns the given type
680     */
681    public static MethodHandle createGetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final int programPoint) {
682        return createGetterInner(
683                forType,
684                type,
685                primitiveGetter,
686                objectGetter,
687                isValid(programPoint) ? CONVERT_OBJECT_OPTIMISTIC : CONVERT_OBJECT,
688                programPoint);
689    }
690
691    /**
692     * This is similar to the {@link ObjectClassGenerator#createGetter} function. Performs
693     * the necessary operations to massage a setter operand of type {@code type} to
694     * fit into the primitive field (if primitive and dual fields is enabled) or into
695     * the object field (box if primitive and dual fields is disabled)
696     *
697     * @param forType         representation of the underlying object
698     * @param type            representation of field to write, and setter signature
699     * @param primitiveSetter setter that writes to the primitive field (null if Objects Only)
700     * @param objectSetter    setter that writes to the object field
701     *
702     * @return the setter for the given representation that takes a {@code type}
703     */
704    public static MethodHandle createSetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveSetter, final MethodHandle objectSetter) {
705        assert forType != null;
706
707        final int fti = getAccessorTypeIndex(forType);
708        final int ti  = getAccessorTypeIndex(type);
709
710        if (fti == TYPE_OBJECT_INDEX || primitiveSetter == null) {
711            if (ti == TYPE_OBJECT_INDEX) {
712                return objectSetter;
713            }
714
715            return MH.asType(objectSetter, objectSetter.type().changeParameterType(1, type));
716        }
717
718        final MethodType pmt = primitiveSetter.type();
719
720        switch (fti) {
721        case TYPE_INT_INDEX:
722            switch (ti) {
723            case TYPE_INT_INDEX:
724                return MH.asType(primitiveSetter, pmt.changeParameterType(1, int.class));
725            case TYPE_DOUBLE_INDEX:
726                return MH.filterArguments(primitiveSetter, 1, PACK_DOUBLE);
727            default:
728                return objectSetter;
729            }
730        case TYPE_DOUBLE_INDEX:
731            if (ti == TYPE_OBJECT_INDEX) {
732                return objectSetter;
733            }
734            return MH.asType(MH.filterArguments(primitiveSetter, 1, PACK_DOUBLE), pmt.changeParameterType(1, type));
735        default:
736            throw new UnsupportedOperationException(forType + "=>" + type);
737        }
738    }
739
740    @SuppressWarnings("unused")
741    private static boolean isType(final Class<?> boxedForType, final Object x) {
742        return x != null && x.getClass() == boxedForType;
743    }
744
745    private static Class<? extends Number> getBoxedType(final Class<?> forType) {
746        if (forType == int.class) {
747            return Integer.class;
748        }
749
750        if (forType == long.class) {
751            return Long.class;
752        }
753
754        if (forType == double.class) {
755            return Double.class;
756        }
757
758        assert false;
759        return null;
760    }
761
762    /**
763     * If we are setting boxed types (because the compiler couldn't determine which they were) to
764     * a primitive field, we can reuse the primitive field getter, as long as we are setting an element
765     * of the same boxed type as the primitive type representation
766     *
767     * @param forType           the current type
768     * @param primitiveSetter   primitive setter for the current type with an element of the current type
769     * @param objectSetter      the object setter
770     *
771     * @return method handle that checks if the element to be set is of the current type, even though it's boxed
772     *  and instead of using the generic object setter, that would blow up the type and invalidate the map,
773     *  unbox it and call the primitive setter instead
774     */
775    public static MethodHandle createGuardBoxedPrimitiveSetter(final Class<?> forType, final MethodHandle primitiveSetter, final MethodHandle objectSetter) {
776        final Class<? extends Number> boxedForType = getBoxedType(forType);
777        //object setter that checks for primitive if current type is primitive
778        return MH.guardWithTest(
779            MH.insertArguments(
780                MH.dropArguments(
781                    IS_TYPE_GUARD,
782                    1,
783                    Object.class),
784                0,
785                boxedForType),
786                MH.asType(
787                    primitiveSetter,
788                    objectSetter.type()),
789                objectSetter);
790    }
791    /**
792     * Add padding to field count to avoid creating too many classes and have some spare fields
793     * @param count the field count
794     * @return the padded field count
795     */
796    static int getPaddedFieldCount(final int count) {
797        return count / FIELD_PADDING * FIELD_PADDING + FIELD_PADDING;
798    }
799
800    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
801        return MH.findStatic(MethodHandles.lookup(), ObjectClassGenerator.class, name, MH.type(rtype, types));
802    }
803
804    /**
805     * Creates the allocator class name and property map for a constructor function with the specified
806     * number of "this" properties that it initializes.
807     * @param thisProperties number of properties assigned to "this"
808     * @return the allocation strategy
809     */
810    static AllocationStrategy createAllocationStrategy(final int thisProperties, final boolean dualFields) {
811        final int paddedFieldCount = getPaddedFieldCount(thisProperties);
812        return new AllocationStrategy(paddedFieldCount, dualFields);
813    }
814}
815