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 */ 213 public ScriptObject() { 214 this(null); 215 } 216 217 /** 218 * Constructor 219 * 220 * @param map {@link PropertyMap} used to create the initial object 221 */ 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 */ 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 */ 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 */ 263 protected boolean isGlobal() { 264 return false; 265 } 266 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 1184 protected Context getContext() { 1185 return Context.fromClass(getClass()); 1186 } 1187 1188 /** 1189 * Return the map of an object. 1190 * @return PropertyMap object. 1191 */ 1192 public final PropertyMap getMap() { 1193 return map; 1194 } 1195 1196 /** 1197 * Set the initial map. 1198 * @param map Initial map. 1199 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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") 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 1514 public final boolean isArray() { 1515 return (flags & IS_ARRAY) != 0; 1516 } 1517 1518 /** 1519 * Flag this ScriptObject as being an array 1520 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 1647 public boolean isFrozen() { 1648 return getMap().isFrozen(); 1649 } 1650 1651 /** 1652 * Check whether this ScriptObject is scope 1653 * @return true if scope 1654 */ 1655 public boolean isScope() { 1656 return false; 1657 } 1658 1659 /** 1660 * Tag this script object as built in 1661 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 1909 protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { 1910 return notAFunction(desc); 1911 } 1912 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 */ 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 */ 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 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") 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 3354 private boolean hasOwnArrayProperty(final int index) { 3355 return getArray().has(index) || getMap().containsArrayKeys() && hasProperty(ArrayIndex.toKey(index), false); 3356 } 3357 3358 @Override 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 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 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 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 */ 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 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 */ 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 */ 3473 protected boolean useDualFields() { 3474 return !StructureLoader.isSingleFieldStructure(getClass().getName()); 3475 } 3476 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 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 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 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") 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 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 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") 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 */ 3575 public static long getCount() { 3576 return count.longValue(); 3577 } 3578 }