Fri, 14 Jun 2013 21:16:14 +0530
8016618: script mirror object access should be improved
Reviewed-by: jlaskey, lagergren
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 */
26 package jdk.nashorn.internal.runtime;
28 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
29 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
30 import static jdk.nashorn.internal.lookup.Lookup.MH;
31 import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
32 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
33 import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE;
34 import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE;
35 import static jdk.nashorn.internal.runtime.PropertyDescriptor.GET;
36 import static jdk.nashorn.internal.runtime.PropertyDescriptor.SET;
37 import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE;
38 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
39 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
41 import java.lang.invoke.MethodHandle;
42 import java.lang.invoke.MethodHandles;
43 import java.lang.invoke.MethodType;
44 import java.util.AbstractMap;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.Collection;
48 import java.util.Collections;
49 import java.util.HashSet;
50 import java.util.Iterator;
51 import java.util.LinkedHashSet;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Set;
55 import jdk.internal.dynalink.CallSiteDescriptor;
56 import jdk.internal.dynalink.linker.GuardedInvocation;
57 import jdk.internal.dynalink.linker.LinkRequest;
58 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
59 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
60 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
61 import jdk.nashorn.internal.lookup.Lookup;
62 import jdk.nashorn.internal.lookup.MethodHandleFactory;
63 import jdk.nashorn.internal.objects.AccessorPropertyDescriptor;
64 import jdk.nashorn.internal.objects.DataPropertyDescriptor;
65 import jdk.nashorn.internal.runtime.arrays.ArrayData;
66 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
67 import jdk.nashorn.internal.runtime.linker.Bootstrap;
68 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
69 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
70 import jdk.nashorn.internal.runtime.linker.NashornGuards;
72 /**
73 * Base class for generic JavaScript objects.
74 * <p>
75 * Notes:
76 * <ul>
77 * <li>The map is used to identify properties in the object.</li>
78 * <li>If the map is modified then it must be cloned and replaced. This notifies
79 * any code that made assumptions about the object that things have changed.
80 * Ex. CallSites that have been validated must check to see if the map has
81 * changed (or a map from a different object type) and hence relink the method
82 * to call.</li>
83 * <li>Modifications of the map include adding/deleting attributes or changing a
84 * function field value.</li>
85 * </ul>
86 */
88 public abstract class ScriptObject extends PropertyListenerManager implements PropertyAccess {
90 /** Search fall back routine name for "no such method" */
91 static final String NO_SUCH_METHOD_NAME = "__noSuchMethod__";
93 /** Search fall back routine name for "no such property" */
94 static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__";
96 /** Per ScriptObject flag - is this a scope object? */
97 public static final int IS_SCOPE = 0b0000_0001;
99 /** Per ScriptObject flag - is this an array object? */
100 public static final int IS_ARRAY = 0b0000_0010;
102 /** Per ScriptObject flag - is this an arguments object? */
103 public static final int IS_ARGUMENTS = 0b0000_0100;
105 /** Is this a prototype PropertyMap? */
106 public static final int IS_PROTOTYPE = 0b0000_1000;
108 /** Is length property not-writable? */
109 public static final int IS_LENGTH_NOT_WRITABLE = 0b0001_0000;
111 /** Spill growth rate - by how many elements does {@link ScriptObject#spill} when full */
112 public static final int SPILL_RATE = 8;
114 /** Map to property information and accessor functions. Ordered by insertion. */
115 private PropertyMap map;
117 /** objects proto. */
118 private ScriptObject proto;
120 /** Context of the object, lazily cached. */
121 private Context context;
123 /** Object flags. */
124 private int flags;
126 /** Area for properties added to object after instantiation, see {@link AccessorProperty} */
127 public Object[] spill;
129 /** Indexed array data. */
130 private ArrayData arrayData;
132 static final MethodHandle SETFIELD = findOwnMH("setField", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, Object.class, Object.class);
133 static final MethodHandle SETSPILL = findOwnMH("setSpill", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
134 static final MethodHandle SETSPILLWITHNEW = findOwnMH("setSpillWithNew", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
135 static final MethodHandle SETSPILLWITHGROW = findOwnMH("setSpillWithGrow", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, int.class, Object.class, Object.class);
137 private static final MethodHandle TRUNCATINGFILTER = findOwnMH("truncatingFilter", Object[].class, int.class, Object[].class);
138 private static final MethodHandle KNOWNFUNCPROPGUARD = findOwnMH("knownFunctionPropertyGuard", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, Object.class, ScriptFunction.class);
140 /** Method handle for getting a function argument at a given index. Used from MapCreator */
141 public static final Call GET_ARGUMENT = virtualCall(ScriptObject.class, "getArgument", Object.class, int.class);
143 /** Method handle for setting a function argument at a given index. Used from MapCreator */
144 public static final Call SET_ARGUMENT = virtualCall(ScriptObject.class, "setArgument", void.class, int.class, Object.class);
146 /** Method handle for getting the proto of a ScriptObject */
147 public static final Call GET_PROTO = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class);
149 /** Method handle for setting the proto of a ScriptObject */
150 public static final Call SET_PROTO = virtualCallNoLookup(ScriptObject.class, "setProto", void.class, ScriptObject.class);
152 /** Method handle for setting the user accessors of a ScriptObject */
153 public static final Call SET_USER_ACCESSORS = virtualCall(ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class);
155 /**
156 * Constructor
157 */
158 public ScriptObject() {
159 this(null);
160 }
162 /**
163 * Constructor
164 *
165 * @param map {@link PropertyMap} used to create the initial object
166 */
167 public ScriptObject(final PropertyMap map) {
168 if (Context.DEBUG) {
169 ScriptObject.count++;
170 }
172 this.arrayData = ArrayData.EMPTY_ARRAY;
174 if (map == null) {
175 this.setMap(PropertyMap.newMap(getClass()));
176 return;
177 }
179 this.setMap(map);
180 }
182 /**
183 * Copy all properties from the source object with their receiver bound to the source.
184 * This function was known as mergeMap
185 *
186 * @param source The source object to copy from.
187 */
188 public void addBoundProperties(final ScriptObject source) {
189 PropertyMap newMap = this.getMap();
191 for (final Property property : source.getMap().getProperties()) {
192 final String key = property.getKey();
194 if (newMap.findProperty(key) == null) {
195 if (property instanceof UserAccessorProperty) {
196 final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
197 newMap = newMap.addProperty(prop);
198 } else {
199 newMap = newMap.newPropertyBind((AccessorProperty)property, source);
200 }
201 }
202 }
204 this.setMap(newMap);
205 }
207 /**
208 * Bind the method handle to the specified receiver, while preserving its original type (it will just ignore the
209 * first argument in lieu of the bound argument).
210 * @param methodHandle Method handle to bind to.
211 * @param receiver Object to bind.
212 * @return Bound method handle.
213 */
214 static MethodHandle bindTo(final MethodHandle methodHandle, final Object receiver) {
215 return MH.dropArguments(MH.bindTo(methodHandle, receiver), 0, methodHandle.type().parameterType(0));
216 }
218 /**
219 * Return a property iterator.
220 * @return Property iterator.
221 */
222 public Iterator<String> propertyIterator() {
223 return new KeyIterator(this);
224 }
226 /**
227 * Return a property value iterator.
228 * @return Property value iterator.
229 */
230 public Iterator<Object> valueIterator() {
231 return new ValueIterator(this);
232 }
234 /**
235 * ECMA 8.10.1 IsAccessorDescriptor ( Desc )
236 * @return true if this has a {@link AccessorPropertyDescriptor} with a getter or a setter
237 */
238 public final boolean isAccessorDescriptor() {
239 return has(GET) || has(SET);
240 }
242 /**
243 * ECMA 8.10.2 IsDataDescriptor ( Desc )
244 * @return true if this has a {@link DataPropertyDescriptor}, i.e. the object has a property value and is writable
245 */
246 public final boolean isDataDescriptor() {
247 return has(VALUE) || has(WRITABLE);
248 }
250 /**
251 * ECMA 8.10.3 IsGenericDescriptor ( Desc )
252 * @return true if this has a descriptor describing an {@link AccessorPropertyDescriptor} or {@link DataPropertyDescriptor}
253 */
254 public final boolean isGenericDescriptor() {
255 return isAccessorDescriptor() || isDataDescriptor();
256 }
258 /**
259 * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
260 *
261 * @return property descriptor
262 */
263 public final PropertyDescriptor toPropertyDescriptor() {
264 final GlobalObject global = (GlobalObject) Context.getGlobalTrusted();
266 final PropertyDescriptor desc;
267 if (isDataDescriptor()) {
268 if (has(SET) || has(GET)) {
269 throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
270 }
272 desc = global.newDataDescriptor(UNDEFINED, false, false, false);
273 } else if (isAccessorDescriptor()) {
274 if (has(VALUE) || has(WRITABLE)) {
275 throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
276 }
278 desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false);
279 } else {
280 desc = global.newGenericDescriptor(false, false);
281 }
283 return desc.fillFrom(this);
284 }
286 /**
287 * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
288 *
289 * @param global global scope object
290 * @param obj object to create property descriptor from
291 *
292 * @return property descriptor
293 */
294 public static PropertyDescriptor toPropertyDescriptor(final ScriptObject global, final Object obj) {
295 if (obj instanceof ScriptObject) {
296 return ((ScriptObject)obj).toPropertyDescriptor();
297 }
299 throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj));
300 }
302 /**
303 * ECMA 8.12.1 [[GetOwnProperty]] (P)
304 *
305 * @param key property key
306 *
307 * @return Returns the Property Descriptor of the named own property of this
308 * object, or undefined if absent.
309 */
310 public Object getOwnPropertyDescriptor(final String key) {
311 final Property property = getMap().findProperty(key);
313 final GlobalObject global = (GlobalObject)Context.getGlobalTrusted();
315 if (property != null) {
316 final ScriptFunction get = property.getGetterFunction(this);
317 final ScriptFunction set = property.getSetterFunction(this);
319 final boolean configurable = property.isConfigurable();
320 final boolean enumerable = property.isEnumerable();
321 final boolean writable = property.isWritable();
323 if (property instanceof UserAccessorProperty) {
324 return global.newAccessorDescriptor(
325 (get != null) ?
326 get :
327 UNDEFINED,
328 (set != null) ?
329 set :
330 UNDEFINED,
331 configurable,
332 enumerable);
333 }
335 return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable);
336 }
338 final int index = ArrayIndex.getArrayIndex(key);
339 final ArrayData array = getArray();
341 if (array.has(index)) {
342 return array.getDescriptor(global, index);
343 }
345 return UNDEFINED;
346 }
348 /**
349 * ECMA 8.12.2 [[GetProperty]] (P)
350 *
351 * @param key property key
352 *
353 * @return Returns the fully populated Property Descriptor of the named property
354 * of this object, or undefined if absent.
355 */
356 public Object getPropertyDescriptor(final String key) {
357 final Object res = getOwnPropertyDescriptor(key);
359 if (res != UNDEFINED) {
360 return res;
361 } else if (getProto() != null) {
362 return getProto().getOwnPropertyDescriptor(key);
363 } else {
364 return UNDEFINED;
365 }
366 }
368 /**
369 * ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw)
370 *
371 * @param key the property key
372 * @param propertyDesc the property descriptor
373 * @param reject is the property extensible - true means new definitions are rejected
374 *
375 * @return true if property was successfully defined
376 */
377 public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
378 final ScriptObject global = Context.getGlobalTrusted();
379 final PropertyDescriptor desc = toPropertyDescriptor(global, propertyDesc);
380 final Object current = getOwnPropertyDescriptor(key);
381 final String name = JSType.toString(key);
383 if (current == UNDEFINED) {
384 if (isExtensible()) {
385 // add a new own property
386 addOwnProperty(key, desc);
387 return true;
388 }
389 // new property added to non-extensible object
390 if (reject) {
391 throw typeError(global, "object.non.extensible", name, ScriptRuntime.safeToString(this));
392 }
393 return false;
394 }
395 // modifying an existing property
396 final PropertyDescriptor currentDesc = (PropertyDescriptor) current;
397 final PropertyDescriptor newDesc = desc;
399 if (newDesc.type() == PropertyDescriptor.GENERIC &&
400 ! newDesc.has(CONFIGURABLE) && ! newDesc.has(ENUMERABLE)) {
401 // every descriptor field is absent
402 return true;
403 }
405 if (currentDesc.equals(newDesc)) {
406 // every descriptor field of the new is same as the current
407 return true;
408 }
410 if (! currentDesc.isConfigurable()) {
411 if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) {
412 // not configurable can not be made configurable
413 if (reject) {
414 throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
415 }
416 return false;
417 }
419 if (newDesc.has(ENUMERABLE) &&
420 currentDesc.isEnumerable() != newDesc.isEnumerable()) {
421 // cannot make non-enumerable as enumerable or vice-versa
422 if (reject) {
423 throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
424 }
425 return false;
426 }
427 }
429 int propFlags = Property.mergeFlags(currentDesc, newDesc);
430 Property property = getMap().findProperty(key);
432 if (currentDesc.type() == PropertyDescriptor.DATA &&
433 (newDesc.type() == PropertyDescriptor.DATA || newDesc.type() == PropertyDescriptor.GENERIC)) {
434 if (! currentDesc.isConfigurable() && ! currentDesc.isWritable()) {
435 if (newDesc.has(WRITABLE) && newDesc.isWritable() ||
436 newDesc.has(VALUE) && ! ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) {
437 if (reject) {
438 throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
439 }
440 return false;
441 }
442 }
444 final boolean newValue = newDesc.has(VALUE);
445 final Object value = newValue? newDesc.getValue() : currentDesc.getValue();
446 if (newValue && property != null) {
447 // Temporarily clear flags.
448 property = modifyOwnProperty(property, 0);
449 set(key, value, false);
450 }
452 if (property == null) {
453 // promoting an arrayData value to actual property
454 addOwnProperty(key, propFlags, value);
455 removeArraySlot(key);
456 } else {
457 // Now set the new flags
458 modifyOwnProperty(property, propFlags);
459 }
460 } else if (currentDesc.type() == PropertyDescriptor.ACCESSOR &&
461 (newDesc.type() == PropertyDescriptor.ACCESSOR ||
462 newDesc.type() == PropertyDescriptor.GENERIC)) {
463 if (! currentDesc.isConfigurable()) {
464 if (newDesc.has(PropertyDescriptor.GET) && ! ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) ||
465 newDesc.has(PropertyDescriptor.SET) && ! ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) {
466 if (reject) {
467 throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
468 }
469 return false;
470 }
471 }
473 // New set the new features.
474 modifyOwnProperty(property, propFlags,
475 newDesc.has(GET) ? newDesc.getGetter() : currentDesc.getGetter(),
476 newDesc.has(SET) ? newDesc.getSetter() : currentDesc.getSetter());
477 } else {
478 // changing descriptor type
479 if (! currentDesc.isConfigurable()) {
480 // not configurable can not be made configurable
481 if (reject) {
482 throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
483 }
484 return false;
485 }
487 propFlags = 0;
489 // Preserve only configurable and enumerable from current desc
490 // if those are not overridden in the new property descriptor.
491 boolean value = newDesc.has(CONFIGURABLE)? newDesc.isConfigurable() : currentDesc.isConfigurable();
492 if (!value) {
493 propFlags |= Property.NOT_CONFIGURABLE;
494 }
495 value = newDesc.has(ENUMERABLE)? newDesc.isEnumerable() : currentDesc.isEnumerable();
496 if (!value) {
497 propFlags |= Property.NOT_ENUMERABLE;
498 }
500 final int type = newDesc.type();
501 if (type == PropertyDescriptor.DATA) {
502 // get writable from the new descriptor
503 value = newDesc.has(WRITABLE) && newDesc.isWritable();
504 if (! value) {
505 propFlags |= Property.NOT_WRITABLE;
506 }
508 // delete the old property
509 deleteOwnProperty(property);
510 // add new data property
511 addOwnProperty(key, propFlags, newDesc.getValue());
512 } else if (type == PropertyDescriptor.ACCESSOR) {
513 if (property == null) {
514 addOwnProperty(key, propFlags,
515 newDesc.has(GET) ? newDesc.getGetter() : null,
516 newDesc.has(SET) ? newDesc.getSetter() : null);
517 } else {
518 // Modify old property with the new features.
519 modifyOwnProperty(property, propFlags,
520 newDesc.has(GET) ? newDesc.getGetter() : null,
521 newDesc.has(SET) ? newDesc.getSetter() : null);
522 }
523 }
524 }
526 checkIntegerKey(key);
528 return true;
529 }
531 /**
532 * Spec. mentions use of [[DefineOwnProperty]] for indexed properties in
533 * certain places (eg. Array.prototype.map, filter). We can not use ScriptObject.set
534 * method in such cases. This is because set method uses inherited setters (if any)
535 * from any object in proto chain such as Array.prototype, Object.prototype.
536 * This method directly sets a particular element value in the current object.
537 *
538 * @param index key for property
539 * @param value value to define
540 */
541 protected final void defineOwnProperty(final int index, final Object value) {
542 assert ArrayIndex.isValidArrayIndex(index) : "invalid array index";
543 final long longIndex = ArrayIndex.toLongIndex(index);
544 if (longIndex >= getArray().length()) {
545 // make array big enough to hold..
546 setArray(getArray().ensure(longIndex));
547 }
548 setArray(getArray().set(index, value, false));
549 }
551 private void checkIntegerKey(final String key) {
552 final int index = ArrayIndex.getArrayIndex(key);
554 if (ArrayIndex.isValidArrayIndex(index)) {
555 final ArrayData data = getArray();
557 if (data.has(index)) {
558 setArray(data.delete(index));
559 }
560 }
561 }
563 private void removeArraySlot(final String key) {
564 final int index = ArrayIndex.getArrayIndex(key);
565 final ArrayData array = getArray();
567 if (array.has(index)) {
568 setArray(array.delete(index));
569 }
570 }
572 /**
573 * Add a new property to the object.
574 *
575 * @param key property key
576 * @param propertyDesc property descriptor for property
577 */
578 public final void addOwnProperty(final String key, final PropertyDescriptor propertyDesc) {
579 // Already checked that there is no own property with that key.
580 PropertyDescriptor pdesc = propertyDesc;
582 final int propFlags = Property.toFlags(pdesc);
584 if (pdesc.type() == PropertyDescriptor.GENERIC) {
585 final GlobalObject global = (GlobalObject) Context.getGlobalTrusted();
586 final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false);
588 dDesc.fillFrom((ScriptObject)pdesc);
589 pdesc = dDesc;
590 }
592 final int type = pdesc.type();
593 if (type == PropertyDescriptor.DATA) {
594 addOwnProperty(key, propFlags, pdesc.getValue());
595 } else if (type == PropertyDescriptor.ACCESSOR) {
596 addOwnProperty(key, propFlags,
597 pdesc.has(GET) ? pdesc.getGetter() : null,
598 pdesc.has(SET) ? pdesc.getSetter() : null);
599 }
601 checkIntegerKey(key);
602 }
604 /**
605 * Low level property API (not using property descriptors)
606 * <p>
607 * Find a property in the prototype hierarchy. Note: this is final and not
608 * a good idea to override. If you have to, use
609 * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
610 * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
611 * overriding way to find array properties
612 *
613 * @see jdk.nashorn.internal.objects.NativeArray
614 *
615 * @param key Property key.
616 * @param deep Whether the search should look up proto chain.
617 *
618 * @return FindPropertyData or null if not found.
619 */
620 public final FindProperty findProperty(final String key, final boolean deep) {
621 return findProperty(key, deep, false, this);
622 }
624 /**
625 * Low level property API (not using property descriptors)
626 * <p>
627 * Find a property in the prototype hierarchy. Note: this is not a good idea
628 * to override except as it was done in {@link WithObject}.
629 * If you have to, use
630 * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
631 * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
632 * overriding way to find array properties
633 *
634 * @see jdk.nashorn.internal.objects.NativeArray
635 *
636 * @param key Property key.
637 * @param deep Whether the search should look up proto chain.
638 * @param stopOnNonScope should a deep search stop on the first non-scope object?
639 * @param start the object on which the lookup was originally initiated
640 *
641 * @return FindPropertyData or null if not found.
642 */
643 FindProperty findProperty(final String key, final boolean deep, final boolean stopOnNonScope, final ScriptObject start) {
644 // if doing deep search, stop search on the first non-scope object if asked to do so
645 if (stopOnNonScope && start != this && !isScope()) {
646 return null;
647 }
649 final PropertyMap selfMap = getMap();
650 final Property property = selfMap.findProperty(key);
652 if (property != null) {
653 return new FindProperty(start, this, property);
654 }
656 if (deep) {
657 final ScriptObject myProto = getProto();
658 if (myProto != null) {
659 return myProto.findProperty(key, deep, stopOnNonScope, start);
660 }
661 }
663 return null;
664 }
666 /**
667 * Add a new property to the object.
668 * <p>
669 * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
670 *
671 * @param key Property key.
672 * @param propertyFlags Property flags.
673 * @param getter Property getter, or null if not defined
674 * @param setter Property setter, or null if not defined
675 *
676 * @return New property.
677 */
678 public final Property addOwnProperty(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
679 return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter));
680 }
682 /**
683 * Add a new property to the object.
684 * <p>
685 * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
686 *
687 * @param key Property key.
688 * @param propertyFlags Property flags.
689 * @param value Value of property
690 *
691 * @return New property.
692 */
693 public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) {
694 final Property property = addSpillProperty(key, propertyFlags);
695 property.setObjectValue(this, this, value, false);
696 return property;
697 }
699 /**
700 * Add a new property to the object.
701 * <p>
702 * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
703 *
704 * @param newProperty property to add
705 *
706 * @return New property.
707 */
708 public final Property addOwnProperty(final Property newProperty) {
709 PropertyMap oldMap = getMap();
711 while (true) {
712 final PropertyMap newMap = oldMap.addProperty(newProperty);
714 if (!compareAndSetMap(oldMap, newMap)) {
715 oldMap = getMap();
716 final Property oldProperty = oldMap.findProperty(newProperty.getKey());
718 if (oldProperty != null) {
719 return oldProperty;
720 }
721 } else {
722 return newProperty;
723 }
724 }
725 }
727 private void erasePropertyValue(final Property property) {
728 // Erase the property field value with undefined. If the property is defined
729 // by user-defined accessors, we don't want to call the setter!!
730 if (!(property instanceof UserAccessorProperty)) {
731 property.setObjectValue(this, this, UNDEFINED, false);
732 }
733 }
735 /**
736 * Delete a property from the object.
737 *
738 * @param property Property to delete.
739 *
740 * @return true if deleted.
741 */
742 public final boolean deleteOwnProperty(final Property property) {
743 erasePropertyValue(property);
744 PropertyMap oldMap = getMap();
746 while (true) {
747 final PropertyMap newMap = oldMap.deleteProperty(property);
749 if (newMap == null) {
750 return false;
751 }
753 if (!compareAndSetMap(oldMap, newMap)) {
754 oldMap = getMap();
755 } else {
756 // delete getter and setter function references so that we don't leak
757 if (property instanceof UserAccessorProperty) {
758 final UserAccessorProperty uc = (UserAccessorProperty) property;
759 setSpill(uc.getGetterSlot(), null);
760 setSpill(uc.getSetterSlot(), null);
761 }
762 return true;
763 }
764 }
765 }
767 /**
768 * Modify a property in the object
769 *
770 * @param oldProperty property to modify
771 * @param propertyFlags new property flags
772 * @param getter getter for {@link UserAccessorProperty}, null if not present or N/A
773 * @param setter setter for {@link UserAccessorProperty}, null if not present or N/A
774 *
775 * @return new property
776 */
777 public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
778 Property newProperty;
779 if (oldProperty instanceof UserAccessorProperty) {
780 // re-use the slots of the old user accessor property.
781 final UserAccessorProperty uc = (UserAccessorProperty) oldProperty;
783 int getterSlot = uc.getGetterSlot();
784 // clear the old getter and set the new getter
785 setSpill(getterSlot, getter);
786 // if getter function is null, flag the slot to be negative (less by 1)
787 if (getter == null) {
788 getterSlot = -getterSlot - 1;
789 }
791 int setterSlot = uc.getSetterSlot();
792 // clear the old setter and set the new setter
793 setSpill(setterSlot, setter);
794 // if setter function is null, flag the slot to be negative (less by 1)
795 if (setter == null) {
796 setterSlot = -setterSlot - 1;
797 }
799 newProperty = new UserAccessorProperty(oldProperty.getKey(), propertyFlags, getterSlot, setterSlot);
800 // if just flipping getter and setter with new functions, no need to change property or map
801 if (oldProperty.equals(newProperty)) {
802 return oldProperty;
803 }
804 } else {
805 // erase old property value and create new user accessor property
806 erasePropertyValue(oldProperty);
807 newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter);
808 }
810 notifyPropertyModified(this, oldProperty, newProperty);
812 return modifyOwnProperty(oldProperty, newProperty);
813 }
815 /**
816 * Modify a property in the object
817 *
818 * @param oldProperty property to modify
819 * @param propertyFlags new property flags
820 *
821 * @return new property
822 */
823 public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags) {
824 return modifyOwnProperty(oldProperty, oldProperty.setFlags(propertyFlags));
825 }
827 /**
828 * Modify a property in the object, replacing a property with a new one
829 *
830 * @param oldProperty property to replace
831 * @param newProperty property to replace it with
832 *
833 * @return new property
834 */
835 private Property modifyOwnProperty(final Property oldProperty, final Property newProperty) {
836 assert newProperty.getKey().equals(oldProperty.getKey()) : "replacing property with different key";
838 PropertyMap oldMap = getMap();
840 while (true) {
841 final PropertyMap newMap = oldMap.replaceProperty(oldProperty, newProperty);
843 if (!compareAndSetMap(oldMap, newMap)) {
844 oldMap = getMap();
845 final Property oldPropertyLookup = oldMap.findProperty(oldProperty.getKey());
847 if (oldPropertyLookup != null && oldPropertyLookup.equals(newProperty)) {
848 return oldPropertyLookup;
849 }
850 } else {
851 return newProperty;
852 }
853 }
854 }
856 /**
857 * Update getter and setter in an object literal.
858 *
859 * @param key Property key.
860 * @param getter {@link UserAccessorProperty} defined getter, or null if none
861 * @param setter {@link UserAccessorProperty} defined setter, or null if none
862 */
863 public final void setUserAccessors(final String key, final ScriptFunction getter, final ScriptFunction setter) {
864 final Property oldProperty = getMap().findProperty(key);
865 if (oldProperty != null) {
866 final UserAccessorProperty newProperty = newUserAccessors(oldProperty.getKey(), oldProperty.getFlags(), getter, setter);
867 modifyOwnProperty(oldProperty, newProperty);
868 } else {
869 final UserAccessorProperty newProperty = newUserAccessors(key, 0, getter, setter);
870 addOwnProperty(newProperty);
871 }
872 }
874 private static int getIntValue(final FindProperty find) {
875 final MethodHandle getter = find.getGetter(int.class);
876 if (getter != null) {
877 try {
878 return (int)getter.invokeExact((Object)find.getGetterReceiver());
879 } catch (final Error|RuntimeException e) {
880 throw e;
881 } catch (final Throwable e) {
882 throw new RuntimeException(e);
883 }
884 }
886 return ObjectClassGenerator.UNDEFINED_INT;
887 }
889 private static long getLongValue(final FindProperty find) {
890 final MethodHandle getter = find.getGetter(long.class);
891 if (getter != null) {
892 try {
893 return (long)getter.invokeExact((Object)find.getGetterReceiver());
894 } catch (final Error|RuntimeException e) {
895 throw e;
896 } catch (final Throwable e) {
897 throw new RuntimeException(e);
898 }
899 }
901 return ObjectClassGenerator.UNDEFINED_LONG;
902 }
904 private static double getDoubleValue(final FindProperty find) {
905 final MethodHandle getter = find.getGetter(double.class);
906 if (getter != null) {
907 try {
908 return (double)getter.invokeExact((Object)find.getGetterReceiver());
909 } catch (final Error|RuntimeException e) {
910 throw e;
911 } catch (final Throwable e) {
912 throw new RuntimeException(e);
913 }
914 }
916 return ObjectClassGenerator.UNDEFINED_DOUBLE;
917 }
919 /**
920 * Get the object value of a property
921 *
922 * @param find {@link FindProperty} lookup result
923 *
924 * @return the value of the property
925 */
926 protected static Object getObjectValue(final FindProperty find) {
927 return find.getObjectValue();
928 }
930 /**
931 * Return methodHandle of value function for call.
932 *
933 * @param find data from find property.
934 * @param type method type of function.
935 * @param bindName null or name to bind to second argument (property not found method.)
936 *
937 * @return value of property as a MethodHandle or null.
938 */
939 protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) {
940 return getCallMethodHandle(getObjectValue(find), type, bindName);
941 }
943 /**
944 * Return methodHandle of value function for call.
945 *
946 * @param value value of receiver, it not a {@link ScriptFunction} this will return null.
947 * @param type method type of function.
948 * @param bindName null or name to bind to second argument (property not found method.)
949 *
950 * @return value of property as a MethodHandle or null.
951 */
952 protected static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) {
953 return value instanceof ScriptFunction ? ((ScriptFunction)value).getCallMethodHandle(type, bindName) : null;
954 }
956 /**
957 * Get value using found property.
958 *
959 * @param property Found property.
960 *
961 * @return Value of property.
962 */
963 public final Object getWithProperty(final Property property) {
964 return getObjectValue(new FindProperty(this, this, property));
965 }
967 /**
968 * Get a property given a key
969 *
970 * @param key property key
971 *
972 * @return property for key
973 */
974 public final Property getProperty(final String key) {
975 return getMap().findProperty(key);
976 }
978 static String convertKey(final Object key) {
979 return (key instanceof String) ? (String)key : JSType.toString(key);
980 }
982 /**
983 * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
984 * Used for argument access in a vararg function using parameter name.
985 * Returns the argument at a given key (index)
986 *
987 * @param key argument index
988 *
989 * @return the argument at the given position, or undefined if not present
990 */
991 public Object getArgument(final int key) {
992 return get(key);
993 }
995 /**
996 * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
997 * Used for argument access in a vararg function using parameter name.
998 * Returns the argument at a given key (index)
999 *
1000 * @param key argument index
1001 * @param value the value to write at the given index
1002 */
1003 public void setArgument(final int key, final Object value) {
1004 set(key, value, false);
1005 }
1007 /**
1008 * Return true if the script object context is strict
1009 * @return true if strict context
1010 */
1011 public final boolean isStrictContext() {
1012 return getContext()._strict;
1013 }
1015 /**
1016 * Return the current context from the object's map.
1017 * @return Current context.
1018 */
1019 protected final Context getContext() {
1020 if (context == null) {
1021 context = Context.fromClass(getClass());
1022 }
1023 return context;
1024 }
1026 /**
1027 * Return the map of an object.
1028 * @return PropertyMap object.
1029 */
1030 public final PropertyMap getMap() {
1031 return map;
1032 }
1034 /**
1035 * Set the initial map.
1036 * @param map Initial map.
1037 */
1038 public final void setMap(final PropertyMap map) {
1039 this.map = map;
1040 }
1042 /**
1043 * Conditionally set the new map if the old map is the same.
1044 * @param oldMap Map prior to manipulation.
1045 * @param newMap Replacement map.
1046 * @return true if the operation succeeded.
1047 */
1048 protected synchronized final boolean compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap) {
1049 final boolean update = oldMap == this.map;
1051 if (update) {
1052 this.map = newMap;
1053 }
1055 return update;
1056 }
1058 /**
1059 * Return the __proto__ of an object.
1060 * @return __proto__ object.
1061 */
1062 public final ScriptObject getProto() {
1063 return proto;
1064 }
1066 /**
1067 * Set the __proto__ of an object.
1068 * @param newProto new __proto__ to set.
1069 */
1070 public synchronized final void setProto(final ScriptObject newProto) {
1071 final ScriptObject oldProto = proto;
1072 map = map.changeProto(oldProto, newProto);
1074 if (newProto != null) {
1075 newProto.setIsPrototype();
1076 }
1078 proto = newProto;
1080 if (isPrototype()) {
1081 if (oldProto != null) {
1082 oldProto.removePropertyListener(this);
1083 }
1085 if (newProto != null) {
1086 newProto.addPropertyListener(this);
1087 }
1088 }
1089 }
1091 /**
1092 * Set the __proto__ of an object with checks.
1093 * @param newProto Prototype to set.
1094 */
1095 public final void setProtoCheck(final Object newProto) {
1096 if (newProto == null || newProto instanceof ScriptObject) {
1097 setProto((ScriptObject)newProto);
1098 } else {
1099 final ScriptObject global = Context.getGlobalTrusted();
1100 final Object newProtoObject = JSType.toScriptObject(global, newProto);
1102 if (newProtoObject instanceof ScriptObject) {
1103 setProto((ScriptObject)newProtoObject);
1104 } else {
1105 throw typeError(global, "cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
1106 }
1107 }
1108 }
1110 /**
1111 * return an array of own property keys associated with the object.
1112 *
1113 * @param all True if to include non-enumerable keys.
1114 * @return Array of keys.
1115 */
1116 public String[] getOwnKeys(final boolean all) {
1117 final List<Object> keys = new ArrayList<>();
1118 final PropertyMap selfMap = this.getMap();
1120 final ArrayData array = getArray();
1121 final long length = array.length();
1123 for (long i = 0; i < length; i = array.nextIndex(i)) {
1124 if (array.has((int)i)) {
1125 keys.add(JSType.toString(i));
1126 }
1127 }
1129 for (final Property property : selfMap.getProperties()) {
1130 if (all || property.isEnumerable()) {
1131 keys.add(property.getKey());
1132 }
1133 }
1135 return keys.toArray(new String[keys.size()]);
1136 }
1138 /**
1139 * Check if this ScriptObject has array entries. This means that someone has
1140 * set values with numeric keys in the object.
1141 *
1142 * Note: this can be O(n) up to the array length
1143 *
1144 * @return true if array entries exists.
1145 */
1146 public boolean hasArrayEntries() {
1147 final ArrayData array = getArray();
1148 final long length = array.length();
1150 for (long i = 0; i < length; i++) {
1151 if (array.has((int)i)) {
1152 return true;
1153 }
1154 }
1156 return false;
1157 }
1159 /**
1160 * Return the valid JavaScript type name descriptor
1161 *
1162 * @return "Object"
1163 */
1164 public String getClassName() {
1165 return "Object";
1166 }
1168 /**
1169 * {@code length} is a well known property. This is its getter.
1170 * Note that this *may* be optimized by other classes
1171 *
1172 * @return length property value for this ScriptObject
1173 */
1174 public Object getLength() {
1175 return get("length");
1176 }
1178 /**
1179 * Stateless toString for ScriptObjects.
1180 *
1181 * @return string description of this object, e.g. {@code [object Object]}
1182 */
1183 public String safeToString() {
1184 return "[object " + getClassName() + "]";
1185 }
1187 /**
1188 * Return the default value of the object with a given preferred type hint.
1189 * The preferred type hints are String.class for type String, Number.class
1190 * for type Number. <p>
1191 *
1192 * A <code>hint</code> of null means "no hint".
1193 *
1194 * ECMA 8.12.8 [[DefaultValue]](hint)
1195 *
1196 * @param typeHint the preferred type hint
1197 * @return the default value
1198 */
1199 public Object getDefaultValue(final Class<?> typeHint) {
1200 // We delegate to GlobalObject, as the implementation uses dynamic call sites to invoke object's "toString" and
1201 // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts
1202 // are being executed in a long-running program, we move the code and their associated dynamic call sites
1203 // (Global.TO_STRING and Global.VALUE_OF) into per-context code.
1204 return ((GlobalObject)Context.getGlobalTrusted()).getDefaultValue(this, typeHint);
1205 }
1207 /**
1208 * Checking whether a script object is an instance of another. Used
1209 * in {@link ScriptFunction} for hasInstance implementation, walks
1210 * the proto chain
1211 *
1212 * @param instance instace to check
1213 * @return true if 'instance' is an instance of this object
1214 */
1215 public boolean isInstance(final ScriptObject instance) {
1216 return false;
1217 }
1219 /**
1220 * Flag this ScriptObject as non extensible
1221 *
1222 * @return the object after being made non extensible
1223 */
1224 public ScriptObject preventExtensions() {
1225 PropertyMap oldMap = getMap();
1227 while (true) {
1228 final PropertyMap newMap = getMap().preventExtensions();
1230 if (!compareAndSetMap(oldMap, newMap)) {
1231 oldMap = getMap();
1232 } else {
1233 return this;
1234 }
1235 }
1236 }
1238 /**
1239 * Check whether if an Object (not just a ScriptObject) represents JavaScript array
1240 *
1241 * @param obj object to check
1242 *
1243 * @return true if array
1244 */
1245 public static boolean isArray(final Object obj) {
1246 return (obj instanceof ScriptObject) && ((ScriptObject)obj).isArray();
1247 }
1249 /**
1250 * Check if this ScriptObject is an array
1251 * @return true if array
1252 */
1253 public final boolean isArray() {
1254 return (flags & IS_ARRAY) != 0;
1255 }
1257 /**
1258 * Flag this ScriptObject as being an array
1259 */
1260 public final void setIsArray() {
1261 flags |= IS_ARRAY;
1262 }
1264 /**
1265 * Check if this ScriptObject is an {@code arguments} vector
1266 * @return true if arguments vector
1267 */
1268 public final boolean isArguments() {
1269 return (flags & IS_ARGUMENTS) != 0;
1270 }
1272 /**
1273 * Flag this ScriptObject as being an {@code arguments} vector
1274 */
1275 public final void setIsArguments() {
1276 flags |= IS_ARGUMENTS;
1277 }
1279 /**
1280 * Check if this object is a prototype
1281 *
1282 * @return {@code true} if is prototype
1283 */
1284 public final boolean isPrototype() {
1285 return (flags & IS_PROTOTYPE) != 0;
1286 }
1288 /**
1289 * Flag this object as having a prototype.
1290 */
1291 public final void setIsPrototype() {
1292 if (proto != null && !isPrototype()) {
1293 proto.addPropertyListener(this);
1294 }
1295 flags |= IS_PROTOTYPE;
1296 }
1298 /**
1299 * Check if this object has non-writable length property
1300 *
1301 * @return {@code true} if 'length' property is non-writable
1302 */
1303 public final boolean isLengthNotWritable() {
1304 return (flags & IS_LENGTH_NOT_WRITABLE) != 0;
1305 }
1307 /**
1308 * Flag this object as having non-writable length property
1309 */
1310 public void setIsLengthNotWritable() {
1311 flags |= IS_LENGTH_NOT_WRITABLE;
1312 }
1314 /**
1315 * Get the {@link ArrayData} for this ScriptObject if it is an array
1316 * @return array data
1317 */
1318 public final ArrayData getArray() {
1319 return arrayData;
1320 }
1322 /**
1323 * Set the {@link ArrayData} for this ScriptObject if it is to be an array
1324 * @param arrayData the array data
1325 */
1326 public final void setArray(final ArrayData arrayData) {
1327 this.arrayData = arrayData;
1328 }
1330 /**
1331 * Check if this ScriptObject is extensible
1332 * @return true if extensible
1333 */
1334 public boolean isExtensible() {
1335 return getMap().isExtensible();
1336 }
1338 /**
1339 * ECMAScript 15.2.3.8 - seal implementation
1340 * @return the sealed ScriptObject
1341 */
1342 public ScriptObject seal() {
1343 PropertyMap oldMap = getMap();
1345 while (true) {
1346 final PropertyMap newMap = getMap().seal();
1348 if (!compareAndSetMap(oldMap, newMap)) {
1349 oldMap = getMap();
1350 } else {
1351 setArray(ArrayData.seal(getArray()));
1352 return this;
1353 }
1354 }
1355 }
1357 /**
1358 * Check whether this ScriptObject is sealed
1359 * @return true if sealed
1360 */
1361 public boolean isSealed() {
1362 return getMap().isSealed();
1363 }
1365 /**
1366 * ECMA 15.2.39 - freeze implementation. Freeze this ScriptObject
1367 * @return the frozen ScriptObject
1368 */
1369 public ScriptObject freeze() {
1370 PropertyMap oldMap = getMap();
1372 while (true) {
1373 final PropertyMap newMap = getMap().freeze();
1375 if (!compareAndSetMap(oldMap, newMap)) {
1376 oldMap = getMap();
1377 } else {
1378 setArray(ArrayData.freeze(getArray()));
1379 return this;
1380 }
1381 }
1382 }
1384 /**
1385 * Check whether this ScriptObject is frozen
1386 * @return true if frozen
1387 */
1388 public boolean isFrozen() {
1389 return getMap().isFrozen();
1390 }
1393 /**
1394 * Flag this ScriptObject as scope
1395 */
1396 public final void setIsScope() {
1397 if (Context.DEBUG) {
1398 scopeCount++;
1399 }
1400 flags |= IS_SCOPE;
1401 }
1403 /**
1404 * Check whether this ScriptObject is scope
1405 * @return true if scope
1406 */
1407 public final boolean isScope() {
1408 return (flags & IS_SCOPE) != 0;
1409 }
1411 /**
1412 * Clears the properties from a ScriptObject
1413 * (java.util.Map-like method to help ScriptObjectMirror implementation)
1414 */
1415 public void clear() {
1416 final boolean strict = isStrictContext();
1417 final Iterator<String> iter = propertyIterator();
1418 while (iter.hasNext()) {
1419 delete(iter.next(), strict);
1420 }
1421 }
1423 /**
1424 * Checks if a property with a given key is present in a ScriptObject
1425 * (java.util.Map-like method to help ScriptObjectMirror implementation)
1426 *
1427 * @param key the key to check for
1428 * @return true if a property with the given key exists, false otherwise
1429 */
1430 public boolean containsKey(final Object key) {
1431 return has(key);
1432 }
1434 /**
1435 * Checks if a property with a given value is present in a ScriptObject
1436 * (java.util.Map-like method to help ScriptObjectMirror implementation)
1437 *
1438 * @param value value to check for
1439 * @return true if a property with the given value exists, false otherwise
1440 */
1441 public boolean containsValue(final Object value) {
1442 final Iterator<Object> iter = valueIterator();
1443 while (iter.hasNext()) {
1444 if (iter.next().equals(value)) {
1445 return true;
1446 }
1447 }
1448 return false;
1449 }
1451 /**
1452 * Returns the set of {@literal <property, value>} entries that make up this
1453 * ScriptObject's properties
1454 * (java.util.Map-like method to help ScriptObjectMirror implementation)
1455 *
1456 * @return an entry set of all the properties in this object
1457 */
1458 public Set<Map.Entry<Object, Object>> entrySet() {
1459 final Iterator<String> iter = propertyIterator();
1460 final Set<Map.Entry<Object, Object>> entries = new HashSet<>();
1461 while (iter.hasNext()) {
1462 final Object key = iter.next();
1463 entries.add(new AbstractMap.SimpleImmutableEntry<>(key, get(key)));
1464 }
1465 return Collections.unmodifiableSet(entries);
1466 }
1468 /**
1469 * Check whether a ScriptObject contains no properties
1470 * (java.util.Map-like method to help ScriptObjectMirror implementation)
1471 *
1472 * @return true if object has no properties
1473 */
1474 public boolean isEmpty() {
1475 return !propertyIterator().hasNext();
1476 }
1478 /**
1479 * Return the set of keys (property names) for all properties
1480 * in this ScriptObject
1481 * (java.util.Map-like method to help ScriptObjectMirror implementation)
1482 *
1483 * @return keySet of this ScriptObject
1484 */
1485 public Set<Object> keySet() {
1486 final Iterator<String> iter = propertyIterator();
1487 final Set<Object> keySet = new HashSet<>();
1488 while (iter.hasNext()) {
1489 keySet.add(iter.next());
1490 }
1491 return Collections.unmodifiableSet(keySet);
1492 }
1494 /**
1495 * Put a property in the ScriptObject
1496 * (java.util.Map-like method to help ScriptObjectMirror implementation)
1497 *
1498 * @param key property key
1499 * @param value property value
1500 * @return oldValue if property with same key existed already
1501 */
1502 public Object put(final Object key, final Object value) {
1503 final Object oldValue = get(key);
1504 set(key, value, isStrictContext());
1505 return oldValue;
1506 }
1508 /**
1509 * Put several properties in the ScriptObject given a mapping
1510 * of their keys to their values
1511 * (java.util.Map-like method to help ScriptObjectMirror implementation)
1512 *
1513 * @param otherMap a {@literal <key,value>} map of properties to add
1514 */
1515 public void putAll(final Map<?, ?> otherMap) {
1516 final boolean strict = isStrictContext();
1517 for (final Map.Entry<?, ?> entry : otherMap.entrySet()) {
1518 set(entry.getKey(), entry.getValue(), strict);
1519 }
1520 }
1522 /**
1523 * Remove a property from the ScriptObject.
1524 * (java.util.Map-like method to help ScriptObjectMirror implementation)
1525 *
1526 * @param key the key of the property
1527 * @return the oldValue of the removed property
1528 */
1529 public Object remove(final Object key) {
1530 final Object oldValue = get(key);
1531 delete(key, isStrictContext());
1532 return oldValue;
1533 }
1535 /**
1536 * Delete a property from the ScriptObject.
1537 * (to help ScriptObjectMirror implementation)
1538 *
1539 * @param key the key of the property
1540 * @return if the delete was successful or not
1541 */
1542 public boolean delete(final Object key) {
1543 return delete(key, isStrictContext());
1544 }
1546 /**
1547 * Return the size of the ScriptObject - i.e. the number of properties
1548 * it contains
1549 * (java.util.Map-like method to help ScriptObjectMirror implementation)
1550 *
1551 * @return number of properties in ScriptObject
1552 */
1553 public int size() {
1554 int n = 0;
1555 for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) {
1556 n++;
1557 }
1558 return n;
1559 }
1561 /**
1562 * Return the values of the properties in the ScriptObject
1563 * (java.util.Map-like method to help ScriptObjectMirror implementation)
1564 *
1565 * @return collection of values for the properties in this ScriptObject
1566 */
1567 public Collection<Object> values() {
1568 final List<Object> values = new ArrayList<>(size());
1569 final Iterator<Object> iter = valueIterator();
1570 while (iter.hasNext()) {
1571 values.add(iter.next());
1572 }
1573 return Collections.unmodifiableList(values);
1574 }
1576 /**
1577 * Lookup method that, given a CallSiteDescriptor, looks up the target
1578 * MethodHandle and creates a GuardedInvocation
1579 * with the appropriate guard(s).
1580 *
1581 * @param desc call site descriptor
1582 * @param request the link request
1583 *
1584 * @return GuardedInvocation for the callsite
1585 */
1586 public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
1587 final int c = desc.getNameTokenCount();
1588 // JavaScript is "immune" to all currently defined Dynalink composite operation - getProp is the same as getElem
1589 // is the same as getMethod as JavaScript objects have a single namespace for all three. Therefore, we don't
1590 // care about them, and just link to whatever is the first operation.
1591 final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
1592 // NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself
1593 // emits "dyn:getProp:identifier" for "<expr>.<identifier>" and "dyn:getElem" for "<expr>[<expr>]", but we are
1594 // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the
1595 // operation has an associated name or not.
1596 switch (operator) {
1597 case "getProp":
1598 case "getElem":
1599 case "getMethod":
1600 return c > 2 ? findGetMethod(desc, request, operator) : findGetIndexMethod(desc, request);
1601 case "setProp":
1602 case "setElem":
1603 return c > 2 ? findSetMethod(desc, request) : findSetIndexMethod(desc);
1604 case "call":
1605 return findCallMethod(desc, request);
1606 case "new":
1607 return findNewMethod(desc);
1608 case "callMethod":
1609 return findCallMethodMethod(desc, request);
1610 default:
1611 return null;
1612 }
1613 }
1615 /**
1616 * Find the appropriate New method for an invoke dynamic call.
1617 *
1618 * @param desc The invoke dynamic call site descriptor.
1619 *
1620 * @return GuardedInvocation to be invoked at call site.
1621 */
1622 protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
1623 return notAFunction();
1624 }
1626 /**
1627 * Find the appropriate CALL method for an invoke dynamic call.
1628 * This generates "not a function" always
1629 *
1630 * @param desc the call site descriptor.
1631 * @param request the link request
1632 *
1633 * @return GuardedInvocation to be invoed at call site.
1634 */
1635 protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1636 return notAFunction();
1637 }
1639 private GuardedInvocation notAFunction() {
1640 throw typeError("not.a.function", ScriptRuntime.safeToString(this));
1641 }
1643 /**
1644 * Find an implementation for a "dyn:callMethod" operation. Note that Nashorn internally never uses
1645 * "dyn:callMethod", but instead always emits two call sites in bytecode, one for "dyn:getMethod", and then another
1646 * one for "dyn:call". Explicit support for "dyn:callMethod" is provided for the benefit of potential external
1647 * callers. The implementation itself actually folds a "dyn:getMethod" method handle into a "dyn:call" method handle.
1648 *
1649 * @param desc the call site descriptor.
1650 * @param request the link request
1651 *
1652 * @return GuardedInvocation to be invoked at call site.
1653 */
1654 protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1655 // R(P0, P1, ...)
1656 final MethodType callType = desc.getMethodType();
1657 // use type Object(P0) for the getter
1658 final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0)));
1659 final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod");
1661 // Object(P0) => Object(P0, P1, ...)
1662 final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount()));
1663 // R(Object, P0, P1, ...)
1664 final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
1665 // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
1666 return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
1667 }
1669 /**
1670 * Find the appropriate GET method for an invoke dynamic call.
1671 *
1672 * @param desc the call site descriptor
1673 * @param request the link request
1674 * @param operator operator for get: getProp, getMethod, getElem etc
1675 *
1676 * @return GuardedInvocation to be invoked at call site.
1677 */
1678 protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
1679 final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1680 final FindProperty find = findProperty(name, true);
1682 MethodHandle methodHandle;
1684 if (find == null) {
1685 if ("getProp".equals(operator)) {
1686 return noSuchProperty(desc, request);
1687 } else if ("getMethod".equals(operator)) {
1688 return noSuchMethod(desc, request);
1689 } else if ("getElem".equals(operator)) {
1690 return createEmptyGetter(desc, name);
1691 }
1692 throw new AssertionError(); // never invoked with any other operation
1693 }
1695 if (request.isCallSiteUnstable()) {
1696 return findMegaMorphicGetMethod(desc, name);
1697 }
1699 final Class<?> returnType = desc.getMethodType().returnType();
1700 final Property property = find.getProperty();
1701 methodHandle = find.getGetter(returnType);
1703 // getMap() is fine as we have the prototype switchpoint depending on where the property was found
1704 final MethodHandle guard = NashornGuards.getMapGuard(getMap());
1706 if (methodHandle != null) {
1707 assert methodHandle.type().returnType().equals(returnType);
1708 if (find.isSelf()) {
1709 return new GuardedInvocation(methodHandle, ObjectClassGenerator.OBJECT_FIELDS_ONLY &&
1710 NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType() ? null : guard);
1711 }
1713 final ScriptObject prototype = find.getOwner();
1715 if (!property.hasGetterFunction()) {
1716 methodHandle = bindTo(methodHandle, prototype);
1717 }
1718 return new GuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(proto, name), guard);
1719 }
1721 assert !NashornCallSiteDescriptor.isFastScope(desc);
1722 return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard);
1723 }
1725 private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) {
1726 final MethodType mhType = desc.getMethodType().insertParameterTypes(1, Object.class);
1727 final GuardedInvocation inv = findGetIndexMethod(mhType);
1729 return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
1730 }
1732 /**
1733 * Find the appropriate GETINDEX method for an invoke dynamic call.
1734 *
1735 * @param desc the call site descriptor
1736 * @param request the link request
1737 *
1738 * @return GuardedInvocation to be invoked at call site.
1739 */
1740 protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1741 return findGetIndexMethod(desc.getMethodType());
1742 }
1744 /**
1745 * Find the appropriate GETINDEX method for an invoke dynamic call.
1746 *
1747 * @param callType the call site method type
1748 * @return GuardedInvocation to be invoked at call site.
1749 */
1750 private static GuardedInvocation findGetIndexMethod(final MethodType callType) {
1751 final Class<?> returnClass = callType.returnType();
1752 final Class<?> keyClass = callType.parameterType(1);
1754 String name = "get";
1755 if (returnClass.isPrimitive()) {
1756 //turn e.g. get with a double into getDouble
1757 final String returnTypeName = returnClass.getName();
1758 name += Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
1759 }
1761 return new GuardedInvocation(findOwnMH(name, returnClass, keyClass), getScriptObjectGuard(callType));
1762 }
1764 private static MethodHandle getScriptObjectGuard(final MethodType type) {
1765 return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard();
1766 }
1768 /**
1769 * Find the appropriate SET method for an invoke dynamic call.
1770 *
1771 * @param desc the call site descriptor
1772 * @param request the link request
1773 *
1774 * @return GuardedInvocation to be invoked at call site.
1775 */
1776 protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1777 final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1778 if(request.isCallSiteUnstable()) {
1779 return findMegaMorphicSetMethod(desc, name);
1780 }
1782 final boolean scope = isScope();
1783 /*
1784 * If doing property set on a scope object, we should stop proto search on the first
1785 * non-scope object. Without this, for example, when assigning "toString" on global scope,
1786 * we'll end up assigning it on it's proto - which is Object.prototype.toString !!
1787 *
1788 * toString = function() { print("global toString"); } // don't affect Object.prototype.toString
1789 */
1790 FindProperty find = findProperty(name, true, scope, this);
1791 // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors.
1792 if (!scope && find != null && find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
1793 // We should still check if inherited data property is not writable
1794 if (isExtensible() && !find.getProperty().isWritable()) {
1795 return createEmptySetMethod(desc, "property.not.writable", false);
1796 }
1797 // Otherwise, forget the found property
1798 find = null;
1799 }
1801 if (find != null) {
1802 if(!find.getProperty().isWritable()) {
1803 // Existing, non-writable property
1804 return createEmptySetMethod(desc, "property.not.writable", true);
1805 }
1806 } else if (!isExtensible()) {
1807 // Non-existing property on a non-extensible object
1808 return createEmptySetMethod(desc, "object.non.extensible", false);
1809 }
1811 return new SetMethodCreator(this, find, desc).createGuardedInvocation();
1812 }
1814 private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, String strictErrorMessage, boolean canBeFastScope) {
1815 final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1816 if (NashornCallSiteDescriptor.isStrict(desc)) {
1817 throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString((this)));
1818 }
1819 assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
1820 final PropertyMap myMap = getMap();
1821 return new GuardedInvocation(Lookup.EMPTY_SETTER, myMap.getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(myMap));
1822 }
1824 @SuppressWarnings("unused")
1825 private static void setField(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final MethodHandle setter, final Object self, final Object value) throws Throwable {
1826 final ScriptObject obj = (ScriptObject)self;
1827 final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
1828 if (!obj.isExtensible()) {
1829 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
1830 } else if (obj.compareAndSetMap(oldMap, newMap)) {
1831 setter.invokeExact(self, value);
1832 } else {
1833 obj.set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
1834 }
1835 }
1837 @SuppressWarnings("unused")
1838 private static void setSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
1839 final ScriptObject obj = (ScriptObject)self;
1840 if (obj.trySetSpill(desc, oldMap, newMap, value)) {
1841 obj.spill[index] = value;
1842 }
1843 }
1845 private boolean trySetSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final Object value) {
1846 final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
1847 if (!isExtensible() && isStrict) {
1848 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(this));
1849 } else if (compareAndSetMap(oldMap, newMap)) {
1850 return true;
1851 } else {
1852 set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
1853 return false;
1854 }
1855 }
1857 @SuppressWarnings("unused")
1858 private static void setSpillWithNew(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
1859 final ScriptObject obj = (ScriptObject)self;
1860 final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
1862 if (!obj.isExtensible()) {
1863 if (isStrict) {
1864 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
1865 }
1866 } else if (obj.compareAndSetMap(oldMap, newMap)) {
1867 obj.spill = new Object[SPILL_RATE];
1868 obj.spill[index] = value;
1869 } else {
1870 obj.set(desc.getNameToken(2), value, isStrict);
1871 }
1872 }
1874 @SuppressWarnings("unused")
1875 private static void setSpillWithGrow(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final int newLength, final Object self, final Object value) {
1876 final ScriptObject obj = (ScriptObject)self;
1877 final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
1879 if (!obj.isExtensible()) {
1880 if (isStrict) {
1881 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
1882 }
1883 } else if (obj.compareAndSetMap(oldMap, newMap)) {
1884 final int oldLength = obj.spill.length;
1885 final Object[] newSpill = new Object[newLength];
1886 System.arraycopy(obj.spill, 0, newSpill, 0, oldLength);
1887 obj.spill = newSpill;
1888 obj.spill[index] = value;
1889 } else {
1890 obj.set(desc.getNameToken(2), value, isStrict);
1891 }
1892 }
1894 private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
1895 final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
1896 final GuardedInvocation inv = findSetIndexMethod(type, NashornCallSiteDescriptor.isStrict(desc));
1897 return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
1898 }
1900 private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc) { // array, index, value
1901 return findSetIndexMethod(desc.getMethodType(), NashornCallSiteDescriptor.isStrict(desc));
1902 }
1904 /**
1905 * Find the appropriate SETINDEX method for an invoke dynamic call.
1906 *
1907 * @param callType the method type at the call site
1908 * @param isStrict are we in strict mode?
1909 *
1910 * @return GuardedInvocation to be invoked at call site.
1911 */
1912 private static GuardedInvocation findSetIndexMethod(final MethodType callType, final boolean isStrict) {
1913 assert callType.parameterCount() == 3;
1915 final Class<?> keyClass = callType.parameterType(1);
1916 final Class<?> valueClass = callType.parameterType(2);
1918 MethodHandle methodHandle = findOwnMH("set", void.class, keyClass, valueClass, boolean.class);
1919 methodHandle = MH.insertArguments(methodHandle, 3, isStrict);
1921 return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType));
1922 }
1924 /**
1925 * Fall back if a function property is not found.
1926 * @param desc The call site descriptor
1927 * @param request the link request
1928 * @return GuardedInvocation to be invoked at call site.
1929 */
1930 public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1931 final String name = desc.getNameToken(2);
1932 final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true);
1933 final boolean scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc);
1935 if (find == null) {
1936 return noSuchProperty(desc, request);
1937 }
1939 final ScriptFunction func = (ScriptFunction)getObjectValue(find);
1940 final Object thiz = scopeCall && func.isStrict() ? ScriptRuntime.UNDEFINED : this;
1941 // TODO: It'd be awesome if we could bind "name" without binding "this".
1942 return new GuardedInvocation(MH.dropArguments(MH.constant(ScriptFunction.class,
1943 func.makeBoundFunction(thiz, new Object[] { name })), 0, Object.class),
1944 null, NashornGuards.getMapGuard(getMap()));
1945 }
1947 /**
1948 * Fall back if a property is not found.
1949 * @param desc the call site descriptor.
1950 * @param request the link request
1951 * @return GuardedInvocation to be invoked at call site.
1952 */
1953 public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
1954 final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1955 final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
1956 final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
1958 if (find != null) {
1959 final ScriptFunction func = (ScriptFunction)getObjectValue(find);
1960 MethodHandle methodHandle = getCallMethodHandle(func, desc.getMethodType(), name);
1962 if (methodHandle != null) {
1963 if (scopeAccess && func.isStrict()) {
1964 methodHandle = bindTo(methodHandle, UNDEFINED);
1965 }
1966 return new GuardedInvocation(methodHandle,
1967 find.isInherited()? getMap().getProtoGetSwitchPoint(proto, NO_SUCH_PROPERTY_NAME) : null,
1968 getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func));
1969 }
1970 }
1972 if (scopeAccess) {
1973 throw referenceError("not.defined", name);
1974 }
1976 return createEmptyGetter(desc, name);
1977 }
1978 /**
1979 * Invoke fall back if a property is not found.
1980 * @param name Name of property.
1981 * @return Result from call.
1982 */
1983 private Object invokeNoSuchProperty(final String name) {
1984 final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
1986 if (find != null) {
1987 final Object func = getObjectValue(find);
1989 if (func instanceof ScriptFunction) {
1990 return ScriptRuntime.apply((ScriptFunction)func, this, name);
1991 }
1992 }
1994 return UNDEFINED;
1995 }
1997 private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
1998 return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(getMap()));
1999 }
2001 private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
2002 protected T[] values;
2003 protected final ScriptObject object;
2004 private int index;
2006 ScriptObjectIterator(final ScriptObject object) {
2007 this.object = object;
2008 }
2010 protected abstract void init();
2012 @Override
2013 public boolean hasNext() {
2014 if (values == null) {
2015 init();
2016 }
2017 return index < values.length;
2018 }
2020 @Override
2021 public T next() {
2022 if (values == null) {
2023 init();
2024 }
2025 return values[index++];
2026 }
2028 @Override
2029 public void remove() {
2030 throw new UnsupportedOperationException();
2031 }
2032 }
2034 private static class KeyIterator extends ScriptObjectIterator<String> {
2035 KeyIterator(final ScriptObject object) {
2036 super(object);
2037 }
2039 @Override
2040 protected void init() {
2041 final Set<String> keys = new LinkedHashSet<>();
2042 for (ScriptObject self = object; self != null; self = self.getProto()) {
2043 keys.addAll(Arrays.asList(self.getOwnKeys(false)));
2044 }
2045 this.values = keys.toArray(new String[keys.size()]);
2046 }
2047 }
2049 private static class ValueIterator extends ScriptObjectIterator<Object> {
2050 ValueIterator(final ScriptObject object) {
2051 super(object);
2052 }
2054 @Override
2055 protected void init() {
2056 final ArrayList<Object> valueList = new ArrayList<>();
2057 for (ScriptObject self = object; self != null; self = self.getProto()) {
2058 for (final String key : self.getOwnKeys(false)) {
2059 valueList.add(self.get(key));
2060 }
2061 }
2062 this.values = valueList.toArray(new Object[valueList.size()]);
2063 }
2064 }
2066 /**
2067 * Add a spill property for the given key.
2068 * @param key Property key.
2069 * @param propertyFlags Property flags.
2070 * @return Added property.
2071 */
2072 private Property addSpillProperty(final String key, final int propertyFlags) {
2073 int fieldCount = getMap().getFieldCount();
2074 int fieldMaximum = getMap().getFieldMaximum();
2075 Property property;
2077 if (fieldCount < fieldMaximum) {
2078 property = new AccessorProperty(key, propertyFlags & ~Property.IS_SPILL, getClass(), fieldCount);
2079 notifyPropertyAdded(this, property);
2080 property = addOwnProperty(property);
2081 } else {
2082 int i = getMap().getSpillLength();
2083 property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i);
2084 notifyPropertyAdded(this, property);
2085 property = addOwnProperty(property);
2086 i = property.getSlot();
2088 final int newLength = (i + SPILL_RATE) / SPILL_RATE * SPILL_RATE;
2090 if (spill == null || newLength > spill.length) {
2091 final Object[] newSpill = new Object[newLength];
2093 if (spill != null) {
2094 System.arraycopy(spill, 0, newSpill, 0, spill.length);
2095 }
2097 spill = newSpill;
2098 }
2099 }
2101 return property;
2102 }
2105 /**
2106 * Add a spill entry for the given key.
2107 * @param key Property key.
2108 * @return Setter method handle.
2109 */
2110 MethodHandle addSpill(final String key) {
2111 final Property spillProperty = addSpillProperty(key, 0);
2112 final Class<?> type = Object.class;
2113 return spillProperty.getSetter(type, getMap()); //TODO specfields
2114 }
2116 /**
2117 * Make sure arguments are paired correctly, with respect to more parameters than declared,
2118 * fewer parameters than declared and other things that JavaScript allows. This might involve
2119 * creating collectors.
2120 *
2121 * @param methodHandle method handle for invoke
2122 * @param callType type of the call
2123 *
2124 * @return method handle with adjusted arguments
2125 */
2126 protected static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType) {
2127 return pairArguments(methodHandle, callType, null);
2128 }
2130 /**
2131 * Make sure arguments are paired correctly, with respect to more parameters than declared,
2132 * fewer parameters than declared and other things that JavaScript allows. This might involve
2133 * creating collectors.
2134 *
2135 * Make sure arguments are paired correctly.
2136 * @param methodHandle MethodHandle to adjust.
2137 * @param callType MethodType of the call site.
2138 * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the
2139 * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a
2140 * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites
2141 * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters.
2142 *
2143 * @return method handle with adjusted arguments
2144 */
2145 public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) {
2147 final MethodType methodType = methodHandle.type();
2148 if (methodType.equals(callType)) {
2149 return methodHandle;
2150 }
2152 final int parameterCount = methodType.parameterCount();
2153 final int callCount = callType.parameterCount();
2155 final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
2156 final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : (callCount > 0 &&
2157 callType.parameterType(callCount - 1).isArray());
2159 if (callCount < parameterCount) {
2160 final int missingArgs = parameterCount - callCount;
2161 final Object[] fillers = new Object[missingArgs];
2163 Arrays.fill(fillers, UNDEFINED);
2165 if (isCalleeVarArg) {
2166 fillers[missingArgs - 1] = new Object[0];
2167 }
2169 return MH.insertArguments(
2170 methodHandle,
2171 parameterCount - missingArgs,
2172 fillers);
2173 }
2175 if (isCalleeVarArg) {
2176 return isCallerVarArg ?
2177 methodHandle :
2178 MH.asCollector(methodHandle, Object[].class, callCount - parameterCount + 1);
2179 }
2181 if (isCallerVarArg) {
2182 final int spreadArgs = parameterCount - callCount + 1;
2183 return MH.filterArguments(
2184 MH.asSpreader(
2185 methodHandle,
2186 Object[].class,
2187 spreadArgs),
2188 callCount - 1,
2189 MH.insertArguments(
2190 TRUNCATINGFILTER,
2191 0,
2192 spreadArgs)
2193 );
2194 }
2196 if (callCount > parameterCount) {
2197 final int discardedArgs = callCount - parameterCount;
2199 final Class<?>[] discards = new Class<?>[discardedArgs];
2200 Arrays.fill(discards, Object.class);
2202 return MH.dropArguments(methodHandle, callCount - discardedArgs, discards);
2203 }
2205 return methodHandle;
2206 }
2208 @SuppressWarnings("unused")
2209 private static Object[] truncatingFilter(final int n, final Object[] array) {
2210 final int length = array == null ? 0 : array.length;
2211 if (n == length) {
2212 return array == null ? new Object[0] : array;
2213 }
2215 final Object[] newArray = new Object[n];
2217 if (array != null) {
2218 for (int i = 0; i < n && i < length; i++) {
2219 newArray[i] = array[i];
2220 }
2221 }
2223 if (length < n) {
2224 final Object fill = UNDEFINED;
2226 for (int i = length; i < n; i++) {
2227 newArray[i] = fill;
2228 }
2229 }
2231 return newArray;
2232 }
2234 /**
2235 * Numeric length setter for length property
2236 *
2237 * @param newLength new length to set
2238 */
2239 public final void setLength(final long newLength) {
2240 final long arrayLength = getArray().length();
2241 if (newLength == arrayLength) {
2242 return;
2243 }
2245 final boolean isStrict = isStrictContext();
2247 if (newLength > arrayLength) {
2248 setArray(getArray().ensure(newLength - 1));
2249 if (getArray().canDelete(arrayLength, (newLength - 1), isStrict)) {
2250 setArray(getArray().delete(arrayLength, (newLength - 1)));
2251 }
2252 return;
2253 }
2255 if (newLength < arrayLength) {
2256 setArray(getArray().shrink(newLength));
2257 getArray().setLength(newLength);
2258 }
2259 }
2261 private int getInt(final int index, final String key) {
2262 if (ArrayIndex.isValidArrayIndex(index)) {
2263 for (ScriptObject object = this; ; ) {
2264 final FindProperty find = object.findProperty(key, false, false, this);
2266 if (find != null) {
2267 return getIntValue(find);
2268 }
2270 if ((object = object.getProto()) == null) {
2271 break;
2272 }
2274 final ArrayData array = object.getArray();
2276 if (array.has(index)) {
2277 return array.getInt(index);
2278 }
2279 }
2280 } else {
2281 final FindProperty find = findProperty(key, true);
2283 if (find != null) {
2284 return getIntValue(find);
2285 }
2286 }
2288 return JSType.toInt32(invokeNoSuchProperty(key));
2289 }
2291 @Override
2292 public int getInt(final Object key) {
2293 final int index = ArrayIndex.getArrayIndex(key);
2294 final ArrayData array = getArray();
2296 if (array.has(index)) {
2297 return array.getInt(index);
2298 }
2300 return getInt(index, convertKey(key));
2301 }
2303 @Override
2304 public int getInt(final double key) {
2305 final int index = ArrayIndex.getArrayIndex(key);
2306 final ArrayData array = getArray();
2308 if (array.has(index)) {
2309 return array.getInt(index);
2310 }
2312 return getInt(index, convertKey(key));
2313 }
2315 @Override
2316 public int getInt(final long key) {
2317 final int index = ArrayIndex.getArrayIndex(key);
2318 final ArrayData array = getArray();
2320 if (array.has(index)) {
2321 return array.getInt(index);
2322 }
2324 return getInt(index, convertKey(key));
2325 }
2327 @Override
2328 public int getInt(final int key) {
2329 final ArrayData array = getArray();
2331 if (array.has(key)) {
2332 return array.getInt(key);
2333 }
2335 return getInt(key, convertKey(key));
2336 }
2338 private long getLong(final int index, final String key) {
2339 if (ArrayIndex.isValidArrayIndex(index)) {
2340 for (ScriptObject object = this; ; ) {
2341 final FindProperty find = object.findProperty(key, false, false, this);
2343 if (find != null) {
2344 return getLongValue(find);
2345 }
2347 if ((object = object.getProto()) == null) {
2348 break;
2349 }
2351 final ArrayData array = object.getArray();
2353 if (array.has(index)) {
2354 return array.getLong(index);
2355 }
2356 }
2357 } else {
2358 final FindProperty find = findProperty(key, true);
2360 if (find != null) {
2361 return getLongValue(find);
2362 }
2363 }
2365 return JSType.toLong(invokeNoSuchProperty(key));
2366 }
2368 @Override
2369 public long getLong(final Object key) {
2370 final int index = ArrayIndex.getArrayIndex(key);
2371 final ArrayData array = getArray();
2373 if (array.has(index)) {
2374 return array.getLong(index);
2375 }
2377 return getLong(index, convertKey(key));
2378 }
2380 @Override
2381 public long getLong(final double key) {
2382 final int index = ArrayIndex.getArrayIndex(key);
2383 final ArrayData array = getArray();
2385 if (array.has(index)) {
2386 return array.getLong(index);
2387 }
2389 return getLong(index, convertKey(key));
2390 }
2392 @Override
2393 public long getLong(final long key) {
2394 final int index = ArrayIndex.getArrayIndex(key);
2395 final ArrayData array = getArray();
2397 if (array.has(index)) {
2398 return array.getLong(index);
2399 }
2401 return getLong(index, convertKey(key));
2402 }
2404 @Override
2405 public long getLong(final int key) {
2406 final ArrayData array = getArray();
2408 if (array.has(key)) {
2409 return array.getLong(key);
2410 }
2412 return getLong(key, convertKey(key));
2413 }
2415 private double getDouble(final int index, final String key) {
2416 if (ArrayIndex.isValidArrayIndex(index)) {
2417 for (ScriptObject object = this; ; ) {
2418 final FindProperty find = object.findProperty(key, false, false, this);
2420 if (find != null) {
2421 return getDoubleValue(find);
2422 }
2424 if ((object = object.getProto()) == null) {
2425 break;
2426 }
2428 final ArrayData array = object.getArray();
2430 if (array.has(index)) {
2431 return array.getDouble(index);
2432 }
2433 }
2434 } else {
2435 final FindProperty find = findProperty(key, true);
2437 if (find != null) {
2438 return getDoubleValue(find);
2439 }
2440 }
2442 return JSType.toNumber(invokeNoSuchProperty(key));
2443 }
2445 @Override
2446 public double getDouble(final Object key) {
2447 final int index = ArrayIndex.getArrayIndex(key);
2448 final ArrayData array = getArray();
2450 if (array.has(index)) {
2451 return array.getDouble(index);
2452 }
2454 return getDouble(index, convertKey(key));
2455 }
2457 @Override
2458 public double getDouble(final double key) {
2459 final int index = ArrayIndex.getArrayIndex(key);
2460 final ArrayData array = getArray();
2462 if (array.has(index)) {
2463 return array.getDouble(index);
2464 }
2466 return getDouble(index, convertKey(key));
2467 }
2469 @Override
2470 public double getDouble(final long key) {
2471 final int index = ArrayIndex.getArrayIndex(key);
2472 final ArrayData array = getArray();
2474 if (array.has(index)) {
2475 return array.getDouble(index);
2476 }
2478 return getDouble(index, convertKey(key));
2479 }
2481 @Override
2482 public double getDouble(final int key) {
2483 final ArrayData array = getArray();
2485 if (array.has(key)) {
2486 return array.getDouble(key);
2487 }
2489 return getDouble(key, convertKey(key));
2490 }
2492 private Object get(final int index, final String key) {
2493 if (ArrayIndex.isValidArrayIndex(index)) {
2494 for (ScriptObject object = this; ; ) {
2495 final FindProperty find = object.findProperty(key, false, false, this);
2497 if (find != null) {
2498 return getObjectValue(find);
2499 }
2501 if ((object = object.getProto()) == null) {
2502 break;
2503 }
2505 final ArrayData array = object.getArray();
2507 if (array.has(index)) {
2508 return array.getObject(index);
2509 }
2510 }
2511 } else {
2512 final FindProperty find = findProperty(key, true);
2514 if (find != null) {
2515 return getObjectValue(find);
2516 }
2517 }
2519 return invokeNoSuchProperty(key);
2520 }
2522 @Override
2523 public Object get(final Object key) {
2524 final int index = ArrayIndex.getArrayIndex(key);
2525 final ArrayData array = getArray();
2527 if (array.has(index)) {
2528 return array.getObject(index);
2529 }
2531 return get(index, convertKey(key));
2532 }
2534 @Override
2535 public Object get(final double key) {
2536 final int index = ArrayIndex.getArrayIndex(key);
2537 final ArrayData array = getArray();
2539 if (array.has(index)) {
2540 return array.getObject(index);
2541 }
2543 return get(index, convertKey(key));
2544 }
2546 @Override
2547 public Object get(final long key) {
2548 final int index = ArrayIndex.getArrayIndex(key);
2549 final ArrayData array = getArray();
2551 if (array.has(index)) {
2552 return array.getObject(index);
2553 }
2555 return get(index, convertKey(key));
2556 }
2558 @Override
2559 public Object get(final int key) {
2560 final ArrayData array = getArray();
2562 if (array.has(key)) {
2563 return array.getObject(key);
2564 }
2566 return get(key, convertKey(key));
2567 }
2569 /**
2570 * Handle when an array doesn't have a slot - possibly grow and/or convert array.
2571 *
2572 * @param index key as index
2573 * @param value element value
2574 * @param strict are we in strict mode
2575 */
2576 private void doesNotHave(final int index, final Object value, final boolean strict) {
2577 final long oldLength = getArray().length();
2578 final long longIndex = index & JSType.MAX_UINT;
2580 if (!getArray().has(index)) {
2581 final String key = convertKey(longIndex);
2582 final FindProperty find = findProperty(key, true);
2584 if (find != null) {
2585 setObject(find, strict, key, value);
2586 return;
2587 }
2588 }
2590 if (longIndex >= oldLength) {
2591 if (!isExtensible()) {
2592 if (strict) {
2593 throw typeError("object.non.extensible", JSType.toString(index), ScriptRuntime.safeToString(this));
2594 }
2595 return;
2596 }
2597 setArray(getArray().ensure(longIndex));
2598 }
2600 if (value instanceof Integer) {
2601 setArray(getArray().set(index, (int)value, strict));
2602 } else if (value instanceof Long) {
2603 setArray(getArray().set(index, (long)value, strict));
2604 } else if (value instanceof Double) {
2605 setArray(getArray().set(index, (double)value, strict));
2606 } else {
2607 setArray(getArray().set(index, value, strict));
2608 }
2610 if (longIndex > oldLength) {
2611 ArrayData array = getArray();
2613 if (array.canDelete(oldLength, (longIndex - 1), strict)) {
2614 array = array.delete(oldLength, (longIndex - 1));
2615 }
2617 setArray(array);
2618 }
2619 }
2621 /**
2622 * This is the most generic of all Object setters. Most of the others use this in some form.
2623 * TODO: should be further specialized
2624 *
2625 * @param find found property
2626 * @param strict are we in strict mode
2627 * @param key property key
2628 * @param value property value
2629 */
2630 public final void setObject(final FindProperty find, final boolean strict, final String key, final Object value) {
2631 FindProperty f = find;
2633 if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) {
2634 f = null;
2635 }
2637 if (f != null) {
2638 if (!f.getProperty().isWritable()) {
2639 if (strict) {
2640 throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this));
2641 }
2643 return;
2644 }
2646 f.setObjectValue(value, strict);
2648 } else if (!isExtensible()) {
2649 if (strict) {
2650 throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
2651 }
2652 } else {
2653 spill(key, value);
2654 }
2655 }
2657 private void spill(final String key, final Object value) {
2658 addSpillProperty(key, 0).setObjectValue(this, this, value, false);
2659 }
2662 @Override
2663 public void set(final Object key, final int value, final boolean strict) {
2664 final int index = ArrayIndex.getArrayIndex(key);
2666 if (ArrayIndex.isValidArrayIndex(index)) {
2667 if (getArray().has(index)) {
2668 setArray(getArray().set(index, value, strict));
2669 } else {
2670 doesNotHave(index, value, strict);
2671 }
2673 return;
2674 }
2676 set(key, JSType.toObject(value), strict);
2677 }
2679 @Override
2680 public void set(final Object key, final long value, final boolean strict) {
2681 final int index = ArrayIndex.getArrayIndex(key);
2683 if (ArrayIndex.isValidArrayIndex(index)) {
2684 if (getArray().has(index)) {
2685 setArray(getArray().set(index, value, strict));
2686 } else {
2687 doesNotHave(index, value, strict);
2688 }
2690 return;
2691 }
2693 set(key, JSType.toObject(value), strict);
2694 }
2696 @Override
2697 public void set(final Object key, final double value, final boolean strict) {
2698 final int index = ArrayIndex.getArrayIndex(key);
2700 if (ArrayIndex.isValidArrayIndex(index)) {
2701 if (getArray().has(index)) {
2702 setArray(getArray().set(index, value, strict));
2703 } else {
2704 doesNotHave(index, value, strict);
2705 }
2707 return;
2708 }
2710 set(key, JSType.toObject(value), strict);
2711 }
2713 @Override
2714 public void set(final Object key, final Object value, final boolean strict) {
2715 final int index = ArrayIndex.getArrayIndex(key);
2717 if (ArrayIndex.isValidArrayIndex(index)) {
2718 if (getArray().has(index)) {
2719 setArray(getArray().set(index, value, strict));
2720 } else {
2721 doesNotHave(index, value, strict);
2722 }
2724 return;
2725 }
2727 final String propName = convertKey(key);
2728 final FindProperty find = findProperty(propName, true);
2730 setObject(find, strict, propName, value);
2731 }
2733 @Override
2734 public void set(final double key, final int value, final boolean strict) {
2735 final int index = ArrayIndex.getArrayIndex(key);
2737 if (ArrayIndex.isValidArrayIndex(index)) {
2738 if (getArray().has(index)) {
2739 setArray(getArray().set(index, value, strict));
2740 } else {
2741 doesNotHave(index, value, strict);
2742 }
2744 return;
2745 }
2747 set(JSType.toObject(key), JSType.toObject(value), strict);
2748 }
2750 @Override
2751 public void set(final double key, final long value, final boolean strict) {
2752 final int index = ArrayIndex.getArrayIndex(key);
2754 if (ArrayIndex.isValidArrayIndex(index)) {
2755 if (getArray().has(index)) {
2756 setArray(getArray().set(index, value, strict));
2757 } else {
2758 doesNotHave(index, value, strict);
2759 }
2761 return;
2762 }
2764 set(JSType.toObject(key), JSType.toObject(value), strict);
2765 }
2767 @Override
2768 public void set(final double key, final double value, final boolean strict) {
2769 final int index = ArrayIndex.getArrayIndex(key);
2771 if (ArrayIndex.isValidArrayIndex(index)) {
2772 if (getArray().has(index)) {
2773 setArray(getArray().set(index, value, strict));
2774 } else {
2775 doesNotHave(index, value, strict);
2776 }
2778 return;
2779 }
2781 set(JSType.toObject(key), JSType.toObject(value), strict);
2782 }
2784 @Override
2785 public void set(final double key, final Object value, final boolean strict) {
2786 final int index = ArrayIndex.getArrayIndex(key);
2788 if (ArrayIndex.isValidArrayIndex(index)) {
2789 if (getArray().has(index)) {
2790 setArray(getArray().set(index, value, strict));
2791 } else {
2792 doesNotHave(index, value, strict);
2793 }
2795 return;
2796 }
2798 set(JSType.toObject(key), value, strict);
2799 }
2801 @Override
2802 public void set(final long key, final int value, final boolean strict) {
2803 final int index = ArrayIndex.getArrayIndex(key);
2805 if (ArrayIndex.isValidArrayIndex(index)) {
2806 if (getArray().has(index)) {
2807 setArray(getArray().set(index, value, strict));
2808 } else {
2809 doesNotHave(index, value, strict);
2810 }
2812 return;
2813 }
2815 set(JSType.toObject(key), JSType.toObject(value), strict);
2816 }
2818 @Override
2819 public void set(final long key, final long value, final boolean strict) {
2820 final int index = ArrayIndex.getArrayIndex(key);
2822 if (ArrayIndex.isValidArrayIndex(index)) {
2823 if (getArray().has(index)) {
2824 setArray(getArray().set(index, value, strict));
2825 } else {
2826 doesNotHave(index, value, strict);
2827 }
2829 return;
2830 }
2832 set(JSType.toObject(key), JSType.toObject(value), strict);
2833 }
2835 @Override
2836 public void set(final long key, final double value, final boolean strict) {
2837 final int index = ArrayIndex.getArrayIndex(key);
2839 if (ArrayIndex.isValidArrayIndex(index)) {
2840 if (getArray().has(index)) {
2841 setArray(getArray().set(index, value, strict));
2842 } else {
2843 doesNotHave(index, value, strict);
2844 }
2846 return;
2847 }
2849 set(JSType.toObject(key), JSType.toObject(value), strict);
2850 }
2852 @Override
2853 public void set(final long key, final Object value, final boolean strict) {
2854 final int index = ArrayIndex.getArrayIndex(key);
2856 if (ArrayIndex.isValidArrayIndex(index)) {
2857 if (getArray().has(index)) {
2858 setArray(getArray().set(index, value, strict));
2859 } else {
2860 doesNotHave(index, value, strict);
2861 }
2863 return;
2864 }
2866 set(JSType.toObject(key), value, strict);
2867 }
2869 @Override
2870 public void set(final int key, final int value, final boolean strict) {
2871 final int index = ArrayIndex.getArrayIndex(key);
2873 if (ArrayIndex.isValidArrayIndex(index)) {
2874 if (getArray().has(index)) {
2875 setArray(getArray().set(index, value, strict));
2876 } else {
2877 doesNotHave(index, value, strict);
2878 }
2880 return;
2881 }
2883 set(JSType.toObject(key), JSType.toObject(value), strict);
2884 }
2886 @Override
2887 public void set(final int key, final long value, final boolean strict) {
2888 final int index = ArrayIndex.getArrayIndex(key);
2890 if (ArrayIndex.isValidArrayIndex(index)) {
2891 if (getArray().has(index)) {
2892 setArray(getArray().set(index, value, strict));
2893 } else {
2894 doesNotHave(index, value, strict);
2895 }
2897 return;
2898 }
2900 set(JSType.toObject(key), JSType.toObject(value), strict);
2901 }
2903 @Override
2904 public void set(final int key, final double value, final boolean strict) {
2905 final int index = ArrayIndex.getArrayIndex(key);
2907 if (ArrayIndex.isValidArrayIndex(index)) {
2908 if (getArray().has(index)) {
2909 setArray(getArray().set(index, value, strict));
2910 } else {
2911 doesNotHave(index, value, strict);
2912 }
2914 return;
2915 }
2917 set(JSType.toObject(key), JSType.toObject(value), strict);
2918 }
2920 @Override
2921 public void set(final int key, final Object value, final boolean strict) {
2922 final int index = ArrayIndex.getArrayIndex(key);
2924 if (ArrayIndex.isValidArrayIndex(index)) {
2925 if (getArray().has(index)) {
2926 setArray(getArray().set(index, value, strict));
2927 } else {
2928 doesNotHave(index, value, strict);
2929 }
2931 return;
2932 }
2934 set(JSType.toObject(key), value, strict);
2935 }
2937 @Override
2938 public boolean has(final Object key) {
2939 final int index = ArrayIndex.getArrayIndex(key);
2941 if (ArrayIndex.isValidArrayIndex(index)) {
2942 for (ScriptObject self = this; self != null; self = self.getProto()) {
2943 if (self.getArray().has(index)) {
2944 return true;
2945 }
2946 }
2947 }
2949 final FindProperty find = findProperty(convertKey(key), true);
2951 return find != null;
2952 }
2954 @Override
2955 public boolean has(final double key) {
2956 final int index = ArrayIndex.getArrayIndex(key);
2958 if (ArrayIndex.isValidArrayIndex(index)) {
2959 for (ScriptObject self = this; self != null; self = self.getProto()) {
2960 if (self.getArray().has(index)) {
2961 return true;
2962 }
2963 }
2964 }
2966 final FindProperty find = findProperty(convertKey(key), true);
2968 return find != null;
2969 }
2971 @Override
2972 public boolean has(final long key) {
2973 final int index = ArrayIndex.getArrayIndex(key);
2975 if (ArrayIndex.isValidArrayIndex(index)) {
2976 for (ScriptObject self = this; self != null; self = self.getProto()) {
2977 if (self.getArray().has(index)) {
2978 return true;
2979 }
2980 }
2981 }
2983 final FindProperty find = findProperty(convertKey(key), true);
2985 return find != null;
2986 }
2988 @Override
2989 public boolean has(final int key) {
2990 final int index = ArrayIndex.getArrayIndex(key);
2992 if (ArrayIndex.isValidArrayIndex(index)) {
2993 for (ScriptObject self = this; self != null; self = self.getProto()) {
2994 if (self.getArray().has(index)) {
2995 return true;
2996 }
2997 }
2998 }
3000 final FindProperty find = findProperty(convertKey(key), true);
3002 return find != null;
3003 }
3005 @Override
3006 public boolean hasOwnProperty(final Object key) {
3007 final int index = ArrayIndex.getArrayIndex(key);
3009 if (getArray().has(index)) {
3010 return true;
3011 }
3013 final FindProperty find = findProperty(convertKey(key), false);
3015 return find != null;
3016 }
3018 @Override
3019 public boolean hasOwnProperty(final int key) {
3020 final int index = ArrayIndex.getArrayIndex(key);
3022 if (getArray().has(index)) {
3023 return true;
3024 }
3026 final FindProperty find = findProperty(convertKey(key), false);
3028 return find != null;
3029 }
3031 @Override
3032 public boolean hasOwnProperty(final long key) {
3033 final int index = ArrayIndex.getArrayIndex(key);
3035 if (getArray().has(index)) {
3036 return true;
3037 }
3039 final FindProperty find = findProperty(convertKey(key), false);
3041 return find != null;
3042 }
3044 @Override
3045 public boolean hasOwnProperty(final double key) {
3046 final int index = ArrayIndex.getArrayIndex(key);
3048 if (getArray().has(index)) {
3049 return true;
3050 }
3052 final FindProperty find = findProperty(convertKey(key), false);
3054 return find != null;
3055 }
3057 @Override
3058 public boolean delete(final int key, final boolean strict) {
3059 final int index = ArrayIndex.getArrayIndex(key);
3060 final ArrayData array = getArray();
3062 if (array.has(index)) {
3063 if (array.canDelete(index, strict)) {
3064 setArray(array.delete(index));
3065 return true;
3066 }
3067 return false;
3068 }
3070 return deleteObject(JSType.toObject(key), strict);
3071 }
3073 @Override
3074 public boolean delete(final long key, final boolean strict) {
3075 final int index = ArrayIndex.getArrayIndex(key);
3076 final ArrayData array = getArray();
3078 if (array.has(index)) {
3079 if (array.canDelete(index, strict)) {
3080 setArray(array.delete(index));
3081 return true;
3082 }
3083 return false;
3084 }
3086 return deleteObject(JSType.toObject(key), strict);
3087 }
3089 @Override
3090 public boolean delete(final double key, final boolean strict) {
3091 final int index = ArrayIndex.getArrayIndex(key);
3092 final ArrayData array = getArray();
3094 if (array.has(index)) {
3095 if (array.canDelete(index, strict)) {
3096 setArray(array.delete(index));
3097 return true;
3098 }
3099 return false;
3100 }
3102 return deleteObject(JSType.toObject(key), strict);
3103 }
3105 @Override
3106 public boolean delete(final Object key, final boolean strict) {
3107 final int index = ArrayIndex.getArrayIndex(key);
3108 final ArrayData array = getArray();
3110 if (array.has(index)) {
3111 if (array.canDelete(index, strict)) {
3112 setArray(array.delete(index));
3113 return true;
3114 }
3115 return false;
3116 }
3118 return deleteObject(key, strict);
3119 }
3121 private boolean deleteObject(final Object key, final boolean strict) {
3122 final String propName = convertKey(key);
3123 final FindProperty find = findProperty(propName, false);
3125 if (find == null) {
3126 return true;
3127 }
3129 if (!find.getProperty().isConfigurable()) {
3130 if (strict) {
3131 throw typeError("cant.delete.property", propName, ScriptRuntime.safeToString(this));
3132 }
3133 return false;
3134 }
3136 final Property prop = find.getProperty();
3137 notifyPropertyDeleted(this, prop);
3138 deleteOwnProperty(prop);
3140 return true;
3141 }
3143 /*
3144 * Make a new UserAccessorProperty property. getter and setter functions are stored in
3145 * this ScriptObject and slot values are used in property object.
3146 */
3147 private UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
3148 int oldSpillLength = getMap().getSpillLength();
3150 int getterSlot = oldSpillLength++;
3151 setSpill(getterSlot, getter);
3152 // if getter function is null, flag the slot to be negative (less by 1)
3153 if (getter == null) {
3154 getterSlot = -getterSlot - 1;
3155 }
3157 int setterSlot = oldSpillLength++;
3159 setSpill(setterSlot, setter);
3160 // if setter function is null, flag the slot to be negative (less by 1)
3161 if (setter == null) {
3162 setterSlot = -setterSlot - 1;
3163 }
3165 return new UserAccessorProperty(key, propertyFlags, getterSlot, setterSlot);
3166 }
3168 private void setSpill(final int slot, final Object value) {
3169 if (slot >= 0) {
3170 final int index = slot;
3171 if (spill == null) {
3172 // create new spill.
3173 spill = new Object[Math.max(index + 1, SPILL_RATE)];
3174 } else if (index >= spill.length) {
3175 // grow spill as needed
3176 final Object[] newSpill = new Object[index + 1];
3177 System.arraycopy(spill, 0, newSpill, 0, spill.length);
3178 spill = newSpill;
3179 }
3181 spill[index] = value;
3182 }
3183 }
3185 // user accessors are either stored in spill array slots
3186 // get the accessor value using slot number. Note that slot is spill array index.
3187 Object getSpill(final int slot) {
3188 final int index = slot;
3189 return (index < 0 || (index >= spill.length)) ? null : spill[index];
3190 }
3192 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
3193 final Class<?> own = ScriptObject.class;
3194 final MethodType mt = MH.type(rtype, types);
3195 try {
3196 return MH.findStatic(MethodHandles.lookup(), own, name, mt);
3197 } catch (final MethodHandleFactory.LookupException e) {
3198 return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
3199 }
3200 }
3202 private static MethodHandle getKnownFunctionPropertyGuard(final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
3203 return MH.insertArguments(KNOWNFUNCPROPGUARD, 1, map, getter, where, func);
3204 }
3206 @SuppressWarnings("unused")
3207 private static boolean knownFunctionPropertyGuard(final Object self, final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
3208 if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) {
3209 try {
3210 return getter.invokeExact(where) == func;
3211 } catch (final RuntimeException | Error e) {
3212 throw e;
3213 } catch (final Throwable t) {
3214 throw new RuntimeException(t);
3215 }
3216 }
3218 return false;
3219 }
3221 /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */
3222 private static int count;
3224 /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created that are scope */
3225 private static int scopeCount;
3227 /**
3228 * Get number of {@code ScriptObject} instances created. If not running in debug
3229 * mode this is always 0
3230 *
3231 * @return number of ScriptObjects created
3232 */
3233 public static int getCount() {
3234 return count;
3235 }
3237 /**
3238 * Get number of scope {@code ScriptObject} instances created. If not running in debug
3239 * mode this is always 0
3240 *
3241 * @return number of scope ScriptObjects created
3242 */
3243 public static int getScopeCount() {
3244 return scopeCount;
3245 }
3247 }