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