ObjectClassGenerator.java revision 1006:f04f14587586
1/* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.nashorn.internal.codegen; 27 28import static jdk.nashorn.internal.codegen.Compiler.SCRIPTS_PACKAGE; 29import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE; 30import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_ARGUMENTS; 31import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_MAP; 32import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_SCOPE; 33import static jdk.nashorn.internal.codegen.CompilerConstants.JAVA_THIS; 34import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_PREFIX; 35import static jdk.nashorn.internal.codegen.CompilerConstants.className; 36import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup; 37import static jdk.nashorn.internal.lookup.Lookup.MH; 38import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT; 39import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT_OPTIMISTIC; 40import static jdk.nashorn.internal.runtime.JSType.GET_UNDEFINED; 41import static jdk.nashorn.internal.runtime.JSType.TYPE_DOUBLE_INDEX; 42import static jdk.nashorn.internal.runtime.JSType.TYPE_INT_INDEX; 43import static jdk.nashorn.internal.runtime.JSType.TYPE_LONG_INDEX; 44import static jdk.nashorn.internal.runtime.JSType.TYPE_OBJECT_INDEX; 45import static jdk.nashorn.internal.runtime.JSType.TYPE_UNDEFINED_INDEX; 46import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex; 47import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; 48 49import java.lang.invoke.MethodHandle; 50import java.lang.invoke.MethodHandles; 51import java.lang.invoke.MethodType; 52import java.util.EnumSet; 53import java.util.Iterator; 54import java.util.LinkedList; 55import java.util.List; 56 57import jdk.nashorn.internal.codegen.ClassEmitter.Flag; 58import jdk.nashorn.internal.codegen.types.Type; 59import jdk.nashorn.internal.runtime.AccessorProperty; 60import jdk.nashorn.internal.runtime.Context; 61import jdk.nashorn.internal.runtime.FunctionScope; 62import jdk.nashorn.internal.runtime.JSType; 63import jdk.nashorn.internal.runtime.PropertyMap; 64import jdk.nashorn.internal.runtime.ScriptEnvironment; 65import jdk.nashorn.internal.runtime.ScriptObject; 66import jdk.nashorn.internal.runtime.Undefined; 67import jdk.nashorn.internal.runtime.UnwarrantedOptimismException; 68import jdk.nashorn.internal.runtime.logging.DebugLogger; 69import jdk.nashorn.internal.runtime.logging.Loggable; 70import jdk.nashorn.internal.runtime.logging.Logger; 71import jdk.nashorn.internal.runtime.options.Options; 72 73/** 74 * Generates the ScriptObject subclass structure with fields for a user objects. 75 */ 76@Logger(name="fields") 77public 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 /** 103 * Should the runtime only use java.lang.Object slots for fields? If this is false, the representation 104 * will be a primitive 64-bit long value used for all primitives and a java.lang.Object for references. 105 * This introduces a larger number of method handles in the system, as we need to have different getters 106 * and setters for the different fields. 107 * 108 * This is engineered to plug into the TaggedArray implementation, when it's done. 109 */ 110 public static final boolean OBJECT_FIELDS_ONLY = Options.getBooleanProperty("nashorn.fields.objects"); 111 112 /** The field types in the system */ 113 private static final List<Type> FIELD_TYPES = new LinkedList<>(); 114 115 /** What type is the primitive type in dual representation */ 116 public static final Type PRIMITIVE_FIELD_TYPE = Type.LONG; 117 118 private static final MethodHandle GET_DIFFERENT = findOwnMH("getDifferent", Object.class, Object.class, Class.class, MethodHandle.class, MethodHandle.class, int.class); 119 private static final MethodHandle GET_DIFFERENT_UNDEFINED = findOwnMH("getDifferentUndefined", Object.class, int.class); 120 121 /** 122 * The list of field types that we support - one type creates one field. This is currently either 123 * LONG + OBJECT or just OBJECT for classic mode. 124 */ 125 static { 126 if (!OBJECT_FIELDS_ONLY) { 127 FIELD_TYPES.add(PRIMITIVE_FIELD_TYPE); 128 } 129 FIELD_TYPES.add(Type.OBJECT); 130 } 131 private static boolean initialized = false; 132 133 /** The context */ 134 private final Context context; 135 136 /** 137 * Constructor 138 * 139 * @param context a context 140 */ 141 public ObjectClassGenerator(final Context context) { 142 this.context = context; 143 assert context != null; 144 this.log = initLogger(context); 145 if (!initialized) { 146 initialized = true; 147 if (OBJECT_FIELDS_ONLY) { 148 log.warning("Running with object fields only - this is a deprecated configuration."); 149 } 150 } 151 } 152 153 @Override 154 public DebugLogger getLogger() { 155 return log; 156 } 157 158 @Override 159 public DebugLogger initLogger(final Context ctxt) { 160 return ctxt.getLogger(this.getClass()); 161 } 162 163 /** 164 * Pack a number into a primitive long field 165 * @param n number object 166 * @return primitive long value with all the bits in the number 167 */ 168 public static long pack(final Number n) { 169 if (n instanceof Integer) { 170 return n.intValue(); 171 } else if (n instanceof Long) { 172 return n.longValue(); 173 } else if (n instanceof Double) { 174 return Double.doubleToRawLongBits(n.doubleValue()); 175 } 176 throw new AssertionError("cannot pack" + n); 177 } 178 179 /** 180 * Returns the class name for JavaScript objects with fieldCount fields. 181 * 182 * @param fieldCount Number of fields to allocate. 183 * 184 * @return The class name. 185 */ 186 public static String getClassName(final int fieldCount) { 187 return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount : 188 SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName(); 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 * 198 * @return The class name. 199 */ 200 public static String getClassName(final int fieldCount, final int paramCount) { 201 return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + 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)} or {@link #getClassName(int, int)}. 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 = JS_OBJECT_PREFIX.symbolName(); 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 static void initializeToUndefined(final MethodEmitter init, final String className, final List<String> fieldNames) { 242 if (!OBJECT_FIELDS_ONLY) { 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); 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); 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 static List<String> addFields(final ClassEmitter classEmitter, final int fieldCount) { 357 final List<String> initFields = new LinkedList<>(); 358 359 for (int i = 0; i < fieldCount; i++) { 360 for (final Type type : FIELD_TYPES) { 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 assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields"; 537 return MH.explicitCastArguments(primitiveGetter, primitiveGetter.type().changeReturnType(int.class)); 538 case TYPE_LONG_INDEX: 539 assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields"; 540 return primitiveGetter; 541 case TYPE_DOUBLE_INDEX: 542 assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields"; 543 return MH.filterReturnValue(primitiveGetter, UNPACK_DOUBLE); 544 case TYPE_OBJECT_INDEX: 545 return objectGetter; 546 default: 547 throw new AssertionError(forType); 548 } 549 } 550 551 //no optimism here. we do unconditional conversion to types 552 private static MethodHandle createGetterInner(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final List<MethodHandle> converters, final int programPoint) { 553 final int fti = forType == null ? TYPE_UNDEFINED_INDEX : getAccessorTypeIndex(forType); 554 final int ti = getAccessorTypeIndex(type); 555 //this means fail if forType != type 556 final boolean isOptimistic = converters == CONVERT_OBJECT_OPTIMISTIC; 557 final boolean isPrimitiveStorage = forType != null && forType.isPrimitive(); 558 559 //which is the primordial getter 560 final MethodHandle getter = OBJECT_FIELDS_ONLY ? objectGetter : isPrimitiveStorage ? primitiveGetter : objectGetter; 561 562 if (forType == null) { 563 if (isOptimistic) { 564 //return undefined if asking for object. otherwise throw UnwarrantedOptimismException 565 if (ti == TYPE_OBJECT_INDEX) { 566 return MH.dropArguments(GET_UNDEFINED.get(TYPE_OBJECT_INDEX), 0, Object.class); 567 } 568 //throw exception 569 return MH.asType( 570 MH.dropArguments( 571 MH.insertArguments( 572 GET_DIFFERENT_UNDEFINED, 573 0, 574 programPoint), 575 0, 576 Object.class), 577 getter.type().changeReturnType(type)); 578 } 579 //return an undefined and coerce it to the appropriate type 580 return MH.dropArguments(GET_UNDEFINED.get(ti), 0, Object.class); 581 } 582 583 assert forType != null; 584 assert !OBJECT_FIELDS_ONLY || forType == Object.class : forType; 585 586 if (isOptimistic) { 587 if (fti < ti) { 588 //asking for a wider type than currently stored. then it's OK to coerce. 589 //e.g. stored as int, ask for long or double 590 //e.g. stored as long, ask for double 591 assert fti != TYPE_UNDEFINED_INDEX; 592 final MethodHandle tgetter = getterForType(forType, primitiveGetter, objectGetter); 593 return MH.asType(tgetter, tgetter.type().changeReturnType(type)); 594 } else if (fti == ti) { 595 //Fast path, never throw exception - exact getter, just unpack if needed 596 return getterForType(forType, primitiveGetter, objectGetter); 597 } else { 598 assert fti > ti; 599 //if asking for a narrower type than the storage - throw exception 600 //unless FTI is object, in that case we have to go through the converters 601 //there is no 602 if (fti == TYPE_OBJECT_INDEX) { 603 return MH.filterReturnValue( 604 objectGetter, 605 MH.insertArguments( 606 converters.get(ti), 607 1, 608 programPoint)); 609 } 610 611 //asking for narrower primitive than we have stored, that is an 612 //UnwarrantedOptimismException 613 return MH.asType( 614 MH.filterArguments( 615 objectGetter, 616 0, 617 MH.insertArguments( 618 GET_DIFFERENT, 619 1, 620 forType, 621 primitiveGetter, 622 objectGetter, 623 programPoint)), 624 objectGetter.type().changeReturnType(type)); 625 } 626 } 627 628 assert !isOptimistic; 629 //freely coerce the result to whatever you asked for, this is e.g. Object->int for a & b 630 final MethodHandle tgetter = getterForType(forType, primitiveGetter, objectGetter); 631 if (fti == TYPE_OBJECT_INDEX) { 632 if (fti != ti) { 633 return MH.filterReturnValue(tgetter, CONVERT_OBJECT.get(ti)); 634 } 635 return tgetter; 636 } 637 638 assert !OBJECT_FIELDS_ONLY; 639 //final MethodType pmt = primitiveGetter.type(); 640 assert primitiveGetter != null; 641 final MethodType tgetterType = tgetter.type(); 642 switch (fti) { 643 case TYPE_INT_INDEX: { 644 return MH.asType(tgetter, tgetterType.changeReturnType(type)); 645 } 646 case TYPE_LONG_INDEX: 647 switch (ti) { 648 case TYPE_INT_INDEX: 649 //get int while an int, truncating cast of long value 650 return MH.filterReturnValue(tgetter, JSType.TO_INT32_L.methodHandle); 651 case TYPE_LONG_INDEX: 652 return primitiveGetter; 653 default: 654 return MH.asType(tgetter, tgetterType.changeReturnType(type)); 655 } 656 case TYPE_DOUBLE_INDEX: 657 switch (ti) { 658 case TYPE_INT_INDEX: 659 return MH.filterReturnValue(tgetter, JSType.TO_INT32_D.methodHandle); 660 case TYPE_LONG_INDEX: 661 return MH.explicitCastArguments(tgetter, tgetterType.changeReturnType(type)); 662 case TYPE_DOUBLE_INDEX: 663 assert tgetterType.returnType() == double.class; 664 return tgetter; 665 default: 666 return MH.asType(tgetter, tgetterType.changeReturnType(Object.class)); 667 } 668 default: 669 throw new UnsupportedOperationException(forType + "=>" + type); 670 } 671 } 672 673 /** 674 * Given a primitiveGetter (optional for non dual fields) and an objectSetter that retrieve 675 * the primitive and object version of a field respectively, return one with the correct 676 * method type and the correct filters. For example, if the value is stored as a double 677 * and we want an Object getter, in the dual fields world we'd pick the primitiveGetter, 678 * which reads a long, use longBitsToDouble on the result to unpack it, and then change the 679 * return type to Object, boxing it. In the objects only world there are only object fields, 680 * primitives are boxed when asked for them and we don't need to bother with primitive encoding 681 * (or even undefined, which if forType==null) representation, so we just return whatever is 682 * in the object field. The object field is always initiated to Undefined, so here, where we have 683 * the representation for Undefined in all our bits, this is not a problem. 684 * <p> 685 * Representing undefined in a primitive is hard, for an int there aren't enough bits, for a long 686 * we could limit the width of a representation, and for a double (as long as it is stored as long, 687 * as all NaNs will turn into QNaN on ia32, which is one bit pattern, we should use a special NaN). 688 * Naturally we could have special undefined values for all types which mean "go look in a wider field", 689 * but the guards needed on every getter took too much time. 690 * <p> 691 * To see how this is used, look for example in {@link AccessorProperty#getGetter} 692 * <p> 693 * @param forType representation of the underlying type in the field, null if undefined 694 * @param type type to retrieve it as 695 * @param primitiveGetter getter to read the primitive version of this field (null if Objects Only) 696 * @param objectGetter getter to read the object version of this field 697 * @param programPoint program point for getter, if program point is INVALID_PROGRAM_POINT, then this is not an optimistic getter 698 * 699 * @return getter for the given representation that returns the given type 700 */ 701 public static MethodHandle createGetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final int programPoint) { 702 return createGetterInner( 703 forType, 704 type, 705 primitiveGetter, 706 objectGetter, 707 isValid(programPoint) ? CONVERT_OBJECT_OPTIMISTIC : CONVERT_OBJECT, 708 programPoint); 709 } 710 711 /** 712 * This is similar to the {@link ObjectClassGenerator#createGetter} function. Performs 713 * the necessary operations to massage a setter operand of type {@code type} to 714 * fit into the primitive field (if primitive and dual fields is enabled) or into 715 * the object field (box if primitive and dual fields is disabled) 716 * 717 * @param forType representation of the underlying object 718 * @param type representation of field to write, and setter signature 719 * @param primitiveSetter setter that writes to the primitive field (null if Objects Only) 720 * @param objectSetter setter that writes to the object field 721 * 722 * @return the setter for the given representation that takes a {@code type} 723 */ 724 public static MethodHandle createSetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveSetter, final MethodHandle objectSetter) { 725 assert forType != null; 726 727 final int fti = getAccessorTypeIndex(forType); 728 final int ti = getAccessorTypeIndex(type); 729 730 if (fti == TYPE_OBJECT_INDEX || OBJECT_FIELDS_ONLY) { 731 if (ti == TYPE_OBJECT_INDEX) { 732 return objectSetter; 733 } 734 735 return MH.asType(objectSetter, objectSetter.type().changeParameterType(1, type)); 736 } 737 738 assert !OBJECT_FIELDS_ONLY; 739 740 final MethodType pmt = primitiveSetter.type(); 741 742 switch (fti) { 743 case TYPE_INT_INDEX: 744 case TYPE_LONG_INDEX: 745 switch (ti) { 746 case TYPE_INT_INDEX: 747 return MH.asType(primitiveSetter, pmt.changeParameterType(1, int.class)); 748 case TYPE_LONG_INDEX: 749 return primitiveSetter; 750 case TYPE_DOUBLE_INDEX: 751 return MH.filterArguments(primitiveSetter, 1, PACK_DOUBLE); 752 default: 753 return objectSetter; 754 } 755 case TYPE_DOUBLE_INDEX: 756 if (ti == TYPE_OBJECT_INDEX) { 757 return objectSetter; 758 } 759 return MH.asType(MH.filterArguments(primitiveSetter, 1, PACK_DOUBLE), pmt.changeParameterType(1, type)); 760 default: 761 throw new UnsupportedOperationException(forType + "=>" + type); 762 } 763 } 764 765 @SuppressWarnings("unused") 766 private static boolean isType(final Class<?> boxedForType, final Object x) { 767 return x != null && x.getClass() == boxedForType; 768 } 769 770 private static Class<? extends Number> getBoxedType(final Class<?> forType) { 771 if (forType == int.class) { 772 return Integer.class; 773 } 774 775 if (forType == long.class) { 776 return Long.class; 777 } 778 779 if (forType == double.class) { 780 return Double.class; 781 } 782 783 assert false; 784 return null; 785 } 786 787 /** 788 * If we are setting boxed types (because the compiler couldn't determine which they were) to 789 * a primitive field, we can reuse the primitive field getter, as long as we are setting an element 790 * of the same boxed type as the primitive type representation 791 * 792 * @param forType the current type 793 * @param primitiveSetter primitive setter for the current type with an element of the current type 794 * @param objectSetter the object setter 795 * 796 * @return method handle that checks if the element to be set is of the currenttype, even though it's boxed 797 * and instead of using the generic object setter, that would blow up the type and invalidate the map, 798 * unbox it and call the primitive setter instead 799 */ 800 public static MethodHandle createGuardBoxedPrimitiveSetter(final Class<?> forType, final MethodHandle primitiveSetter, final MethodHandle objectSetter) { 801 final Class<? extends Number> boxedForType = getBoxedType(forType); 802 //object setter that checks for primitive if current type is primitive 803 return MH.guardWithTest( 804 MH.insertArguments( 805 MH.dropArguments( 806 IS_TYPE_GUARD, 807 1, 808 Object.class), 809 0, 810 boxedForType), 811 MH.asType( 812 primitiveSetter, 813 objectSetter.type()), 814 objectSetter); 815 } 816 /** 817 * Add padding to field count to avoid creating too many classes and have some spare fields 818 * @param count the field count 819 * @return the padded field count 820 */ 821 static int getPaddedFieldCount(final int count) { 822 return count / FIELD_PADDING * FIELD_PADDING + FIELD_PADDING; 823 } 824 825 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 826 return MH.findStatic(MethodHandles.lookup(), ObjectClassGenerator.class, name, MH.type(rtype, types)); 827 } 828 829 830} 831