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