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