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