1 /* 2 * Copyright (c) 2010, 2017, 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.CompilerConstants.staticCallNoLookup; 29 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall; 30 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 31 import static jdk.nashorn.internal.lookup.Lookup.MH; 32 import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError; 33 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 34 import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_DOUBLE; 35 import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_INT; 36 import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE; 37 import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE; 38 import static jdk.nashorn.internal.runtime.PropertyDescriptor.GET; 39 import static jdk.nashorn.internal.runtime.PropertyDescriptor.SET; 40 import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE; 41 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE; 42 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 43 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 44 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; 45 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex; 46 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; 47 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isScopeFlag; 48 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isStrictFlag; 49 import static jdk.nashorn.internal.runtime.linker.NashornGuards.explicitInstanceOfCheck; 50 51 import java.lang.invoke.MethodHandle; 52 import java.lang.invoke.MethodHandles; 53 import java.lang.invoke.MethodType; 54 import java.lang.invoke.SwitchPoint; 55 import java.lang.reflect.Array; 56 import java.util.AbstractMap; 57 import java.util.ArrayList; 58 import java.util.Arrays; 59 import java.util.Collection; 60 import java.util.Collections; 61 import java.util.HashSet; 62 import java.util.Iterator; 63 import java.util.LinkedHashSet; 64 import java.util.List; 65 import java.util.Map; 66 import java.util.Set; 67 import java.util.concurrent.atomic.LongAdder; 68 import jdk.dynalink.CallSiteDescriptor; 69 import jdk.dynalink.NamedOperation; 70 import jdk.dynalink.linker.GuardedInvocation; 71 import jdk.dynalink.linker.LinkRequest; 72 import jdk.nashorn.internal.codegen.CompilerConstants.Call; 73 import jdk.nashorn.internal.codegen.ObjectClassGenerator; 74 import jdk.nashorn.internal.codegen.types.Type; 75 import jdk.nashorn.internal.lookup.Lookup; 76 import jdk.nashorn.internal.objects.AccessorPropertyDescriptor; 77 import jdk.nashorn.internal.objects.DataPropertyDescriptor; 78 import jdk.nashorn.internal.objects.Global; 79 import jdk.nashorn.internal.objects.NativeArray; 80 import jdk.nashorn.internal.runtime.arrays.ArrayData; 81 import jdk.nashorn.internal.runtime.arrays.ArrayIndex; 82 import jdk.nashorn.internal.runtime.linker.LinkerCallSite; 83 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 84 import jdk.nashorn.internal.runtime.linker.NashornGuards; 85 86 /** 87 * Base class for generic JavaScript objects. 88 * <p> 89 * Notes: 90 * <ul> 91 * <li>The map is used to identify properties in the object.</li> 92 * <li>If the map is modified then it must be cloned and replaced. This notifies 93 * any code that made assumptions about the object that things have changed. 94 * Ex. CallSites that have been validated must check to see if the map has 95 * changed (or a map from a different object type) and hence relink the method 96 * to call.</li> 97 * <li>Modifications of the map include adding/deleting attributes or changing a 98 * function field value.</li> 99 * </ul> 100 */ 101 102 public abstract class ScriptObject implements PropertyAccess, Cloneable { 103 /** __proto__ special property name inside object literals. ES6 draft. */ 104 public static final String PROTO_PROPERTY_NAME = "__proto__"; 105 106 /** Search fall back routine name for "no such method" */ 107 public static final String NO_SUCH_METHOD_NAME = "__noSuchMethod__"; 108 109 /** Search fall back routine name for "no such property" */ 110 public static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__"; 111 112 /** Per ScriptObject flag - is this an array object? */ 113 public static final int IS_ARRAY = 1 << 0; 114 115 /** Per ScriptObject flag - is this an arguments object? */ 116 public static final int IS_ARGUMENTS = 1 << 1; 117 118 /** Is length property not-writable? */ 119 public static final int IS_LENGTH_NOT_WRITABLE = 1 << 2; 120 121 /** Is this a builtin object? */ 122 public static final int IS_BUILTIN = 1 << 3; 123 124 /** Is this an internal object that should not be visible to scripts? */ 125 public static final int IS_INTERNAL = 1 << 4; 126 127 /** 128 * Spill growth rate - by how many elements does {@link ScriptObject#primitiveSpill} and 129 * {@link ScriptObject#objectSpill} when full 130 */ 131 public static final int SPILL_RATE = 8; 132 133 /** Map to property information and accessor functions. Ordered by insertion. */ 134 private PropertyMap map; 135 136 /** objects proto. */ 137 private ScriptObject proto; 138 139 /** Object flags. */ 140 private int flags; 141 142 /** Area for primitive properties added to object after instantiation, see {@link AccessorProperty} */ 143 protected long[] primitiveSpill; 144 145 /** Area for reference properties added to object after instantiation, see {@link AccessorProperty} */ 146 protected Object[] objectSpill; 147 148 /** Indexed array data. */ 149 private ArrayData arrayData; 150 151 /** Method handle to retrieve prototype of this object */ 152 public static final MethodHandle GETPROTO = findOwnMH_V("getProto", ScriptObject.class); 153 154 static final MethodHandle MEGAMORPHIC_GET = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class, boolean.class); 155 static final MethodHandle GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class); 156 static final MethodHandle DECLARE_AND_SET = findOwnMH_V("declareAndSet", void.class, String.class, Object.class); 157 158 private static final MethodHandle TRUNCATINGFILTER = findOwnMH_S("truncatingFilter", Object[].class, int.class, Object[].class); 159 private static final MethodHandle KNOWNFUNCPROPGUARDSELF = findOwnMH_S("knownFunctionPropertyGuardSelf", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, ScriptFunction.class); 160 private static final MethodHandle KNOWNFUNCPROPGUARDPROTO = findOwnMH_S("knownFunctionPropertyGuardProto", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, int.class, ScriptFunction.class); 161 162 private static final ArrayList<MethodHandle> PROTO_FILTERS = new ArrayList<>(); 163 164 /** Method handle for getting the array data */ 165 public static final Call GET_ARRAY = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArray", ArrayData.class); 166 167 /** Method handle for getting a function argument at a given index. Used from MapCreator */ 168 public static final Call GET_ARGUMENT = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class); 169 170 /** Method handle for setting a function argument at a given index. Used from MapCreator */ 171 public static final Call SET_ARGUMENT = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArgument", void.class, int.class, Object.class); 172 173 /** Method handle for getting the proto of a ScriptObject */ 174 public static final Call GET_PROTO = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class); 175 176 /** Method handle for getting the proto of a ScriptObject */ 177 public static final Call GET_PROTO_DEPTH = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class, int.class); 178 179 /** Method handle for setting the proto of a ScriptObject */ 180 public static final Call SET_GLOBAL_OBJECT_PROTO = staticCallNoLookup(ScriptObject.class, "setGlobalObjectProto", void.class, ScriptObject.class); 181 182 /** Method handle for setting the proto of a ScriptObject after checking argument */ 183 public static final Call SET_PROTO_FROM_LITERAL = virtualCallNoLookup(ScriptObject.class, "setProtoFromLiteral", void.class, Object.class); 184 185 /** Method handle for setting the user accessors of a ScriptObject */ 186 //TODO fastpath this 187 public static final Call SET_USER_ACCESSORS = virtualCallNoLookup(ScriptObject.class, "setUserAccessors", void.class, Object.class, ScriptFunction.class, ScriptFunction.class); 188 189 /** Method handle for generic property setter */ 190 public static final Call GENERIC_SET = virtualCallNoLookup(ScriptObject.class, "set", void.class, Object.class, Object.class, int.class); 191 192 public static final Call DELETE = virtualCall(MethodHandles.lookup(), ScriptObject.class, "delete", boolean.class, Object.class, boolean.class); 193 194 static final MethodHandle[] SET_SLOW = new MethodHandle[] { 195 findOwnMH_V("set", void.class, Object.class, int.class, int.class), 196 findOwnMH_V("set", void.class, Object.class, double.class, int.class), 197 findOwnMH_V("set", void.class, Object.class, Object.class, int.class) 198 }; 199 200 /** Method handle to reset the map of this ScriptObject */ 201 public static final Call SET_MAP = virtualCallNoLookup(ScriptObject.class, "setMap", void.class, PropertyMap.class); 202 203 static final MethodHandle CAS_MAP = findOwnMH_V("compareAndSetMap", boolean.class, PropertyMap.class, PropertyMap.class); 204 static final MethodHandle EXTENSION_CHECK = findOwnMH_V("extensionCheck", boolean.class, boolean.class, String.class); 205 static final MethodHandle ENSURE_SPILL_SIZE = findOwnMH_V("ensureSpillSize", Object.class, int.class); 206 207 private static final GuardedInvocation DELETE_GUARDED = new GuardedInvocation(MH.insertArguments(DELETE.methodHandle(), 2, false), NashornGuards.getScriptObjectGuard()); 208 private static final GuardedInvocation DELETE_GUARDED_STRICT = new GuardedInvocation(MH.insertArguments(DELETE.methodHandle(), 2, true), NashornGuards.getScriptObjectGuard()); 209 210 /** 211 * Constructor 212 */ ScriptObject()213 public ScriptObject() { 214 this(null); 215 } 216 217 /** 218 * Constructor 219 * 220 * @param map {@link PropertyMap} used to create the initial object 221 */ ScriptObject(final PropertyMap map)222 public ScriptObject(final PropertyMap map) { 223 if (Context.DEBUG) { 224 ScriptObject.count.increment(); 225 } 226 this.arrayData = ArrayData.EMPTY_ARRAY; 227 this.setMap(map == null ? PropertyMap.newMap() : map); 228 } 229 230 /** 231 * Constructor that directly sets the prototype to {@code proto} and property map to 232 * {@code map} without invalidating the map as calling {@link #setProto(ScriptObject)} 233 * would do. This should only be used for objects that are always constructed with the 234 * same combination of prototype and property map. 235 * 236 * @param proto the prototype object 237 * @param map initial {@link PropertyMap} 238 */ ScriptObject(final ScriptObject proto, final PropertyMap map)239 protected ScriptObject(final ScriptObject proto, final PropertyMap map) { 240 this(map); 241 this.proto = proto; 242 } 243 244 /** 245 * Constructor used to instantiate spill properties directly. Used from 246 * SpillObjectCreator. 247 * 248 * @param map property maps 249 * @param primitiveSpill primitive spills 250 * @param objectSpill reference spills 251 */ ScriptObject(final PropertyMap map, final long[] primitiveSpill, final Object[] objectSpill)252 public ScriptObject(final PropertyMap map, final long[] primitiveSpill, final Object[] objectSpill) { 253 this(map); 254 this.primitiveSpill = primitiveSpill; 255 this.objectSpill = objectSpill; 256 assert primitiveSpill == null || primitiveSpill.length == objectSpill.length : " primitive spill pool size is not the same length as object spill pool size"; 257 } 258 259 /** 260 * Check whether this is a global object 261 * @return true if global 262 */ isGlobal()263 protected boolean isGlobal() { 264 return false; 265 } 266 alignUp(final int size, final int alignment)267 private static int alignUp(final int size, final int alignment) { 268 return size + alignment - 1 & ~(alignment - 1); 269 } 270 271 /** 272 * Given a number of properties, return the aligned to SPILL_RATE 273 * buffer size required for the smallest spill pool needed to 274 * house them 275 * @param nProperties number of properties 276 * @return property buffer length, a multiple of SPILL_RATE 277 */ spillAllocationLength(final int nProperties)278 public static int spillAllocationLength(final int nProperties) { 279 return alignUp(nProperties, SPILL_RATE); 280 } 281 282 /** 283 * Copy all properties from the source object with their receiver bound to the source. 284 * This function was known as mergeMap 285 * 286 * @param source The source object to copy from. 287 */ addBoundProperties(final ScriptObject source)288 public void addBoundProperties(final ScriptObject source) { 289 addBoundProperties(source, source.getMap().getProperties()); 290 } 291 292 /** 293 * Copy all properties from the array with their receiver bound to the source. 294 * 295 * @param source The source object to copy from. 296 * @param properties The array of properties to copy. 297 */ addBoundProperties(final ScriptObject source, final Property[] properties)298 public void addBoundProperties(final ScriptObject source, final Property[] properties) { 299 PropertyMap newMap = this.getMap(); 300 final boolean extensible = newMap.isExtensible(); 301 302 for (final Property property : properties) { 303 newMap = addBoundProperty(newMap, source, property, extensible); 304 } 305 306 this.setMap(newMap); 307 } 308 309 /** 310 * Add a bound property from {@code source}, using the interim property map {@code propMap}, and return the 311 * new interim property map. 312 * 313 * @param propMap the property map 314 * @param source the source object 315 * @param property the property to be added 316 * @param extensible whether the current object is extensible or not 317 * @return the new property map 318 */ addBoundProperty(final PropertyMap propMap, final ScriptObject source, final Property property, final boolean extensible)319 protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final Property property, final boolean extensible) { 320 PropertyMap newMap = propMap; 321 final Object key = property.getKey(); 322 final Property oldProp = newMap.findProperty(key); 323 if (oldProp == null) { 324 if (! extensible) { 325 throw typeError("object.non.extensible", key.toString(), ScriptRuntime.safeToString(this)); 326 } 327 328 if (property instanceof UserAccessorProperty) { 329 // Note: we copy accessor functions to this object which is semantically different from binding. 330 final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source)); 331 newMap = newMap.addPropertyNoHistory(prop); 332 } else { 333 newMap = newMap.addPropertyBind((AccessorProperty)property, source); 334 } 335 } else { 336 // See ECMA section 10.5 Declaration Binding Instantiation 337 // step 5 processing each function declaration. 338 if (property.isFunctionDeclaration() && !oldProp.isConfigurable()) { 339 if (oldProp instanceof UserAccessorProperty || 340 !(oldProp.isWritable() && oldProp.isEnumerable())) { 341 throw typeError("cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this)); 342 } 343 } 344 } 345 return newMap; 346 } 347 348 /** 349 * Copy all properties from the array with their receiver bound to the source. 350 * 351 * @param source The source object to copy from. 352 * @param properties The collection of accessor properties to copy. 353 */ addBoundProperties(final Object source, final AccessorProperty[] properties)354 public void addBoundProperties(final Object source, final AccessorProperty[] properties) { 355 PropertyMap newMap = this.getMap(); 356 final boolean extensible = newMap.isExtensible(); 357 358 for (final AccessorProperty property : properties) { 359 final Object key = property.getKey(); 360 361 if (newMap.findProperty(key) == null) { 362 if (! extensible) { 363 throw typeError("object.non.extensible", key.toString(), ScriptRuntime.safeToString(this)); 364 } 365 newMap = newMap.addPropertyBind(property, source); 366 } 367 } 368 369 this.setMap(newMap); 370 } 371 372 /** 373 * Bind the method handle to the specified receiver, while preserving its original type (it will just ignore the 374 * first argument in lieu of the bound argument). 375 * @param methodHandle Method handle to bind to. 376 * @param receiver Object to bind. 377 * @return Bound method handle. 378 */ bindTo(final MethodHandle methodHandle, final Object receiver)379 static MethodHandle bindTo(final MethodHandle methodHandle, final Object receiver) { 380 return MH.dropArguments(MH.bindTo(methodHandle, receiver), 0, methodHandle.type().parameterType(0)); 381 } 382 383 /** 384 * Return a property iterator. 385 * @return Property iterator. 386 */ propertyIterator()387 public Iterator<String> propertyIterator() { 388 return new KeyIterator(this); 389 } 390 391 /** 392 * Return a property value iterator. 393 * @return Property value iterator. 394 */ valueIterator()395 public Iterator<Object> valueIterator() { 396 return new ValueIterator(this); 397 } 398 399 /** 400 * ECMA 8.10.1 IsAccessorDescriptor ( Desc ) 401 * @return true if this has a {@link AccessorPropertyDescriptor} with a getter or a setter 402 */ isAccessorDescriptor()403 public final boolean isAccessorDescriptor() { 404 return has(GET) || has(SET); 405 } 406 407 /** 408 * ECMA 8.10.2 IsDataDescriptor ( Desc ) 409 * @return true if this has a {@link DataPropertyDescriptor}, i.e. the object has a property value and is writable 410 */ isDataDescriptor()411 public final boolean isDataDescriptor() { 412 return has(VALUE) || has(WRITABLE); 413 } 414 415 /** 416 * ECMA 8.10.5 ToPropertyDescriptor ( Obj ) 417 * 418 * @return property descriptor 419 */ toPropertyDescriptor()420 public final PropertyDescriptor toPropertyDescriptor() { 421 final Global global = Context.getGlobal(); 422 423 final PropertyDescriptor desc; 424 if (isDataDescriptor()) { 425 if (has(SET) || has(GET)) { 426 throw typeError(global, "inconsistent.property.descriptor"); 427 } 428 429 desc = global.newDataDescriptor(UNDEFINED, false, false, false); 430 } else if (isAccessorDescriptor()) { 431 if (has(VALUE) || has(WRITABLE)) { 432 throw typeError(global, "inconsistent.property.descriptor"); 433 } 434 435 desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false); 436 } else { 437 desc = global.newGenericDescriptor(false, false); 438 } 439 440 return desc.fillFrom(this); 441 } 442 443 /** 444 * ECMA 8.10.5 ToPropertyDescriptor ( Obj ) 445 * 446 * @param global global scope object 447 * @param obj object to create property descriptor from 448 * 449 * @return property descriptor 450 */ toPropertyDescriptor(final Global global, final Object obj)451 public static PropertyDescriptor toPropertyDescriptor(final Global global, final Object obj) { 452 if (obj instanceof ScriptObject) { 453 return ((ScriptObject)obj).toPropertyDescriptor(); 454 } 455 456 throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj)); 457 } 458 459 /** 460 * ECMA 8.12.1 [[GetOwnProperty]] (P) 461 * 462 * @param key property key 463 * 464 * @return Returns the Property Descriptor of the named own property of this 465 * object, or undefined if absent. 466 */ getOwnPropertyDescriptor(final Object key)467 public Object getOwnPropertyDescriptor(final Object key) { 468 final Property property = getMap().findProperty(key); 469 470 final Global global = Context.getGlobal(); 471 472 if (property != null) { 473 final ScriptFunction get = property.getGetterFunction(this); 474 final ScriptFunction set = property.getSetterFunction(this); 475 476 final boolean configurable = property.isConfigurable(); 477 final boolean enumerable = property.isEnumerable(); 478 final boolean writable = property.isWritable(); 479 480 if (property.isAccessorProperty()) { 481 return global.newAccessorDescriptor( 482 get != null ? 483 get : 484 UNDEFINED, 485 set != null ? 486 set : 487 UNDEFINED, 488 configurable, 489 enumerable); 490 } 491 492 return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable); 493 } 494 495 final int index = getArrayIndex(key); 496 final ArrayData array = getArray(); 497 498 if (array.has(index)) { 499 return array.getDescriptor(global, index); 500 } 501 502 return UNDEFINED; 503 } 504 505 /** 506 * ECMA 8.12.2 [[GetProperty]] (P) 507 * 508 * @param key property key 509 * 510 * @return Returns the fully populated Property Descriptor of the named property 511 * of this object, or undefined if absent. 512 */ getPropertyDescriptor(final String key)513 public Object getPropertyDescriptor(final String key) { 514 final Object res = getOwnPropertyDescriptor(key); 515 516 if (res != UNDEFINED) { 517 return res; 518 } else if (getProto() != null) { 519 return getProto().getOwnPropertyDescriptor(key); 520 } else { 521 return UNDEFINED; 522 } 523 } 524 525 /** 526 * Invalidate any existing global constant method handles that may exist for {@code key}. 527 * @param key the property name 528 */ invalidateGlobalConstant(final Object key)529 protected void invalidateGlobalConstant(final Object key) { 530 final GlobalConstants globalConstants = getGlobalConstants(); 531 if (globalConstants != null) { 532 globalConstants.delete(key); 533 } 534 } 535 536 /** 537 * ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw) 538 * 539 * @param key the property key 540 * @param propertyDesc the property descriptor 541 * @param reject is the property extensible - true means new definitions are rejected 542 * 543 * @return true if property was successfully defined 544 */ defineOwnProperty(final Object key, final Object propertyDesc, final boolean reject)545 public boolean defineOwnProperty(final Object key, final Object propertyDesc, final boolean reject) { 546 final Global global = Context.getGlobal(); 547 final PropertyDescriptor desc = toPropertyDescriptor(global, propertyDesc); 548 final Object current = getOwnPropertyDescriptor(key); 549 550 invalidateGlobalConstant(key); 551 552 if (current == UNDEFINED) { 553 if (isExtensible()) { 554 // add a new own property 555 addOwnProperty(key, desc); 556 return true; 557 } 558 // new property added to non-extensible object 559 if (reject) { 560 throw typeError(global, "object.non.extensible", key.toString(), ScriptRuntime.safeToString(this)); 561 } 562 return false; 563 } 564 565 // modifying an existing property 566 final PropertyDescriptor currentDesc = (PropertyDescriptor)current; 567 final PropertyDescriptor newDesc = desc; 568 569 if (newDesc.type() == PropertyDescriptor.GENERIC && !newDesc.has(CONFIGURABLE) && !newDesc.has(ENUMERABLE)) { 570 // every descriptor field is absent 571 return true; 572 } 573 574 if (newDesc.hasAndEquals(currentDesc)) { 575 // every descriptor field of the new is same as the current 576 return true; 577 } 578 579 if (!currentDesc.isConfigurable()) { 580 if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) { 581 // not configurable can not be made configurable 582 if (reject) { 583 throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this)); 584 } 585 return false; 586 } 587 588 if (newDesc.has(ENUMERABLE) && 589 currentDesc.isEnumerable() != newDesc.isEnumerable()) { 590 // cannot make non-enumerable as enumerable or vice-versa 591 if (reject) { 592 throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this)); 593 } 594 return false; 595 } 596 } 597 598 int propFlags = Property.mergeFlags(currentDesc, newDesc); 599 Property property = getMap().findProperty(key); 600 601 if (currentDesc.type() == PropertyDescriptor.DATA && 602 (newDesc.type() == PropertyDescriptor.DATA || 603 newDesc.type() == PropertyDescriptor.GENERIC)) { 604 if (!currentDesc.isConfigurable() && !currentDesc.isWritable()) { 605 if (newDesc.has(WRITABLE) && newDesc.isWritable() || 606 newDesc.has(VALUE) && !ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) { 607 if (reject) { 608 throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this)); 609 } 610 return false; 611 } 612 } 613 614 final boolean newValue = newDesc.has(VALUE); 615 final Object value = newValue ? newDesc.getValue() : currentDesc.getValue(); 616 617 if (newValue && property != null) { 618 // Temporarily clear flags. 619 property = modifyOwnProperty(property, 0); 620 set(key, value, 0); 621 //this might change the map if we change types of the property 622 //hence we need to read it again. note that we should probably 623 //have the setter return the new property throughout and in 624 //general respect Property return values from modify and add 625 //functions - which we don't seem to do at all here :-( 626 //There is already a bug filed to generify PropertyAccess so we 627 //can have the setter return e.g. a Property 628 property = getMap().findProperty(key); 629 } 630 631 if (property == null) { 632 // promoting an arrayData value to actual property 633 addOwnProperty(key, propFlags, value); 634 checkIntegerKey(key); 635 } else { 636 // Now set the new flags 637 modifyOwnProperty(property, propFlags); 638 } 639 } else if (currentDesc.type() == PropertyDescriptor.ACCESSOR && 640 (newDesc.type() == PropertyDescriptor.ACCESSOR || 641 newDesc.type() == PropertyDescriptor.GENERIC)) { 642 if (!currentDesc.isConfigurable()) { 643 if (newDesc.has(PropertyDescriptor.GET) && !ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) || 644 newDesc.has(PropertyDescriptor.SET) && !ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) { 645 if (reject) { 646 throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this)); 647 } 648 return false; 649 } 650 } 651 // New set the new features. 652 modifyOwnProperty(property, propFlags, 653 newDesc.has(GET) ? newDesc.getGetter() : currentDesc.getGetter(), 654 newDesc.has(SET) ? newDesc.getSetter() : currentDesc.getSetter()); 655 } else { 656 // changing descriptor type 657 if (!currentDesc.isConfigurable()) { 658 // not configurable can not be made configurable 659 if (reject) { 660 throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this)); 661 } 662 return false; 663 } 664 665 propFlags = 0; 666 667 // Preserve only configurable and enumerable from current desc 668 // if those are not overridden in the new property descriptor. 669 boolean value = newDesc.has(CONFIGURABLE) ? newDesc.isConfigurable() : currentDesc.isConfigurable(); 670 if (!value) { 671 propFlags |= Property.NOT_CONFIGURABLE; 672 } 673 value = newDesc.has(ENUMERABLE)? newDesc.isEnumerable() : currentDesc.isEnumerable(); 674 if (!value) { 675 propFlags |= Property.NOT_ENUMERABLE; 676 } 677 678 final int type = newDesc.type(); 679 if (type == PropertyDescriptor.DATA) { 680 // get writable from the new descriptor 681 value = newDesc.has(WRITABLE) && newDesc.isWritable(); 682 if (!value) { 683 propFlags |= Property.NOT_WRITABLE; 684 } 685 686 // delete the old property 687 deleteOwnProperty(property); 688 // add new data property 689 addOwnProperty(key, propFlags, newDesc.getValue()); 690 } else if (type == PropertyDescriptor.ACCESSOR) { 691 if (property == null) { 692 addOwnProperty(key, propFlags, 693 newDesc.has(GET) ? newDesc.getGetter() : null, 694 newDesc.has(SET) ? newDesc.getSetter() : null); 695 } else { 696 // Modify old property with the new features. 697 modifyOwnProperty(property, propFlags, 698 newDesc.has(GET) ? newDesc.getGetter() : null, 699 newDesc.has(SET) ? newDesc.getSetter() : null); 700 } 701 } 702 } 703 704 checkIntegerKey(key); 705 706 return true; 707 } 708 709 /** 710 * Almost like defineOwnProperty(int,Object) for arrays this one does 711 * not add 'gap' elements (like the array one does). 712 * 713 * @param index key for property 714 * @param value value to define 715 */ defineOwnProperty(final int index, final Object value)716 public void defineOwnProperty(final int index, final Object value) { 717 assert isValidArrayIndex(index) : "invalid array index"; 718 final long longIndex = ArrayIndex.toLongIndex(index); 719 final long oldLength = getArray().length(); 720 if (longIndex >= oldLength) { 721 setArray(getArray().ensure(longIndex).safeDelete(oldLength, longIndex - 1, false)); 722 } 723 setArray(getArray().set(index, value, false)); 724 } 725 checkIntegerKey(final Object key)726 private void checkIntegerKey(final Object key) { 727 final int index = getArrayIndex(key); 728 729 if (isValidArrayIndex(index)) { 730 final ArrayData data = getArray(); 731 732 if (data.has(index)) { 733 setArray(data.delete(index)); 734 } 735 } 736 } 737 738 /** 739 * Add a new property to the object. 740 * 741 * @param key property key 742 * @param propertyDesc property descriptor for property 743 */ addOwnProperty(final Object key, final PropertyDescriptor propertyDesc)744 public final void addOwnProperty(final Object key, final PropertyDescriptor propertyDesc) { 745 // Already checked that there is no own property with that key. 746 PropertyDescriptor pdesc = propertyDesc; 747 748 final int propFlags = Property.toFlags(pdesc); 749 750 if (pdesc.type() == PropertyDescriptor.GENERIC) { 751 final Global global = Context.getGlobal(); 752 final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false); 753 754 dDesc.fillFrom((ScriptObject)pdesc); 755 pdesc = dDesc; 756 } 757 758 final int type = pdesc.type(); 759 if (type == PropertyDescriptor.DATA) { 760 addOwnProperty(key, propFlags, pdesc.getValue()); 761 } else if (type == PropertyDescriptor.ACCESSOR) { 762 addOwnProperty(key, propFlags, 763 pdesc.has(GET) ? pdesc.getGetter() : null, 764 pdesc.has(SET) ? pdesc.getSetter() : null); 765 } 766 767 checkIntegerKey(key); 768 } 769 770 /** 771 * Low level property API (not using property descriptors) 772 * <p> 773 * Find a property in the prototype hierarchy. Note: this is final and not 774 * a good idea to override. If you have to, use 775 * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or 776 * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the 777 * overriding way to find array properties 778 * 779 * @see jdk.nashorn.internal.objects.NativeArray 780 * 781 * @param key Property key. 782 * @param deep Whether the search should look up proto chain. 783 * 784 * @return FindPropertyData or null if not found. 785 */ findProperty(final Object key, final boolean deep)786 public final FindProperty findProperty(final Object key, final boolean deep) { 787 return findProperty(key, deep, false, this); 788 } 789 790 /** 791 * Low level property API (not using property descriptors) 792 * <p> 793 * Find a property in the prototype hierarchy. Note: this is not a good idea 794 * to override except as it was done in {@link WithObject}. 795 * If you have to, use 796 * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or 797 * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the 798 * overriding way to find array properties 799 * 800 * @see jdk.nashorn.internal.objects.NativeArray 801 * 802 * @param key Property key. 803 * @param deep true if the search should look up proto chain 804 * @param isScope true if this is a scope access 805 * @param start the object on which the lookup was originally initiated 806 * @return FindPropertyData or null if not found. 807 */ findProperty(final Object key, final boolean deep, final boolean isScope, final ScriptObject start)808 protected FindProperty findProperty(final Object key, final boolean deep, final boolean isScope, final ScriptObject start) { 809 810 final PropertyMap selfMap = getMap(); 811 final Property property = selfMap.findProperty(key); 812 813 if (property != null) { 814 return new FindProperty(start, this, property); 815 } 816 817 if (deep) { 818 final ScriptObject myProto = getProto(); 819 final FindProperty find = myProto == null ? null : myProto.findProperty(key, true, isScope, start); 820 // checkSharedProtoMap must be invoked after myProto.checkSharedProtoMap to propagate 821 // shared proto invalidation up the prototype chain. It also must be invoked when prototype is null. 822 checkSharedProtoMap(); 823 return find; 824 } 825 826 return null; 827 } 828 829 /** 830 * Low level property API. This is similar to {@link #findProperty(Object, boolean)} but returns a 831 * {@code boolean} value instead of a {@link FindProperty} object. 832 * @param key Property key. 833 * @param deep Whether the search should look up proto chain. 834 * @return true if the property was found. 835 */ hasProperty(final Object key, final boolean deep)836 boolean hasProperty(final Object key, final boolean deep) { 837 if (getMap().findProperty(key) != null) { 838 return true; 839 } 840 841 if (deep) { 842 final ScriptObject myProto = getProto(); 843 if (myProto != null) { 844 return myProto.hasProperty(key, true); 845 } 846 } 847 848 return false; 849 } 850 findBuiltinSwitchPoint(final Object key)851 private SwitchPoint findBuiltinSwitchPoint(final Object key) { 852 for (ScriptObject myProto = getProto(); myProto != null; myProto = myProto.getProto()) { 853 final Property prop = myProto.getMap().findProperty(key); 854 if (prop != null) { 855 final SwitchPoint sp = prop.getBuiltinSwitchPoint(); 856 if (sp != null && !sp.hasBeenInvalidated()) { 857 return sp; 858 } 859 } 860 } 861 return null; 862 } 863 864 /** 865 * Add a new property to the object. 866 * <p> 867 * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s 868 * 869 * @param key Property key. 870 * @param propertyFlags Property flags. 871 * @param getter Property getter, or null if not defined 872 * @param setter Property setter, or null if not defined 873 * 874 * @return New property. 875 */ addOwnProperty(final Object key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter)876 public final Property addOwnProperty(final Object key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { 877 return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter)); 878 } 879 880 /** 881 * Add a new property to the object. 882 * <p> 883 * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s 884 * 885 * @param key Property key. 886 * @param propertyFlags Property flags. 887 * @param value Value of property 888 * 889 * @return New property. 890 */ addOwnProperty(final Object key, final int propertyFlags, final Object value)891 public final Property addOwnProperty(final Object key, final int propertyFlags, final Object value) { 892 return addSpillProperty(key, propertyFlags, value, true); 893 } 894 895 /** 896 * Add a new property to the object. 897 * <p> 898 * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s 899 * 900 * @param newProperty property to add 901 * 902 * @return New property. 903 */ addOwnProperty(final Property newProperty)904 public final Property addOwnProperty(final Property newProperty) { 905 PropertyMap oldMap = getMap(); 906 while (true) { 907 final PropertyMap newMap = oldMap.addProperty(newProperty); 908 if (!compareAndSetMap(oldMap, newMap)) { 909 oldMap = getMap(); 910 final Property oldProperty = oldMap.findProperty(newProperty.getKey()); 911 912 if (oldProperty != null) { 913 return oldProperty; 914 } 915 } else { 916 return newProperty; 917 } 918 } 919 } 920 erasePropertyValue(final Property property)921 private void erasePropertyValue(final Property property) { 922 // Erase the property field value with undefined. If the property is an accessor property 923 // we don't want to call the setter!! 924 if (property != null && !property.isAccessorProperty()) { 925 property.setValue(this, this, UNDEFINED, false); 926 } 927 } 928 929 /** 930 * Delete a property from the object. 931 * 932 * @param property Property to delete. 933 * 934 * @return true if deleted. 935 */ deleteOwnProperty(final Property property)936 public final boolean deleteOwnProperty(final Property property) { 937 erasePropertyValue(property); 938 PropertyMap oldMap = getMap(); 939 940 while (true) { 941 final PropertyMap newMap = oldMap.deleteProperty(property); 942 943 if (newMap == null) { 944 return false; 945 } 946 947 if (!compareAndSetMap(oldMap, newMap)) { 948 oldMap = getMap(); 949 } else { 950 // delete getter and setter function references so that we don't leak 951 if (property instanceof UserAccessorProperty) { 952 ((UserAccessorProperty)property).setAccessors(this, getMap(), null); 953 } 954 955 invalidateGlobalConstant(property.getKey()); 956 return true; 957 } 958 } 959 960 } 961 962 /** 963 * Fast initialization functions for ScriptFunctions that are strict, to avoid 964 * creating setters that probably aren't used. Inject directly into the spill pool 965 * the defaults for "arguments" and "caller", asserting the property is already 966 * defined in the map. 967 * 968 * @param key property key 969 * @param getter getter for {@link UserAccessorProperty} 970 * @param setter setter for {@link UserAccessorProperty} 971 */ initUserAccessors(final String key, final ScriptFunction getter, final ScriptFunction setter)972 protected final void initUserAccessors(final String key, final ScriptFunction getter, final ScriptFunction setter) { 973 final PropertyMap map = getMap(); 974 final Property property = map.findProperty(key); 975 assert property instanceof UserAccessorProperty; 976 ensureSpillSize(property.getSlot()); 977 objectSpill[property.getSlot()] = new UserAccessorProperty.Accessors(getter, setter); 978 } 979 980 /** 981 * Modify a property in the object 982 * 983 * @param oldProperty property to modify 984 * @param propertyFlags new property flags 985 * @param getter getter for {@link UserAccessorProperty}, null if not present or N/A 986 * @param setter setter for {@link UserAccessorProperty}, null if not present or N/A 987 * 988 * @return new property 989 */ modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter)990 public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { 991 Property newProperty; 992 993 if (oldProperty instanceof UserAccessorProperty) { 994 final UserAccessorProperty uc = (UserAccessorProperty)oldProperty; 995 final int slot = uc.getSlot(); 996 997 assert uc.getLocalType() == Object.class; 998 final UserAccessorProperty.Accessors gs = uc.getAccessors(this); //this crashes 999 assert gs != null; 1000 //reuse existing getter setter for speed 1001 gs.set(getter, setter); 1002 if (uc.getFlags() == (propertyFlags | Property.IS_ACCESSOR_PROPERTY)) { 1003 return oldProperty; 1004 } 1005 newProperty = new UserAccessorProperty(uc.getKey(), propertyFlags, slot); 1006 } else { 1007 // erase old property value and create new user accessor property 1008 erasePropertyValue(oldProperty); 1009 newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter); 1010 } 1011 1012 return modifyOwnProperty(oldProperty, newProperty); 1013 } 1014 1015 /** 1016 * Modify a property in the object 1017 * 1018 * @param oldProperty property to modify 1019 * @param propertyFlags new property flags 1020 * 1021 * @return new property 1022 */ modifyOwnProperty(final Property oldProperty, final int propertyFlags)1023 public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags) { 1024 return modifyOwnProperty(oldProperty, oldProperty.setFlags(propertyFlags)); 1025 } 1026 1027 /** 1028 * Modify a property in the object, replacing a property with a new one 1029 * 1030 * @param oldProperty property to replace 1031 * @param newProperty property to replace it with 1032 * 1033 * @return new property 1034 */ modifyOwnProperty(final Property oldProperty, final Property newProperty)1035 private Property modifyOwnProperty(final Property oldProperty, final Property newProperty) { 1036 if (oldProperty == newProperty) { 1037 return newProperty; //nop 1038 } 1039 1040 assert newProperty.getKey().equals(oldProperty.getKey()) : "replacing property with different key"; 1041 1042 PropertyMap oldMap = getMap(); 1043 1044 while (true) { 1045 final PropertyMap newMap = oldMap.replaceProperty(oldProperty, newProperty); 1046 1047 if (!compareAndSetMap(oldMap, newMap)) { 1048 oldMap = getMap(); 1049 final Property oldPropertyLookup = oldMap.findProperty(oldProperty.getKey()); 1050 1051 if (oldPropertyLookup != null && oldPropertyLookup.equals(newProperty)) { 1052 return oldPropertyLookup; 1053 } 1054 } else { 1055 return newProperty; 1056 } 1057 } 1058 } 1059 1060 /** 1061 * Update getter and setter in an object literal. 1062 * 1063 * @param key Property key. 1064 * @param getter {@link UserAccessorProperty} defined getter, or null if none 1065 * @param setter {@link UserAccessorProperty} defined setter, or null if none 1066 */ setUserAccessors(final Object key, final ScriptFunction getter, final ScriptFunction setter)1067 public final void setUserAccessors(final Object key, final ScriptFunction getter, final ScriptFunction setter) { 1068 final Object realKey = JSType.toPropertyKey(key); 1069 final Property oldProperty = getMap().findProperty(realKey); 1070 if (oldProperty instanceof UserAccessorProperty) { 1071 modifyOwnProperty(oldProperty, oldProperty.getFlags(), getter, setter); 1072 } else { 1073 addOwnProperty(newUserAccessors(realKey, oldProperty != null ? oldProperty.getFlags() : 0, getter, setter)); 1074 } 1075 } 1076 getIntValue(final FindProperty find, final int programPoint)1077 private static int getIntValue(final FindProperty find, final int programPoint) { 1078 final MethodHandle getter = find.getGetter(int.class, programPoint, null); 1079 if (getter != null) { 1080 try { 1081 return (int)getter.invokeExact((Object)find.getGetterReceiver()); 1082 } catch (final Error|RuntimeException e) { 1083 throw e; 1084 } catch (final Throwable e) { 1085 throw new RuntimeException(e); 1086 } 1087 } 1088 1089 return UNDEFINED_INT; 1090 } 1091 getDoubleValue(final FindProperty find, final int programPoint)1092 private static double getDoubleValue(final FindProperty find, final int programPoint) { 1093 final MethodHandle getter = find.getGetter(double.class, programPoint, null); 1094 if (getter != null) { 1095 try { 1096 return (double)getter.invokeExact((Object)find.getGetterReceiver()); 1097 } catch (final Error|RuntimeException e) { 1098 throw e; 1099 } catch (final Throwable e) { 1100 throw new RuntimeException(e); 1101 } 1102 } 1103 1104 return UNDEFINED_DOUBLE; 1105 } 1106 1107 /** 1108 * Return methodHandle of value function for call. 1109 * 1110 * @param find data from find property. 1111 * @param type method type of function. 1112 * @param bindName null or name to bind to second argument (property not found method.) 1113 * 1114 * @return value of property as a MethodHandle or null. 1115 */ getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName)1116 protected static MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) { 1117 return getCallMethodHandle(find.getObjectValue(), type, bindName); 1118 } 1119 1120 /** 1121 * Return methodHandle of value function for call. 1122 * 1123 * @param value value of receiver, it not a {@link ScriptFunction} this will return null. 1124 * @param type method type of function. 1125 * @param bindName null or name to bind to second argument (property not found method.) 1126 * 1127 * @return value of property as a MethodHandle or null. 1128 */ getCallMethodHandle(final Object value, final MethodType type, final String bindName)1129 private static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) { 1130 return value instanceof ScriptFunction ? ((ScriptFunction)value).getCallMethodHandle(type, bindName) : null; 1131 } 1132 1133 /** 1134 * Get value using found property. 1135 * 1136 * @param property Found property. 1137 * 1138 * @return Value of property. 1139 */ getWithProperty(final Property property)1140 public final Object getWithProperty(final Property property) { 1141 return new FindProperty(this, this, property).getObjectValue(); 1142 } 1143 1144 /** 1145 * Get a property given a key 1146 * 1147 * @param key property key 1148 * 1149 * @return property for key 1150 */ getProperty(final String key)1151 public final Property getProperty(final String key) { 1152 return getMap().findProperty(key); 1153 } 1154 1155 /** 1156 * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.) 1157 * Used for argument access in a vararg function using parameter name. 1158 * Returns the argument at a given key (index) 1159 * 1160 * @param key argument index 1161 * 1162 * @return the argument at the given position, or undefined if not present 1163 */ getArgument(final int key)1164 public Object getArgument(final int key) { 1165 return get(key); 1166 } 1167 1168 /** 1169 * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.) 1170 * Used for argument access in a vararg function using parameter name. 1171 * Returns the argument at a given key (index) 1172 * 1173 * @param key argument index 1174 * @param value the value to write at the given index 1175 */ setArgument(final int key, final Object value)1176 public void setArgument(final int key, final Object value) { 1177 set(key, value, 0); 1178 } 1179 1180 /** 1181 * Return the current context from the object's map. 1182 * @return Current context. 1183 */ getContext()1184 protected Context getContext() { 1185 return Context.fromClass(getClass()); 1186 } 1187 1188 /** 1189 * Return the map of an object. 1190 * @return PropertyMap object. 1191 */ getMap()1192 public final PropertyMap getMap() { 1193 return map; 1194 } 1195 1196 /** 1197 * Set the initial map. 1198 * @param map Initial map. 1199 */ setMap(final PropertyMap map)1200 public final void setMap(final PropertyMap map) { 1201 this.map = map; 1202 } 1203 1204 /** 1205 * Conditionally set the new map if the old map is the same. 1206 * @param oldMap Map prior to manipulation. 1207 * @param newMap Replacement map. 1208 * @return true if the operation succeeded. 1209 */ compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap)1210 protected final boolean compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap) { 1211 if (oldMap == this.map) { 1212 this.map = newMap; 1213 return true; 1214 } 1215 return false; 1216 } 1217 1218 /** 1219 * Return the __proto__ of an object. 1220 * @return __proto__ object. 1221 */ getProto()1222 public final ScriptObject getProto() { 1223 return proto; 1224 } 1225 1226 /** 1227 * Get the proto of a specific depth 1228 * @param n depth 1229 * @return proto at given depth 1230 */ getProto(final int n)1231 public final ScriptObject getProto(final int n) { 1232 ScriptObject p = this; 1233 for (int i = n; i > 0; i--) { 1234 p = p.getProto(); 1235 } 1236 return p; 1237 } 1238 1239 /** 1240 * Set the __proto__ of an object. 1241 * @param newProto new __proto__ to set. 1242 */ setProto(final ScriptObject newProto)1243 public final void setProto(final ScriptObject newProto) { 1244 final ScriptObject oldProto = proto; 1245 1246 if (oldProto != newProto) { 1247 proto = newProto; 1248 1249 // Let current listeners know that the prototype has changed 1250 getMap().protoChanged(); 1251 // Replace our current allocator map with one that is associated with the new prototype. 1252 setMap(getMap().changeProto(newProto)); 1253 } 1254 } 1255 1256 /** 1257 * Set the initial __proto__ of this object. This should be used instead of 1258 * {@link #setProto} if it is known that the current property map will not be 1259 * used on a new object with any other parent property map, so we can pass over 1260 * property map invalidation/evolution. 1261 * 1262 * @param initialProto the initial __proto__ to set. 1263 */ setInitialProto(final ScriptObject initialProto)1264 public void setInitialProto(final ScriptObject initialProto) { 1265 this.proto = initialProto; 1266 } 1267 1268 /** 1269 * Invoked from generated bytecode to initialize the prototype of object literals to the global Object prototype. 1270 * @param obj the object literal that needs to have its prototype initialized to the global Object prototype. 1271 */ setGlobalObjectProto(final ScriptObject obj)1272 public static void setGlobalObjectProto(final ScriptObject obj) { 1273 obj.setInitialProto(Global.objectPrototype()); 1274 } 1275 1276 /** 1277 * Set the __proto__ of an object with checks. 1278 * This is the built-in operation [[SetPrototypeOf]] 1279 * See ES6 draft spec: 9.1.2 [[SetPrototypeOf]] (V) 1280 * 1281 * @param newProto Prototype to set. 1282 */ setPrototypeOf(final Object newProto)1283 public final void setPrototypeOf(final Object newProto) { 1284 if (newProto == null || newProto instanceof ScriptObject) { 1285 if (! isExtensible()) { 1286 // okay to set same proto again - even if non-extensible 1287 1288 if (newProto == getProto()) { 1289 return; 1290 } 1291 throw typeError("__proto__.set.non.extensible", ScriptRuntime.safeToString(this)); 1292 } 1293 1294 // check for circularity 1295 ScriptObject p = (ScriptObject)newProto; 1296 while (p != null) { 1297 if (p == this) { 1298 throw typeError("circular.__proto__.set", ScriptRuntime.safeToString(this)); 1299 } 1300 p = p.getProto(); 1301 } 1302 setProto((ScriptObject) newProto); 1303 } else { 1304 throw typeError("cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto)); 1305 } 1306 } 1307 1308 /** 1309 * Set the __proto__ of an object from an object literal. 1310 * See ES6 draft spec: B.3.1 __proto__ Property Names in 1311 * Object Initializers. Step 6 handling of "__proto__". 1312 * 1313 * @param newProto Prototype to set. 1314 */ setProtoFromLiteral(final Object newProto)1315 public final void setProtoFromLiteral(final Object newProto) { 1316 if (newProto == null || newProto instanceof ScriptObject) { 1317 setPrototypeOf(newProto); 1318 } else { 1319 // Some non-object, non-null. Then, we need to set 1320 // Object.prototype as the new __proto__ 1321 // 1322 // var obj = { __proto__ : 34 }; 1323 // print(obj.__proto__ === Object.prototype); // => true 1324 setPrototypeOf(Global.objectPrototype()); 1325 } 1326 } 1327 1328 /** 1329 * return an array of all property keys - all inherited, non-enumerable included. 1330 * This is meant for source code completion by interactive shells or editors. 1331 * 1332 * @return Array of keys, order of properties is undefined. 1333 */ getAllKeys()1334 public String[] getAllKeys() { 1335 final Set<String> keys = new HashSet<>(); 1336 final Set<String> nonEnumerable = new HashSet<>(); 1337 for (ScriptObject self = this; self != null; self = self.getProto()) { 1338 keys.addAll(Arrays.asList(self.getOwnKeys(String.class, true, nonEnumerable))); 1339 } 1340 return keys.toArray(new String[0]); 1341 } 1342 1343 /** 1344 * Return an array of own property keys associated with the object. 1345 * 1346 * @param all True if to include non-enumerable keys. 1347 * @return Array of keys. 1348 */ getOwnKeys(final boolean all)1349 public final String[] getOwnKeys(final boolean all) { 1350 return getOwnKeys(String.class, all, null); 1351 } 1352 1353 /** 1354 * Return an array of own property keys associated with the object. 1355 * 1356 * @param all True if to include non-enumerable keys. 1357 * @return Array of keys. 1358 */ getOwnSymbols(final boolean all)1359 public final Symbol[] getOwnSymbols(final boolean all) { 1360 return getOwnKeys(Symbol.class, all, null); 1361 } 1362 1363 /** 1364 * return an array of own property keys associated with the object. 1365 * 1366 * @param <T> the type returned keys. 1367 * @param type the type of keys to return, either {@code String.class} or {@code Symbol.class}. 1368 * @param all True if to include non-enumerable keys. 1369 * @param nonEnumerable set of non-enumerable properties seen already. Used to 1370 * filter out shadowed, but enumerable properties from proto children. 1371 * @return Array of keys. 1372 */ 1373 @SuppressWarnings("unchecked") getOwnKeys(final Class<T> type, final boolean all, final Set<T> nonEnumerable)1374 protected <T> T[] getOwnKeys(final Class<T> type, final boolean all, final Set<T> nonEnumerable) { 1375 final List<Object> keys = new ArrayList<>(); 1376 final PropertyMap selfMap = this.getMap(); 1377 1378 final ArrayData array = getArray(); 1379 1380 if (type == String.class) { 1381 for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) { 1382 keys.add(JSType.toString(iter.next().longValue())); 1383 } 1384 } 1385 1386 for (final Property property : selfMap.getProperties()) { 1387 final boolean enumerable = property.isEnumerable(); 1388 final Object key = property.getKey(); 1389 if (!type.isInstance(key)) { 1390 continue; 1391 } 1392 if (all) { 1393 keys.add(key); 1394 } else if (enumerable) { 1395 // either we don't have non-enumerable filter set or filter set 1396 // does not contain the current property. 1397 if (nonEnumerable == null || !nonEnumerable.contains(key)) { 1398 keys.add(key); 1399 } 1400 } else { 1401 // store this non-enumerable property for later proto walk 1402 if (nonEnumerable != null) { 1403 nonEnumerable.add((T) key); 1404 } 1405 } 1406 } 1407 1408 return keys.toArray((T[]) Array.newInstance(type, keys.size())); 1409 } 1410 1411 /** 1412 * Check if this ScriptObject has array entries. This means that someone has 1413 * set values with numeric keys in the object. 1414 * 1415 * @return true if array entries exists. 1416 */ hasArrayEntries()1417 public boolean hasArrayEntries() { 1418 return getArray().length() > 0 || getMap().containsArrayKeys(); 1419 } 1420 1421 /** 1422 * Return the valid JavaScript type name descriptor 1423 * 1424 * @return "Object" 1425 */ getClassName()1426 public String getClassName() { 1427 return "Object"; 1428 } 1429 1430 /** 1431 * {@code length} is a well known property. This is its getter. 1432 * Note that this *may* be optimized by other classes 1433 * 1434 * @return length property value for this ScriptObject 1435 */ getLength()1436 public Object getLength() { 1437 return get("length"); 1438 } 1439 1440 /** 1441 * Stateless toString for ScriptObjects. 1442 * 1443 * @return string description of this object, e.g. {@code [object Object]} 1444 */ safeToString()1445 public String safeToString() { 1446 return "[object " + getClassName() + "]"; 1447 } 1448 1449 /** 1450 * Return the default value of the object with a given preferred type hint. 1451 * The preferred type hints are String.class for type String, Number.class 1452 * for type Number. <p> 1453 * 1454 * A <code>hint</code> of null means "no hint". 1455 * 1456 * ECMA 8.12.8 [[DefaultValue]](hint) 1457 * 1458 * @param typeHint the preferred type hint 1459 * @return the default value 1460 */ getDefaultValue(final Class<?> typeHint)1461 public Object getDefaultValue(final Class<?> typeHint) { 1462 // We delegate to Global, as the implementation uses dynamic call sites to invoke object's "toString" and 1463 // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts 1464 // are being executed in a long-running program, we move the code and their associated dynamic call sites 1465 // (Global.TO_STRING and Global.VALUE_OF) into per-context code. 1466 return Context.getGlobal().getDefaultValue(this, typeHint); 1467 } 1468 1469 /** 1470 * Checking whether a script object is an instance of another. Used 1471 * in {@link ScriptFunction} for hasInstance implementation, walks 1472 * the proto chain 1473 * 1474 * @param instance instance to check 1475 * @return true if 'instance' is an instance of this object 1476 */ isInstance(final ScriptObject instance)1477 public boolean isInstance(final ScriptObject instance) { 1478 return false; 1479 } 1480 1481 /** 1482 * Flag this ScriptObject as non extensible 1483 * 1484 * @return the object after being made non extensible 1485 */ preventExtensions()1486 public ScriptObject preventExtensions() { 1487 PropertyMap oldMap = getMap(); 1488 while (!compareAndSetMap(oldMap, getMap().preventExtensions())) { 1489 oldMap = getMap(); 1490 } 1491 1492 //invalidate any fast array setters 1493 final ArrayData array = getArray(); 1494 assert array != null; 1495 setArray(ArrayData.preventExtension(array)); 1496 return this; 1497 } 1498 1499 /** 1500 * Check whether if an Object (not just a ScriptObject) represents JavaScript array 1501 * 1502 * @param obj object to check 1503 * 1504 * @return true if array 1505 */ isArray(final Object obj)1506 public static boolean isArray(final Object obj) { 1507 return obj instanceof ScriptObject && ((ScriptObject)obj).isArray(); 1508 } 1509 1510 /** 1511 * Check if this ScriptObject is an array 1512 * @return true if array 1513 */ isArray()1514 public final boolean isArray() { 1515 return (flags & IS_ARRAY) != 0; 1516 } 1517 1518 /** 1519 * Flag this ScriptObject as being an array 1520 */ setIsArray()1521 public final void setIsArray() { 1522 flags |= IS_ARRAY; 1523 } 1524 1525 /** 1526 * Check if this ScriptObject is an {@code arguments} vector 1527 * @return true if arguments vector 1528 */ isArguments()1529 public final boolean isArguments() { 1530 return (flags & IS_ARGUMENTS) != 0; 1531 } 1532 1533 /** 1534 * Flag this ScriptObject as being an {@code arguments} vector 1535 */ setIsArguments()1536 public final void setIsArguments() { 1537 flags |= IS_ARGUMENTS; 1538 } 1539 1540 /** 1541 * Check if this object has non-writable length property 1542 * 1543 * @return {@code true} if 'length' property is non-writable 1544 */ isLengthNotWritable()1545 public boolean isLengthNotWritable() { 1546 return (flags & IS_LENGTH_NOT_WRITABLE) != 0; 1547 } 1548 1549 /** 1550 * Flag this object as having non-writable length property. 1551 */ setIsLengthNotWritable()1552 public void setIsLengthNotWritable() { 1553 flags |= IS_LENGTH_NOT_WRITABLE; 1554 } 1555 1556 /** 1557 * Get the {@link ArrayData}, for this ScriptObject, ensuring it is of a type 1558 * that can handle elementType 1559 * @param elementType elementType 1560 * @return array data 1561 */ getArray(final Class<?> elementType)1562 public final ArrayData getArray(final Class<?> elementType) { 1563 if (elementType == null) { 1564 return arrayData; 1565 } 1566 final ArrayData newArrayData = arrayData.convert(elementType); 1567 if (newArrayData != arrayData) { 1568 arrayData = newArrayData; 1569 } 1570 return newArrayData; 1571 } 1572 1573 /** 1574 * Get the {@link ArrayData} for this ScriptObject if it is an array 1575 * @return array data 1576 */ getArray()1577 public final ArrayData getArray() { 1578 return arrayData; 1579 } 1580 1581 /** 1582 * Set the {@link ArrayData} for this ScriptObject if it is to be an array 1583 * @param arrayData the array data 1584 */ setArray(final ArrayData arrayData)1585 public final void setArray(final ArrayData arrayData) { 1586 this.arrayData = arrayData; 1587 } 1588 1589 /** 1590 * Check if this ScriptObject is extensible 1591 * @return true if extensible 1592 */ isExtensible()1593 public boolean isExtensible() { 1594 return getMap().isExtensible(); 1595 } 1596 1597 /** 1598 * ECMAScript 15.2.3.8 - seal implementation 1599 * @return the sealed ScriptObject 1600 */ seal()1601 public ScriptObject seal() { 1602 PropertyMap oldMap = getMap(); 1603 1604 while (true) { 1605 final PropertyMap newMap = getMap().seal(); 1606 1607 if (!compareAndSetMap(oldMap, newMap)) { 1608 oldMap = getMap(); 1609 } else { 1610 setArray(ArrayData.seal(getArray())); 1611 return this; 1612 } 1613 } 1614 } 1615 1616 /** 1617 * Check whether this ScriptObject is sealed 1618 * @return true if sealed 1619 */ isSealed()1620 public boolean isSealed() { 1621 return getMap().isSealed(); 1622 } 1623 1624 /** 1625 * ECMA 15.2.39 - freeze implementation. Freeze this ScriptObject 1626 * @return the frozen ScriptObject 1627 */ freeze()1628 public ScriptObject freeze() { 1629 PropertyMap oldMap = getMap(); 1630 1631 while (true) { 1632 final PropertyMap newMap = getMap().freeze(); 1633 1634 if (!compareAndSetMap(oldMap, newMap)) { 1635 oldMap = getMap(); 1636 } else { 1637 setArray(ArrayData.freeze(getArray())); 1638 return this; 1639 } 1640 } 1641 } 1642 1643 /** 1644 * Check whether this ScriptObject is frozen 1645 * @return true if frozen 1646 */ isFrozen()1647 public boolean isFrozen() { 1648 return getMap().isFrozen(); 1649 } 1650 1651 /** 1652 * Check whether this ScriptObject is scope 1653 * @return true if scope 1654 */ isScope()1655 public boolean isScope() { 1656 return false; 1657 } 1658 1659 /** 1660 * Tag this script object as built in 1661 */ setIsBuiltin()1662 public final void setIsBuiltin() { 1663 flags |= IS_BUILTIN; 1664 } 1665 1666 /** 1667 * Check if this script object is built in 1668 * @return true if build in 1669 */ isBuiltin()1670 public final boolean isBuiltin() { 1671 return (flags & IS_BUILTIN) != 0; 1672 } 1673 1674 /** 1675 * Tag this script object as internal object that should not be visible to script code. 1676 */ setIsInternal()1677 public final void setIsInternal() { 1678 flags |= IS_INTERNAL; 1679 } 1680 1681 /** 1682 * Check if this script object is an internal object that should not be visible to script code. 1683 * @return true if internal 1684 */ isInternal()1685 public final boolean isInternal() { 1686 return (flags & IS_INTERNAL) != 0; 1687 } 1688 1689 /** 1690 * Clears the properties from a ScriptObject 1691 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1692 * 1693 * @param strict strict mode or not 1694 */ clear(final boolean strict)1695 public void clear(final boolean strict) { 1696 final Iterator<String> iter = propertyIterator(); 1697 while (iter.hasNext()) { 1698 delete(iter.next(), strict); 1699 } 1700 } 1701 1702 /** 1703 * Checks if a property with a given key is present in a ScriptObject 1704 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1705 * 1706 * @param key the key to check for 1707 * @return true if a property with the given key exists, false otherwise 1708 */ containsKey(final Object key)1709 public boolean containsKey(final Object key) { 1710 return has(key); 1711 } 1712 1713 /** 1714 * Checks if a property with a given value is present in a ScriptObject 1715 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1716 * 1717 * @param value value to check for 1718 * @return true if a property with the given value exists, false otherwise 1719 */ containsValue(final Object value)1720 public boolean containsValue(final Object value) { 1721 final Iterator<Object> iter = valueIterator(); 1722 while (iter.hasNext()) { 1723 if (iter.next().equals(value)) { 1724 return true; 1725 } 1726 } 1727 return false; 1728 } 1729 1730 /** 1731 * Returns the set of {@literal <property, value>} entries that make up this 1732 * ScriptObject's properties 1733 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1734 * 1735 * @return an entry set of all the properties in this object 1736 */ entrySet()1737 public Set<Map.Entry<Object, Object>> entrySet() { 1738 final Iterator<String> iter = propertyIterator(); 1739 final Set<Map.Entry<Object, Object>> entries = new HashSet<>(); 1740 while (iter.hasNext()) { 1741 final Object key = iter.next(); 1742 entries.add(new AbstractMap.SimpleImmutableEntry<>(key, get(key))); 1743 } 1744 return Collections.unmodifiableSet(entries); 1745 } 1746 1747 /** 1748 * Check whether a ScriptObject contains no properties 1749 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1750 * 1751 * @return true if object has no properties 1752 */ isEmpty()1753 public boolean isEmpty() { 1754 return !propertyIterator().hasNext(); 1755 } 1756 1757 /** 1758 * Return the set of keys (property names) for all properties 1759 * in this ScriptObject 1760 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1761 * 1762 * @return keySet of this ScriptObject 1763 */ keySet()1764 public Set<Object> keySet() { 1765 final Iterator<String> iter = propertyIterator(); 1766 final Set<Object> keySet = new HashSet<>(); 1767 while (iter.hasNext()) { 1768 keySet.add(iter.next()); 1769 } 1770 return Collections.unmodifiableSet(keySet); 1771 } 1772 1773 /** 1774 * Put a property in the ScriptObject 1775 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1776 * 1777 * @param key property key 1778 * @param value property value 1779 * @param strict strict mode or not 1780 * @return oldValue if property with same key existed already 1781 */ put(final Object key, final Object value, final boolean strict)1782 public Object put(final Object key, final Object value, final boolean strict) { 1783 final Object oldValue = get(key); 1784 final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0; 1785 set(key, value, scriptObjectFlags); 1786 return oldValue; 1787 } 1788 1789 /** 1790 * Put several properties in the ScriptObject given a mapping 1791 * of their keys to their values 1792 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1793 * 1794 * @param otherMap a {@literal <key,value>} map of properties to add 1795 * @param strict strict mode or not 1796 */ putAll(final Map<?, ?> otherMap, final boolean strict)1797 public void putAll(final Map<?, ?> otherMap, final boolean strict) { 1798 final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0; 1799 for (final Map.Entry<?, ?> entry : otherMap.entrySet()) { 1800 set(entry.getKey(), entry.getValue(), scriptObjectFlags); 1801 } 1802 } 1803 1804 /** 1805 * Remove a property from the ScriptObject. 1806 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1807 * 1808 * @param key the key of the property 1809 * @param strict strict mode or not 1810 * @return the oldValue of the removed property 1811 */ remove(final Object key, final boolean strict)1812 public Object remove(final Object key, final boolean strict) { 1813 final Object oldValue = get(key); 1814 delete(key, strict); 1815 return oldValue; 1816 } 1817 1818 /** 1819 * Return the size of the ScriptObject - i.e. the number of properties 1820 * it contains 1821 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1822 * 1823 * @return number of properties in ScriptObject 1824 */ size()1825 public int size() { 1826 int n = 0; 1827 for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) { 1828 n++; 1829 } 1830 return n; 1831 } 1832 1833 /** 1834 * Return the values of the properties in the ScriptObject 1835 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1836 * 1837 * @return collection of values for the properties in this ScriptObject 1838 */ values()1839 public Collection<Object> values() { 1840 final List<Object> values = new ArrayList<>(size()); 1841 final Iterator<Object> iter = valueIterator(); 1842 while (iter.hasNext()) { 1843 values.add(iter.next()); 1844 } 1845 return Collections.unmodifiableList(values); 1846 } 1847 1848 /** 1849 * Lookup method that, given a CallSiteDescriptor, looks up the target 1850 * MethodHandle and creates a GuardedInvocation 1851 * with the appropriate guard(s). 1852 * 1853 * @param desc call site descriptor 1854 * @param request the link request 1855 * 1856 * @return GuardedInvocation for the callsite 1857 */ lookup(final CallSiteDescriptor desc, final LinkRequest request)1858 public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) { 1859 // NOTE: we support GET:ELEMENT and SET:ELEMENT as JavaScript doesn't distinguish items from properties. Nashorn itself 1860 // emits "GET:PROPERTY|ELEMENT|METHOD:identifier" for "<expr>.<identifier>" and "GET:ELEMENT|PROPERTY|METHOD" for "<expr>[<expr>]", but we are 1861 // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the 1862 // operation has an associated name or not. 1863 switch (NashornCallSiteDescriptor.getStandardOperation(desc)) { 1864 case GET: 1865 return desc.getOperation() instanceof NamedOperation 1866 ? findGetMethod(desc, request) 1867 : findGetIndexMethod(desc, request); 1868 case SET: 1869 return desc.getOperation() instanceof NamedOperation 1870 ? findSetMethod(desc, request) 1871 : findSetIndexMethod(desc, request); 1872 case REMOVE: 1873 final GuardedInvocation inv = NashornCallSiteDescriptor.isStrict(desc) ? DELETE_GUARDED_STRICT : DELETE_GUARDED; 1874 final Object name = NamedOperation.getName(desc.getOperation()); 1875 if (name != null) { 1876 return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard()); 1877 } 1878 return inv; 1879 case CALL: 1880 return findCallMethod(desc, request); 1881 case NEW: 1882 return findNewMethod(desc, request); 1883 default: 1884 return null; 1885 } 1886 } 1887 1888 /** 1889 * Find the appropriate New method for an invoke dynamic call. 1890 * 1891 * @param desc The invoke dynamic call site descriptor. 1892 * @param request The link request 1893 * 1894 * @return GuardedInvocation to be invoked at call site. 1895 */ findNewMethod(final CallSiteDescriptor desc, final LinkRequest request)1896 protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) { 1897 return notAFunction(desc); 1898 } 1899 1900 /** 1901 * Find the appropriate CALL method for an invoke dynamic call. 1902 * This generates "not a function" always 1903 * 1904 * @param desc the call site descriptor. 1905 * @param request the link request 1906 * 1907 * @return GuardedInvocation to be invoked at call site. 1908 */ findCallMethod(final CallSiteDescriptor desc, final LinkRequest request)1909 protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { 1910 return notAFunction(desc); 1911 } 1912 notAFunction(final CallSiteDescriptor desc)1913 private GuardedInvocation notAFunction(final CallSiteDescriptor desc) { 1914 throw typeError("not.a.function", NashornCallSiteDescriptor.getFunctionErrorMessage(desc, this)); 1915 } 1916 1917 /** 1918 * Test whether this object contains in its prototype chain or is itself a with-object. 1919 * @return true if a with-object was found 1920 */ hasWithScope()1921 boolean hasWithScope() { 1922 return false; 1923 } 1924 1925 /** 1926 * Add a filter to the first argument of {@code methodHandle} that calls its {@link #getProto()} method 1927 * {@code depth} times. 1928 * @param methodHandle a method handle 1929 * @param depth distance to target prototype 1930 * @return the filtered method handle 1931 */ addProtoFilter(final MethodHandle methodHandle, final int depth)1932 static MethodHandle addProtoFilter(final MethodHandle methodHandle, final int depth) { 1933 if (depth == 0) { 1934 return methodHandle; 1935 } 1936 final int listIndex = depth - 1; // We don't need 0-deep walker 1937 MethodHandle filter = listIndex < PROTO_FILTERS.size() ? PROTO_FILTERS.get(listIndex) : null; 1938 1939 if (filter == null) { 1940 filter = addProtoFilter(GETPROTO, depth - 1); 1941 PROTO_FILTERS.add(null); 1942 PROTO_FILTERS.set(listIndex, filter); 1943 } 1944 1945 return MH.filterArguments(methodHandle, 0, filter.asType(filter.type().changeReturnType(methodHandle.type().parameterType(0)))); 1946 } 1947 1948 /** 1949 * Find the appropriate GET method for an invoke dynamic call. 1950 * 1951 * @param desc the call site descriptor 1952 * @param request the link request 1953 * 1954 * @return GuardedInvocation to be invoked at call site. 1955 */ 1956 protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request) { 1957 final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request); 1958 1959 String name = NashornCallSiteDescriptor.getOperand(desc); 1960 if (NashornCallSiteDescriptor.isApplyToCall(desc)) { 1961 if (Global.isBuiltinFunctionPrototypeApply()) { 1962 name = "call"; 1963 } 1964 } 1965 1966 if (request.isCallSiteUnstable() || hasWithScope()) { 1967 return findMegaMorphicGetMethod(desc, name, NashornCallSiteDescriptor.isMethodFirstOperation(desc)); 1968 } 1969 1970 final FindProperty find = findProperty(name, true, NashornCallSiteDescriptor.isScope(desc), this); 1971 MethodHandle mh; 1972 1973 if (find == null) { 1974 if (!NashornCallSiteDescriptor.isMethodFirstOperation(desc)) { 1975 return noSuchProperty(desc, request); 1976 } else { 1977 return noSuchMethod(desc, request); 1978 } 1979 } 1980 1981 final GlobalConstants globalConstants = getGlobalConstants(); 1982 if (globalConstants != null) { 1983 final GuardedInvocation cinv = globalConstants.findGetMethod(find, this, desc); 1984 if (cinv != null) { 1985 return cinv; 1986 } 1987 } 1988 1989 final Class<?> returnType = desc.getMethodType().returnType(); 1990 final Property property = find.getProperty(); 1991 1992 final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? 1993 NashornCallSiteDescriptor.getProgramPoint(desc) : 1994 UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 1995 1996 mh = find.getGetter(returnType, programPoint, request); 1997 // Get the appropriate guard for this callsite and property. 1998 final MethodHandle guard = NashornGuards.getGuard(this, property, desc, explicitInstanceOfCheck); 1999 final ScriptObject owner = find.getOwner(); 2000 final Class<ClassCastException> exception = explicitInstanceOfCheck ? null : ClassCastException.class; 2001 2002 final SwitchPoint[] protoSwitchPoints; 2003 2004 if (mh == null) { 2005 mh = Lookup.emptyGetter(returnType); 2006 protoSwitchPoints = getProtoSwitchPoints(name, owner); 2007 } else if (!find.isSelf()) { 2008 assert mh.type().returnType().equals(returnType) : 2009 "return type mismatch for getter " + mh.type().returnType() + " != " + returnType; 2010 if (!property.isAccessorProperty()) { 2011 // Add a filter that replaces the self object with the prototype owning the property. 2012 mh = addProtoFilter(mh, find.getProtoChainLength()); 2013 } 2014 protoSwitchPoints = getProtoSwitchPoints(name, owner); 2015 } else { 2016 protoSwitchPoints = null; 2017 } 2018 2019 final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoints, exception); 2020 return inv.addSwitchPoint(findBuiltinSwitchPoint(name)); 2021 } 2022 2023 private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) { 2024 Context.getContextTrusted().getLogger(ObjectClassGenerator.class).warning("Megamorphic getter: ", desc, " ", name + " ", isMethod); 2025 final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod, NashornCallSiteDescriptor.isScope(desc)); 2026 final MethodHandle guard = getScriptObjectGuard(desc.getMethodType(), true); 2027 return new GuardedInvocation(invoker, guard); 2028 } 2029 2030 @SuppressWarnings("unused") 2031 private Object megamorphicGet(final String key, final boolean isMethod, final boolean isScope) { 2032 final FindProperty find = findProperty(key, true, isScope, this); 2033 if (find != null) { 2034 // If this is a method invocation, and found property has a different self object then this, 2035 // then return a function bound to the self object. This is the case for functions in with expressions. 2036 final Object value = find.getObjectValue(); 2037 if (isMethod && value instanceof ScriptFunction && find.getSelf() != this && !find.getSelf().isInternal()) { 2038 return ((ScriptFunction) value).createBound(find.getSelf(), ScriptRuntime.EMPTY_ARRAY); 2039 } 2040 return value; 2041 } 2042 2043 return isMethod ? getNoSuchMethod(key, isScope, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, isScope, INVALID_PROGRAM_POINT); 2044 } 2045 2046 // Marks a property as declared and sets its value. Used as slow path for block-scoped LET and CONST 2047 @SuppressWarnings("unused") 2048 private void declareAndSet(final String key, final Object value) { 2049 declareAndSet(findProperty(key, false), value); 2050 } 2051 2052 private void declareAndSet(final FindProperty find, final Object value) { 2053 final PropertyMap oldMap = getMap(); 2054 assert find != null; 2055 2056 final Property property = find.getProperty(); 2057 assert property != null; 2058 assert property.needsDeclaration(); 2059 2060 final PropertyMap newMap = oldMap.replaceProperty(property, property.removeFlags(Property.NEEDS_DECLARATION)); 2061 setMap(newMap); 2062 set(property.getKey(), value, NashornCallSiteDescriptor.CALLSITE_DECLARE); 2063 } 2064 2065 /** 2066 * Find the appropriate GETINDEX method for an invoke dynamic call. 2067 * 2068 * @param desc the call site descriptor 2069 * @param request the link request 2070 * 2071 * @return GuardedInvocation to be invoked at call site. 2072 */ 2073 protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { 2074 final MethodType callType = desc.getMethodType(); 2075 final Class<?> returnType = callType.returnType(); 2076 final Class<?> returnClass = returnType.isPrimitive() ? returnType : Object.class; 2077 final Class<?> keyClass = callType.parameterType(1); 2078 final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request); 2079 2080 final String name; 2081 if (returnClass.isPrimitive()) { 2082 //turn e.g. get with a double into getDouble 2083 final String returnTypeName = returnClass.getName(); 2084 name = "get" + Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length()); 2085 } else { 2086 name = "get"; 2087 } 2088 2089 final MethodHandle mh = findGetIndexMethodHandle(returnClass, name, keyClass, desc); 2090 return new GuardedInvocation(mh, getScriptObjectGuard(callType, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class); 2091 } 2092 2093 private static MethodHandle getScriptObjectGuard(final MethodType type, final boolean explicitInstanceOfCheck) { 2094 return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard(explicitInstanceOfCheck); 2095 } 2096 2097 /** 2098 * Find a handle for a getIndex method 2099 * @param returnType return type for getter 2100 * @param name name 2101 * @param elementType index type for getter 2102 * @param desc call site descriptor 2103 * @return method handle for getter 2104 */ 2105 private static MethodHandle findGetIndexMethodHandle(final Class<?> returnType, final String name, final Class<?> elementType, final CallSiteDescriptor desc) { 2106 if (!returnType.isPrimitive()) { 2107 return findOwnMH_V(name, returnType, elementType); 2108 } 2109 2110 return MH.insertArguments( 2111 findOwnMH_V(name, returnType, elementType, int.class), 2112 2, 2113 NashornCallSiteDescriptor.isOptimistic(desc) ? 2114 NashornCallSiteDescriptor.getProgramPoint(desc) : 2115 INVALID_PROGRAM_POINT); 2116 } 2117 2118 /** 2119 * Get an array of switch points for a property with the given {@code name} that will be 2120 * invalidated when the property definition is changed in this object's prototype chain. 2121 * Returns {@code null} if the property is defined in this object itself. 2122 * 2123 * @param name the property name 2124 * @param owner the property owner, null if property is not defined 2125 * @return an array of SwitchPoints or null 2126 */ 2127 public final SwitchPoint[] getProtoSwitchPoints(final String name, final ScriptObject owner) { 2128 if (owner == this || getProto() == null) { 2129 return null; 2130 } 2131 2132 final Set<SwitchPoint> switchPoints = new HashSet<>(); 2133 SwitchPoint switchPoint = getProto().getMap().getSwitchPoint(name); 2134 2135 if (switchPoint == null) { 2136 switchPoint = new SwitchPoint(); 2137 for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) { 2138 obj.getProto().getMap().addSwitchPoint(name, switchPoint); 2139 } 2140 } 2141 2142 switchPoints.add(switchPoint); 2143 2144 for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) { 2145 final SwitchPoint sharedProtoSwitchPoint = obj.getProto().getMap().getSharedProtoSwitchPoint(); 2146 if (sharedProtoSwitchPoint != null && !sharedProtoSwitchPoint.hasBeenInvalidated()) { 2147 switchPoints.add(sharedProtoSwitchPoint); 2148 } 2149 } 2150 2151 return switchPoints.toArray(new SwitchPoint[0]); 2152 } 2153 2154 // Similar to getProtoSwitchPoints method above, but used for additional prototype switchpoints of 2155 // properties that are known not to exist, e.g. the original property name in a __noSuchProperty__ invocation. 2156 final SwitchPoint getProtoSwitchPoint(final String name) { 2157 if (getProto() == null) { 2158 return null; 2159 } 2160 2161 SwitchPoint switchPoint = getProto().getMap().getSwitchPoint(name); 2162 2163 if (switchPoint == null) { 2164 switchPoint = new SwitchPoint(); 2165 for (ScriptObject obj = this; obj.getProto() != null; obj = obj.getProto()) { 2166 obj.getProto().getMap().addSwitchPoint(name, switchPoint); 2167 } 2168 } 2169 2170 return switchPoint; 2171 } 2172 2173 private void checkSharedProtoMap() { 2174 // Check if our map has an expected shared prototype property map. If it has, make sure that 2175 // the prototype map has not been invalidated, and that it does match the actual map of the prototype. 2176 if (getMap().isInvalidSharedMapFor(getProto())) { 2177 // Change our own map to one that does not assume a shared prototype map. 2178 setMap(getMap().makeUnsharedCopy()); 2179 } 2180 } 2181 2182 /** 2183 * Find the appropriate SET method for an invoke dynamic call. 2184 * 2185 * @param desc the call site descriptor 2186 * @param request the link request 2187 * 2188 * @return GuardedInvocation to be invoked at call site. 2189 */ 2190 protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) { 2191 final String name = NashornCallSiteDescriptor.getOperand(desc); 2192 2193 if (request.isCallSiteUnstable() || hasWithScope()) { 2194 return findMegaMorphicSetMethod(desc, name); 2195 } 2196 2197 final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request); 2198 2199 /* 2200 * If doing property set on a scope object, we should stop proto search on the first 2201 * non-scope object. Without this, for example, when assigning "toString" on global scope, 2202 * we'll end up assigning it on it's proto - which is Object.prototype.toString !! 2203 * 2204 * toString = function() { print("global toString"); } // don't affect Object.prototype.toString 2205 */ 2206 FindProperty find = findProperty(name, true, NashornCallSiteDescriptor.isScope(desc), this); 2207 2208 // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors. 2209 if (find != null && find.isInheritedOrdinaryProperty()) { 2210 // We should still check if inherited data property is not writable 2211 if (isExtensible() && !find.getProperty().isWritable()) { 2212 return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true); 2213 } 2214 // Otherwise, forget the found property unless this is a scope callsite and the owner is a scope object as well. 2215 if (!NashornCallSiteDescriptor.isScope(desc) || !find.getOwner().isScope()) { 2216 find = null; 2217 } 2218 } 2219 2220 if (find != null) { 2221 if (!find.getProperty().isWritable() && !NashornCallSiteDescriptor.isDeclaration(desc)) { 2222 if (NashornCallSiteDescriptor.isScope(desc) && find.getProperty().isLexicalBinding()) { 2223 throw typeError("assign.constant", name); // Overwriting ES6 const should throw also in non-strict mode. 2224 } 2225 // Existing, non-writable data property 2226 return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true); 2227 } 2228 if (!find.getProperty().hasNativeSetter()) { 2229 // Existing accessor property without setter 2230 return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.has.no.setter", true); 2231 } 2232 } else { 2233 if (!isExtensible()) { 2234 return createEmptySetMethod(desc, explicitInstanceOfCheck, "object.non.extensible", false); 2235 } 2236 } 2237 2238 final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation(findBuiltinSwitchPoint(name)); 2239 2240 final GlobalConstants globalConstants = getGlobalConstants(); 2241 if (globalConstants != null) { 2242 final GuardedInvocation cinv = globalConstants.findSetMethod(find, this, inv, desc, request); 2243 if (cinv != null) { 2244 return cinv; 2245 } 2246 } 2247 2248 return inv; 2249 } 2250 2251 private GlobalConstants getGlobalConstants() { 2252 // Avoid hitting getContext() which might be costly for a non-Global unless needed. 2253 return GlobalConstants.GLOBAL_ONLY && !isGlobal() ? null : getContext().getGlobalConstants(); 2254 } 2255 2256 private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String strictErrorMessage, final boolean canBeFastScope) { 2257 final String name = NashornCallSiteDescriptor.getOperand(desc); 2258 if (NashornCallSiteDescriptor.isStrict(desc)) { 2259 throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString(this)); 2260 } 2261 assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc); 2262 return new GuardedInvocation( 2263 Lookup.EMPTY_SETTER, 2264 NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), 2265 getProtoSwitchPoints(name, null), 2266 explicitInstanceOfCheck ? null : ClassCastException.class); 2267 } 2268 2269 @SuppressWarnings("unused") 2270 private boolean extensionCheck(final boolean isStrict, final String name) { 2271 if (isExtensible()) { 2272 return true; //go on and do the set. this is our guard 2273 } else if (isStrict) { 2274 //throw an error for attempting to do the set in strict mode 2275 throw typeError("object.non.extensible", name, ScriptRuntime.safeToString(this)); 2276 } else { 2277 //not extensible, non strict - this is a nop 2278 return false; 2279 } 2280 } 2281 2282 private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) { 2283 Context.getContextTrusted().getLogger(ObjectClassGenerator.class).warning("Megamorphic setter: ", desc, " ", name); 2284 final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class); 2285 //never bother with ClassCastExceptionGuard for megamorphic callsites 2286 final GuardedInvocation inv = findSetIndexMethod(desc, false, type); 2287 return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard()); 2288 } 2289 2290 @SuppressWarnings("unused") 2291 private static Object globalFilter(final Object object) { 2292 ScriptObject sobj = (ScriptObject) object; 2293 while (sobj != null && !(sobj instanceof Global)) { 2294 sobj = sobj.getProto(); 2295 } 2296 return sobj; 2297 } 2298 2299 /** 2300 * Lookup function for the set index method, available for subclasses as well, e.g. {@link NativeArray} 2301 * provides special quick accessor linkage for continuous arrays that are represented as Java arrays 2302 * 2303 * @param desc call site descriptor 2304 * @param request link request 2305 * 2306 * @return GuardedInvocation to be invoked at call site. 2307 */ 2308 protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value 2309 return findSetIndexMethod(desc, explicitInstanceOfCheck(desc, request), desc.getMethodType()); 2310 } 2311 2312 /** 2313 * Find the appropriate SETINDEX method for an invoke dynamic call. 2314 * 2315 * @param desc the call site descriptor 2316 * @param explicitInstanceOfCheck add an explicit instanceof check? 2317 * @param callType the method type at the call site 2318 * 2319 * @return GuardedInvocation to be invoked at call site. 2320 */ 2321 private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final MethodType callType) { 2322 assert callType.parameterCount() == 3; 2323 final Class<?> keyClass = callType.parameterType(1); 2324 final Class<?> valueClass = callType.parameterType(2); 2325 2326 MethodHandle methodHandle = findOwnMH_V("set", void.class, keyClass, valueClass, int.class); 2327 methodHandle = MH.insertArguments(methodHandle, 3, NashornCallSiteDescriptor.getFlags(desc)); 2328 2329 return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class); 2330 } 2331 2332 /** 2333 * Fall back if a function property is not found. 2334 * @param desc The call site descriptor 2335 * @param request the link request 2336 * @return GuardedInvocation to be invoked at call site. 2337 */ 2338 public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) { 2339 final String name = NashornCallSiteDescriptor.getOperand(desc); 2340 final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true); 2341 final boolean scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc); 2342 2343 if (find == null) { 2344 return noSuchProperty(desc, request) 2345 // Add proto switchpoint to switch from no-such-property to no-such-method if it is ever defined. 2346 .addSwitchPoint(getProtoSwitchPoint(NO_SUCH_METHOD_NAME)); 2347 } 2348 2349 final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request); 2350 2351 final Object value = find.getObjectValue(); 2352 if (!(value instanceof ScriptFunction)) { 2353 return createEmptyGetter(desc, explicitInstanceOfCheck, name); 2354 } 2355 2356 final ScriptFunction func = (ScriptFunction)value; 2357 final Object thiz = scopeCall && func.isStrict() ? UNDEFINED : this; 2358 // TODO: It'd be awesome if we could bind "name" without binding "this". 2359 // Since we're binding this we must use an identity guard here. 2360 return new GuardedInvocation( 2361 MH.dropArguments( 2362 MH.constant( 2363 ScriptFunction.class, 2364 func.createBound(thiz, new Object[] { name })), 2365 0, 2366 Object.class), 2367 NashornGuards.combineGuards( 2368 NashornGuards.getIdentityGuard(this), 2369 NashornGuards.getMapGuard(getMap(), true))) 2370 // Add a protoype switchpoint for the original name so this gets invalidated if it is ever defined. 2371 .addSwitchPoint(getProtoSwitchPoint(name)); 2372 } 2373 2374 /** 2375 * Fall back if a property is not found. 2376 * @param desc the call site descriptor. 2377 * @param request the link request 2378 * @return GuardedInvocation to be invoked at call site. 2379 */ 2380 public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) { 2381 final String name = NashornCallSiteDescriptor.getOperand(desc); 2382 final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true); 2383 final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc); 2384 2385 if (find != null) { 2386 final Object value = find.getObjectValue(); 2387 ScriptFunction func = null; 2388 MethodHandle mh = null; 2389 2390 if (value instanceof ScriptFunction) { 2391 func = (ScriptFunction)value; 2392 mh = getCallMethodHandle(func, desc.getMethodType(), name); 2393 } 2394 2395 if (mh != null) { 2396 assert func != null; 2397 if (scopeAccess && func.isStrict()) { 2398 mh = bindTo(mh, UNDEFINED); 2399 } 2400 2401 return new GuardedInvocation( 2402 mh, 2403 find.isSelf()? 2404 getKnownFunctionPropertyGuardSelf( 2405 getMap(), 2406 find.getGetter(Object.class, INVALID_PROGRAM_POINT, request), 2407 func) 2408 : 2409 //TODO this always does a scriptobject check 2410 getKnownFunctionPropertyGuardProto( 2411 getMap(), 2412 find.getGetter(Object.class, INVALID_PROGRAM_POINT, request), 2413 find.getProtoChainLength(), 2414 func), 2415 getProtoSwitchPoints(NO_SUCH_PROPERTY_NAME, find.getOwner()), 2416 //TODO this doesn't need a ClassCastException as guard always checks script object 2417 null) 2418 // Add a protoype switchpoint for the original name so this gets invalidated if it is ever defined. 2419 .addSwitchPoint(getProtoSwitchPoint(name)); 2420 } 2421 } 2422 2423 if (scopeAccess) { 2424 throw referenceError("not.defined", name); 2425 } 2426 2427 return createEmptyGetter(desc, explicitInstanceOfCheck(desc, request), name); 2428 } 2429 2430 /** 2431 * Invoke fall back if a property is not found. 2432 * @param key Name of property. 2433 * @param isScope is this a scope access? 2434 * @param programPoint program point 2435 * @return Result from call. 2436 */ 2437 protected Object invokeNoSuchProperty(final Object key, final boolean isScope, final int programPoint) { 2438 final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true); 2439 final Object func = (find != null)? find.getObjectValue() : null; 2440 2441 Object ret = UNDEFINED; 2442 if (func instanceof ScriptFunction) { 2443 final ScriptFunction sfunc = (ScriptFunction)func; 2444 final Object self = isScope && sfunc.isStrict()? UNDEFINED : this; 2445 ret = ScriptRuntime.apply(sfunc, self, key); 2446 } else if (isScope) { 2447 throw referenceError("not.defined", key.toString()); 2448 } 2449 2450 if (isValid(programPoint)) { 2451 throw new UnwarrantedOptimismException(ret, programPoint); 2452 } 2453 2454 return ret; 2455 } 2456 2457 2458 /** 2459 * Get __noSuchMethod__ as a function bound to this object and {@code name} if it is defined. 2460 * @param name the method name 2461 * @param isScope is this a scope access? 2462 * @return the bound function, or undefined 2463 */ 2464 private Object getNoSuchMethod(final String name, final boolean isScope, final int programPoint) { 2465 final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true); 2466 2467 if (find == null) { 2468 return invokeNoSuchProperty(name, isScope, programPoint); 2469 } 2470 2471 final Object value = find.getObjectValue(); 2472 if (!(value instanceof ScriptFunction)) { 2473 if (isScope) { 2474 throw referenceError("not.defined", name); 2475 } 2476 return UNDEFINED; 2477 } 2478 2479 final ScriptFunction func = (ScriptFunction)value; 2480 final Object self = isScope && func.isStrict()? UNDEFINED : this; 2481 return func.createBound(self, new Object[] {name}); 2482 } 2483 2484 private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String name) { 2485 if (NashornCallSiteDescriptor.isOptimistic(desc)) { 2486 throw new UnwarrantedOptimismException(UNDEFINED, NashornCallSiteDescriptor.getProgramPoint(desc), Type.OBJECT); 2487 } 2488 2489 return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), 2490 NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoints(name, null), 2491 explicitInstanceOfCheck ? null : ClassCastException.class); 2492 } 2493 2494 private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> { 2495 protected T[] values; 2496 protected final ScriptObject object; 2497 private int index; 2498 2499 ScriptObjectIterator(final ScriptObject object) { 2500 this.object = object; 2501 } 2502 2503 protected abstract void init(); 2504 2505 @Override 2506 public boolean hasNext() { 2507 if (values == null) { 2508 init(); 2509 } 2510 return index < values.length; 2511 } 2512 2513 @Override 2514 public T next() { 2515 if (values == null) { 2516 init(); 2517 } 2518 return values[index++]; 2519 } 2520 2521 @Override 2522 public void remove() { 2523 throw new UnsupportedOperationException("remove"); 2524 } 2525 } 2526 2527 private static class KeyIterator extends ScriptObjectIterator<String> { 2528 KeyIterator(final ScriptObject object) { 2529 super(object); 2530 } 2531 2532 @Override 2533 protected void init() { 2534 final Set<String> keys = new LinkedHashSet<>(); 2535 final Set<String> nonEnumerable = new HashSet<>(); 2536 for (ScriptObject self = object; self != null; self = self.getProto()) { 2537 keys.addAll(Arrays.asList(self.getOwnKeys(String.class, false, nonEnumerable))); 2538 } 2539 this.values = keys.toArray(new String[0]); 2540 } 2541 } 2542 2543 private static class ValueIterator extends ScriptObjectIterator<Object> { 2544 ValueIterator(final ScriptObject object) { 2545 super(object); 2546 } 2547 2548 @Override 2549 protected void init() { 2550 final ArrayList<Object> valueList = new ArrayList<>(); 2551 final Set<String> nonEnumerable = new HashSet<>(); 2552 for (ScriptObject self = object; self != null; self = self.getProto()) { 2553 for (final String key : self.getOwnKeys(String.class, false, nonEnumerable)) { 2554 valueList.add(self.get(key)); 2555 } 2556 } 2557 this.values = valueList.toArray(new Object[0]); 2558 } 2559 } 2560 2561 /** 2562 * Add a spill property for the given key. 2563 * @param key Property key. 2564 * @param flags Property flags. 2565 * @return Added property. 2566 */ 2567 private Property addSpillProperty(final Object key, final int flags, final Object value, final boolean hasInitialValue) { 2568 final PropertyMap propertyMap = getMap(); 2569 final int fieldSlot = propertyMap.getFreeFieldSlot(); 2570 final int propertyFlags = flags | (useDualFields() ? Property.DUAL_FIELDS : 0); 2571 2572 Property property; 2573 if (fieldSlot > -1) { 2574 property = hasInitialValue ? 2575 new AccessorProperty(key, propertyFlags, fieldSlot, this, value) : 2576 new AccessorProperty(key, propertyFlags, getClass(), fieldSlot); 2577 property = addOwnProperty(property); 2578 } else { 2579 final int spillSlot = propertyMap.getFreeSpillSlot(); 2580 property = hasInitialValue ? 2581 new SpillProperty(key, propertyFlags, spillSlot, this, value) : 2582 new SpillProperty(key, propertyFlags, spillSlot); 2583 property = addOwnProperty(property); 2584 ensureSpillSize(property.getSlot()); 2585 } 2586 return property; 2587 } 2588 2589 /** 2590 * Add a spill entry for the given key. 2591 * @param key Property key. 2592 * @return Setter method handle. 2593 */ 2594 MethodHandle addSpill(final Class<?> type, final String key) { 2595 return addSpillProperty(key, 0, null, false).getSetter(type, getMap()); 2596 } 2597 2598 /** 2599 * Make sure arguments are paired correctly, with respect to more parameters than declared, 2600 * fewer parameters than declared and other things that JavaScript allows. This might involve 2601 * creating collectors. 2602 * 2603 * @param methodHandle method handle for invoke 2604 * @param callType type of the call 2605 * 2606 * @return method handle with adjusted arguments 2607 */ 2608 protected static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType) { 2609 return pairArguments(methodHandle, callType, null); 2610 } 2611 2612 /** 2613 * Make sure arguments are paired correctly, with respect to more parameters than declared, 2614 * fewer parameters than declared and other things that JavaScript allows. This might involve 2615 * creating collectors. 2616 * 2617 * Make sure arguments are paired correctly. 2618 * @param methodHandle MethodHandle to adjust. 2619 * @param callType MethodType of the call site. 2620 * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the 2621 * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a 2622 * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites 2623 * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters. 2624 * 2625 * @return method handle with adjusted arguments 2626 */ 2627 public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) { 2628 final MethodType methodType = methodHandle.type(); 2629 if (methodType.equals(callType.changeReturnType(methodType.returnType()))) { 2630 return methodHandle; 2631 } 2632 2633 final int parameterCount = methodType.parameterCount(); 2634 final int callCount = callType.parameterCount(); 2635 2636 final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray(); 2637 final boolean isCallerVarArg = callerVarArg != null ? callerVarArg : callCount > 0 && 2638 callType.parameterType(callCount - 1).isArray(); 2639 2640 if (isCalleeVarArg) { 2641 return isCallerVarArg ? 2642 methodHandle : 2643 MH.asCollector(methodHandle, Object[].class, callCount - parameterCount + 1); 2644 } 2645 2646 if (isCallerVarArg) { 2647 return adaptHandleToVarArgCallSite(methodHandle, callCount); 2648 } 2649 2650 if (callCount < parameterCount) { 2651 final int missingArgs = parameterCount - callCount; 2652 final Object[] fillers = new Object[missingArgs]; 2653 2654 Arrays.fill(fillers, UNDEFINED); 2655 2656 if (isCalleeVarArg) { 2657 fillers[missingArgs - 1] = ScriptRuntime.EMPTY_ARRAY; 2658 } 2659 2660 return MH.insertArguments( 2661 methodHandle, 2662 parameterCount - missingArgs, 2663 fillers); 2664 } 2665 2666 if (callCount > parameterCount) { 2667 final int discardedArgs = callCount - parameterCount; 2668 2669 final Class<?>[] discards = new Class<?>[discardedArgs]; 2670 Arrays.fill(discards, Object.class); 2671 2672 return MH.dropArguments(methodHandle, callCount - discardedArgs, discards); 2673 } 2674 2675 return methodHandle; 2676 } 2677 adaptHandleToVarArgCallSite(final MethodHandle mh, final int callSiteParamCount)2678 static MethodHandle adaptHandleToVarArgCallSite(final MethodHandle mh, final int callSiteParamCount) { 2679 final int spreadArgs = mh.type().parameterCount() - callSiteParamCount + 1; 2680 return MH.filterArguments( 2681 MH.asSpreader( 2682 mh, 2683 Object[].class, 2684 spreadArgs), 2685 callSiteParamCount - 1, 2686 MH.insertArguments( 2687 TRUNCATINGFILTER, 2688 0, 2689 spreadArgs) 2690 ); 2691 } 2692 2693 @SuppressWarnings("unused") truncatingFilter(final int n, final Object[] array)2694 private static Object[] truncatingFilter(final int n, final Object[] array) { 2695 final int length = array == null ? 0 : array.length; 2696 if (n == length) { 2697 return array == null ? ScriptRuntime.EMPTY_ARRAY : array; 2698 } 2699 2700 final Object[] newArray = new Object[n]; 2701 2702 if (array != null) { 2703 System.arraycopy(array, 0, newArray, 0, Math.min(n, length)); 2704 } 2705 2706 if (length < n) { 2707 final Object fill = UNDEFINED; 2708 2709 for (int i = length; i < n; i++) { 2710 newArray[i] = fill; 2711 } 2712 } 2713 2714 return newArray; 2715 } 2716 2717 /** 2718 * Numeric length setter for length property 2719 * 2720 * @param newLength new length to set 2721 */ setLength(final long newLength)2722 public final void setLength(final long newLength) { 2723 final ArrayData data = getArray(); 2724 final long arrayLength = data.length(); 2725 if (newLength == arrayLength) { 2726 return; 2727 } 2728 2729 if (newLength > arrayLength) { 2730 setArray(data.ensure(newLength - 1).safeDelete(arrayLength, newLength - 1, false)); 2731 return; 2732 } 2733 2734 if (newLength < arrayLength) { 2735 long actualLength = newLength; 2736 2737 // Check for numeric keys in property map and delete them or adjust length, depending on whether 2738 // they're defined as configurable. See ES5 #15.4.5.2 2739 if (getMap().containsArrayKeys()) { 2740 2741 for (long l = arrayLength - 1; l >= newLength; l--) { 2742 final FindProperty find = findProperty(JSType.toString(l), false); 2743 2744 if (find != null) { 2745 2746 if (find.getProperty().isConfigurable()) { 2747 deleteOwnProperty(find.getProperty()); 2748 } else { 2749 actualLength = l + 1; 2750 break; 2751 } 2752 } 2753 } 2754 } 2755 2756 setArray(data.shrink(actualLength)); 2757 data.setLength(actualLength); 2758 } 2759 } 2760 getInt(final int index, final Object key, final int programPoint)2761 private int getInt(final int index, final Object key, final int programPoint) { 2762 if (isValidArrayIndex(index)) { 2763 for (ScriptObject object = this; ; ) { 2764 if (object.getMap().containsArrayKeys()) { 2765 final FindProperty find = object.findProperty(key, false); 2766 2767 if (find != null) { 2768 return getIntValue(find, programPoint); 2769 } 2770 } 2771 2772 if ((object = object.getProto()) == null) { 2773 break; 2774 } 2775 2776 final ArrayData array = object.getArray(); 2777 2778 if (array.has(index)) { 2779 return isValid(programPoint) ? 2780 array.getIntOptimistic(index, programPoint) : 2781 array.getInt(index); 2782 } 2783 } 2784 } else { 2785 final FindProperty find = findProperty(key, true); 2786 2787 if (find != null) { 2788 return getIntValue(find, programPoint); 2789 } 2790 } 2791 2792 return JSType.toInt32(invokeNoSuchProperty(key, false, programPoint)); 2793 } 2794 2795 @Override getInt(final Object key, final int programPoint)2796 public int getInt(final Object key, final int programPoint) { 2797 final Object primitiveKey = JSType.toPrimitive(key, String.class); 2798 final int index = getArrayIndex(primitiveKey); 2799 final ArrayData array = getArray(); 2800 2801 if (array.has(index)) { 2802 return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index); 2803 } 2804 2805 return getInt(index, JSType.toPropertyKey(primitiveKey), programPoint); 2806 } 2807 2808 @Override getInt(final double key, final int programPoint)2809 public int getInt(final double key, final int programPoint) { 2810 final int index = getArrayIndex(key); 2811 final ArrayData array = getArray(); 2812 2813 if (array.has(index)) { 2814 return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index); 2815 } 2816 2817 return getInt(index, JSType.toString(key), programPoint); 2818 } 2819 2820 @Override getInt(final int key, final int programPoint)2821 public int getInt(final int key, final int programPoint) { 2822 final int index = getArrayIndex(key); 2823 final ArrayData array = getArray(); 2824 2825 if (array.has(index)) { 2826 return isValid(programPoint) ? array.getIntOptimistic(key, programPoint) : array.getInt(key); 2827 } 2828 2829 return getInt(index, JSType.toString(key), programPoint); 2830 } 2831 getDouble(final int index, final Object key, final int programPoint)2832 private double getDouble(final int index, final Object key, final int programPoint) { 2833 if (isValidArrayIndex(index)) { 2834 for (ScriptObject object = this; ; ) { 2835 if (object.getMap().containsArrayKeys()) { 2836 final FindProperty find = object.findProperty(key, false); 2837 if (find != null) { 2838 return getDoubleValue(find, programPoint); 2839 } 2840 } 2841 2842 if ((object = object.getProto()) == null) { 2843 break; 2844 } 2845 2846 final ArrayData array = object.getArray(); 2847 2848 if (array.has(index)) { 2849 return isValid(programPoint) ? 2850 array.getDoubleOptimistic(index, programPoint) : 2851 array.getDouble(index); 2852 } 2853 } 2854 } else { 2855 final FindProperty find = findProperty(key, true); 2856 2857 if (find != null) { 2858 return getDoubleValue(find, programPoint); 2859 } 2860 } 2861 2862 return JSType.toNumber(invokeNoSuchProperty(key, false, INVALID_PROGRAM_POINT)); 2863 } 2864 2865 @Override getDouble(final Object key, final int programPoint)2866 public double getDouble(final Object key, final int programPoint) { 2867 final Object primitiveKey = JSType.toPrimitive(key, String.class); 2868 final int index = getArrayIndex(primitiveKey); 2869 final ArrayData array = getArray(); 2870 2871 if (array.has(index)) { 2872 return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index); 2873 } 2874 2875 return getDouble(index, JSType.toPropertyKey(primitiveKey), programPoint); 2876 } 2877 2878 @Override getDouble(final double key, final int programPoint)2879 public double getDouble(final double key, final int programPoint) { 2880 final int index = getArrayIndex(key); 2881 final ArrayData array = getArray(); 2882 2883 if (array.has(index)) { 2884 return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index); 2885 } 2886 2887 return getDouble(index, JSType.toString(key), programPoint); 2888 } 2889 2890 @Override getDouble(final int key, final int programPoint)2891 public double getDouble(final int key, final int programPoint) { 2892 final int index = getArrayIndex(key); 2893 final ArrayData array = getArray(); 2894 2895 if (array.has(index)) { 2896 return isValid(programPoint) ? array.getDoubleOptimistic(key, programPoint) : array.getDouble(key); 2897 } 2898 2899 return getDouble(index, JSType.toString(key), programPoint); 2900 } 2901 get(final int index, final Object key)2902 private Object get(final int index, final Object key) { 2903 if (isValidArrayIndex(index)) { 2904 for (ScriptObject object = this; ; ) { 2905 if (object.getMap().containsArrayKeys()) { 2906 final FindProperty find = object.findProperty(key, false); 2907 2908 if (find != null) { 2909 return find.getObjectValue(); 2910 } 2911 } 2912 2913 if ((object = object.getProto()) == null) { 2914 break; 2915 } 2916 2917 final ArrayData array = object.getArray(); 2918 2919 if (array.has(index)) { 2920 return array.getObject(index); 2921 } 2922 } 2923 } else { 2924 final FindProperty find = findProperty(key, true); 2925 2926 if (find != null) { 2927 return find.getObjectValue(); 2928 } 2929 } 2930 2931 return invokeNoSuchProperty(key, false, INVALID_PROGRAM_POINT); 2932 } 2933 2934 @Override get(final Object key)2935 public Object get(final Object key) { 2936 final Object primitiveKey = JSType.toPrimitive(key, String.class); 2937 final int index = getArrayIndex(primitiveKey); 2938 final ArrayData array = getArray(); 2939 2940 if (array.has(index)) { 2941 return array.getObject(index); 2942 } 2943 2944 return get(index, JSType.toPropertyKey(primitiveKey)); 2945 } 2946 2947 @Override get(final double key)2948 public Object get(final double key) { 2949 final int index = getArrayIndex(key); 2950 final ArrayData array = getArray(); 2951 2952 if (array.has(index)) { 2953 return array.getObject(index); 2954 } 2955 2956 return get(index, JSType.toString(key)); 2957 } 2958 2959 @Override get(final int key)2960 public Object get(final int key) { 2961 final int index = getArrayIndex(key); 2962 final ArrayData array = getArray(); 2963 2964 if (array.has(index)) { 2965 return array.getObject(index); 2966 } 2967 2968 return get(index, JSType.toString(key)); 2969 } 2970 doesNotHaveCheckArrayKeys(final long longIndex, final int value, final int callSiteFlags)2971 private boolean doesNotHaveCheckArrayKeys(final long longIndex, final int value, final int callSiteFlags) { 2972 if (hasDefinedArrayProperties()) { 2973 final String key = JSType.toString(longIndex); 2974 final FindProperty find = findProperty(key, true); 2975 if (find != null) { 2976 setObject(find, callSiteFlags, key, value); 2977 return true; 2978 } 2979 } 2980 return false; 2981 } 2982 doesNotHaveCheckArrayKeys(final long longIndex, final double value, final int callSiteFlags)2983 private boolean doesNotHaveCheckArrayKeys(final long longIndex, final double value, final int callSiteFlags) { 2984 if (hasDefinedArrayProperties()) { 2985 final String key = JSType.toString(longIndex); 2986 final FindProperty find = findProperty(key, true); 2987 if (find != null) { 2988 setObject(find, callSiteFlags, key, value); 2989 return true; 2990 } 2991 } 2992 return false; 2993 } 2994 doesNotHaveCheckArrayKeys(final long longIndex, final Object value, final int callSiteFlags)2995 private boolean doesNotHaveCheckArrayKeys(final long longIndex, final Object value, final int callSiteFlags) { 2996 if (hasDefinedArrayProperties()) { 2997 final String key = JSType.toString(longIndex); 2998 final FindProperty find = findProperty(key, true); 2999 if (find != null) { 3000 setObject(find, callSiteFlags, key, value); 3001 return true; 3002 } 3003 } 3004 return false; 3005 } 3006 hasDefinedArrayProperties()3007 private boolean hasDefinedArrayProperties() { 3008 for (ScriptObject obj = this; obj != null; obj = obj.getProto()) { 3009 if (obj.getMap().containsArrayKeys()) { 3010 return true; 3011 } 3012 } 3013 return false; 3014 } 3015 3016 //value agnostic doesNotHaveEnsureLength(final long longIndex, final long oldLength, final int callSiteFlags)3017 private boolean doesNotHaveEnsureLength(final long longIndex, final long oldLength, final int callSiteFlags) { 3018 if (longIndex >= oldLength) { 3019 if (!isExtensible()) { 3020 if (isStrictFlag(callSiteFlags)) { 3021 throw typeError("object.non.extensible", JSType.toString(longIndex), ScriptRuntime.safeToString(this)); 3022 } 3023 return true; 3024 } 3025 setArray(getArray().ensure(longIndex)); 3026 } 3027 return false; 3028 } 3029 doesNotHave(final int index, final int value, final int callSiteFlags)3030 private void doesNotHave(final int index, final int value, final int callSiteFlags) { 3031 final long oldLength = getArray().length(); 3032 final long longIndex = ArrayIndex.toLongIndex(index); 3033 if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { 3034 final boolean strict = isStrictFlag(callSiteFlags); 3035 setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); 3036 } 3037 } 3038 doesNotHave(final int index, final double value, final int callSiteFlags)3039 private void doesNotHave(final int index, final double value, final int callSiteFlags) { 3040 final long oldLength = getArray().length(); 3041 final long longIndex = ArrayIndex.toLongIndex(index); 3042 if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { 3043 final boolean strict = isStrictFlag(callSiteFlags); 3044 setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); 3045 } 3046 } 3047 doesNotHave(final int index, final Object value, final int callSiteFlags)3048 private void doesNotHave(final int index, final Object value, final int callSiteFlags) { 3049 final long oldLength = getArray().length(); 3050 final long longIndex = ArrayIndex.toLongIndex(index); 3051 if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { 3052 final boolean strict = isStrictFlag(callSiteFlags); 3053 setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); 3054 } 3055 } 3056 3057 /** 3058 * This is the most generic of all Object setters. Most of the others use this in some form. 3059 * TODO: should be further specialized 3060 * 3061 * @param find found property 3062 * @param callSiteFlags callsite flags 3063 * @param key property key 3064 * @param value property value 3065 */ setObject(final FindProperty find, final int callSiteFlags, final Object key, final Object value)3066 public final void setObject(final FindProperty find, final int callSiteFlags, final Object key, final Object value) { 3067 FindProperty f = find; 3068 3069 invalidateGlobalConstant(key); 3070 3071 if (f != null && f.isInheritedOrdinaryProperty()) { 3072 final boolean isScope = isScopeFlag(callSiteFlags); 3073 // If the start object of the find is not this object it means the property was found inside a 3074 // 'with' statement expression (see WithObject.findProperty()). In this case we forward the 'set' 3075 // to the 'with' object. 3076 // Note that although a 'set' operation involving a with statement follows scope rules outside 3077 // the 'with' expression (the 'set' operation is performed on the owning prototype if it exists), 3078 // it follows non-scope rules inside the 'with' expression (set is performed on the top level object). 3079 // This is why we clear the callsite flags and FindProperty in the forward call to the 'with' object. 3080 if (isScope && f.getSelf() != this) { 3081 f.getSelf().setObject(null, 0, key, value); 3082 return; 3083 } 3084 // Setting a property should not modify the property in prototype unless this is a scope callsite 3085 // and the owner is a scope object as well (with the exception of 'with' statement handled above). 3086 if (!isScope || !f.getOwner().isScope()) { 3087 f = null; 3088 } 3089 } 3090 3091 if (f != null) { 3092 if ((!f.getProperty().isWritable() && !NashornCallSiteDescriptor.isDeclaration(callSiteFlags)) || !f.getProperty().hasNativeSetter()) { 3093 if (isScopeFlag(callSiteFlags) && f.getProperty().isLexicalBinding()) { 3094 throw typeError("assign.constant", key.toString()); // Overwriting ES6 const should throw also in non-strict mode. 3095 } 3096 if (isStrictFlag(callSiteFlags)) { 3097 throw typeError( 3098 f.getProperty().isAccessorProperty() ? "property.has.no.setter" : "property.not.writable", 3099 key.toString(), ScriptRuntime.safeToString(this)); 3100 } 3101 return; 3102 } 3103 3104 if (NashornCallSiteDescriptor.isDeclaration(callSiteFlags) && f.getProperty().needsDeclaration()) { 3105 f.getOwner().declareAndSet(f, value); 3106 return; 3107 } 3108 3109 f.setValue(value, isStrictFlag(callSiteFlags)); 3110 3111 } else if (!isExtensible()) { 3112 if (isStrictFlag(callSiteFlags)) { 3113 throw typeError("object.non.extensible", key.toString(), ScriptRuntime.safeToString(this)); 3114 } 3115 } else { 3116 ScriptObject sobj = this; 3117 // undefined scope properties are set in the global object. 3118 if (isScope()) { 3119 while (sobj != null && !(sobj instanceof Global)) { 3120 sobj = sobj.getProto(); 3121 } 3122 assert sobj != null : "no parent global object in scope"; 3123 } 3124 //this will unbox any Number object to its primitive type in case the 3125 //property supports primitive types, so it doesn't matter that it comes 3126 //in as an Object. 3127 sobj.addSpillProperty(key, 0, value, true); 3128 } 3129 } 3130 3131 @Override set(final Object key, final int value, final int callSiteFlags)3132 public void set(final Object key, final int value, final int callSiteFlags) { 3133 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3134 final int index = getArrayIndex(primitiveKey); 3135 3136 if (isValidArrayIndex(index)) { 3137 final ArrayData data = getArray(); 3138 if (data.has(index)) { 3139 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3140 } else { 3141 doesNotHave(index, value, callSiteFlags); 3142 } 3143 3144 return; 3145 } 3146 3147 final Object propName = JSType.toPropertyKey(primitiveKey); 3148 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3149 } 3150 3151 @Override set(final Object key, final double value, final int callSiteFlags)3152 public void set(final Object key, final double value, final int callSiteFlags) { 3153 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3154 final int index = getArrayIndex(primitiveKey); 3155 3156 if (isValidArrayIndex(index)) { 3157 final ArrayData data = getArray(); 3158 if (data.has(index)) { 3159 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3160 } else { 3161 doesNotHave(index, value, callSiteFlags); 3162 } 3163 3164 return; 3165 } 3166 3167 final Object propName = JSType.toPropertyKey(primitiveKey); 3168 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3169 } 3170 3171 @Override set(final Object key, final Object value, final int callSiteFlags)3172 public void set(final Object key, final Object value, final int callSiteFlags) { 3173 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3174 final int index = getArrayIndex(primitiveKey); 3175 3176 if (isValidArrayIndex(index)) { 3177 final ArrayData data = getArray(); 3178 if (data.has(index)) { 3179 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3180 } else { 3181 doesNotHave(index, value, callSiteFlags); 3182 } 3183 3184 return; 3185 } 3186 3187 final Object propName = JSType.toPropertyKey(primitiveKey); 3188 setObject(findProperty(propName, true), callSiteFlags, propName, value); 3189 } 3190 3191 @Override set(final double key, final int value, final int callSiteFlags)3192 public void set(final double key, final int value, final int callSiteFlags) { 3193 final int index = getArrayIndex(key); 3194 3195 if (isValidArrayIndex(index)) { 3196 final ArrayData data = getArray(); 3197 if (data.has(index)) { 3198 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3199 } else { 3200 doesNotHave(index, value, callSiteFlags); 3201 } 3202 3203 return; 3204 } 3205 3206 final String propName = JSType.toString(key); 3207 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3208 } 3209 3210 @Override set(final double key, final double value, final int callSiteFlags)3211 public void set(final double key, final double value, final int callSiteFlags) { 3212 final int index = getArrayIndex(key); 3213 3214 if (isValidArrayIndex(index)) { 3215 final ArrayData data = getArray(); 3216 if (data.has(index)) { 3217 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3218 } else { 3219 doesNotHave(index, value, callSiteFlags); 3220 } 3221 3222 return; 3223 } 3224 3225 final String propName = JSType.toString(key); 3226 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3227 } 3228 3229 @Override set(final double key, final Object value, final int callSiteFlags)3230 public void set(final double key, final Object value, final int callSiteFlags) { 3231 final int index = getArrayIndex(key); 3232 3233 if (isValidArrayIndex(index)) { 3234 final ArrayData data = getArray(); 3235 if (data.has(index)) { 3236 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3237 } else { 3238 doesNotHave(index, value, callSiteFlags); 3239 } 3240 3241 return; 3242 } 3243 3244 final String propName = JSType.toString(key); 3245 setObject(findProperty(propName, true), callSiteFlags, propName, value); 3246 } 3247 3248 @Override set(final int key, final int value, final int callSiteFlags)3249 public void set(final int key, final int value, final int callSiteFlags) { 3250 final int index = getArrayIndex(key); 3251 if (isValidArrayIndex(index)) { 3252 if (getArray().has(index)) { 3253 final ArrayData data = getArray(); 3254 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3255 } else { 3256 doesNotHave(index, value, callSiteFlags); 3257 } 3258 return; 3259 } 3260 3261 final String propName = JSType.toString(key); 3262 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3263 } 3264 3265 @Override set(final int key, final double value, final int callSiteFlags)3266 public void set(final int key, final double value, final int callSiteFlags) { 3267 final int index = getArrayIndex(key); 3268 3269 if (isValidArrayIndex(index)) { 3270 final ArrayData data = getArray(); 3271 if (data.has(index)) { 3272 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3273 } else { 3274 doesNotHave(index, value, callSiteFlags); 3275 } 3276 3277 return; 3278 } 3279 3280 final String propName = JSType.toString(key); 3281 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3282 } 3283 3284 @Override set(final int key, final Object value, final int callSiteFlags)3285 public void set(final int key, final Object value, final int callSiteFlags) { 3286 final int index = getArrayIndex(key); 3287 3288 if (isValidArrayIndex(index)) { 3289 final ArrayData data = getArray(); 3290 if (data.has(index)) { 3291 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3292 } else { 3293 doesNotHave(index, value, callSiteFlags); 3294 } 3295 3296 return; 3297 } 3298 3299 final String propName = JSType.toString(key); 3300 setObject(findProperty(propName, true), callSiteFlags, propName, value); 3301 } 3302 3303 @Override has(final Object key)3304 public boolean has(final Object key) { 3305 final Object primitiveKey = JSType.toPrimitive(key); 3306 final int index = getArrayIndex(primitiveKey); 3307 return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toPropertyKey(primitiveKey), true); 3308 } 3309 3310 @Override has(final double key)3311 public boolean has(final double key) { 3312 final int index = getArrayIndex(key); 3313 return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true); 3314 } 3315 3316 @Override has(final int key)3317 public boolean has(final int key) { 3318 final int index = getArrayIndex(key); 3319 return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true); 3320 } 3321 hasArrayProperty(final int index)3322 private boolean hasArrayProperty(final int index) { 3323 boolean hasArrayKeys = false; 3324 3325 for (ScriptObject self = this; self != null; self = self.getProto()) { 3326 if (self.getArray().has(index)) { 3327 return true; 3328 } 3329 hasArrayKeys = hasArrayKeys || self.getMap().containsArrayKeys(); 3330 } 3331 3332 return hasArrayKeys && hasProperty(ArrayIndex.toKey(index), true); 3333 } 3334 3335 @Override hasOwnProperty(final Object key)3336 public boolean hasOwnProperty(final Object key) { 3337 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3338 final int index = getArrayIndex(primitiveKey); 3339 return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toPropertyKey(primitiveKey), false); 3340 } 3341 3342 @Override hasOwnProperty(final int key)3343 public boolean hasOwnProperty(final int key) { 3344 final int index = getArrayIndex(key); 3345 return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false); 3346 } 3347 3348 @Override hasOwnProperty(final double key)3349 public boolean hasOwnProperty(final double key) { 3350 final int index = getArrayIndex(key); 3351 return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false); 3352 } 3353 hasOwnArrayProperty(final int index)3354 private boolean hasOwnArrayProperty(final int index) { 3355 return getArray().has(index) || getMap().containsArrayKeys() && hasProperty(ArrayIndex.toKey(index), false); 3356 } 3357 3358 @Override delete(final int key, final boolean strict)3359 public boolean delete(final int key, final boolean strict) { 3360 final int index = getArrayIndex(key); 3361 final ArrayData array = getArray(); 3362 3363 if (array.has(index)) { 3364 if (array.canDelete(index, strict)) { 3365 setArray(array.delete(index)); 3366 return true; 3367 } 3368 return false; 3369 } 3370 return deleteObject(JSType.toObject(key), strict); 3371 } 3372 3373 @Override delete(final double key, final boolean strict)3374 public boolean delete(final double key, final boolean strict) { 3375 final int index = getArrayIndex(key); 3376 final ArrayData array = getArray(); 3377 3378 if (array.has(index)) { 3379 if (array.canDelete(index, strict)) { 3380 setArray(array.delete(index)); 3381 return true; 3382 } 3383 return false; 3384 } 3385 3386 return deleteObject(JSType.toObject(key), strict); 3387 } 3388 3389 @Override delete(final Object key, final boolean strict)3390 public boolean delete(final Object key, final boolean strict) { 3391 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3392 final int index = getArrayIndex(primitiveKey); 3393 final ArrayData array = getArray(); 3394 3395 if (array.has(index)) { 3396 if (array.canDelete(index, strict)) { 3397 setArray(array.delete(index)); 3398 return true; 3399 } 3400 return false; 3401 } 3402 3403 return deleteObject(primitiveKey, strict); 3404 } 3405 deleteObject(final Object key, final boolean strict)3406 private boolean deleteObject(final Object key, final boolean strict) { 3407 final Object propName = JSType.toPropertyKey(key); 3408 final FindProperty find = findProperty(propName, false); 3409 3410 if (find == null) { 3411 return true; 3412 } 3413 3414 if (!find.getProperty().isConfigurable()) { 3415 if (strict) { 3416 throw typeError("cant.delete.property", propName.toString(), ScriptRuntime.safeToString(this)); 3417 } 3418 return false; 3419 } 3420 3421 final Property prop = find.getProperty(); 3422 deleteOwnProperty(prop); 3423 3424 return true; 3425 } 3426 3427 /** 3428 * Return a shallow copy of this ScriptObject. 3429 * @return a shallow copy. 3430 */ copy()3431 public final ScriptObject copy() { 3432 try { 3433 return clone(); 3434 } catch (final CloneNotSupportedException e) { 3435 throw new RuntimeException(e); 3436 } 3437 } 3438 3439 @Override clone()3440 protected ScriptObject clone() throws CloneNotSupportedException { 3441 final ScriptObject clone = (ScriptObject) super.clone(); 3442 if (objectSpill != null) { 3443 clone.objectSpill = objectSpill.clone(); 3444 if (primitiveSpill != null) { 3445 clone.primitiveSpill = primitiveSpill.clone(); 3446 } 3447 } 3448 clone.arrayData = arrayData.copy(); 3449 return clone; 3450 } 3451 3452 /** 3453 * Make a new UserAccessorProperty property. getter and setter functions are stored in 3454 * this ScriptObject and slot values are used in property object. 3455 * 3456 * @param key the property name 3457 * @param propertyFlags attribute flags of the property 3458 * @param getter getter function for the property 3459 * @param setter setter function for the property 3460 * @return the newly created UserAccessorProperty 3461 */ newUserAccessors(final Object key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter)3462 protected final UserAccessorProperty newUserAccessors(final Object key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { 3463 final UserAccessorProperty uc = getMap().newUserAccessors(key, propertyFlags); 3464 //property.getSetter(Object.class, getMap()); 3465 uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter)); 3466 return uc; 3467 } 3468 3469 /** 3470 * Returns {@code true} if properties for this object should use dual field mode, {@code false} otherwise. 3471 * @return {@code true} if dual fields should be used. 3472 */ useDualFields()3473 protected boolean useDualFields() { 3474 return !StructureLoader.isSingleFieldStructure(getClass().getName()); 3475 } 3476 ensureSpillSize(final int slot)3477 Object ensureSpillSize(final int slot) { 3478 final int oldLength = objectSpill == null ? 0 : objectSpill.length; 3479 if (slot < oldLength) { 3480 return this; 3481 } 3482 final int newLength = alignUp(slot + 1, SPILL_RATE); 3483 final Object[] newObjectSpill = new Object[newLength]; 3484 final long[] newPrimitiveSpill = useDualFields() ? new long[newLength] : null; 3485 3486 if (objectSpill != null) { 3487 System.arraycopy(objectSpill, 0, newObjectSpill, 0, oldLength); 3488 if (primitiveSpill != null && newPrimitiveSpill != null) { 3489 System.arraycopy(primitiveSpill, 0, newPrimitiveSpill, 0, oldLength); 3490 } 3491 } 3492 3493 this.primitiveSpill = newPrimitiveSpill; 3494 this.objectSpill = newObjectSpill; 3495 3496 return this; 3497 } 3498 findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types)3499 private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) { 3500 return MH.findVirtual(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types)); 3501 } 3502 findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types)3503 private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) { 3504 return MH.findStatic(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types)); 3505 } 3506 getKnownFunctionPropertyGuardSelf(final PropertyMap map, final MethodHandle getter, final ScriptFunction func)3507 private static MethodHandle getKnownFunctionPropertyGuardSelf(final PropertyMap map, final MethodHandle getter, final ScriptFunction func) { 3508 return MH.insertArguments(KNOWNFUNCPROPGUARDSELF, 1, map, getter, func); 3509 } 3510 3511 @SuppressWarnings("unused") knownFunctionPropertyGuardSelf(final Object self, final PropertyMap map, final MethodHandle getter, final ScriptFunction func)3512 private static boolean knownFunctionPropertyGuardSelf(final Object self, final PropertyMap map, final MethodHandle getter, final ScriptFunction func) { 3513 if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) { 3514 try { 3515 return getter.invokeExact(self) == func; 3516 } catch (final RuntimeException | Error e) { 3517 throw e; 3518 } catch (final Throwable t) { 3519 throw new RuntimeException(t); 3520 } 3521 } 3522 3523 return false; 3524 } 3525 getKnownFunctionPropertyGuardProto(final PropertyMap map, final MethodHandle getter, final int depth, final ScriptFunction func)3526 private static MethodHandle getKnownFunctionPropertyGuardProto(final PropertyMap map, final MethodHandle getter, final int depth, final ScriptFunction func) { 3527 return MH.insertArguments(KNOWNFUNCPROPGUARDPROTO, 1, map, getter, depth, func); 3528 } 3529 getProto(final ScriptObject self, final int depth)3530 private static ScriptObject getProto(final ScriptObject self, final int depth) { 3531 ScriptObject proto = self; 3532 for (int d = 0; d < depth; d++) { 3533 proto = proto.getProto(); 3534 if (proto == null) { 3535 return null; 3536 } 3537 } 3538 3539 return proto; 3540 } 3541 3542 @SuppressWarnings("unused") knownFunctionPropertyGuardProto(final Object self, final PropertyMap map, final MethodHandle getter, final int depth, final ScriptFunction func)3543 private static boolean knownFunctionPropertyGuardProto(final Object self, final PropertyMap map, final MethodHandle getter, final int depth, final ScriptFunction func) { 3544 if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) { 3545 final ScriptObject proto = getProto((ScriptObject)self, depth); 3546 if (proto == null) { 3547 return false; 3548 } 3549 try { 3550 return getter.invokeExact((Object)proto) == func; 3551 } catch (final RuntimeException | Error e) { 3552 throw e; 3553 } catch (final Throwable t) { 3554 throw new RuntimeException(t); 3555 } 3556 } 3557 3558 return false; 3559 } 3560 3561 /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */ 3562 private static LongAdder count; 3563 3564 static { 3565 if (Context.DEBUG) { 3566 count = new LongAdder(); 3567 } 3568 } 3569 /** 3570 * Get number of {@code ScriptObject} instances created. If not running in debug 3571 * mode this is always 0 3572 * 3573 * @return number of ScriptObjects created 3574 */ getCount()3575 public static long getCount() { 3576 return count.longValue(); 3577 } 3578 } 3579