src/jdk/nashorn/internal/runtime/ScriptObject.java

Wed, 24 Apr 2013 13:28:25 +0200

author
hannesw
date
Wed, 24 Apr 2013 13:28:25 +0200
changeset 219
a6c53280343d
parent 217
0547a1c76259
child 228
5c98cc846f92
permissions
-rw-r--r--

8012334: ToUint32, ToInt32, and ToUint16 don't conform to spec
Reviewed-by: lagergren, attila

     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  */
    26 package jdk.nashorn.internal.runtime;
    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.lookup.Lookup.MH;
    32 import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
    33 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
    34 import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE;
    35 import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE;
    36 import static jdk.nashorn.internal.runtime.PropertyDescriptor.GET;
    37 import static jdk.nashorn.internal.runtime.PropertyDescriptor.SET;
    38 import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE;
    39 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
    40 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
    41 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndexNoThrow;
    42 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
    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.internal.dynalink.CallSiteDescriptor;
    59 import jdk.internal.dynalink.linker.GuardedInvocation;
    60 import jdk.internal.dynalink.linker.LinkRequest;
    61 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
    62 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
    63 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
    64 import jdk.nashorn.internal.lookup.Lookup;
    65 import jdk.nashorn.internal.lookup.MethodHandleFactory;
    66 import jdk.nashorn.internal.objects.AccessorPropertyDescriptor;
    67 import jdk.nashorn.internal.objects.DataPropertyDescriptor;
    68 import jdk.nashorn.internal.runtime.arrays.ArrayData;
    69 import jdk.nashorn.internal.runtime.linker.Bootstrap;
    70 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
    71 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
    72 import jdk.nashorn.internal.runtime.linker.NashornGuards;
    74 /**
    75  * Base class for generic JavaScript objects.
    76  * <p>
    77  * Notes:
    78  * <ul>
    79  * <li>The map is used to identify properties in the object.</li>
    80  * <li>If the map is modified then it must be cloned and replaced.  This notifies
    81  *     any code that made assumptions about the object that things have changed.
    82  *     Ex. CallSites that have been validated must check to see if the map has
    83  *     changed (or a map from a different object type) and hence relink the method
    84  *     to call.</li>
    85  * <li>Modifications of the map include adding/deleting attributes or changing a
    86  *     function field value.</li>
    87  * </ul>
    88  */
    90 public abstract class ScriptObject extends PropertyListenerManager implements PropertyAccess {
    92     /** Search fall back routine name for "no such method" */
    93     static final String NO_SUCH_METHOD_NAME   = "__noSuchMethod__";
    95     /** Search fall back routine name for "no such property" */
    96     static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__";
    98     /** Per ScriptObject flag - is this a scope object? */
    99     public static final int IS_SCOPE       = 0b0000_0001;
   101     /** Per ScriptObject flag - is this an array object? */
   102     public static final int IS_ARRAY       = 0b0000_0010;
   104     /** Per ScriptObject flag - is this an arguments object? */
   105     public static final int IS_ARGUMENTS   = 0b0000_0100;
   107     /** Spill growth rate - by how many elements does {@link ScriptObject#spill} when full */
   108     public static final int SPILL_RATE = 8;
   110     /** Map to property information and accessor functions. Ordered by insertion. */
   111     private PropertyMap map;
   113     /** Object flags. */
   114     private int flags;
   116     /** Area for properties added to object after instantiation, see {@link SpillProperty} */
   117     public Object[] spill;
   119     /** Local embed area position 0 - used for {@link SpillProperty} before {@link ScriptObject#spill} */
   120     public Object embed0;
   122     /** Local embed area position 1 - used for {@link SpillProperty} before {@link ScriptObject#spill} */
   123     public Object embed1;
   125     /** Local embed area position 2 - used for {@link SpillProperty} before {@link ScriptObject#spill} */
   126     public Object embed2;
   128     /** Local embed area position 3 - used for {@link SpillProperty} before {@link ScriptObject#spill} */
   129     public Object embed3;
   131     /** Indexed array data. */
   132     private ArrayData arrayData;
   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);
   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);
   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);
   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);
   148     /** Method handle for getting the proto of a ScriptObject */
   149     public static final Call GET_PROTO          = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class);
   151     /** Method handle for setting the proto of a ScriptObject */
   152     public static final Call SET_PROTO          = virtualCallNoLookup(ScriptObject.class, "setProto", void.class, ScriptObject.class);
   154     /** Method handle for setting the user accessors of a ScriptObject */
   155     public static final Call SET_USER_ACCESSORS = virtualCall(ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class);
   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);
   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);
   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);
   168     /**
   169      * Constructor
   170      */
   171     public ScriptObject() {
   172         this(null);
   173     }
   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         }
   185         this.arrayData = ArrayData.EMPTY_ARRAY;
   187         if (map == null) {
   188             this.setMap(PropertyMap.newMap(getClass()));
   189             return;
   190         }
   192         this.setMap(map);
   193     }
   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();
   204         for (final Property property : source.getMap().getProperties()) {
   205             final String key = property.getKey();
   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         }
   217         this.setMap(newMap);
   218     }
   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     }
   231     /**
   232      * Return a property iterator.
   233      * @return Property iterator.
   234      */
   235     public Iterator<String> propertyIterator() {
   236         return new KeyIterator(this);
   237     }
   239     /**
   240      * Return a property value iterator.
   241      * @return Property value iterator.
   242      */
   243     public Iterator<Object> valueIterator() {
   244         return new ValueIterator(this);
   245     }
   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     }
   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     }
   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     }
   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();
   279         final PropertyDescriptor desc;
   280         if (isDataDescriptor()) {
   281             if (has(SET) || has(GET)) {
   282                 throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
   283             }
   285             desc = global.newDataDescriptor(UNDEFINED, false, false, false);
   286         } else if (isAccessorDescriptor()) {
   287             if (has(VALUE) || has(WRITABLE)) {
   288                 throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
   289             }
   291             desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false);
   292         } else {
   293             desc = global.newGenericDescriptor(false, false);
   294         }
   296         return desc.fillFrom(this);
   297     }
   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         }
   312         throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj));
   313     }
   315     /**
   316      * ECMA 8.12.1 [[GetOwnProperty]] (P)
   317      *
   318      * @param key property key
   319      *
   320      * @return Returns the Property Descriptor of the named own property of this
   321      * object, or undefined if absent.
   322      */
   323     public Object getOwnPropertyDescriptor(final String key) {
   324         final Property property = getMap().findProperty(key);
   326         final GlobalObject global = (GlobalObject)Context.getGlobalTrusted();
   328         if (property != null) {
   329             final ScriptFunction get   = property.getGetterFunction(this);
   330             final ScriptFunction set   = property.getSetterFunction(this);
   332             final boolean configurable = property.isConfigurable();
   333             final boolean enumerable   = property.isEnumerable();
   334             final boolean writable     = property.isWritable();
   336             if (property instanceof UserAccessorProperty) {
   337                 return global.newAccessorDescriptor(
   338                     (get != null) ?
   339                         get :
   340                         UNDEFINED,
   341                     (set != null) ?
   342                         set :
   343                         UNDEFINED,
   344                     configurable,
   345                     enumerable);
   346             }
   348             return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable);
   349         }
   351         final int index = getArrayIndexNoThrow(key);
   352         final ArrayData array = getArray();
   354         if (array.has(index)) {
   355             return array.getDescriptor(global, index);
   356         }
   358         return UNDEFINED;
   359     }
   361     /**
   362      * ECMA 8.12.2 [[GetProperty]] (P)
   363      *
   364      * @param key property key
   365      *
   366      * @return Returns the fully populated Property Descriptor of the named property
   367      * of this object, or undefined if absent.
   368      */
   369     public Object getPropertyDescriptor(final String key) {
   370         final Object res = getOwnPropertyDescriptor(key);
   372         if (res != UNDEFINED) {
   373             return res;
   374         } else if (getProto() != null) {
   375             return getProto().getOwnPropertyDescriptor(key);
   376         } else {
   377             return UNDEFINED;
   378         }
   379     }
   381     /**
   382      * ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw)
   383      *
   384      * @param key the property key
   385      * @param propertyDesc the property descriptor
   386      * @param reject is the property extensible - true means new definitions are rejected
   387      *
   388      * @return true if property was successfully defined
   389      */
   390     public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
   391         final ScriptObject       global  = Context.getGlobalTrusted();
   392         final PropertyDescriptor desc    = toPropertyDescriptor(global, propertyDesc);
   393         final Object             current = getOwnPropertyDescriptor(key);
   394         final String             name    = JSType.toString(key);
   396         if (current == UNDEFINED) {
   397             if (isExtensible()) {
   398                 // add a new own property
   399                 addOwnProperty(key, desc);
   400                 return true;
   401             }
   402             // new property added to non-extensible object
   403             if (reject) {
   404                 throw typeError(global, "object.non.extensible", name, ScriptRuntime.safeToString(this));
   405             }
   406             return false;
   407         }
   408         // modifying an existing property
   409         final PropertyDescriptor currentDesc = (PropertyDescriptor) current;
   410         final PropertyDescriptor newDesc     = desc;
   412         if (newDesc.type() == PropertyDescriptor.GENERIC &&
   413             ! newDesc.has(CONFIGURABLE) && ! newDesc.has(ENUMERABLE)) {
   414             // every descriptor field is absent
   415             return true;
   416         }
   418         if (currentDesc.equals(newDesc)) {
   419             // every descriptor field of the new is same as the current
   420             return true;
   421         }
   423         if (! currentDesc.isConfigurable()) {
   424             if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) {
   425                 // not configurable can not be made configurable
   426                 if (reject) {
   427                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   428                 }
   429                 return false;
   430             }
   432             if (newDesc.has(ENUMERABLE) &&
   433                 currentDesc.isEnumerable() != newDesc.isEnumerable()) {
   434                 // cannot make non-enumerable as enumerable or vice-versa
   435                 if (reject) {
   436                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   437                 }
   438                 return false;
   439             }
   440         }
   442         int propFlags = Property.mergeFlags(currentDesc, newDesc);
   443         Property property = getMap().findProperty(key);
   445         if (currentDesc.type() == PropertyDescriptor.DATA &&
   446             (newDesc.type() == PropertyDescriptor.DATA || newDesc.type() == PropertyDescriptor.GENERIC)) {
   447             if (! currentDesc.isConfigurable() && ! currentDesc.isWritable()) {
   448                 if (newDesc.has(WRITABLE) && newDesc.isWritable() ||
   449                     newDesc.has(VALUE) && ! ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) {
   450                     if (reject) {
   451                         throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   452                     }
   453                     return false;
   454                 }
   455             }
   457             final boolean newValue = newDesc.has(VALUE);
   458             final Object value     = newValue? newDesc.getValue() : currentDesc.getValue();
   459             if (newValue && property != null) {
   460                 // Temporarily clear flags.
   461                 property = modifyOwnProperty(property, 0);
   462                 set(key, value, getContext()._strict);
   463             }
   465             if (property == null) {
   466                 // promoting an arrayData value to actual property
   467                 addOwnProperty(key, propFlags, value);
   468                 removeArraySlot(key);
   469             } else {
   470                 // Now set the new flags
   471                 modifyOwnProperty(property, propFlags);
   472             }
   473         } else if (currentDesc.type() == PropertyDescriptor.ACCESSOR &&
   474                    (newDesc.type() == PropertyDescriptor.ACCESSOR ||
   475                     newDesc.type() == PropertyDescriptor.GENERIC)) {
   476             if (! currentDesc.isConfigurable()) {
   477                 if (newDesc.has(PropertyDescriptor.GET) && ! ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) ||
   478                     newDesc.has(PropertyDescriptor.SET) && ! ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) {
   479                     if (reject) {
   480                         throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   481                     }
   482                     return false;
   483                 }
   484             }
   486             // New set the new features.
   487             modifyOwnProperty(property, propFlags,
   488                                       newDesc.has(GET) ? newDesc.getGetter() : currentDesc.getGetter(),
   489                                       newDesc.has(SET) ? newDesc.getSetter() : currentDesc.getSetter());
   490         } else {
   491             // changing descriptor type
   492             if (! currentDesc.isConfigurable()) {
   493                 // not configurable can not be made configurable
   494                 if (reject) {
   495                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   496                 }
   497                 return false;
   498             }
   500             propFlags = 0;
   502             // Preserve only configurable and enumerable from current desc
   503             // if those are not overridden in the new property descriptor.
   504             boolean value = newDesc.has(CONFIGURABLE)? newDesc.isConfigurable() : currentDesc.isConfigurable();
   505             if (!value) {
   506                 propFlags |= Property.NOT_CONFIGURABLE;
   507             }
   508             value = newDesc.has(ENUMERABLE)? newDesc.isEnumerable() : currentDesc.isEnumerable();
   509             if (!value) {
   510                 propFlags |= Property.NOT_ENUMERABLE;
   511             }
   513             final int type = newDesc.type();
   514             if (type == PropertyDescriptor.DATA) {
   515                 // get writable from the new descriptor
   516                 value = newDesc.has(WRITABLE) && newDesc.isWritable();
   517                 if (! value) {
   518                     propFlags |= Property.NOT_WRITABLE;
   519                 }
   521                 // delete the old property
   522                 deleteOwnProperty(property);
   523                 // add new data property
   524                 addOwnProperty(key, propFlags, newDesc.getValue());
   525             } else if (type == PropertyDescriptor.ACCESSOR) {
   526                 if (property == null) {
   527                     addOwnProperty(key, propFlags,
   528                                      newDesc.has(GET) ? newDesc.getGetter() : null,
   529                                      newDesc.has(SET) ? newDesc.getSetter() : null);
   530                 } else {
   531                     // Modify old property with the new features.
   532                     modifyOwnProperty(property, propFlags,
   533                                         newDesc.has(GET) ? newDesc.getGetter() : null,
   534                                         newDesc.has(SET) ? newDesc.getSetter() : null);
   535                 }
   536             }
   537         }
   539         checkIntegerKey(key);
   541         return true;
   542     }
   544     /**
   545      * Spec. mentions use of [[DefineOwnProperty]] for indexed properties in
   546      * certain places (eg. Array.prototype.map, filter). We can not use ScriptObject.set
   547      * method in such cases. This is because set method uses inherited setters (if any)
   548      * from any object in proto chain such as Array.prototype, Object.prototype.
   549      * This method directly sets a particular element value in the current object.
   550      *
   551      * @param index index key for property
   552      * @param value value to define
   553      */
   554     protected final void defineOwnProperty(final int index, final Object value) {
   555         if (index >= getArray().length()) {
   556             // make array big enough to hold..
   557             setArray(getArray().ensure(index));
   558         }
   559         setArray(getArray().set(index, value, false));
   560     }
   562     private void checkIntegerKey(final String key) {
   563         final int index = getArrayIndexNoThrow(key);
   565         if (isValidArrayIndex(index)) {
   566             final ArrayData data = getArray();
   568             if (data.has(index)) {
   569                 setArray(data.delete(index));
   570             }
   571         }
   572     }
   574     private void removeArraySlot(final String key) {
   575         final int index = getArrayIndexNoThrow(key);
   576         final ArrayData array = getArray();
   578         if (array.has(index)) {
   579             setArray(array.delete(index));
   580         }
   581     }
   583     /**
   584       * Add a new property to the object.
   585       *
   586       * @param key          property key
   587       * @param propertyDesc property descriptor for property
   588       */
   589     public final void addOwnProperty(final String key, final PropertyDescriptor propertyDesc) {
   590         // Already checked that there is no own property with that key.
   591         PropertyDescriptor pdesc = propertyDesc;
   593         final int propFlags = Property.toFlags(pdesc);
   595         if (pdesc.type() == PropertyDescriptor.GENERIC) {
   596             final GlobalObject global = (GlobalObject) Context.getGlobalTrusted();
   597             final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false);
   599             dDesc.fillFrom((ScriptObject)pdesc);
   600             pdesc = dDesc;
   601         }
   603         final int type = pdesc.type();
   604         if (type == PropertyDescriptor.DATA) {
   605             addOwnProperty(key, propFlags, pdesc.getValue());
   606         } else if (type == PropertyDescriptor.ACCESSOR) {
   607             addOwnProperty(key, propFlags,
   608                     pdesc.has(GET) ? pdesc.getGetter() : null,
   609                     pdesc.has(SET) ? pdesc.getSetter() : null);
   610         }
   612         checkIntegerKey(key);
   613     }
   615     /**
   616      * Low level property API (not using property descriptors)
   617      * <p>
   618      * Find a property in the prototype hierarchy. Note: this is final and not
   619      * a good idea to override. If you have to, use
   620      * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
   621      * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
   622      * overriding way to find array properties
   623      *
   624      * @see jdk.nashorn.internal.objects.NativeArray
   625      *
   626      * @param key  Property key.
   627      * @param deep Whether the search should look up proto chain.
   628      *
   629      * @return FindPropertyData or null if not found.
   630      */
   631     public final FindProperty findProperty(final String key, final boolean deep) {
   632         return findProperty(key, deep, false, this);
   633     }
   635     /**
   636      * Low level property API (not using property descriptors)
   637      * <p>
   638      * Find a property in the prototype hierarchy. Note: this is not a good idea
   639      * to override except as it was done in {@link WithObject}.
   640      * 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      * @param start the object on which the lookup was originally initiated
   651      *
   652      * @return FindPropertyData or null if not found.
   653      */
   654     FindProperty findProperty(final String key, final boolean deep, final boolean stopOnNonScope, final ScriptObject start) {
   655         // if doing deep search, stop search on the first non-scope object if asked to do so
   656         if (stopOnNonScope && start != this && !isScope()) {
   657             return null;
   658         }
   660         final PropertyMap selfMap  = getMap();
   661         final Property    property = selfMap.findProperty(key);
   663         if (property != null) {
   664             return new FindProperty(start, this, property);
   665         }
   667         if (deep) {
   668             final ScriptObject proto = getProto();
   669             if(proto != null) {
   670                 return proto.findProperty(key, deep, stopOnNonScope, start);
   671             }
   672         }
   674         return null;
   675     }
   677     /**
   678      * Add a new property to the object.
   679      * <p>
   680      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
   681      *
   682      * @param key             Property key.
   683      * @param propertyFlags   Property flags.
   684      * @param getter          Property getter, or null if not defined
   685      * @param setter          Property setter, or null if not defined
   686      *
   687      * @return New property.
   688      */
   689     public final Property addOwnProperty(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
   690         return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter));
   691     }
   693     /**
   694      * Add a new property to the object.
   695      * <p>
   696      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
   697      *
   698      * @param key             Property key.
   699      * @param propertyFlags   Property flags.
   700      * @param value           Value of property
   701      *
   702      * @return New property.
   703      */
   704     public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) {
   705         final MethodHandle setter = addSpill(key, propertyFlags);
   707         try {
   708             setter.invokeExact((Object)this, value);
   709         } catch (final Error|RuntimeException e) {
   710             throw e;
   711         } catch (final Throwable e) {
   712             throw new RuntimeException(e);
   713         }
   715         return getMap().findProperty(key);
   716     }
   718     /**
   719      * Add a new property to the object.
   720      * <p>
   721      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
   722      *
   723      * @param newProperty property to add
   724      *
   725      * @return New property.
   726      */
   727     public final Property addOwnProperty(final Property newProperty) {
   728         PropertyMap oldMap = getMap();
   730         while (true) {
   731             final PropertyMap newMap = oldMap.addProperty(newProperty);
   733             if (!compareAndSetMap(oldMap, newMap)) {
   734                 oldMap = getMap();
   735                 final Property oldProperty = oldMap.findProperty(newProperty.getKey());
   737                 if (oldProperty != null) {
   738                     return oldProperty;
   739                 }
   740             } else {
   741                 return newProperty;
   742             }
   743         }
   744     }
   746     private void erasePropertyValue(final Property property) {
   747         // Erase the property field value with undefined. If the property is defined
   748         // by user-defined accessors, we don't want to call the setter!!
   749         if (!(property instanceof UserAccessorProperty)) {
   750             try {
   751                 // make the property value to be undefined
   752                 //TODO specproperties
   753                 property.getSetter(Object.class, getMap()).invokeExact((Object)this, (Object)UNDEFINED);
   754             } catch (final RuntimeException | Error e) {
   755                 throw e;
   756             } catch (final Throwable t) {
   757                 throw new RuntimeException(t);
   758             }
   759         }
   760     }
   762     /**
   763      * Delete a property from the object.
   764      *
   765      * @param property Property to delete.
   766      *
   767      * @return true if deleted.
   768      */
   769     public final boolean deleteOwnProperty(final Property property) {
   770         erasePropertyValue(property);
   771         PropertyMap oldMap = getMap();
   773         while (true) {
   774             final PropertyMap newMap = oldMap.deleteProperty(property);
   776             if (newMap == null) {
   777                 return false;
   778             }
   780             if (!compareAndSetMap(oldMap, newMap)) {
   781                 oldMap = getMap();
   782             } else {
   783                 // delete getter and setter function references so that we don't leak
   784                 if (property instanceof UserAccessorProperty) {
   785                     final UserAccessorProperty uc = (UserAccessorProperty) property;
   786                     setEmbedOrSpill(uc.getGetterSlot(), null);
   787                     setEmbedOrSpill(uc.getSetterSlot(), null);
   788                 }
   789                 return true;
   790             }
   791         }
   792     }
   794     /**
   795      * Modify a property in the object
   796      *
   797      * @param oldProperty    property to modify
   798      * @param propertyFlags  new property flags
   799      * @param getter         getter for {@link UserAccessorProperty}, null if not present or N/A
   800      * @param setter         setter for {@link UserAccessorProperty}, null if not present or N/A
   801      *
   802      * @return new property
   803      */
   804     public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
   805         Property newProperty;
   806         if (oldProperty instanceof UserAccessorProperty) {
   807             // re-use the slots of the old user accessor property.
   808             final UserAccessorProperty uc = (UserAccessorProperty) oldProperty;
   810             int getterSlot = uc.getGetterSlot();
   811             // clear the old getter and set the new getter
   812             setEmbedOrSpill(getterSlot, getter);
   813             // if getter function is null, flag the slot to be negative (less by 1)
   814             if (getter == null) {
   815                 getterSlot = -getterSlot - 1;
   816             }
   818             int setterSlot = uc.getSetterSlot();
   819             // clear the old setter and set the new setter
   820             setEmbedOrSpill(setterSlot, setter);
   821             // if setter function is null, flag the slot to be negative (less by 1)
   822             if (setter == null) {
   823                 setterSlot = -setterSlot - 1;
   824             }
   826             newProperty = new UserAccessorProperty(oldProperty.getKey(), propertyFlags, getterSlot, setterSlot);
   827             // if just flipping getter and setter with new functions, no need to change property or map
   828             if (oldProperty.equals(newProperty)) {
   829                 return oldProperty;
   830             }
   831         } else {
   832             // erase old property value and create new user accessor property
   833             erasePropertyValue(oldProperty);
   834             newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter);
   835         }
   837         notifyPropertyModified(this, oldProperty, newProperty);
   839         return modifyOwnProperty(oldProperty, newProperty);
   840     }
   842     /**
   843       * Modify a property in the object
   844       *
   845       * @param oldProperty    property to modify
   846       * @param propertyFlags  new property flags
   847       *
   848       * @return new property
   849       */
   850     public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags) {
   851         return modifyOwnProperty(oldProperty, oldProperty.setFlags(propertyFlags));
   852     }
   854     /**
   855      * Modify a property in the object, replacing a property with a new one
   856      *
   857      * @param oldProperty   property to replace
   858      * @param newProperty   property to replace it with
   859      *
   860      * @return new property
   861      */
   862     private Property modifyOwnProperty(final Property oldProperty, final Property newProperty) {
   863         assert newProperty.getKey().equals(oldProperty.getKey()) : "replacing property with different key";
   865         PropertyMap oldMap = getMap();
   867         while (true) {
   868             final PropertyMap newMap = oldMap.replaceProperty(oldProperty, newProperty);
   870             if (!compareAndSetMap(oldMap, newMap)) {
   871                 oldMap = getMap();
   872                 final Property oldPropertyLookup = oldMap.findProperty(oldProperty.getKey());
   874                 if (oldPropertyLookup != null && oldPropertyLookup.equals(newProperty)) {
   875                     return oldPropertyLookup;
   876                 }
   877             } else {
   878                 return newProperty;
   879             }
   880         }
   881     }
   883     /**
   884      * Update getter and setter in an object literal.
   885      *
   886      * @param key    Property key.
   887      * @param getter {@link UserAccessorProperty} defined getter, or null if none
   888      * @param setter {@link UserAccessorProperty} defined setter, or null if none
   889      */
   890     public final void setUserAccessors(final String key, final ScriptFunction getter, final ScriptFunction setter) {
   891         final Property oldProperty = getMap().findProperty(key);
   892         if (oldProperty != null) {
   893             final UserAccessorProperty newProperty = newUserAccessors(oldProperty.getKey(), oldProperty.getFlags(), getter, setter);
   894             modifyOwnProperty(oldProperty, newProperty);
   895         } else {
   896             final UserAccessorProperty newProperty = newUserAccessors(key, 0, getter, setter);
   897             addOwnProperty(newProperty);
   898         }
   899     }
   901     private static int getIntValue(final FindProperty find) {
   902         final MethodHandle getter = find.getGetter(int.class);
   903         if (getter != null) {
   904             try {
   905                 return (int)getter.invokeExact((Object)find.getGetterReceiver());
   906             } catch (final Error|RuntimeException e) {
   907                 throw e;
   908             } catch (final Throwable e) {
   909                 throw new RuntimeException(e);
   910             }
   911         }
   913         return ObjectClassGenerator.UNDEFINED_INT;
   914     }
   916     private static long getLongValue(final FindProperty find) {
   917         final MethodHandle getter = find.getGetter(long.class);
   918         if (getter != null) {
   919             try {
   920                 return (long)getter.invokeExact((Object)find.getGetterReceiver());
   921             } catch (final Error|RuntimeException e) {
   922                 throw e;
   923             } catch (final Throwable e) {
   924                 throw new RuntimeException(e);
   925             }
   926         }
   928         return ObjectClassGenerator.UNDEFINED_LONG;
   929     }
   931     private static double getDoubleValue(final FindProperty find) {
   932         final MethodHandle getter = find.getGetter(double.class);
   933         if (getter != null) {
   934             try {
   935                 return (double)getter.invokeExact((Object)find.getGetterReceiver());
   936             } catch (final Error|RuntimeException e) {
   937                 throw e;
   938             } catch (final Throwable e) {
   939                 throw new RuntimeException(e);
   940             }
   941         }
   943         return ObjectClassGenerator.UNDEFINED_DOUBLE;
   944     }
   946     /**
   947       * Get the object value of a property
   948       *
   949       * @param find {@link FindProperty} lookup result
   950       *
   951       * @return the value of the property
   952       */
   953     protected static Object getObjectValue(final FindProperty find) {
   954         final MethodHandle getter = find.getGetter(Object.class);
   955         if (getter != null) {
   956             try {
   957                 return getter.invokeExact((Object)find.getGetterReceiver());
   958             } catch (final Error|RuntimeException e) {
   959                 throw e;
   960             } catch (final Throwable e) {
   961                 throw new RuntimeException(e);
   962             }
   963         }
   965         return UNDEFINED;
   966     }
   968     /**
   969      * Return methodHandle of value function for call.
   970      *
   971      * @param find      data from find property.
   972      * @param type      method type of function.
   973      * @param bindName  null or name to bind to second argument (property not found method.)
   974      *
   975      * @return value of property as a MethodHandle or null.
   976      *
   977      */
   978     @SuppressWarnings("static-method")
   979     protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) {
   980         return getCallMethodHandle(getObjectValue(find), type, bindName);
   981     }
   983     /**
   984      * Return methodHandle of value function for call.
   985      *
   986      * @param value     value of receiver, it not a {@link ScriptFunction} this will return null.
   987      * @param type      method type of function.
   988      * @param bindName  null or name to bind to second argument (property not found method.)
   989      *
   990      * @return value of property as a MethodHandle or null.
   991      */
   992     protected static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) {
   993         return value instanceof ScriptFunction ? ((ScriptFunction)value).getCallMethodHandle(type, bindName) : null;
   994     }
   996     /**
   997      * Get value using found property.
   998      *
   999      * @param property Found property.
  1001      * @return Value of property.
  1002      */
  1003     public final Object getWithProperty(final Property property) {
  1004         return getObjectValue(new FindProperty(this, this, property));
  1007     /**
  1008      * Get a property given a key
  1010      * @param key property key
  1012      * @return property for key
  1013      */
  1014     public final Property getProperty(final String key) {
  1015         return getMap().findProperty(key);
  1018     static String convertKey(final Object key) {
  1019         return (key instanceof String) ? (String)key : JSType.toString(key);
  1022     /**
  1023      * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
  1024      * Used for argument access in a vararg function using parameter name.
  1025      * Returns the argument at a given key (index)
  1027      * @param key argument index
  1029      * @return the argument at the given position, or undefined if not present
  1030      */
  1031     public Object getArgument(final int key) {
  1032         return get(key);
  1035     /**
  1036      * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
  1037      * Used for argument access in a vararg function using parameter name.
  1038      * Returns the argument at a given key (index)
  1040      * @param key   argument index
  1041      * @param value the value to write at the given index
  1042      */
  1043     public void setArgument(final int key, final Object value) {
  1044         set(key, value, getContext()._strict);
  1047     /**
  1048      * Return true if the script object context is strict
  1049      * @return true if strict context
  1050      */
  1051     public final boolean isStrictContext() {
  1052         return getContext()._strict;
  1055     /**
  1056      * Return the current context from the object's map.
  1057      * @return Current context.
  1058      */
  1059     final Context getContext() {
  1060         return getMap().getContext();
  1063     /**
  1064      * Return the map of an object.
  1065      * @return PropertyMap object.
  1066      */
  1067     public final PropertyMap getMap() {
  1068         return map;
  1071     /**
  1072      * Set the initial map.
  1073      * @param map Initial map.
  1074      */
  1075     public final void setMap(final PropertyMap map) {
  1076         this.map = map;
  1079     /**
  1080      * Conditionally set the new map if the old map is the same.
  1081      * @param oldMap Map prior to manipulation.
  1082      * @param newMap Replacement map.
  1083      * @return true if the operation succeeded.
  1084      */
  1085     protected synchronized final boolean compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap) {
  1086         final boolean update = oldMap == this.map;
  1088         if (update) {
  1089             this.map = newMap;
  1092         return update;
  1095     /**
  1096      * Return the __proto__ of an object.
  1097      * @return __proto__ object.
  1098      */
  1099     public final ScriptObject getProto() {
  1100         return getMap().getProto();
  1103     /**
  1104      * Check if this is a prototype
  1105      * @return true if {@link PropertyMap#isPrototype()} is true for this ScriptObject
  1106      */
  1107     public final boolean isPrototype() {
  1108         return getMap().isPrototype();
  1111     /**
  1112      * Set the __proto__ of an object.
  1113      * @param newProto new __proto__ to set.
  1114      */
  1115     public final void setProto(final ScriptObject newProto) {
  1116         PropertyMap  oldMap   = getMap();
  1117         ScriptObject oldProto = getProto();
  1119         while (oldProto != newProto) {
  1120             final PropertyMap newMap = oldMap.setProto(newProto);
  1122             if (!compareAndSetMap(oldMap, newMap)) {
  1123                 oldMap = getMap();
  1124                 oldProto = getProto();
  1125             } else {
  1126                 if (isPrototype()) {
  1128                     if (oldProto != null) {
  1129                         oldProto.removePropertyListener(this);
  1132                     if (newProto != null) {
  1133                         newProto.addPropertyListener(this);
  1137                 return;
  1142     /**
  1143      * Set the __proto__ of an object with checks.
  1144      * @param newProto Prototype to set.
  1145      */
  1146     public final void setProtoCheck(final Object newProto) {
  1147         if (newProto == null || newProto instanceof ScriptObject) {
  1148             setProto((ScriptObject)newProto);
  1149         } else {
  1150             final ScriptObject global = Context.getGlobalTrusted();
  1151             final Object  newProtoObject = JSType.toScriptObject(global, newProto);
  1153             if (newProtoObject instanceof ScriptObject) {
  1154                 setProto((ScriptObject)newProtoObject);
  1155             } else {
  1156                 throw typeError(global, "cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
  1161     /**
  1162      * return a List of own keys associated with the object.
  1163      * @param all True if to include non-enumerable keys.
  1164      * @return Array of keys.
  1165      */
  1166     public String[] getOwnKeys(final boolean all) {
  1167         final List<Object> keys    = new ArrayList<>();
  1168         final PropertyMap  selfMap = this.getMap();
  1170         final ArrayData array  = getArray();
  1171         final long length      = array.length();
  1173         for (long i = 0; i < length; i = array.nextIndex(i)) {
  1174             if (array.has((int)i)) {
  1175                 keys.add(JSType.toString(i));
  1179         for (final Property property : selfMap.getProperties()) {
  1180             if (all || property.isEnumerable()) {
  1181                 keys.add(property.getKey());
  1185         return keys.toArray(new String[keys.size()]);
  1188     /**
  1189      * Check if this ScriptObject has array entries. This means that someone has
  1190      * set values with numeric keys in the object.
  1192      * Note: this can be O(n) up to the array length
  1194      * @return true if array entries exists.
  1195      */
  1196     public boolean hasArrayEntries() {
  1197         final ArrayData array = getArray();
  1198         final long length = array.length();
  1200         for (long i = 0; i < length; i++) {
  1201             if (array.has((int)i)) {
  1202                 return true;
  1206         return false;
  1209     /**
  1210      * Return the valid JavaScript type name descriptor
  1212      * @return "Object"
  1213      */
  1214     public String getClassName() {
  1215         return "Object";
  1218     /**
  1219      * {@code length} is a well known property. This is its getter.
  1220      * Note that this *may* be optimized by other classes
  1222      * @return length property value for this ScriptObject
  1223      */
  1224     public Object getLength() {
  1225         return get("length");
  1228     /**
  1229      * Stateless toString for ScriptObjects.
  1231      * @return string description of this object, e.g. {@code [object Object]}
  1232      */
  1233     public String safeToString() {
  1234         return "[object " + getClassName() + "]";
  1237     /**
  1238      * Return the default value of the object with a given preferred type hint.
  1239      * The preferred type hints are String.class for type String, Number.class
  1240      * for type Number. <p>
  1242      * A <code>hint</code> of null means "no hint".
  1244      * ECMA 8.12.8 [[DefaultValue]](hint)
  1246      * @param typeHint the preferred type hint
  1247      * @return the default value
  1248      */
  1249     public Object getDefaultValue(final Class<?> typeHint) {
  1250         // We delegate to GlobalObject, as the implementation uses dynamic call sites to invoke object's "toString" and
  1251         // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts
  1252         // are being executed in a long-running program, we move the code and their associated dynamic call sites
  1253         // (Global.TO_STRING and Global.VALUE_OF) into per-context code.
  1254         return ((GlobalObject)Context.getGlobalTrusted()).getDefaultValue(this, typeHint);
  1257     /**
  1258      * Checking whether a script object is an instance of another. Used
  1259      * in {@link ScriptFunction} for hasInstance implementation, walks
  1260      * the proto chain
  1262      * @param instance instace to check
  1263      * @return true if instance of instance
  1264      */
  1265     public boolean isInstance(final ScriptObject instance) {
  1266         return false;
  1269     /**
  1270      * Flag this ScriptObject as non extensible
  1272      * @return the object after being made non extensible
  1273      */
  1274     public ScriptObject preventExtensions() {
  1275         PropertyMap oldMap = getMap();
  1277         while (true) {
  1278             final PropertyMap newMap = getMap().preventExtensions();
  1280             if (!compareAndSetMap(oldMap, newMap)) {
  1281                 oldMap = getMap();
  1282             } else {
  1283                 return this;
  1288     /**
  1289      * Check whether if an Object (not just a ScriptObject) represents JavaScript array
  1291      * @param obj object to check
  1293      * @return true if array
  1294      */
  1295     public static boolean isArray(final Object obj) {
  1296         return (obj instanceof ScriptObject) && ((ScriptObject)obj).isArray();
  1299     /**
  1300      * Check if this ScriptObject is an array
  1301      * @return true if array
  1302      */
  1303     public final boolean isArray() {
  1304         return (flags & IS_ARRAY) != 0;
  1307     /**
  1308      * Flag this ScriptObject as being an array
  1309      */
  1310     public final void setIsArray() {
  1311         flags |= IS_ARRAY;
  1314     /**
  1315      * Check if this ScriptObject is an {@code arguments} vector
  1316      * @return true if arguments vector
  1317      */
  1318     public final boolean isArguments() {
  1319         return (flags & IS_ARGUMENTS) != 0;
  1322     /**
  1323      * Flag this ScriptObject as being an {@code arguments} vector
  1324      */
  1325     public final void setIsArguments() {
  1326         flags |= IS_ARGUMENTS;
  1329     /**
  1330      * Get the {@link ArrayData} for this ScriptObject if it is an array
  1331      * @return array data
  1332      */
  1333     public final ArrayData getArray() {
  1334         return arrayData;
  1337     /**
  1338      * Set the {@link ArrayData} for this ScriptObject if it is to be an array
  1339      * @param arrayData the array data
  1340      */
  1341     public final void setArray(final ArrayData arrayData) {
  1342         this.arrayData = arrayData;
  1345     /**
  1346      * Check if this ScriptObject is extensible
  1347      * @return true if extensible
  1348      */
  1349     public boolean isExtensible() {
  1350         return getMap().isExtensible();
  1353     /**
  1354      * ECMAScript 15.2.3.8 - seal implementation
  1355      * @return the sealed ScriptObject
  1356      */
  1357     public ScriptObject seal() {
  1358         PropertyMap oldMap = getMap();
  1360         while (true) {
  1361             final PropertyMap newMap = getMap().seal();
  1363             if (!compareAndSetMap(oldMap, newMap)) {
  1364                 oldMap = getMap();
  1365             } else {
  1366                 setArray(ArrayData.seal(getArray()));
  1367                 return this;
  1372     /**
  1373      * Check whether this ScriptObject is sealed
  1374      * @return true if sealed
  1375      */
  1376     public boolean isSealed() {
  1377         return getMap().isSealed();
  1380     /**
  1381      * ECMA 15.2.39 - freeze implementation. Freeze this ScriptObject
  1382      * @return the frozen ScriptObject
  1383      */
  1384     public ScriptObject freeze() {
  1385         PropertyMap oldMap = getMap();
  1387         while (true) {
  1388             final PropertyMap newMap = getMap().freeze();
  1390             if (!compareAndSetMap(oldMap, newMap)) {
  1391                 oldMap = getMap();
  1392             } else {
  1393                 setArray(ArrayData.freeze(getArray()));
  1394                 return this;
  1399     /**
  1400      * Check whether this ScriptObject is frozen
  1401      * @return true if frozed
  1402      */
  1403     public boolean isFrozen() {
  1404         return getMap().isFrozen();
  1408     /**
  1409      * Flag this ScriptObject as scope
  1410      */
  1411     public final void setIsScope() {
  1412         if (Context.DEBUG) {
  1413             scopeCount++;
  1415         flags |= IS_SCOPE;
  1418     /**
  1419      * Check whether this ScriptObject is scope
  1420      * @return true if scope
  1421      */
  1422     public final boolean isScope() {
  1423         return (flags & IS_SCOPE) != 0;
  1426     /**
  1427      * Clears the properties from a ScriptObject
  1428      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1429      */
  1430     public void clear() {
  1431         final boolean strict = getContext()._strict;
  1432         final Iterator<String> iter = propertyIterator();
  1433         while (iter.hasNext()) {
  1434             delete(iter.next(), strict);
  1438     /**
  1439      * Checks if a property with a given key is present in a ScriptObject
  1440      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1442      * @param key the key to check for
  1443      * @return true if a property with the given key exists, false otherwise
  1444      */
  1445     public boolean containsKey(final Object key) {
  1446         return has(key);
  1449     /**
  1450      * Checks if a property with a given value is present in a ScriptObject
  1451      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1453      * @param value value to check for
  1454      * @return true if a property with the given value exists, false otherwise
  1455      */
  1456     public boolean containsValue(final Object value) {
  1457         final Iterator<Object> iter = valueIterator();
  1458         while (iter.hasNext()) {
  1459             if (iter.next().equals(value)) {
  1460                 return true;
  1463         return false;
  1466     /**
  1467      * Returns the set of {@literal <property, value>} entries that make up this
  1468      * ScriptObject's properties
  1469      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1471      * @return an entry set of all the properties in this object
  1472      */
  1473     public Set<Map.Entry<Object, Object>> entrySet() {
  1474         final Iterator<String> iter = propertyIterator();
  1475         final Set<Map.Entry<Object, Object>> entries = new HashSet<>();
  1476         while (iter.hasNext()) {
  1477             final Object key = iter.next();
  1478             entries.add(new AbstractMap.SimpleImmutableEntry<>(key, get(key)));
  1480         return Collections.unmodifiableSet(entries);
  1483     /**
  1484      * Check whether a ScriptObject contains no properties
  1485      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1487      * @return true if object has no properties
  1488      */
  1489     public boolean isEmpty() {
  1490         return !propertyIterator().hasNext();
  1493     /**
  1494      * Return the set of keys (property names) for all properties
  1495      * in this ScriptObject
  1496      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1498      * @return keySet of this ScriptObject
  1499      */
  1500     public Set<Object> keySet() {
  1501         final Iterator<String> iter = propertyIterator();
  1502         final Set<Object> keySet = new HashSet<>();
  1503         while (iter.hasNext()) {
  1504             keySet.add(iter.next());
  1506         return Collections.unmodifiableSet(keySet);
  1509     /**
  1510      * Put a property in the ScriptObject
  1511      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1513      * @param key property key
  1514      * @param value property value
  1515      * @return oldValue if property with same key existed already
  1516      */
  1517     public Object put(final Object key, final Object value) {
  1518         final Object oldValue = get(key);
  1519         set(key, value, getContext()._strict);
  1520         return oldValue;
  1523     /**
  1524      * Put several properties in the ScriptObject given a mapping
  1525      * of their keys to their values
  1526      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1528      * @param otherMap a {@literal <key,value>} map of properties to add
  1529      */
  1530     public void putAll(final Map<?, ?> otherMap) {
  1531         final boolean strict = getContext()._strict;
  1532         for (final Map.Entry<?, ?> entry : otherMap.entrySet()) {
  1533             set(entry.getKey(), entry.getValue(), strict);
  1537     /**
  1538      * Remove a property from the ScriptObject.
  1539      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1541      * @param key the key of the property
  1542      * @return the oldValue of the removed property
  1543      */
  1544     public Object remove(final Object key) {
  1545         final Object oldValue = get(key);
  1546         delete(key, getContext()._strict);
  1547         return oldValue;
  1550     /**
  1551      * Return the size of the ScriptObject - i.e. the number of properties
  1552      * it contains
  1553      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1555      * @return number of properties in ScriptObject
  1556      */
  1557     public int size() {
  1558         int n = 0;
  1559         for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) {
  1560             n++;
  1562         return n;
  1565     /**
  1566      * Return the values of the properties in the ScriptObject
  1567      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1569      * @return collection of values for the properties in this ScriptObject
  1570      */
  1571     public Collection<Object> values() {
  1572         final List<Object>     values = new ArrayList<>(size());
  1573         final Iterator<Object> iter   = valueIterator();
  1574         while (iter.hasNext()) {
  1575             values.add(iter.next());
  1577         return Collections.unmodifiableList(values);
  1580     /**
  1581      * Lookup method that, given a CallSiteDescriptor, looks up the target
  1582      * MethodHandle and creates a GuardedInvocation
  1583      * with the appropriate guard(s).
  1585      * @param desc call site descriptor
  1586      * @param request the link request
  1588      * @return GuardedInvocation for the callsite
  1589      */
  1590     public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
  1591         final int c = desc.getNameTokenCount();
  1592         // JavaScript is "immune" to all currently defined Dynalink composite operation - getProp is the same as getElem
  1593         // is the same as getMethod as JavaScript objects have a single namespace for all three. Therefore, we don't
  1594         // care about them, and just link to whatever is the first operation.
  1595         final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
  1596         // NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself
  1597         // emits "dyn:getProp:identifier" for "<expr>.<identifier>" and "dyn:getElem" for "<expr>[<expr>]", but we are
  1598         // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the
  1599         // operation has an associated name or not.
  1600         switch (operator) {
  1601         case "getProp":
  1602         case "getElem":
  1603         case "getMethod":
  1604             return c > 2 ? findGetMethod(desc, request, operator) : findGetIndexMethod(desc, request);
  1605         case "setProp":
  1606         case "setElem":
  1607             return c > 2 ? findSetMethod(desc, request) : findSetIndexMethod(desc);
  1608         case "call":
  1609             return findCallMethod(desc, request);
  1610         case "new":
  1611             return findNewMethod(desc);
  1612         case "callMethod":
  1613             return findCallMethodMethod(desc, request);
  1614         default:
  1615             return null;
  1619     /**
  1620      * Find the appropriate New method for an invoke dynamic call.
  1622      * @param desc The invoke dynamic call site descriptor.
  1624      * @return GuardedInvocation to be invoked at call site.
  1625      */
  1626     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
  1627         return notAFunction();
  1630     /**
  1631      * Find the appropriate CALL method for an invoke dynamic call.
  1632      * This generates "not a function" always
  1634      * @param desc    the call site descriptor.
  1635      * @param request the link request
  1637      * @return GuardedInvocation to be invoed at call site.
  1638      */
  1639     protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1640         return notAFunction();
  1643     private GuardedInvocation notAFunction() {
  1644         throw typeError("not.a.function", ScriptRuntime.safeToString(this));
  1647     /**
  1648      * Find an implementation for a "dyn:callMethod" operation. Note that Nashorn internally never uses
  1649      * "dyn:callMethod", but instead always emits two call sites in bytecode, one for "dyn:getMethod", and then another
  1650      * one for "dyn:call". Explicit support for "dyn:callMethod" is provided for the benefit of potential external
  1651      * callers. The implementation itself actually folds a "dyn:getMethod" method handle into a "dyn:call" method handle.
  1653      * @param desc    the call site descriptor.
  1654      * @param request the link request
  1656      * @return GuardedInvocation to be invoked at call site.
  1657      */
  1658     protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1659         // R(P0, P1, ...)
  1660         final MethodType callType = desc.getMethodType();
  1661         // use type Object(P0) for the getter
  1662         final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0)));
  1663         final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod");
  1665         // Object(P0) => Object(P0, P1, ...)
  1666         final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount()));
  1667         // R(Object, P0, P1, ...)
  1668         final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
  1669         // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
  1670         return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
  1673     /**
  1674      * Find the appropriate GET method for an invoke dynamic call.
  1676      * @param desc     the call site descriptor
  1677      * @param request  the link request
  1678      * @param operator operator for get: getProp, getMethod, getElem etc
  1680      * @return GuardedInvocation to be invoked at call site.
  1681      */
  1682     protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
  1683         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  1684         final FindProperty find = findProperty(name, true);
  1686         MethodHandle methodHandle;
  1688         if (find == null) {
  1689             if ("getProp".equals(operator)) {
  1690                 return noSuchProperty(desc, request);
  1691             } else if ("getMethod".equals(operator)) {
  1692                 return noSuchMethod(desc, request);
  1693             } else if ("getElem".equals(operator)) {
  1694                 return createEmptyGetter(desc, name);
  1696             throw new AssertionError(); // never invoked with any other operation
  1699         if (request.isCallSiteUnstable()) {
  1700             return findMegaMorphicGetMethod(desc, name);
  1703         final Class<?> returnType = desc.getMethodType().returnType();
  1704         final Property property = find.getProperty();
  1705         methodHandle = find.getGetter(returnType);
  1707         // getMap() is fine as we have the prototype switchpoint depending on where the property was found
  1708         final MethodHandle guard = NashornGuards.getMapGuard(getMap());
  1710         if (methodHandle != null) {
  1711             assert methodHandle.type().returnType().equals(returnType);
  1712             if (find.isSelf()) {
  1713                 return new GuardedInvocation(methodHandle, ObjectClassGenerator.OBJECT_FIELDS_ONLY &&
  1714                         NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType() ? null : guard);
  1717             final ScriptObject prototype = find.getOwner();
  1719             if (!property.hasGetterFunction()) {
  1720                 methodHandle = bindTo(methodHandle, prototype);
  1722             return new GuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(name), guard);
  1725         assert !NashornCallSiteDescriptor.isFastScope(desc);
  1726         return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(name), guard);
  1729     private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) {
  1730         final MethodType mhType = desc.getMethodType().insertParameterTypes(1, Object.class);
  1731         final GuardedInvocation inv = findGetIndexMethod(mhType);
  1733         return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
  1736     /**
  1737      * Find the appropriate GETINDEX method for an invoke dynamic call.
  1739      * @param desc    the call site descriptor
  1740      * @param request the link request
  1742      * @return GuardedInvocation to be invoked at call site.
  1743      */
  1744     protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1745         return findGetIndexMethod(desc.getMethodType());
  1748     /**
  1749      * Find the appropriate GETINDEX method for an invoke dynamic call.
  1751      * @param callType the call site method type
  1752      * @return GuardedInvocation to be invoked at call site.
  1753      */
  1754     private static GuardedInvocation findGetIndexMethod(final MethodType callType) {
  1755         final Class<?> returnClass = callType.returnType();
  1756         final Class<?> keyClass    = callType.parameterType(1);
  1758         String name = "get";
  1759         if (returnClass.isPrimitive()) {
  1760             //turn e.g. get with a double into getDouble
  1761             final String returnTypeName = returnClass.getName();
  1762             name += Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
  1765         return new GuardedInvocation(findOwnMH(name, returnClass, keyClass), getScriptObjectGuard(callType));
  1768     private static MethodHandle getScriptObjectGuard(final MethodType type) {
  1769         return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard();
  1772     /**
  1773      * Find the appropriate SET method for an invoke dynamic call.
  1775      * @param desc    the call site descriptor
  1776      * @param request the link request
  1778      * @return GuardedInvocation to be invoked at call site.
  1779      */
  1780     protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1781         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  1782         if(request.isCallSiteUnstable()) {
  1783             return findMegaMorphicSetMethod(desc, name);
  1786         final boolean scope = isScope();
  1787         /*
  1788          * If doing property set on a scope object, we should stop proto search on the first
  1789          * non-scope object. Without this, for example, when assigning "toString" on global scope,
  1790          * we'll end up assigning it on it's proto - which is Object.prototype.toString !!
  1792          * toString = function() { print("global toString"); } // don't affect Object.prototype.toString
  1793          */
  1794         FindProperty find = findProperty(name, true, scope, this);
  1795         // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors.
  1796         if (!scope && find != null && find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
  1797             // We should still check if inherited data property is not writable
  1798             if (isExtensible() && !find.getProperty().isWritable()) {
  1799                 return createEmptySetMethod(desc, "property.not.writable", false);
  1801             // Otherwise, forget the found property
  1802             find = null;
  1805         if (find != null) {
  1806             if(!find.getProperty().isWritable()) {
  1807                 // Existing, non-writable property
  1808                 return createEmptySetMethod(desc, "property.not.writable", true);
  1810         } else if (!isExtensible()) {
  1811             // Non-existing property on a non-extensible object
  1812             return createEmptySetMethod(desc, "object.non.extensible", false);
  1815         return new SetMethodCreator(this, find, desc).createGuardedInvocation();
  1818     private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, String strictErrorMessage, boolean canBeFastScope) {
  1819         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  1820         if (NashornCallSiteDescriptor.isStrict(desc)) {
  1821                throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString((this)));
  1823            assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
  1824            final PropertyMap myMap = getMap();
  1825            return new GuardedInvocation(Lookup.EMPTY_SETTER, myMap.getProtoGetSwitchPoint(name), NashornGuards.getMapGuard(myMap));
  1828     @SuppressWarnings("unused")
  1829     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 {
  1830         final ScriptObject obj = (ScriptObject)self;
  1831         if (obj.trySetEmbedOrSpill(desc, oldMap, newMap, value)) {
  1832             obj.useEmbed(i);
  1833             setter.invokeExact(self, value);
  1837     @SuppressWarnings("unused")
  1838     private static void setSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
  1839         final ScriptObject obj = (ScriptObject)self;
  1840         if (obj.trySetEmbedOrSpill(desc, oldMap, newMap, value)) {
  1841             obj.spill[index] = value;
  1845     private boolean trySetEmbedOrSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final Object value) {
  1846         final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
  1847         if (!isExtensible() && isStrict) {
  1848             throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(this));
  1849         } else if (compareAndSetMap(oldMap, newMap)) {
  1850             return true;
  1851         } else {
  1852             set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
  1853             return false;
  1857     @SuppressWarnings("unused")
  1858     private static void setSpillWithNew(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
  1859         final ScriptObject obj      = (ScriptObject)self;
  1860         final boolean      isStrict = NashornCallSiteDescriptor.isStrict(desc);
  1862         if (!obj.isExtensible()) {
  1863             if (isStrict) {
  1864                 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
  1866         } else if (obj.compareAndSetMap(oldMap, newMap)) {
  1867             obj.spill = new Object[SPILL_RATE];
  1868             obj.spill[index] = value;
  1869         } else {
  1870             obj.set(desc.getNameToken(2), value, isStrict);
  1874     @SuppressWarnings("unused")
  1875     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) {
  1876         final ScriptObject obj      = (ScriptObject)self;
  1877         final boolean      isStrict = NashornCallSiteDescriptor.isStrict(desc);
  1879         if (!obj.isExtensible()) {
  1880             if (isStrict) {
  1881                 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
  1883         } else if (obj.compareAndSetMap(oldMap, newMap)) {
  1884             final int oldLength = obj.spill.length;
  1885             final Object[] newSpill = new Object[newLength];
  1886             System.arraycopy(obj.spill, 0, newSpill, 0, oldLength);
  1887             obj.spill = newSpill;
  1888             obj.spill[index] = value;
  1889         } else {
  1890             obj.set(desc.getNameToken(2), value, isStrict);
  1894     private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
  1895         final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
  1896         final GuardedInvocation inv = findSetIndexMethod(type, NashornCallSiteDescriptor.isStrict(desc));
  1897         return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
  1900     private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc) { // array, index, value
  1901         return findSetIndexMethod(desc.getMethodType(), NashornCallSiteDescriptor.isStrict(desc));
  1904     /**
  1905      * Find the appropriate SETINDEX method for an invoke dynamic call.
  1907      * @param callType the method type at the call site
  1908      * @param isStrict are we in strict mode?
  1910      * @return GuardedInvocation to be invoked at call site.
  1911      */
  1912     private static GuardedInvocation findSetIndexMethod(final MethodType callType, final boolean isStrict) {
  1913         assert callType.parameterCount() == 3;
  1915         final Class<?>   keyClass   = callType.parameterType(1);
  1916         final Class<?>   valueClass = callType.parameterType(2);
  1918         MethodHandle methodHandle = findOwnMH("set", void.class, keyClass, valueClass, boolean.class);
  1919         methodHandle = MH.insertArguments(methodHandle, 3, isStrict);
  1921         return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType));
  1924     /**
  1925      * Fall back if a function property is not found.
  1926      * @param desc The call site descriptor
  1927      * @param request the link request
  1928      * @return GuardedInvocation to be invoked at call site.
  1929      */
  1930     public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1931         final String       name      = desc.getNameToken(2);
  1932         final FindProperty find      = findProperty(NO_SUCH_METHOD_NAME, true);
  1933         final boolean      scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc);
  1935         if (find == null) {
  1936             return noSuchProperty(desc, request);
  1939         final ScriptFunction func = (ScriptFunction)getObjectValue(find);
  1940         final Object thiz = scopeCall && func.isStrict() ? ScriptRuntime.UNDEFINED : this;
  1941         // TODO: It'd be awesome if we could bind "name" without binding "this".
  1942         return new GuardedInvocation(MH.dropArguments(MH.constant(ScriptFunction.class,
  1943                 func.makeBoundFunction(thiz, new Object[] { name })), 0, Object.class),
  1944                 null, NashornGuards.getMapGuard(getMap()));
  1947     /**
  1948      * Fall back if a property is not found.
  1949      * @param desc the call site descriptor.
  1950      * @param request the link request
  1951      * @return GuardedInvocation to be invoked at call site.
  1952      */
  1953     public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
  1954         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  1955         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
  1956         final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
  1958         if (find != null) {
  1959             final ScriptFunction func = (ScriptFunction)getObjectValue(find);
  1960             MethodHandle methodHandle = getCallMethodHandle(func, desc.getMethodType(), name);
  1962             if (methodHandle != null) {
  1963                 if (scopeAccess && func.isStrict()) {
  1964                     methodHandle = bindTo(methodHandle, UNDEFINED);
  1966                 return new GuardedInvocation(methodHandle,
  1967                         find.isInherited()? getMap().getProtoGetSwitchPoint(NO_SUCH_PROPERTY_NAME) : null,
  1968                         getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func));
  1972         if (scopeAccess) {
  1973             throw referenceError("not.defined", name);
  1976         return createEmptyGetter(desc, name);
  1978     /**
  1979      * Invoke fall back if a property is not found.
  1980      * @param name Name of property.
  1981      * @return Result from call.
  1982      */
  1983     private Object invokeNoSuchProperty(final String name) {
  1984         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
  1986         if (find != null) {
  1987             final Object func = getObjectValue(find);
  1989             if (func instanceof ScriptFunction) {
  1990                 return ScriptRuntime.apply((ScriptFunction)func, this, name);
  1994         return UNDEFINED;
  1997     private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
  1998         return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(name), NashornGuards.getMapGuard(getMap()));
  2001     private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
  2002         protected T[] values;
  2003         protected final ScriptObject object;
  2004         private int index;
  2006         ScriptObjectIterator(final ScriptObject object) {
  2007             this.object = object;
  2010         protected abstract void init();
  2012         @Override
  2013         public boolean hasNext() {
  2014             if (values == null) {
  2015                 init();
  2017             return index < values.length;
  2020         @Override
  2021         public T next() {
  2022             if (values == null) {
  2023                 init();
  2025             return values[index++];
  2028         @Override
  2029         public void remove() {
  2030             throw new UnsupportedOperationException();
  2034     private static class KeyIterator extends ScriptObjectIterator<String> {
  2035         KeyIterator(final ScriptObject object) {
  2036             super(object);
  2039         @Override
  2040         protected void init() {
  2041             final Set<String> keys = new LinkedHashSet<>();
  2042             for (ScriptObject self = object; self != null; self = self.getProto()) {
  2043                 keys.addAll(Arrays.asList(self.getOwnKeys(false)));
  2045             this.values = keys.toArray(new String[keys.size()]);
  2049     private static class ValueIterator extends ScriptObjectIterator<Object> {
  2050         ValueIterator(final ScriptObject object) {
  2051             super(object);
  2054         @Override
  2055         protected void init() {
  2056             final ArrayList<Object> valueList = new ArrayList<>();
  2057             for (ScriptObject self = object; self != null; self = self.getProto()) {
  2058                 for (final String key : self.getOwnKeys(false)) {
  2059                     valueList.add(self.get(key));
  2062             this.values = valueList.toArray(new Object[valueList.size()]);
  2066     /**
  2067      * Add a spill property for the given key.
  2068      * @param key           Property key.
  2069      * @param propertyFlags Property flags.
  2070      * @return Added property.
  2071      */
  2072     private Property addSpillProperty(final String key, final int propertyFlags) {
  2073         int i = findEmbed();
  2074         Property spillProperty;
  2076         if (i >= EMBED_SIZE) {
  2077             i = getMap().getSpillLength();
  2078             MethodHandle getter = MH.arrayElementGetter(Object[].class);
  2079             MethodHandle setter = MH.arrayElementSetter(Object[].class);
  2080             getter = MH.asType(MH.insertArguments(getter, 1, i), Lookup.GET_OBJECT_TYPE);
  2081             setter = MH.asType(MH.insertArguments(setter, 1, i), Lookup.SET_OBJECT_TYPE);
  2082             spillProperty = new SpillProperty(key, propertyFlags | Property.IS_SPILL, i, getter, setter);
  2083             notifyPropertyAdded(this, spillProperty);
  2084             spillProperty = addOwnProperty(spillProperty);
  2085             i = spillProperty.getSlot();
  2087             final int newLength = (i + SPILL_RATE) / SPILL_RATE * SPILL_RATE;
  2088             final Object[] newSpill = new Object[newLength];
  2090             if (spill != null) {
  2091                 System.arraycopy(spill, 0, newSpill, 0, spill.length);
  2094             spill = newSpill;
  2095          } else {
  2096             useEmbed(i);
  2097             spillProperty = new SpillProperty(key, propertyFlags, i, GET_EMBED[i], SET_EMBED[i]);
  2098             notifyPropertyAdded(this, spillProperty);
  2099             spillProperty = addOwnProperty(spillProperty);
  2102         return spillProperty;
  2106     /**
  2107      * Add a spill entry for the given key.
  2108      * @param key           Property key.
  2109      * @param propertyFlags Property flags.
  2110      * @return Setter method handle.
  2111      */
  2112     private MethodHandle addSpill(final String key, final int propertyFlags) {
  2113         final Property spillProperty = addSpillProperty(key, propertyFlags);
  2114         final Class<?> type = Object.class;
  2115         return spillProperty.getSetter(type, getMap()); //TODO specfields
  2118     MethodHandle addSpill(final String key) {
  2119         return addSpill(key, 0);
  2122     /**
  2123      * Make sure arguments are paired correctly, with respect to more parameters than declared,
  2124      * fewer parameters than declared and other things that JavaScript allows. This might involve
  2125      * creating collectors.
  2127      * @param methodHandle method handle for invoke
  2128      * @param callType     type of the call
  2130      * @return method handle with adjusted arguments
  2131      */
  2132     protected static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType) {
  2133         return pairArguments(methodHandle, callType, null);
  2136     /**
  2137      * Make sure arguments are paired correctly, with respect to more parameters than declared,
  2138      * fewer parameters than declared and other things that JavaScript allows. This might involve
  2139      * creating collectors.
  2141      * Make sure arguments are paired correctly.
  2142      * @param methodHandle MethodHandle to adjust.
  2143      * @param callType     MethodType of the call site.
  2144      * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the
  2145      * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a
  2146      * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites
  2147      * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters.
  2149      * @return method handle with adjusted arguments
  2150      */
  2151     public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) {
  2153         final MethodType methodType = methodHandle.type();
  2154         if (methodType.equals(callType)) {
  2155             return methodHandle;
  2158         final int parameterCount = methodType.parameterCount();
  2159         final int callCount      = callType.parameterCount();
  2161         final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
  2162         final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : (callCount > 0 &&
  2163                 callType.parameterType(callCount - 1).isArray());
  2165         if (callCount < parameterCount) {
  2166             final int      missingArgs = parameterCount - callCount;
  2167             final Object[] fillers     = new Object[missingArgs];
  2169             Arrays.fill(fillers, UNDEFINED);
  2171             if (isCalleeVarArg) {
  2172                 fillers[missingArgs - 1] = new Object[0];
  2175             return MH.insertArguments(
  2176                 methodHandle,
  2177                 parameterCount - missingArgs,
  2178                 fillers);
  2181         if (isCalleeVarArg) {
  2182             return isCallerVarArg ?
  2183                 methodHandle :
  2184                 MH.asCollector(methodHandle, Object[].class, callCount - parameterCount + 1);
  2187         if (isCallerVarArg) {
  2188             final int spreadArgs = parameterCount - callCount + 1;
  2189             return MH.filterArguments(
  2190                 MH.asSpreader(
  2191                     methodHandle,
  2192                     Object[].class,
  2193                     spreadArgs),
  2194                 callCount - 1,
  2195                 MH.insertArguments(
  2196                     TRUNCATINGFILTER,
  2197                     0,
  2198                     spreadArgs)
  2199                 );
  2202         if (callCount > parameterCount) {
  2203             final int discardedArgs = callCount - parameterCount;
  2205             final Class<?>[] discards = new Class<?>[discardedArgs];
  2206             Arrays.fill(discards, Object.class);
  2208             return MH.dropArguments(methodHandle, callCount - discardedArgs, discards);
  2211         return methodHandle;
  2214     @SuppressWarnings("unused")
  2215     private static Object[] truncatingFilter(final int n, final Object[] array) {
  2216         final int length = array == null ? 0 : array.length;
  2217         if (n == length) {
  2218             return array == null ? new Object[0] : array;
  2221         final Object[] newArray = new Object[n];
  2223         if (array != null) {
  2224             for (int i = 0; i < n && i < length; i++) {
  2225                 newArray[i] = array[i];
  2229         if (length < n) {
  2230             final Object fill = UNDEFINED;
  2232             for (int i = length; i < n; i++) {
  2233                 newArray[i] = fill;
  2237         return newArray;
  2240     /**
  2241       * Numeric length setter for length property
  2243       * @param newLength new length to set
  2244       */
  2245     public final void setLength(final long newLength) {
  2246        final long arrayLength = getArray().length();
  2247        if (newLength == arrayLength) {
  2248            return;
  2251        final boolean isStrict = getContext()._strict;
  2253        if (newLength > arrayLength) {
  2254            setArray(getArray().ensure(newLength - 1));
  2255             if (getArray().canDelete(arrayLength, (newLength - 1), isStrict)) {
  2256                setArray(getArray().delete(arrayLength, (newLength - 1)));
  2258            return;
  2261        if (newLength < arrayLength) {
  2262            setArray(getArray().shrink(newLength));
  2263            getArray().setLength(newLength);
  2267     private int getInt(final int index, final String key) {
  2268         for (ScriptObject object = this; object != null; object = object.getProto()) {
  2269             final ArrayData array = object.getArray();
  2271             if (array.has(index)) {
  2272                 return array.getInt(index);
  2275             final FindProperty find = object.findProperty(key, false);
  2277             if (find != null) {
  2278                 return getIntValue(new FindProperty(this, find.getOwner(), find.getProperty()));
  2282         return JSType.toInt32(invokeNoSuchProperty(key));
  2285     @Override
  2286     public int getInt(final Object key) {
  2287         return getInt(getArrayIndexNoThrow(key), convertKey(key));
  2290     @Override
  2291     public int getInt(final double key) {
  2292         return getInt(getArrayIndexNoThrow(key), convertKey(key));
  2295     @Override
  2296     public int getInt(final long key) {
  2297         return getInt(getArrayIndexNoThrow(key), convertKey(key));
  2300     @Override
  2301     public int getInt(final int key) {
  2302         return getInt(getArrayIndexNoThrow(key), convertKey(key));
  2305     private long getLong(final int index, final String key) {
  2306         for (ScriptObject object = this; object != null; object = object.getProto()) {
  2307             final ArrayData array = object.getArray();
  2309             if (array.has(index)) {
  2310                 return array.getLong(index);
  2313             final FindProperty find = object.findProperty(key, false);
  2315             if (find != null) {
  2316                 return getLongValue(new FindProperty(this, find.getOwner(), find.getProperty()));
  2320         return JSType.toLong(invokeNoSuchProperty(key));
  2323     @Override
  2324     public long getLong(final Object key) {
  2325         return getLong(getArrayIndexNoThrow(key), convertKey(key));
  2328     @Override
  2329     public long getLong(final double key) {
  2330         return getLong(getArrayIndexNoThrow(key), convertKey(key));
  2333     @Override
  2334     public long getLong(final long key) {
  2335         return getLong(getArrayIndexNoThrow(key), convertKey(key));
  2338     @Override
  2339     public long getLong(final int key) {
  2340         return getLong(getArrayIndexNoThrow(key), convertKey(key));
  2343     private double getDouble(final int index, final String key) {
  2344         for (ScriptObject object = this; object != null; object = object.getProto()) {
  2345             final ArrayData array = object.getArray();
  2347             if (array.has(index)) {
  2348                 return array.getDouble(index);
  2351             final FindProperty find = object.findProperty(key, false);
  2353             if (find != null) {
  2354                 return getDoubleValue(new FindProperty(this, find.getOwner(), find.getProperty()));
  2358         return JSType.toNumber(invokeNoSuchProperty(key));
  2361     @Override
  2362     public double getDouble(final Object key) {
  2363         return getDouble(getArrayIndexNoThrow(key), convertKey(key));
  2366     @Override
  2367     public double getDouble(final double key) {
  2368         return getDouble(getArrayIndexNoThrow(key), convertKey(key));
  2371     @Override
  2372     public double getDouble(final long key) {
  2373         return getDouble(getArrayIndexNoThrow(key), convertKey(key));
  2376     @Override
  2377     public double getDouble(final int key) {
  2378         return getDouble(getArrayIndexNoThrow(key), convertKey(key));
  2381     private Object get(final int index, final String key) {
  2382         for (ScriptObject object = this; object != null; object = object.getProto()) {
  2383             final ArrayData array = object.getArray();
  2385             if (array.has(index)) {
  2386                 return array.getObject(index);
  2389             final FindProperty find = object.findProperty(key, false);
  2391             if (find != null) {
  2392                 return getObjectValue(new FindProperty(this, find.getOwner(), find.getProperty()));
  2396         return invokeNoSuchProperty(key);
  2399     @Override
  2400     public Object get(final Object key) {
  2401         return get(getArrayIndexNoThrow(key), convertKey(key));
  2404     @Override
  2405     public Object get(final double key) {
  2406         return get(getArrayIndexNoThrow(key), convertKey(key));
  2409     @Override
  2410     public Object get(final long key) {
  2411         return get(getArrayIndexNoThrow(key), convertKey(key));
  2414     @Override
  2415     public Object get(final int key) {
  2416         return get(getArrayIndexNoThrow(key), convertKey(key));
  2419     /**
  2420      * Handle when an array doesn't have a slot - possibly grow and/or convert array.
  2422      * @param index  key as index
  2423      * @param value  element value
  2424      * @param strict are we in strict mode
  2425      */
  2426     private void doesNotHave(final int index, final Object value, final boolean strict) {
  2427         final long oldLength = getArray().length();
  2428         final long longIndex = index & JSType.MAX_UINT;
  2430         if (!getArray().has(index)) {
  2431             final String key = convertKey(longIndex);
  2432             final FindProperty find = findProperty(key, true);
  2434             if (find != null) {
  2435                 setObject(find, strict, key, value);
  2436                 return;
  2440         if (longIndex >= oldLength) {
  2441             if (!isExtensible()) {
  2442                 if (strict) {
  2443                     throw typeError("object.non.extensible", JSType.toString(index), ScriptRuntime.safeToString(this));
  2445                 return;
  2447             setArray(getArray().ensure(longIndex));
  2450         if (value instanceof Integer) {
  2451             setArray(getArray().set(index, (int)value, strict));
  2452         } else if (value instanceof Long) {
  2453             setArray(getArray().set(index, (long)value, strict));
  2454         } else if (value instanceof Double) {
  2455             setArray(getArray().set(index, (double)value, strict));
  2456         } else {
  2457             setArray(getArray().set(index, value, strict));
  2460         if (longIndex > oldLength) {
  2461             ArrayData array = getArray();
  2463             if (array.canDelete(oldLength, (longIndex - 1), strict)) {
  2464                 array = array.delete(oldLength, (longIndex - 1));
  2467             setArray(array);
  2471     /**
  2472      * This is the most generic of all Object setters. Most of the others use this in some form.
  2473      * TODO: should be further specialized
  2475      * @param find    found property
  2476      * @param strict  are we in strict mode
  2477      * @param key     property key
  2478      * @param value   property value
  2479      */
  2480     public final void setObject(final FindProperty find, final boolean strict, final String key, final Object value) {
  2481         FindProperty f = find;
  2483         if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) {
  2484             f = null;
  2487         if (f != null) {
  2488             if (!f.getProperty().isWritable()) {
  2489                 if (strict) {
  2490                     throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this));
  2493                 return;
  2496             try {
  2497                 final MethodHandle setter = f.getSetter(Object.class, strict); //TODO specfields
  2498                 setter.invokeExact((Object)f.getSetterReceiver(), value);
  2499             } catch (final Error|RuntimeException e) {
  2500                 throw e;
  2501             } catch (final Throwable e) {
  2502                 throw new RuntimeException(e);
  2504         } else if (!isExtensible()) {
  2505             if (strict) {
  2506                 throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
  2508         } else {
  2509             spill(key, value);
  2513     private void spill(final String key, final Object value) {
  2514         try {
  2515             addSpill(key).invokeExact((Object)this, value);
  2516         } catch (final Error|RuntimeException e) {
  2517             throw e;
  2518         } catch (final Throwable e) {
  2519             throw new RuntimeException(e);
  2524     @Override
  2525     public void set(final Object key, final int value, final boolean strict) {
  2526         final int index = getArrayIndexNoThrow(key);
  2528         if (isValidArrayIndex(index)) {
  2529             if (getArray().has(index)) {
  2530                 setArray(getArray().set(index, value, strict));
  2531             } else {
  2532                 doesNotHave(index, value, strict);
  2535             return;
  2538         set(key, JSType.toObject(value), strict);
  2541     @Override
  2542     public void set(final Object key, final long value, final boolean strict) {
  2543         final int index = getArrayIndexNoThrow(key);
  2545         if (isValidArrayIndex(index)) {
  2546             if (getArray().has(index)) {
  2547                 setArray(getArray().set(index, value, strict));
  2548             } else {
  2549                 doesNotHave(index, value, strict);
  2552             return;
  2555         set(key, JSType.toObject(value), strict);
  2558     @Override
  2559     public void set(final Object key, final double value, final boolean strict) {
  2560         final int index = getArrayIndexNoThrow(key);
  2562         if (isValidArrayIndex(index)) {
  2563             if (getArray().has(index)) {
  2564                 setArray(getArray().set(index, value, strict));
  2565             } else {
  2566                 doesNotHave(index, value, strict);
  2569             return;
  2572         set(key, JSType.toObject(value), strict);
  2575     @Override
  2576     public void set(final Object key, final Object value, final boolean strict) {
  2577         final int index = getArrayIndexNoThrow(key);
  2579         if (isValidArrayIndex(index)) {
  2580             if (getArray().has(index)) {
  2581                 setArray(getArray().set(index, value, strict));
  2582             } else {
  2583                 doesNotHave(index, value, strict);
  2586             return;
  2589         final String       propName = convertKey(key);
  2590         final FindProperty find     = findProperty(propName, true);
  2592         setObject(find, strict, propName, value);
  2595     @Override
  2596     public void set(final double key, final int value, final boolean strict) {
  2597         final int index = getArrayIndexNoThrow(key);
  2599         if (isValidArrayIndex(index)) {
  2600             if (getArray().has(index)) {
  2601                 setArray(getArray().set(index, value, strict));
  2602             } else {
  2603                 doesNotHave(index, value, strict);
  2606             return;
  2609         set(JSType.toObject(key), JSType.toObject(value), strict);
  2612     @Override
  2613     public void set(final double key, final long value, final boolean strict) {
  2614         final int index = getArrayIndexNoThrow(key);
  2616         if (isValidArrayIndex(index)) {
  2617             if (getArray().has(index)) {
  2618                 setArray(getArray().set(index, value, strict));
  2619             } else {
  2620                 doesNotHave(index, value, strict);
  2623             return;
  2626         set(JSType.toObject(key), JSType.toObject(value), strict);
  2629     @Override
  2630     public void set(final double key, final double value, final boolean strict) {
  2631         final int index = getArrayIndexNoThrow(key);
  2633         if (isValidArrayIndex(index)) {
  2634             if (getArray().has(index)) {
  2635                 setArray(getArray().set(index, value, strict));
  2636             } else {
  2637                 doesNotHave(index, value, strict);
  2640             return;
  2643         set(JSType.toObject(key), JSType.toObject(value), strict);
  2646     @Override
  2647     public void set(final double key, final Object value, final boolean strict) {
  2648         final int index = getArrayIndexNoThrow(key);
  2650         if (isValidArrayIndex(index)) {
  2651             if (getArray().has(index)) {
  2652                 setArray(getArray().set(index, value, strict));
  2653             } else {
  2654                 doesNotHave(index, value, strict);
  2657             return;
  2660         set(JSType.toObject(key), value, strict);
  2663     @Override
  2664     public void set(final long key, final int value, final boolean strict) {
  2665         final int index = getArrayIndexNoThrow(key);
  2667         if (isValidArrayIndex(index)) {
  2668             if (getArray().has(index)) {
  2669                 setArray(getArray().set(index, value, strict));
  2670             } else {
  2671                 doesNotHave(index, value, strict);
  2674             return;
  2677         set(JSType.toObject(key), JSType.toObject(value), strict);
  2680     @Override
  2681     public void set(final long key, final long value, final boolean strict) {
  2682         final int index = getArrayIndexNoThrow(key);
  2684         if (isValidArrayIndex(index)) {
  2685             if (getArray().has(index)) {
  2686                 setArray(getArray().set(index, value, strict));
  2687             } else {
  2688                 doesNotHave(index, value, strict);
  2691             return;
  2694         set(JSType.toObject(key), JSType.toObject(value), strict);
  2697     @Override
  2698     public void set(final long key, final double value, final boolean strict) {
  2699         final int index = getArrayIndexNoThrow(key);
  2701         if (isValidArrayIndex(index)) {
  2702             if (getArray().has(index)) {
  2703                 setArray(getArray().set(index, value, strict));
  2704             } else {
  2705                 doesNotHave(index, value, strict);
  2708             return;
  2711         set(JSType.toObject(key), JSType.toObject(value), strict);
  2714     @Override
  2715     public void set(final long key, final Object value, final boolean strict) {
  2716         final int index = getArrayIndexNoThrow(key);
  2718         if (isValidArrayIndex(index)) {
  2719             if (getArray().has(index)) {
  2720                 setArray(getArray().set(index, value, strict));
  2721             } else {
  2722                 doesNotHave(index, value, strict);
  2725             return;
  2728         set(JSType.toObject(key), value, strict);
  2731     @Override
  2732     public void set(final int key, final int value, final boolean strict) {
  2733         final int index = getArrayIndexNoThrow(key);
  2735         if (isValidArrayIndex(index)) {
  2736             if (getArray().has(index)) {
  2737                 setArray(getArray().set(index, value, strict));
  2738             } else {
  2739                 doesNotHave(index, value, strict);
  2742             return;
  2745         set(JSType.toObject(key), JSType.toObject(value), strict);
  2748     @Override
  2749     public void set(final int key, final long value, final boolean strict) {
  2750         final int index = getArrayIndexNoThrow(key);
  2752         if (isValidArrayIndex(index)) {
  2753             if (getArray().has(index)) {
  2754                 setArray(getArray().set(index, value, strict));
  2755             } else {
  2756                 doesNotHave(index, value, strict);
  2759             return;
  2762         set(JSType.toObject(key), JSType.toObject(value), strict);
  2765     @Override
  2766     public void set(final int key, final double value, final boolean strict) {
  2767         final int index = getArrayIndexNoThrow(key);
  2769         if (isValidArrayIndex(index)) {
  2770             if (getArray().has(index)) {
  2771                 setArray(getArray().set(index, value, strict));
  2772             } else {
  2773                 doesNotHave(index, value, strict);
  2776             return;
  2779         set(JSType.toObject(key), JSType.toObject(value), strict);
  2782     @Override
  2783     public void set(final int key, final Object value, final boolean strict) {
  2784         final int index = getArrayIndexNoThrow(key);
  2786         if (isValidArrayIndex(index)) {
  2787             if (getArray().has(index)) {
  2788                 setArray(getArray().set(index, value, strict));
  2789             } else {
  2790                 doesNotHave(index, value, strict);
  2793             return;
  2796         set(JSType.toObject(key), value, strict);
  2799     @Override
  2800     public boolean has(final Object key) {
  2801         final int index = getArrayIndexNoThrow(key);
  2803         if (isValidArrayIndex(index)) {
  2804             for (ScriptObject self = this; self != null; self = self.getProto()) {
  2805                 if (self.getArray().has(index)) {
  2806                     return true;
  2811         final FindProperty find = findProperty(convertKey(key), true);
  2813         return find != null;
  2816     @Override
  2817     public boolean has(final double key) {
  2818         final int index = getArrayIndexNoThrow(key);
  2820         if (isValidArrayIndex(index)) {
  2821             for (ScriptObject self = this; self != null; self = self.getProto()) {
  2822                 if (self.getArray().has(index)) {
  2823                     return true;
  2828         final FindProperty find = findProperty(convertKey(key), true);
  2830         return find != null;
  2833     @Override
  2834     public boolean has(final long key) {
  2835         final int index = getArrayIndexNoThrow(key);
  2837         if (isValidArrayIndex(index)) {
  2838             for (ScriptObject self = this; self != null; self = self.getProto()) {
  2839                 if (self.getArray().has(index)) {
  2840                     return true;
  2845         final FindProperty find = findProperty(convertKey(key), true);
  2847         return find != null;
  2850     @Override
  2851     public boolean has(final int key) {
  2852         final int index = getArrayIndexNoThrow(key);
  2854         if (isValidArrayIndex(index)) {
  2855             for (ScriptObject self = this; self != null; self = self.getProto()) {
  2856                 if (self.getArray().has(index)) {
  2857                     return true;
  2862         final FindProperty find = findProperty(convertKey(key), true);
  2864         return find != null;
  2867     @Override
  2868     public boolean hasOwnProperty(final Object key) {
  2869         final int index = getArrayIndexNoThrow(key);
  2871         if (getArray().has(index)) {
  2872             return true;
  2875         final FindProperty find = findProperty(convertKey(key), false);
  2877         return find != null;
  2880     @Override
  2881     public boolean hasOwnProperty(final int key) {
  2882         final int index = getArrayIndexNoThrow(key);
  2884         if (getArray().has(index)) {
  2885             return true;
  2888         final FindProperty find = findProperty(convertKey(key), false);
  2890         return find != null;
  2893     @Override
  2894     public boolean hasOwnProperty(final long key) {
  2895         final int index = getArrayIndexNoThrow(key);
  2897         if (getArray().has(index)) {
  2898             return true;
  2901         final FindProperty find = findProperty(convertKey(key), false);
  2903         return find != null;
  2906     @Override
  2907     public boolean hasOwnProperty(final double key) {
  2908         final int index = getArrayIndexNoThrow(key);
  2910         if (getArray().has(index)) {
  2911             return true;
  2914         final FindProperty find = findProperty(convertKey(key), false);
  2916         return find != null;
  2919     @Override
  2920     public boolean delete(final int key, final boolean strict) {
  2921         final int index = getArrayIndexNoThrow(key);
  2922         final ArrayData array = getArray();
  2924         if (array.has(index)) {
  2925             if (array.canDelete(index, strict)) {
  2926                 setArray(array.delete(index));
  2927                 return true;
  2929             return false;
  2932         return deleteObject(JSType.toObject(key), strict);
  2935     @Override
  2936     public boolean delete(final long key, final boolean strict) {
  2937         final int index = getArrayIndexNoThrow(key);
  2938         final ArrayData array = getArray();
  2940         if (array.has(index)) {
  2941             if (array.canDelete(index, strict)) {
  2942                 setArray(array.delete(index));
  2943                 return true;
  2945             return false;
  2948         return deleteObject(JSType.toObject(key), strict);
  2951     @Override
  2952     public boolean delete(final double key, final boolean strict) {
  2953         final int index = getArrayIndexNoThrow(key);
  2954         final ArrayData array = getArray();
  2956         if (array.has(index)) {
  2957             if (array.canDelete(index, strict)) {
  2958                 setArray(array.delete(index));
  2959                 return true;
  2961             return false;
  2964         return deleteObject(JSType.toObject(key), strict);
  2967     @Override
  2968     public boolean delete(final Object key, final boolean strict) {
  2969         final int index = getArrayIndexNoThrow(key);
  2970         final ArrayData array = getArray();
  2972         if (array.has(index)) {
  2973             if (array.canDelete(index, strict)) {
  2974                 setArray(array.delete(index));
  2975                 return true;
  2977             return false;
  2980         return deleteObject(key, strict);
  2983     private boolean deleteObject(final Object key, final boolean strict) {
  2984         final String propName = convertKey(key);
  2985         final FindProperty find = findProperty(propName, false);
  2987         if (find == null) {
  2988             return true;
  2991         if (!find.getProperty().isConfigurable()) {
  2992             if (strict) {
  2993                 throw typeError("cant.delete.property", propName, ScriptRuntime.safeToString(this));
  2995             return false;
  2998         final Property prop = find.getProperty();
  2999         notifyPropertyDeleted(this, prop);
  3000         deleteOwnProperty(prop);
  3002         return true;
  3005     /*
  3006      * Embed management
  3007      */
  3009     /** Number of embed slots */
  3010     public static final int EMBED_SIZE   = 4;
  3011     /** Embed offset */
  3012     public static final int EMBED_OFFSET = 32 - EMBED_SIZE;
  3014     static final MethodHandle[] GET_EMBED;
  3015     static final MethodHandle[] SET_EMBED;
  3017     static {
  3018         GET_EMBED = new MethodHandle[EMBED_SIZE];
  3019         SET_EMBED = new MethodHandle[EMBED_SIZE];
  3021         for (int i = 0; i < EMBED_SIZE; i++) {
  3022             final String name = "embed" + i;
  3023             GET_EMBED[i] = MH.asType(MH.getter(MethodHandles.lookup(), ScriptObject.class, name, Object.class), Lookup.GET_OBJECT_TYPE);
  3024             SET_EMBED[i] = MH.asType(MH.setter(MethodHandles.lookup(), ScriptObject.class, name, Object.class), Lookup.SET_OBJECT_TYPE);
  3028     void useEmbed(final int i) {
  3029         flags |= 1 << (EMBED_OFFSET + i);
  3032     int findEmbed() {
  3033         final int bits  = ~(flags >>> EMBED_OFFSET);
  3034         final int least = bits ^ -bits;
  3035         final int index = Integer.numberOfTrailingZeros(least) - 1;
  3037         return index;
  3040     /*
  3041      * Make a new UserAccessorProperty property. getter and setter functions are stored in
  3042      * this ScriptObject and slot values are used in property object.
  3043      */
  3044     private UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
  3045         int oldSpillLength = getMap().getSpillLength();
  3047         int getterSlot = findEmbed();
  3048         if (getterSlot >= EMBED_SIZE) {
  3049             getterSlot = oldSpillLength + EMBED_SIZE;
  3050             ++oldSpillLength;
  3051         } else {
  3052             useEmbed(getterSlot);
  3054         setEmbedOrSpill(getterSlot, getter);
  3055         // if getter function is null, flag the slot to be negative (less by 1)
  3056         if (getter == null) {
  3057             getterSlot = -getterSlot - 1;
  3060         int setterSlot = findEmbed();
  3061         if (setterSlot >= EMBED_SIZE) {
  3062             setterSlot = oldSpillLength + EMBED_SIZE;
  3063         } else {
  3064             useEmbed(setterSlot);
  3066         setEmbedOrSpill(setterSlot, setter);
  3067         // if setter function is null, flag the slot to be negative (less by 1)
  3068         if (setter == null) {
  3069             setterSlot = -setterSlot - 1;
  3072         return new UserAccessorProperty(key, propertyFlags, getterSlot, setterSlot);
  3075     private void setEmbedOrSpill(final int slot, final Object value) {
  3076         switch (slot) {
  3077         case 0:
  3078             embed0 = value;
  3079             break;
  3080         case 1:
  3081             embed1 = value;
  3082             break;
  3083         case 2:
  3084             embed2 = value;
  3085             break;
  3086         case 3:
  3087             embed3 = value;
  3088             break;
  3089         default:
  3090             if (slot >= 0) {
  3091                 final int index = (slot - EMBED_SIZE);
  3092                 if (spill == null) {
  3093                     // create new spill.
  3094                     spill = new Object[Math.max(index + 1, SPILL_RATE)];
  3095                 } else if (index >= spill.length) {
  3096                     // grow spill as needed
  3097                     final Object[] newSpill = new Object[index + 1];
  3098                     System.arraycopy(spill, 0, newSpill, 0, spill.length);
  3099                     spill = newSpill;
  3102                 spill[index] = value;
  3104             break;
  3108     // user accessors are either stored in embed fields or spill array slots
  3109     // get the accessor value using slot number. Note that slot is either embed
  3110     // field number or (spill array index + embedSize).
  3111     Object getEmbedOrSpill(final int slot) {
  3112         switch (slot) {
  3113         case 0:
  3114             return embed0;
  3115         case 1:
  3116             return embed1;
  3117         case 2:
  3118             return embed2;
  3119         case 3:
  3120             return embed3;
  3121         default:
  3122             final int index = (slot - EMBED_SIZE);
  3123             return (index < 0 || (index >= spill.length)) ? null : spill[index];
  3127     // User defined getter and setter are always called by "dyn:call". Note that the user
  3128     // getter/setter may be inherited. If so, proto is bound during lookup. In either
  3129     // inherited or self case, slot is also bound during lookup. Actual ScriptFunction
  3130     // to be called is retrieved everytime and applied.
  3131     @SuppressWarnings("unused")
  3132     private static Object userAccessorGetter(final ScriptObject proto, final int slot, final Object self) {
  3133         final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
  3134         final Object       func      = container.getEmbedOrSpill(slot);
  3136         if (func instanceof ScriptFunction) {
  3137             try {
  3138                 return INVOKE_UA_GETTER.invokeExact(func, self);
  3139             } catch(final Error|RuntimeException t) {
  3140                 throw t;
  3141             } catch(final Throwable t) {
  3142                 throw new RuntimeException(t);
  3146         return UNDEFINED;
  3149     @SuppressWarnings("unused")
  3150     private static void userAccessorSetter(final ScriptObject proto, final int slot, final String name, final Object self, final Object value) {
  3151         final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
  3152         final Object       func      = container.getEmbedOrSpill(slot);
  3154         if (func instanceof ScriptFunction) {
  3155             try {
  3156                 INVOKE_UA_SETTER.invokeExact(func, self, value);
  3157             } catch(final Error|RuntimeException t) {
  3158                 throw t;
  3159             } catch(final Throwable t) {
  3160                 throw new RuntimeException(t);
  3162         }  else if (name != null) {
  3163             throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
  3167     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
  3168         final Class<?>   own = ScriptObject.class;
  3169         final MethodType mt  = MH.type(rtype, types);
  3170         try {
  3171             return MH.findStatic(MethodHandles.lookup(), own, name, mt);
  3172         } catch (final MethodHandleFactory.LookupException e) {
  3173             return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
  3177     private static MethodHandle getKnownFunctionPropertyGuard(final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
  3178         return MH.insertArguments(KNOWNFUNCPROPGUARD, 1, map, getter, where, func);
  3181     @SuppressWarnings("unused")
  3182     private static boolean knownFunctionPropertyGuard(final Object self, final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
  3183         if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) {
  3184             try {
  3185                 return getter.invokeExact(where) == func;
  3186             } catch (final RuntimeException | Error e) {
  3187                 throw e;
  3188             } catch (final Throwable t) {
  3189                 throw new RuntimeException(t);
  3193         return false;
  3196     /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */
  3197     private static int count;
  3199     /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created that are scope */
  3200     private static int scopeCount;
  3202     /**
  3203      * Get number of {@code ScriptObject} instances created. If not running in debug
  3204      * mode this is always 0
  3206      * @return number of ScriptObjects created
  3207      */
  3208     public static int getCount() {
  3209         return count;
  3212     /**
  3213      * Get number of scope {@code ScriptObject} instances created. If not running in debug
  3214      * mode this is always 0
  3216      * @return number of scope ScriptObjects created
  3217      */
  3218     public static int getScopeCount() {
  3219         return scopeCount;

mercurial