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