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 an array of own property keys associated with the object.
1112      *
1113      * @param all True if to include non-enumerable keys.
1114      * @return Array of keys.
1115      */
1116     public String[] getOwnKeys(final boolean all) {
1117         final List<Object> keys    = new ArrayList<>();
1118         final PropertyMap  selfMap = this.getMap();
1119 
1120         final ArrayData array  = getArray();
1121         final long length      = array.length();
1122 
1123         for (long i = 0; i < length; i = array.nextIndex(i)) {
1124             if (array.has((int)i)) {
1125                 keys.add(JSType.toString(i));
1126             }
1127         }
1128 
1129         for (final Property property : selfMap.getProperties()) {
1130             if (all || property.isEnumerable()) {
1131                 keys.add(property.getKey());
1132             }
1133         }
1134 
1135         return keys.toArray(new String[keys.size()]);
1136     }
1137 
1138     /**
1139      * Check if this ScriptObject has array entries. This means that someone has
1140      * set values with numeric keys in the object.
1141      *
1142      * Note: this can be O(n) up to the array length
1143      *
1144      * @return true if array entries exists.
1145      */
1146     public boolean hasArrayEntries() {
1147         final ArrayData array = getArray();
1148         final long length = array.length();
1149 
1150         for (long i = 0; i < length; i++) {
1151             if (array.has((int)i)) {
1152                 return true;
1153             }
1154         }
1155 
1156         return false;
1157     }
1158 
1159     /**
1160      * Return the valid JavaScript type name descriptor
1161      *
1162      * @return "Object"
1163      */
1164     public String getClassName() {
1165         return "Object";
1166     }
1167 
1168     /**
1169      * {@code length} is a well known property. This is its getter.
1170      * Note that this *may* be optimized by other classes
1171      *
1172      * @return length property value for this ScriptObject
1173      */
1174     public Object getLength() {
1175         return get("length");
1176     }
1177 
1178     /**
1179      * Stateless toString for ScriptObjects.
1180      *
1181      * @return string description of this object, e.g. {@code [object Object]}
1182      */
1183     public String safeToString() {
1184         return "[object " + getClassName() + "]";
1185     }
1186 
1187     /**
1188      * Return the default value of the object with a given preferred type hint.
1189      * The preferred type hints are String.class for type String, Number.class
1190      * for type Number. <p>
1191      *
1192      * A <code>hint</code> of null means "no hint".
1193      *
1194      * ECMA 8.12.8 [[DefaultValue]](hint)
1195      *
1196      * @param typeHint the preferred type hint
1197      * @return the default value
1198      */
1199     public Object getDefaultValue(final Class<?> typeHint) {
1200         // We delegate to GlobalObject, as the implementation uses dynamic call sites to invoke object's "toString" and
1201         // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts
1202         // are being executed in a long-running program, we move the code and their associated dynamic call sites
1203         // (Global.TO_STRING and Global.VALUE_OF) into per-context code.
1204         return ((GlobalObject)Context.getGlobalTrusted()).getDefaultValue(this, typeHint);
1205     }
1206 
1207     /**
1208      * Checking whether a script object is an instance of another. Used
1209      * in {@link ScriptFunction} for hasInstance implementation, walks
1210      * the proto chain
1211      *
1212      * @param instance instace to check
1213      * @return true if 'instance' is an instance of this object
1214      */
1215     public boolean isInstance(final ScriptObject instance) {
1216         return false;
1217     }
1218 
1219     /**
1220      * Flag this ScriptObject as non extensible
1221      *
1222      * @return the object after being made non extensible
1223      */
1224     public ScriptObject preventExtensions() {
1225         PropertyMap oldMap = getMap();
1226 
1227         while (true) {
1228             final PropertyMap newMap = getMap().preventExtensions();
1229 
1230             if (!compareAndSetMap(oldMap, newMap)) {
1231                 oldMap = getMap();
1232             } else {
1233                 return this;
1234             }
1235         }
1236     }
1237 
1238     /**
1239      * Check whether if an Object (not just a ScriptObject) represents JavaScript array
1240      *
1241      * @param obj object to check
1242      *
1243      * @return true if array
1244      */
1245     public static boolean isArray(final Object obj) {
1246         return (obj instanceof ScriptObject) && ((ScriptObject)obj).isArray();
1247     }
1248 
1249     /**
1250      * Check if this ScriptObject is an array
1251      * @return true if array
1252      */
1253     public final boolean isArray() {
1254         return (flags & IS_ARRAY) != 0;
1255     }
1256 
1257     /**
1258      * Flag this ScriptObject as being an array
1259      */
1260     public final void setIsArray() {
1261         flags |= IS_ARRAY;
1262     }
1263 
1264     /**
1265      * Check if this ScriptObject is an {@code arguments} vector
1266      * @return true if arguments vector
1267      */
1268     public final boolean isArguments() {
1269         return (flags & IS_ARGUMENTS) != 0;
1270     }
1271 
1272     /**
1273      * Flag this ScriptObject as being an {@code arguments} vector
1274      */
1275     public final void setIsArguments() {
1276         flags |= IS_ARGUMENTS;
1277     }
1278 
1279     /**
1280      * Check if this object is a prototype
1281      *
1282      * @return {@code true} if is prototype
1283      */
1284     public final boolean isPrototype() {
1285         return (flags & IS_PROTOTYPE) != 0;
1286     }
1287 
1288     /**
1289      * Flag this object as having a prototype.
1290      */
1291     public final void setIsPrototype() {
1292         if (proto != null && !isPrototype()) {
1293             proto.addPropertyListener(this);
1294         }
1295         flags |= IS_PROTOTYPE;
1296     }
1297 
1298     /**
1299      * Check if this object has non-writable length property
1300      *
1301      * @return {@code true} if 'length' property is non-writable
1302      */
1303     public final boolean isLengthNotWritable() {
1304         return (flags & IS_LENGTH_NOT_WRITABLE) != 0;
1305     }
1306 
1307     /**
1308      * Flag this object as having non-writable length property
1309      */
1310     public void setIsLengthNotWritable() {
1311         flags |= IS_LENGTH_NOT_WRITABLE;
1312     }
1313 
1314     /**
1315      * Get the {@link ArrayData} for this ScriptObject if it is an array
1316      * @return array data
1317      */
1318     public final ArrayData getArray() {
1319         return arrayData;
1320     }
1321 
1322     /**
1323      * Set the {@link ArrayData} for this ScriptObject if it is to be an array
1324      * @param arrayData the array data
1325      */
1326     public final void setArray(final ArrayData arrayData) {
1327         this.arrayData = arrayData;
1328     }
1329 
1330     /**
1331      * Check if this ScriptObject is extensible
1332      * @return true if extensible
1333      */
1334     public boolean isExtensible() {
1335         return getMap().isExtensible();
1336     }
1337 
1338     /**
1339      * ECMAScript 15.2.3.8 - seal implementation
1340      * @return the sealed ScriptObject
1341      */
1342     public ScriptObject seal() {
1343         PropertyMap oldMap = getMap();
1344 
1345         while (true) {
1346             final PropertyMap newMap = getMap().seal();
1347 
1348             if (!compareAndSetMap(oldMap, newMap)) {
1349                 oldMap = getMap();
1350             } else {
1351                 setArray(ArrayData.seal(getArray()));
1352                 return this;
1353             }
1354         }
1355     }
1356 
1357     /**
1358      * Check whether this ScriptObject is sealed
1359      * @return true if sealed
1360      */
1361     public boolean isSealed() {
1362         return getMap().isSealed();
1363     }
1364 
1365     /**
1366      * ECMA 15.2.39 - freeze implementation. Freeze this ScriptObject
1367      * @return the frozen ScriptObject
1368      */
1369     public ScriptObject freeze() {
1370         PropertyMap oldMap = getMap();
1371 
1372         while (true) {
1373             final PropertyMap newMap = getMap().freeze();
1374 
1375             if (!compareAndSetMap(oldMap, newMap)) {
1376                 oldMap = getMap();
1377             } else {
1378                 setArray(ArrayData.freeze(getArray()));
1379                 return this;
1380             }
1381         }
1382     }
1383 
1384     /**
1385      * Check whether this ScriptObject is frozen
1386      * @return true if frozen
1387      */
1388     public boolean isFrozen() {
1389         return getMap().isFrozen();
1390     }
1391 
1392 
1393     /**
1394      * Flag this ScriptObject as scope
1395      */
1396     public final void setIsScope() {
1397         if (Context.DEBUG) {
1398             scopeCount++;
1399         }
1400         flags |= IS_SCOPE;
1401     }
1402 
1403     /**
1404      * Check whether this ScriptObject is scope
1405      * @return true if scope
1406      */
1407     public final boolean isScope() {
1408         return (flags & IS_SCOPE) != 0;
1409     }
1410 
1411     /**
1412      * Clears the properties from a ScriptObject
1413      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1414      */
1415     public void clear() {
1416         final boolean strict = isStrictContext();
1417         final Iterator<String> iter = propertyIterator();
1418         while (iter.hasNext()) {
1419             delete(iter.next(), strict);
1420         }
1421     }
1422 
1423     /**
1424      * Checks if a property with a given key is present in a ScriptObject
1425      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1426      *
1427      * @param key the key to check for
1428      * @return true if a property with the given key exists, false otherwise
1429      */
1430     public boolean containsKey(final Object key) {
1431         return has(key);
1432     }
1433 
1434     /**
1435      * Checks if a property with a given value is present in a ScriptObject
1436      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1437      *
1438      * @param value value to check for
1439      * @return true if a property with the given value exists, false otherwise
1440      */
1441     public boolean containsValue(final Object value) {
1442         final Iterator<Object> iter = valueIterator();
1443         while (iter.hasNext()) {
1444             if (iter.next().equals(value)) {
1445                 return true;
1446             }
1447         }
1448         return false;
1449     }
1450 
1451     /**
1452      * Returns the set of {@literal <property, value>} entries that make up this
1453      * ScriptObject's properties
1454      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1455      *
1456      * @return an entry set of all the properties in this object
1457      */
1458     public Set<Map.Entry<Object, Object>> entrySet() {
1459         final Iterator<String> iter = propertyIterator();
1460         final Set<Map.Entry<Object, Object>> entries = new HashSet<>();
1461         while (iter.hasNext()) {
1462             final Object key = iter.next();
1463             entries.add(new AbstractMap.SimpleImmutableEntry<>(key, get(key)));
1464         }
1465         return Collections.unmodifiableSet(entries);
1466     }
1467 
1468     /**
1469      * Check whether a ScriptObject contains no properties
1470      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1471      *
1472      * @return true if object has no properties
1473      */
1474     public boolean isEmpty() {
1475         return !propertyIterator().hasNext();
1476     }
1477 
1478     /**
1479      * Return the set of keys (property names) for all properties
1480      * in this ScriptObject
1481      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1482      *
1483      * @return keySet of this ScriptObject
1484      */
1485     public Set<Object> keySet() {
1486         final Iterator<String> iter = propertyIterator();
1487         final Set<Object> keySet = new HashSet<>();
1488         while (iter.hasNext()) {
1489             keySet.add(iter.next());
1490         }
1491         return Collections.unmodifiableSet(keySet);
1492     }
1493 
1494     /**
1495      * Put a property in the ScriptObject
1496      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1497      *
1498      * @param key property key
1499      * @param value property value
1500      * @return oldValue if property with same key existed already
1501      */
1502     public Object put(final Object key, final Object value) {
1503         final Object oldValue = get(key);
1504         set(key, value, isStrictContext());
1505         return oldValue;
1506     }
1507 
1508     /**
1509      * Put several properties in the ScriptObject given a mapping
1510      * of their keys to their values
1511      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1512      *
1513      * @param otherMap a {@literal <key,value>} map of properties to add
1514      */
1515     public void putAll(final Map<?, ?> otherMap) {
1516         final boolean strict = isStrictContext();
1517         for (final Map.Entry<?, ?> entry : otherMap.entrySet()) {
1518             set(entry.getKey(), entry.getValue(), strict);
1519         }
1520     }
1521 
1522     /**
1523      * Remove a property from the ScriptObject.
1524      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1525      *
1526      * @param key the key of the property
1527      * @return the oldValue of the removed property
1528      */
1529     public Object remove(final Object key) {
1530         final Object oldValue = get(key);
1531         delete(key, isStrictContext());
1532         return oldValue;
1533     }
1534 
1535     /**
1536      * Delete a property from the ScriptObject.
1537      * (to help ScriptObjectMirror implementation)
1538      *
1539      * @param key the key of the property
1540      * @return if the delete was successful or not
1541      */
1542     public boolean delete(final Object key) {
1543         return delete(key, isStrictContext());
1544     }
1545 
1546     /**
1547      * Return the size of the ScriptObject - i.e. the number of properties
1548      * it contains
1549      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1550      *
1551      * @return number of properties in ScriptObject
1552      */
1553     public int size() {
1554         int n = 0;
1555         for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) {
1556             n++;
1557         }
1558         return n;
1559     }
1560 
1561     /**
1562      * Return the values of the properties in the ScriptObject
1563      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1564      *
1565      * @return collection of values for the properties in this ScriptObject
1566      */
1567     public Collection<Object> values() {
1568         final List<Object>     values = new ArrayList<>(size());
1569         final Iterator<Object> iter   = valueIterator();
1570         while (iter.hasNext()) {
1571             values.add(iter.next());
1572         }
1573         return Collections.unmodifiableList(values);
1574     }
1575 
1576     /**
1577      * Lookup method that, given a CallSiteDescriptor, looks up the target
1578      * MethodHandle and creates a GuardedInvocation
1579      * with the appropriate guard(s).
1580      *
1581      * @param desc call site descriptor
1582      * @param request the link request
1583      *
1584      * @return GuardedInvocation for the callsite
1585      */
1586     public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
1587         final int c = desc.getNameTokenCount();
1588         // JavaScript is "immune" to all currently defined Dynalink composite operation - getProp is the same as getElem
1589         // is the same as getMethod as JavaScript objects have a single namespace for all three. Therefore, we don't
1590         // care about them, and just link to whatever is the first operation.
1591         final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
1592         // NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself
1593         // emits "dyn:getProp:identifier" for "<expr>.<identifier>" and "dyn:getElem" for "<expr>[<expr>]", but we are
1594         // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the
1595         // operation has an associated name or not.
1596         switch (operator) {
1597         case "getProp":
1598         case "getElem":
1599         case "getMethod":
1600             return c > 2 ? findGetMethod(desc, request, operator) : findGetIndexMethod(desc, request);
1601         case "setProp":
1602         case "setElem":
1603             return c > 2 ? findSetMethod(desc, request) : findSetIndexMethod(desc);
1604         case "call":
1605             return findCallMethod(desc, request);
1606         case "new":
1607             return findNewMethod(desc);
1608         case "callMethod":
1609             return findCallMethodMethod(desc, request);
1610         default:
1611             return null;
1612         }
1613     }
1614 
1615     /**
1616      * Find the appropriate New method for an invoke dynamic call.
1617      *
1618      * @param desc The invoke dynamic call site descriptor.
1619      *
1620      * @return GuardedInvocation to be invoked at call site.
1621      */
1622     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
1623         return notAFunction();
1624     }
1625 
1626     /**
1627      * Find the appropriate CALL method for an invoke dynamic call.
1628      * This generates "not a function" always
1629      *
1630      * @param desc    the call site descriptor.
1631      * @param request the link request
1632      *
1633      * @return GuardedInvocation to be invoed at call site.
1634      */
1635     protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1636         return notAFunction();
1637     }
1638 
1639     private GuardedInvocation notAFunction() {
1640         throw typeError("not.a.function", ScriptRuntime.safeToString(this));
1641     }
1642 
1643     /**
1644      * Find an implementation for a "dyn:callMethod" operation. Note that Nashorn internally never uses
1645      * "dyn:callMethod", but instead always emits two call sites in bytecode, one for "dyn:getMethod", and then another
1646      * one for "dyn:call". Explicit support for "dyn:callMethod" is provided for the benefit of potential external
1647      * callers. The implementation itself actually folds a "dyn:getMethod" method handle into a "dyn:call" method handle.
1648      *
1649      * @param desc    the call site descriptor.
1650      * @param request the link request
1651      *
1652      * @return GuardedInvocation to be invoked at call site.
1653      */
1654     protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1655         // R(P0, P1, ...)
1656         final MethodType callType = desc.getMethodType();
1657         // use type Object(P0) for the getter
1658         final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0)));
1659         final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod");
1660 
1661         // Object(P0) => Object(P0, P1, ...)
1662         final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount()));
1663         // R(Object, P0, P1, ...)
1664         final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
1665         // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
1666         return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
1667     }
1668 
1669     /**
1670      * Find the appropriate GET method for an invoke dynamic call.
1671      *
1672      * @param desc     the call site descriptor
1673      * @param request  the link request
1674      * @param operator operator for get: getProp, getMethod, getElem etc
1675      *
1676      * @return GuardedInvocation to be invoked at call site.
1677      */
1678     protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
1679         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1680         final FindProperty find = findProperty(name, true);
1681 
1682         MethodHandle methodHandle;
1683 
1684         if (find == null) {
1685             if ("getProp".equals(operator)) {
1686                 return noSuchProperty(desc, request);
1687             } else if ("getMethod".equals(operator)) {
1688                 return noSuchMethod(desc, request);
1689             } else if ("getElem".equals(operator)) {
1690                 return createEmptyGetter(desc, name);
1691             }
1692             throw new AssertionError(); // never invoked with any other operation
1693         }
1694 
1695         if (request.isCallSiteUnstable()) {
1696             return findMegaMorphicGetMethod(desc, name);
1697         }
1698 
1699         final Class<?> returnType = desc.getMethodType().returnType();
1700         final Property property = find.getProperty();
1701         methodHandle = find.getGetter(returnType);
1702 
1703         // getMap() is fine as we have the prototype switchpoint depending on where the property was found
1704         final MethodHandle guard = NashornGuards.getMapGuard(getMap());
1705 
1706         if (methodHandle != null) {
1707             assert methodHandle.type().returnType().equals(returnType);
1708             if (find.isSelf()) {
1709                 return new GuardedInvocation(methodHandle, ObjectClassGenerator.OBJECT_FIELDS_ONLY &&
1710                         NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType() ? null : guard);
1711             }
1712 
1713             final ScriptObject prototype = find.getOwner();
1714 
1715             if (!property.hasGetterFunction()) {
1716                 methodHandle = bindTo(methodHandle, prototype);
1717             }
1718             return new GuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(proto, name), guard);
1719         }
1720 
1721         assert !NashornCallSiteDescriptor.isFastScope(desc);
1722         return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard);
1723     }
1724 
1725     private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) {
1726         final MethodType mhType = desc.getMethodType().insertParameterTypes(1, Object.class);
1727         final GuardedInvocation inv = findGetIndexMethod(mhType);
1728 
1729         return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
1730     }
1731 
1732     /**
1733      * Find the appropriate GETINDEX method for an invoke dynamic call.
1734      *
1735      * @param desc    the call site descriptor
1736      * @param request the link request
1737      *
1738      * @return GuardedInvocation to be invoked at call site.
1739      */
1740     protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1741         return findGetIndexMethod(desc.getMethodType());
1742     }
1743 
1744     /**
1745      * Find the appropriate GETINDEX method for an invoke dynamic call.
1746      *
1747      * @param callType the call site method type
1748      * @return GuardedInvocation to be invoked at call site.
1749      */
1750     private static GuardedInvocation findGetIndexMethod(final MethodType callType) {
1751         final Class<?> returnClass = callType.returnType();
1752         final Class<?> keyClass    = callType.parameterType(1);
1753 
1754         String name = "get";
1755         if (returnClass.isPrimitive()) {
1756             //turn e.g. get with a double into getDouble
1757             final String returnTypeName = returnClass.getName();
1758             name += Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
1759         }
1760 
1761         return new GuardedInvocation(findOwnMH(name, returnClass, keyClass), getScriptObjectGuard(callType));
1762     }
1763 
1764     private static MethodHandle getScriptObjectGuard(final MethodType type) {
1765         return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard();
1766     }
1767 
1768     /**
1769      * Find the appropriate SET method for an invoke dynamic call.
1770      *
1771      * @param desc    the call site descriptor
1772      * @param request the link request
1773      *
1774      * @return GuardedInvocation to be invoked at call site.
1775      */
1776     protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1777         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1778         if(request.isCallSiteUnstable()) {
1779             return findMegaMorphicSetMethod(desc, name);
1780         }
1781 
1782         final boolean scope = isScope();
1783         /*
1784          * If doing property set on a scope object, we should stop proto search on the first
1785          * non-scope object. Without this, for example, when assigning "toString" on global scope,
1786          * we'll end up assigning it on it's proto - which is Object.prototype.toString !!
1787          *
1788          * toString = function() { print("global toString"); } // don't affect Object.prototype.toString
1789          */
1790         FindProperty find = findProperty(name, true, scope, this);
1791         // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors.
1792         if (!scope && find != null && find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
1793             // We should still check if inherited data property is not writable
1794             if (isExtensible() && !find.getProperty().isWritable()) {
1795                 return createEmptySetMethod(desc, "property.not.writable", false);
1796             }
1797             // Otherwise, forget the found property
1798             find = null;
1799         }
1800 
1801         if (find != null) {
1802             if(!find.getProperty().isWritable()) {
1803                 // Existing, non-writable property
1804                 return createEmptySetMethod(desc, "property.not.writable", true);
1805             }
1806         } else if (!isExtensible()) {
1807             // Non-existing property on a non-extensible object
1808             return createEmptySetMethod(desc, "object.non.extensible", false);
1809         }
1810 
1811         return new SetMethodCreator(this, find, desc).createGuardedInvocation();
1812     }
1813 
1814     private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, String strictErrorMessage, boolean canBeFastScope) {
1815         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1816         if (NashornCallSiteDescriptor.isStrict(desc)) {
1817                throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString((this)));
1818            }
1819            assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
1820            final PropertyMap myMap = getMap();
1821            return new GuardedInvocation(Lookup.EMPTY_SETTER, myMap.getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(myMap));
1822     }
1823 
1824     @SuppressWarnings("unused")
1825     private static void setField(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final MethodHandle setter, final Object self, final Object value) throws Throwable {
1826         final ScriptObject obj = (ScriptObject)self;
1827         final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
1828         if (!obj.isExtensible()) {
1829             throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
1830         } else if (obj.compareAndSetMap(oldMap, newMap)) {
1831             setter.invokeExact(self, value);
1832         } else {
1833             obj.set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
1834         }
1835     }
1836 
1837     @SuppressWarnings("unused")
1838     private static void setSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
1839         final ScriptObject obj = (ScriptObject)self;
1840         if (obj.trySetSpill(desc, oldMap, newMap, value)) {
1841             obj.spill[index] = value;
1842         }
1843     }
1844 
1845     private boolean trySetSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final Object value) {
1846         final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
1847         if (!isExtensible() && isStrict) {
1848             throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(this));
1849         } else if (compareAndSetMap(oldMap, newMap)) {
1850             return true;
1851         } else {
1852             set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
1853             return false;
1854         }
1855     }
1856 
1857     @SuppressWarnings("unused")
1858     private static void setSpillWithNew(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
1859         final ScriptObject obj      = (ScriptObject)self;
1860         final boolean      isStrict = NashornCallSiteDescriptor.isStrict(desc);
1861 
1862         if (!obj.isExtensible()) {
1863             if (isStrict) {
1864                 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
1865             }
1866         } else if (obj.compareAndSetMap(oldMap, newMap)) {
1867             obj.spill = new Object[SPILL_RATE];
1868             obj.spill[index] = value;
1869         } else {
1870             obj.set(desc.getNameToken(2), value, isStrict);
1871         }
1872     }
1873 
1874     @SuppressWarnings("unused")
1875     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) {
1876         final ScriptObject obj      = (ScriptObject)self;
1877         final boolean      isStrict = NashornCallSiteDescriptor.isStrict(desc);
1878 
1879         if (!obj.isExtensible()) {
1880             if (isStrict) {
1881                 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
1882             }
1883         } else if (obj.compareAndSetMap(oldMap, newMap)) {
1884             final int oldLength = obj.spill.length;
1885             final Object[] newSpill = new Object[newLength];
1886             System.arraycopy(obj.spill, 0, newSpill, 0, oldLength);
1887             obj.spill = newSpill;
1888             obj.spill[index] = value;
1889         } else {
1890             obj.set(desc.getNameToken(2), value, isStrict);
1891         }
1892     }
1893 
1894     private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
1895         final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
1896         final GuardedInvocation inv = findSetIndexMethod(type, NashornCallSiteDescriptor.isStrict(desc));
1897         return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
1898     }
1899 
1900     private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc) { // array, index, value
1901         return findSetIndexMethod(desc.getMethodType(), NashornCallSiteDescriptor.isStrict(desc));
1902     }
1903 
1904     /**
1905      * Find the appropriate SETINDEX method for an invoke dynamic call.
1906      *
1907      * @param callType the method type at the call site
1908      * @param isStrict are we in strict mode?
1909      *
1910      * @return GuardedInvocation to be invoked at call site.
1911      */
1912     private static GuardedInvocation findSetIndexMethod(final MethodType callType, final boolean isStrict) {
1913         assert callType.parameterCount() == 3;
1914 
1915         final Class<?>   keyClass   = callType.parameterType(1);
1916         final Class<?>   valueClass = callType.parameterType(2);
1917 
1918         MethodHandle methodHandle = findOwnMH("set", void.class, keyClass, valueClass, boolean.class);
1919         methodHandle = MH.insertArguments(methodHandle, 3, isStrict);
1920 
1921         return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType));
1922     }
1923 
1924     /**
1925      * Fall back if a function property is not found.
1926      * @param desc The call site descriptor
1927      * @param request the link request
1928      * @return GuardedInvocation to be invoked at call site.
1929      */
1930     public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1931         final String       name      = desc.getNameToken(2);
1932         final FindProperty find      = findProperty(NO_SUCH_METHOD_NAME, true);
1933         final boolean      scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc);
1934 
1935         if (find == null) {
1936             return noSuchProperty(desc, request);
1937         }
1938 
1939         final ScriptFunction func = (ScriptFunction)getObjectValue(find);
1940         final Object thiz = scopeCall && func.isStrict() ? ScriptRuntime.UNDEFINED : this;
1941         // TODO: It'd be awesome if we could bind "name" without binding "this".
1942         return new GuardedInvocation(MH.dropArguments(MH.constant(ScriptFunction.class,
1943                 func.makeBoundFunction(thiz, new Object[] { name })), 0, Object.class),
1944                 null, NashornGuards.getMapGuard(getMap()));
1945     }
1946 
1947     /**
1948      * Fall back if a property is not found.
1949      * @param desc the call site descriptor.
1950      * @param request the link request
1951      * @return GuardedInvocation to be invoked at call site.
1952      */
1953     public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
1954         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1955         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
1956         final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
1957 
1958         if (find != null) {
1959             final ScriptFunction func = (ScriptFunction)getObjectValue(find);
1960             MethodHandle methodHandle = getCallMethodHandle(func, desc.getMethodType(), name);
1961 
1962             if (methodHandle != null) {
1963                 if (scopeAccess && func.isStrict()) {
1964                     methodHandle = bindTo(methodHandle, UNDEFINED);
1965                 }
1966                 return new GuardedInvocation(methodHandle,
1967                         find.isInherited()? getMap().getProtoGetSwitchPoint(proto, NO_SUCH_PROPERTY_NAME) : null,
1968                         getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func));
1969             }
1970         }
1971 
1972         if (scopeAccess) {
1973             throw referenceError("not.defined", name);
1974         }
1975 
1976         return createEmptyGetter(desc, name);
1977     }
1978     /**
1979      * Invoke fall back if a property is not found.
1980      * @param name Name of property.
1981      * @return Result from call.
1982      */
1983     private Object invokeNoSuchProperty(final String name) {
1984         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
1985 
1986         if (find != null) {
1987             final Object func = getObjectValue(find);
1988 
1989             if (func instanceof ScriptFunction) {
1990                 return ScriptRuntime.apply((ScriptFunction)func, this, name);
1991             }
1992         }
1993 
1994         return UNDEFINED;
1995     }
1996 
1997     private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
1998         return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(getMap()));
1999     }
2000 
2001     private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
2002         protected T[] values;
2003         protected final ScriptObject object;
2004         private int index;
2005 
2006         ScriptObjectIterator(final ScriptObject object) {
2007             this.object = object;
2008         }
2009 
2010         protected abstract void init();
2011 
2012         @Override
2013         public boolean hasNext() {
2014             if (values == null) {
2015                 init();
2016             }
2017             return index < values.length;
2018         }
2019 
2020         @Override
2021         public T next() {
2022             if (values == null) {
2023                 init();
2024             }
2025             return values[index++];
2026         }
2027 
2028         @Override
2029         public void remove() {
2030             throw new UnsupportedOperationException();
2031         }
2032     }
2033 
2034     private static class KeyIterator extends ScriptObjectIterator<String> {
2035         KeyIterator(final ScriptObject object) {
2036             super(object);
2037         }
2038 
2039         @Override
2040         protected void init() {
2041             final Set<String> keys = new LinkedHashSet<>();
2042             for (ScriptObject self = object; self != null; self = self.getProto()) {
2043                 keys.addAll(Arrays.asList(self.getOwnKeys(false)));
2044             }
2045             this.values = keys.toArray(new String[keys.size()]);
2046         }
2047     }
2048 
2049     private static class ValueIterator extends ScriptObjectIterator<Object> {
2050         ValueIterator(final ScriptObject object) {
2051             super(object);
2052         }
2053 
2054         @Override
2055         protected void init() {
2056             final ArrayList<Object> valueList = new ArrayList<>();
2057             for (ScriptObject self = object; self != null; self = self.getProto()) {
2058                 for (final String key : self.getOwnKeys(false)) {
2059                     valueList.add(self.get(key));
2060                 }
2061             }
2062             this.values = valueList.toArray(new Object[valueList.size()]);
2063         }
2064     }
2065 
2066     /**
2067      * Add a spill property for the given key.
2068      * @param key           Property key.
2069      * @param propertyFlags Property flags.
2070      * @return Added property.
2071      */
2072     private Property addSpillProperty(final String key, final int propertyFlags) {
2073         int fieldCount   = getMap().getFieldCount();
2074         int fieldMaximum = getMap().getFieldMaximum();
2075         Property property;
2076 
2077         if (fieldCount < fieldMaximum) {
2078             property = new AccessorProperty(key, propertyFlags & ~Property.IS_SPILL, getClass(), fieldCount);
2079             notifyPropertyAdded(this, property);
2080             property = addOwnProperty(property);
2081         } else {
2082             int i = getMap().getSpillLength();
2083             property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i);
2084             notifyPropertyAdded(this, property);
2085             property = addOwnProperty(property);
2086             i = property.getSlot();
2087 
2088             final int newLength = (i + SPILL_RATE) / SPILL_RATE * SPILL_RATE;
2089 
2090             if (spill == null || newLength > spill.length) {
2091                 final Object[] newSpill = new Object[newLength];
2092 
2093                 if (spill != null) {
2094                     System.arraycopy(spill, 0, newSpill, 0, spill.length);
2095                 }
2096 
2097                 spill = newSpill;
2098             }
2099         }
2100 
2101         return property;
2102     }
2103 
2104 
2105     /**
2106      * Add a spill entry for the given key.
2107      * @param key Property key.
2108      * @return Setter method handle.
2109      */
2110     MethodHandle addSpill(final String key) {
2111         final Property spillProperty = addSpillProperty(key, 0);
2112         final Class<?> type = Object.class;
2113         return spillProperty.getSetter(type, getMap()); //TODO specfields
2114     }
2115 
2116     /**
2117      * Make sure arguments are paired correctly, with respect to more parameters than declared,
2118      * fewer parameters than declared and other things that JavaScript allows. This might involve
2119      * creating collectors.
2120      *
2121      * @param methodHandle method handle for invoke
2122      * @param callType     type of the call
2123      *
2124      * @return method handle with adjusted arguments
2125      */
2126     protected static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType) {
2127         return pairArguments(methodHandle, callType, null);
2128     }
2129 
2130     /**
2131      * Make sure arguments are paired correctly, with respect to more parameters than declared,
2132      * fewer parameters than declared and other things that JavaScript allows. This might involve
2133      * creating collectors.
2134      *
2135      * Make sure arguments are paired correctly.
2136      * @param methodHandle MethodHandle to adjust.
2137      * @param callType     MethodType of the call site.
2138      * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the
2139      * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a
2140      * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites
2141      * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters.
2142      *
2143      * @return method handle with adjusted arguments
2144      */
2145     public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) {
2146 
2147         final MethodType methodType = methodHandle.type();
2148         if (methodType.equals(callType)) {
2149             return methodHandle;
2150         }
2151 
2152         final int parameterCount = methodType.parameterCount();
2153         final int callCount      = callType.parameterCount();
2154 
2155         final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
2156         final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : (callCount > 0 &&
2157                 callType.parameterType(callCount - 1).isArray());
2158 
2159         if (callCount < parameterCount) {
2160             final int      missingArgs = parameterCount - callCount;
2161             final Object[] fillers     = new Object[missingArgs];
2162 
2163             Arrays.fill(fillers, UNDEFINED);
2164 
2165             if (isCalleeVarArg) {
2166                 fillers[missingArgs - 1] = new Object[0];
2167             }
2168 
2169             return MH.insertArguments(
2170                 methodHandle,
2171                 parameterCount - missingArgs,
2172                 fillers);
2173         }
2174 
2175         if (isCalleeVarArg) {
2176             return isCallerVarArg ?
2177                 methodHandle :
2178                 MH.asCollector(methodHandle, Object[].class, callCount - parameterCount + 1);
2179         }
2180 
2181         if (isCallerVarArg) {
2182             final int spreadArgs = parameterCount - callCount + 1;
2183             return MH.filterArguments(
2184                 MH.asSpreader(
2185                     methodHandle,
2186                     Object[].class,
2187                     spreadArgs),
2188                 callCount - 1,
2189                 MH.insertArguments(
2190                     TRUNCATINGFILTER,
2191                     0,
2192                     spreadArgs)
2193                 );
2194         }
2195 
2196         if (callCount > parameterCount) {
2197             final int discardedArgs = callCount - parameterCount;
2198 
2199             final Class<?>[] discards = new Class<?>[discardedArgs];
2200             Arrays.fill(discards, Object.class);
2201 
2202             return MH.dropArguments(methodHandle, callCount - discardedArgs, discards);
2203         }
2204 
2205         return methodHandle;
2206     }
2207 
2208     @SuppressWarnings("unused")
2209     private static Object[] truncatingFilter(final int n, final Object[] array) {
2210         final int length = array == null ? 0 : array.length;
2211         if (n == length) {
2212             return array == null ? new Object[0] : array;
2213         }
2214 
2215         final Object[] newArray = new Object[n];
2216 
2217         if (array != null) {
2218             for (int i = 0; i < n && i < length; i++) {
2219                 newArray[i] = array[i];
2220             }
2221         }
2222 
2223         if (length < n) {
2224             final Object fill = UNDEFINED;
2225 
2226             for (int i = length; i < n; i++) {
2227                 newArray[i] = fill;
2228             }
2229         }
2230 
2231         return newArray;
2232     }
2233 
2234     /**
2235       * Numeric length setter for length property
2236       *
2237       * @param newLength new length to set
2238       */
2239     public final void setLength(final long newLength) {
2240        final long arrayLength = getArray().length();
2241        if (newLength == arrayLength) {
2242            return;
2243        }
2244 
2245        final boolean isStrict = isStrictContext();
2246 
2247        if (newLength > arrayLength) {
2248            setArray(getArray().ensure(newLength - 1));
2249             if (getArray().canDelete(arrayLength, (newLength - 1), isStrict)) {
2250                setArray(getArray().delete(arrayLength, (newLength - 1)));
2251            }
2252            return;
2253        }
2254 
2255        if (newLength < arrayLength) {
2256            setArray(getArray().shrink(newLength));
2257            getArray().setLength(newLength);
2258        }
2259     }
2260 
2261     private int getInt(final int index, final String key) {
2262         if (ArrayIndex.isValidArrayIndex(index)) {
2263              for (ScriptObject object = this; ; ) {
2264                 final FindProperty find = object.findProperty(key, false, false, this);
2265 
2266                 if (find != null) {
2267                     return getIntValue(find);
2268                 }
2269 
2270                 if ((object = object.getProto()) == null) {
2271                     break;
2272                 }
2273 
2274                 final ArrayData array = object.getArray();
2275 
2276                 if (array.has(index)) {
2277                     return array.getInt(index);
2278                 }
2279            }
2280         } else {
2281             final FindProperty find = findProperty(key, true);
2282 
2283             if (find != null) {
2284                 return getIntValue(find);
2285             }
2286         }
2287 
2288         return JSType.toInt32(invokeNoSuchProperty(key));
2289     }
2290 
2291     @Override
2292     public int getInt(final Object key) {
2293         final int index = ArrayIndex.getArrayIndex(key);
2294         final ArrayData array = getArray();
2295 
2296         if (array.has(index)) {
2297             return array.getInt(index);
2298         }
2299 
2300         return getInt(index, convertKey(key));
2301     }
2302 
2303     @Override
2304     public int getInt(final double key) {
2305         final int index = ArrayIndex.getArrayIndex(key);
2306         final ArrayData array = getArray();
2307 
2308         if (array.has(index)) {
2309             return array.getInt(index);
2310         }
2311 
2312         return getInt(index, convertKey(key));
2313     }
2314 
2315     @Override
2316     public int getInt(final long key) {
2317         final int index = ArrayIndex.getArrayIndex(key);
2318         final ArrayData array = getArray();
2319 
2320         if (array.has(index)) {
2321             return array.getInt(index);
2322         }
2323 
2324         return getInt(index, convertKey(key));
2325     }
2326 
2327     @Override
2328     public int getInt(final int key) {
2329         final ArrayData array = getArray();
2330 
2331         if (array.has(key)) {
2332             return array.getInt(key);
2333         }
2334 
2335         return getInt(key, convertKey(key));
2336     }
2337 
2338     private long getLong(final int index, final String key) {
2339         if (ArrayIndex.isValidArrayIndex(index)) {
2340             for (ScriptObject object = this; ; ) {
2341                 final FindProperty find = object.findProperty(key, false, false, this);
2342 
2343                 if (find != null) {
2344                     return getLongValue(find);
2345                 }
2346 
2347                 if ((object = object.getProto()) == null) {
2348                     break;
2349                 }
2350 
2351                 final ArrayData array = object.getArray();
2352 
2353                 if (array.has(index)) {
2354                     return array.getLong(index);
2355                 }
2356            }
2357         } else {
2358             final FindProperty find = findProperty(key, true);
2359 
2360             if (find != null) {
2361                 return getLongValue(find);
2362             }
2363         }
2364 
2365         return JSType.toLong(invokeNoSuchProperty(key));
2366     }
2367 
2368     @Override
2369     public long getLong(final Object key) {
2370         final int index = ArrayIndex.getArrayIndex(key);
2371         final ArrayData array = getArray();
2372 
2373         if (array.has(index)) {
2374             return array.getLong(index);
2375         }
2376 
2377         return getLong(index, convertKey(key));
2378     }
2379 
2380     @Override
2381     public long getLong(final double key) {
2382         final int index = ArrayIndex.getArrayIndex(key);
2383         final ArrayData array = getArray();
2384 
2385         if (array.has(index)) {
2386             return array.getLong(index);
2387         }
2388 
2389         return getLong(index, convertKey(key));
2390     }
2391 
2392     @Override
2393     public long getLong(final long key) {
2394         final int index = ArrayIndex.getArrayIndex(key);
2395         final ArrayData array = getArray();
2396 
2397         if (array.has(index)) {
2398             return array.getLong(index);
2399         }
2400 
2401         return getLong(index, convertKey(key));
2402     }
2403 
2404     @Override
2405     public long getLong(final int key) {
2406         final ArrayData array = getArray();
2407 
2408         if (array.has(key)) {
2409             return array.getLong(key);
2410         }
2411 
2412         return getLong(key, convertKey(key));
2413     }
2414 
2415     private double getDouble(final int index, final String key) {
2416         if (ArrayIndex.isValidArrayIndex(index)) {
2417             for (ScriptObject object = this; ; ) {
2418                 final FindProperty find = object.findProperty(key, false, false, this);
2419 
2420                 if (find != null) {
2421                     return getDoubleValue(find);
2422                 }
2423 
2424                 if ((object = object.getProto()) == null) {
2425                     break;
2426                 }
2427 
2428                 final ArrayData array = object.getArray();
2429 
2430                 if (array.has(index)) {
2431                     return array.getDouble(index);
2432                 }
2433            }
2434         } else {
2435             final FindProperty find = findProperty(key, true);
2436 
2437             if (find != null) {
2438                 return getDoubleValue(find);
2439             }
2440         }
2441 
2442         return JSType.toNumber(invokeNoSuchProperty(key));
2443     }
2444 
2445     @Override
2446     public double getDouble(final Object key) {
2447         final int index = ArrayIndex.getArrayIndex(key);
2448         final ArrayData array = getArray();
2449 
2450         if (array.has(index)) {
2451             return array.getDouble(index);
2452         }
2453 
2454         return getDouble(index, convertKey(key));
2455     }
2456 
2457     @Override
2458     public double getDouble(final double key) {
2459         final int index = ArrayIndex.getArrayIndex(key);
2460         final ArrayData array = getArray();
2461 
2462         if (array.has(index)) {
2463             return array.getDouble(index);
2464         }
2465 
2466         return getDouble(index, convertKey(key));
2467     }
2468 
2469     @Override
2470     public double getDouble(final long key) {
2471         final int index = ArrayIndex.getArrayIndex(key);
2472         final ArrayData array = getArray();
2473 
2474         if (array.has(index)) {
2475             return array.getDouble(index);
2476         }
2477 
2478         return getDouble(index, convertKey(key));
2479     }
2480 
2481     @Override
2482     public double getDouble(final int key) {
2483         final ArrayData array = getArray();
2484 
2485         if (array.has(key)) {
2486             return array.getDouble(key);
2487         }
2488 
2489         return getDouble(key, convertKey(key));
2490     }
2491 
2492     private Object get(final int index, final String key) {
2493         if (ArrayIndex.isValidArrayIndex(index)) {
2494             for (ScriptObject object = this; ; ) {
2495                 final FindProperty find = object.findProperty(key, false, false, this);
2496 
2497                 if (find != null) {
2498                     return getObjectValue(find);
2499                 }
2500 
2501                 if ((object = object.getProto()) == null) {
2502                     break;
2503                 }
2504 
2505                 final ArrayData array = object.getArray();
2506 
2507                 if (array.has(index)) {
2508                     return array.getObject(index);
2509                 }
2510             }
2511         } else {
2512             final FindProperty find = findProperty(key, true);
2513 
2514             if (find != null) {
2515                 return getObjectValue(find);
2516             }
2517         }
2518 
2519         return invokeNoSuchProperty(key);
2520     }
2521 
2522     @Override
2523     public Object get(final Object key) {
2524         final int index = ArrayIndex.getArrayIndex(key);
2525         final ArrayData array = getArray();
2526 
2527         if (array.has(index)) {
2528             return array.getObject(index);
2529         }
2530 
2531         return get(index, convertKey(key));
2532     }
2533 
2534     @Override
2535     public Object get(final double key) {
2536         final int index = ArrayIndex.getArrayIndex(key);
2537         final ArrayData array = getArray();
2538 
2539         if (array.has(index)) {
2540             return array.getObject(index);
2541         }
2542 
2543         return get(index, convertKey(key));
2544     }
2545 
2546     @Override
2547     public Object get(final long key) {
2548         final int index = ArrayIndex.getArrayIndex(key);
2549         final ArrayData array = getArray();
2550 
2551         if (array.has(index)) {
2552             return array.getObject(index);
2553         }
2554 
2555         return get(index, convertKey(key));
2556     }
2557 
2558     @Override
2559     public Object get(final int key) {
2560         final ArrayData array = getArray();
2561 
2562         if (array.has(key)) {
2563             return array.getObject(key);
2564         }
2565 
2566         return get(key, convertKey(key));
2567     }
2568 
2569     /**
2570      * Handle when an array doesn't have a slot - possibly grow and/or convert array.
2571      *
2572      * @param index  key as index
2573      * @param value  element value
2574      * @param strict are we in strict mode
2575      */
2576     private void doesNotHave(final int index, final Object value, final boolean strict) {
2577         final long oldLength = getArray().length();
2578         final long longIndex = index & JSType.MAX_UINT;
2579 
2580         if (!getArray().has(index)) {
2581             final String key = convertKey(longIndex);
2582             final FindProperty find = findProperty(key, true);
2583 
2584             if (find != null) {
2585                 setObject(find, strict, key, value);
2586                 return;
2587             }
2588         }
2589 
2590         if (longIndex >= oldLength) {
2591             if (!isExtensible()) {
2592                 if (strict) {
2593                     throw typeError("object.non.extensible", JSType.toString(index), ScriptRuntime.safeToString(this));
2594                 }
2595                 return;
2596             }
2597             setArray(getArray().ensure(longIndex));
2598         }
2599 
2600         if (value instanceof Integer) {
2601             setArray(getArray().set(index, (int)value, strict));
2602         } else if (value instanceof Long) {
2603             setArray(getArray().set(index, (long)value, strict));
2604         } else if (value instanceof Double) {
2605             setArray(getArray().set(index, (double)value, strict));
2606         } else {
2607             setArray(getArray().set(index, value, strict));
2608         }
2609 
2610         if (longIndex > oldLength) {
2611             ArrayData array = getArray();
2612 
2613             if (array.canDelete(oldLength, (longIndex - 1), strict)) {
2614                 array = array.delete(oldLength, (longIndex - 1));
2615             }
2616 
2617             setArray(array);
2618         }
2619     }
2620 
2621     /**
2622      * This is the most generic of all Object setters. Most of the others use this in some form.
2623      * TODO: should be further specialized
2624      *
2625      * @param find    found property
2626      * @param strict  are we in strict mode
2627      * @param key     property key
2628      * @param value   property value
2629      */
2630     public final void setObject(final FindProperty find, final boolean strict, final String key, final Object value) {
2631         FindProperty f = find;
2632 
2633         if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) {
2634             f = null;
2635         }
2636 
2637         if (f != null) {
2638             if (!f.getProperty().isWritable()) {
2639                 if (strict) {
2640                     throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this));
2641                 }
2642 
2643                 return;
2644             }
2645 
2646             f.setObjectValue(value, strict);
2647 
2648         } else if (!isExtensible()) {
2649             if (strict) {
2650                 throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
2651             }
2652         } else {
2653             spill(key, value);
2654         }
2655     }
2656 
2657     private void spill(final String key, final Object value) {
2658         addSpillProperty(key, 0).setObjectValue(this, this, value, false);
2659     }
2660 
2661 
2662     @Override
2663     public void set(final Object key, final int value, final boolean strict) {
2664         final int index = ArrayIndex.getArrayIndex(key);
2665 
2666         if (ArrayIndex.isValidArrayIndex(index)) {
2667             if (getArray().has(index)) {
2668                 setArray(getArray().set(index, value, strict));
2669             } else {
2670                 doesNotHave(index, value, strict);
2671             }
2672 
2673             return;
2674         }
2675 
2676         set(key, JSType.toObject(value), strict);
2677     }
2678 
2679     @Override
2680     public void set(final Object key, final long value, final boolean strict) {
2681         final int index = ArrayIndex.getArrayIndex(key);
2682 
2683         if (ArrayIndex.isValidArrayIndex(index)) {
2684             if (getArray().has(index)) {
2685                 setArray(getArray().set(index, value, strict));
2686             } else {
2687                 doesNotHave(index, value, strict);
2688             }
2689 
2690             return;
2691         }
2692 
2693         set(key, JSType.toObject(value), strict);
2694     }
2695 
2696     @Override
2697     public void set(final Object key, final double value, final boolean strict) {
2698         final int index = ArrayIndex.getArrayIndex(key);
2699 
2700         if (ArrayIndex.isValidArrayIndex(index)) {
2701             if (getArray().has(index)) {
2702                 setArray(getArray().set(index, value, strict));
2703             } else {
2704                 doesNotHave(index, value, strict);
2705             }
2706 
2707             return;
2708         }
2709 
2710         set(key, JSType.toObject(value), strict);
2711     }
2712 
2713     @Override
2714     public void set(final Object key, final Object value, final boolean strict) {
2715         final int index = ArrayIndex.getArrayIndex(key);
2716 
2717         if (ArrayIndex.isValidArrayIndex(index)) {
2718             if (getArray().has(index)) {
2719                 setArray(getArray().set(index, value, strict));
2720             } else {
2721                 doesNotHave(index, value, strict);
2722             }
2723 
2724             return;
2725         }
2726 
2727         final String       propName = convertKey(key);
2728         final FindProperty find     = findProperty(propName, true);
2729 
2730         setObject(find, strict, propName, value);
2731     }
2732 
2733     @Override
2734     public void set(final double key, final int value, final boolean strict) {
2735         final int index = ArrayIndex.getArrayIndex(key);
2736 
2737         if (ArrayIndex.isValidArrayIndex(index)) {
2738             if (getArray().has(index)) {
2739                 setArray(getArray().set(index, value, strict));
2740             } else {
2741                 doesNotHave(index, value, strict);
2742             }
2743 
2744             return;
2745         }
2746 
2747         set(JSType.toObject(key), JSType.toObject(value), strict);
2748     }
2749 
2750     @Override
2751     public void set(final double key, final long value, final boolean strict) {
2752         final int index = ArrayIndex.getArrayIndex(key);
2753 
2754         if (ArrayIndex.isValidArrayIndex(index)) {
2755             if (getArray().has(index)) {
2756                 setArray(getArray().set(index, value, strict));
2757             } else {
2758                 doesNotHave(index, value, strict);
2759             }
2760 
2761             return;
2762         }
2763 
2764         set(JSType.toObject(key), JSType.toObject(value), strict);
2765     }
2766 
2767     @Override
2768     public void set(final double key, final double value, final boolean strict) {
2769         final int index = ArrayIndex.getArrayIndex(key);
2770 
2771         if (ArrayIndex.isValidArrayIndex(index)) {
2772             if (getArray().has(index)) {
2773                 setArray(getArray().set(index, value, strict));
2774             } else {
2775                 doesNotHave(index, value, strict);
2776             }
2777 
2778             return;
2779         }
2780 
2781         set(JSType.toObject(key), JSType.toObject(value), strict);
2782     }
2783 
2784     @Override
2785     public void set(final double key, final Object value, final boolean strict) {
2786         final int index = ArrayIndex.getArrayIndex(key);
2787 
2788         if (ArrayIndex.isValidArrayIndex(index)) {
2789             if (getArray().has(index)) {
2790                 setArray(getArray().set(index, value, strict));
2791             } else {
2792                 doesNotHave(index, value, strict);
2793             }
2794 
2795             return;
2796         }
2797 
2798         set(JSType.toObject(key), value, strict);
2799     }
2800 
2801     @Override
2802     public void set(final long key, final int value, final boolean strict) {
2803         final int index = ArrayIndex.getArrayIndex(key);
2804 
2805         if (ArrayIndex.isValidArrayIndex(index)) {
2806             if (getArray().has(index)) {
2807                 setArray(getArray().set(index, value, strict));
2808             } else {
2809                 doesNotHave(index, value, strict);
2810             }
2811 
2812             return;
2813         }
2814 
2815         set(JSType.toObject(key), JSType.toObject(value), strict);
2816     }
2817 
2818     @Override
2819     public void set(final long key, final long value, final boolean strict) {
2820         final int index = ArrayIndex.getArrayIndex(key);
2821 
2822         if (ArrayIndex.isValidArrayIndex(index)) {
2823             if (getArray().has(index)) {
2824                 setArray(getArray().set(index, value, strict));
2825             } else {
2826                 doesNotHave(index, value, strict);
2827             }
2828 
2829             return;
2830         }
2831 
2832         set(JSType.toObject(key), JSType.toObject(value), strict);
2833     }
2834 
2835     @Override
2836     public void set(final long key, final double value, final boolean strict) {
2837         final int index = ArrayIndex.getArrayIndex(key);
2838 
2839         if (ArrayIndex.isValidArrayIndex(index)) {
2840             if (getArray().has(index)) {
2841                 setArray(getArray().set(index, value, strict));
2842             } else {
2843                 doesNotHave(index, value, strict);
2844             }
2845 
2846             return;
2847         }
2848 
2849         set(JSType.toObject(key), JSType.toObject(value), strict);
2850     }
2851 
2852     @Override
2853     public void set(final long key, final Object value, final boolean strict) {
2854         final int index = ArrayIndex.getArrayIndex(key);
2855 
2856         if (ArrayIndex.isValidArrayIndex(index)) {
2857             if (getArray().has(index)) {
2858                 setArray(getArray().set(index, value, strict));
2859             } else {
2860                 doesNotHave(index, value, strict);
2861             }
2862 
2863             return;
2864         }
2865 
2866         set(JSType.toObject(key), value, strict);
2867     }
2868 
2869     @Override
2870     public void set(final int key, final int value, final boolean strict) {
2871         final int index = ArrayIndex.getArrayIndex(key);
2872 
2873         if (ArrayIndex.isValidArrayIndex(index)) {
2874             if (getArray().has(index)) {
2875                 setArray(getArray().set(index, value, strict));
2876             } else {
2877                 doesNotHave(index, value, strict);
2878             }
2879 
2880             return;
2881         }
2882 
2883         set(JSType.toObject(key), JSType.toObject(value), strict);
2884     }
2885 
2886     @Override
2887     public void set(final int key, final long value, final boolean strict) {
2888         final int index = ArrayIndex.getArrayIndex(key);
2889 
2890         if (ArrayIndex.isValidArrayIndex(index)) {
2891             if (getArray().has(index)) {
2892                 setArray(getArray().set(index, value, strict));
2893             } else {
2894                 doesNotHave(index, value, strict);
2895             }
2896 
2897             return;
2898         }
2899 
2900         set(JSType.toObject(key), JSType.toObject(value), strict);
2901     }
2902 
2903     @Override
2904     public void set(final int key, final double value, final boolean strict) {
2905         final int index = ArrayIndex.getArrayIndex(key);
2906 
2907         if (ArrayIndex.isValidArrayIndex(index)) {
2908             if (getArray().has(index)) {
2909                 setArray(getArray().set(index, value, strict));
2910             } else {
2911                 doesNotHave(index, value, strict);
2912             }
2913 
2914             return;
2915         }
2916 
2917         set(JSType.toObject(key), JSType.toObject(value), strict);
2918     }
2919 
2920     @Override
2921     public void set(final int key, final Object value, final boolean strict) {
2922         final int index = ArrayIndex.getArrayIndex(key);
2923 
2924         if (ArrayIndex.isValidArrayIndex(index)) {
2925             if (getArray().has(index)) {
2926                 setArray(getArray().set(index, value, strict));
2927             } else {
2928                 doesNotHave(index, value, strict);
2929             }
2930 
2931             return;
2932         }
2933 
2934         set(JSType.toObject(key), value, strict);
2935     }
2936 
2937     @Override
2938     public boolean has(final Object key) {
2939         final int index = ArrayIndex.getArrayIndex(key);
2940 
2941         if (ArrayIndex.isValidArrayIndex(index)) {
2942             for (ScriptObject self = this; self != null; self = self.getProto()) {
2943                 if (self.getArray().has(index)) {
2944                     return true;
2945                 }
2946             }
2947         }
2948 
2949         final FindProperty find = findProperty(convertKey(key), true);
2950 
2951         return find != null;
2952     }
2953 
2954     @Override
2955     public boolean has(final double key) {
2956         final int index = ArrayIndex.getArrayIndex(key);
2957 
2958         if (ArrayIndex.isValidArrayIndex(index)) {
2959             for (ScriptObject self = this; self != null; self = self.getProto()) {
2960                 if (self.getArray().has(index)) {
2961                     return true;
2962                 }
2963             }
2964         }
2965 
2966         final FindProperty find = findProperty(convertKey(key), true);
2967 
2968         return find != null;
2969     }
2970 
2971     @Override
2972     public boolean has(final long key) {
2973         final int index = ArrayIndex.getArrayIndex(key);
2974 
2975         if (ArrayIndex.isValidArrayIndex(index)) {
2976             for (ScriptObject self = this; self != null; self = self.getProto()) {
2977                 if (self.getArray().has(index)) {
2978                     return true;
2979                 }
2980             }
2981         }
2982 
2983         final FindProperty find = findProperty(convertKey(key), true);
2984 
2985         return find != null;
2986     }
2987 
2988     @Override
2989     public boolean has(final int key) {
2990         final int index = ArrayIndex.getArrayIndex(key);
2991 
2992         if (ArrayIndex.isValidArrayIndex(index)) {
2993             for (ScriptObject self = this; self != null; self = self.getProto()) {
2994                 if (self.getArray().has(index)) {
2995                     return true;
2996                 }
2997             }
2998         }
2999 
3000         final FindProperty find = findProperty(convertKey(key), true);
3001 
3002         return find != null;
3003     }
3004 
3005     @Override
3006     public boolean hasOwnProperty(final Object key) {
3007         final int index = ArrayIndex.getArrayIndex(key);
3008 
3009         if (getArray().has(index)) {
3010             return true;
3011         }
3012 
3013         final FindProperty find = findProperty(convertKey(key), false);
3014 
3015         return find != null;
3016     }
3017 
3018     @Override
3019     public boolean hasOwnProperty(final int key) {
3020         final int index = ArrayIndex.getArrayIndex(key);
3021 
3022         if (getArray().has(index)) {
3023             return true;
3024         }
3025 
3026         final FindProperty find = findProperty(convertKey(key), false);
3027 
3028         return find != null;
3029     }
3030 
3031     @Override
3032     public boolean hasOwnProperty(final long key) {
3033         final int index = ArrayIndex.getArrayIndex(key);
3034 
3035         if (getArray().has(index)) {
3036             return true;
3037         }
3038 
3039         final FindProperty find = findProperty(convertKey(key), false);
3040 
3041         return find != null;
3042     }
3043 
3044     @Override
3045     public boolean hasOwnProperty(final double key) {
3046         final int index = ArrayIndex.getArrayIndex(key);
3047 
3048         if (getArray().has(index)) {
3049             return true;
3050         }
3051 
3052         final FindProperty find = findProperty(convertKey(key), false);
3053 
3054         return find != null;
3055     }
3056 
3057     @Override
3058     public boolean delete(final int key, final boolean strict) {
3059         final int index = ArrayIndex.getArrayIndex(key);
3060         final ArrayData array = getArray();
3061 
3062         if (array.has(index)) {
3063             if (array.canDelete(index, strict)) {
3064                 setArray(array.delete(index));
3065                 return true;
3066             }
3067             return false;
3068         }
3069 
3070         return deleteObject(JSType.toObject(key), strict);
3071     }
3072 
3073     @Override
3074     public boolean delete(final long key, final boolean strict) {
3075         final int index = ArrayIndex.getArrayIndex(key);
3076         final ArrayData array = getArray();
3077 
3078         if (array.has(index)) {
3079             if (array.canDelete(index, strict)) {
3080                 setArray(array.delete(index));
3081                 return true;
3082             }
3083             return false;
3084         }
3085 
3086         return deleteObject(JSType.toObject(key), strict);
3087     }
3088 
3089     @Override
3090     public boolean delete(final double key, final boolean strict) {
3091         final int index = ArrayIndex.getArrayIndex(key);
3092         final ArrayData array = getArray();
3093 
3094         if (array.has(index)) {
3095             if (array.canDelete(index, strict)) {
3096                 setArray(array.delete(index));
3097                 return true;
3098             }
3099             return false;
3100         }
3101 
3102         return deleteObject(JSType.toObject(key), strict);
3103     }
3104 
3105     @Override
3106     public boolean delete(final Object key, final boolean strict) {
3107         final int index = ArrayIndex.getArrayIndex(key);
3108         final ArrayData array = getArray();
3109 
3110         if (array.has(index)) {
3111             if (array.canDelete(index, strict)) {
3112                 setArray(array.delete(index));
3113                 return true;
3114             }
3115             return false;
3116         }
3117 
3118         return deleteObject(key, strict);
3119     }
3120 
3121     private boolean deleteObject(final Object key, final boolean strict) {
3122         final String propName = convertKey(key);
3123         final FindProperty find = findProperty(propName, false);
3124 
3125         if (find == null) {
3126             return true;
3127         }
3128 
3129         if (!find.getProperty().isConfigurable()) {
3130             if (strict) {
3131                 throw typeError("cant.delete.property", propName, ScriptRuntime.safeToString(this));
3132             }
3133             return false;
3134         }
3135 
3136         final Property prop = find.getProperty();
3137         notifyPropertyDeleted(this, prop);
3138         deleteOwnProperty(prop);
3139 
3140         return true;
3141     }
3142 
3143     /*
3144      * Make a new UserAccessorProperty property. getter and setter functions are stored in
3145      * this ScriptObject and slot values are used in property object.
3146      */
3147     private UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
3148         int oldSpillLength = getMap().getSpillLength();
3149 
3150         int getterSlot = oldSpillLength++;
3151         setSpill(getterSlot, getter);
3152         // if getter function is null, flag the slot to be negative (less by 1)
3153         if (getter == null) {
3154             getterSlot = -getterSlot - 1;
3155         }
3156 
3157         int setterSlot = oldSpillLength++;
3158 
3159         setSpill(setterSlot, setter);
3160         // if setter function is null, flag the slot to be negative (less by 1)
3161         if (setter == null) {
3162             setterSlot = -setterSlot - 1;
3163         }
3164 
3165         return new UserAccessorProperty(key, propertyFlags, getterSlot, setterSlot);
3166     }
3167 
3168     private void setSpill(final int slot, final Object value) {
3169         if (slot >= 0) {
3170             final int index = slot;
3171             if (spill == null) {
3172                 // create new spill.
3173                 spill = new Object[Math.max(index + 1, SPILL_RATE)];
3174             } else if (index >= spill.length) {
3175                 // grow spill as needed
3176                 final Object[] newSpill = new Object[index + 1];
3177                 System.arraycopy(spill, 0, newSpill, 0, spill.length);
3178                 spill = newSpill;
3179             }
3180 
3181             spill[index] = value;
3182         }
3183     }
3184 
3185     // user accessors are either stored in spill array slots
3186     // get the accessor value using slot number. Note that slot is spill array index.
3187     Object getSpill(final int slot) {
3188         final int index = slot;
3189         return (index < 0 || (index >= spill.length)) ? null : spill[index];
3190     }
3191 
3192     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
3193         final Class<?>   own = ScriptObject.class;
3194         final MethodType mt  = MH.type(rtype, types);
3195         try {
3196             return MH.findStatic(MethodHandles.lookup(), own, name, mt);
3197         } catch (final MethodHandleFactory.LookupException e) {
3198             return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
3199         }
3200     }
3201 
3202     private static MethodHandle getKnownFunctionPropertyGuard(final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
3203         return MH.insertArguments(KNOWNFUNCPROPGUARD, 1, map, getter, where, func);
3204     }
3205 
3206     @SuppressWarnings("unused")
3207     private static boolean knownFunctionPropertyGuard(final Object self, final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
3208         if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) {
3209             try {
3210                 return getter.invokeExact(where) == func;
3211             } catch (final RuntimeException | Error e) {
3212                 throw e;
3213             } catch (final Throwable t) {
3214                 throw new RuntimeException(t);
3215             }
3216         }
3217 
3218         return false;
3219     }
3220 
3221     /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */
3222     private static int count;
3223 
3224     /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created that are scope */
3225     private static int scopeCount;
3226 
3227     /**
3228      * Get number of {@code ScriptObject} instances created. If not running in debug
3229      * mode this is always 0
3230      *
3231      * @return number of ScriptObjects created
3232      */
3233     public static int getCount() {
3234         return count;
3235     }
3236 
3237     /**
3238      * Get number of scope {@code ScriptObject} instances created. If not running in debug
3239      * mode this is always 0
3240      *
3241      * @return number of scope ScriptObjects created
3242      */
3243     public static int getScopeCount() {
3244         return scopeCount;
3245     }
3246 
3247 }
--- EOF ---