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