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