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