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