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