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