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.runtime; 27 28 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE; 29 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createGetter; 30 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createSetter; 31 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getFieldCount; 32 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getFieldName; 33 import static jdk.nashorn.internal.lookup.Lookup.MH; 34 import static jdk.nashorn.internal.lookup.MethodHandleFactory.stripName; 35 import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex; 36 import static jdk.nashorn.internal.runtime.JSType.getNumberOfAccessorTypes; 37 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 38 import java.io.IOException; 39 import java.io.ObjectInputStream; 40 import java.lang.invoke.MethodHandle; 41 import java.lang.invoke.MethodHandles; 42 import java.lang.invoke.SwitchPoint; 43 import java.util.function.Supplier; 44 import java.util.logging.Level; 45 import jdk.nashorn.internal.codegen.ObjectClassGenerator; 46 import jdk.nashorn.internal.codegen.types.Type; 47 import jdk.nashorn.internal.lookup.Lookup; 48 import jdk.nashorn.internal.objects.Global; 49 50 /** 51 * An AccessorProperty is the most generic property type. An AccessorProperty is 52 * represented as fields in a ScriptObject class. 53 */ 54 public class AccessorProperty extends Property { 55 private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 56 57 private static final MethodHandle REPLACE_MAP = findOwnMH_S("replaceMap", Object.class, Object.class, PropertyMap.class); 58 private static final MethodHandle INVALIDATE_SP = findOwnMH_S("invalidateSwitchPoint", Object.class, AccessorProperty.class, Object.class); 59 60 private static final int NOOF_TYPES = getNumberOfAccessorTypes(); 61 private static final long serialVersionUID = 3371720170182154920L; 62 63 /** 64 * Properties in different maps for the same structure class will share their field getters and setters. This could 65 * be further extended to other method handles that are looked up in the AccessorProperty constructor, but right now 66 * these are the most frequently retrieved ones, and lookup of method handle natives only registers in the profiler 67 * for them. 68 */ 69 private static ClassValue<Accessors> GETTERS_SETTERS = new ClassValue<Accessors>() { 70 @Override 71 protected Accessors computeValue(final Class<?> structure) { 72 return new Accessors(structure); 73 } 74 }; 75 76 private static class Accessors { 77 final MethodHandle[] objectGetters; 78 final MethodHandle[] objectSetters; 79 final MethodHandle[] primitiveGetters; 80 final MethodHandle[] primitiveSetters; 81 82 /** 83 * Normal 84 * @param structure 85 */ 86 Accessors(final Class<?> structure) { 87 final int fieldCount = getFieldCount(structure); 88 objectGetters = new MethodHandle[fieldCount]; 89 objectSetters = new MethodHandle[fieldCount]; 90 primitiveGetters = new MethodHandle[fieldCount]; 91 primitiveSetters = new MethodHandle[fieldCount]; 92 93 for (int i = 0; i < fieldCount; i++) { 94 final String fieldName = getFieldName(i, Type.OBJECT); 95 final Class<?> typeClass = Type.OBJECT.getTypeClass(); 96 objectGetters[i] = MH.asType(MH.getter(LOOKUP, structure, fieldName, typeClass), Lookup.GET_OBJECT_TYPE); 97 objectSetters[i] = MH.asType(MH.setter(LOOKUP, structure, fieldName, typeClass), Lookup.SET_OBJECT_TYPE); 98 } 99 100 if (!StructureLoader.isSingleFieldStructure(structure.getName())) { 101 for (int i = 0; i < fieldCount; i++) { 102 final String fieldNamePrimitive = getFieldName(i, PRIMITIVE_FIELD_TYPE); 103 final Class<?> typeClass = PRIMITIVE_FIELD_TYPE.getTypeClass(); 104 primitiveGetters[i] = MH.asType(MH.getter(LOOKUP, structure, fieldNamePrimitive, typeClass), Lookup.GET_PRIMITIVE_TYPE); 105 primitiveSetters[i] = MH.asType(MH.setter(LOOKUP, structure, fieldNamePrimitive, typeClass), Lookup.SET_PRIMITIVE_TYPE); 106 } 107 } 108 } 109 } 110 111 /** 112 * Property getter cache 113 * Note that we can't do the same simple caching for optimistic getters, 114 * due to the fact that they are bound to a program point, which will 115 * produce different boun method handles wrapping the same access mechanism 116 * depending on callsite 117 */ 118 private transient MethodHandle[] GETTER_CACHE = new MethodHandle[NOOF_TYPES]; 119 120 /** 121 * Create a new accessor property. Factory method used by nasgen generated code. 122 * 123 * @param key {@link Property} key. 124 * @param propertyFlags {@link Property} flags. 125 * @param getter {@link Property} get accessor method. 126 * @param setter {@link Property} set accessor method. 127 * 128 * @return New {@link AccessorProperty} created. 129 */ 130 public static AccessorProperty create(final Object key, final int propertyFlags, final MethodHandle getter, final MethodHandle setter) { 131 return new AccessorProperty(key, propertyFlags, -1, getter, setter); 132 } 133 134 /** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */ 135 transient MethodHandle primitiveGetter; 136 137 /** Seed setter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */ 138 transient MethodHandle primitiveSetter; 139 140 /** Seed getter for the Object version of this field */ 141 transient MethodHandle objectGetter; 142 143 /** Seed setter for the Object version of this field */ 144 transient MethodHandle objectSetter; 145 146 /** 147 * Delegate constructor for bound properties. This is used for properties created by 148 * {@link ScriptRuntime#mergeScope} and the Nashorn {@code Object.bindProperties} method. 149 * The former is used to add a script's defined globals to the current global scope while 150 * still storing them in a JO-prefixed ScriptObject class. 151 * 152 * <p>All properties created by this constructor have the {@link #IS_BOUND} flag set.</p> 153 * 154 * @param property accessor property to rebind 155 * @param delegate delegate object to rebind receiver to 156 */ 157 AccessorProperty(final AccessorProperty property, final Object delegate) { 158 super(property, property.getFlags() | IS_BOUND); 159 160 this.primitiveGetter = bindTo(property.primitiveGetter, delegate); 161 this.primitiveSetter = bindTo(property.primitiveSetter, delegate); 162 this.objectGetter = bindTo(property.objectGetter, delegate); 163 this.objectSetter = bindTo(property.objectSetter, delegate); 164 property.GETTER_CACHE = new MethodHandle[NOOF_TYPES]; 165 // Properties created this way are bound to a delegate 166 setType(property.getType()); 167 } 168 169 /** 170 * SPILL PROPERTY or USER ACCESSOR PROPERTY abstract constructor 171 * 172 * Constructor for spill properties. Array getters and setters will be created on demand. 173 * 174 * @param key the property key 175 * @param flags the property flags 176 * @param slot spill slot 177 * @param primitiveGetter primitive getter 178 * @param primitiveSetter primitive setter 179 * @param objectGetter object getter 180 * @param objectSetter object setter 181 */ 182 protected AccessorProperty( 183 final Object key, 184 final int flags, 185 final int slot, 186 final MethodHandle primitiveGetter, 187 final MethodHandle primitiveSetter, 188 final MethodHandle objectGetter, 189 final MethodHandle objectSetter) { 190 super(key, flags, slot); 191 assert getClass() != AccessorProperty.class; 192 this.primitiveGetter = primitiveGetter; 193 this.primitiveSetter = primitiveSetter; 194 this.objectGetter = objectGetter; 195 this.objectSetter = objectSetter; 196 initializeType(); 197 } 198 199 /** 200 * NASGEN constructor 201 * 202 * Constructor. Similar to the constructor with both primitive getters and setters, the difference 203 * here being that only one getter and setter (setter is optional for non writable fields) is given 204 * to the constructor, and the rest are created from those. Used e.g. by Nasgen classes 205 * 206 * @param key the property key 207 * @param flags the property flags 208 * @param slot the property field number or spill slot 209 * @param getter the property getter 210 * @param setter the property setter or null if non writable, non configurable 211 */ 212 private AccessorProperty(final Object key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) { 213 super(key, flags | IS_BUILTIN | DUAL_FIELDS | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot); 214 assert !isSpill(); 215 216 // we don't need to prep the setters these will never be invalidated as this is a nasgen 217 // or known type getter/setter. No invalidations will take place 218 219 final Class<?> getterType = getter.type().returnType(); 220 final Class<?> setterType = setter == null ? null : setter.type().parameterType(1); 221 222 assert setterType == null || setterType == getterType; 223 224 if (getterType == int.class) { 225 primitiveGetter = MH.asType(getter, Lookup.GET_PRIMITIVE_TYPE); 226 primitiveSetter = setter == null ? null : MH.asType(setter, Lookup.SET_PRIMITIVE_TYPE); 227 } else if (getterType == double.class) { 228 primitiveGetter = MH.asType(MH.filterReturnValue(getter, ObjectClassGenerator.PACK_DOUBLE), Lookup.GET_PRIMITIVE_TYPE); 229 primitiveSetter = setter == null ? null : MH.asType(MH.filterArguments(setter, 1, ObjectClassGenerator.UNPACK_DOUBLE), Lookup.SET_PRIMITIVE_TYPE); 230 } else { 231 primitiveGetter = primitiveSetter = null; 232 } 233 234 assert primitiveGetter == null || primitiveGetter.type() == Lookup.GET_PRIMITIVE_TYPE : primitiveGetter + "!=" + Lookup.GET_PRIMITIVE_TYPE; 235 assert primitiveSetter == null || primitiveSetter.type() == Lookup.SET_PRIMITIVE_TYPE : primitiveSetter; 236 237 objectGetter = getter.type() != Lookup.GET_OBJECT_TYPE ? MH.asType(getter, Lookup.GET_OBJECT_TYPE) : getter; 238 objectSetter = setter != null && setter.type() != Lookup.SET_OBJECT_TYPE ? MH.asType(setter, Lookup.SET_OBJECT_TYPE) : setter; 239 240 setType(getterType); 241 } 242 243 /** 244 * Normal ACCESS PROPERTY constructor given a structure class. 245 * Constructor for dual field AccessorPropertys. 246 * 247 * @param key property key 248 * @param flags property flags 249 * @param structure structure for objects associated with this property 250 * @param slot property field number or spill slot 251 */ 252 public AccessorProperty(final Object key, final int flags, final Class<?> structure, final int slot) { 253 super(key, flags, slot); 254 255 initGetterSetter(structure); 256 initializeType(); 257 } 258 259 private void initGetterSetter(final Class<?> structure) { 260 final int slot = getSlot(); 261 /* 262 * primitiveGetter and primitiveSetter are only used in dual fields mode. Setting them to null also 263 * works in dual field mode, it only means that the property never has a primitive 264 * representation. 265 */ 266 267 if (isParameter() && hasArguments()) { 268 //parameters are always stored in an object array, which may or may not be a good idea 269 final MethodHandle arguments = MH.getter(LOOKUP, structure, "arguments", ScriptObject.class); 270 objectGetter = MH.asType(MH.insertArguments(MH.filterArguments(ScriptObject.GET_ARGUMENT.methodHandle(), 0, arguments), 1, slot), Lookup.GET_OBJECT_TYPE); 271 objectSetter = MH.asType(MH.insertArguments(MH.filterArguments(ScriptObject.SET_ARGUMENT.methodHandle(), 0, arguments), 1, slot), Lookup.SET_OBJECT_TYPE); 272 primitiveGetter = null; 273 primitiveSetter = null; 274 } else { 275 final Accessors gs = GETTERS_SETTERS.get(structure); 276 objectGetter = gs.objectGetters[slot]; 277 primitiveGetter = gs.primitiveGetters[slot]; 278 objectSetter = gs.objectSetters[slot]; 279 primitiveSetter = gs.primitiveSetters[slot]; 280 } 281 282 // Always use dual fields except for single field structures 283 assert hasDualFields() != StructureLoader.isSingleFieldStructure(structure.getName()); 284 } 285 286 /** 287 * Constructor 288 * 289 * @param key key 290 * @param flags flags 291 * @param slot field slot index 292 * @param owner owner of property 293 * @param initialValue initial value to which the property can be set 294 */ 295 protected AccessorProperty(final Object key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) { 296 this(key, flags, owner.getClass(), slot); 297 setInitialValue(owner, initialValue); 298 } 299 300 /** 301 * Normal access property constructor that overrides the type 302 * Override the initial type. Used for Object Literals 303 * 304 * @param key key 305 * @param flags flags 306 * @param structure structure to JO subclass 307 * @param slot field slot index 308 * @param initialType initial type of the property 309 */ 310 public AccessorProperty(final Object key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) { 311 this(key, flags, structure, slot); 312 setType(hasDualFields() ? initialType : Object.class); 313 } 314 315 /** 316 * Copy constructor that may change type and in that case clear the cache. Important to do that before 317 * type change or getters will be created already stale. 318 * 319 * @param property property 320 * @param newType new type 321 */ 322 protected AccessorProperty(final AccessorProperty property, final Class<?> newType) { 323 super(property, property.getFlags()); 324 325 this.GETTER_CACHE = newType != property.getLocalType() ? new MethodHandle[NOOF_TYPES] : property.GETTER_CACHE; 326 this.primitiveGetter = property.primitiveGetter; 327 this.primitiveSetter = property.primitiveSetter; 328 this.objectGetter = property.objectGetter; 329 this.objectSetter = property.objectSetter; 330 331 setType(newType); 332 } 333 334 /** 335 * COPY constructor 336 * 337 * @param property source property 338 */ 339 protected AccessorProperty(final AccessorProperty property) { 340 this(property, property.getLocalType()); 341 } 342 343 /** 344 * Set initial value of a script object's property 345 * @param owner owner 346 * @param initialValue initial value 347 */ 348 protected final void setInitialValue(final ScriptObject owner, final Object initialValue) { 349 setType(hasDualFields() ? JSType.unboxedFieldType(initialValue) : Object.class); 350 if (initialValue instanceof Integer) { 351 invokeSetter(owner, ((Integer)initialValue).intValue()); 352 } else if (initialValue instanceof Double) { 353 invokeSetter(owner, ((Double)initialValue).doubleValue()); 354 } else { 355 invokeSetter(owner, initialValue); 356 } 357 } 358 359 /** 360 * Initialize the type of a property 361 */ 362 protected final void initializeType() { 363 setType(!hasDualFields() ? Object.class : null); 364 } 365 366 private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException { 367 s.defaultReadObject(); 368 // Restore getters array 369 GETTER_CACHE = new MethodHandle[NOOF_TYPES]; 370 } 371 372 private static MethodHandle bindTo(final MethodHandle mh, final Object receiver) { 373 if (mh == null) { 374 return null; 375 } 376 377 return MH.dropArguments(MH.bindTo(mh, receiver), 0, Object.class); 378 } 379 380 @Override 381 public Property copy() { 382 return new AccessorProperty(this); 383 } 384 385 @Override 386 public Property copy(final Class<?> newType) { 387 return new AccessorProperty(this, newType); 388 } 389 390 @Override 391 public int getIntValue(final ScriptObject self, final ScriptObject owner) { 392 try { 393 return (int)getGetter(int.class).invokeExact((Object)self); 394 } catch (final Error | RuntimeException e) { 395 throw e; 396 } catch (final Throwable e) { 397 throw new RuntimeException(e); 398 } 399 } 400 401 @Override 402 public double getDoubleValue(final ScriptObject self, final ScriptObject owner) { 403 try { 404 return (double)getGetter(double.class).invokeExact((Object)self); 405 } catch (final Error | RuntimeException e) { 406 throw e; 407 } catch (final Throwable e) { 408 throw new RuntimeException(e); 409 } 410 } 411 412 @Override 413 public Object getObjectValue(final ScriptObject self, final ScriptObject owner) { 414 try { 415 return getGetter(Object.class).invokeExact((Object)self); 416 } catch (final Error | RuntimeException e) { 417 throw e; 418 } catch (final Throwable e) { 419 throw new RuntimeException(e); 420 } 421 } 422 423 /** 424 * Invoke setter for this property with a value 425 * @param self owner 426 * @param value value 427 */ 428 protected final void invokeSetter(final ScriptObject self, final int value) { 429 try { 430 getSetter(int.class, self.getMap()).invokeExact((Object)self, value); 431 } catch (final Error | RuntimeException e) { 432 throw e; 433 } catch (final Throwable e) { 434 throw new RuntimeException(e); 435 } 436 } 437 438 /** 439 * Invoke setter for this property with a value 440 * @param self owner 441 * @param value value 442 */ 443 protected final void invokeSetter(final ScriptObject self, final double value) { 444 try { 445 getSetter(double.class, self.getMap()).invokeExact((Object)self, value); 446 } catch (final Error | RuntimeException e) { 447 throw e; 448 } catch (final Throwable e) { 449 throw new RuntimeException(e); 450 } 451 } 452 453 /** 454 * Invoke setter for this property with a value 455 * @param self owner 456 * @param value value 457 */ 458 protected final void invokeSetter(final ScriptObject self, final Object value) { 459 try { 460 getSetter(Object.class, self.getMap()).invokeExact((Object)self, value); 461 } catch (final Error | RuntimeException e) { 462 throw e; 463 } catch (final Throwable e) { 464 throw new RuntimeException(e); 465 } 466 } 467 468 @Override 469 public void setValue(final ScriptObject self, final ScriptObject owner, final int value, final boolean strict) { 470 assert isConfigurable() || isWritable() : getKey() + " is not writable or configurable"; 471 invokeSetter(self, value); 472 } 473 474 @Override 475 public void setValue(final ScriptObject self, final ScriptObject owner, final double value, final boolean strict) { 476 assert isConfigurable() || isWritable() : getKey() + " is not writable or configurable"; 477 invokeSetter(self, value); 478 } 479 480 @Override 481 public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) { 482 //this is sometimes used for bootstrapping, hence no assert. ugly. 483 invokeSetter(self, value); 484 } 485 486 @Override 487 void initMethodHandles(final Class<?> structure) { 488 // sanity check for structure class 489 if (!ScriptObject.class.isAssignableFrom(structure) || !StructureLoader.isStructureClass(structure.getName())) { 490 throw new IllegalArgumentException(); 491 } 492 // this method is overridden in SpillProperty 493 assert !isSpill(); 494 initGetterSetter(structure); 495 } 496 497 @Override 498 public MethodHandle getGetter(final Class<?> type) { 499 final int i = getAccessorTypeIndex(type); 500 501 assert type == int.class || 502 type == double.class || 503 type == Object.class : 504 "invalid getter type " + type + " for " + getKey(); 505 506 checkUndeclared(); 507 508 //all this does is add a return value filter for object fields only 509 final MethodHandle[] getterCache = GETTER_CACHE; 510 final MethodHandle cachedGetter = getterCache[i]; 511 final MethodHandle getter; 512 if (cachedGetter != null) { 513 getter = cachedGetter; 514 } else { 515 getter = debug( 516 createGetter( 517 getLocalType(), 518 type, 519 primitiveGetter, 520 objectGetter, 521 INVALID_PROGRAM_POINT), 522 getLocalType(), 523 type, 524 "get"); 525 getterCache[i] = getter; 526 } 527 assert getter.type().returnType() == type && getter.type().parameterType(0) == Object.class; 528 return getter; 529 } 530 531 @Override 532 public MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint) { 533 // nasgen generated primitive fields like Math.PI have only one known unchangeable primitive type 534 if (objectGetter == null) { 535 return getOptimisticPrimitiveGetter(type, programPoint); 536 } 537 538 checkUndeclared(); 539 540 return debug( 541 createGetter( 542 getLocalType(), 543 type, 544 primitiveGetter, 545 objectGetter, 546 programPoint), 547 getLocalType(), 548 type, 549 "get"); 550 } 551 552 private MethodHandle getOptimisticPrimitiveGetter(final Class<?> type, final int programPoint) { 553 final MethodHandle g = getGetter(getLocalType()); 554 return MH.asType(OptimisticReturnFilters.filterOptimisticReturnValue(g, type, programPoint), g.type().changeReturnType(type)); 555 } 556 557 private Property getWiderProperty(final Class<?> type) { 558 return copy(type); //invalidate cache of new property 559 } 560 561 private PropertyMap getWiderMap(final PropertyMap oldMap, final Property newProperty) { 562 final PropertyMap newMap = oldMap.replaceProperty(this, newProperty); 563 assert oldMap.size() > 0; 564 assert newMap.size() == oldMap.size(); 565 return newMap; 566 } 567 568 private void checkUndeclared() { 569 if ((getFlags() & NEEDS_DECLARATION) != 0) { 570 // a lexically defined variable that hasn't seen its declaration - throw ReferenceError 571 throw ECMAErrors.referenceError("not.defined", getKey().toString()); 572 } 573 } 574 575 // the final three arguments are for debug printout purposes only 576 @SuppressWarnings("unused") 577 private static Object replaceMap(final Object sobj, final PropertyMap newMap) { 578 ((ScriptObject)sobj).setMap(newMap); 579 return sobj; 580 } 581 582 @SuppressWarnings("unused") 583 private static Object invalidateSwitchPoint(final AccessorProperty property, final Object obj) { 584 if (!property.builtinSwitchPoint.hasBeenInvalidated()) { 585 SwitchPoint.invalidateAll(new SwitchPoint[] { property.builtinSwitchPoint }); 586 } 587 return obj; 588 } 589 590 private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) { 591 return debug(createSetter(forType, type, primitiveSetter, objectSetter), getLocalType(), type, "set"); 592 } 593 594 /** 595 * Is this property of the undefined type? 596 * @return true if undefined 597 */ 598 protected final boolean isUndefined() { 599 return getLocalType() == null; 600 } 601 602 @Override 603 public boolean hasNativeSetter() { 604 return objectSetter != null; 605 } 606 607 @Override 608 public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) { 609 checkUndeclared(); 610 611 final int typeIndex = getAccessorTypeIndex(type); 612 final int currentTypeIndex = getAccessorTypeIndex(getLocalType()); 613 614 //if we are asking for an object setter, but are still a primitive type, we might try to box it 615 MethodHandle mh; 616 if (needsInvalidator(typeIndex, currentTypeIndex)) { 617 final Property newProperty = getWiderProperty(type); 618 final PropertyMap newMap = getWiderMap(currentMap, newProperty); 619 620 final MethodHandle widerSetter = newProperty.getSetter(type, newMap); 621 final Class<?> ct = getLocalType(); 622 mh = MH.filterArguments(widerSetter, 0, MH.insertArguments(debugReplace(ct, type, currentMap, newMap) , 1, newMap)); 623 if (ct != null && ct.isPrimitive() && !type.isPrimitive()) { 624 mh = ObjectClassGenerator.createGuardBoxedPrimitiveSetter(ct, generateSetter(ct, ct), mh); 625 } 626 } else { 627 final Class<?> forType = isUndefined() ? type : getLocalType(); 628 mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type); 629 } 630 631 if (isBuiltin()) { 632 mh = MH.filterArguments(mh, 0, debugInvalidate(MH.insertArguments(INVALIDATE_SP, 0, this), getKey().toString())); 633 } 634 635 assert mh.type().returnType() == void.class : mh.type(); 636 637 return mh; 638 } 639 640 @Override 641 public final boolean canChangeType() { 642 if (!hasDualFields()) { 643 return false; 644 } 645 // Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST. 646 return getLocalType() == null || (getLocalType() != Object.class && (isConfigurable() || isWritable())); 647 } 648 649 private boolean needsInvalidator(final int typeIndex, final int currentTypeIndex) { 650 return canChangeType() && typeIndex > currentTypeIndex; 651 } 652 653 private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) { 654 if (!Context.DEBUG || !Global.hasInstance()) { 655 return mh; 656 } 657 658 final Context context = Context.getContextTrusted(); 659 assert context != null; 660 661 return context.addLoggingToHandle( 662 ObjectClassGenerator.class, 663 Level.INFO, 664 mh, 665 0, 666 true, 667 new Supplier<String>() { 668 @Override 669 public String get() { 670 return tag + " '" + getKey() + "' (property="+ Debug.id(this) + ", slot=" + getSlot() + " " + getClass().getSimpleName() + " forType=" + stripName(forType) + ", type=" + stripName(type) + ')'; 671 } 672 }); 673 } 674 675 private MethodHandle debugReplace(final Class<?> oldType, final Class<?> newType, final PropertyMap oldMap, final PropertyMap newMap) { 676 if (!Context.DEBUG || !Global.hasInstance()) { 677 return REPLACE_MAP; 678 } 679 680 final Context context = Context.getContextTrusted(); 681 assert context != null; 682 683 MethodHandle mh = context.addLoggingToHandle( 684 ObjectClassGenerator.class, 685 REPLACE_MAP, 686 new Supplier<String>() { 687 @Override 688 public String get() { 689 return "Type change for '" + getKey() + "' " + oldType + "=>" + newType; 690 } 691 }); 692 693 mh = context.addLoggingToHandle( 694 ObjectClassGenerator.class, 695 Level.FINEST, 696 mh, 697 Integer.MAX_VALUE, 698 false, 699 new Supplier<String>() { 700 @Override 701 public String get() { 702 return "Setting map " + Debug.id(oldMap) + " => " + Debug.id(newMap) + " " + oldMap + " => " + newMap; 703 } 704 }); 705 return mh; 706 } 707 708 private static MethodHandle debugInvalidate(final MethodHandle invalidator, final String key) { 709 if (!Context.DEBUG || !Global.hasInstance()) { 710 return invalidator; 711 } 712 713 final Context context = Context.getContextTrusted(); 714 assert context != null; 715 716 return context.addLoggingToHandle( 717 ObjectClassGenerator.class, 718 invalidator, 719 new Supplier<String>() { 720 @Override 721 public String get() { 722 return "Field change callback for " + key + " triggered "; 723 } 724 }); 725 } 726 727 private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) { 728 return MH.findStatic(LOOKUP, AccessorProperty.class, name, MH.type(rtype, types)); 729 } 730 }