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