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

Thu, 02 May 2013 09:19:44 +0200

author
hannesw
date
Thu, 02 May 2013 09:19:44 +0200
changeset 243
80cb02dedc83
parent 242
b754fb89367d
child 247
5a3f7867e19c
permissions
-rw-r--r--

8013729: SwitchPoint invalidation not working over prototype chain
Reviewed-by: lagergren, sundar

     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     /** Is this a prototype PropertyMap? */
   108     public static final int IS_PROTOTYPE   = 0b0000_1000;
   110     /** Spill growth rate - by how many elements does {@link ScriptObject#spill} when full */
   111     public static final int SPILL_RATE = 8;
   113     /** Map to property information and accessor functions. Ordered by insertion. */
   114     private PropertyMap map;
   116     /** objects proto. */
   117     private ScriptObject proto;
   119     /** Context of the object, lazily cached. */
   120     private Context context;
   122     /** Object flags. */
   123     private int flags;
   125     /** Area for properties added to object after instantiation, see {@link AccessorProperty} */
   126     public Object[] spill;
   128     /** Indexed array data. */
   129     private ArrayData arrayData;
   131     static final MethodHandle SETFIELD           = findOwnMH("setField",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, Object.class, Object.class);
   132     static final MethodHandle SETSPILL           = findOwnMH("setSpill",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
   133     static final MethodHandle SETSPILLWITHNEW    = findOwnMH("setSpillWithNew",  void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
   134     static final MethodHandle SETSPILLWITHGROW   = findOwnMH("setSpillWithGrow", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, int.class, Object.class, Object.class);
   136     private static final MethodHandle TRUNCATINGFILTER   = findOwnMH("truncatingFilter", Object[].class, int.class, Object[].class);
   137     private static final MethodHandle KNOWNFUNCPROPGUARD = findOwnMH("knownFunctionPropertyGuard", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, Object.class, ScriptFunction.class);
   139     /** Method handle for getting a function argument at a given index. Used from MapCreator */
   140     public static final Call GET_ARGUMENT       = virtualCall(ScriptObject.class, "getArgument", Object.class, int.class);
   142     /** Method handle for setting a function argument at a given index. Used from MapCreator */
   143     public static final Call SET_ARGUMENT       = virtualCall(ScriptObject.class, "setArgument", void.class, int.class, Object.class);
   145     /** Method handle for getting the proto of a ScriptObject */
   146     public static final Call GET_PROTO          = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class);
   148     /** Method handle for setting the proto of a ScriptObject */
   149     public static final Call SET_PROTO          = virtualCallNoLookup(ScriptObject.class, "setProto", void.class, ScriptObject.class);
   151     /** Method handle for setting the user accessors of a ScriptObject */
   152     public static final Call SET_USER_ACCESSORS = virtualCall(ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class);
   154     /** Method handle for getter for {@link UserAccessorProperty}, given a slot */
   155     static final Call USER_ACCESSOR_GETTER = staticCall(MethodHandles.lookup(), ScriptObject.class, "userAccessorGetter", Object.class, ScriptObject.class, int.class, Object.class);
   157     /** Method handle for setter for {@link UserAccessorProperty}, given a slot */
   158     static final Call USER_ACCESSOR_SETTER = staticCall(MethodHandles.lookup(), ScriptObject.class, "userAccessorSetter", void.class, ScriptObject.class, int.class, String.class, Object.class, Object.class);
   160     private static final MethodHandle INVOKE_UA_GETTER = Bootstrap.createDynamicInvoker("dyn:call", Object.class,
   161             Object.class, Object.class);
   162     private static final MethodHandle INVOKE_UA_SETTER = Bootstrap.createDynamicInvoker("dyn:call", void.class,
   163             Object.class, Object.class, Object.class);
   165     /**
   166      * Constructor
   167      */
   168     public ScriptObject() {
   169         this(null);
   170     }
   172     /**
   173     * Constructor
   174     *
   175     * @param map {@link PropertyMap} used to create the initial object
   176     */
   177     public ScriptObject(final PropertyMap map) {
   178         if (Context.DEBUG) {
   179             ScriptObject.count++;
   180         }
   182         this.arrayData = ArrayData.EMPTY_ARRAY;
   184         if (map == null) {
   185             this.setMap(PropertyMap.newMap(getClass()));
   186             return;
   187         }
   189         this.setMap(map);
   190     }
   192     /**
   193      * Copy all properties from the source object with their receiver bound to the source.
   194      * This function was known as mergeMap
   195      *
   196      * @param source The source object to copy from.
   197      */
   198     public void addBoundProperties(final ScriptObject source) {
   199         PropertyMap newMap = this.getMap();
   201         for (final Property property : source.getMap().getProperties()) {
   202             final String key = property.getKey();
   204             if (newMap.findProperty(key) == null) {
   205                 if (property instanceof UserAccessorProperty) {
   206                     final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
   207                     newMap = newMap.addProperty(prop);
   208                 } else {
   209                     newMap = newMap.newPropertyBind((AccessorProperty)property, source);
   210                 }
   211             }
   212         }
   214         this.setMap(newMap);
   215     }
   217     /**
   218      * Bind the method handle to the specified receiver, while preserving its original type (it will just ignore the
   219      * first argument in lieu of the bound argument).
   220      * @param methodHandle Method handle to bind to.
   221      * @param receiver     Object to bind.
   222      * @return Bound method handle.
   223      */
   224     static MethodHandle bindTo(final MethodHandle methodHandle, final Object receiver) {
   225         return MH.dropArguments(MH.bindTo(methodHandle, receiver), 0, methodHandle.type().parameterType(0));
   226     }
   228     /**
   229      * Return a property iterator.
   230      * @return Property iterator.
   231      */
   232     public Iterator<String> propertyIterator() {
   233         return new KeyIterator(this);
   234     }
   236     /**
   237      * Return a property value iterator.
   238      * @return Property value iterator.
   239      */
   240     public Iterator<Object> valueIterator() {
   241         return new ValueIterator(this);
   242     }
   244     /**
   245      * ECMA 8.10.1 IsAccessorDescriptor ( Desc )
   246      * @return true if this has a {@link AccessorPropertyDescriptor} with a getter or a setter
   247      */
   248     public final boolean isAccessorDescriptor() {
   249         return has(GET) || has(SET);
   250     }
   252     /**
   253      * ECMA 8.10.2 IsDataDescriptor ( Desc )
   254      * @return true if this has a {@link DataPropertyDescriptor}, i.e. the object has a property value and is writable
   255      */
   256     public final boolean isDataDescriptor() {
   257         return has(VALUE) || has(WRITABLE);
   258     }
   260     /**
   261      * ECMA 8.10.3 IsGenericDescriptor ( Desc )
   262      * @return true if this has a descriptor describing an {@link AccessorPropertyDescriptor} or {@link DataPropertyDescriptor}
   263      */
   264     public final boolean isGenericDescriptor() {
   265         return isAccessorDescriptor() || isDataDescriptor();
   266     }
   268     /**
   269       * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
   270       *
   271       * @return property descriptor
   272       */
   273     public final PropertyDescriptor toPropertyDescriptor() {
   274         final GlobalObject global = (GlobalObject) Context.getGlobalTrusted();
   276         final PropertyDescriptor desc;
   277         if (isDataDescriptor()) {
   278             if (has(SET) || has(GET)) {
   279                 throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
   280             }
   282             desc = global.newDataDescriptor(UNDEFINED, false, false, false);
   283         } else if (isAccessorDescriptor()) {
   284             if (has(VALUE) || has(WRITABLE)) {
   285                 throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
   286             }
   288             desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false);
   289         } else {
   290             desc = global.newGenericDescriptor(false, false);
   291         }
   293         return desc.fillFrom(this);
   294     }
   296     /**
   297      * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
   298      *
   299      * @param global  global scope object
   300      * @param obj object to create property descriptor from
   301      *
   302      * @return property descriptor
   303      */
   304     public static PropertyDescriptor toPropertyDescriptor(final ScriptObject global, final Object obj) {
   305         if (obj instanceof ScriptObject) {
   306             return ((ScriptObject)obj).toPropertyDescriptor();
   307         }
   309         throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj));
   310     }
   312     /**
   313      * ECMA 8.12.1 [[GetOwnProperty]] (P)
   314      *
   315      * @param key property key
   316      *
   317      * @return Returns the Property Descriptor of the named own property of this
   318      * object, or undefined if absent.
   319      */
   320     public Object getOwnPropertyDescriptor(final String key) {
   321         final Property property = getMap().findProperty(key);
   323         final GlobalObject global = (GlobalObject)Context.getGlobalTrusted();
   325         if (property != null) {
   326             final ScriptFunction get   = property.getGetterFunction(this);
   327             final ScriptFunction set   = property.getSetterFunction(this);
   329             final boolean configurable = property.isConfigurable();
   330             final boolean enumerable   = property.isEnumerable();
   331             final boolean writable     = property.isWritable();
   333             if (property instanceof UserAccessorProperty) {
   334                 return global.newAccessorDescriptor(
   335                     (get != null) ?
   336                         get :
   337                         UNDEFINED,
   338                     (set != null) ?
   339                         set :
   340                         UNDEFINED,
   341                     configurable,
   342                     enumerable);
   343             }
   345             return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable);
   346         }
   348         final int index = getArrayIndexNoThrow(key);
   349         final ArrayData array = getArray();
   351         if (array.has(index)) {
   352             return array.getDescriptor(global, index);
   353         }
   355         return UNDEFINED;
   356     }
   358     /**
   359      * ECMA 8.12.2 [[GetProperty]] (P)
   360      *
   361      * @param key property key
   362      *
   363      * @return Returns the fully populated Property Descriptor of the named property
   364      * of this object, or undefined if absent.
   365      */
   366     public Object getPropertyDescriptor(final String key) {
   367         final Object res = getOwnPropertyDescriptor(key);
   369         if (res != UNDEFINED) {
   370             return res;
   371         } else if (getProto() != null) {
   372             return getProto().getOwnPropertyDescriptor(key);
   373         } else {
   374             return UNDEFINED;
   375         }
   376     }
   378     /**
   379      * ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw)
   380      *
   381      * @param key the property key
   382      * @param propertyDesc the property descriptor
   383      * @param reject is the property extensible - true means new definitions are rejected
   384      *
   385      * @return true if property was successfully defined
   386      */
   387     public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
   388         final ScriptObject       global  = Context.getGlobalTrusted();
   389         final PropertyDescriptor desc    = toPropertyDescriptor(global, propertyDesc);
   390         final Object             current = getOwnPropertyDescriptor(key);
   391         final String             name    = JSType.toString(key);
   393         if (current == UNDEFINED) {
   394             if (isExtensible()) {
   395                 // add a new own property
   396                 addOwnProperty(key, desc);
   397                 return true;
   398             }
   399             // new property added to non-extensible object
   400             if (reject) {
   401                 throw typeError(global, "object.non.extensible", name, ScriptRuntime.safeToString(this));
   402             }
   403             return false;
   404         }
   405         // modifying an existing property
   406         final PropertyDescriptor currentDesc = (PropertyDescriptor) current;
   407         final PropertyDescriptor newDesc     = desc;
   409         if (newDesc.type() == PropertyDescriptor.GENERIC &&
   410             ! newDesc.has(CONFIGURABLE) && ! newDesc.has(ENUMERABLE)) {
   411             // every descriptor field is absent
   412             return true;
   413         }
   415         if (currentDesc.equals(newDesc)) {
   416             // every descriptor field of the new is same as the current
   417             return true;
   418         }
   420         if (! currentDesc.isConfigurable()) {
   421             if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) {
   422                 // not configurable can not be made configurable
   423                 if (reject) {
   424                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   425                 }
   426                 return false;
   427             }
   429             if (newDesc.has(ENUMERABLE) &&
   430                 currentDesc.isEnumerable() != newDesc.isEnumerable()) {
   431                 // cannot make non-enumerable as enumerable or vice-versa
   432                 if (reject) {
   433                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   434                 }
   435                 return false;
   436             }
   437         }
   439         int propFlags = Property.mergeFlags(currentDesc, newDesc);
   440         Property property = getMap().findProperty(key);
   442         if (currentDesc.type() == PropertyDescriptor.DATA &&
   443             (newDesc.type() == PropertyDescriptor.DATA || newDesc.type() == PropertyDescriptor.GENERIC)) {
   444             if (! currentDesc.isConfigurable() && ! currentDesc.isWritable()) {
   445                 if (newDesc.has(WRITABLE) && newDesc.isWritable() ||
   446                     newDesc.has(VALUE) && ! ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) {
   447                     if (reject) {
   448                         throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   449                     }
   450                     return false;
   451                 }
   452             }
   454             final boolean newValue = newDesc.has(VALUE);
   455             final Object value     = newValue? newDesc.getValue() : currentDesc.getValue();
   456             if (newValue && property != null) {
   457                 // Temporarily clear flags.
   458                 property = modifyOwnProperty(property, 0);
   459                 set(key, value, getContext()._strict);
   460             }
   462             if (property == null) {
   463                 // promoting an arrayData value to actual property
   464                 addOwnProperty(key, propFlags, value);
   465                 removeArraySlot(key);
   466             } else {
   467                 // Now set the new flags
   468                 modifyOwnProperty(property, propFlags);
   469             }
   470         } else if (currentDesc.type() == PropertyDescriptor.ACCESSOR &&
   471                    (newDesc.type() == PropertyDescriptor.ACCESSOR ||
   472                     newDesc.type() == PropertyDescriptor.GENERIC)) {
   473             if (! currentDesc.isConfigurable()) {
   474                 if (newDesc.has(PropertyDescriptor.GET) && ! ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) ||
   475                     newDesc.has(PropertyDescriptor.SET) && ! ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) {
   476                     if (reject) {
   477                         throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   478                     }
   479                     return false;
   480                 }
   481             }
   483             // New set the new features.
   484             modifyOwnProperty(property, propFlags,
   485                                       newDesc.has(GET) ? newDesc.getGetter() : currentDesc.getGetter(),
   486                                       newDesc.has(SET) ? newDesc.getSetter() : currentDesc.getSetter());
   487         } else {
   488             // changing descriptor type
   489             if (! currentDesc.isConfigurable()) {
   490                 // not configurable can not be made configurable
   491                 if (reject) {
   492                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   493                 }
   494                 return false;
   495             }
   497             propFlags = 0;
   499             // Preserve only configurable and enumerable from current desc
   500             // if those are not overridden in the new property descriptor.
   501             boolean value = newDesc.has(CONFIGURABLE)? newDesc.isConfigurable() : currentDesc.isConfigurable();
   502             if (!value) {
   503                 propFlags |= Property.NOT_CONFIGURABLE;
   504             }
   505             value = newDesc.has(ENUMERABLE)? newDesc.isEnumerable() : currentDesc.isEnumerable();
   506             if (!value) {
   507                 propFlags |= Property.NOT_ENUMERABLE;
   508             }
   510             final int type = newDesc.type();
   511             if (type == PropertyDescriptor.DATA) {
   512                 // get writable from the new descriptor
   513                 value = newDesc.has(WRITABLE) && newDesc.isWritable();
   514                 if (! value) {
   515                     propFlags |= Property.NOT_WRITABLE;
   516                 }
   518                 // delete the old property
   519                 deleteOwnProperty(property);
   520                 // add new data property
   521                 addOwnProperty(key, propFlags, newDesc.getValue());
   522             } else if (type == PropertyDescriptor.ACCESSOR) {
   523                 if (property == null) {
   524                     addOwnProperty(key, propFlags,
   525                                      newDesc.has(GET) ? newDesc.getGetter() : null,
   526                                      newDesc.has(SET) ? newDesc.getSetter() : null);
   527                 } else {
   528                     // Modify old property with the new features.
   529                     modifyOwnProperty(property, propFlags,
   530                                         newDesc.has(GET) ? newDesc.getGetter() : null,
   531                                         newDesc.has(SET) ? newDesc.getSetter() : null);
   532                 }
   533             }
   534         }
   536         checkIntegerKey(key);
   538         return true;
   539     }
   541     /**
   542      * Spec. mentions use of [[DefineOwnProperty]] for indexed properties in
   543      * certain places (eg. Array.prototype.map, filter). We can not use ScriptObject.set
   544      * method in such cases. This is because set method uses inherited setters (if any)
   545      * from any object in proto chain such as Array.prototype, Object.prototype.
   546      * This method directly sets a particular element value in the current object.
   547      *
   548      * @param index index key for property
   549      * @param value value to define
   550      */
   551     protected final void defineOwnProperty(final int index, final Object value) {
   552         if (index >= getArray().length()) {
   553             // make array big enough to hold..
   554             setArray(getArray().ensure(index));
   555         }
   556         setArray(getArray().set(index, value, false));
   557     }
   559     private void checkIntegerKey(final String key) {
   560         final int index = getArrayIndexNoThrow(key);
   562         if (isValidArrayIndex(index)) {
   563             final ArrayData data = getArray();
   565             if (data.has(index)) {
   566                 setArray(data.delete(index));
   567             }
   568         }
   569     }
   571     private void removeArraySlot(final String key) {
   572         final int index = getArrayIndexNoThrow(key);
   573         final ArrayData array = getArray();
   575         if (array.has(index)) {
   576             setArray(array.delete(index));
   577         }
   578     }
   580     /**
   581       * Add a new property to the object.
   582       *
   583       * @param key          property key
   584       * @param propertyDesc property descriptor for property
   585       */
   586     public final void addOwnProperty(final String key, final PropertyDescriptor propertyDesc) {
   587         // Already checked that there is no own property with that key.
   588         PropertyDescriptor pdesc = propertyDesc;
   590         final int propFlags = Property.toFlags(pdesc);
   592         if (pdesc.type() == PropertyDescriptor.GENERIC) {
   593             final GlobalObject global = (GlobalObject) Context.getGlobalTrusted();
   594             final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false);
   596             dDesc.fillFrom((ScriptObject)pdesc);
   597             pdesc = dDesc;
   598         }
   600         final int type = pdesc.type();
   601         if (type == PropertyDescriptor.DATA) {
   602             addOwnProperty(key, propFlags, pdesc.getValue());
   603         } else if (type == PropertyDescriptor.ACCESSOR) {
   604             addOwnProperty(key, propFlags,
   605                     pdesc.has(GET) ? pdesc.getGetter() : null,
   606                     pdesc.has(SET) ? pdesc.getSetter() : null);
   607         }
   609         checkIntegerKey(key);
   610     }
   612     /**
   613      * Low level property API (not using property descriptors)
   614      * <p>
   615      * Find a property in the prototype hierarchy. Note: this is final and not
   616      * a good idea to override. If you have to, use
   617      * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
   618      * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
   619      * overriding way to find array properties
   620      *
   621      * @see jdk.nashorn.internal.objects.NativeArray
   622      *
   623      * @param key  Property key.
   624      * @param deep Whether the search should look up proto chain.
   625      *
   626      * @return FindPropertyData or null if not found.
   627      */
   628     public final FindProperty findProperty(final String key, final boolean deep) {
   629         return findProperty(key, deep, false, this);
   630     }
   632     /**
   633      * Low level property API (not using property descriptors)
   634      * <p>
   635      * Find a property in the prototype hierarchy. Note: this is not a good idea
   636      * to override except as it was done in {@link WithObject}.
   637      * If you have to, use
   638      * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
   639      * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
   640      * overriding way to find array properties
   641      *
   642      * @see jdk.nashorn.internal.objects.NativeArray
   643      *
   644      * @param key  Property key.
   645      * @param deep Whether the search should look up proto chain.
   646      * @param stopOnNonScope should a deep search stop on the first non-scope object?
   647      * @param start the object on which the lookup was originally initiated
   648      *
   649      * @return FindPropertyData or null if not found.
   650      */
   651     FindProperty findProperty(final String key, final boolean deep, final boolean stopOnNonScope, final ScriptObject start) {
   652         // if doing deep search, stop search on the first non-scope object if asked to do so
   653         if (stopOnNonScope && start != this && !isScope()) {
   654             return null;
   655         }
   657         final PropertyMap selfMap  = getMap();
   658         final Property    property = selfMap.findProperty(key);
   660         if (property != null) {
   661             return new FindProperty(start, this, property);
   662         }
   664         if (deep) {
   665             final ScriptObject proto = getProto();
   666             if(proto != null) {
   667                 return proto.findProperty(key, deep, stopOnNonScope, start);
   668             }
   669         }
   671         return null;
   672     }
   674     /**
   675      * Add a new property to the object.
   676      * <p>
   677      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
   678      *
   679      * @param key             Property key.
   680      * @param propertyFlags   Property flags.
   681      * @param getter          Property getter, or null if not defined
   682      * @param setter          Property setter, or null if not defined
   683      *
   684      * @return New property.
   685      */
   686     public final Property addOwnProperty(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
   687         return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter));
   688     }
   690     /**
   691      * Add a new property to the object.
   692      * <p>
   693      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
   694      *
   695      * @param key             Property key.
   696      * @param propertyFlags   Property flags.
   697      * @param value           Value of property
   698      *
   699      * @return New property.
   700      */
   701     public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) {
   702         final MethodHandle setter = addSpill(key, propertyFlags);
   704         try {
   705             setter.invokeExact((Object)this, value);
   706         } catch (final Error|RuntimeException e) {
   707             throw e;
   708         } catch (final Throwable e) {
   709             throw new RuntimeException(e);
   710         }
   712         return getMap().findProperty(key);
   713     }
   715     /**
   716      * Add a new property to the object.
   717      * <p>
   718      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
   719      *
   720      * @param newProperty property to add
   721      *
   722      * @return New property.
   723      */
   724     public final Property addOwnProperty(final Property newProperty) {
   725         PropertyMap oldMap = getMap();
   727         while (true) {
   728             final PropertyMap newMap = oldMap.addProperty(newProperty);
   730             if (!compareAndSetMap(oldMap, newMap)) {
   731                 oldMap = getMap();
   732                 final Property oldProperty = oldMap.findProperty(newProperty.getKey());
   734                 if (oldProperty != null) {
   735                     return oldProperty;
   736                 }
   737             } else {
   738                 return newProperty;
   739             }
   740         }
   741     }
   743     private void erasePropertyValue(final Property property) {
   744         // Erase the property field value with undefined. If the property is defined
   745         // by user-defined accessors, we don't want to call the setter!!
   746         if (!(property instanceof UserAccessorProperty)) {
   747             try {
   748                 // make the property value to be undefined
   749                 //TODO specproperties
   750                 property.getSetter(Object.class, getMap()).invokeExact((Object)this, (Object)UNDEFINED);
   751             } catch (final RuntimeException | Error e) {
   752                 throw e;
   753             } catch (final Throwable t) {
   754                 throw new RuntimeException(t);
   755             }
   756         }
   757     }
   759     /**
   760      * Delete a property from the object.
   761      *
   762      * @param property Property to delete.
   763      *
   764      * @return true if deleted.
   765      */
   766     public final boolean deleteOwnProperty(final Property property) {
   767         erasePropertyValue(property);
   768         PropertyMap oldMap = getMap();
   770         while (true) {
   771             final PropertyMap newMap = oldMap.deleteProperty(property);
   773             if (newMap == null) {
   774                 return false;
   775             }
   777             if (!compareAndSetMap(oldMap, newMap)) {
   778                 oldMap = getMap();
   779             } else {
   780                 // delete getter and setter function references so that we don't leak
   781                 if (property instanceof UserAccessorProperty) {
   782                     final UserAccessorProperty uc = (UserAccessorProperty) property;
   783                     setSpill(uc.getGetterSlot(), null);
   784                     setSpill(uc.getSetterSlot(), null);
   785                 }
   786                 return true;
   787             }
   788         }
   789     }
   791     /**
   792      * Modify a property in the object
   793      *
   794      * @param oldProperty    property to modify
   795      * @param propertyFlags  new property flags
   796      * @param getter         getter for {@link UserAccessorProperty}, null if not present or N/A
   797      * @param setter         setter for {@link UserAccessorProperty}, null if not present or N/A
   798      *
   799      * @return new property
   800      */
   801     public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
   802         Property newProperty;
   803         if (oldProperty instanceof UserAccessorProperty) {
   804             // re-use the slots of the old user accessor property.
   805             final UserAccessorProperty uc = (UserAccessorProperty) oldProperty;
   807             int getterSlot = uc.getGetterSlot();
   808             // clear the old getter and set the new getter
   809             setSpill(getterSlot, getter);
   810             // if getter function is null, flag the slot to be negative (less by 1)
   811             if (getter == null) {
   812                 getterSlot = -getterSlot - 1;
   813             }
   815             int setterSlot = uc.getSetterSlot();
   816             // clear the old setter and set the new setter
   817             setSpill(setterSlot, setter);
   818             // if setter function is null, flag the slot to be negative (less by 1)
   819             if (setter == null) {
   820                 setterSlot = -setterSlot - 1;
   821             }
   823             newProperty = new UserAccessorProperty(oldProperty.getKey(), propertyFlags, getterSlot, setterSlot);
   824             // if just flipping getter and setter with new functions, no need to change property or map
   825             if (oldProperty.equals(newProperty)) {
   826                 return oldProperty;
   827             }
   828         } else {
   829             // erase old property value and create new user accessor property
   830             erasePropertyValue(oldProperty);
   831             newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter);
   832         }
   834         notifyPropertyModified(this, oldProperty, newProperty);
   836         return modifyOwnProperty(oldProperty, newProperty);
   837     }
   839     /**
   840       * Modify a property in the object
   841       *
   842       * @param oldProperty    property to modify
   843       * @param propertyFlags  new property flags
   844       *
   845       * @return new property
   846       */
   847     public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags) {
   848         return modifyOwnProperty(oldProperty, oldProperty.setFlags(propertyFlags));
   849     }
   851     /**
   852      * Modify a property in the object, replacing a property with a new one
   853      *
   854      * @param oldProperty   property to replace
   855      * @param newProperty   property to replace it with
   856      *
   857      * @return new property
   858      */
   859     private Property modifyOwnProperty(final Property oldProperty, final Property newProperty) {
   860         assert newProperty.getKey().equals(oldProperty.getKey()) : "replacing property with different key";
   862         PropertyMap oldMap = getMap();
   864         while (true) {
   865             final PropertyMap newMap = oldMap.replaceProperty(oldProperty, newProperty);
   867             if (!compareAndSetMap(oldMap, newMap)) {
   868                 oldMap = getMap();
   869                 final Property oldPropertyLookup = oldMap.findProperty(oldProperty.getKey());
   871                 if (oldPropertyLookup != null && oldPropertyLookup.equals(newProperty)) {
   872                     return oldPropertyLookup;
   873                 }
   874             } else {
   875                 return newProperty;
   876             }
   877         }
   878     }
   880     /**
   881      * Update getter and setter in an object literal.
   882      *
   883      * @param key    Property key.
   884      * @param getter {@link UserAccessorProperty} defined getter, or null if none
   885      * @param setter {@link UserAccessorProperty} defined setter, or null if none
   886      */
   887     public final void setUserAccessors(final String key, final ScriptFunction getter, final ScriptFunction setter) {
   888         final Property oldProperty = getMap().findProperty(key);
   889         if (oldProperty != null) {
   890             final UserAccessorProperty newProperty = newUserAccessors(oldProperty.getKey(), oldProperty.getFlags(), getter, setter);
   891             modifyOwnProperty(oldProperty, newProperty);
   892         } else {
   893             final UserAccessorProperty newProperty = newUserAccessors(key, 0, getter, setter);
   894             addOwnProperty(newProperty);
   895         }
   896     }
   898     private static int getIntValue(final FindProperty find) {
   899         final MethodHandle getter = find.getGetter(int.class);
   900         if (getter != null) {
   901             try {
   902                 return (int)getter.invokeExact((Object)find.getGetterReceiver());
   903             } catch (final Error|RuntimeException e) {
   904                 throw e;
   905             } catch (final Throwable e) {
   906                 throw new RuntimeException(e);
   907             }
   908         }
   910         return ObjectClassGenerator.UNDEFINED_INT;
   911     }
   913     private static long getLongValue(final FindProperty find) {
   914         final MethodHandle getter = find.getGetter(long.class);
   915         if (getter != null) {
   916             try {
   917                 return (long)getter.invokeExact((Object)find.getGetterReceiver());
   918             } catch (final Error|RuntimeException e) {
   919                 throw e;
   920             } catch (final Throwable e) {
   921                 throw new RuntimeException(e);
   922             }
   923         }
   925         return ObjectClassGenerator.UNDEFINED_LONG;
   926     }
   928     private static double getDoubleValue(final FindProperty find) {
   929         final MethodHandle getter = find.getGetter(double.class);
   930         if (getter != null) {
   931             try {
   932                 return (double)getter.invokeExact((Object)find.getGetterReceiver());
   933             } catch (final Error|RuntimeException e) {
   934                 throw e;
   935             } catch (final Throwable e) {
   936                 throw new RuntimeException(e);
   937             }
   938         }
   940         return ObjectClassGenerator.UNDEFINED_DOUBLE;
   941     }
   943     /**
   944       * Get the object value of a property
   945       *
   946       * @param find {@link FindProperty} lookup result
   947       *
   948       * @return the value of the property
   949       */
   950     protected static Object getObjectValue(final FindProperty find) {
   951         final MethodHandle getter = find.getGetter(Object.class);
   952         if (getter != null) {
   953             try {
   954                 return getter.invokeExact((Object)find.getGetterReceiver());
   955             } catch (final Error|RuntimeException e) {
   956                 throw e;
   957             } catch (final Throwable e) {
   958                 throw new RuntimeException(e);
   959             }
   960         }
   962         return UNDEFINED;
   963     }
   965     /**
   966      * Return methodHandle of value function for call.
   967      *
   968      * @param find      data from find property.
   969      * @param type      method type of function.
   970      * @param bindName  null or name to bind to second argument (property not found method.)
   971      *
   972      * @return value of property as a MethodHandle or null.
   973      *
   974      */
   975     @SuppressWarnings("static-method")
   976     protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) {
   977         return getCallMethodHandle(getObjectValue(find), type, bindName);
   978     }
   980     /**
   981      * Return methodHandle of value function for call.
   982      *
   983      * @param value     value of receiver, it not a {@link ScriptFunction} this will return null.
   984      * @param type      method type of function.
   985      * @param bindName  null or name to bind to second argument (property not found method.)
   986      *
   987      * @return value of property as a MethodHandle or null.
   988      */
   989     protected static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) {
   990         return value instanceof ScriptFunction ? ((ScriptFunction)value).getCallMethodHandle(type, bindName) : null;
   991     }
   993     /**
   994      * Get value using found property.
   995      *
   996      * @param property Found property.
   997      *
   998      * @return Value of property.
   999      */
  1000     public final Object getWithProperty(final Property property) {
  1001         return getObjectValue(new FindProperty(this, this, property));
  1004     /**
  1005      * Get a property given a key
  1007      * @param key property key
  1009      * @return property for key
  1010      */
  1011     public final Property getProperty(final String key) {
  1012         return getMap().findProperty(key);
  1015     static String convertKey(final Object key) {
  1016         return (key instanceof String) ? (String)key : JSType.toString(key);
  1019     /**
  1020      * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
  1021      * Used for argument access in a vararg function using parameter name.
  1022      * Returns the argument at a given key (index)
  1024      * @param key argument index
  1026      * @return the argument at the given position, or undefined if not present
  1027      */
  1028     public Object getArgument(final int key) {
  1029         return get(key);
  1032     /**
  1033      * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
  1034      * Used for argument access in a vararg function using parameter name.
  1035      * Returns the argument at a given key (index)
  1037      * @param key   argument index
  1038      * @param value the value to write at the given index
  1039      */
  1040     public void setArgument(final int key, final Object value) {
  1041         set(key, value, getContext()._strict);
  1044     /**
  1045      * Return true if the script object context is strict
  1046      * @return true if strict context
  1047      */
  1048     public final boolean isStrictContext() {
  1049         return getContext()._strict;
  1052     /**
  1053      * Return the current context from the object's map.
  1054      * @return Current context.
  1055      */
  1056     protected final Context getContext() {
  1057         if (context == null) {
  1058             context = Context.fromClass(getClass());
  1060         return context;
  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 proto;
  1103     /**
  1104      * Set the __proto__ of an object.
  1105      * @param newProto new __proto__ to set.
  1106      */
  1107     public synchronized final void setProto(final ScriptObject newProto) {
  1108         final ScriptObject oldProto = proto;
  1109         map = map.changeProto(oldProto, newProto);
  1111         if (newProto != null) {
  1112             newProto.setIsPrototype();
  1115         proto = newProto;
  1117         if (isPrototype()) {
  1118             if (oldProto != null) {
  1119                 oldProto.removePropertyListener(this);
  1122             if (newProto != null) {
  1123                 newProto.addPropertyListener(this);
  1128     /**
  1129      * Set the __proto__ of an object with checks.
  1130      * @param newProto Prototype to set.
  1131      */
  1132     public final void setProtoCheck(final Object newProto) {
  1133         if (newProto == null || newProto instanceof ScriptObject) {
  1134             setProto((ScriptObject)newProto);
  1135         } else {
  1136             final ScriptObject global = Context.getGlobalTrusted();
  1137             final Object  newProtoObject = JSType.toScriptObject(global, newProto);
  1139             if (newProtoObject instanceof ScriptObject) {
  1140                 setProto((ScriptObject)newProtoObject);
  1141             } else {
  1142                 throw typeError(global, "cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
  1147     /**
  1148      * return a List of own keys associated with the object.
  1149      * @param all True if to include non-enumerable keys.
  1150      * @return Array of keys.
  1151      */
  1152     public String[] getOwnKeys(final boolean all) {
  1153         final List<Object> keys    = new ArrayList<>();
  1154         final PropertyMap  selfMap = this.getMap();
  1156         final ArrayData array  = getArray();
  1157         final long length      = array.length();
  1159         for (long i = 0; i < length; i = array.nextIndex(i)) {
  1160             if (array.has((int)i)) {
  1161                 keys.add(JSType.toString(i));
  1165         for (final Property property : selfMap.getProperties()) {
  1166             if (all || property.isEnumerable()) {
  1167                 keys.add(property.getKey());
  1171         return keys.toArray(new String[keys.size()]);
  1174     /**
  1175      * Check if this ScriptObject has array entries. This means that someone has
  1176      * set values with numeric keys in the object.
  1178      * Note: this can be O(n) up to the array length
  1180      * @return true if array entries exists.
  1181      */
  1182     public boolean hasArrayEntries() {
  1183         final ArrayData array = getArray();
  1184         final long length = array.length();
  1186         for (long i = 0; i < length; i++) {
  1187             if (array.has((int)i)) {
  1188                 return true;
  1192         return false;
  1195     /**
  1196      * Return the valid JavaScript type name descriptor
  1198      * @return "Object"
  1199      */
  1200     public String getClassName() {
  1201         return "Object";
  1204     /**
  1205      * {@code length} is a well known property. This is its getter.
  1206      * Note that this *may* be optimized by other classes
  1208      * @return length property value for this ScriptObject
  1209      */
  1210     public Object getLength() {
  1211         return get("length");
  1214     /**
  1215      * Stateless toString for ScriptObjects.
  1217      * @return string description of this object, e.g. {@code [object Object]}
  1218      */
  1219     public String safeToString() {
  1220         return "[object " + getClassName() + "]";
  1223     /**
  1224      * Return the default value of the object with a given preferred type hint.
  1225      * The preferred type hints are String.class for type String, Number.class
  1226      * for type Number. <p>
  1228      * A <code>hint</code> of null means "no hint".
  1230      * ECMA 8.12.8 [[DefaultValue]](hint)
  1232      * @param typeHint the preferred type hint
  1233      * @return the default value
  1234      */
  1235     public Object getDefaultValue(final Class<?> typeHint) {
  1236         // We delegate to GlobalObject, as the implementation uses dynamic call sites to invoke object's "toString" and
  1237         // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts
  1238         // are being executed in a long-running program, we move the code and their associated dynamic call sites
  1239         // (Global.TO_STRING and Global.VALUE_OF) into per-context code.
  1240         return ((GlobalObject)Context.getGlobalTrusted()).getDefaultValue(this, typeHint);
  1243     /**
  1244      * Checking whether a script object is an instance of another. Used
  1245      * in {@link ScriptFunction} for hasInstance implementation, walks
  1246      * the proto chain
  1248      * @param instance instace to check
  1249      * @return true if instance of instance
  1250      */
  1251     public boolean isInstance(final ScriptObject instance) {
  1252         return false;
  1255     /**
  1256      * Flag this ScriptObject as non extensible
  1258      * @return the object after being made non extensible
  1259      */
  1260     public ScriptObject preventExtensions() {
  1261         PropertyMap oldMap = getMap();
  1263         while (true) {
  1264             final PropertyMap newMap = getMap().preventExtensions();
  1266             if (!compareAndSetMap(oldMap, newMap)) {
  1267                 oldMap = getMap();
  1268             } else {
  1269                 return this;
  1274     /**
  1275      * Check whether if an Object (not just a ScriptObject) represents JavaScript array
  1277      * @param obj object to check
  1279      * @return true if array
  1280      */
  1281     public static boolean isArray(final Object obj) {
  1282         return (obj instanceof ScriptObject) && ((ScriptObject)obj).isArray();
  1285     /**
  1286      * Check if this ScriptObject is an array
  1287      * @return true if array
  1288      */
  1289     public final boolean isArray() {
  1290         return (flags & IS_ARRAY) != 0;
  1293     /**
  1294      * Flag this ScriptObject as being an array
  1295      */
  1296     public final void setIsArray() {
  1297         flags |= IS_ARRAY;
  1300     /**
  1301      * Check if this ScriptObject is an {@code arguments} vector
  1302      * @return true if arguments vector
  1303      */
  1304     public final boolean isArguments() {
  1305         return (flags & IS_ARGUMENTS) != 0;
  1308     /**
  1309      * Flag this ScriptObject as being an {@code arguments} vector
  1310      */
  1311     public final void setIsArguments() {
  1312         flags |= IS_ARGUMENTS;
  1315     /**
  1316      * Check if this object is a prototype
  1318      * @return {@code true} if is prototype
  1319      */
  1320     public boolean isPrototype() {
  1321         return (flags & IS_PROTOTYPE) != 0;
  1324     /**
  1325      * Flag this object as having a prototype.
  1326      */
  1327     public void setIsPrototype() {
  1328         if (proto != null && !isPrototype()) {
  1329             proto.addPropertyListener(this);
  1331         flags |= IS_PROTOTYPE;
  1334     /**
  1335      * Get the {@link ArrayData} for this ScriptObject if it is an array
  1336      * @return array data
  1337      */
  1338     public final ArrayData getArray() {
  1339         return arrayData;
  1342     /**
  1343      * Set the {@link ArrayData} for this ScriptObject if it is to be an array
  1344      * @param arrayData the array data
  1345      */
  1346     public final void setArray(final ArrayData arrayData) {
  1347         this.arrayData = arrayData;
  1350     /**
  1351      * Check if this ScriptObject is extensible
  1352      * @return true if extensible
  1353      */
  1354     public boolean isExtensible() {
  1355         return getMap().isExtensible();
  1358     /**
  1359      * ECMAScript 15.2.3.8 - seal implementation
  1360      * @return the sealed ScriptObject
  1361      */
  1362     public ScriptObject seal() {
  1363         PropertyMap oldMap = getMap();
  1365         while (true) {
  1366             final PropertyMap newMap = getMap().seal();
  1368             if (!compareAndSetMap(oldMap, newMap)) {
  1369                 oldMap = getMap();
  1370             } else {
  1371                 setArray(ArrayData.seal(getArray()));
  1372                 return this;
  1377     /**
  1378      * Check whether this ScriptObject is sealed
  1379      * @return true if sealed
  1380      */
  1381     public boolean isSealed() {
  1382         return getMap().isSealed();
  1385     /**
  1386      * ECMA 15.2.39 - freeze implementation. Freeze this ScriptObject
  1387      * @return the frozen ScriptObject
  1388      */
  1389     public ScriptObject freeze() {
  1390         PropertyMap oldMap = getMap();
  1392         while (true) {
  1393             final PropertyMap newMap = getMap().freeze();
  1395             if (!compareAndSetMap(oldMap, newMap)) {
  1396                 oldMap = getMap();
  1397             } else {
  1398                 setArray(ArrayData.freeze(getArray()));
  1399                 return this;
  1404     /**
  1405      * Check whether this ScriptObject is frozen
  1406      * @return true if frozed
  1407      */
  1408     public boolean isFrozen() {
  1409         return getMap().isFrozen();
  1413     /**
  1414      * Flag this ScriptObject as scope
  1415      */
  1416     public final void setIsScope() {
  1417         if (Context.DEBUG) {
  1418             scopeCount++;
  1420         flags |= IS_SCOPE;
  1423     /**
  1424      * Check whether this ScriptObject is scope
  1425      * @return true if scope
  1426      */
  1427     public final boolean isScope() {
  1428         return (flags & IS_SCOPE) != 0;
  1431     /**
  1432      * Clears the properties from a ScriptObject
  1433      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1434      */
  1435     public void clear() {
  1436         final boolean strict = getContext()._strict;
  1437         final Iterator<String> iter = propertyIterator();
  1438         while (iter.hasNext()) {
  1439             delete(iter.next(), strict);
  1443     /**
  1444      * Checks if a property with a given key is present in a ScriptObject
  1445      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1447      * @param key the key to check for
  1448      * @return true if a property with the given key exists, false otherwise
  1449      */
  1450     public boolean containsKey(final Object key) {
  1451         return has(key);
  1454     /**
  1455      * Checks if a property with a given value is present in a ScriptObject
  1456      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1458      * @param value value to check for
  1459      * @return true if a property with the given value exists, false otherwise
  1460      */
  1461     public boolean containsValue(final Object value) {
  1462         final Iterator<Object> iter = valueIterator();
  1463         while (iter.hasNext()) {
  1464             if (iter.next().equals(value)) {
  1465                 return true;
  1468         return false;
  1471     /**
  1472      * Returns the set of {@literal <property, value>} entries that make up this
  1473      * ScriptObject's properties
  1474      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1476      * @return an entry set of all the properties in this object
  1477      */
  1478     public Set<Map.Entry<Object, Object>> entrySet() {
  1479         final Iterator<String> iter = propertyIterator();
  1480         final Set<Map.Entry<Object, Object>> entries = new HashSet<>();
  1481         while (iter.hasNext()) {
  1482             final Object key = iter.next();
  1483             entries.add(new AbstractMap.SimpleImmutableEntry<>(key, get(key)));
  1485         return Collections.unmodifiableSet(entries);
  1488     /**
  1489      * Check whether a ScriptObject contains no properties
  1490      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1492      * @return true if object has no properties
  1493      */
  1494     public boolean isEmpty() {
  1495         return !propertyIterator().hasNext();
  1498     /**
  1499      * Return the set of keys (property names) for all properties
  1500      * in this ScriptObject
  1501      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1503      * @return keySet of this ScriptObject
  1504      */
  1505     public Set<Object> keySet() {
  1506         final Iterator<String> iter = propertyIterator();
  1507         final Set<Object> keySet = new HashSet<>();
  1508         while (iter.hasNext()) {
  1509             keySet.add(iter.next());
  1511         return Collections.unmodifiableSet(keySet);
  1514     /**
  1515      * Put a property in the ScriptObject
  1516      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1518      * @param key property key
  1519      * @param value property value
  1520      * @return oldValue if property with same key existed already
  1521      */
  1522     public Object put(final Object key, final Object value) {
  1523         final Object oldValue = get(key);
  1524         set(key, value, getContext()._strict);
  1525         return oldValue;
  1528     /**
  1529      * Put several properties in the ScriptObject given a mapping
  1530      * of their keys to their values
  1531      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1533      * @param otherMap a {@literal <key,value>} map of properties to add
  1534      */
  1535     public void putAll(final Map<?, ?> otherMap) {
  1536         final boolean strict = getContext()._strict;
  1537         for (final Map.Entry<?, ?> entry : otherMap.entrySet()) {
  1538             set(entry.getKey(), entry.getValue(), strict);
  1542     /**
  1543      * Remove a property from the ScriptObject.
  1544      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1546      * @param key the key of the property
  1547      * @return the oldValue of the removed property
  1548      */
  1549     public Object remove(final Object key) {
  1550         final Object oldValue = get(key);
  1551         delete(key, getContext()._strict);
  1552         return oldValue;
  1555     /**
  1556      * Return the size of the ScriptObject - i.e. the number of properties
  1557      * it contains
  1558      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1560      * @return number of properties in ScriptObject
  1561      */
  1562     public int size() {
  1563         int n = 0;
  1564         for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) {
  1565             n++;
  1567         return n;
  1570     /**
  1571      * Return the values of the properties in the ScriptObject
  1572      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1574      * @return collection of values for the properties in this ScriptObject
  1575      */
  1576     public Collection<Object> values() {
  1577         final List<Object>     values = new ArrayList<>(size());
  1578         final Iterator<Object> iter   = valueIterator();
  1579         while (iter.hasNext()) {
  1580             values.add(iter.next());
  1582         return Collections.unmodifiableList(values);
  1585     /**
  1586      * Lookup method that, given a CallSiteDescriptor, looks up the target
  1587      * MethodHandle and creates a GuardedInvocation
  1588      * with the appropriate guard(s).
  1590      * @param desc call site descriptor
  1591      * @param request the link request
  1593      * @return GuardedInvocation for the callsite
  1594      */
  1595     public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
  1596         final int c = desc.getNameTokenCount();
  1597         // JavaScript is "immune" to all currently defined Dynalink composite operation - getProp is the same as getElem
  1598         // is the same as getMethod as JavaScript objects have a single namespace for all three. Therefore, we don't
  1599         // care about them, and just link to whatever is the first operation.
  1600         final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
  1601         // NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself
  1602         // emits "dyn:getProp:identifier" for "<expr>.<identifier>" and "dyn:getElem" for "<expr>[<expr>]", but we are
  1603         // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the
  1604         // operation has an associated name or not.
  1605         switch (operator) {
  1606         case "getProp":
  1607         case "getElem":
  1608         case "getMethod":
  1609             return c > 2 ? findGetMethod(desc, request, operator) : findGetIndexMethod(desc, request);
  1610         case "setProp":
  1611         case "setElem":
  1612             return c > 2 ? findSetMethod(desc, request) : findSetIndexMethod(desc);
  1613         case "call":
  1614             return findCallMethod(desc, request);
  1615         case "new":
  1616             return findNewMethod(desc);
  1617         case "callMethod":
  1618             return findCallMethodMethod(desc, request);
  1619         default:
  1620             return null;
  1624     /**
  1625      * Find the appropriate New method for an invoke dynamic call.
  1627      * @param desc The invoke dynamic call site descriptor.
  1629      * @return GuardedInvocation to be invoked at call site.
  1630      */
  1631     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
  1632         return notAFunction();
  1635     /**
  1636      * Find the appropriate CALL method for an invoke dynamic call.
  1637      * This generates "not a function" always
  1639      * @param desc    the call site descriptor.
  1640      * @param request the link request
  1642      * @return GuardedInvocation to be invoed at call site.
  1643      */
  1644     protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1645         return notAFunction();
  1648     private GuardedInvocation notAFunction() {
  1649         throw typeError("not.a.function", ScriptRuntime.safeToString(this));
  1652     /**
  1653      * Find an implementation for a "dyn:callMethod" operation. Note that Nashorn internally never uses
  1654      * "dyn:callMethod", but instead always emits two call sites in bytecode, one for "dyn:getMethod", and then another
  1655      * one for "dyn:call". Explicit support for "dyn:callMethod" is provided for the benefit of potential external
  1656      * callers. The implementation itself actually folds a "dyn:getMethod" method handle into a "dyn:call" method handle.
  1658      * @param desc    the call site descriptor.
  1659      * @param request the link request
  1661      * @return GuardedInvocation to be invoked at call site.
  1662      */
  1663     protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1664         // R(P0, P1, ...)
  1665         final MethodType callType = desc.getMethodType();
  1666         // use type Object(P0) for the getter
  1667         final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0)));
  1668         final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod");
  1670         // Object(P0) => Object(P0, P1, ...)
  1671         final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount()));
  1672         // R(Object, P0, P1, ...)
  1673         final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
  1674         // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
  1675         return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
  1678     /**
  1679      * Find the appropriate GET method for an invoke dynamic call.
  1681      * @param desc     the call site descriptor
  1682      * @param request  the link request
  1683      * @param operator operator for get: getProp, getMethod, getElem etc
  1685      * @return GuardedInvocation to be invoked at call site.
  1686      */
  1687     protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
  1688         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  1689         final FindProperty find = findProperty(name, true);
  1691         MethodHandle methodHandle;
  1693         if (find == null) {
  1694             if ("getProp".equals(operator)) {
  1695                 return noSuchProperty(desc, request);
  1696             } else if ("getMethod".equals(operator)) {
  1697                 return noSuchMethod(desc, request);
  1698             } else if ("getElem".equals(operator)) {
  1699                 return createEmptyGetter(desc, name);
  1701             throw new AssertionError(); // never invoked with any other operation
  1704         if (request.isCallSiteUnstable()) {
  1705             return findMegaMorphicGetMethod(desc, name);
  1708         final Class<?> returnType = desc.getMethodType().returnType();
  1709         final Property property = find.getProperty();
  1710         methodHandle = find.getGetter(returnType);
  1712         // getMap() is fine as we have the prototype switchpoint depending on where the property was found
  1713         final MethodHandle guard = NashornGuards.getMapGuard(getMap());
  1715         if (methodHandle != null) {
  1716             assert methodHandle.type().returnType().equals(returnType);
  1717             if (find.isSelf()) {
  1718                 return new GuardedInvocation(methodHandle, ObjectClassGenerator.OBJECT_FIELDS_ONLY &&
  1719                         NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType() ? null : guard);
  1722             final ScriptObject prototype = find.getOwner();
  1724             if (!property.hasGetterFunction()) {
  1725                 methodHandle = bindTo(methodHandle, prototype);
  1727             return new GuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(proto, name), guard);
  1730         assert !NashornCallSiteDescriptor.isFastScope(desc);
  1731         return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard);
  1734     private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) {
  1735         final MethodType mhType = desc.getMethodType().insertParameterTypes(1, Object.class);
  1736         final GuardedInvocation inv = findGetIndexMethod(mhType);
  1738         return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
  1741     /**
  1742      * Find the appropriate GETINDEX method for an invoke dynamic call.
  1744      * @param desc    the call site descriptor
  1745      * @param request the link request
  1747      * @return GuardedInvocation to be invoked at call site.
  1748      */
  1749     protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1750         return findGetIndexMethod(desc.getMethodType());
  1753     /**
  1754      * Find the appropriate GETINDEX method for an invoke dynamic call.
  1756      * @param callType the call site method type
  1757      * @return GuardedInvocation to be invoked at call site.
  1758      */
  1759     private static GuardedInvocation findGetIndexMethod(final MethodType callType) {
  1760         final Class<?> returnClass = callType.returnType();
  1761         final Class<?> keyClass    = callType.parameterType(1);
  1763         String name = "get";
  1764         if (returnClass.isPrimitive()) {
  1765             //turn e.g. get with a double into getDouble
  1766             final String returnTypeName = returnClass.getName();
  1767             name += Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
  1770         return new GuardedInvocation(findOwnMH(name, returnClass, keyClass), getScriptObjectGuard(callType));
  1773     private static MethodHandle getScriptObjectGuard(final MethodType type) {
  1774         return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard();
  1777     /**
  1778      * Find the appropriate SET method for an invoke dynamic call.
  1780      * @param desc    the call site descriptor
  1781      * @param request the link request
  1783      * @return GuardedInvocation to be invoked at call site.
  1784      */
  1785     protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1786         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  1787         if(request.isCallSiteUnstable()) {
  1788             return findMegaMorphicSetMethod(desc, name);
  1791         final boolean scope = isScope();
  1792         /*
  1793          * If doing property set on a scope object, we should stop proto search on the first
  1794          * non-scope object. Without this, for example, when assigning "toString" on global scope,
  1795          * we'll end up assigning it on it's proto - which is Object.prototype.toString !!
  1797          * toString = function() { print("global toString"); } // don't affect Object.prototype.toString
  1798          */
  1799         FindProperty find = findProperty(name, true, scope, this);
  1800         // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors.
  1801         if (!scope && find != null && find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
  1802             // We should still check if inherited data property is not writable
  1803             if (isExtensible() && !find.getProperty().isWritable()) {
  1804                 return createEmptySetMethod(desc, "property.not.writable", false);
  1806             // Otherwise, forget the found property
  1807             find = null;
  1810         if (find != null) {
  1811             if(!find.getProperty().isWritable()) {
  1812                 // Existing, non-writable property
  1813                 return createEmptySetMethod(desc, "property.not.writable", true);
  1815         } else if (!isExtensible()) {
  1816             // Non-existing property on a non-extensible object
  1817             return createEmptySetMethod(desc, "object.non.extensible", false);
  1820         return new SetMethodCreator(this, find, desc).createGuardedInvocation();
  1823     private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, String strictErrorMessage, boolean canBeFastScope) {
  1824         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  1825         if (NashornCallSiteDescriptor.isStrict(desc)) {
  1826                throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString((this)));
  1828            assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
  1829            final PropertyMap myMap = getMap();
  1830            return new GuardedInvocation(Lookup.EMPTY_SETTER, myMap.getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(myMap));
  1833     @SuppressWarnings("unused")
  1834     private static void setField(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final MethodHandle setter, final Object self, final Object value) throws Throwable {
  1835         final ScriptObject obj = (ScriptObject)self;
  1836         final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
  1837         if (!obj.isExtensible()) {
  1838             throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
  1839         } else if (obj.compareAndSetMap(oldMap, newMap)) {
  1840             setter.invokeExact(self, value);
  1841         } else {
  1842             obj.set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
  1846     @SuppressWarnings("unused")
  1847     private static void setSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
  1848         final ScriptObject obj = (ScriptObject)self;
  1849         if (obj.trySetSpill(desc, oldMap, newMap, value)) {
  1850             obj.spill[index] = value;
  1854     private boolean trySetSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final Object value) {
  1855         final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
  1856         if (!isExtensible() && isStrict) {
  1857             throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(this));
  1858         } else if (compareAndSetMap(oldMap, newMap)) {
  1859             return true;
  1860         } else {
  1861             set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
  1862             return false;
  1866     @SuppressWarnings("unused")
  1867     private static void setSpillWithNew(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
  1868         final ScriptObject obj      = (ScriptObject)self;
  1869         final boolean      isStrict = NashornCallSiteDescriptor.isStrict(desc);
  1871         if (!obj.isExtensible()) {
  1872             if (isStrict) {
  1873                 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
  1875         } else if (obj.compareAndSetMap(oldMap, newMap)) {
  1876             obj.spill = new Object[SPILL_RATE];
  1877             obj.spill[index] = value;
  1878         } else {
  1879             obj.set(desc.getNameToken(2), value, isStrict);
  1883     @SuppressWarnings("unused")
  1884     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) {
  1885         final ScriptObject obj      = (ScriptObject)self;
  1886         final boolean      isStrict = NashornCallSiteDescriptor.isStrict(desc);
  1888         if (!obj.isExtensible()) {
  1889             if (isStrict) {
  1890                 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
  1892         } else if (obj.compareAndSetMap(oldMap, newMap)) {
  1893             final int oldLength = obj.spill.length;
  1894             final Object[] newSpill = new Object[newLength];
  1895             System.arraycopy(obj.spill, 0, newSpill, 0, oldLength);
  1896             obj.spill = newSpill;
  1897             obj.spill[index] = value;
  1898         } else {
  1899             obj.set(desc.getNameToken(2), value, isStrict);
  1903     private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
  1904         final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
  1905         final GuardedInvocation inv = findSetIndexMethod(type, NashornCallSiteDescriptor.isStrict(desc));
  1906         return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
  1909     private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc) { // array, index, value
  1910         return findSetIndexMethod(desc.getMethodType(), NashornCallSiteDescriptor.isStrict(desc));
  1913     /**
  1914      * Find the appropriate SETINDEX method for an invoke dynamic call.
  1916      * @param callType the method type at the call site
  1917      * @param isStrict are we in strict mode?
  1919      * @return GuardedInvocation to be invoked at call site.
  1920      */
  1921     private static GuardedInvocation findSetIndexMethod(final MethodType callType, final boolean isStrict) {
  1922         assert callType.parameterCount() == 3;
  1924         final Class<?>   keyClass   = callType.parameterType(1);
  1925         final Class<?>   valueClass = callType.parameterType(2);
  1927         MethodHandle methodHandle = findOwnMH("set", void.class, keyClass, valueClass, boolean.class);
  1928         methodHandle = MH.insertArguments(methodHandle, 3, isStrict);
  1930         return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType));
  1933     /**
  1934      * Fall back if a function property is not found.
  1935      * @param desc The call site descriptor
  1936      * @param request the link request
  1937      * @return GuardedInvocation to be invoked at call site.
  1938      */
  1939     public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1940         final String       name      = desc.getNameToken(2);
  1941         final FindProperty find      = findProperty(NO_SUCH_METHOD_NAME, true);
  1942         final boolean      scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc);
  1944         if (find == null) {
  1945             return noSuchProperty(desc, request);
  1948         final ScriptFunction func = (ScriptFunction)getObjectValue(find);
  1949         final Object thiz = scopeCall && func.isStrict() ? ScriptRuntime.UNDEFINED : this;
  1950         // TODO: It'd be awesome if we could bind "name" without binding "this".
  1951         return new GuardedInvocation(MH.dropArguments(MH.constant(ScriptFunction.class,
  1952                 func.makeBoundFunction(thiz, new Object[] { name })), 0, Object.class),
  1953                 null, NashornGuards.getMapGuard(getMap()));
  1956     /**
  1957      * Fall back if a property is not found.
  1958      * @param desc the call site descriptor.
  1959      * @param request the link request
  1960      * @return GuardedInvocation to be invoked at call site.
  1961      */
  1962     public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
  1963         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  1964         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
  1965         final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
  1967         if (find != null) {
  1968             final ScriptFunction func = (ScriptFunction)getObjectValue(find);
  1969             MethodHandle methodHandle = getCallMethodHandle(func, desc.getMethodType(), name);
  1971             if (methodHandle != null) {
  1972                 if (scopeAccess && func.isStrict()) {
  1973                     methodHandle = bindTo(methodHandle, UNDEFINED);
  1975                 return new GuardedInvocation(methodHandle,
  1976                         find.isInherited()? getMap().getProtoGetSwitchPoint(proto, NO_SUCH_PROPERTY_NAME) : null,
  1977                         getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func));
  1981         if (scopeAccess) {
  1982             throw referenceError("not.defined", name);
  1985         return createEmptyGetter(desc, name);
  1987     /**
  1988      * Invoke fall back if a property is not found.
  1989      * @param name Name of property.
  1990      * @return Result from call.
  1991      */
  1992     private Object invokeNoSuchProperty(final String name) {
  1993         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
  1995         if (find != null) {
  1996             final Object func = getObjectValue(find);
  1998             if (func instanceof ScriptFunction) {
  1999                 return ScriptRuntime.apply((ScriptFunction)func, this, name);
  2003         return UNDEFINED;
  2006     private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
  2007         return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(getMap()));
  2010     private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
  2011         protected T[] values;
  2012         protected final ScriptObject object;
  2013         private int index;
  2015         ScriptObjectIterator(final ScriptObject object) {
  2016             this.object = object;
  2019         protected abstract void init();
  2021         @Override
  2022         public boolean hasNext() {
  2023             if (values == null) {
  2024                 init();
  2026             return index < values.length;
  2029         @Override
  2030         public T next() {
  2031             if (values == null) {
  2032                 init();
  2034             return values[index++];
  2037         @Override
  2038         public void remove() {
  2039             throw new UnsupportedOperationException();
  2043     private static class KeyIterator extends ScriptObjectIterator<String> {
  2044         KeyIterator(final ScriptObject object) {
  2045             super(object);
  2048         @Override
  2049         protected void init() {
  2050             final Set<String> keys = new LinkedHashSet<>();
  2051             for (ScriptObject self = object; self != null; self = self.getProto()) {
  2052                 keys.addAll(Arrays.asList(self.getOwnKeys(false)));
  2054             this.values = keys.toArray(new String[keys.size()]);
  2058     private static class ValueIterator extends ScriptObjectIterator<Object> {
  2059         ValueIterator(final ScriptObject object) {
  2060             super(object);
  2063         @Override
  2064         protected void init() {
  2065             final ArrayList<Object> valueList = new ArrayList<>();
  2066             for (ScriptObject self = object; self != null; self = self.getProto()) {
  2067                 for (final String key : self.getOwnKeys(false)) {
  2068                     valueList.add(self.get(key));
  2071             this.values = valueList.toArray(new Object[valueList.size()]);
  2075     /**
  2076      * Add a spill property for the given key.
  2077      * @param key           Property key.
  2078      * @param propertyFlags Property flags.
  2079      * @return Added property.
  2080      */
  2081     private Property addSpillProperty(final String key, final int propertyFlags) {
  2082         int fieldCount   = getMap().getFieldCount();
  2083         int fieldMaximum = getMap().getFieldMaximum();
  2084         Property property;
  2086         if (fieldCount < fieldMaximum) {
  2087             property = new AccessorProperty(key, propertyFlags & ~Property.IS_SPILL, getClass(), fieldCount);
  2088             notifyPropertyAdded(this, property);
  2089             property = addOwnProperty(property);
  2090         } else {
  2091             int i = getMap().getSpillLength();
  2092             MethodHandle getter = MH.arrayElementGetter(Object[].class);
  2093             MethodHandle setter = MH.arrayElementSetter(Object[].class);
  2094             getter = MH.asType(MH.insertArguments(getter, 1, i), Lookup.GET_OBJECT_TYPE);
  2095             setter = MH.asType(MH.insertArguments(setter, 1, i), Lookup.SET_OBJECT_TYPE);
  2096             property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i, getter, setter);
  2097             notifyPropertyAdded(this, property);
  2098             property = addOwnProperty(property);
  2099             i = property.getSlot();
  2101             final int newLength = (i + SPILL_RATE) / SPILL_RATE * SPILL_RATE;
  2103             if (spill == null || newLength > spill.length) {
  2104                 final Object[] newSpill = new Object[newLength];
  2106                 if (spill != null) {
  2107                     System.arraycopy(spill, 0, newSpill, 0, spill.length);
  2110                 spill = newSpill;
  2114         return property;
  2118     /**
  2119      * Add a spill entry for the given key.
  2120      * @param key           Property key.
  2121      * @param propertyFlags Property flags.
  2122      * @return Setter method handle.
  2123      */
  2124     private MethodHandle addSpill(final String key, final int propertyFlags) {
  2125         final Property spillProperty = addSpillProperty(key, propertyFlags);
  2126         final Class<?> type = Object.class;
  2127         return spillProperty.getSetter(type, getMap()); //TODO specfields
  2130     MethodHandle addSpill(final String key) {
  2131         return addSpill(key, 0);
  2134     /**
  2135      * Make sure arguments are paired correctly, with respect to more parameters than declared,
  2136      * fewer parameters than declared and other things that JavaScript allows. This might involve
  2137      * creating collectors.
  2139      * @param methodHandle method handle for invoke
  2140      * @param callType     type of the call
  2142      * @return method handle with adjusted arguments
  2143      */
  2144     protected static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType) {
  2145         return pairArguments(methodHandle, callType, null);
  2148     /**
  2149      * Make sure arguments are paired correctly, with respect to more parameters than declared,
  2150      * fewer parameters than declared and other things that JavaScript allows. This might involve
  2151      * creating collectors.
  2153      * Make sure arguments are paired correctly.
  2154      * @param methodHandle MethodHandle to adjust.
  2155      * @param callType     MethodType of the call site.
  2156      * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the
  2157      * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a
  2158      * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites
  2159      * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters.
  2161      * @return method handle with adjusted arguments
  2162      */
  2163     public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) {
  2165         final MethodType methodType = methodHandle.type();
  2166         if (methodType.equals(callType)) {
  2167             return methodHandle;
  2170         final int parameterCount = methodType.parameterCount();
  2171         final int callCount      = callType.parameterCount();
  2173         final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
  2174         final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : (callCount > 0 &&
  2175                 callType.parameterType(callCount - 1).isArray());
  2177         if (callCount < parameterCount) {
  2178             final int      missingArgs = parameterCount - callCount;
  2179             final Object[] fillers     = new Object[missingArgs];
  2181             Arrays.fill(fillers, UNDEFINED);
  2183             if (isCalleeVarArg) {
  2184                 fillers[missingArgs - 1] = new Object[0];
  2187             return MH.insertArguments(
  2188                 methodHandle,
  2189                 parameterCount - missingArgs,
  2190                 fillers);
  2193         if (isCalleeVarArg) {
  2194             return isCallerVarArg ?
  2195                 methodHandle :
  2196                 MH.asCollector(methodHandle, Object[].class, callCount - parameterCount + 1);
  2199         if (isCallerVarArg) {
  2200             final int spreadArgs = parameterCount - callCount + 1;
  2201             return MH.filterArguments(
  2202                 MH.asSpreader(
  2203                     methodHandle,
  2204                     Object[].class,
  2205                     spreadArgs),
  2206                 callCount - 1,
  2207                 MH.insertArguments(
  2208                     TRUNCATINGFILTER,
  2209                     0,
  2210                     spreadArgs)
  2211                 );
  2214         if (callCount > parameterCount) {
  2215             final int discardedArgs = callCount - parameterCount;
  2217             final Class<?>[] discards = new Class<?>[discardedArgs];
  2218             Arrays.fill(discards, Object.class);
  2220             return MH.dropArguments(methodHandle, callCount - discardedArgs, discards);
  2223         return methodHandle;
  2226     @SuppressWarnings("unused")
  2227     private static Object[] truncatingFilter(final int n, final Object[] array) {
  2228         final int length = array == null ? 0 : array.length;
  2229         if (n == length) {
  2230             return array == null ? new Object[0] : array;
  2233         final Object[] newArray = new Object[n];
  2235         if (array != null) {
  2236             for (int i = 0; i < n && i < length; i++) {
  2237                 newArray[i] = array[i];
  2241         if (length < n) {
  2242             final Object fill = UNDEFINED;
  2244             for (int i = length; i < n; i++) {
  2245                 newArray[i] = fill;
  2249         return newArray;
  2252     /**
  2253       * Numeric length setter for length property
  2255       * @param newLength new length to set
  2256       */
  2257     public final void setLength(final long newLength) {
  2258        final long arrayLength = getArray().length();
  2259        if (newLength == arrayLength) {
  2260            return;
  2263        final boolean isStrict = getContext()._strict;
  2265        if (newLength > arrayLength) {
  2266            setArray(getArray().ensure(newLength - 1));
  2267             if (getArray().canDelete(arrayLength, (newLength - 1), isStrict)) {
  2268                setArray(getArray().delete(arrayLength, (newLength - 1)));
  2270            return;
  2273        if (newLength < arrayLength) {
  2274            setArray(getArray().shrink(newLength));
  2275            getArray().setLength(newLength);
  2279     private int getInt(final int index, final String key) {
  2280         if (isValidArrayIndex(index)) {
  2281              for (ScriptObject object = this; ; ) {
  2282                 final FindProperty find = object.findProperty(key, false, false, this);
  2284                 if (find != null) {
  2285                     return getIntValue(find);
  2288                 if ((object = object.getProto()) == null) {
  2289                     break;
  2292                 final ArrayData array = object.getArray();
  2294                 if (array.has(index)) {
  2295                     return array.getInt(index);
  2298         } else {
  2299             final FindProperty find = findProperty(key, true);
  2301             if (find != null) {
  2302                 return getIntValue(find);
  2306         return JSType.toInt32(invokeNoSuchProperty(key));
  2309     @Override
  2310     public int getInt(final Object key) {
  2311         final int index = getArrayIndexNoThrow(key);
  2312         final ArrayData array = getArray();
  2314         if (array.has(index)) {
  2315             return array.getInt(index);
  2318         return getInt(index, convertKey(key));
  2321     @Override
  2322     public int getInt(final double key) {
  2323         final int index = getArrayIndexNoThrow(key);
  2324         final ArrayData array = getArray();
  2326         if (array.has(index)) {
  2327             return array.getInt(index);
  2330         return getInt(index, convertKey(key));
  2333     @Override
  2334     public int getInt(final long key) {
  2335         final int index = getArrayIndexNoThrow(key);
  2336         final ArrayData array = getArray();
  2338         if (array.has(index)) {
  2339             return array.getInt(index);
  2342         return getInt(index, convertKey(key));
  2345     @Override
  2346     public int getInt(final int key) {
  2347         final ArrayData array = getArray();
  2349         if (array.has(key)) {
  2350             return array.getInt(key);
  2353         return getInt(key, convertKey(key));
  2356     private long getLong(final int index, final String key) {
  2357         if (isValidArrayIndex(index)) {
  2358             for (ScriptObject object = this; ; ) {
  2359                 final FindProperty find = object.findProperty(key, false, false, this);
  2361                 if (find != null) {
  2362                     return getLongValue(find);
  2365                 if ((object = object.getProto()) == null) {
  2366                     break;
  2369                 final ArrayData array = object.getArray();
  2371                 if (array.has(index)) {
  2372                     return array.getLong(index);
  2375         } else {
  2376             final FindProperty find = findProperty(key, true);
  2378             if (find != null) {
  2379                 return getLongValue(find);
  2383         return JSType.toLong(invokeNoSuchProperty(key));
  2386     @Override
  2387     public long getLong(final Object key) {
  2388         final int index = getArrayIndexNoThrow(key);
  2389         final ArrayData array = getArray();
  2391         if (array.has(index)) {
  2392             return array.getLong(index);
  2395         return getLong(index, convertKey(key));
  2398     @Override
  2399     public long getLong(final double key) {
  2400         final int index = getArrayIndexNoThrow(key);
  2401         final ArrayData array = getArray();
  2403         if (array.has(index)) {
  2404             return array.getLong(index);
  2407         return getLong(index, convertKey(key));
  2410     @Override
  2411     public long getLong(final long key) {
  2412         final int index = getArrayIndexNoThrow(key);
  2413         final ArrayData array = getArray();
  2415         if (array.has(index)) {
  2416             return array.getLong(index);
  2419         return getLong(index, convertKey(key));
  2422     @Override
  2423     public long getLong(final int key) {
  2424         final ArrayData array = getArray();
  2426         if (array.has(key)) {
  2427             return array.getLong(key);
  2430         return getLong(key, convertKey(key));
  2433     private double getDouble(final int index, final String key) {
  2434         if (isValidArrayIndex(index)) {
  2435             for (ScriptObject object = this; ; ) {
  2436                 final FindProperty find = object.findProperty(key, false, false, this);
  2438                 if (find != null) {
  2439                     return getDoubleValue(find);
  2442                 if ((object = object.getProto()) == null) {
  2443                     break;
  2446                 final ArrayData array = object.getArray();
  2448                 if (array.has(index)) {
  2449                     return array.getDouble(index);
  2452         } else {
  2453             final FindProperty find = findProperty(key, true);
  2455             if (find != null) {
  2456                 return getDoubleValue(find);
  2460         return JSType.toNumber(invokeNoSuchProperty(key));
  2463     @Override
  2464     public double getDouble(final Object key) {
  2465         final int index = getArrayIndexNoThrow(key);
  2466         final ArrayData array = getArray();
  2468         if (array.has(index)) {
  2469             return array.getDouble(index);
  2472         return getDouble(index, convertKey(key));
  2475     @Override
  2476     public double getDouble(final double key) {
  2477         final int index = getArrayIndexNoThrow(key);
  2478         final ArrayData array = getArray();
  2480         if (array.has(index)) {
  2481             return array.getDouble(index);
  2484         return getDouble(index, convertKey(key));
  2487     @Override
  2488     public double getDouble(final long key) {
  2489         final int index = getArrayIndexNoThrow(key);
  2490         final ArrayData array = getArray();
  2492         if (array.has(index)) {
  2493             return array.getDouble(index);
  2496         return getDouble(index, convertKey(key));
  2499     @Override
  2500     public double getDouble(final int key) {
  2501         final ArrayData array = getArray();
  2503         if (array.has(key)) {
  2504             return array.getDouble(key);
  2507         return getDouble(key, convertKey(key));
  2510     private Object get(final int index, final String key) {
  2511         if (isValidArrayIndex(index)) {
  2512             for (ScriptObject object = this; ; ) {
  2513                 final FindProperty find = object.findProperty(key, false, false, this);
  2515                 if (find != null) {
  2516                     return getObjectValue(find);
  2519                 if ((object = object.getProto()) == null) {
  2520                     break;
  2523                 final ArrayData array = object.getArray();
  2525                 if (array.has(index)) {
  2526                     return array.getObject(index);
  2529         } else {
  2530             final FindProperty find = findProperty(key, true);
  2532             if (find != null) {
  2533                 return getObjectValue(find);
  2537         return invokeNoSuchProperty(key);
  2540     @Override
  2541     public Object get(final Object key) {
  2542         final int index = getArrayIndexNoThrow(key);
  2543         final ArrayData array = getArray();
  2545         if (array.has(index)) {
  2546             return array.getObject(index);
  2549         return get(index, convertKey(key));
  2552     @Override
  2553     public Object get(final double key) {
  2554         final int index = getArrayIndexNoThrow(key);
  2555         final ArrayData array = getArray();
  2557         if (array.has(index)) {
  2558             return array.getObject(index);
  2561         return get(index, convertKey(key));
  2564     @Override
  2565     public Object get(final long key) {
  2566         final int index = getArrayIndexNoThrow(key);
  2567         final ArrayData array = getArray();
  2569         if (array.has(index)) {
  2570             return array.getObject(index);
  2573         return get(index, convertKey(key));
  2576     @Override
  2577     public Object get(final int key) {
  2578         final ArrayData array = getArray();
  2580         if (array.has(key)) {
  2581             return array.getObject(key);
  2584         return get(key, convertKey(key));
  2587     /**
  2588      * Handle when an array doesn't have a slot - possibly grow and/or convert array.
  2590      * @param index  key as index
  2591      * @param value  element value
  2592      * @param strict are we in strict mode
  2593      */
  2594     private void doesNotHave(final int index, final Object value, final boolean strict) {
  2595         final long oldLength = getArray().length();
  2596         final long longIndex = index & JSType.MAX_UINT;
  2598         if (!getArray().has(index)) {
  2599             final String key = convertKey(longIndex);
  2600             final FindProperty find = findProperty(key, true);
  2602             if (find != null) {
  2603                 setObject(find, strict, key, value);
  2604                 return;
  2608         if (longIndex >= oldLength) {
  2609             if (!isExtensible()) {
  2610                 if (strict) {
  2611                     throw typeError("object.non.extensible", JSType.toString(index), ScriptRuntime.safeToString(this));
  2613                 return;
  2615             setArray(getArray().ensure(longIndex));
  2618         if (value instanceof Integer) {
  2619             setArray(getArray().set(index, (int)value, strict));
  2620         } else if (value instanceof Long) {
  2621             setArray(getArray().set(index, (long)value, strict));
  2622         } else if (value instanceof Double) {
  2623             setArray(getArray().set(index, (double)value, strict));
  2624         } else {
  2625             setArray(getArray().set(index, value, strict));
  2628         if (longIndex > oldLength) {
  2629             ArrayData array = getArray();
  2631             if (array.canDelete(oldLength, (longIndex - 1), strict)) {
  2632                 array = array.delete(oldLength, (longIndex - 1));
  2635             setArray(array);
  2639     /**
  2640      * This is the most generic of all Object setters. Most of the others use this in some form.
  2641      * TODO: should be further specialized
  2643      * @param find    found property
  2644      * @param strict  are we in strict mode
  2645      * @param key     property key
  2646      * @param value   property value
  2647      */
  2648     public final void setObject(final FindProperty find, final boolean strict, final String key, final Object value) {
  2649         FindProperty f = find;
  2651         if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) {
  2652             f = null;
  2655         if (f != null) {
  2656             if (!f.getProperty().isWritable()) {
  2657                 if (strict) {
  2658                     throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this));
  2661                 return;
  2664             try {
  2665                 final MethodHandle setter = f.getSetter(Object.class, strict); //TODO specfields
  2666                 setter.invokeExact((Object)f.getSetterReceiver(), value);
  2667             } catch (final Error|RuntimeException e) {
  2668                 throw e;
  2669             } catch (final Throwable e) {
  2670                 throw new RuntimeException(e);
  2672         } else if (!isExtensible()) {
  2673             if (strict) {
  2674                 throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
  2676         } else {
  2677             spill(key, value);
  2681     private void spill(final String key, final Object value) {
  2682         try {
  2683             addSpill(key).invokeExact((Object)this, value);
  2684         } catch (final Error|RuntimeException e) {
  2685             throw e;
  2686         } catch (final Throwable e) {
  2687             throw new RuntimeException(e);
  2692     @Override
  2693     public void set(final Object key, final int value, final boolean strict) {
  2694         final int index = getArrayIndexNoThrow(key);
  2696         if (isValidArrayIndex(index)) {
  2697             if (getArray().has(index)) {
  2698                 setArray(getArray().set(index, value, strict));
  2699             } else {
  2700                 doesNotHave(index, value, strict);
  2703             return;
  2706         set(key, JSType.toObject(value), strict);
  2709     @Override
  2710     public void set(final Object key, final long value, final boolean strict) {
  2711         final int index = getArrayIndexNoThrow(key);
  2713         if (isValidArrayIndex(index)) {
  2714             if (getArray().has(index)) {
  2715                 setArray(getArray().set(index, value, strict));
  2716             } else {
  2717                 doesNotHave(index, value, strict);
  2720             return;
  2723         set(key, JSType.toObject(value), strict);
  2726     @Override
  2727     public void set(final Object key, final double value, final boolean strict) {
  2728         final int index = getArrayIndexNoThrow(key);
  2730         if (isValidArrayIndex(index)) {
  2731             if (getArray().has(index)) {
  2732                 setArray(getArray().set(index, value, strict));
  2733             } else {
  2734                 doesNotHave(index, value, strict);
  2737             return;
  2740         set(key, JSType.toObject(value), strict);
  2743     @Override
  2744     public void set(final Object key, final Object value, final boolean strict) {
  2745         final int index = getArrayIndexNoThrow(key);
  2747         if (isValidArrayIndex(index)) {
  2748             if (getArray().has(index)) {
  2749                 setArray(getArray().set(index, value, strict));
  2750             } else {
  2751                 doesNotHave(index, value, strict);
  2754             return;
  2757         final String       propName = convertKey(key);
  2758         final FindProperty find     = findProperty(propName, true);
  2760         setObject(find, strict, propName, value);
  2763     @Override
  2764     public void set(final double key, final int value, final boolean strict) {
  2765         final int index = getArrayIndexNoThrow(key);
  2767         if (isValidArrayIndex(index)) {
  2768             if (getArray().has(index)) {
  2769                 setArray(getArray().set(index, value, strict));
  2770             } else {
  2771                 doesNotHave(index, value, strict);
  2774             return;
  2777         set(JSType.toObject(key), JSType.toObject(value), strict);
  2780     @Override
  2781     public void set(final double key, final long value, final boolean strict) {
  2782         final int index = getArrayIndexNoThrow(key);
  2784         if (isValidArrayIndex(index)) {
  2785             if (getArray().has(index)) {
  2786                 setArray(getArray().set(index, value, strict));
  2787             } else {
  2788                 doesNotHave(index, value, strict);
  2791             return;
  2794         set(JSType.toObject(key), JSType.toObject(value), strict);
  2797     @Override
  2798     public void set(final double key, final double value, final boolean strict) {
  2799         final int index = getArrayIndexNoThrow(key);
  2801         if (isValidArrayIndex(index)) {
  2802             if (getArray().has(index)) {
  2803                 setArray(getArray().set(index, value, strict));
  2804             } else {
  2805                 doesNotHave(index, value, strict);
  2808             return;
  2811         set(JSType.toObject(key), JSType.toObject(value), strict);
  2814     @Override
  2815     public void set(final double key, final Object value, final boolean strict) {
  2816         final int index = getArrayIndexNoThrow(key);
  2818         if (isValidArrayIndex(index)) {
  2819             if (getArray().has(index)) {
  2820                 setArray(getArray().set(index, value, strict));
  2821             } else {
  2822                 doesNotHave(index, value, strict);
  2825             return;
  2828         set(JSType.toObject(key), value, strict);
  2831     @Override
  2832     public void set(final long key, final int value, final boolean strict) {
  2833         final int index = getArrayIndexNoThrow(key);
  2835         if (isValidArrayIndex(index)) {
  2836             if (getArray().has(index)) {
  2837                 setArray(getArray().set(index, value, strict));
  2838             } else {
  2839                 doesNotHave(index, value, strict);
  2842             return;
  2845         set(JSType.toObject(key), JSType.toObject(value), strict);
  2848     @Override
  2849     public void set(final long key, final long value, final boolean strict) {
  2850         final int index = getArrayIndexNoThrow(key);
  2852         if (isValidArrayIndex(index)) {
  2853             if (getArray().has(index)) {
  2854                 setArray(getArray().set(index, value, strict));
  2855             } else {
  2856                 doesNotHave(index, value, strict);
  2859             return;
  2862         set(JSType.toObject(key), JSType.toObject(value), strict);
  2865     @Override
  2866     public void set(final long key, final double value, final boolean strict) {
  2867         final int index = getArrayIndexNoThrow(key);
  2869         if (isValidArrayIndex(index)) {
  2870             if (getArray().has(index)) {
  2871                 setArray(getArray().set(index, value, strict));
  2872             } else {
  2873                 doesNotHave(index, value, strict);
  2876             return;
  2879         set(JSType.toObject(key), JSType.toObject(value), strict);
  2882     @Override
  2883     public void set(final long key, final Object value, final boolean strict) {
  2884         final int index = getArrayIndexNoThrow(key);
  2886         if (isValidArrayIndex(index)) {
  2887             if (getArray().has(index)) {
  2888                 setArray(getArray().set(index, value, strict));
  2889             } else {
  2890                 doesNotHave(index, value, strict);
  2893             return;
  2896         set(JSType.toObject(key), value, strict);
  2899     @Override
  2900     public void set(final int key, final int value, final boolean strict) {
  2901         final int index = getArrayIndexNoThrow(key);
  2903         if (isValidArrayIndex(index)) {
  2904             if (getArray().has(index)) {
  2905                 setArray(getArray().set(index, value, strict));
  2906             } else {
  2907                 doesNotHave(index, value, strict);
  2910             return;
  2913         set(JSType.toObject(key), JSType.toObject(value), strict);
  2916     @Override
  2917     public void set(final int key, final long value, final boolean strict) {
  2918         final int index = getArrayIndexNoThrow(key);
  2920         if (isValidArrayIndex(index)) {
  2921             if (getArray().has(index)) {
  2922                 setArray(getArray().set(index, value, strict));
  2923             } else {
  2924                 doesNotHave(index, value, strict);
  2927             return;
  2930         set(JSType.toObject(key), JSType.toObject(value), strict);
  2933     @Override
  2934     public void set(final int key, final double value, final boolean strict) {
  2935         final int index = getArrayIndexNoThrow(key);
  2937         if (isValidArrayIndex(index)) {
  2938             if (getArray().has(index)) {
  2939                 setArray(getArray().set(index, value, strict));
  2940             } else {
  2941                 doesNotHave(index, value, strict);
  2944             return;
  2947         set(JSType.toObject(key), JSType.toObject(value), strict);
  2950     @Override
  2951     public void set(final int key, final Object value, final boolean strict) {
  2952         final int index = getArrayIndexNoThrow(key);
  2954         if (isValidArrayIndex(index)) {
  2955             if (getArray().has(index)) {
  2956                 setArray(getArray().set(index, value, strict));
  2957             } else {
  2958                 doesNotHave(index, value, strict);
  2961             return;
  2964         set(JSType.toObject(key), value, strict);
  2967     @Override
  2968     public boolean has(final Object key) {
  2969         final int index = getArrayIndexNoThrow(key);
  2971         if (isValidArrayIndex(index)) {
  2972             for (ScriptObject self = this; self != null; self = self.getProto()) {
  2973                 if (self.getArray().has(index)) {
  2974                     return true;
  2979         final FindProperty find = findProperty(convertKey(key), true);
  2981         return find != null;
  2984     @Override
  2985     public boolean has(final double key) {
  2986         final int index = getArrayIndexNoThrow(key);
  2988         if (isValidArrayIndex(index)) {
  2989             for (ScriptObject self = this; self != null; self = self.getProto()) {
  2990                 if (self.getArray().has(index)) {
  2991                     return true;
  2996         final FindProperty find = findProperty(convertKey(key), true);
  2998         return find != null;
  3001     @Override
  3002     public boolean has(final long key) {
  3003         final int index = getArrayIndexNoThrow(key);
  3005         if (isValidArrayIndex(index)) {
  3006             for (ScriptObject self = this; self != null; self = self.getProto()) {
  3007                 if (self.getArray().has(index)) {
  3008                     return true;
  3013         final FindProperty find = findProperty(convertKey(key), true);
  3015         return find != null;
  3018     @Override
  3019     public boolean has(final int key) {
  3020         final int index = getArrayIndexNoThrow(key);
  3022         if (isValidArrayIndex(index)) {
  3023             for (ScriptObject self = this; self != null; self = self.getProto()) {
  3024                 if (self.getArray().has(index)) {
  3025                     return true;
  3030         final FindProperty find = findProperty(convertKey(key), true);
  3032         return find != null;
  3035     @Override
  3036     public boolean hasOwnProperty(final Object key) {
  3037         final int index = getArrayIndexNoThrow(key);
  3039         if (getArray().has(index)) {
  3040             return true;
  3043         final FindProperty find = findProperty(convertKey(key), false);
  3045         return find != null;
  3048     @Override
  3049     public boolean hasOwnProperty(final int key) {
  3050         final int index = getArrayIndexNoThrow(key);
  3052         if (getArray().has(index)) {
  3053             return true;
  3056         final FindProperty find = findProperty(convertKey(key), false);
  3058         return find != null;
  3061     @Override
  3062     public boolean hasOwnProperty(final long key) {
  3063         final int index = getArrayIndexNoThrow(key);
  3065         if (getArray().has(index)) {
  3066             return true;
  3069         final FindProperty find = findProperty(convertKey(key), false);
  3071         return find != null;
  3074     @Override
  3075     public boolean hasOwnProperty(final double key) {
  3076         final int index = getArrayIndexNoThrow(key);
  3078         if (getArray().has(index)) {
  3079             return true;
  3082         final FindProperty find = findProperty(convertKey(key), false);
  3084         return find != null;
  3087     @Override
  3088     public boolean delete(final int key, final boolean strict) {
  3089         final int index = getArrayIndexNoThrow(key);
  3090         final ArrayData array = getArray();
  3092         if (array.has(index)) {
  3093             if (array.canDelete(index, strict)) {
  3094                 setArray(array.delete(index));
  3095                 return true;
  3097             return false;
  3100         return deleteObject(JSType.toObject(key), strict);
  3103     @Override
  3104     public boolean delete(final long key, final boolean strict) {
  3105         final int index = getArrayIndexNoThrow(key);
  3106         final ArrayData array = getArray();
  3108         if (array.has(index)) {
  3109             if (array.canDelete(index, strict)) {
  3110                 setArray(array.delete(index));
  3111                 return true;
  3113             return false;
  3116         return deleteObject(JSType.toObject(key), strict);
  3119     @Override
  3120     public boolean delete(final double key, final boolean strict) {
  3121         final int index = getArrayIndexNoThrow(key);
  3122         final ArrayData array = getArray();
  3124         if (array.has(index)) {
  3125             if (array.canDelete(index, strict)) {
  3126                 setArray(array.delete(index));
  3127                 return true;
  3129             return false;
  3132         return deleteObject(JSType.toObject(key), strict);
  3135     @Override
  3136     public boolean delete(final Object key, final boolean strict) {
  3137         final int index = getArrayIndexNoThrow(key);
  3138         final ArrayData array = getArray();
  3140         if (array.has(index)) {
  3141             if (array.canDelete(index, strict)) {
  3142                 setArray(array.delete(index));
  3143                 return true;
  3145             return false;
  3148         return deleteObject(key, strict);
  3151     private boolean deleteObject(final Object key, final boolean strict) {
  3152         final String propName = convertKey(key);
  3153         final FindProperty find = findProperty(propName, false);
  3155         if (find == null) {
  3156             return true;
  3159         if (!find.getProperty().isConfigurable()) {
  3160             if (strict) {
  3161                 throw typeError("cant.delete.property", propName, ScriptRuntime.safeToString(this));
  3163             return false;
  3166         final Property prop = find.getProperty();
  3167         notifyPropertyDeleted(this, prop);
  3168         deleteOwnProperty(prop);
  3170         return true;
  3173     /*
  3174      * Make a new UserAccessorProperty property. getter and setter functions are stored in
  3175      * this ScriptObject and slot values are used in property object.
  3176      */
  3177     private UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
  3178         int oldSpillLength = getMap().getSpillLength();
  3180         int getterSlot = oldSpillLength++;
  3181         setSpill(getterSlot, getter);
  3182         // if getter function is null, flag the slot to be negative (less by 1)
  3183         if (getter == null) {
  3184             getterSlot = -getterSlot - 1;
  3187         int setterSlot = oldSpillLength++;
  3189         setSpill(setterSlot, setter);
  3190         // if setter function is null, flag the slot to be negative (less by 1)
  3191         if (setter == null) {
  3192             setterSlot = -setterSlot - 1;
  3195         return new UserAccessorProperty(key, propertyFlags, getterSlot, setterSlot);
  3198     private void setSpill(final int slot, final Object value) {
  3199         if (slot >= 0) {
  3200             final int index = slot;
  3201             if (spill == null) {
  3202                 // create new spill.
  3203                 spill = new Object[Math.max(index + 1, SPILL_RATE)];
  3204             } else if (index >= spill.length) {
  3205                 // grow spill as needed
  3206                 final Object[] newSpill = new Object[index + 1];
  3207                 System.arraycopy(spill, 0, newSpill, 0, spill.length);
  3208                 spill = newSpill;
  3211             spill[index] = value;
  3215     // user accessors are either stored in spill array slots
  3216     // get the accessor value using slot number. Note that slot is spill array index.
  3217     Object getSpill(final int slot) {
  3218         final int index = slot;
  3219         return (index < 0 || (index >= spill.length)) ? null : spill[index];
  3222     // User defined getter and setter are always called by "dyn:call". Note that the user
  3223     // getter/setter may be inherited. If so, proto is bound during lookup. In either
  3224     // inherited or self case, slot is also bound during lookup. Actual ScriptFunction
  3225     // to be called is retrieved everytime and applied.
  3226     @SuppressWarnings("unused")
  3227     private static Object userAccessorGetter(final ScriptObject proto, final int slot, final Object self) {
  3228         final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
  3229         final Object       func      = container.getSpill(slot);
  3231         if (func instanceof ScriptFunction) {
  3232             try {
  3233                 return INVOKE_UA_GETTER.invokeExact(func, self);
  3234             } catch(final Error|RuntimeException t) {
  3235                 throw t;
  3236             } catch(final Throwable t) {
  3237                 throw new RuntimeException(t);
  3241         return UNDEFINED;
  3244     @SuppressWarnings("unused")
  3245     private static void userAccessorSetter(final ScriptObject proto, final int slot, final String name, final Object self, final Object value) {
  3246         final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
  3247         final Object       func      = container.getSpill(slot);
  3249         if (func instanceof ScriptFunction) {
  3250             try {
  3251                 INVOKE_UA_SETTER.invokeExact(func, self, value);
  3252             } catch(final Error|RuntimeException t) {
  3253                 throw t;
  3254             } catch(final Throwable t) {
  3255                 throw new RuntimeException(t);
  3257         }  else if (name != null) {
  3258             throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
  3262     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
  3263         final Class<?>   own = ScriptObject.class;
  3264         final MethodType mt  = MH.type(rtype, types);
  3265         try {
  3266             return MH.findStatic(MethodHandles.lookup(), own, name, mt);
  3267         } catch (final MethodHandleFactory.LookupException e) {
  3268             return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
  3272     private static MethodHandle getKnownFunctionPropertyGuard(final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
  3273         return MH.insertArguments(KNOWNFUNCPROPGUARD, 1, map, getter, where, func);
  3276     @SuppressWarnings("unused")
  3277     private static boolean knownFunctionPropertyGuard(final Object self, final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
  3278         if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) {
  3279             try {
  3280                 return getter.invokeExact(where) == func;
  3281             } catch (final RuntimeException | Error e) {
  3282                 throw e;
  3283             } catch (final Throwable t) {
  3284                 throw new RuntimeException(t);
  3288         return false;
  3291     /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */
  3292     private static int count;
  3294     /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created that are scope */
  3295     private static int scopeCount;
  3297     /**
  3298      * Get number of {@code ScriptObject} instances created. If not running in debug
  3299      * mode this is always 0
  3301      * @return number of ScriptObjects created
  3302      */
  3303     public static int getCount() {
  3304         return count;
  3307     /**
  3308      * Get number of scope {@code ScriptObject} instances created. If not running in debug
  3309      * mode this is always 0
  3311      * @return number of scope ScriptObjects created
  3312      */
  3313     public static int getScopeCount() {
  3314         return scopeCount;

mercurial