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

Fri, 14 Jun 2013 21:16:14 +0530

author
sundar
date
Fri, 14 Jun 2013 21:16:14 +0530
changeset 350
3d947baa33cc
parent 344
b0dcc3727fc3
child 379
682889823712
permissions
-rw-r--r--

8016618: script mirror object access should be improved
Reviewed-by: jlaskey, lagergren

     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.virtualCall;
    29 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
    30 import static jdk.nashorn.internal.lookup.Lookup.MH;
    31 import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
    32 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
    33 import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE;
    34 import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE;
    35 import static jdk.nashorn.internal.runtime.PropertyDescriptor.GET;
    36 import static jdk.nashorn.internal.runtime.PropertyDescriptor.SET;
    37 import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE;
    38 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
    39 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
    41 import java.lang.invoke.MethodHandle;
    42 import java.lang.invoke.MethodHandles;
    43 import java.lang.invoke.MethodType;
    44 import java.util.AbstractMap;
    45 import java.util.ArrayList;
    46 import java.util.Arrays;
    47 import java.util.Collection;
    48 import java.util.Collections;
    49 import java.util.HashSet;
    50 import java.util.Iterator;
    51 import java.util.LinkedHashSet;
    52 import java.util.List;
    53 import java.util.Map;
    54 import java.util.Set;
    55 import jdk.internal.dynalink.CallSiteDescriptor;
    56 import jdk.internal.dynalink.linker.GuardedInvocation;
    57 import jdk.internal.dynalink.linker.LinkRequest;
    58 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
    59 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
    60 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
    61 import jdk.nashorn.internal.lookup.Lookup;
    62 import jdk.nashorn.internal.lookup.MethodHandleFactory;
    63 import jdk.nashorn.internal.objects.AccessorPropertyDescriptor;
    64 import jdk.nashorn.internal.objects.DataPropertyDescriptor;
    65 import jdk.nashorn.internal.runtime.arrays.ArrayData;
    66 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
    67 import jdk.nashorn.internal.runtime.linker.Bootstrap;
    68 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
    69 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
    70 import jdk.nashorn.internal.runtime.linker.NashornGuards;
    72 /**
    73  * Base class for generic JavaScript objects.
    74  * <p>
    75  * Notes:
    76  * <ul>
    77  * <li>The map is used to identify properties in the object.</li>
    78  * <li>If the map is modified then it must be cloned and replaced.  This notifies
    79  *     any code that made assumptions about the object that things have changed.
    80  *     Ex. CallSites that have been validated must check to see if the map has
    81  *     changed (or a map from a different object type) and hence relink the method
    82  *     to call.</li>
    83  * <li>Modifications of the map include adding/deleting attributes or changing a
    84  *     function field value.</li>
    85  * </ul>
    86  */
    88 public abstract class ScriptObject extends PropertyListenerManager implements PropertyAccess {
    90     /** Search fall back routine name for "no such method" */
    91     static final String NO_SUCH_METHOD_NAME   = "__noSuchMethod__";
    93     /** Search fall back routine name for "no such property" */
    94     static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__";
    96     /** Per ScriptObject flag - is this a scope object? */
    97     public static final int IS_SCOPE       = 0b0000_0001;
    99     /** Per ScriptObject flag - is this an array object? */
   100     public static final int IS_ARRAY       = 0b0000_0010;
   102     /** Per ScriptObject flag - is this an arguments object? */
   103     public static final int IS_ARGUMENTS   = 0b0000_0100;
   105     /** Is this a prototype PropertyMap? */
   106     public static final int IS_PROTOTYPE   = 0b0000_1000;
   108     /** Is length property not-writable? */
   109     public static final int IS_LENGTH_NOT_WRITABLE = 0b0001_0000;
   111     /** Spill growth rate - by how many elements does {@link ScriptObject#spill} when full */
   112     public static final int SPILL_RATE = 8;
   114     /** Map to property information and accessor functions. Ordered by insertion. */
   115     private PropertyMap map;
   117     /** objects proto. */
   118     private ScriptObject proto;
   120     /** Context of the object, lazily cached. */
   121     private Context context;
   123     /** Object flags. */
   124     private int flags;
   126     /** Area for properties added to object after instantiation, see {@link AccessorProperty} */
   127     public Object[] spill;
   129     /** Indexed array data. */
   130     private ArrayData arrayData;
   132     static final MethodHandle SETFIELD           = findOwnMH("setField",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, Object.class, Object.class);
   133     static final MethodHandle SETSPILL           = findOwnMH("setSpill",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
   134     static final MethodHandle SETSPILLWITHNEW    = findOwnMH("setSpillWithNew",  void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
   135     static final MethodHandle SETSPILLWITHGROW   = findOwnMH("setSpillWithGrow", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, int.class, Object.class, Object.class);
   137     private static final MethodHandle TRUNCATINGFILTER   = findOwnMH("truncatingFilter", Object[].class, int.class, Object[].class);
   138     private static final MethodHandle KNOWNFUNCPROPGUARD = findOwnMH("knownFunctionPropertyGuard", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, Object.class, ScriptFunction.class);
   140     /** Method handle for getting a function argument at a given index. Used from MapCreator */
   141     public static final Call GET_ARGUMENT       = virtualCall(ScriptObject.class, "getArgument", Object.class, int.class);
   143     /** Method handle for setting a function argument at a given index. Used from MapCreator */
   144     public static final Call SET_ARGUMENT       = virtualCall(ScriptObject.class, "setArgument", void.class, int.class, Object.class);
   146     /** Method handle for getting the proto of a ScriptObject */
   147     public static final Call GET_PROTO          = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class);
   149     /** Method handle for setting the proto of a ScriptObject */
   150     public static final Call SET_PROTO          = virtualCallNoLookup(ScriptObject.class, "setProto", void.class, ScriptObject.class);
   152     /** Method handle for setting the user accessors of a ScriptObject */
   153     public static final Call SET_USER_ACCESSORS = virtualCall(ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class);
   155     /**
   156      * Constructor
   157      */
   158     public ScriptObject() {
   159         this(null);
   160     }
   162     /**
   163     * Constructor
   164     *
   165     * @param map {@link PropertyMap} used to create the initial object
   166     */
   167     public ScriptObject(final PropertyMap map) {
   168         if (Context.DEBUG) {
   169             ScriptObject.count++;
   170         }
   172         this.arrayData = ArrayData.EMPTY_ARRAY;
   174         if (map == null) {
   175             this.setMap(PropertyMap.newMap(getClass()));
   176             return;
   177         }
   179         this.setMap(map);
   180     }
   182     /**
   183      * Copy all properties from the source object with their receiver bound to the source.
   184      * This function was known as mergeMap
   185      *
   186      * @param source The source object to copy from.
   187      */
   188     public void addBoundProperties(final ScriptObject source) {
   189         PropertyMap newMap = this.getMap();
   191         for (final Property property : source.getMap().getProperties()) {
   192             final String key = property.getKey();
   194             if (newMap.findProperty(key) == null) {
   195                 if (property instanceof UserAccessorProperty) {
   196                     final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
   197                     newMap = newMap.addProperty(prop);
   198                 } else {
   199                     newMap = newMap.newPropertyBind((AccessorProperty)property, source);
   200                 }
   201             }
   202         }
   204         this.setMap(newMap);
   205     }
   207     /**
   208      * Bind the method handle to the specified receiver, while preserving its original type (it will just ignore the
   209      * first argument in lieu of the bound argument).
   210      * @param methodHandle Method handle to bind to.
   211      * @param receiver     Object to bind.
   212      * @return Bound method handle.
   213      */
   214     static MethodHandle bindTo(final MethodHandle methodHandle, final Object receiver) {
   215         return MH.dropArguments(MH.bindTo(methodHandle, receiver), 0, methodHandle.type().parameterType(0));
   216     }
   218     /**
   219      * Return a property iterator.
   220      * @return Property iterator.
   221      */
   222     public Iterator<String> propertyIterator() {
   223         return new KeyIterator(this);
   224     }
   226     /**
   227      * Return a property value iterator.
   228      * @return Property value iterator.
   229      */
   230     public Iterator<Object> valueIterator() {
   231         return new ValueIterator(this);
   232     }
   234     /**
   235      * ECMA 8.10.1 IsAccessorDescriptor ( Desc )
   236      * @return true if this has a {@link AccessorPropertyDescriptor} with a getter or a setter
   237      */
   238     public final boolean isAccessorDescriptor() {
   239         return has(GET) || has(SET);
   240     }
   242     /**
   243      * ECMA 8.10.2 IsDataDescriptor ( Desc )
   244      * @return true if this has a {@link DataPropertyDescriptor}, i.e. the object has a property value and is writable
   245      */
   246     public final boolean isDataDescriptor() {
   247         return has(VALUE) || has(WRITABLE);
   248     }
   250     /**
   251      * ECMA 8.10.3 IsGenericDescriptor ( Desc )
   252      * @return true if this has a descriptor describing an {@link AccessorPropertyDescriptor} or {@link DataPropertyDescriptor}
   253      */
   254     public final boolean isGenericDescriptor() {
   255         return isAccessorDescriptor() || isDataDescriptor();
   256     }
   258     /**
   259       * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
   260       *
   261       * @return property descriptor
   262       */
   263     public final PropertyDescriptor toPropertyDescriptor() {
   264         final GlobalObject global = (GlobalObject) Context.getGlobalTrusted();
   266         final PropertyDescriptor desc;
   267         if (isDataDescriptor()) {
   268             if (has(SET) || has(GET)) {
   269                 throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
   270             }
   272             desc = global.newDataDescriptor(UNDEFINED, false, false, false);
   273         } else if (isAccessorDescriptor()) {
   274             if (has(VALUE) || has(WRITABLE)) {
   275                 throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
   276             }
   278             desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false);
   279         } else {
   280             desc = global.newGenericDescriptor(false, false);
   281         }
   283         return desc.fillFrom(this);
   284     }
   286     /**
   287      * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
   288      *
   289      * @param global  global scope object
   290      * @param obj object to create property descriptor from
   291      *
   292      * @return property descriptor
   293      */
   294     public static PropertyDescriptor toPropertyDescriptor(final ScriptObject global, final Object obj) {
   295         if (obj instanceof ScriptObject) {
   296             return ((ScriptObject)obj).toPropertyDescriptor();
   297         }
   299         throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj));
   300     }
   302     /**
   303      * ECMA 8.12.1 [[GetOwnProperty]] (P)
   304      *
   305      * @param key property key
   306      *
   307      * @return Returns the Property Descriptor of the named own property of this
   308      * object, or undefined if absent.
   309      */
   310     public Object getOwnPropertyDescriptor(final String key) {
   311         final Property property = getMap().findProperty(key);
   313         final GlobalObject global = (GlobalObject)Context.getGlobalTrusted();
   315         if (property != null) {
   316             final ScriptFunction get   = property.getGetterFunction(this);
   317             final ScriptFunction set   = property.getSetterFunction(this);
   319             final boolean configurable = property.isConfigurable();
   320             final boolean enumerable   = property.isEnumerable();
   321             final boolean writable     = property.isWritable();
   323             if (property instanceof UserAccessorProperty) {
   324                 return global.newAccessorDescriptor(
   325                     (get != null) ?
   326                         get :
   327                         UNDEFINED,
   328                     (set != null) ?
   329                         set :
   330                         UNDEFINED,
   331                     configurable,
   332                     enumerable);
   333             }
   335             return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable);
   336         }
   338         final int index = ArrayIndex.getArrayIndex(key);
   339         final ArrayData array = getArray();
   341         if (array.has(index)) {
   342             return array.getDescriptor(global, index);
   343         }
   345         return UNDEFINED;
   346     }
   348     /**
   349      * ECMA 8.12.2 [[GetProperty]] (P)
   350      *
   351      * @param key property key
   352      *
   353      * @return Returns the fully populated Property Descriptor of the named property
   354      * of this object, or undefined if absent.
   355      */
   356     public Object getPropertyDescriptor(final String key) {
   357         final Object res = getOwnPropertyDescriptor(key);
   359         if (res != UNDEFINED) {
   360             return res;
   361         } else if (getProto() != null) {
   362             return getProto().getOwnPropertyDescriptor(key);
   363         } else {
   364             return UNDEFINED;
   365         }
   366     }
   368     /**
   369      * ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw)
   370      *
   371      * @param key the property key
   372      * @param propertyDesc the property descriptor
   373      * @param reject is the property extensible - true means new definitions are rejected
   374      *
   375      * @return true if property was successfully defined
   376      */
   377     public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
   378         final ScriptObject       global  = Context.getGlobalTrusted();
   379         final PropertyDescriptor desc    = toPropertyDescriptor(global, propertyDesc);
   380         final Object             current = getOwnPropertyDescriptor(key);
   381         final String             name    = JSType.toString(key);
   383         if (current == UNDEFINED) {
   384             if (isExtensible()) {
   385                 // add a new own property
   386                 addOwnProperty(key, desc);
   387                 return true;
   388             }
   389             // new property added to non-extensible object
   390             if (reject) {
   391                 throw typeError(global, "object.non.extensible", name, ScriptRuntime.safeToString(this));
   392             }
   393             return false;
   394         }
   395         // modifying an existing property
   396         final PropertyDescriptor currentDesc = (PropertyDescriptor) current;
   397         final PropertyDescriptor newDesc     = desc;
   399         if (newDesc.type() == PropertyDescriptor.GENERIC &&
   400             ! newDesc.has(CONFIGURABLE) && ! newDesc.has(ENUMERABLE)) {
   401             // every descriptor field is absent
   402             return true;
   403         }
   405         if (currentDesc.equals(newDesc)) {
   406             // every descriptor field of the new is same as the current
   407             return true;
   408         }
   410         if (! currentDesc.isConfigurable()) {
   411             if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) {
   412                 // not configurable can not be made configurable
   413                 if (reject) {
   414                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   415                 }
   416                 return false;
   417             }
   419             if (newDesc.has(ENUMERABLE) &&
   420                 currentDesc.isEnumerable() != newDesc.isEnumerable()) {
   421                 // cannot make non-enumerable as enumerable or vice-versa
   422                 if (reject) {
   423                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   424                 }
   425                 return false;
   426             }
   427         }
   429         int propFlags = Property.mergeFlags(currentDesc, newDesc);
   430         Property property = getMap().findProperty(key);
   432         if (currentDesc.type() == PropertyDescriptor.DATA &&
   433             (newDesc.type() == PropertyDescriptor.DATA || newDesc.type() == PropertyDescriptor.GENERIC)) {
   434             if (! currentDesc.isConfigurable() && ! currentDesc.isWritable()) {
   435                 if (newDesc.has(WRITABLE) && newDesc.isWritable() ||
   436                     newDesc.has(VALUE) && ! ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) {
   437                     if (reject) {
   438                         throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   439                     }
   440                     return false;
   441                 }
   442             }
   444             final boolean newValue = newDesc.has(VALUE);
   445             final Object value     = newValue? newDesc.getValue() : currentDesc.getValue();
   446             if (newValue && property != null) {
   447                 // Temporarily clear flags.
   448                 property = modifyOwnProperty(property, 0);
   449                 set(key, value, false);
   450             }
   452             if (property == null) {
   453                 // promoting an arrayData value to actual property
   454                 addOwnProperty(key, propFlags, value);
   455                 removeArraySlot(key);
   456             } else {
   457                 // Now set the new flags
   458                 modifyOwnProperty(property, propFlags);
   459             }
   460         } else if (currentDesc.type() == PropertyDescriptor.ACCESSOR &&
   461                    (newDesc.type() == PropertyDescriptor.ACCESSOR ||
   462                     newDesc.type() == PropertyDescriptor.GENERIC)) {
   463             if (! currentDesc.isConfigurable()) {
   464                 if (newDesc.has(PropertyDescriptor.GET) && ! ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) ||
   465                     newDesc.has(PropertyDescriptor.SET) && ! ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) {
   466                     if (reject) {
   467                         throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   468                     }
   469                     return false;
   470                 }
   471             }
   473             // New set the new features.
   474             modifyOwnProperty(property, propFlags,
   475                                       newDesc.has(GET) ? newDesc.getGetter() : currentDesc.getGetter(),
   476                                       newDesc.has(SET) ? newDesc.getSetter() : currentDesc.getSetter());
   477         } else {
   478             // changing descriptor type
   479             if (! currentDesc.isConfigurable()) {
   480                 // not configurable can not be made configurable
   481                 if (reject) {
   482                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   483                 }
   484                 return false;
   485             }
   487             propFlags = 0;
   489             // Preserve only configurable and enumerable from current desc
   490             // if those are not overridden in the new property descriptor.
   491             boolean value = newDesc.has(CONFIGURABLE)? newDesc.isConfigurable() : currentDesc.isConfigurable();
   492             if (!value) {
   493                 propFlags |= Property.NOT_CONFIGURABLE;
   494             }
   495             value = newDesc.has(ENUMERABLE)? newDesc.isEnumerable() : currentDesc.isEnumerable();
   496             if (!value) {
   497                 propFlags |= Property.NOT_ENUMERABLE;
   498             }
   500             final int type = newDesc.type();
   501             if (type == PropertyDescriptor.DATA) {
   502                 // get writable from the new descriptor
   503                 value = newDesc.has(WRITABLE) && newDesc.isWritable();
   504                 if (! value) {
   505                     propFlags |= Property.NOT_WRITABLE;
   506                 }
   508                 // delete the old property
   509                 deleteOwnProperty(property);
   510                 // add new data property
   511                 addOwnProperty(key, propFlags, newDesc.getValue());
   512             } else if (type == PropertyDescriptor.ACCESSOR) {
   513                 if (property == null) {
   514                     addOwnProperty(key, propFlags,
   515                                      newDesc.has(GET) ? newDesc.getGetter() : null,
   516                                      newDesc.has(SET) ? newDesc.getSetter() : null);
   517                 } else {
   518                     // Modify old property with the new features.
   519                     modifyOwnProperty(property, propFlags,
   520                                         newDesc.has(GET) ? newDesc.getGetter() : null,
   521                                         newDesc.has(SET) ? newDesc.getSetter() : null);
   522                 }
   523             }
   524         }
   526         checkIntegerKey(key);
   528         return true;
   529     }
   531     /**
   532      * Spec. mentions use of [[DefineOwnProperty]] for indexed properties in
   533      * certain places (eg. Array.prototype.map, filter). We can not use ScriptObject.set
   534      * method in such cases. This is because set method uses inherited setters (if any)
   535      * from any object in proto chain such as Array.prototype, Object.prototype.
   536      * This method directly sets a particular element value in the current object.
   537      *
   538      * @param index key for property
   539      * @param value value to define
   540      */
   541     protected final void defineOwnProperty(final int index, final Object value) {
   542         assert ArrayIndex.isValidArrayIndex(index) : "invalid array index";
   543         final long longIndex = ArrayIndex.toLongIndex(index);
   544         if (longIndex >= getArray().length()) {
   545             // make array big enough to hold..
   546             setArray(getArray().ensure(longIndex));
   547         }
   548         setArray(getArray().set(index, value, false));
   549     }
   551     private void checkIntegerKey(final String key) {
   552         final int index = ArrayIndex.getArrayIndex(key);
   554         if (ArrayIndex.isValidArrayIndex(index)) {
   555             final ArrayData data = getArray();
   557             if (data.has(index)) {
   558                 setArray(data.delete(index));
   559             }
   560         }
   561     }
   563     private void removeArraySlot(final String key) {
   564         final int index = ArrayIndex.getArrayIndex(key);
   565         final ArrayData array = getArray();
   567         if (array.has(index)) {
   568             setArray(array.delete(index));
   569         }
   570     }
   572     /**
   573       * Add a new property to the object.
   574       *
   575       * @param key          property key
   576       * @param propertyDesc property descriptor for property
   577       */
   578     public final void addOwnProperty(final String key, final PropertyDescriptor propertyDesc) {
   579         // Already checked that there is no own property with that key.
   580         PropertyDescriptor pdesc = propertyDesc;
   582         final int propFlags = Property.toFlags(pdesc);
   584         if (pdesc.type() == PropertyDescriptor.GENERIC) {
   585             final GlobalObject global = (GlobalObject) Context.getGlobalTrusted();
   586             final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false);
   588             dDesc.fillFrom((ScriptObject)pdesc);
   589             pdesc = dDesc;
   590         }
   592         final int type = pdesc.type();
   593         if (type == PropertyDescriptor.DATA) {
   594             addOwnProperty(key, propFlags, pdesc.getValue());
   595         } else if (type == PropertyDescriptor.ACCESSOR) {
   596             addOwnProperty(key, propFlags,
   597                     pdesc.has(GET) ? pdesc.getGetter() : null,
   598                     pdesc.has(SET) ? pdesc.getSetter() : null);
   599         }
   601         checkIntegerKey(key);
   602     }
   604     /**
   605      * Low level property API (not using property descriptors)
   606      * <p>
   607      * Find a property in the prototype hierarchy. Note: this is final and not
   608      * a good idea to override. If you have to, use
   609      * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
   610      * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
   611      * overriding way to find array properties
   612      *
   613      * @see jdk.nashorn.internal.objects.NativeArray
   614      *
   615      * @param key  Property key.
   616      * @param deep Whether the search should look up proto chain.
   617      *
   618      * @return FindPropertyData or null if not found.
   619      */
   620     public final FindProperty findProperty(final String key, final boolean deep) {
   621         return findProperty(key, deep, false, this);
   622     }
   624     /**
   625      * Low level property API (not using property descriptors)
   626      * <p>
   627      * Find a property in the prototype hierarchy. Note: this is not a good idea
   628      * to override except as it was done in {@link WithObject}.
   629      * If you have to, use
   630      * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
   631      * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
   632      * overriding way to find array properties
   633      *
   634      * @see jdk.nashorn.internal.objects.NativeArray
   635      *
   636      * @param key  Property key.
   637      * @param deep Whether the search should look up proto chain.
   638      * @param stopOnNonScope should a deep search stop on the first non-scope object?
   639      * @param start the object on which the lookup was originally initiated
   640      *
   641      * @return FindPropertyData or null if not found.
   642      */
   643     FindProperty findProperty(final String key, final boolean deep, final boolean stopOnNonScope, final ScriptObject start) {
   644         // if doing deep search, stop search on the first non-scope object if asked to do so
   645         if (stopOnNonScope && start != this && !isScope()) {
   646             return null;
   647         }
   649         final PropertyMap selfMap  = getMap();
   650         final Property    property = selfMap.findProperty(key);
   652         if (property != null) {
   653             return new FindProperty(start, this, property);
   654         }
   656         if (deep) {
   657             final ScriptObject myProto = getProto();
   658             if (myProto != null) {
   659                 return myProto.findProperty(key, deep, stopOnNonScope, start);
   660             }
   661         }
   663         return null;
   664     }
   666     /**
   667      * Add a new property to the object.
   668      * <p>
   669      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
   670      *
   671      * @param key             Property key.
   672      * @param propertyFlags   Property flags.
   673      * @param getter          Property getter, or null if not defined
   674      * @param setter          Property setter, or null if not defined
   675      *
   676      * @return New property.
   677      */
   678     public final Property addOwnProperty(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
   679         return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter));
   680     }
   682     /**
   683      * Add a new property to the object.
   684      * <p>
   685      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
   686      *
   687      * @param key             Property key.
   688      * @param propertyFlags   Property flags.
   689      * @param value           Value of property
   690      *
   691      * @return New property.
   692      */
   693     public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) {
   694         final Property property = addSpillProperty(key, propertyFlags);
   695         property.setObjectValue(this, this, value, false);
   696         return property;
   697     }
   699     /**
   700      * Add a new property to the object.
   701      * <p>
   702      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
   703      *
   704      * @param newProperty property to add
   705      *
   706      * @return New property.
   707      */
   708     public final Property addOwnProperty(final Property newProperty) {
   709         PropertyMap oldMap = getMap();
   711         while (true) {
   712             final PropertyMap newMap = oldMap.addProperty(newProperty);
   714             if (!compareAndSetMap(oldMap, newMap)) {
   715                 oldMap = getMap();
   716                 final Property oldProperty = oldMap.findProperty(newProperty.getKey());
   718                 if (oldProperty != null) {
   719                     return oldProperty;
   720                 }
   721             } else {
   722                 return newProperty;
   723             }
   724         }
   725     }
   727     private void erasePropertyValue(final Property property) {
   728         // Erase the property field value with undefined. If the property is defined
   729         // by user-defined accessors, we don't want to call the setter!!
   730         if (!(property instanceof UserAccessorProperty)) {
   731             property.setObjectValue(this, this, UNDEFINED, false);
   732         }
   733     }
   735     /**
   736      * Delete a property from the object.
   737      *
   738      * @param property Property to delete.
   739      *
   740      * @return true if deleted.
   741      */
   742     public final boolean deleteOwnProperty(final Property property) {
   743         erasePropertyValue(property);
   744         PropertyMap oldMap = getMap();
   746         while (true) {
   747             final PropertyMap newMap = oldMap.deleteProperty(property);
   749             if (newMap == null) {
   750                 return false;
   751             }
   753             if (!compareAndSetMap(oldMap, newMap)) {
   754                 oldMap = getMap();
   755             } else {
   756                 // delete getter and setter function references so that we don't leak
   757                 if (property instanceof UserAccessorProperty) {
   758                     final UserAccessorProperty uc = (UserAccessorProperty) property;
   759                     setSpill(uc.getGetterSlot(), null);
   760                     setSpill(uc.getSetterSlot(), null);
   761                 }
   762                 return true;
   763             }
   764         }
   765     }
   767     /**
   768      * Modify a property in the object
   769      *
   770      * @param oldProperty    property to modify
   771      * @param propertyFlags  new property flags
   772      * @param getter         getter for {@link UserAccessorProperty}, null if not present or N/A
   773      * @param setter         setter for {@link UserAccessorProperty}, null if not present or N/A
   774      *
   775      * @return new property
   776      */
   777     public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
   778         Property newProperty;
   779         if (oldProperty instanceof UserAccessorProperty) {
   780             // re-use the slots of the old user accessor property.
   781             final UserAccessorProperty uc = (UserAccessorProperty) oldProperty;
   783             int getterSlot = uc.getGetterSlot();
   784             // clear the old getter and set the new getter
   785             setSpill(getterSlot, getter);
   786             // if getter function is null, flag the slot to be negative (less by 1)
   787             if (getter == null) {
   788                 getterSlot = -getterSlot - 1;
   789             }
   791             int setterSlot = uc.getSetterSlot();
   792             // clear the old setter and set the new setter
   793             setSpill(setterSlot, setter);
   794             // if setter function is null, flag the slot to be negative (less by 1)
   795             if (setter == null) {
   796                 setterSlot = -setterSlot - 1;
   797             }
   799             newProperty = new UserAccessorProperty(oldProperty.getKey(), propertyFlags, getterSlot, setterSlot);
   800             // if just flipping getter and setter with new functions, no need to change property or map
   801             if (oldProperty.equals(newProperty)) {
   802                 return oldProperty;
   803             }
   804         } else {
   805             // erase old property value and create new user accessor property
   806             erasePropertyValue(oldProperty);
   807             newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter);
   808         }
   810         notifyPropertyModified(this, oldProperty, newProperty);
   812         return modifyOwnProperty(oldProperty, newProperty);
   813     }
   815     /**
   816       * Modify a property in the object
   817       *
   818       * @param oldProperty    property to modify
   819       * @param propertyFlags  new property flags
   820       *
   821       * @return new property
   822       */
   823     public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags) {
   824         return modifyOwnProperty(oldProperty, oldProperty.setFlags(propertyFlags));
   825     }
   827     /**
   828      * Modify a property in the object, replacing a property with a new one
   829      *
   830      * @param oldProperty   property to replace
   831      * @param newProperty   property to replace it with
   832      *
   833      * @return new property
   834      */
   835     private Property modifyOwnProperty(final Property oldProperty, final Property newProperty) {
   836         assert newProperty.getKey().equals(oldProperty.getKey()) : "replacing property with different key";
   838         PropertyMap oldMap = getMap();
   840         while (true) {
   841             final PropertyMap newMap = oldMap.replaceProperty(oldProperty, newProperty);
   843             if (!compareAndSetMap(oldMap, newMap)) {
   844                 oldMap = getMap();
   845                 final Property oldPropertyLookup = oldMap.findProperty(oldProperty.getKey());
   847                 if (oldPropertyLookup != null && oldPropertyLookup.equals(newProperty)) {
   848                     return oldPropertyLookup;
   849                 }
   850             } else {
   851                 return newProperty;
   852             }
   853         }
   854     }
   856     /**
   857      * Update getter and setter in an object literal.
   858      *
   859      * @param key    Property key.
   860      * @param getter {@link UserAccessorProperty} defined getter, or null if none
   861      * @param setter {@link UserAccessorProperty} defined setter, or null if none
   862      */
   863     public final void setUserAccessors(final String key, final ScriptFunction getter, final ScriptFunction setter) {
   864         final Property oldProperty = getMap().findProperty(key);
   865         if (oldProperty != null) {
   866             final UserAccessorProperty newProperty = newUserAccessors(oldProperty.getKey(), oldProperty.getFlags(), getter, setter);
   867             modifyOwnProperty(oldProperty, newProperty);
   868         } else {
   869             final UserAccessorProperty newProperty = newUserAccessors(key, 0, getter, setter);
   870             addOwnProperty(newProperty);
   871         }
   872     }
   874     private static int getIntValue(final FindProperty find) {
   875         final MethodHandle getter = find.getGetter(int.class);
   876         if (getter != null) {
   877             try {
   878                 return (int)getter.invokeExact((Object)find.getGetterReceiver());
   879             } catch (final Error|RuntimeException e) {
   880                 throw e;
   881             } catch (final Throwable e) {
   882                 throw new RuntimeException(e);
   883             }
   884         }
   886         return ObjectClassGenerator.UNDEFINED_INT;
   887     }
   889     private static long getLongValue(final FindProperty find) {
   890         final MethodHandle getter = find.getGetter(long.class);
   891         if (getter != null) {
   892             try {
   893                 return (long)getter.invokeExact((Object)find.getGetterReceiver());
   894             } catch (final Error|RuntimeException e) {
   895                 throw e;
   896             } catch (final Throwable e) {
   897                 throw new RuntimeException(e);
   898             }
   899         }
   901         return ObjectClassGenerator.UNDEFINED_LONG;
   902     }
   904     private static double getDoubleValue(final FindProperty find) {
   905         final MethodHandle getter = find.getGetter(double.class);
   906         if (getter != null) {
   907             try {
   908                 return (double)getter.invokeExact((Object)find.getGetterReceiver());
   909             } catch (final Error|RuntimeException e) {
   910                 throw e;
   911             } catch (final Throwable e) {
   912                 throw new RuntimeException(e);
   913             }
   914         }
   916         return ObjectClassGenerator.UNDEFINED_DOUBLE;
   917     }
   919     /**
   920       * Get the object value of a property
   921       *
   922       * @param find {@link FindProperty} lookup result
   923       *
   924       * @return the value of the property
   925       */
   926     protected static Object getObjectValue(final FindProperty find) {
   927         return find.getObjectValue();
   928     }
   930     /**
   931      * Return methodHandle of value function for call.
   932      *
   933      * @param find      data from find property.
   934      * @param type      method type of function.
   935      * @param bindName  null or name to bind to second argument (property not found method.)
   936      *
   937      * @return value of property as a MethodHandle or null.
   938      */
   939     protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) {
   940         return getCallMethodHandle(getObjectValue(find), type, bindName);
   941     }
   943     /**
   944      * Return methodHandle of value function for call.
   945      *
   946      * @param value     value of receiver, it not a {@link ScriptFunction} this will return null.
   947      * @param type      method type of function.
   948      * @param bindName  null or name to bind to second argument (property not found method.)
   949      *
   950      * @return value of property as a MethodHandle or null.
   951      */
   952     protected static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) {
   953         return value instanceof ScriptFunction ? ((ScriptFunction)value).getCallMethodHandle(type, bindName) : null;
   954     }
   956     /**
   957      * Get value using found property.
   958      *
   959      * @param property Found property.
   960      *
   961      * @return Value of property.
   962      */
   963     public final Object getWithProperty(final Property property) {
   964         return getObjectValue(new FindProperty(this, this, property));
   965     }
   967     /**
   968      * Get a property given a key
   969      *
   970      * @param key property key
   971      *
   972      * @return property for key
   973      */
   974     public final Property getProperty(final String key) {
   975         return getMap().findProperty(key);
   976     }
   978     static String convertKey(final Object key) {
   979         return (key instanceof String) ? (String)key : JSType.toString(key);
   980     }
   982     /**
   983      * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
   984      * Used for argument access in a vararg function using parameter name.
   985      * Returns the argument at a given key (index)
   986      *
   987      * @param key argument index
   988      *
   989      * @return the argument at the given position, or undefined if not present
   990      */
   991     public Object getArgument(final int key) {
   992         return get(key);
   993     }
   995     /**
   996      * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
   997      * Used for argument access in a vararg function using parameter name.
   998      * Returns the argument at a given key (index)
   999      *
  1000      * @param key   argument index
  1001      * @param value the value to write at the given index
  1002      */
  1003     public void setArgument(final int key, final Object value) {
  1004         set(key, value, false);
  1007     /**
  1008      * Return true if the script object context is strict
  1009      * @return true if strict context
  1010      */
  1011     public final boolean isStrictContext() {
  1012         return getContext()._strict;
  1015     /**
  1016      * Return the current context from the object's map.
  1017      * @return Current context.
  1018      */
  1019     protected final Context getContext() {
  1020         if (context == null) {
  1021             context = Context.fromClass(getClass());
  1023         return context;
  1026     /**
  1027      * Return the map of an object.
  1028      * @return PropertyMap object.
  1029      */
  1030     public final PropertyMap getMap() {
  1031         return map;
  1034     /**
  1035      * Set the initial map.
  1036      * @param map Initial map.
  1037      */
  1038     public final void setMap(final PropertyMap map) {
  1039         this.map = map;
  1042     /**
  1043      * Conditionally set the new map if the old map is the same.
  1044      * @param oldMap Map prior to manipulation.
  1045      * @param newMap Replacement map.
  1046      * @return true if the operation succeeded.
  1047      */
  1048     protected synchronized final boolean compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap) {
  1049         final boolean update = oldMap == this.map;
  1051         if (update) {
  1052             this.map = newMap;
  1055         return update;
  1058     /**
  1059      * Return the __proto__ of an object.
  1060      * @return __proto__ object.
  1061      */
  1062     public final ScriptObject getProto() {
  1063         return proto;
  1066     /**
  1067      * Set the __proto__ of an object.
  1068      * @param newProto new __proto__ to set.
  1069      */
  1070     public synchronized final void setProto(final ScriptObject newProto) {
  1071         final ScriptObject oldProto = proto;
  1072         map = map.changeProto(oldProto, newProto);
  1074         if (newProto != null) {
  1075             newProto.setIsPrototype();
  1078         proto = newProto;
  1080         if (isPrototype()) {
  1081             if (oldProto != null) {
  1082                 oldProto.removePropertyListener(this);
  1085             if (newProto != null) {
  1086                 newProto.addPropertyListener(this);
  1091     /**
  1092      * Set the __proto__ of an object with checks.
  1093      * @param newProto Prototype to set.
  1094      */
  1095     public final void setProtoCheck(final Object newProto) {
  1096         if (newProto == null || newProto instanceof ScriptObject) {
  1097             setProto((ScriptObject)newProto);
  1098         } else {
  1099             final ScriptObject global = Context.getGlobalTrusted();
  1100             final Object  newProtoObject = JSType.toScriptObject(global, newProto);
  1102             if (newProtoObject instanceof ScriptObject) {
  1103                 setProto((ScriptObject)newProtoObject);
  1104             } else {
  1105                 throw typeError(global, "cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
  1110     /**
  1111      * return an array of own property keys associated with the object.
  1113      * @param all True if to include non-enumerable keys.
  1114      * @return Array of keys.
  1115      */
  1116     public String[] getOwnKeys(final boolean all) {
  1117         final List<Object> keys    = new ArrayList<>();
  1118         final PropertyMap  selfMap = this.getMap();
  1120         final ArrayData array  = getArray();
  1121         final long length      = array.length();
  1123         for (long i = 0; i < length; i = array.nextIndex(i)) {
  1124             if (array.has((int)i)) {
  1125                 keys.add(JSType.toString(i));
  1129         for (final Property property : selfMap.getProperties()) {
  1130             if (all || property.isEnumerable()) {
  1131                 keys.add(property.getKey());
  1135         return keys.toArray(new String[keys.size()]);
  1138     /**
  1139      * Check if this ScriptObject has array entries. This means that someone has
  1140      * set values with numeric keys in the object.
  1142      * Note: this can be O(n) up to the array length
  1144      * @return true if array entries exists.
  1145      */
  1146     public boolean hasArrayEntries() {
  1147         final ArrayData array = getArray();
  1148         final long length = array.length();
  1150         for (long i = 0; i < length; i++) {
  1151             if (array.has((int)i)) {
  1152                 return true;
  1156         return false;
  1159     /**
  1160      * Return the valid JavaScript type name descriptor
  1162      * @return "Object"
  1163      */
  1164     public String getClassName() {
  1165         return "Object";
  1168     /**
  1169      * {@code length} is a well known property. This is its getter.
  1170      * Note that this *may* be optimized by other classes
  1172      * @return length property value for this ScriptObject
  1173      */
  1174     public Object getLength() {
  1175         return get("length");
  1178     /**
  1179      * Stateless toString for ScriptObjects.
  1181      * @return string description of this object, e.g. {@code [object Object]}
  1182      */
  1183     public String safeToString() {
  1184         return "[object " + getClassName() + "]";
  1187     /**
  1188      * Return the default value of the object with a given preferred type hint.
  1189      * The preferred type hints are String.class for type String, Number.class
  1190      * for type Number. <p>
  1192      * A <code>hint</code> of null means "no hint".
  1194      * ECMA 8.12.8 [[DefaultValue]](hint)
  1196      * @param typeHint the preferred type hint
  1197      * @return the default value
  1198      */
  1199     public Object getDefaultValue(final Class<?> typeHint) {
  1200         // We delegate to GlobalObject, as the implementation uses dynamic call sites to invoke object's "toString" and
  1201         // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts
  1202         // are being executed in a long-running program, we move the code and their associated dynamic call sites
  1203         // (Global.TO_STRING and Global.VALUE_OF) into per-context code.
  1204         return ((GlobalObject)Context.getGlobalTrusted()).getDefaultValue(this, typeHint);
  1207     /**
  1208      * Checking whether a script object is an instance of another. Used
  1209      * in {@link ScriptFunction} for hasInstance implementation, walks
  1210      * the proto chain
  1212      * @param instance instace to check
  1213      * @return true if 'instance' is an instance of this object
  1214      */
  1215     public boolean isInstance(final ScriptObject instance) {
  1216         return false;
  1219     /**
  1220      * Flag this ScriptObject as non extensible
  1222      * @return the object after being made non extensible
  1223      */
  1224     public ScriptObject preventExtensions() {
  1225         PropertyMap oldMap = getMap();
  1227         while (true) {
  1228             final PropertyMap newMap = getMap().preventExtensions();
  1230             if (!compareAndSetMap(oldMap, newMap)) {
  1231                 oldMap = getMap();
  1232             } else {
  1233                 return this;
  1238     /**
  1239      * Check whether if an Object (not just a ScriptObject) represents JavaScript array
  1241      * @param obj object to check
  1243      * @return true if array
  1244      */
  1245     public static boolean isArray(final Object obj) {
  1246         return (obj instanceof ScriptObject) && ((ScriptObject)obj).isArray();
  1249     /**
  1250      * Check if this ScriptObject is an array
  1251      * @return true if array
  1252      */
  1253     public final boolean isArray() {
  1254         return (flags & IS_ARRAY) != 0;
  1257     /**
  1258      * Flag this ScriptObject as being an array
  1259      */
  1260     public final void setIsArray() {
  1261         flags |= IS_ARRAY;
  1264     /**
  1265      * Check if this ScriptObject is an {@code arguments} vector
  1266      * @return true if arguments vector
  1267      */
  1268     public final boolean isArguments() {
  1269         return (flags & IS_ARGUMENTS) != 0;
  1272     /**
  1273      * Flag this ScriptObject as being an {@code arguments} vector
  1274      */
  1275     public final void setIsArguments() {
  1276         flags |= IS_ARGUMENTS;
  1279     /**
  1280      * Check if this object is a prototype
  1282      * @return {@code true} if is prototype
  1283      */
  1284     public final boolean isPrototype() {
  1285         return (flags & IS_PROTOTYPE) != 0;
  1288     /**
  1289      * Flag this object as having a prototype.
  1290      */
  1291     public final void setIsPrototype() {
  1292         if (proto != null && !isPrototype()) {
  1293             proto.addPropertyListener(this);
  1295         flags |= IS_PROTOTYPE;
  1298     /**
  1299      * Check if this object has non-writable length property
  1301      * @return {@code true} if 'length' property is non-writable
  1302      */
  1303     public final boolean isLengthNotWritable() {
  1304         return (flags & IS_LENGTH_NOT_WRITABLE) != 0;
  1307     /**
  1308      * Flag this object as having non-writable length property
  1309      */
  1310     public void setIsLengthNotWritable() {
  1311         flags |= IS_LENGTH_NOT_WRITABLE;
  1314     /**
  1315      * Get the {@link ArrayData} for this ScriptObject if it is an array
  1316      * @return array data
  1317      */
  1318     public final ArrayData getArray() {
  1319         return arrayData;
  1322     /**
  1323      * Set the {@link ArrayData} for this ScriptObject if it is to be an array
  1324      * @param arrayData the array data
  1325      */
  1326     public final void setArray(final ArrayData arrayData) {
  1327         this.arrayData = arrayData;
  1330     /**
  1331      * Check if this ScriptObject is extensible
  1332      * @return true if extensible
  1333      */
  1334     public boolean isExtensible() {
  1335         return getMap().isExtensible();
  1338     /**
  1339      * ECMAScript 15.2.3.8 - seal implementation
  1340      * @return the sealed ScriptObject
  1341      */
  1342     public ScriptObject seal() {
  1343         PropertyMap oldMap = getMap();
  1345         while (true) {
  1346             final PropertyMap newMap = getMap().seal();
  1348             if (!compareAndSetMap(oldMap, newMap)) {
  1349                 oldMap = getMap();
  1350             } else {
  1351                 setArray(ArrayData.seal(getArray()));
  1352                 return this;
  1357     /**
  1358      * Check whether this ScriptObject is sealed
  1359      * @return true if sealed
  1360      */
  1361     public boolean isSealed() {
  1362         return getMap().isSealed();
  1365     /**
  1366      * ECMA 15.2.39 - freeze implementation. Freeze this ScriptObject
  1367      * @return the frozen ScriptObject
  1368      */
  1369     public ScriptObject freeze() {
  1370         PropertyMap oldMap = getMap();
  1372         while (true) {
  1373             final PropertyMap newMap = getMap().freeze();
  1375             if (!compareAndSetMap(oldMap, newMap)) {
  1376                 oldMap = getMap();
  1377             } else {
  1378                 setArray(ArrayData.freeze(getArray()));
  1379                 return this;
  1384     /**
  1385      * Check whether this ScriptObject is frozen
  1386      * @return true if frozen
  1387      */
  1388     public boolean isFrozen() {
  1389         return getMap().isFrozen();
  1393     /**
  1394      * Flag this ScriptObject as scope
  1395      */
  1396     public final void setIsScope() {
  1397         if (Context.DEBUG) {
  1398             scopeCount++;
  1400         flags |= IS_SCOPE;
  1403     /**
  1404      * Check whether this ScriptObject is scope
  1405      * @return true if scope
  1406      */
  1407     public final boolean isScope() {
  1408         return (flags & IS_SCOPE) != 0;
  1411     /**
  1412      * Clears the properties from a ScriptObject
  1413      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1414      */
  1415     public void clear() {
  1416         final boolean strict = isStrictContext();
  1417         final Iterator<String> iter = propertyIterator();
  1418         while (iter.hasNext()) {
  1419             delete(iter.next(), strict);
  1423     /**
  1424      * Checks if a property with a given key is present in a ScriptObject
  1425      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1427      * @param key the key to check for
  1428      * @return true if a property with the given key exists, false otherwise
  1429      */
  1430     public boolean containsKey(final Object key) {
  1431         return has(key);
  1434     /**
  1435      * Checks if a property with a given value is present in a ScriptObject
  1436      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1438      * @param value value to check for
  1439      * @return true if a property with the given value exists, false otherwise
  1440      */
  1441     public boolean containsValue(final Object value) {
  1442         final Iterator<Object> iter = valueIterator();
  1443         while (iter.hasNext()) {
  1444             if (iter.next().equals(value)) {
  1445                 return true;
  1448         return false;
  1451     /**
  1452      * Returns the set of {@literal <property, value>} entries that make up this
  1453      * ScriptObject's properties
  1454      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1456      * @return an entry set of all the properties in this object
  1457      */
  1458     public Set<Map.Entry<Object, Object>> entrySet() {
  1459         final Iterator<String> iter = propertyIterator();
  1460         final Set<Map.Entry<Object, Object>> entries = new HashSet<>();
  1461         while (iter.hasNext()) {
  1462             final Object key = iter.next();
  1463             entries.add(new AbstractMap.SimpleImmutableEntry<>(key, get(key)));
  1465         return Collections.unmodifiableSet(entries);
  1468     /**
  1469      * Check whether a ScriptObject contains no properties
  1470      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1472      * @return true if object has no properties
  1473      */
  1474     public boolean isEmpty() {
  1475         return !propertyIterator().hasNext();
  1478     /**
  1479      * Return the set of keys (property names) for all properties
  1480      * in this ScriptObject
  1481      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1483      * @return keySet of this ScriptObject
  1484      */
  1485     public Set<Object> keySet() {
  1486         final Iterator<String> iter = propertyIterator();
  1487         final Set<Object> keySet = new HashSet<>();
  1488         while (iter.hasNext()) {
  1489             keySet.add(iter.next());
  1491         return Collections.unmodifiableSet(keySet);
  1494     /**
  1495      * Put a property in the ScriptObject
  1496      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1498      * @param key property key
  1499      * @param value property value
  1500      * @return oldValue if property with same key existed already
  1501      */
  1502     public Object put(final Object key, final Object value) {
  1503         final Object oldValue = get(key);
  1504         set(key, value, isStrictContext());
  1505         return oldValue;
  1508     /**
  1509      * Put several properties in the ScriptObject given a mapping
  1510      * of their keys to their values
  1511      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1513      * @param otherMap a {@literal <key,value>} map of properties to add
  1514      */
  1515     public void putAll(final Map<?, ?> otherMap) {
  1516         final boolean strict = isStrictContext();
  1517         for (final Map.Entry<?, ?> entry : otherMap.entrySet()) {
  1518             set(entry.getKey(), entry.getValue(), strict);
  1522     /**
  1523      * Remove a property from the ScriptObject.
  1524      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1526      * @param key the key of the property
  1527      * @return the oldValue of the removed property
  1528      */
  1529     public Object remove(final Object key) {
  1530         final Object oldValue = get(key);
  1531         delete(key, isStrictContext());
  1532         return oldValue;
  1535     /**
  1536      * Delete a property from the ScriptObject.
  1537      * (to help ScriptObjectMirror implementation)
  1539      * @param key the key of the property
  1540      * @return if the delete was successful or not
  1541      */
  1542     public boolean delete(final Object key) {
  1543         return delete(key, isStrictContext());
  1546     /**
  1547      * Return the size of the ScriptObject - i.e. the number of properties
  1548      * it contains
  1549      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1551      * @return number of properties in ScriptObject
  1552      */
  1553     public int size() {
  1554         int n = 0;
  1555         for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) {
  1556             n++;
  1558         return n;
  1561     /**
  1562      * Return the values of the properties in the ScriptObject
  1563      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1565      * @return collection of values for the properties in this ScriptObject
  1566      */
  1567     public Collection<Object> values() {
  1568         final List<Object>     values = new ArrayList<>(size());
  1569         final Iterator<Object> iter   = valueIterator();
  1570         while (iter.hasNext()) {
  1571             values.add(iter.next());
  1573         return Collections.unmodifiableList(values);
  1576     /**
  1577      * Lookup method that, given a CallSiteDescriptor, looks up the target
  1578      * MethodHandle and creates a GuardedInvocation
  1579      * with the appropriate guard(s).
  1581      * @param desc call site descriptor
  1582      * @param request the link request
  1584      * @return GuardedInvocation for the callsite
  1585      */
  1586     public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
  1587         final int c = desc.getNameTokenCount();
  1588         // JavaScript is "immune" to all currently defined Dynalink composite operation - getProp is the same as getElem
  1589         // is the same as getMethod as JavaScript objects have a single namespace for all three. Therefore, we don't
  1590         // care about them, and just link to whatever is the first operation.
  1591         final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
  1592         // NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself
  1593         // emits "dyn:getProp:identifier" for "<expr>.<identifier>" and "dyn:getElem" for "<expr>[<expr>]", but we are
  1594         // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the
  1595         // operation has an associated name or not.
  1596         switch (operator) {
  1597         case "getProp":
  1598         case "getElem":
  1599         case "getMethod":
  1600             return c > 2 ? findGetMethod(desc, request, operator) : findGetIndexMethod(desc, request);
  1601         case "setProp":
  1602         case "setElem":
  1603             return c > 2 ? findSetMethod(desc, request) : findSetIndexMethod(desc);
  1604         case "call":
  1605             return findCallMethod(desc, request);
  1606         case "new":
  1607             return findNewMethod(desc);
  1608         case "callMethod":
  1609             return findCallMethodMethod(desc, request);
  1610         default:
  1611             return null;
  1615     /**
  1616      * Find the appropriate New method for an invoke dynamic call.
  1618      * @param desc The invoke dynamic call site descriptor.
  1620      * @return GuardedInvocation to be invoked at call site.
  1621      */
  1622     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
  1623         return notAFunction();
  1626     /**
  1627      * Find the appropriate CALL method for an invoke dynamic call.
  1628      * This generates "not a function" always
  1630      * @param desc    the call site descriptor.
  1631      * @param request the link request
  1633      * @return GuardedInvocation to be invoed at call site.
  1634      */
  1635     protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1636         return notAFunction();
  1639     private GuardedInvocation notAFunction() {
  1640         throw typeError("not.a.function", ScriptRuntime.safeToString(this));
  1643     /**
  1644      * Find an implementation for a "dyn:callMethod" operation. Note that Nashorn internally never uses
  1645      * "dyn:callMethod", but instead always emits two call sites in bytecode, one for "dyn:getMethod", and then another
  1646      * one for "dyn:call". Explicit support for "dyn:callMethod" is provided for the benefit of potential external
  1647      * callers. The implementation itself actually folds a "dyn:getMethod" method handle into a "dyn:call" method handle.
  1649      * @param desc    the call site descriptor.
  1650      * @param request the link request
  1652      * @return GuardedInvocation to be invoked at call site.
  1653      */
  1654     protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1655         // R(P0, P1, ...)
  1656         final MethodType callType = desc.getMethodType();
  1657         // use type Object(P0) for the getter
  1658         final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0)));
  1659         final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod");
  1661         // Object(P0) => Object(P0, P1, ...)
  1662         final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount()));
  1663         // R(Object, P0, P1, ...)
  1664         final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
  1665         // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
  1666         return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
  1669     /**
  1670      * Find the appropriate GET method for an invoke dynamic call.
  1672      * @param desc     the call site descriptor
  1673      * @param request  the link request
  1674      * @param operator operator for get: getProp, getMethod, getElem etc
  1676      * @return GuardedInvocation to be invoked at call site.
  1677      */
  1678     protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
  1679         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  1680         final FindProperty find = findProperty(name, true);
  1682         MethodHandle methodHandle;
  1684         if (find == null) {
  1685             if ("getProp".equals(operator)) {
  1686                 return noSuchProperty(desc, request);
  1687             } else if ("getMethod".equals(operator)) {
  1688                 return noSuchMethod(desc, request);
  1689             } else if ("getElem".equals(operator)) {
  1690                 return createEmptyGetter(desc, name);
  1692             throw new AssertionError(); // never invoked with any other operation
  1695         if (request.isCallSiteUnstable()) {
  1696             return findMegaMorphicGetMethod(desc, name);
  1699         final Class<?> returnType = desc.getMethodType().returnType();
  1700         final Property property = find.getProperty();
  1701         methodHandle = find.getGetter(returnType);
  1703         // getMap() is fine as we have the prototype switchpoint depending on where the property was found
  1704         final MethodHandle guard = NashornGuards.getMapGuard(getMap());
  1706         if (methodHandle != null) {
  1707             assert methodHandle.type().returnType().equals(returnType);
  1708             if (find.isSelf()) {
  1709                 return new GuardedInvocation(methodHandle, ObjectClassGenerator.OBJECT_FIELDS_ONLY &&
  1710                         NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType() ? null : guard);
  1713             final ScriptObject prototype = find.getOwner();
  1715             if (!property.hasGetterFunction()) {
  1716                 methodHandle = bindTo(methodHandle, prototype);
  1718             return new GuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(proto, name), guard);
  1721         assert !NashornCallSiteDescriptor.isFastScope(desc);
  1722         return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard);
  1725     private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) {
  1726         final MethodType mhType = desc.getMethodType().insertParameterTypes(1, Object.class);
  1727         final GuardedInvocation inv = findGetIndexMethod(mhType);
  1729         return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
  1732     /**
  1733      * Find the appropriate GETINDEX method for an invoke dynamic call.
  1735      * @param desc    the call site descriptor
  1736      * @param request the link request
  1738      * @return GuardedInvocation to be invoked at call site.
  1739      */
  1740     protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1741         return findGetIndexMethod(desc.getMethodType());
  1744     /**
  1745      * Find the appropriate GETINDEX method for an invoke dynamic call.
  1747      * @param callType the call site method type
  1748      * @return GuardedInvocation to be invoked at call site.
  1749      */
  1750     private static GuardedInvocation findGetIndexMethod(final MethodType callType) {
  1751         final Class<?> returnClass = callType.returnType();
  1752         final Class<?> keyClass    = callType.parameterType(1);
  1754         String name = "get";
  1755         if (returnClass.isPrimitive()) {
  1756             //turn e.g. get with a double into getDouble
  1757             final String returnTypeName = returnClass.getName();
  1758             name += Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
  1761         return new GuardedInvocation(findOwnMH(name, returnClass, keyClass), getScriptObjectGuard(callType));
  1764     private static MethodHandle getScriptObjectGuard(final MethodType type) {
  1765         return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard();
  1768     /**
  1769      * Find the appropriate SET method for an invoke dynamic call.
  1771      * @param desc    the call site descriptor
  1772      * @param request the link request
  1774      * @return GuardedInvocation to be invoked at call site.
  1775      */
  1776     protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1777         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  1778         if(request.isCallSiteUnstable()) {
  1779             return findMegaMorphicSetMethod(desc, name);
  1782         final boolean scope = isScope();
  1783         /*
  1784          * If doing property set on a scope object, we should stop proto search on the first
  1785          * non-scope object. Without this, for example, when assigning "toString" on global scope,
  1786          * we'll end up assigning it on it's proto - which is Object.prototype.toString !!
  1788          * toString = function() { print("global toString"); } // don't affect Object.prototype.toString
  1789          */
  1790         FindProperty find = findProperty(name, true, scope, this);
  1791         // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors.
  1792         if (!scope && find != null && find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
  1793             // We should still check if inherited data property is not writable
  1794             if (isExtensible() && !find.getProperty().isWritable()) {
  1795                 return createEmptySetMethod(desc, "property.not.writable", false);
  1797             // Otherwise, forget the found property
  1798             find = null;
  1801         if (find != null) {
  1802             if(!find.getProperty().isWritable()) {
  1803                 // Existing, non-writable property
  1804                 return createEmptySetMethod(desc, "property.not.writable", true);
  1806         } else if (!isExtensible()) {
  1807             // Non-existing property on a non-extensible object
  1808             return createEmptySetMethod(desc, "object.non.extensible", false);
  1811         return new SetMethodCreator(this, find, desc).createGuardedInvocation();
  1814     private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, String strictErrorMessage, boolean canBeFastScope) {
  1815         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  1816         if (NashornCallSiteDescriptor.isStrict(desc)) {
  1817                throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString((this)));
  1819            assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
  1820            final PropertyMap myMap = getMap();
  1821            return new GuardedInvocation(Lookup.EMPTY_SETTER, myMap.getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(myMap));
  1824     @SuppressWarnings("unused")
  1825     private static void setField(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final MethodHandle setter, final Object self, final Object value) throws Throwable {
  1826         final ScriptObject obj = (ScriptObject)self;
  1827         final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
  1828         if (!obj.isExtensible()) {
  1829             throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
  1830         } else if (obj.compareAndSetMap(oldMap, newMap)) {
  1831             setter.invokeExact(self, value);
  1832         } else {
  1833             obj.set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
  1837     @SuppressWarnings("unused")
  1838     private static void setSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
  1839         final ScriptObject obj = (ScriptObject)self;
  1840         if (obj.trySetSpill(desc, oldMap, newMap, value)) {
  1841             obj.spill[index] = value;
  1845     private boolean trySetSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final Object value) {
  1846         final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
  1847         if (!isExtensible() && isStrict) {
  1848             throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(this));
  1849         } else if (compareAndSetMap(oldMap, newMap)) {
  1850             return true;
  1851         } else {
  1852             set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
  1853             return false;
  1857     @SuppressWarnings("unused")
  1858     private static void setSpillWithNew(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
  1859         final ScriptObject obj      = (ScriptObject)self;
  1860         final boolean      isStrict = NashornCallSiteDescriptor.isStrict(desc);
  1862         if (!obj.isExtensible()) {
  1863             if (isStrict) {
  1864                 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
  1866         } else if (obj.compareAndSetMap(oldMap, newMap)) {
  1867             obj.spill = new Object[SPILL_RATE];
  1868             obj.spill[index] = value;
  1869         } else {
  1870             obj.set(desc.getNameToken(2), value, isStrict);
  1874     @SuppressWarnings("unused")
  1875     private static void setSpillWithGrow(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final int newLength, final Object self, final Object value) {
  1876         final ScriptObject obj      = (ScriptObject)self;
  1877         final boolean      isStrict = NashornCallSiteDescriptor.isStrict(desc);
  1879         if (!obj.isExtensible()) {
  1880             if (isStrict) {
  1881                 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
  1883         } else if (obj.compareAndSetMap(oldMap, newMap)) {
  1884             final int oldLength = obj.spill.length;
  1885             final Object[] newSpill = new Object[newLength];
  1886             System.arraycopy(obj.spill, 0, newSpill, 0, oldLength);
  1887             obj.spill = newSpill;
  1888             obj.spill[index] = value;
  1889         } else {
  1890             obj.set(desc.getNameToken(2), value, isStrict);
  1894     private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
  1895         final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
  1896         final GuardedInvocation inv = findSetIndexMethod(type, NashornCallSiteDescriptor.isStrict(desc));
  1897         return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
  1900     private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc) { // array, index, value
  1901         return findSetIndexMethod(desc.getMethodType(), NashornCallSiteDescriptor.isStrict(desc));
  1904     /**
  1905      * Find the appropriate SETINDEX method for an invoke dynamic call.
  1907      * @param callType the method type at the call site
  1908      * @param isStrict are we in strict mode?
  1910      * @return GuardedInvocation to be invoked at call site.
  1911      */
  1912     private static GuardedInvocation findSetIndexMethod(final MethodType callType, final boolean isStrict) {
  1913         assert callType.parameterCount() == 3;
  1915         final Class<?>   keyClass   = callType.parameterType(1);
  1916         final Class<?>   valueClass = callType.parameterType(2);
  1918         MethodHandle methodHandle = findOwnMH("set", void.class, keyClass, valueClass, boolean.class);
  1919         methodHandle = MH.insertArguments(methodHandle, 3, isStrict);
  1921         return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType));
  1924     /**
  1925      * Fall back if a function property is not found.
  1926      * @param desc The call site descriptor
  1927      * @param request the link request
  1928      * @return GuardedInvocation to be invoked at call site.
  1929      */
  1930     public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1931         final String       name      = desc.getNameToken(2);
  1932         final FindProperty find      = findProperty(NO_SUCH_METHOD_NAME, true);
  1933         final boolean      scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc);
  1935         if (find == null) {
  1936             return noSuchProperty(desc, request);
  1939         final ScriptFunction func = (ScriptFunction)getObjectValue(find);
  1940         final Object thiz = scopeCall && func.isStrict() ? ScriptRuntime.UNDEFINED : this;
  1941         // TODO: It'd be awesome if we could bind "name" without binding "this".
  1942         return new GuardedInvocation(MH.dropArguments(MH.constant(ScriptFunction.class,
  1943                 func.makeBoundFunction(thiz, new Object[] { name })), 0, Object.class),
  1944                 null, NashornGuards.getMapGuard(getMap()));
  1947     /**
  1948      * Fall back if a property is not found.
  1949      * @param desc the call site descriptor.
  1950      * @param request the link request
  1951      * @return GuardedInvocation to be invoked at call site.
  1952      */
  1953     public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
  1954         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  1955         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
  1956         final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
  1958         if (find != null) {
  1959             final ScriptFunction func = (ScriptFunction)getObjectValue(find);
  1960             MethodHandle methodHandle = getCallMethodHandle(func, desc.getMethodType(), name);
  1962             if (methodHandle != null) {
  1963                 if (scopeAccess && func.isStrict()) {
  1964                     methodHandle = bindTo(methodHandle, UNDEFINED);
  1966                 return new GuardedInvocation(methodHandle,
  1967                         find.isInherited()? getMap().getProtoGetSwitchPoint(proto, NO_SUCH_PROPERTY_NAME) : null,
  1968                         getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func));
  1972         if (scopeAccess) {
  1973             throw referenceError("not.defined", name);
  1976         return createEmptyGetter(desc, name);
  1978     /**
  1979      * Invoke fall back if a property is not found.
  1980      * @param name Name of property.
  1981      * @return Result from call.
  1982      */
  1983     private Object invokeNoSuchProperty(final String name) {
  1984         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
  1986         if (find != null) {
  1987             final Object func = getObjectValue(find);
  1989             if (func instanceof ScriptFunction) {
  1990                 return ScriptRuntime.apply((ScriptFunction)func, this, name);
  1994         return UNDEFINED;
  1997     private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
  1998         return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(getMap()));
  2001     private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
  2002         protected T[] values;
  2003         protected final ScriptObject object;
  2004         private int index;
  2006         ScriptObjectIterator(final ScriptObject object) {
  2007             this.object = object;
  2010         protected abstract void init();
  2012         @Override
  2013         public boolean hasNext() {
  2014             if (values == null) {
  2015                 init();
  2017             return index < values.length;
  2020         @Override
  2021         public T next() {
  2022             if (values == null) {
  2023                 init();
  2025             return values[index++];
  2028         @Override
  2029         public void remove() {
  2030             throw new UnsupportedOperationException();
  2034     private static class KeyIterator extends ScriptObjectIterator<String> {
  2035         KeyIterator(final ScriptObject object) {
  2036             super(object);
  2039         @Override
  2040         protected void init() {
  2041             final Set<String> keys = new LinkedHashSet<>();
  2042             for (ScriptObject self = object; self != null; self = self.getProto()) {
  2043                 keys.addAll(Arrays.asList(self.getOwnKeys(false)));
  2045             this.values = keys.toArray(new String[keys.size()]);
  2049     private static class ValueIterator extends ScriptObjectIterator<Object> {
  2050         ValueIterator(final ScriptObject object) {
  2051             super(object);
  2054         @Override
  2055         protected void init() {
  2056             final ArrayList<Object> valueList = new ArrayList<>();
  2057             for (ScriptObject self = object; self != null; self = self.getProto()) {
  2058                 for (final String key : self.getOwnKeys(false)) {
  2059                     valueList.add(self.get(key));
  2062             this.values = valueList.toArray(new Object[valueList.size()]);
  2066     /**
  2067      * Add a spill property for the given key.
  2068      * @param key           Property key.
  2069      * @param propertyFlags Property flags.
  2070      * @return Added property.
  2071      */
  2072     private Property addSpillProperty(final String key, final int propertyFlags) {
  2073         int fieldCount   = getMap().getFieldCount();
  2074         int fieldMaximum = getMap().getFieldMaximum();
  2075         Property property;
  2077         if (fieldCount < fieldMaximum) {
  2078             property = new AccessorProperty(key, propertyFlags & ~Property.IS_SPILL, getClass(), fieldCount);
  2079             notifyPropertyAdded(this, property);
  2080             property = addOwnProperty(property);
  2081         } else {
  2082             int i = getMap().getSpillLength();
  2083             property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i);
  2084             notifyPropertyAdded(this, property);
  2085             property = addOwnProperty(property);
  2086             i = property.getSlot();
  2088             final int newLength = (i + SPILL_RATE) / SPILL_RATE * SPILL_RATE;
  2090             if (spill == null || newLength > spill.length) {
  2091                 final Object[] newSpill = new Object[newLength];
  2093                 if (spill != null) {
  2094                     System.arraycopy(spill, 0, newSpill, 0, spill.length);
  2097                 spill = newSpill;
  2101         return property;
  2105     /**
  2106      * Add a spill entry for the given key.
  2107      * @param key Property key.
  2108      * @return Setter method handle.
  2109      */
  2110     MethodHandle addSpill(final String key) {
  2111         final Property spillProperty = addSpillProperty(key, 0);
  2112         final Class<?> type = Object.class;
  2113         return spillProperty.getSetter(type, getMap()); //TODO specfields
  2116     /**
  2117      * Make sure arguments are paired correctly, with respect to more parameters than declared,
  2118      * fewer parameters than declared and other things that JavaScript allows. This might involve
  2119      * creating collectors.
  2121      * @param methodHandle method handle for invoke
  2122      * @param callType     type of the call
  2124      * @return method handle with adjusted arguments
  2125      */
  2126     protected static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType) {
  2127         return pairArguments(methodHandle, callType, null);
  2130     /**
  2131      * Make sure arguments are paired correctly, with respect to more parameters than declared,
  2132      * fewer parameters than declared and other things that JavaScript allows. This might involve
  2133      * creating collectors.
  2135      * Make sure arguments are paired correctly.
  2136      * @param methodHandle MethodHandle to adjust.
  2137      * @param callType     MethodType of the call site.
  2138      * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the
  2139      * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a
  2140      * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites
  2141      * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters.
  2143      * @return method handle with adjusted arguments
  2144      */
  2145     public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) {
  2147         final MethodType methodType = methodHandle.type();
  2148         if (methodType.equals(callType)) {
  2149             return methodHandle;
  2152         final int parameterCount = methodType.parameterCount();
  2153         final int callCount      = callType.parameterCount();
  2155         final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
  2156         final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : (callCount > 0 &&
  2157                 callType.parameterType(callCount - 1).isArray());
  2159         if (callCount < parameterCount) {
  2160             final int      missingArgs = parameterCount - callCount;
  2161             final Object[] fillers     = new Object[missingArgs];
  2163             Arrays.fill(fillers, UNDEFINED);
  2165             if (isCalleeVarArg) {
  2166                 fillers[missingArgs - 1] = new Object[0];
  2169             return MH.insertArguments(
  2170                 methodHandle,
  2171                 parameterCount - missingArgs,
  2172                 fillers);
  2175         if (isCalleeVarArg) {
  2176             return isCallerVarArg ?
  2177                 methodHandle :
  2178                 MH.asCollector(methodHandle, Object[].class, callCount - parameterCount + 1);
  2181         if (isCallerVarArg) {
  2182             final int spreadArgs = parameterCount - callCount + 1;
  2183             return MH.filterArguments(
  2184                 MH.asSpreader(
  2185                     methodHandle,
  2186                     Object[].class,
  2187                     spreadArgs),
  2188                 callCount - 1,
  2189                 MH.insertArguments(
  2190                     TRUNCATINGFILTER,
  2191                     0,
  2192                     spreadArgs)
  2193                 );
  2196         if (callCount > parameterCount) {
  2197             final int discardedArgs = callCount - parameterCount;
  2199             final Class<?>[] discards = new Class<?>[discardedArgs];
  2200             Arrays.fill(discards, Object.class);
  2202             return MH.dropArguments(methodHandle, callCount - discardedArgs, discards);
  2205         return methodHandle;
  2208     @SuppressWarnings("unused")
  2209     private static Object[] truncatingFilter(final int n, final Object[] array) {
  2210         final int length = array == null ? 0 : array.length;
  2211         if (n == length) {
  2212             return array == null ? new Object[0] : array;
  2215         final Object[] newArray = new Object[n];
  2217         if (array != null) {
  2218             for (int i = 0; i < n && i < length; i++) {
  2219                 newArray[i] = array[i];
  2223         if (length < n) {
  2224             final Object fill = UNDEFINED;
  2226             for (int i = length; i < n; i++) {
  2227                 newArray[i] = fill;
  2231         return newArray;
  2234     /**
  2235       * Numeric length setter for length property
  2237       * @param newLength new length to set
  2238       */
  2239     public final void setLength(final long newLength) {
  2240        final long arrayLength = getArray().length();
  2241        if (newLength == arrayLength) {
  2242            return;
  2245        final boolean isStrict = isStrictContext();
  2247        if (newLength > arrayLength) {
  2248            setArray(getArray().ensure(newLength - 1));
  2249             if (getArray().canDelete(arrayLength, (newLength - 1), isStrict)) {
  2250                setArray(getArray().delete(arrayLength, (newLength - 1)));
  2252            return;
  2255        if (newLength < arrayLength) {
  2256            setArray(getArray().shrink(newLength));
  2257            getArray().setLength(newLength);
  2261     private int getInt(final int index, final String key) {
  2262         if (ArrayIndex.isValidArrayIndex(index)) {
  2263              for (ScriptObject object = this; ; ) {
  2264                 final FindProperty find = object.findProperty(key, false, false, this);
  2266                 if (find != null) {
  2267                     return getIntValue(find);
  2270                 if ((object = object.getProto()) == null) {
  2271                     break;
  2274                 final ArrayData array = object.getArray();
  2276                 if (array.has(index)) {
  2277                     return array.getInt(index);
  2280         } else {
  2281             final FindProperty find = findProperty(key, true);
  2283             if (find != null) {
  2284                 return getIntValue(find);
  2288         return JSType.toInt32(invokeNoSuchProperty(key));
  2291     @Override
  2292     public int getInt(final Object key) {
  2293         final int index = ArrayIndex.getArrayIndex(key);
  2294         final ArrayData array = getArray();
  2296         if (array.has(index)) {
  2297             return array.getInt(index);
  2300         return getInt(index, convertKey(key));
  2303     @Override
  2304     public int getInt(final double key) {
  2305         final int index = ArrayIndex.getArrayIndex(key);
  2306         final ArrayData array = getArray();
  2308         if (array.has(index)) {
  2309             return array.getInt(index);
  2312         return getInt(index, convertKey(key));
  2315     @Override
  2316     public int getInt(final long key) {
  2317         final int index = ArrayIndex.getArrayIndex(key);
  2318         final ArrayData array = getArray();
  2320         if (array.has(index)) {
  2321             return array.getInt(index);
  2324         return getInt(index, convertKey(key));
  2327     @Override
  2328     public int getInt(final int key) {
  2329         final ArrayData array = getArray();
  2331         if (array.has(key)) {
  2332             return array.getInt(key);
  2335         return getInt(key, convertKey(key));
  2338     private long getLong(final int index, final String key) {
  2339         if (ArrayIndex.isValidArrayIndex(index)) {
  2340             for (ScriptObject object = this; ; ) {
  2341                 final FindProperty find = object.findProperty(key, false, false, this);
  2343                 if (find != null) {
  2344                     return getLongValue(find);
  2347                 if ((object = object.getProto()) == null) {
  2348                     break;
  2351                 final ArrayData array = object.getArray();
  2353                 if (array.has(index)) {
  2354                     return array.getLong(index);
  2357         } else {
  2358             final FindProperty find = findProperty(key, true);
  2360             if (find != null) {
  2361                 return getLongValue(find);
  2365         return JSType.toLong(invokeNoSuchProperty(key));
  2368     @Override
  2369     public long getLong(final Object key) {
  2370         final int index = ArrayIndex.getArrayIndex(key);
  2371         final ArrayData array = getArray();
  2373         if (array.has(index)) {
  2374             return array.getLong(index);
  2377         return getLong(index, convertKey(key));
  2380     @Override
  2381     public long getLong(final double key) {
  2382         final int index = ArrayIndex.getArrayIndex(key);
  2383         final ArrayData array = getArray();
  2385         if (array.has(index)) {
  2386             return array.getLong(index);
  2389         return getLong(index, convertKey(key));
  2392     @Override
  2393     public long getLong(final long key) {
  2394         final int index = ArrayIndex.getArrayIndex(key);
  2395         final ArrayData array = getArray();
  2397         if (array.has(index)) {
  2398             return array.getLong(index);
  2401         return getLong(index, convertKey(key));
  2404     @Override
  2405     public long getLong(final int key) {
  2406         final ArrayData array = getArray();
  2408         if (array.has(key)) {
  2409             return array.getLong(key);
  2412         return getLong(key, convertKey(key));
  2415     private double getDouble(final int index, final String key) {
  2416         if (ArrayIndex.isValidArrayIndex(index)) {
  2417             for (ScriptObject object = this; ; ) {
  2418                 final FindProperty find = object.findProperty(key, false, false, this);
  2420                 if (find != null) {
  2421                     return getDoubleValue(find);
  2424                 if ((object = object.getProto()) == null) {
  2425                     break;
  2428                 final ArrayData array = object.getArray();
  2430                 if (array.has(index)) {
  2431                     return array.getDouble(index);
  2434         } else {
  2435             final FindProperty find = findProperty(key, true);
  2437             if (find != null) {
  2438                 return getDoubleValue(find);
  2442         return JSType.toNumber(invokeNoSuchProperty(key));
  2445     @Override
  2446     public double getDouble(final Object key) {
  2447         final int index = ArrayIndex.getArrayIndex(key);
  2448         final ArrayData array = getArray();
  2450         if (array.has(index)) {
  2451             return array.getDouble(index);
  2454         return getDouble(index, convertKey(key));
  2457     @Override
  2458     public double getDouble(final double key) {
  2459         final int index = ArrayIndex.getArrayIndex(key);
  2460         final ArrayData array = getArray();
  2462         if (array.has(index)) {
  2463             return array.getDouble(index);
  2466         return getDouble(index, convertKey(key));
  2469     @Override
  2470     public double getDouble(final long key) {
  2471         final int index = ArrayIndex.getArrayIndex(key);
  2472         final ArrayData array = getArray();
  2474         if (array.has(index)) {
  2475             return array.getDouble(index);
  2478         return getDouble(index, convertKey(key));
  2481     @Override
  2482     public double getDouble(final int key) {
  2483         final ArrayData array = getArray();
  2485         if (array.has(key)) {
  2486             return array.getDouble(key);
  2489         return getDouble(key, convertKey(key));
  2492     private Object get(final int index, final String key) {
  2493         if (ArrayIndex.isValidArrayIndex(index)) {
  2494             for (ScriptObject object = this; ; ) {
  2495                 final FindProperty find = object.findProperty(key, false, false, this);
  2497                 if (find != null) {
  2498                     return getObjectValue(find);
  2501                 if ((object = object.getProto()) == null) {
  2502                     break;
  2505                 final ArrayData array = object.getArray();
  2507                 if (array.has(index)) {
  2508                     return array.getObject(index);
  2511         } else {
  2512             final FindProperty find = findProperty(key, true);
  2514             if (find != null) {
  2515                 return getObjectValue(find);
  2519         return invokeNoSuchProperty(key);
  2522     @Override
  2523     public Object get(final Object key) {
  2524         final int index = ArrayIndex.getArrayIndex(key);
  2525         final ArrayData array = getArray();
  2527         if (array.has(index)) {
  2528             return array.getObject(index);
  2531         return get(index, convertKey(key));
  2534     @Override
  2535     public Object get(final double key) {
  2536         final int index = ArrayIndex.getArrayIndex(key);
  2537         final ArrayData array = getArray();
  2539         if (array.has(index)) {
  2540             return array.getObject(index);
  2543         return get(index, convertKey(key));
  2546     @Override
  2547     public Object get(final long key) {
  2548         final int index = ArrayIndex.getArrayIndex(key);
  2549         final ArrayData array = getArray();
  2551         if (array.has(index)) {
  2552             return array.getObject(index);
  2555         return get(index, convertKey(key));
  2558     @Override
  2559     public Object get(final int key) {
  2560         final ArrayData array = getArray();
  2562         if (array.has(key)) {
  2563             return array.getObject(key);
  2566         return get(key, convertKey(key));
  2569     /**
  2570      * Handle when an array doesn't have a slot - possibly grow and/or convert array.
  2572      * @param index  key as index
  2573      * @param value  element value
  2574      * @param strict are we in strict mode
  2575      */
  2576     private void doesNotHave(final int index, final Object value, final boolean strict) {
  2577         final long oldLength = getArray().length();
  2578         final long longIndex = index & JSType.MAX_UINT;
  2580         if (!getArray().has(index)) {
  2581             final String key = convertKey(longIndex);
  2582             final FindProperty find = findProperty(key, true);
  2584             if (find != null) {
  2585                 setObject(find, strict, key, value);
  2586                 return;
  2590         if (longIndex >= oldLength) {
  2591             if (!isExtensible()) {
  2592                 if (strict) {
  2593                     throw typeError("object.non.extensible", JSType.toString(index), ScriptRuntime.safeToString(this));
  2595                 return;
  2597             setArray(getArray().ensure(longIndex));
  2600         if (value instanceof Integer) {
  2601             setArray(getArray().set(index, (int)value, strict));
  2602         } else if (value instanceof Long) {
  2603             setArray(getArray().set(index, (long)value, strict));
  2604         } else if (value instanceof Double) {
  2605             setArray(getArray().set(index, (double)value, strict));
  2606         } else {
  2607             setArray(getArray().set(index, value, strict));
  2610         if (longIndex > oldLength) {
  2611             ArrayData array = getArray();
  2613             if (array.canDelete(oldLength, (longIndex - 1), strict)) {
  2614                 array = array.delete(oldLength, (longIndex - 1));
  2617             setArray(array);
  2621     /**
  2622      * This is the most generic of all Object setters. Most of the others use this in some form.
  2623      * TODO: should be further specialized
  2625      * @param find    found property
  2626      * @param strict  are we in strict mode
  2627      * @param key     property key
  2628      * @param value   property value
  2629      */
  2630     public final void setObject(final FindProperty find, final boolean strict, final String key, final Object value) {
  2631         FindProperty f = find;
  2633         if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) {
  2634             f = null;
  2637         if (f != null) {
  2638             if (!f.getProperty().isWritable()) {
  2639                 if (strict) {
  2640                     throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this));
  2643                 return;
  2646             f.setObjectValue(value, strict);
  2648         } else if (!isExtensible()) {
  2649             if (strict) {
  2650                 throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
  2652         } else {
  2653             spill(key, value);
  2657     private void spill(final String key, final Object value) {
  2658         addSpillProperty(key, 0).setObjectValue(this, this, value, false);
  2662     @Override
  2663     public void set(final Object key, final int value, final boolean strict) {
  2664         final int index = ArrayIndex.getArrayIndex(key);
  2666         if (ArrayIndex.isValidArrayIndex(index)) {
  2667             if (getArray().has(index)) {
  2668                 setArray(getArray().set(index, value, strict));
  2669             } else {
  2670                 doesNotHave(index, value, strict);
  2673             return;
  2676         set(key, JSType.toObject(value), strict);
  2679     @Override
  2680     public void set(final Object key, final long value, final boolean strict) {
  2681         final int index = ArrayIndex.getArrayIndex(key);
  2683         if (ArrayIndex.isValidArrayIndex(index)) {
  2684             if (getArray().has(index)) {
  2685                 setArray(getArray().set(index, value, strict));
  2686             } else {
  2687                 doesNotHave(index, value, strict);
  2690             return;
  2693         set(key, JSType.toObject(value), strict);
  2696     @Override
  2697     public void set(final Object key, final double value, final boolean strict) {
  2698         final int index = ArrayIndex.getArrayIndex(key);
  2700         if (ArrayIndex.isValidArrayIndex(index)) {
  2701             if (getArray().has(index)) {
  2702                 setArray(getArray().set(index, value, strict));
  2703             } else {
  2704                 doesNotHave(index, value, strict);
  2707             return;
  2710         set(key, JSType.toObject(value), strict);
  2713     @Override
  2714     public void set(final Object key, final Object value, final boolean strict) {
  2715         final int index = ArrayIndex.getArrayIndex(key);
  2717         if (ArrayIndex.isValidArrayIndex(index)) {
  2718             if (getArray().has(index)) {
  2719                 setArray(getArray().set(index, value, strict));
  2720             } else {
  2721                 doesNotHave(index, value, strict);
  2724             return;
  2727         final String       propName = convertKey(key);
  2728         final FindProperty find     = findProperty(propName, true);
  2730         setObject(find, strict, propName, value);
  2733     @Override
  2734     public void set(final double key, final int value, final boolean strict) {
  2735         final int index = ArrayIndex.getArrayIndex(key);
  2737         if (ArrayIndex.isValidArrayIndex(index)) {
  2738             if (getArray().has(index)) {
  2739                 setArray(getArray().set(index, value, strict));
  2740             } else {
  2741                 doesNotHave(index, value, strict);
  2744             return;
  2747         set(JSType.toObject(key), JSType.toObject(value), strict);
  2750     @Override
  2751     public void set(final double key, final long value, final boolean strict) {
  2752         final int index = ArrayIndex.getArrayIndex(key);
  2754         if (ArrayIndex.isValidArrayIndex(index)) {
  2755             if (getArray().has(index)) {
  2756                 setArray(getArray().set(index, value, strict));
  2757             } else {
  2758                 doesNotHave(index, value, strict);
  2761             return;
  2764         set(JSType.toObject(key), JSType.toObject(value), strict);
  2767     @Override
  2768     public void set(final double key, final double value, final boolean strict) {
  2769         final int index = ArrayIndex.getArrayIndex(key);
  2771         if (ArrayIndex.isValidArrayIndex(index)) {
  2772             if (getArray().has(index)) {
  2773                 setArray(getArray().set(index, value, strict));
  2774             } else {
  2775                 doesNotHave(index, value, strict);
  2778             return;
  2781         set(JSType.toObject(key), JSType.toObject(value), strict);
  2784     @Override
  2785     public void set(final double key, final Object value, final boolean strict) {
  2786         final int index = ArrayIndex.getArrayIndex(key);
  2788         if (ArrayIndex.isValidArrayIndex(index)) {
  2789             if (getArray().has(index)) {
  2790                 setArray(getArray().set(index, value, strict));
  2791             } else {
  2792                 doesNotHave(index, value, strict);
  2795             return;
  2798         set(JSType.toObject(key), value, strict);
  2801     @Override
  2802     public void set(final long key, final int value, final boolean strict) {
  2803         final int index = ArrayIndex.getArrayIndex(key);
  2805         if (ArrayIndex.isValidArrayIndex(index)) {
  2806             if (getArray().has(index)) {
  2807                 setArray(getArray().set(index, value, strict));
  2808             } else {
  2809                 doesNotHave(index, value, strict);
  2812             return;
  2815         set(JSType.toObject(key), JSType.toObject(value), strict);
  2818     @Override
  2819     public void set(final long key, final long value, final boolean strict) {
  2820         final int index = ArrayIndex.getArrayIndex(key);
  2822         if (ArrayIndex.isValidArrayIndex(index)) {
  2823             if (getArray().has(index)) {
  2824                 setArray(getArray().set(index, value, strict));
  2825             } else {
  2826                 doesNotHave(index, value, strict);
  2829             return;
  2832         set(JSType.toObject(key), JSType.toObject(value), strict);
  2835     @Override
  2836     public void set(final long key, final double value, final boolean strict) {
  2837         final int index = ArrayIndex.getArrayIndex(key);
  2839         if (ArrayIndex.isValidArrayIndex(index)) {
  2840             if (getArray().has(index)) {
  2841                 setArray(getArray().set(index, value, strict));
  2842             } else {
  2843                 doesNotHave(index, value, strict);
  2846             return;
  2849         set(JSType.toObject(key), JSType.toObject(value), strict);
  2852     @Override
  2853     public void set(final long key, final Object value, final boolean strict) {
  2854         final int index = ArrayIndex.getArrayIndex(key);
  2856         if (ArrayIndex.isValidArrayIndex(index)) {
  2857             if (getArray().has(index)) {
  2858                 setArray(getArray().set(index, value, strict));
  2859             } else {
  2860                 doesNotHave(index, value, strict);
  2863             return;
  2866         set(JSType.toObject(key), value, strict);
  2869     @Override
  2870     public void set(final int key, final int value, final boolean strict) {
  2871         final int index = ArrayIndex.getArrayIndex(key);
  2873         if (ArrayIndex.isValidArrayIndex(index)) {
  2874             if (getArray().has(index)) {
  2875                 setArray(getArray().set(index, value, strict));
  2876             } else {
  2877                 doesNotHave(index, value, strict);
  2880             return;
  2883         set(JSType.toObject(key), JSType.toObject(value), strict);
  2886     @Override
  2887     public void set(final int key, final long value, final boolean strict) {
  2888         final int index = ArrayIndex.getArrayIndex(key);
  2890         if (ArrayIndex.isValidArrayIndex(index)) {
  2891             if (getArray().has(index)) {
  2892                 setArray(getArray().set(index, value, strict));
  2893             } else {
  2894                 doesNotHave(index, value, strict);
  2897             return;
  2900         set(JSType.toObject(key), JSType.toObject(value), strict);
  2903     @Override
  2904     public void set(final int key, final double value, final boolean strict) {
  2905         final int index = ArrayIndex.getArrayIndex(key);
  2907         if (ArrayIndex.isValidArrayIndex(index)) {
  2908             if (getArray().has(index)) {
  2909                 setArray(getArray().set(index, value, strict));
  2910             } else {
  2911                 doesNotHave(index, value, strict);
  2914             return;
  2917         set(JSType.toObject(key), JSType.toObject(value), strict);
  2920     @Override
  2921     public void set(final int key, final Object value, final boolean strict) {
  2922         final int index = ArrayIndex.getArrayIndex(key);
  2924         if (ArrayIndex.isValidArrayIndex(index)) {
  2925             if (getArray().has(index)) {
  2926                 setArray(getArray().set(index, value, strict));
  2927             } else {
  2928                 doesNotHave(index, value, strict);
  2931             return;
  2934         set(JSType.toObject(key), value, strict);
  2937     @Override
  2938     public boolean has(final Object key) {
  2939         final int index = ArrayIndex.getArrayIndex(key);
  2941         if (ArrayIndex.isValidArrayIndex(index)) {
  2942             for (ScriptObject self = this; self != null; self = self.getProto()) {
  2943                 if (self.getArray().has(index)) {
  2944                     return true;
  2949         final FindProperty find = findProperty(convertKey(key), true);
  2951         return find != null;
  2954     @Override
  2955     public boolean has(final double key) {
  2956         final int index = ArrayIndex.getArrayIndex(key);
  2958         if (ArrayIndex.isValidArrayIndex(index)) {
  2959             for (ScriptObject self = this; self != null; self = self.getProto()) {
  2960                 if (self.getArray().has(index)) {
  2961                     return true;
  2966         final FindProperty find = findProperty(convertKey(key), true);
  2968         return find != null;
  2971     @Override
  2972     public boolean has(final long key) {
  2973         final int index = ArrayIndex.getArrayIndex(key);
  2975         if (ArrayIndex.isValidArrayIndex(index)) {
  2976             for (ScriptObject self = this; self != null; self = self.getProto()) {
  2977                 if (self.getArray().has(index)) {
  2978                     return true;
  2983         final FindProperty find = findProperty(convertKey(key), true);
  2985         return find != null;
  2988     @Override
  2989     public boolean has(final int key) {
  2990         final int index = ArrayIndex.getArrayIndex(key);
  2992         if (ArrayIndex.isValidArrayIndex(index)) {
  2993             for (ScriptObject self = this; self != null; self = self.getProto()) {
  2994                 if (self.getArray().has(index)) {
  2995                     return true;
  3000         final FindProperty find = findProperty(convertKey(key), true);
  3002         return find != null;
  3005     @Override
  3006     public boolean hasOwnProperty(final Object key) {
  3007         final int index = ArrayIndex.getArrayIndex(key);
  3009         if (getArray().has(index)) {
  3010             return true;
  3013         final FindProperty find = findProperty(convertKey(key), false);
  3015         return find != null;
  3018     @Override
  3019     public boolean hasOwnProperty(final int key) {
  3020         final int index = ArrayIndex.getArrayIndex(key);
  3022         if (getArray().has(index)) {
  3023             return true;
  3026         final FindProperty find = findProperty(convertKey(key), false);
  3028         return find != null;
  3031     @Override
  3032     public boolean hasOwnProperty(final long key) {
  3033         final int index = ArrayIndex.getArrayIndex(key);
  3035         if (getArray().has(index)) {
  3036             return true;
  3039         final FindProperty find = findProperty(convertKey(key), false);
  3041         return find != null;
  3044     @Override
  3045     public boolean hasOwnProperty(final double key) {
  3046         final int index = ArrayIndex.getArrayIndex(key);
  3048         if (getArray().has(index)) {
  3049             return true;
  3052         final FindProperty find = findProperty(convertKey(key), false);
  3054         return find != null;
  3057     @Override
  3058     public boolean delete(final int key, final boolean strict) {
  3059         final int index = ArrayIndex.getArrayIndex(key);
  3060         final ArrayData array = getArray();
  3062         if (array.has(index)) {
  3063             if (array.canDelete(index, strict)) {
  3064                 setArray(array.delete(index));
  3065                 return true;
  3067             return false;
  3070         return deleteObject(JSType.toObject(key), strict);
  3073     @Override
  3074     public boolean delete(final long key, final boolean strict) {
  3075         final int index = ArrayIndex.getArrayIndex(key);
  3076         final ArrayData array = getArray();
  3078         if (array.has(index)) {
  3079             if (array.canDelete(index, strict)) {
  3080                 setArray(array.delete(index));
  3081                 return true;
  3083             return false;
  3086         return deleteObject(JSType.toObject(key), strict);
  3089     @Override
  3090     public boolean delete(final double key, final boolean strict) {
  3091         final int index = ArrayIndex.getArrayIndex(key);
  3092         final ArrayData array = getArray();
  3094         if (array.has(index)) {
  3095             if (array.canDelete(index, strict)) {
  3096                 setArray(array.delete(index));
  3097                 return true;
  3099             return false;
  3102         return deleteObject(JSType.toObject(key), strict);
  3105     @Override
  3106     public boolean delete(final Object key, final boolean strict) {
  3107         final int index = ArrayIndex.getArrayIndex(key);
  3108         final ArrayData array = getArray();
  3110         if (array.has(index)) {
  3111             if (array.canDelete(index, strict)) {
  3112                 setArray(array.delete(index));
  3113                 return true;
  3115             return false;
  3118         return deleteObject(key, strict);
  3121     private boolean deleteObject(final Object key, final boolean strict) {
  3122         final String propName = convertKey(key);
  3123         final FindProperty find = findProperty(propName, false);
  3125         if (find == null) {
  3126             return true;
  3129         if (!find.getProperty().isConfigurable()) {
  3130             if (strict) {
  3131                 throw typeError("cant.delete.property", propName, ScriptRuntime.safeToString(this));
  3133             return false;
  3136         final Property prop = find.getProperty();
  3137         notifyPropertyDeleted(this, prop);
  3138         deleteOwnProperty(prop);
  3140         return true;
  3143     /*
  3144      * Make a new UserAccessorProperty property. getter and setter functions are stored in
  3145      * this ScriptObject and slot values are used in property object.
  3146      */
  3147     private UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
  3148         int oldSpillLength = getMap().getSpillLength();
  3150         int getterSlot = oldSpillLength++;
  3151         setSpill(getterSlot, getter);
  3152         // if getter function is null, flag the slot to be negative (less by 1)
  3153         if (getter == null) {
  3154             getterSlot = -getterSlot - 1;
  3157         int setterSlot = oldSpillLength++;
  3159         setSpill(setterSlot, setter);
  3160         // if setter function is null, flag the slot to be negative (less by 1)
  3161         if (setter == null) {
  3162             setterSlot = -setterSlot - 1;
  3165         return new UserAccessorProperty(key, propertyFlags, getterSlot, setterSlot);
  3168     private void setSpill(final int slot, final Object value) {
  3169         if (slot >= 0) {
  3170             final int index = slot;
  3171             if (spill == null) {
  3172                 // create new spill.
  3173                 spill = new Object[Math.max(index + 1, SPILL_RATE)];
  3174             } else if (index >= spill.length) {
  3175                 // grow spill as needed
  3176                 final Object[] newSpill = new Object[index + 1];
  3177                 System.arraycopy(spill, 0, newSpill, 0, spill.length);
  3178                 spill = newSpill;
  3181             spill[index] = value;
  3185     // user accessors are either stored in spill array slots
  3186     // get the accessor value using slot number. Note that slot is spill array index.
  3187     Object getSpill(final int slot) {
  3188         final int index = slot;
  3189         return (index < 0 || (index >= spill.length)) ? null : spill[index];
  3192     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
  3193         final Class<?>   own = ScriptObject.class;
  3194         final MethodType mt  = MH.type(rtype, types);
  3195         try {
  3196             return MH.findStatic(MethodHandles.lookup(), own, name, mt);
  3197         } catch (final MethodHandleFactory.LookupException e) {
  3198             return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
  3202     private static MethodHandle getKnownFunctionPropertyGuard(final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
  3203         return MH.insertArguments(KNOWNFUNCPROPGUARD, 1, map, getter, where, func);
  3206     @SuppressWarnings("unused")
  3207     private static boolean knownFunctionPropertyGuard(final Object self, final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
  3208         if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) {
  3209             try {
  3210                 return getter.invokeExact(where) == func;
  3211             } catch (final RuntimeException | Error e) {
  3212                 throw e;
  3213             } catch (final Throwable t) {
  3214                 throw new RuntimeException(t);
  3218         return false;
  3221     /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */
  3222     private static int count;
  3224     /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created that are scope */
  3225     private static int scopeCount;
  3227     /**
  3228      * Get number of {@code ScriptObject} instances created. If not running in debug
  3229      * mode this is always 0
  3231      * @return number of ScriptObjects created
  3232      */
  3233     public static int getCount() {
  3234         return count;
  3237     /**
  3238      * Get number of scope {@code ScriptObject} instances created. If not running in debug
  3239      * mode this is always 0
  3241      * @return number of scope ScriptObjects created
  3242      */
  3243     public static int getScopeCount() {
  3244         return scopeCount;

mercurial