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