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