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