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