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