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