AccessorProperty.java revision 1571:fd97b9047199
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.runtime; 27 28import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE; 29import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createGetter; 30import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createSetter; 31import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getFieldCount; 32import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getFieldName; 33import static jdk.nashorn.internal.lookup.Lookup.MH; 34import static jdk.nashorn.internal.lookup.MethodHandleFactory.stripName; 35import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex; 36import static jdk.nashorn.internal.runtime.JSType.getNumberOfAccessorTypes; 37import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 38import java.io.IOException; 39import java.io.ObjectInputStream; 40import java.lang.invoke.MethodHandle; 41import java.lang.invoke.MethodHandles; 42import java.lang.invoke.SwitchPoint; 43import java.util.function.Supplier; 44import java.util.logging.Level; 45import jdk.nashorn.internal.codegen.ObjectClassGenerator; 46import jdk.nashorn.internal.codegen.types.Type; 47import jdk.nashorn.internal.lookup.Lookup; 48import 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 */ 54public 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 String 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 Long) { 353 invokeSetter(owner, ((Long)initialValue).longValue()); 354 } else if (initialValue instanceof Double) { 355 invokeSetter(owner, ((Double)initialValue).doubleValue()); 356 } else { 357 invokeSetter(owner, initialValue); 358 } 359 } 360 361 /** 362 * Initialize the type of a property 363 */ 364 protected final void initializeType() { 365 setType(!hasDualFields() ? Object.class : null); 366 } 367 368 private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException { 369 s.defaultReadObject(); 370 // Restore getters array 371 GETTER_CACHE = new MethodHandle[NOOF_TYPES]; 372 } 373 374 private static MethodHandle bindTo(final MethodHandle mh, final Object receiver) { 375 if (mh == null) { 376 return null; 377 } 378 379 return MH.dropArguments(MH.bindTo(mh, receiver), 0, Object.class); 380 } 381 382 @Override 383 public Property copy() { 384 return new AccessorProperty(this); 385 } 386 387 @Override 388 public Property copy(final Class<?> newType) { 389 return new AccessorProperty(this, newType); 390 } 391 392 @Override 393 public int getIntValue(final ScriptObject self, final ScriptObject owner) { 394 try { 395 return (int)getGetter(int.class).invokeExact((Object)self); 396 } catch (final Error | RuntimeException e) { 397 throw e; 398 } catch (final Throwable e) { 399 throw new RuntimeException(e); 400 } 401 } 402 403 @Override 404 public double getDoubleValue(final ScriptObject self, final ScriptObject owner) { 405 try { 406 return (double)getGetter(double.class).invokeExact((Object)self); 407 } catch (final Error | RuntimeException e) { 408 throw e; 409 } catch (final Throwable e) { 410 throw new RuntimeException(e); 411 } 412 } 413 414 @Override 415 public Object getObjectValue(final ScriptObject self, final ScriptObject owner) { 416 try { 417 return getGetter(Object.class).invokeExact((Object)self); 418 } catch (final Error | RuntimeException e) { 419 throw e; 420 } catch (final Throwable e) { 421 throw new RuntimeException(e); 422 } 423 } 424 425 /** 426 * Invoke setter for this property with a value 427 * @param self owner 428 * @param value value 429 */ 430 protected final void invokeSetter(final ScriptObject self, final int value) { 431 try { 432 getSetter(int.class, self.getMap()).invokeExact((Object)self, value); 433 } catch (final Error | RuntimeException e) { 434 throw e; 435 } catch (final Throwable e) { 436 throw new RuntimeException(e); 437 } 438 } 439 440 /** 441 * Invoke setter for this property with a value 442 * @param self owner 443 * @param value value 444 */ 445 protected final void invokeSetter(final ScriptObject self, final double value) { 446 try { 447 getSetter(double.class, self.getMap()).invokeExact((Object)self, value); 448 } catch (final Error | RuntimeException e) { 449 throw e; 450 } catch (final Throwable e) { 451 throw new RuntimeException(e); 452 } 453 } 454 455 /** 456 * Invoke setter for this property with a value 457 * @param self owner 458 * @param value value 459 */ 460 protected final void invokeSetter(final ScriptObject self, final Object value) { 461 try { 462 getSetter(Object.class, self.getMap()).invokeExact((Object)self, value); 463 } catch (final Error | RuntimeException e) { 464 throw e; 465 } catch (final Throwable e) { 466 throw new RuntimeException(e); 467 } 468 } 469 470 @Override 471 public void setValue(final ScriptObject self, final ScriptObject owner, final int value, final boolean strict) { 472 assert isConfigurable() || isWritable() : getKey() + " is not writable or configurable"; 473 invokeSetter(self, value); 474 } 475 476 @Override 477 public void setValue(final ScriptObject self, final ScriptObject owner, final double value, final boolean strict) { 478 assert isConfigurable() || isWritable() : getKey() + " is not writable or configurable"; 479 invokeSetter(self, value); 480 } 481 482 @Override 483 public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) { 484 //this is sometimes used for bootstrapping, hence no assert. ugly. 485 invokeSetter(self, value); 486 } 487 488 @Override 489 void initMethodHandles(final Class<?> structure) { 490 // sanity check for structure class 491 if (!ScriptObject.class.isAssignableFrom(structure) || !StructureLoader.isStructureClass(structure.getName())) { 492 throw new IllegalArgumentException(); 493 } 494 // this method is overridden in SpillProperty 495 assert !isSpill(); 496 initGetterSetter(structure); 497 } 498 499 @Override 500 public MethodHandle getGetter(final Class<?> type) { 501 final int i = getAccessorTypeIndex(type); 502 503 assert type == int.class || 504 type == double.class || 505 type == Object.class : 506 "invalid getter type " + type + " for " + getKey(); 507 508 checkUndeclared(); 509 510 //all this does is add a return value filter for object fields only 511 final MethodHandle[] getterCache = GETTER_CACHE; 512 final MethodHandle cachedGetter = getterCache[i]; 513 final MethodHandle getter; 514 if (cachedGetter != null) { 515 getter = cachedGetter; 516 } else { 517 getter = debug( 518 createGetter( 519 getLocalType(), 520 type, 521 primitiveGetter, 522 objectGetter, 523 INVALID_PROGRAM_POINT), 524 getLocalType(), 525 type, 526 "get"); 527 getterCache[i] = getter; 528 } 529 assert getter.type().returnType() == type && getter.type().parameterType(0) == Object.class; 530 return getter; 531 } 532 533 @Override 534 public MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint) { 535 // nasgen generated primitive fields like Math.PI have only one known unchangeable primitive type 536 if (objectGetter == null) { 537 return getOptimisticPrimitiveGetter(type, programPoint); 538 } 539 540 checkUndeclared(); 541 542 return debug( 543 createGetter( 544 getLocalType(), 545 type, 546 primitiveGetter, 547 objectGetter, 548 programPoint), 549 getLocalType(), 550 type, 551 "get"); 552 } 553 554 private MethodHandle getOptimisticPrimitiveGetter(final Class<?> type, final int programPoint) { 555 final MethodHandle g = getGetter(getLocalType()); 556 return MH.asType(OptimisticReturnFilters.filterOptimisticReturnValue(g, type, programPoint), g.type().changeReturnType(type)); 557 } 558 559 private Property getWiderProperty(final Class<?> type) { 560 return copy(type); //invalidate cache of new property 561 } 562 563 private PropertyMap getWiderMap(final PropertyMap oldMap, final Property newProperty) { 564 final PropertyMap newMap = oldMap.replaceProperty(this, newProperty); 565 assert oldMap.size() > 0; 566 assert newMap.size() == oldMap.size(); 567 return newMap; 568 } 569 570 private void checkUndeclared() { 571 if ((getFlags() & NEEDS_DECLARATION) != 0) { 572 // a lexically defined variable that hasn't seen its declaration - throw ReferenceError 573 throw ECMAErrors.referenceError("not.defined", getKey().toString()); 574 } 575 } 576 577 // the final three arguments are for debug printout purposes only 578 @SuppressWarnings("unused") 579 private static Object replaceMap(final Object sobj, final PropertyMap newMap) { 580 ((ScriptObject)sobj).setMap(newMap); 581 return sobj; 582 } 583 584 @SuppressWarnings("unused") 585 private static Object invalidateSwitchPoint(final AccessorProperty property, final Object obj) { 586 if (!property.builtinSwitchPoint.hasBeenInvalidated()) { 587 SwitchPoint.invalidateAll(new SwitchPoint[] { property.builtinSwitchPoint }); 588 } 589 return obj; 590 } 591 592 private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) { 593 return debug(createSetter(forType, type, primitiveSetter, objectSetter), getLocalType(), type, "set"); 594 } 595 596 /** 597 * Is this property of the undefined type? 598 * @return true if undefined 599 */ 600 protected final boolean isUndefined() { 601 return getLocalType() == null; 602 } 603 604 @Override 605 public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) { 606 checkUndeclared(); 607 608 final int typeIndex = getAccessorTypeIndex(type); 609 final int currentTypeIndex = getAccessorTypeIndex(getLocalType()); 610 611 //if we are asking for an object setter, but are still a primitive type, we might try to box it 612 MethodHandle mh; 613 if (needsInvalidator(typeIndex, currentTypeIndex)) { 614 final Property newProperty = getWiderProperty(type); 615 final PropertyMap newMap = getWiderMap(currentMap, newProperty); 616 617 final MethodHandle widerSetter = newProperty.getSetter(type, newMap); 618 final Class<?> ct = getLocalType(); 619 mh = MH.filterArguments(widerSetter, 0, MH.insertArguments(debugReplace(ct, type, currentMap, newMap) , 1, newMap)); 620 if (ct != null && ct.isPrimitive() && !type.isPrimitive()) { 621 mh = ObjectClassGenerator.createGuardBoxedPrimitiveSetter(ct, generateSetter(ct, ct), mh); 622 } 623 } else { 624 final Class<?> forType = isUndefined() ? type : getLocalType(); 625 mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type); 626 } 627 628 if (isBuiltin()) { 629 mh = MH.filterArguments(mh, 0, debugInvalidate(MH.insertArguments(INVALIDATE_SP, 0, this), getKey().toString())); 630 } 631 632 assert mh.type().returnType() == void.class : mh.type(); 633 634 return mh; 635 } 636 637 @Override 638 public final boolean canChangeType() { 639 if (!hasDualFields()) { 640 return false; 641 } 642 // Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST. 643 return getLocalType() == null || (getLocalType() != Object.class && (isConfigurable() || isWritable())); 644 } 645 646 private boolean needsInvalidator(final int typeIndex, final int currentTypeIndex) { 647 return canChangeType() && typeIndex > currentTypeIndex; 648 } 649 650 private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) { 651 if (!Context.DEBUG || !Global.hasInstance()) { 652 return mh; 653 } 654 655 final Context context = Context.getContextTrusted(); 656 assert context != null; 657 658 return context.addLoggingToHandle( 659 ObjectClassGenerator.class, 660 Level.INFO, 661 mh, 662 0, 663 true, 664 new Supplier<String>() { 665 @Override 666 public String get() { 667 return tag + " '" + getKey() + "' (property="+ Debug.id(this) + ", slot=" + getSlot() + " " + getClass().getSimpleName() + " forType=" + stripName(forType) + ", type=" + stripName(type) + ')'; 668 } 669 }); 670 } 671 672 private MethodHandle debugReplace(final Class<?> oldType, final Class<?> newType, final PropertyMap oldMap, final PropertyMap newMap) { 673 if (!Context.DEBUG || !Global.hasInstance()) { 674 return REPLACE_MAP; 675 } 676 677 final Context context = Context.getContextTrusted(); 678 assert context != null; 679 680 MethodHandle mh = context.addLoggingToHandle( 681 ObjectClassGenerator.class, 682 REPLACE_MAP, 683 new Supplier<String>() { 684 @Override 685 public String get() { 686 return "Type change for '" + getKey() + "' " + oldType + "=>" + newType; 687 } 688 }); 689 690 mh = context.addLoggingToHandle( 691 ObjectClassGenerator.class, 692 Level.FINEST, 693 mh, 694 Integer.MAX_VALUE, 695 false, 696 new Supplier<String>() { 697 @Override 698 public String get() { 699 return "Setting map " + Debug.id(oldMap) + " => " + Debug.id(newMap) + " " + oldMap + " => " + newMap; 700 } 701 }); 702 return mh; 703 } 704 705 private static MethodHandle debugInvalidate(final MethodHandle invalidator, final String key) { 706 if (!Context.DEBUG || !Global.hasInstance()) { 707 return invalidator; 708 } 709 710 final Context context = Context.getContextTrusted(); 711 assert context != null; 712 713 return context.addLoggingToHandle( 714 ObjectClassGenerator.class, 715 invalidator, 716 new Supplier<String>() { 717 @Override 718 public String get() { 719 return "Field change callback for " + key + " triggered "; 720 } 721 }); 722 } 723 724 private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) { 725 return MH.findStatic(LOOKUP, AccessorProperty.class, name, MH.type(rtype, types)); 726 } 727} 728