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 */ Accessors(final Class<?> structure)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 */ create(final String key, final int propertyFlags, final MethodHandle getter, final MethodHandle setter)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 */ AccessorProperty(final AccessorProperty property, final Object delegate)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 */ AccessorProperty( final String key, final int flags, final int slot, final MethodHandle primitiveGetter, final MethodHandle primitiveSetter, final MethodHandle objectGetter, final MethodHandle objectSetter)182 protected AccessorProperty( 183 final String 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 */ AccessorProperty(final String key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter)212 private AccessorProperty(final String 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 */ AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot)252 public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot) { 253 super(key, flags, slot); 254 255 initGetterSetter(structure); 256 initializeType(); 257 } 258 initGetterSetter(final Class<?> structure)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 */ AccessorProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue)295 protected AccessorProperty(final String 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 */ AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType)310 public AccessorProperty(final String 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 */ AccessorProperty(final AccessorProperty property, final Class<?> newType)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 */ AccessorProperty(final AccessorProperty property)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 */ setInitialValue(final ScriptObject owner, final Object initialValue)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 */ initializeType()362 protected final void initializeType() { 363 setType(!hasDualFields() ? Object.class : null); 364 } 365 readObject(final ObjectInputStream s)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 bindTo(final MethodHandle mh, final Object receiver)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 copy()381 public Property copy() { 382 return new AccessorProperty(this); 383 } 384 385 @Override copy(final Class<?> newType)386 public Property copy(final Class<?> newType) { 387 return new AccessorProperty(this, newType); 388 } 389 390 @Override getIntValue(final ScriptObject self, final ScriptObject owner)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 getDoubleValue(final ScriptObject self, final ScriptObject owner)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 getObjectValue(final ScriptObject self, final ScriptObject owner)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 */ invokeSetter(final ScriptObject self, final int value)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 */ invokeSetter(final ScriptObject self, final double value)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 */ invokeSetter(final ScriptObject self, final Object value)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 setValue(final ScriptObject self, final ScriptObject owner, final int value, final boolean strict)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 setValue(final ScriptObject self, final ScriptObject owner, final double value, final boolean strict)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 setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict)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 initMethodHandles(final Class<?> structure)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 getGetter(final Class<?> type)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 getOptimisticGetter(final Class<?> type, final int programPoint)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 getOptimisticPrimitiveGetter(final Class<?> type, final int programPoint)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 getWiderProperty(final Class<?> type)557 private Property getWiderProperty(final Class<?> type) { 558 return copy(type); //invalidate cache of new property 559 } 560 getWiderMap(final PropertyMap oldMap, final Property newProperty)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 checkUndeclared()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()); 572 } 573 } 574 575 // the final three arguments are for debug printout purposes only 576 @SuppressWarnings("unused") replaceMap(final Object sobj, final PropertyMap newMap)577 private static Object replaceMap(final Object sobj, final PropertyMap newMap) { 578 ((ScriptObject)sobj).setMap(newMap); 579 return sobj; 580 } 581 582 @SuppressWarnings("unused") invalidateSwitchPoint(final AccessorProperty property, final Object obj)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 generateSetter(final Class<?> forType, final Class<?> type)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 */ isUndefined()598 protected final boolean isUndefined() { 599 return getLocalType() == null; 600 } 601 602 @Override getSetter(final Class<?> type, final PropertyMap currentMap)603 public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) { 604 checkUndeclared(); 605 606 final int typeIndex = getAccessorTypeIndex(type); 607 final int currentTypeIndex = getAccessorTypeIndex(getLocalType()); 608 609 //if we are asking for an object setter, but are still a primitive type, we might try to box it 610 MethodHandle mh; 611 if (needsInvalidator(typeIndex, currentTypeIndex)) { 612 final Property newProperty = getWiderProperty(type); 613 final PropertyMap newMap = getWiderMap(currentMap, newProperty); 614 615 final MethodHandle widerSetter = newProperty.getSetter(type, newMap); 616 final Class<?> ct = getLocalType(); 617 mh = MH.filterArguments(widerSetter, 0, MH.insertArguments(debugReplace(ct, type, currentMap, newMap) , 1, newMap)); 618 if (ct != null && ct.isPrimitive() && !type.isPrimitive()) { 619 mh = ObjectClassGenerator.createGuardBoxedPrimitiveSetter(ct, generateSetter(ct, ct), mh); 620 } 621 } else { 622 final Class<?> forType = isUndefined() ? type : getLocalType(); 623 mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type); 624 } 625 626 if (isBuiltin()) { 627 mh = MH.filterArguments(mh, 0, debugInvalidate(MH.insertArguments(INVALIDATE_SP, 0, this), getKey())); 628 } 629 630 assert mh.type().returnType() == void.class : mh.type(); 631 632 return mh; 633 } 634 635 @Override canChangeType()636 public final boolean canChangeType() { 637 if (!hasDualFields()) { 638 return false; 639 } 640 // Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST. 641 return getLocalType() == null || (getLocalType() != Object.class && (isConfigurable() || isWritable())); 642 } 643 needsInvalidator(final int typeIndex, final int currentTypeIndex)644 private boolean needsInvalidator(final int typeIndex, final int currentTypeIndex) { 645 return canChangeType() && typeIndex > currentTypeIndex; 646 } 647 debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag)648 private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) { 649 if (!Context.DEBUG || !Global.hasInstance()) { 650 return mh; 651 } 652 653 final Context context = Context.getContextTrusted(); 654 assert context != null; 655 656 return context.addLoggingToHandle( 657 ObjectClassGenerator.class, 658 Level.INFO, 659 mh, 660 0, 661 true, 662 new Supplier<String>() { 663 @Override 664 public String get() { 665 return tag + " '" + getKey() + "' (property="+ Debug.id(this) + ", slot=" + getSlot() + " " + getClass().getSimpleName() + " forType=" + stripName(forType) + ", type=" + stripName(type) + ')'; 666 } 667 }); 668 } 669 670 private MethodHandle debugReplace(final Class<?> oldType, final Class<?> newType, final PropertyMap oldMap, final PropertyMap newMap) { 671 if (!Context.DEBUG || !Global.hasInstance()) { 672 return REPLACE_MAP; 673 } 674 675 final Context context = Context.getContextTrusted(); 676 assert context != null; 677 678 MethodHandle mh = context.addLoggingToHandle( 679 ObjectClassGenerator.class, 680 REPLACE_MAP, 681 new Supplier<String>() { 682 @Override 683 public String get() { 684 return "Type change for '" + getKey() + "' " + oldType + "=>" + newType; 685 } 686 }); 687 688 mh = context.addLoggingToHandle( 689 ObjectClassGenerator.class, 690 Level.FINEST, 691 mh, 692 Integer.MAX_VALUE, 693 false, 694 new Supplier<String>() { 695 @Override 696 public String get() { 697 return "Setting map " + Debug.id(oldMap) + " => " + Debug.id(newMap) + " " + oldMap + " => " + newMap; 698 } 699 }); 700 return mh; 701 } 702 703 private static MethodHandle debugInvalidate(final MethodHandle invalidator, final String key) { 704 if (!Context.DEBUG || !Global.hasInstance()) { 705 return invalidator; 706 } 707 708 final Context context = Context.getContextTrusted(); 709 assert context != null; 710 711 return context.addLoggingToHandle( 712 ObjectClassGenerator.class, 713 invalidator, 714 new Supplier<String>() { 715 @Override 716 public String get() { 717 return "Field change callback for " + key + " triggered "; 718 } 719 }); 720 } 721 722 private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) { 723 return MH.findStatic(LOOKUP, AccessorProperty.class, name, MH.type(rtype, types)); 724 } 725 } 726