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