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