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