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