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