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