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_DUAL_FIELD_PREFIX;
  35 import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_SINGLE_FIELD_PREFIX;
  36 import static jdk.nashorn.internal.codegen.CompilerConstants.className;
  37 import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
  38 import static jdk.nashorn.internal.lookup.Lookup.MH;
  39 import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT;
  40 import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT_OPTIMISTIC;
  41 import static jdk.nashorn.internal.runtime.JSType.GET_UNDEFINED;
  42 import static jdk.nashorn.internal.runtime.JSType.TYPE_DOUBLE_INDEX;
  43 import static jdk.nashorn.internal.runtime.JSType.TYPE_INT_INDEX;
  44 import static jdk.nashorn.internal.runtime.JSType.TYPE_OBJECT_INDEX;
  45 import static jdk.nashorn.internal.runtime.JSType.TYPE_UNDEFINED_INDEX;
  46 import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
  47 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
  48 
  49 import java.lang.invoke.MethodHandle;
  50 import java.lang.invoke.MethodHandles;
  51 import java.lang.invoke.MethodType;
  52 import java.util.EnumSet;
  53 import java.util.Iterator;
  54 import java.util.LinkedList;
  55 import java.util.List;
  56 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
  57 import jdk.nashorn.internal.codegen.types.Type;
  58 import jdk.nashorn.internal.runtime.AccessorProperty;
  59 import jdk.nashorn.internal.runtime.AllocationStrategy;
  60 import jdk.nashorn.internal.runtime.Context;
  61 import jdk.nashorn.internal.runtime.FunctionScope;
  62 import jdk.nashorn.internal.runtime.JSType;
  63 import jdk.nashorn.internal.runtime.PropertyMap;
  64 import jdk.nashorn.internal.runtime.ScriptEnvironment;
  65 import jdk.nashorn.internal.runtime.ScriptObject;
  66 import jdk.nashorn.internal.runtime.Undefined;
  67 import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
  68 import jdk.nashorn.internal.runtime.logging.DebugLogger;
  69 import jdk.nashorn.internal.runtime.logging.Loggable;
  70 import 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")
  76 public 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 }