View Javadoc
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  
26  package jdk.nashorn.internal.codegen;
27  
28  import static jdk.nashorn.internal.codegen.Compiler.SCRIPTS_PACKAGE;
29  import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
30  import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_ARGUMENTS;
31  import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_MAP;
32  import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_SCOPE;
33  import static jdk.nashorn.internal.codegen.CompilerConstants.JAVA_THIS;
34  import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_PREFIX;
35  import static jdk.nashorn.internal.codegen.CompilerConstants.className;
36  import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
37  import static jdk.nashorn.internal.lookup.Lookup.MH;
38  
39  import java.lang.invoke.MethodHandle;
40  import java.lang.invoke.MethodHandles;
41  import java.lang.invoke.MethodType;
42  import java.util.Arrays;
43  import java.util.Collections;
44  import java.util.EnumSet;
45  import java.util.Iterator;
46  import java.util.LinkedList;
47  import java.util.List;
48  import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
49  import jdk.nashorn.internal.codegen.types.Type;
50  import jdk.nashorn.internal.runtime.AccessorProperty;
51  import jdk.nashorn.internal.runtime.Context;
52  import jdk.nashorn.internal.runtime.DebugLogger;
53  import jdk.nashorn.internal.runtime.FunctionScope;
54  import jdk.nashorn.internal.runtime.JSType;
55  import jdk.nashorn.internal.runtime.PropertyMap;
56  import jdk.nashorn.internal.runtime.ScriptEnvironment;
57  import jdk.nashorn.internal.runtime.ScriptObject;
58  import jdk.nashorn.internal.runtime.ScriptRuntime;
59  import jdk.nashorn.internal.runtime.options.Options;
60  
61  /**
62   * Generates the ScriptObject subclass structure with fields for a user objects.
63   */
64  public final class ObjectClassGenerator {
65  
66      /**
67       * Marker for scope parameters.
68       */
69      static final String SCOPE_MARKER = "P";
70  
71      /**
72       * Minimum number of extra fields in an object.
73       */
74      static final int FIELD_PADDING  = 4;
75  
76      /**
77       * Debug field logger
78       * Should we print debugging information for fields when they are generated and getters/setters are called?
79       */
80      public static final DebugLogger LOG = new DebugLogger("fields", "nashorn.fields.debug");
81  
82      /**
83       * is field debugging enabled. Several modules in codegen and properties use this, hence
84       * public access.
85       */
86      public static final boolean DEBUG_FIELDS = LOG.isEnabled();
87  
88      /**
89       * Should the runtime only use java.lang.Object slots for fields? If this is false, the representation
90       * will be a primitive 64-bit long value used for all primitives and a java.lang.Object for references.
91       * This introduces a larger number of method handles in the system, as we need to have different getters
92       * and setters for the different fields. Currently this introduces significant overhead in Hotspot.
93       *
94       * This is engineered to plug into the TaggedArray implementation, when it's done.
95       */
96      public static final boolean OBJECT_FIELDS_ONLY = !Options.getBooleanProperty("nashorn.fields.dual");
97  
98      /** The field types in the system */
99      private static final List<Type> FIELD_TYPES = new LinkedList<>();
100 
101     /** What type is the primitive type in dual representation */
102     public static final Type PRIMITIVE_TYPE = Type.LONG;
103 
104     /**
105      * The list of field types that we support - one type creates one field. This is currently either
106      * LONG + OBJECT or just OBJECT for classic mode.
107      */
108     static {
109         if (!OBJECT_FIELDS_ONLY) {
110             System.err.println("WARNING!!! Running with primitive fields - there is untested functionality!");
111             FIELD_TYPES.add(PRIMITIVE_TYPE);
112         }
113         FIELD_TYPES.add(Type.OBJECT);
114     }
115 
116     /** The context */
117     private final Context context;
118 
119     /**
120      * The list of available accessor types in width order. This order is used for type guesses narrow{@literal ->} wide
121      *  in the dual--fields world
122      */
123     public static final List<Type> ACCESSOR_TYPES = Collections.unmodifiableList(
124             Arrays.asList(
125                 Type.INT,
126                 Type.LONG,
127                 Type.NUMBER,
128                 Type.OBJECT));
129 
130     //these are hard coded for speed and so that we can switch on them
131     private static final int TYPE_INT_INDEX    = 0; //getAccessorTypeIndex(int.class);
132     private static final int TYPE_LONG_INDEX   = 1; //getAccessorTypeIndex(long.class);
133     private static final int TYPE_DOUBLE_INDEX = 2; //getAccessorTypeIndex(double.class);
134     private static final int TYPE_OBJECT_INDEX = 3; //getAccessorTypeIndex(Object.class);
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     }
145 
146     /**
147      * Given a type of an accessor, return its index in [0..getNumberOfAccessorTypes())
148      *
149      * @param type the type
150      *
151      * @return the accessor index, or -1 if no accessor of this type exists
152      */
153     public static int getAccessorTypeIndex(final Type type) {
154         return getAccessorTypeIndex(type.getTypeClass());
155     }
156 
157     /**
158      * Given a class of an accessor, return its index in [0..getNumberOfAccessorTypes())
159      *
160      * Note that this is hardcoded with respect to the dynamic contents of the accessor
161      * types array for speed. Hotspot got stuck with this as 5% of the runtime in
162      * a benchmark when it looped over values and increased an index counter. :-(
163      *
164      * @param type the type
165      *
166      * @return the accessor index, or -1 if no accessor of this type exists
167      */
168     public static int getAccessorTypeIndex(final Class<?> type) {
169         if (type == int.class) {
170             return 0;
171         } else if (type == long.class) {
172             return 1;
173         } else if (type == double.class) {
174             return 2;
175         } else if (!type.isPrimitive()) {
176             return 3;
177         }
178         return -1;
179     }
180 
181     /**
182      * Return the number of accessor types available.
183      *
184      * @return number of accessor types in system
185      */
186     public static int getNumberOfAccessorTypes() {
187         return ACCESSOR_TYPES.size();
188     }
189 
190     /**
191      * Return the accessor type based on its index in [0..getNumberOfAccessorTypes())
192      * Indexes are ordered narrower{@literal ->}wider / optimistic{@literal ->}pessimistic. Invalidations always
193      * go to a type of higher index
194      *
195      * @param index accessor type index
196      *
197      * @return a type corresponding to the index.
198      */
199 
200     public static Type getAccessorType(final int index) {
201         return ACCESSOR_TYPES.get(index);
202     }
203 
204     /**
205      * Returns the class name for JavaScript objects with fieldCount fields.
206      *
207      * @param fieldCount Number of fields to allocate.
208      *
209      * @return The class name.
210      */
211     public static String getClassName(final int fieldCount) {
212         return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount :
213                                  SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName();
214     }
215 
216     /**
217      * Returns the class name for JavaScript scope with fieldCount fields and
218      * paramCount parameters.
219      *
220      * @param fieldCount Number of fields to allocate.
221      * @param paramCount Number of parameters to allocate
222      *
223      * @return The class name.
224      */
225     public static String getClassName(final int fieldCount, final int paramCount) {
226         return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount + SCOPE_MARKER + paramCount;
227     }
228 
229     /**
230      * Returns the number of fields in the JavaScript scope class. Its name had to be generated using either
231      * {@link #getClassName(int)} or {@link #getClassName(int, int)}.
232      * @param clazz the JavaScript scope class.
233      * @return the number of fields in the scope class.
234      */
235     public static int getFieldCount(Class<?> clazz) {
236         final String name = clazz.getSimpleName();
237         final String prefix = JS_OBJECT_PREFIX.symbolName();
238         if(prefix.equals(name)) {
239             return 0;
240         }
241         final int scopeMarker = name.indexOf(SCOPE_MARKER);
242         return Integer.parseInt(scopeMarker == -1 ? name.substring(prefix.length()) : name.substring(prefix.length(), scopeMarker));
243     }
244 
245     /**
246      * Returns the name of a field based on number and type.
247      *
248      * @param fieldIndex Ordinal of field.
249      * @param type       Type of field.
250      *
251      * @return The field name.
252      */
253     public static String getFieldName(final int fieldIndex, final Type type) {
254         return type.getDescriptor().substring(0, 1) + fieldIndex;
255     }
256 
257     /**
258      * In the world of Object fields, we also have no undefined SwitchPoint, to reduce as much potential
259      * MethodHandle overhead as possible. In that case, we explicitly need to assign undefined to fields
260      * when we initialize them.
261      *
262      * @param init       constructor to generate code in
263      * @param className  name of class
264      * @param fieldNames fields to initialize to undefined, where applicable
265      */
266     private static void initializeToUndefined(final MethodEmitter init, final String className, final List<String> fieldNames) {
267         if (fieldNames.isEmpty()) {
268             return;
269         }
270 
271         // always initialize fields to undefined, even with --dual-fields. Then it's ok to
272         // remember things like "widest set type" in properties, and if it's object, don't
273         // add any special "return undefined" getters, saving an invalidation
274         init.load(Type.OBJECT, JAVA_THIS.slot());
275         init.loadUndefined(Type.OBJECT);
276 
277         final Iterator<String> iter = fieldNames.iterator();
278         while (iter.hasNext()) {
279             final String fieldName = iter.next();
280             if (iter.hasNext()) {
281                 init.dup2();
282             }
283             init.putField(className, fieldName, Type.OBJECT.getDescriptor());
284         }
285     }
286 
287     /**
288      * Generate the byte codes for a JavaScript object class or scope.
289      * Class name is a function of number of fields and number of param
290      * fields
291      *
292      * @param descriptor Descriptor pulled from class name.
293      *
294      * @return Byte codes for generated class.
295      */
296     public byte[] generate(final String descriptor) {
297         final String[] counts     = descriptor.split(SCOPE_MARKER);
298         final int      fieldCount = Integer.valueOf(counts[0]);
299 
300         if (counts.length == 1) {
301             return generate(fieldCount);
302         }
303 
304         final int paramCount = Integer.valueOf(counts[1]);
305 
306         return generate(fieldCount, paramCount);
307     }
308 
309     /**
310      * Generate the byte codes for a JavaScript object class with fieldCount fields.
311      *
312      * @param fieldCount Number of fields in the JavaScript object.
313      *
314      * @return Byte codes for generated class.
315      */
316     public byte[] generate(final int fieldCount) {
317         final String       className    = getClassName(fieldCount);
318         final String       superName    = className(ScriptObject.class);
319         final ClassEmitter classEmitter = newClassEmitter(className, superName);
320 
321         addFields(classEmitter, fieldCount);
322 
323         final MethodEmitter init = newInitMethod(classEmitter);
324         init.returnVoid();
325         init.end();
326 
327         newEmptyInit(classEmitter, className);
328         newAllocate(classEmitter, className);
329 
330         return toByteArray(classEmitter);
331     }
332 
333     /**
334      * Generate the byte codes for a JavaScript scope class with fieldCount fields
335      * and paramCount parameters.
336      *
337      * @param fieldCount Number of fields in the JavaScript scope.
338      * @param paramCount Number of parameters in the JavaScript scope
339      * .
340      * @return Byte codes for generated class.
341      */
342     public byte[] generate(final int fieldCount, final int paramCount) {
343         final String className          = getClassName(fieldCount, paramCount);
344         final String superName          = className(FunctionScope.class);
345         final ClassEmitter classEmitter = newClassEmitter(className, superName);
346         final List<String> initFields   = addFields(classEmitter, fieldCount);
347 
348         final MethodEmitter init = newInitScopeMethod(classEmitter);
349         initializeToUndefined(init, className, initFields);
350         init.returnVoid();
351         init.end();
352 
353         final MethodEmitter initWithArguments = newInitScopeWithArgumentsMethod(classEmitter);
354         initializeToUndefined(initWithArguments, className, initFields);
355         initWithArguments.returnVoid();
356         initWithArguments.end();
357 
358         return toByteArray(classEmitter);
359     }
360 
361     /**
362      * Generates the needed fields.
363      *
364      * @param classEmitter Open class emitter.
365      * @param fieldCount   Number of fields.
366      *
367      * @return List fields that need to be initialized.
368      */
369     private static List<String> addFields(final ClassEmitter classEmitter, final int fieldCount) {
370         final List<String> initFields = new LinkedList<>();
371 
372         for (int i = 0; i < fieldCount; i++) {
373             for (final Type type : FIELD_TYPES) {
374                 final String fieldName = getFieldName(i, type);
375                 classEmitter.field(fieldName, type.getTypeClass());
376 
377                 if (type == Type.OBJECT) {
378                     initFields.add(fieldName);
379                 }
380             }
381         }
382 
383         return initFields;
384     }
385 
386     /**
387      * Allocate and initialize a new class emitter.
388      *
389      * @param className Name of JavaScript class.
390      *
391      * @return Open class emitter.
392      */
393     private ClassEmitter newClassEmitter(final String className, final String superName) {
394         final ClassEmitter classEmitter = new ClassEmitter(context.getEnv(), className, superName);
395         classEmitter.begin();
396 
397         return classEmitter;
398     }
399 
400     /**
401      * Allocate and initialize a new <init> method.
402      *
403      * @param classEmitter  Open class emitter.
404      *
405      * @return Open method emitter.
406      */
407     private static MethodEmitter newInitMethod(final ClassEmitter classEmitter) {
408         final MethodEmitter init = classEmitter.init(PropertyMap.class);
409         init.begin();
410         init.load(Type.OBJECT, JAVA_THIS.slot());
411         init.load(Type.OBJECT, INIT_MAP.slot());
412         init.invoke(constructorNoLookup(ScriptObject.class, PropertyMap.class));
413 
414         return init;
415     }
416 
417     /**
418      * Allocate and initialize a new <init> method for scopes.
419      * @param classEmitter  Open class emitter.
420      * @return Open method emitter.
421      */
422     private static MethodEmitter newInitScopeMethod(final ClassEmitter classEmitter) {
423         final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class);
424         init.begin();
425         init.load(Type.OBJECT, JAVA_THIS.slot());
426         init.load(Type.OBJECT, INIT_MAP.slot());
427         init.load(Type.OBJECT, INIT_SCOPE.slot());
428         init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class));
429 
430         return init;
431     }
432 
433     /**
434      * Allocate and initialize a new <init> method for scopes with arguments.
435      * @param classEmitter  Open class emitter.
436      * @return Open method emitter.
437      */
438     private static MethodEmitter newInitScopeWithArgumentsMethod(final ClassEmitter classEmitter) {
439         final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class, ScriptObject.class);
440         init.begin();
441         init.load(Type.OBJECT, JAVA_THIS.slot());
442         init.load(Type.OBJECT, INIT_MAP.slot());
443         init.load(Type.OBJECT, INIT_SCOPE.slot());
444         init.load(Type.OBJECT, INIT_ARGUMENTS.slot());
445         init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class, ScriptObject.class));
446 
447         return init;
448     }
449 
450     /**
451      * Add an empty <init> method to the JavaScript class.
452      *
453      * @param classEmitter Open class emitter.
454      * @param className    Name of JavaScript class.
455      */
456     private static void newEmptyInit(final ClassEmitter classEmitter, final String className) {
457         final MethodEmitter emptyInit = classEmitter.init();
458         emptyInit.begin();
459         emptyInit.load(Type.OBJECT, JAVA_THIS.slot());
460         emptyInit.loadNull();
461         emptyInit.invoke(constructorNoLookup(className, PropertyMap.class));
462         emptyInit.returnVoid();
463         emptyInit.end();
464     }
465 
466     /**
467      * Add an empty <init> method to the JavaScript class.
468      *
469      * @param classEmitter Open class emitter.
470      * @param className    Name of JavaScript class.
471      */
472     private static void newAllocate(final ClassEmitter classEmitter, final String className) {
473         final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.symbolName(), ScriptObject.class, PropertyMap.class);
474         allocate.begin();
475         allocate._new(className);
476         allocate.dup();
477         allocate.load(Type.typeFor(PropertyMap.class), 0);
478         allocate.invoke(constructorNoLookup(className, PropertyMap.class));
479         allocate._return();
480         allocate.end();
481     }
482 
483     /**
484      * Collects the byte codes for a generated JavaScript class.
485      *
486      * @param classEmitter Open class emitter.
487      * @return Byte codes for the class.
488      */
489     private byte[] toByteArray(final ClassEmitter classEmitter) {
490         classEmitter.end();
491 
492         final byte[] code = classEmitter.toByteArray();
493         final ScriptEnvironment env = context.getEnv();
494 
495         if (env._print_code) {
496             env.getErr().println(ClassEmitter.disassemble(code));
497         }
498 
499         if (env._verify_code) {
500             context.verify(code);
501         }
502 
503         return code;
504     }
505 
506     /** Double to long bits, used with --dual-fields for primitive double values */
507     private static final MethodHandle PACK_DOUBLE =
508         MH.explicitCastArguments(MH.findStatic(MethodHandles.publicLookup(), Double.class, "doubleToRawLongBits", MH.type(long.class, double.class)), MH.type(long.class, double.class));
509 
510     /** double bits to long, used with --dual-fields for primitive double values */
511     private static MethodHandle UNPACK_DOUBLE =
512         MH.findStatic(MethodHandles.publicLookup(), Double.class, "longBitsToDouble", MH.type(double.class, long.class));
513 
514     /** object conversion quickies with JS semantics - used for return value and parameter filter */
515     private static MethodHandle[] CONVERT_OBJECT = {
516         JSType.TO_INT32.methodHandle(),
517         JSType.TO_UINT32.methodHandle(),
518         JSType.TO_NUMBER.methodHandle(),
519         null
520     };
521 
522     /**
523      * Given a primitiveGetter (optional for non dual fields) and an objectSetter that retrieve
524      * the primitive and object version of a field respectively, return one with the correct
525      * method type and the correct filters. For example, if the value is stored as a double
526      * and we want an Object getter, in the dual fields world we'd pick the primitiveGetter,
527      * which reads a long, use longBitsToDouble on the result to unpack it, and then change the
528      * return type to Object, boxing it. In the objects only world there are only object fields,
529      * primtives are boxed when asked for them and we don't need to bother with primitive encoding
530      * (or even undefined, which if forType==null) representation, so we just return whatever is
531      * in the object field. The object field is always initiated to Undefined, so here, where we have
532      * the representation for Undefined in all our bits, this is not a problem.
533      * <p>
534      * Representing undefined in a primitive is hard, for an int there aren't enough bits, for a long
535      * we could limit the width of a representation, and for a double (as long as it is stored as long,
536      * as all NaNs will turn into QNaN on ia32, which is one bit pattern, we should use a special NaN).
537      * Naturally we could have special undefined values for all types which mean "go look in a wider field",
538      * but the guards needed on every getter took too much time.
539      * <p>
540      * To see how this is used, look for example in {@link AccessorProperty#getGetter}
541      * <p>
542      * @param forType         representation of the underlying type in the field, null if undefined
543      * @param type            type to retrieve it as
544      * @param primitiveGetter getter to read the primitive version of this field (null if Objects Only)
545      * @param objectGetter    getter to read the object version of this field
546      *
547      * @return getter for the given representation that returns the given type
548      */
549     public static MethodHandle createGetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter) {
550         final int fti = forType == null ? -1 : getAccessorTypeIndex(forType);
551         final int ti  = getAccessorTypeIndex(type);
552 
553         if (fti == TYPE_OBJECT_INDEX || OBJECT_FIELDS_ONLY) {
554             if (ti == TYPE_OBJECT_INDEX) {
555                 return objectGetter;
556             }
557 
558             return MH.filterReturnValue(objectGetter, CONVERT_OBJECT[ti]);
559         }
560 
561         assert !OBJECT_FIELDS_ONLY;
562         if (forType == null) {
563             return GET_UNDEFINED[ti];
564         }
565 
566         final MethodType pmt = primitiveGetter.type();
567 
568         switch (fti) {
569         case TYPE_INT_INDEX:
570         case TYPE_LONG_INDEX:
571             switch (ti) {
572             case TYPE_INT_INDEX:
573                 //get int while an int, truncating cast of long value
574                 return MH.explicitCastArguments(primitiveGetter, pmt.changeReturnType(int.class));
575             case TYPE_LONG_INDEX:
576                 return primitiveGetter;
577             default:
578                 return MH.asType(primitiveGetter, pmt.changeReturnType(type));
579             }
580         case TYPE_DOUBLE_INDEX:
581             final MethodHandle getPrimitiveAsDouble = MH.filterReturnValue(primitiveGetter, UNPACK_DOUBLE);
582             switch (ti) {
583             case TYPE_INT_INDEX:
584             case TYPE_LONG_INDEX:
585                 return MH.explicitCastArguments(getPrimitiveAsDouble, pmt.changeReturnType(type));
586             case TYPE_DOUBLE_INDEX:
587                 return getPrimitiveAsDouble;
588             default:
589                 return MH.asType(getPrimitiveAsDouble, pmt.changeReturnType(Object.class));
590             }
591         default:
592             assert false;
593             return null;
594         }
595     }
596 
597     private static final MethodHandle IS_TYPE_GUARD = findOwnMH("isType", boolean.class, Class.class, Object.class);
598 
599     @SuppressWarnings("unused")
600     private static boolean isType(final Class<?> boxedForType, final Object x) {
601         return x.getClass() == boxedForType;
602     }
603 
604     private static Class<? extends Number> getBoxedType(final Class<?> forType) {
605         if (forType == int.class) {
606             return Integer.class;
607         }
608 
609         if (forType == long.class) {
610             return Long.class;
611         }
612 
613         if (forType == double.class) {
614             return Double.class;
615         }
616 
617         assert false;
618         return null;
619     }
620 
621     /**
622      * If we are setting boxed types (because the compiler couldn't determine which they were) to
623      * a primitive field, we can reuse the primitive field getter, as long as we are setting an element
624      * of the same boxed type as the primitive type representation
625      *
626      * @param forType           the current type
627      * @param primitiveSetter   primitive setter for the current type with an element of the current type
628      * @param objectSetter      the object setter
629      *
630      * @return method handle that checks if the element to be set is of the currenttype, even though it's boxed
631      *  and instead of using the generic object setter, that would blow up the type and invalidate the map,
632      *  unbox it and call the primitive setter instead
633      */
634     public static MethodHandle createGuardBoxedPrimitiveSetter(final Class<?> forType, final MethodHandle primitiveSetter, final MethodHandle objectSetter) {
635         final Class<? extends Number> boxedForType = getBoxedType(forType);
636         //object setter that checks for primitive if current type is primitive
637 
638         return MH.guardWithTest(
639             MH.insertArguments(
640                 MH.dropArguments(
641                     IS_TYPE_GUARD,
642                     1,
643                     Object.class),
644                 0,
645                 boxedForType),
646                 MH.asType(
647                     primitiveSetter,
648                     objectSetter.type()),
649                 objectSetter);
650     }
651 
652     /**
653      * This is similar to the {@link ObjectClassGenerator#createGetter} function. Performs
654      * the necessary operations to massage a setter operand of type {@code type} to
655      * fit into the primitive field (if primitive and dual fields is enabled) or into
656      * the object field (box if primitive and dual fields is disabled)
657      *
658      * @param forType         representation of the underlying object
659      * @param type            representation of field to write, and setter signature
660      * @param primitiveSetter setter that writes to the primitive field (null if Objects Only)
661      * @param objectSetter    setter that writes to the object field
662      *
663      * @return the setter for the given representation that takes a {@code type}
664      */
665     public static MethodHandle createSetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveSetter, final MethodHandle objectSetter) {
666         assert forType != null;
667 
668         final int fti = getAccessorTypeIndex(forType);
669         final int ti  = getAccessorTypeIndex(type);
670 
671         if (fti == TYPE_OBJECT_INDEX || OBJECT_FIELDS_ONLY) {
672             if (ti == TYPE_OBJECT_INDEX) {
673                 return objectSetter;
674             }
675 
676             return MH.asType(objectSetter, objectSetter.type().changeParameterType(1, type));
677         }
678 
679         assert !OBJECT_FIELDS_ONLY;
680 
681         final MethodType pmt = primitiveSetter.type();
682 
683         switch (fti) {
684         case TYPE_INT_INDEX:
685         case TYPE_LONG_INDEX:
686             switch (ti) {
687             case TYPE_INT_INDEX:
688                 return MH.asType(primitiveSetter, pmt.changeParameterType(1, int.class));
689             case TYPE_LONG_INDEX:
690                 return primitiveSetter;
691             case TYPE_DOUBLE_INDEX:
692                 return MH.filterArguments(primitiveSetter, 1, PACK_DOUBLE);
693             default:
694                 return objectSetter;
695             }
696         case TYPE_DOUBLE_INDEX:
697             if (ti == TYPE_OBJECT_INDEX) {
698                 return objectSetter;
699             }
700             return MH.asType(MH.filterArguments(primitiveSetter, 1, PACK_DOUBLE), pmt.changeParameterType(1, type));
701         default:
702             assert false;
703             return null;
704         }
705     }
706 
707     /**
708      * Add padding to field count to avoid creating too many classes and have some spare fields
709      * @param count the field count
710      * @return the padded field count
711      */
712     static int getPaddedFieldCount(final int count) {
713         return count / FIELD_PADDING * FIELD_PADDING + FIELD_PADDING;
714     }
715 
716     //
717     // Provide generic getters and setters for undefined types. If a type is undefined, all
718     // and marshals the set to the correct setter depending on the type of the value being set.
719     // Note that there are no actual undefined versions of int, long and double in JavaScript,
720     // but executing toInt32, toLong and toNumber always returns a working result, 0, 0L or NaN
721     //
722 
723     /** The value of Undefined cast to an int32 */
724     public static final int    UNDEFINED_INT    = 0;
725     /** The value of Undefined cast to a long */
726     public static final long   UNDEFINED_LONG   = 0L;
727     /** The value of Undefined cast to a double */
728     public static final double UNDEFINED_DOUBLE = Double.NaN;
729 
730     /**
731      * Compute type name for correct undefined getter
732      * @param type the type
733      * @return name of getter
734      */
735     private static String typeName(final Type type) {
736         String name = type.getTypeClass().getName();
737         final int dot = name.lastIndexOf('.');
738         if (dot != -1) {
739             name = name.substring(dot + 1);
740         }
741         return Character.toUpperCase(name.charAt(0)) + name.substring(1);
742     }
743 
744     /**
745      * Handles for undefined getters of the different types
746      */
747     private static final MethodHandle[] GET_UNDEFINED = new MethodHandle[ObjectClassGenerator.getNumberOfAccessorTypes()];
748 
749     /**
750      * Used to wrap getters for undefined values, where this matters. Currently only in dual fields.
751      * If an object starts out as undefined it needs special getters until it has been assigned
752      * something the first time
753      *
754      * @param returnType type to cast the undefined to
755      *
756      * @return undefined as returnType
757      */
758     public static MethodHandle getUndefined(final Class<?> returnType) {
759         return GET_UNDEFINED[ObjectClassGenerator.getAccessorTypeIndex(returnType)];
760     }
761 
762     static {
763         int pos = 0;
764         for (final Type type : ACCESSOR_TYPES) {
765             GET_UNDEFINED[pos++] = findOwnMH("getUndefined" + typeName(type), type.getTypeClass(), Object.class);
766         }
767     }
768 
769     @SuppressWarnings("unused")
770     private static int getUndefinedInt(final Object obj) {
771         return UNDEFINED_INT;
772     }
773 
774     @SuppressWarnings("unused")
775     private static long getUndefinedLong(final Object obj) {
776         return UNDEFINED_LONG;
777     }
778 
779     @SuppressWarnings("unused")
780     private static double getUndefinedDouble(final Object obj) {
781         return UNDEFINED_DOUBLE;
782     }
783 
784     @SuppressWarnings("unused")
785     private static Object getUndefinedObject(final Object obj) {
786         return ScriptRuntime.UNDEFINED;
787     }
788 
789     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
790         return MH.findStatic(MethodHandles.lookup(), ObjectClassGenerator.class, name, MH.type(rtype, types));
791     }
792 }