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