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