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