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