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