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