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