1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.runtime;
  27 
  28 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
  29 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
  30 import static jdk.nashorn.internal.lookup.Lookup.MH;
  31 import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
  32 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
  33 import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE;
  34 import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE;
  35 import static jdk.nashorn.internal.runtime.PropertyDescriptor.GET;
  36 import static jdk.nashorn.internal.runtime.PropertyDescriptor.SET;
  37 import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE;
  38 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
  39 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
  40 
  41 import java.lang.invoke.MethodHandle;
  42 import java.lang.invoke.MethodHandles;
  43 import java.lang.invoke.MethodType;
  44 import java.util.AbstractMap;
  45 import java.util.ArrayList;
  46 import java.util.Arrays;
  47 import java.util.Collection;
  48 import java.util.Collections;
  49 import java.util.HashSet;
  50 import java.util.Iterator;
  51 import java.util.LinkedHashSet;
  52 import java.util.List;
  53 import java.util.Map;
  54 import java.util.Set;
  55 import jdk.internal.dynalink.CallSiteDescriptor;
  56 import jdk.internal.dynalink.linker.GuardedInvocation;
  57 import jdk.internal.dynalink.linker.LinkRequest;
  58 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
  59 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
  60 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
  61 import jdk.nashorn.internal.lookup.Lookup;
  62 import jdk.nashorn.internal.lookup.MethodHandleFactory;
  63 import jdk.nashorn.internal.objects.AccessorPropertyDescriptor;
  64 import jdk.nashorn.internal.objects.DataPropertyDescriptor;
  65 import jdk.nashorn.internal.runtime.arrays.ArrayData;
  66 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
  67 import jdk.nashorn.internal.runtime.linker.Bootstrap;
  68 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
  69 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
  70 import jdk.nashorn.internal.runtime.linker.NashornGuards;
  71 
  72 /**
  73  * Base class for generic JavaScript objects.
  74  * <p>
  75  * Notes:
  76  * <ul>
  77  * <li>The map is used to identify properties in the object.</li>
  78  * <li>If the map is modified then it must be cloned and replaced.  This notifies
  79  *     any code that made assumptions about the object that things have changed.
  80  *     Ex. CallSites that have been validated must check to see if the map has
  81  *     changed (or a map from a different object type) and hence relink the method
  82  *     to call.</li>
  83  * <li>Modifications of the map include adding/deleting attributes or changing a
  84  *     function field value.</li>
  85  * </ul>
  86  */
  87 
  88 public abstract class ScriptObject extends PropertyListenerManager implements PropertyAccess {
  89 
  90     /** Search fall back routine name for "no such method" */
  91     static final String NO_SUCH_METHOD_NAME   = "__noSuchMethod__";
  92 
  93     /** Search fall back routine name for "no such property" */
  94     static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__";
  95 
  96     /** Per ScriptObject flag - is this a scope object? */
  97     public static final int IS_SCOPE       = 0b0000_0001;
  98 
  99     /** Per ScriptObject flag - is this an array object? */
 100     public static final int IS_ARRAY       = 0b0000_0010;
 101 
 102     /** Per ScriptObject flag - is this an arguments object? */
 103     public static final int IS_ARGUMENTS   = 0b0000_0100;
 104 
 105     /** Is this a prototype PropertyMap? */
 106     public static final int IS_PROTOTYPE   = 0b0000_1000;
 107 
 108     /** Is length property not-writable? */
 109     public static final int IS_LENGTH_NOT_WRITABLE = 0b0001_0000;
 110 
 111     /** Spill growth rate - by how many elements does {@link ScriptObject#spill} when full */
 112     public static final int SPILL_RATE = 8;
 113 
 114     /** Map to property information and accessor functions. Ordered by insertion. */
 115     private PropertyMap map;
 116 
 117     /** objects proto. */
 118     private ScriptObject proto;
 119 
 120     /** Context of the object, lazily cached. */
 121     private Context context;
 122 
 123     /** Object flags. */
 124     private int flags;
 125 
 126     /** Area for properties added to object after instantiation, see {@link AccessorProperty} */
 127     public Object[] spill;
 128 
 129     /** Indexed array data. */
 130     private ArrayData arrayData;
 131 
 132     static final MethodHandle SETFIELD           = findOwnMH("setField",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, Object.class, Object.class);
 133     static final MethodHandle SETSPILL           = findOwnMH("setSpill",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
 134     static final MethodHandle SETSPILLWITHNEW    = findOwnMH("setSpillWithNew",  void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
 135     static final MethodHandle SETSPILLWITHGROW   = findOwnMH("setSpillWithGrow", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, int.class, Object.class, Object.class);
 136 
 137     private static final MethodHandle TRUNCATINGFILTER   = findOwnMH("truncatingFilter", Object[].class, int.class, Object[].class);
 138     private static final MethodHandle KNOWNFUNCPROPGUARD = findOwnMH("knownFunctionPropertyGuard", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, Object.class, ScriptFunction.class);
 139 
 140     /** Method handle for getting a function argument at a given index. Used from MapCreator */
 141     public static final Call GET_ARGUMENT       = virtualCall(ScriptObject.class, "getArgument", Object.class, int.class);
 142 
 143     /** Method handle for setting a function argument at a given index. Used from MapCreator */
 144     public static final Call SET_ARGUMENT       = virtualCall(ScriptObject.class, "setArgument", void.class, int.class, Object.class);
 145 
 146     /** Method handle for getting the proto of a ScriptObject */
 147     public static final Call GET_PROTO          = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class);
 148 
 149     /** Method handle for setting the proto of a ScriptObject */
 150     public static final Call SET_PROTO          = virtualCallNoLookup(ScriptObject.class, "setProto", void.class, ScriptObject.class);
 151 
 152     /** Method handle for setting the user accessors of a ScriptObject */
 153     public static final Call SET_USER_ACCESSORS = virtualCall(ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class);
 154 
 155     /**
 156      * Constructor
 157      */
 158     public ScriptObject() {
 159         this(null);
 160     }
 161 
 162     /**
 163     * Constructor
 164     *
 165     * @param map {@link PropertyMap} used to create the initial object
 166     */
 167     public ScriptObject(final PropertyMap map) {
 168         if (Context.DEBUG) {
 169             ScriptObject.count++;
 170         }
 171 
 172         this.arrayData = ArrayData.EMPTY_ARRAY;
 173 
 174         if (map == null) {
 175             this.setMap(PropertyMap.newMap(getClass()));
 176             return;
 177         }
 178 
 179         this.setMap(map);
 180     }
 181 
 182     /**
 183      * Copy all properties from the source object with their receiver bound to the source.
 184      * This function was known as mergeMap
 185      *
 186      * @param source The source object to copy from.
 187      */
 188     public void addBoundProperties(final ScriptObject source) {
 189         PropertyMap newMap = this.getMap();
 190 
 191         for (final Property property : source.getMap().getProperties()) {
 192             final String key = property.getKey();
 193 
 194             if (newMap.findProperty(key) == null) {
 195                 if (property instanceof UserAccessorProperty) {
 196                     final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
 197                     newMap = newMap.addProperty(prop);
 198                 } else {
 199                     newMap = newMap.newPropertyBind((AccessorProperty)property, source);
 200                 }
 201             }
 202         }
 203 
 204         this.setMap(newMap);
 205     }
 206 
 207     /**
 208      * Bind the method handle to the specified receiver, while preserving its original type (it will just ignore the
 209      * first argument in lieu of the bound argument).
 210      * @param methodHandle Method handle to bind to.
 211      * @param receiver     Object to bind.
 212      * @return Bound method handle.
 213      */
 214     static MethodHandle bindTo(final MethodHandle methodHandle, final Object receiver) {
 215         return MH.dropArguments(MH.bindTo(methodHandle, receiver), 0, methodHandle.type().parameterType(0));
 216     }
 217 
 218     /**
 219      * Return a property iterator.
 220      * @return Property iterator.
 221      */
 222     public Iterator<String> propertyIterator() {
 223         return new KeyIterator(this);
 224     }
 225 
 226     /**
 227      * Return a property value iterator.
 228      * @return Property value iterator.
 229      */
 230     public Iterator<Object> valueIterator() {
 231         return new ValueIterator(this);
 232     }
 233 
 234     /**
 235      * ECMA 8.10.1 IsAccessorDescriptor ( Desc )
 236      * @return true if this has a {@link AccessorPropertyDescriptor} with a getter or a setter
 237      */
 238     public final boolean isAccessorDescriptor() {
 239         return has(GET) || has(SET);
 240     }
 241 
 242     /**
 243      * ECMA 8.10.2 IsDataDescriptor ( Desc )
 244      * @return true if this has a {@link DataPropertyDescriptor}, i.e. the object has a property value and is writable
 245      */
 246     public final boolean isDataDescriptor() {
 247         return has(VALUE) || has(WRITABLE);
 248     }
 249 
 250     /**
 251      * ECMA 8.10.3 IsGenericDescriptor ( Desc )
 252      * @return true if this has a descriptor describing an {@link AccessorPropertyDescriptor} or {@link DataPropertyDescriptor}
 253      */
 254     public final boolean isGenericDescriptor() {
 255         return isAccessorDescriptor() || isDataDescriptor();
 256     }
 257 
 258     /**
 259       * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
 260       *
 261       * @return property descriptor
 262       */
 263     public final PropertyDescriptor toPropertyDescriptor() {
 264         final GlobalObject global = (GlobalObject) Context.getGlobalTrusted();
 265 
 266         final PropertyDescriptor desc;
 267         if (isDataDescriptor()) {
 268             if (has(SET) || has(GET)) {
 269                 throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
 270             }
 271 
 272             desc = global.newDataDescriptor(UNDEFINED, false, false, false);
 273         } else if (isAccessorDescriptor()) {
 274             if (has(VALUE) || has(WRITABLE)) {
 275                 throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
 276             }
 277 
 278             desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false);
 279         } else {
 280             desc = global.newGenericDescriptor(false, false);
 281         }
 282 
 283         return desc.fillFrom(this);
 284     }
 285 
 286     /**
 287      * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
 288      *
 289      * @param global  global scope object
 290      * @param obj object to create property descriptor from
 291      *
 292      * @return property descriptor
 293      */
 294     public static PropertyDescriptor toPropertyDescriptor(final ScriptObject global, final Object obj) {
 295         if (obj instanceof ScriptObject) {
 296             return ((ScriptObject)obj).toPropertyDescriptor();
 297         }
 298 
 299         throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj));
 300     }
 301 
 302     /**
 303      * ECMA 8.12.1 [[GetOwnProperty]] (P)
 304      *
 305      * @param key property key
 306      *
 307      * @return Returns the Property Descriptor of the named own property of this
 308      * object, or undefined if absent.
 309      */
 310     public Object getOwnPropertyDescriptor(final String key) {
 311         final Property property = getMap().findProperty(key);
 312 
 313         final GlobalObject global = (GlobalObject)Context.getGlobalTrusted();
 314 
 315         if (property != null) {
 316             final ScriptFunction get   = property.getGetterFunction(this);
 317             final ScriptFunction set   = property.getSetterFunction(this);
 318 
 319             final boolean configurable = property.isConfigurable();
 320             final boolean enumerable   = property.isEnumerable();
 321             final boolean writable     = property.isWritable();
 322 
 323             if (property instanceof UserAccessorProperty) {
 324                 return global.newAccessorDescriptor(
 325                     (get != null) ?
 326                         get :
 327                         UNDEFINED,
 328                     (set != null) ?
 329                         set :
 330                         UNDEFINED,
 331                     configurable,
 332                     enumerable);
 333             }
 334 
 335             return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable);
 336         }
 337 
 338         final int index = ArrayIndex.getArrayIndex(key);
 339         final ArrayData array = getArray();
 340 
 341         if (array.has(index)) {
 342             return array.getDescriptor(global, index);
 343         }
 344 
 345         return UNDEFINED;
 346     }
 347 
 348     /**
 349      * ECMA 8.12.2 [[GetProperty]] (P)
 350      *
 351      * @param key property key
 352      *
 353      * @return Returns the fully populated Property Descriptor of the named property
 354      * of this object, or undefined if absent.
 355      */
 356     public Object getPropertyDescriptor(final String key) {
 357         final Object res = getOwnPropertyDescriptor(key);
 358 
 359         if (res != UNDEFINED) {
 360             return res;
 361         } else if (getProto() != null) {
 362             return getProto().getOwnPropertyDescriptor(key);
 363         } else {
 364             return UNDEFINED;
 365         }
 366     }
 367 
 368     /**
 369      * ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw)
 370      *
 371      * @param key the property key
 372      * @param propertyDesc the property descriptor
 373      * @param reject is the property extensible - true means new definitions are rejected
 374      *
 375      * @return true if property was successfully defined
 376      */
 377     public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
 378         final ScriptObject       global  = Context.getGlobalTrusted();
 379         final PropertyDescriptor desc    = toPropertyDescriptor(global, propertyDesc);
 380         final Object             current = getOwnPropertyDescriptor(key);
 381         final String             name    = JSType.toString(key);
 382 
 383         if (current == UNDEFINED) {
 384             if (isExtensible()) {
 385                 // add a new own property
 386                 addOwnProperty(key, desc);
 387                 return true;
 388             }
 389             // new property added to non-extensible object
 390             if (reject) {
 391                 throw typeError(global, "object.non.extensible", name, ScriptRuntime.safeToString(this));
 392             }
 393             return false;
 394         }
 395         // modifying an existing property
 396         final PropertyDescriptor currentDesc = (PropertyDescriptor) current;
 397         final PropertyDescriptor newDesc     = desc;
 398 
 399         if (newDesc.type() == PropertyDescriptor.GENERIC &&
 400             ! newDesc.has(CONFIGURABLE) && ! newDesc.has(ENUMERABLE)) {
 401             // every descriptor field is absent
 402             return true;
 403         }
 404 
 405         if (currentDesc.equals(newDesc)) {
 406             // every descriptor field of the new is same as the current
 407             return true;
 408         }
 409 
 410         if (! currentDesc.isConfigurable()) {
 411             if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) {
 412                 // not configurable can not be made configurable
 413                 if (reject) {
 414                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
 415                 }
 416                 return false;
 417             }
 418 
 419             if (newDesc.has(ENUMERABLE) &&
 420                 currentDesc.isEnumerable() != newDesc.isEnumerable()) {
 421                 // cannot make non-enumerable as enumerable or vice-versa
 422                 if (reject) {
 423                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
 424                 }
 425                 return false;
 426             }
 427         }
 428 
 429         int propFlags = Property.mergeFlags(currentDesc, newDesc);
 430         Property property = getMap().findProperty(key);
 431 
 432         if (currentDesc.type() == PropertyDescriptor.DATA &&
 433             (newDesc.type() == PropertyDescriptor.DATA || newDesc.type() == PropertyDescriptor.GENERIC)) {
 434             if (! currentDesc.isConfigurable() && ! currentDesc.isWritable()) {
 435                 if (newDesc.has(WRITABLE) && newDesc.isWritable() ||
 436                     newDesc.has(VALUE) && ! ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) {
 437                     if (reject) {
 438                         throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
 439                     }
 440                     return false;
 441                 }
 442             }
 443 
 444             final boolean newValue = newDesc.has(VALUE);
 445             final Object value     = newValue? newDesc.getValue() : currentDesc.getValue();
 446             if (newValue && property != null) {
 447                 // Temporarily clear flags.
 448                 property = modifyOwnProperty(property, 0);
 449                 set(key, value, false);
 450             }
 451 
 452             if (property == null) {
 453                 // promoting an arrayData value to actual property
 454                 addOwnProperty(key, propFlags, value);
 455                 removeArraySlot(key);
 456             } else {
 457                 // Now set the new flags
 458                 modifyOwnProperty(property, propFlags);
 459             }
 460         } else if (currentDesc.type() == PropertyDescriptor.ACCESSOR &&
 461                    (newDesc.type() == PropertyDescriptor.ACCESSOR ||
 462                     newDesc.type() == PropertyDescriptor.GENERIC)) {
 463             if (! currentDesc.isConfigurable()) {
 464                 if (newDesc.has(PropertyDescriptor.GET) && ! ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) ||
 465                     newDesc.has(PropertyDescriptor.SET) && ! ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) {
 466                     if (reject) {
 467                         throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
 468                     }
 469                     return false;
 470                 }
 471             }
 472 
 473             // New set the new features.
 474             modifyOwnProperty(property, propFlags,
 475                                       newDesc.has(GET) ? newDesc.getGetter() : currentDesc.getGetter(),
 476                                       newDesc.has(SET) ? newDesc.getSetter() : currentDesc.getSetter());
 477         } else {
 478             // changing descriptor type
 479             if (! currentDesc.isConfigurable()) {
 480                 // not configurable can not be made configurable
 481                 if (reject) {
 482                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
 483                 }
 484                 return false;
 485             }
 486 
 487             propFlags = 0;
 488 
 489             // Preserve only configurable and enumerable from current desc
 490             // if those are not overridden in the new property descriptor.
 491             boolean value = newDesc.has(CONFIGURABLE)? newDesc.isConfigurable() : currentDesc.isConfigurable();
 492             if (!value) {
 493                 propFlags |= Property.NOT_CONFIGURABLE;
 494             }
 495             value = newDesc.has(ENUMERABLE)? newDesc.isEnumerable() : currentDesc.isEnumerable();
 496             if (!value) {
 497                 propFlags |= Property.NOT_ENUMERABLE;
 498             }
 499 
 500             final int type = newDesc.type();
 501             if (type == PropertyDescriptor.DATA) {
 502                 // get writable from the new descriptor
 503                 value = newDesc.has(WRITABLE) && newDesc.isWritable();
 504                 if (! value) {
 505                     propFlags |= Property.NOT_WRITABLE;
 506                 }
 507 
 508                 // delete the old property
 509                 deleteOwnProperty(property);
 510                 // add new data property
 511                 addOwnProperty(key, propFlags, newDesc.getValue());
 512             } else if (type == PropertyDescriptor.ACCESSOR) {
 513                 if (property == null) {
 514                     addOwnProperty(key, propFlags,
 515                                      newDesc.has(GET) ? newDesc.getGetter() : null,
 516                                      newDesc.has(SET) ? newDesc.getSetter() : null);
 517                 } else {
 518                     // Modify old property with the new features.
 519                     modifyOwnProperty(property, propFlags,
 520                                         newDesc.has(GET) ? newDesc.getGetter() : null,
 521                                         newDesc.has(SET) ? newDesc.getSetter() : null);
 522                 }
 523             }
 524         }
 525 
 526         checkIntegerKey(key);
 527 
 528         return true;
 529     }
 530 
 531     /**
 532      * Spec. mentions use of [[DefineOwnProperty]] for indexed properties in
 533      * certain places (eg. Array.prototype.map, filter). We can not use ScriptObject.set
 534      * method in such cases. This is because set method uses inherited setters (if any)
 535      * from any object in proto chain such as Array.prototype, Object.prototype.
 536      * This method directly sets a particular element value in the current object.
 537      *
 538      * @param index key for property
 539      * @param value value to define
 540      */
 541     protected final void defineOwnProperty(final int index, final Object value) {
 542         assert ArrayIndex.isValidArrayIndex(index) : "invalid array index";
 543         final long longIndex = ArrayIndex.toLongIndex(index);
 544         if (longIndex >= getArray().length()) {
 545             // make array big enough to hold..
 546             setArray(getArray().ensure(longIndex));
 547         }
 548         setArray(getArray().set(index, value, false));
 549     }
 550 
 551     private void checkIntegerKey(final String key) {
 552         final int index = ArrayIndex.getArrayIndex(key);
 553 
 554         if (ArrayIndex.isValidArrayIndex(index)) {
 555             final ArrayData data = getArray();
 556 
 557             if (data.has(index)) {
 558                 setArray(data.delete(index));
 559             }
 560         }
 561     }
 562 
 563     private void removeArraySlot(final String key) {
 564         final int index = ArrayIndex.getArrayIndex(key);
 565         final ArrayData array = getArray();
 566 
 567         if (array.has(index)) {
 568             setArray(array.delete(index));
 569         }
 570     }
 571 
 572     /**
 573       * Add a new property to the object.
 574       *
 575       * @param key          property key
 576       * @param propertyDesc property descriptor for property
 577       */
 578     public final void addOwnProperty(final String key, final PropertyDescriptor propertyDesc) {
 579         // Already checked that there is no own property with that key.
 580         PropertyDescriptor pdesc = propertyDesc;
 581 
 582         final int propFlags = Property.toFlags(pdesc);
 583 
 584         if (pdesc.type() == PropertyDescriptor.GENERIC) {
 585             final GlobalObject global = (GlobalObject) Context.getGlobalTrusted();
 586             final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false);
 587 
 588             dDesc.fillFrom((ScriptObject)pdesc);
 589             pdesc = dDesc;
 590         }
 591 
 592         final int type = pdesc.type();
 593         if (type == PropertyDescriptor.DATA) {
 594             addOwnProperty(key, propFlags, pdesc.getValue());
 595         } else if (type == PropertyDescriptor.ACCESSOR) {
 596             addOwnProperty(key, propFlags,
 597                     pdesc.has(GET) ? pdesc.getGetter() : null,
 598                     pdesc.has(SET) ? pdesc.getSetter() : null);
 599         }
 600 
 601         checkIntegerKey(key);
 602     }
 603 
 604     /**
 605      * Low level property API (not using property descriptors)
 606      * <p>
 607      * Find a property in the prototype hierarchy. Note: this is final and not
 608      * a good idea to override. If you have to, use
 609      * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
 610      * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
 611      * overriding way to find array properties
 612      *
 613      * @see jdk.nashorn.internal.objects.NativeArray
 614      *
 615      * @param key  Property key.
 616      * @param deep Whether the search should look up proto chain.
 617      *
 618      * @return FindPropertyData or null if not found.
 619      */
 620     public final FindProperty findProperty(final String key, final boolean deep) {
 621         return findProperty(key, deep, false, this);
 622     }
 623 
 624     /**
 625      * Low level property API (not using property descriptors)
 626      * <p>
 627      * Find a property in the prototype hierarchy. Note: this is not a good idea
 628      * to override except as it was done in {@link WithObject}.
 629      * If you have to, use
 630      * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
 631      * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
 632      * overriding way to find array properties
 633      *
 634      * @see jdk.nashorn.internal.objects.NativeArray
 635      *
 636      * @param key  Property key.
 637      * @param deep Whether the search should look up proto chain.
 638      * @param stopOnNonScope should a deep search stop on the first non-scope object?
 639      * @param start the object on which the lookup was originally initiated
 640      *
 641      * @return FindPropertyData or null if not found.
 642      */
 643     FindProperty findProperty(final String key, final boolean deep, final boolean stopOnNonScope, final ScriptObject start) {
 644         // if doing deep search, stop search on the first non-scope object if asked to do so
 645         if (stopOnNonScope && start != this && !isScope()) {
 646             return null;
 647         }
 648 
 649         final PropertyMap selfMap  = getMap();
 650         final Property    property = selfMap.findProperty(key);
 651 
 652         if (property != null) {
 653             return new FindProperty(start, this, property);
 654         }
 655 
 656         if (deep) {
 657             final ScriptObject myProto = getProto();
 658             if (myProto != null) {
 659                 return myProto.findProperty(key, deep, stopOnNonScope, start);
 660             }
 661         }
 662 
 663         return null;
 664     }
 665 
 666     /**
 667      * Add a new property to the object.
 668      * <p>
 669      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
 670      *
 671      * @param key             Property key.
 672      * @param propertyFlags   Property flags.
 673      * @param getter          Property getter, or null if not defined
 674      * @param setter          Property setter, or null if not defined
 675      *
 676      * @return New property.
 677      */
 678     public final Property addOwnProperty(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
 679         return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter));
 680     }
 681 
 682     /**
 683      * Add a new property to the object.
 684      * <p>
 685      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
 686      *
 687      * @param key             Property key.
 688      * @param propertyFlags   Property flags.
 689      * @param value           Value of property
 690      *
 691      * @return New property.
 692      */
 693     public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) {
 694         final Property property = addSpillProperty(key, propertyFlags);
 695         property.setObjectValue(this, this, value, false);
 696         return property;
 697     }
 698 
 699     /**
 700      * Add a new property to the object.
 701      * <p>
 702      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
 703      *
 704      * @param newProperty property to add
 705      *
 706      * @return New property.
 707      */
 708     public final Property addOwnProperty(final Property newProperty) {
 709         PropertyMap oldMap = getMap();
 710 
 711         while (true) {
 712             final PropertyMap newMap = oldMap.addProperty(newProperty);
 713 
 714             if (!compareAndSetMap(oldMap, newMap)) {
 715                 oldMap = getMap();
 716                 final Property oldProperty = oldMap.findProperty(newProperty.getKey());
 717 
 718                 if (oldProperty != null) {
 719                     return oldProperty;
 720                 }
 721             } else {
 722                 return newProperty;
 723             }
 724         }
 725     }
 726 
 727     private void erasePropertyValue(final Property property) {
 728         // Erase the property field value with undefined. If the property is defined
 729         // by user-defined accessors, we don't want to call the setter!!
 730         if (!(property instanceof UserAccessorProperty)) {
 731             property.setObjectValue(this, this, UNDEFINED, false);
 732         }
 733     }
 734 
 735     /**
 736      * Delete a property from the object.
 737      *
 738      * @param property Property to delete.
 739      *
 740      * @return true if deleted.
 741      */
 742     public final boolean deleteOwnProperty(final Property property) {
 743         erasePropertyValue(property);
 744         PropertyMap oldMap = getMap();
 745 
 746         while (true) {
 747             final PropertyMap newMap = oldMap.deleteProperty(property);
 748 
 749             if (newMap == null) {
 750                 return false;
 751             }
 752 
 753             if (!compareAndSetMap(oldMap, newMap)) {
 754                 oldMap = getMap();
 755             } else {
 756                 // delete getter and setter function references so that we don't leak
 757                 if (property instanceof UserAccessorProperty) {
 758                     final UserAccessorProperty uc = (UserAccessorProperty) property;
 759                     setSpill(uc.getGetterSlot(), null);
 760                     setSpill(uc.getSetterSlot(), null);
 761                 }
 762                 return true;
 763             }
 764         }
 765     }
 766 
 767     /**
 768      * Modify a property in the object
 769      *
 770      * @param oldProperty    property to modify
 771      * @param propertyFlags  new property flags
 772      * @param getter         getter for {@link UserAccessorProperty}, null if not present or N/A
 773      * @param setter         setter for {@link UserAccessorProperty}, null if not present or N/A
 774      *
 775      * @return new property
 776      */
 777     public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
 778         Property newProperty;
 779         if (oldProperty instanceof UserAccessorProperty) {
 780             // re-use the slots of the old user accessor property.
 781             final UserAccessorProperty uc = (UserAccessorProperty) oldProperty;
 782 
 783             int getterSlot = uc.getGetterSlot();
 784             // clear the old getter and set the new getter
 785             setSpill(getterSlot, getter);
 786             // if getter function is null, flag the slot to be negative (less by 1)
 787             if (getter == null) {
 788                 getterSlot = -getterSlot - 1;
 789             }
 790 
 791             int setterSlot = uc.getSetterSlot();
 792             // clear the old setter and set the new setter
 793             setSpill(setterSlot, setter);
 794             // if setter function is null, flag the slot to be negative (less by 1)
 795             if (setter == null) {
 796                 setterSlot = -setterSlot - 1;
 797             }
 798 
 799             newProperty = new UserAccessorProperty(oldProperty.getKey(), propertyFlags, getterSlot, setterSlot);
 800             // if just flipping getter and setter with new functions, no need to change property or map
 801             if (oldProperty.equals(newProperty)) {
 802                 return oldProperty;
 803             }
 804         } else {
 805             // erase old property value and create new user accessor property
 806             erasePropertyValue(oldProperty);
 807             newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter);
 808         }
 809 
 810         notifyPropertyModified(this, oldProperty, newProperty);
 811 
 812         return modifyOwnProperty(oldProperty, newProperty);
 813     }
 814 
 815     /**
 816       * Modify a property in the object
 817       *
 818       * @param oldProperty    property to modify
 819       * @param propertyFlags  new property flags
 820       *
 821       * @return new property
 822       */
 823     public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags) {
 824         return modifyOwnProperty(oldProperty, oldProperty.setFlags(propertyFlags));
 825     }
 826 
 827     /**
 828      * Modify a property in the object, replacing a property with a new one
 829      *
 830      * @param oldProperty   property to replace
 831      * @param newProperty   property to replace it with
 832      *
 833      * @return new property
 834      */
 835     private Property modifyOwnProperty(final Property oldProperty, final Property newProperty) {
 836         assert newProperty.getKey().equals(oldProperty.getKey()) : "replacing property with different key";
 837 
 838         PropertyMap oldMap = getMap();
 839 
 840         while (true) {
 841             final PropertyMap newMap = oldMap.replaceProperty(oldProperty, newProperty);
 842 
 843             if (!compareAndSetMap(oldMap, newMap)) {
 844                 oldMap = getMap();
 845                 final Property oldPropertyLookup = oldMap.findProperty(oldProperty.getKey());
 846 
 847                 if (oldPropertyLookup != null && oldPropertyLookup.equals(newProperty)) {
 848                     return oldPropertyLookup;
 849                 }
 850             } else {
 851                 return newProperty;
 852             }
 853         }
 854     }
 855 
 856     /**
 857      * Update getter and setter in an object literal.
 858      *
 859      * @param key    Property key.
 860      * @param getter {@link UserAccessorProperty} defined getter, or null if none
 861      * @param setter {@link UserAccessorProperty} defined setter, or null if none
 862      */
 863     public final void setUserAccessors(final String key, final ScriptFunction getter, final ScriptFunction setter) {
 864         final Property oldProperty = getMap().findProperty(key);
 865         if (oldProperty != null) {
 866             final UserAccessorProperty newProperty = newUserAccessors(oldProperty.getKey(), oldProperty.getFlags(), getter, setter);
 867             modifyOwnProperty(oldProperty, newProperty);
 868         } else {
 869             final UserAccessorProperty newProperty = newUserAccessors(key, 0, getter, setter);
 870             addOwnProperty(newProperty);
 871         }
 872     }
 873 
 874     private static int getIntValue(final FindProperty find) {
 875         final MethodHandle getter = find.getGetter(int.class);
 876         if (getter != null) {
 877             try {
 878                 return (int)getter.invokeExact((Object)find.getGetterReceiver());
 879             } catch (final Error|RuntimeException e) {
 880                 throw e;
 881             } catch (final Throwable e) {
 882                 throw new RuntimeException(e);
 883             }
 884         }
 885 
 886         return ObjectClassGenerator.UNDEFINED_INT;
 887     }
 888 
 889     private static long getLongValue(final FindProperty find) {
 890         final MethodHandle getter = find.getGetter(long.class);
 891         if (getter != null) {
 892             try {
 893                 return (long)getter.invokeExact((Object)find.getGetterReceiver());
 894             } catch (final Error|RuntimeException e) {
 895                 throw e;
 896             } catch (final Throwable e) {
 897                 throw new RuntimeException(e);
 898             }
 899         }
 900 
 901         return ObjectClassGenerator.UNDEFINED_LONG;
 902     }
 903 
 904     private static double getDoubleValue(final FindProperty find) {
 905         final MethodHandle getter = find.getGetter(double.class);
 906         if (getter != null) {
 907             try {
 908                 return (double)getter.invokeExact((Object)find.getGetterReceiver());
 909             } catch (final Error|RuntimeException e) {
 910                 throw e;
 911             } catch (final Throwable e) {
 912                 throw new RuntimeException(e);
 913             }
 914         }
 915 
 916         return ObjectClassGenerator.UNDEFINED_DOUBLE;
 917     }
 918 
 919     /**
 920       * Get the object value of a property
 921       *
 922       * @param find {@link FindProperty} lookup result
 923       *
 924       * @return the value of the property
 925       */
 926     protected static Object getObjectValue(final FindProperty find) {
 927         return find.getObjectValue();
 928     }
 929 
 930     /**
 931      * Return methodHandle of value function for call.
 932      *
 933      * @param find      data from find property.
 934      * @param type      method type of function.
 935      * @param bindName  null or name to bind to second argument (property not found method.)
 936      *
 937      * @return value of property as a MethodHandle or null.
 938      */
 939     protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) {
 940         return getCallMethodHandle(getObjectValue(find), type, bindName);
 941     }
 942 
 943     /**
 944      * Return methodHandle of value function for call.
 945      *
 946      * @param value     value of receiver, it not a {@link ScriptFunction} this will return null.
 947      * @param type      method type of function.
 948      * @param bindName  null or name to bind to second argument (property not found method.)
 949      *
 950      * @return value of property as a MethodHandle or null.
 951      */
 952     protected static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) {
 953         return value instanceof ScriptFunction ? ((ScriptFunction)value).getCallMethodHandle(type, bindName) : null;
 954     }
 955 
 956     /**
 957      * Get value using found property.
 958      *
 959      * @param property Found property.
 960      *
 961      * @return Value of property.
 962      */
 963     public final Object getWithProperty(final Property property) {
 964         return getObjectValue(new FindProperty(this, this, property));
 965     }
 966 
 967     /**
 968      * Get a property given a key
 969      *
 970      * @param key property key
 971      *
 972      * @return property for key
 973      */
 974     public final Property getProperty(final String key) {
 975         return getMap().findProperty(key);
 976     }
 977 
 978     static String convertKey(final Object key) {
 979         return (key instanceof String) ? (String)key : JSType.toString(key);
 980     }
 981 
 982     /**
 983      * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
 984      * Used for argument access in a vararg function using parameter name.
 985      * Returns the argument at a given key (index)
 986      *
 987      * @param key argument index
 988      *
 989      * @return the argument at the given position, or undefined if not present
 990      */
 991     public Object getArgument(final int key) {
 992         return get(key);
 993     }
 994 
 995     /**
 996      * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
 997      * Used for argument access in a vararg function using parameter name.
 998      * Returns the argument at a given key (index)
 999      *
1000      * @param key   argument index
1001      * @param value the value to write at the given index
1002      */
1003     public void setArgument(final int key, final Object value) {
1004         set(key, value, false);
1005     }
1006 
1007     /**
1008      * Return true if the script object context is strict
1009      * @return true if strict context
1010      */
1011     public final boolean isStrictContext() {
1012         return getContext()._strict;
1013     }
1014 
1015     /**
1016      * Return the current context from the object's map.
1017      * @return Current context.
1018      */
1019     protected final Context getContext() {
1020         if (context == null) {
1021             context = Context.fromClass(getClass());
1022         }
1023         return context;
1024     }
1025 
1026     /**
1027      * Return the map of an object.
1028      * @return PropertyMap object.
1029      */
1030     public final PropertyMap getMap() {
1031         return map;
1032     }
1033 
1034     /**
1035      * Set the initial map.
1036      * @param map Initial map.
1037      */
1038     public final void setMap(final PropertyMap map) {
1039         this.map = map;
1040     }
1041 
1042     /**
1043      * Conditionally set the new map if the old map is the same.
1044      * @param oldMap Map prior to manipulation.
1045      * @param newMap Replacement map.
1046      * @return true if the operation succeeded.
1047      */
1048     protected synchronized final boolean compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap) {
1049         final boolean update = oldMap == this.map;
1050 
1051         if (update) {
1052             this.map = newMap;
1053         }
1054 
1055         return update;
1056      }
1057 
1058     /**
1059      * Return the __proto__ of an object.
1060      * @return __proto__ object.
1061      */
1062     public final ScriptObject getProto() {
1063         return proto;
1064     }
1065 
1066     /**
1067      * Set the __proto__ of an object.
1068      * @param newProto new __proto__ to set.
1069      */
1070     public synchronized final void setProto(final ScriptObject newProto) {
1071         final ScriptObject oldProto = proto;
1072         map = map.changeProto(oldProto, newProto);
1073 
1074         if (newProto != null) {
1075             newProto.setIsPrototype();
1076         }
1077 
1078         proto = newProto;
1079 
1080         if (isPrototype()) {
1081             if (oldProto != null) {
1082                 oldProto.removePropertyListener(this);
1083             }
1084 
1085             if (newProto != null) {
1086                 newProto.addPropertyListener(this);
1087             }
1088         }
1089     }
1090 
1091     /**
1092      * Set the __proto__ of an object with checks.
1093      * @param newProto Prototype to set.
1094      */
1095     public final void setProtoCheck(final Object newProto) {
1096         if (newProto == null || newProto instanceof ScriptObject) {
1097             setProto((ScriptObject)newProto);
1098         } else {
1099             final ScriptObject global = Context.getGlobalTrusted();
1100             final Object  newProtoObject = JSType.toScriptObject(global, newProto);
1101 
1102             if (newProtoObject instanceof ScriptObject) {
1103                 setProto((ScriptObject)newProtoObject);
1104             } else {
1105                 throw typeError(global, "cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
1106             }
1107         }
1108     }
1109 
1110     /**
1111      * return a List of own keys associated with the object.

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