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