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