/**
* Copyright (c) 2012-2016 André Bargull
* Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms.
*
* <https://github.com/anba/es6draft>
*/
package com.github.anba.es6draft.runtime.types.builtins;
import static com.github.anba.es6draft.runtime.AbstractOperations.*;
import static com.github.anba.es6draft.runtime.types.Undefined.UNDEFINED;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.github.anba.es6draft.runtime.ExecutionContext;
import com.github.anba.es6draft.runtime.Realm;
import com.github.anba.es6draft.runtime.internal.IndexedMap;
import com.github.anba.es6draft.runtime.internal.ObjectAllocator;
import com.github.anba.es6draft.runtime.internal.PropertyMap;
import com.github.anba.es6draft.runtime.internal.ScriptException;
import com.github.anba.es6draft.runtime.internal.ScriptIterator;
import com.github.anba.es6draft.runtime.objects.simd.SIMDValue;
import com.github.anba.es6draft.runtime.types.Callable;
import com.github.anba.es6draft.runtime.types.Intrinsics;
import com.github.anba.es6draft.runtime.types.Property;
import com.github.anba.es6draft.runtime.types.PropertyDescriptor;
import com.github.anba.es6draft.runtime.types.ScriptObject;
import com.github.anba.es6draft.runtime.types.Symbol;
import com.github.anba.es6draft.runtime.types.Type;
/**
* <h1>9 Ordinary and Exotic Objects Behaviours</h1>
* <ul>
* <li>9.1 Ordinary Object Internal Methods and Internal Slots
* </ul>
*/
public class OrdinaryObject implements ScriptObject {
private static final int STRING_PROPERTIES_DEFAULT_INITIAL_CAPACITY = 16;
private static final int SYMBOL_PROPERTIES_DEFAULT_INITIAL_CAPACITY = 4;
private static final Object[] EMPTY_GETTER_ARGS = new Object[0];
// Maps for String and Symbol valued property keys
private final PropertyMap<String, Property> properties;
private final PropertyMap<Symbol, Property> symbolProperties;
// Map for indexed properties [0, 2^53 - 1]
private final IndexedMap<Property> indexedProperties;
/** [[Realm]] */
@SuppressWarnings("unused")
private final Realm realm;
/** [[Prototype]] */
private ScriptObject prototype = null;
/** [[Extensible]] */
private boolean extensible = true;
/**
* Constructs a new Ordinary Object instance.
*
* @param realm
* the realm object
*/
public OrdinaryObject(Realm realm) {
this.realm = realm;
this.properties = new PropertyMap<>(STRING_PROPERTIES_DEFAULT_INITIAL_CAPACITY);
this.symbolProperties = new PropertyMap<>(SYMBOL_PROPERTIES_DEFAULT_INITIAL_CAPACITY);
this.indexedProperties = new IndexedMap<>();
}
/**
* Constructs a new Ordinary Object instance.
*
* @param realm
* the realm object
* @param prototype
* the prototype object
*/
private OrdinaryObject(Realm realm, ScriptObject prototype) {
this(realm);
this.prototype = prototype;
}
@Override
public String toString() {
return String.format("%s@%x: indexed=%s, strings=%s, symbols=%s, extensible=%b", getClass().getSimpleName(),
System.identityHashCode(this), indexedProperties, properties.keySet(), symbolProperties.keySet(),
extensible);
}
/**
* Returns {@code true} if arguments {@code x} and {@code y} are not the same object reference and:
* <ol>
* <li>{@code x} and {@code y} are both NaN values
* <li>{@code x} and {@code y} are both equal SIMD values
* </ol>
*
* @param x
* the first argument
* @param y
* the second argument
* @return {@code true} if x and y are both NaN values
*/
private static final boolean SameValueNaNorSIMD(Object x, Object y) {
if (x == y) {
return false;
}
if (x instanceof Double && y instanceof Double) {
return Double.isNaN(((Double) x).doubleValue()) && Double.isNaN(((Double) y).doubleValue());
}
if (x instanceof SIMDValue && y instanceof SIMDValue) {
return ((SIMDValue) x).equals(y);
}
return false;
}
/**
* Returns the total number of properties.
*
* @param withSymbols
* {@code true} to include symbol properties
* @return the number of properties
*/
final int countProperties(boolean withSymbols) {
return properties.size() + indexedProperties.size() + (withSymbols ? symbolProperties.size() : 0);
}
/**
* Appends all own string valued properties to the target list.
*
* @param list
* the target list
*/
final void appendProperties(List<? super String> list) {
if (!properties.isEmpty()) {
list.addAll(properties.keySet());
}
}
/**
* Appends all own symbol valued properties to the target list.
*
* @param list
* the target list
*/
final void appendSymbolProperties(List<? super Symbol> list) {
if (!symbolProperties.isEmpty()) {
list.addAll(symbolProperties.keySet());
}
}
/**
* Appends all own integer indexed properties to the target list.
*
* @param list
* the target list
*/
final void appendIndexedProperties(List<? super String> list) {
if (!indexedProperties.isEmpty()) {
list.addAll(indexedProperties.keys());
}
}
final void defineOwnPropertyUnchecked(String propertyKey, Property property) {
// Same as infallibleDefineOwnProperty except extensible check removed.
assert !IndexedMap.isIndex(IndexedMap.toIndex(propertyKey));
assert !properties.containsKey(propertyKey) : "illegal property = " + propertyKey;
properties.put(propertyKey, property);
}
public final void infallibleDefineOwnProperty(String propertyKey, Property property) {
assert extensible : "object not extensible";
assert !IndexedMap.isIndex(IndexedMap.toIndex(propertyKey));
assert !properties.containsKey(propertyKey) : "illegal property = " + propertyKey;
properties.put(propertyKey, property);
}
public final void infallibleDefineOwnProperty(Symbol propertyKey, Property property) {
assert extensible : "object not extensible";
assert !symbolProperties.containsKey(propertyKey) : "illegal property = " + propertyKey;
symbolProperties.put(propertyKey, property);
}
public final Property lookupOwnProperty(String propertyKey) {
assert !IndexedMap.isIndex(IndexedMap.toIndex(propertyKey));
return properties.get(propertyKey);
}
public final Property lookupOwnProperty(Symbol propertyKey) {
return symbolProperties.get(propertyKey);
}
public final void infallibleSetPrototype(ScriptObject prototype) {
this.prototype = prototype;
}
/**
* Returns the indexed properties length.
*
* @return the indexed properties length
*/
long getIndexedLength() {
return indexedProperties.getLength();
}
/**
* Returns the own property value from the given index.
*
* @param propertyKey
* the indexed property key
* @return the property value
*/
Object getIndexed(long propertyKey) {
return indexedProperties.get(propertyKey).getValue();
}
/**
* Set the own property value at the given index to the new value.
*
* @param propertyKey
* the indexed property key
* @param value
* the property value
*/
final void setIndexed(long propertyKey, Object value) {
indexedProperties.put(propertyKey, new Property(value, true, true, true));
}
/**
* Deletes all indexed properties within the range {@code [startIndex, endIndex)} in reverse order, i.e. starting
* from index {@code endIndex - 1}. The range must not be empty.
*
* @param startIndex
* the start index (inclusive)
* @param endIndex
* the end index (exclusive)
* @return {@code -1} if all indexed properties over the requested range have been removed successfully; otherwise
* the index of the first property which is not deletable.
*/
final long deleteRange(long startIndex, long endIndex) {
assert startIndex < endIndex;
IndexedMap<Property> indexed = indexedProperties;
if (indexed.isEmpty()) {
return -1;
}
long lastIndex;
if (indexed.isSparse()) {
lastIndex = deleteRangeSparse(startIndex, endIndex);
} else {
lastIndex = deleteRangeDense(startIndex, endIndex);
}
// Need to call updateLength() manually.
indexed.updateLength();
return lastIndex;
}
private long deleteRangeDense(long startIndex, long endIndex) {
IndexedMap<Property> indexed = indexedProperties;
for (long index = endIndex; startIndex < index;) {
Property prop = indexed.get(--index);
if (prop != null && !prop.isConfigurable()) {
return index;
}
indexed.removeUnchecked(index);
}
return -1;
}
private long deleteRangeSparse(long startIndex, long endIndex) {
Iterator<Map.Entry<Long, Property>> iter = indexedProperties.descendingIterator(startIndex, endIndex);
while (iter.hasNext()) {
Map.Entry<Long, Property> entry = iter.next();
if (!entry.getValue().isConfigurable()) {
return entry.getKey();
}
// Cannot call remove[Unchecked]() directly b/c of ConcurrentModificationException.
iter.remove();
}
return -1;
}
/**
* Returns the list of integer indexed properties.
*
* @return the list of integer indexed properties
*/
public final long[] indices() {
return indexedProperties.indices();
}
/**
* Returns the list of integer indexed properties.
*
* @param from
* from index (inclusive)
* @param to
* to index (exclusive)
* @return the list of integer indexed properties
*/
public final long[] indices(long from, long to) {
return indexedProperties.indices(from, to);
}
/**
* Returns {@code true} if this object has indexed properties.
*
* @return {@code true} if this object has indexed properties
*/
public final boolean hasIndexedProperties() {
return !indexedProperties.isEmpty();
}
/**
* Returns {@code true} if the object has indexed accessors.
*
* @return {@code true} if the object has indexed accessors
*/
public boolean hasIndexedAccessors() {
if (indexedProperties.isEmpty()) {
return false;
}
for (Iterator<Property> iter = indexedProperties.valuesIterator(); iter.hasNext();) {
if (iter.next().isAccessorDescriptor()) {
return true;
}
}
return false;
}
/**
* Returns the object's length.
*
* @return the length or {@code -1} if not available
*/
public long getLength() {
Property length = ordinaryGetOwnProperty("length");
if (length == null || !length.isDataDescriptor() || !Type.isNumber(length.getValue())) {
return -1;
}
return ToLength(Type.numberValue(length.getValue()));
}
/**
* Returns the number of indexed properties.
*
* @return the number of indexed properties
*/
public int getIndexedSize() {
return indexedProperties.size();
}
/**
* Returns {@code true} if the array is dense and has no indexed accessors.
*
* @return {@code true} if the array is dense
*/
public final boolean isDenseArray() {
return isDenseArray(getLength());
}
/**
* Returns {@code true} if the array is dense and has no indexed accessors.
*
* @param length
* the array length
* @return {@code true} if the array is dense
*/
public final boolean isDenseArray(long length) {
assert !hasSpecialIndexedProperties() : "cannot report dense if special indexed present";
IndexedMap<Property> ix = indexedProperties;
return !hasIndexedAccessors() && ix.getLength() == length && !ix.isSparse() && !ix.hasHoles();
}
/**
* Returns {@code true} if this object has "special" indexed properties.
*
* @return {@code true} if this object has special indexed properties
*/
public boolean hasSpecialIndexedProperties() {
return false;
}
/**
* Returns the array's indexed property values. Only applicable for dense arrays.
*
* @return the array's indexed values
*/
public final Object[] toArray() {
return toArray(getLength());
}
/**
* Returns the array's indexed property values. Only applicable for dense arrays.
*
* @param length
* the array length
* @return the array's indexed values
*/
public final Object[] toArray(long length) {
assert isDenseArray(length);
assert 0 <= length && length <= Integer.MAX_VALUE : "length=" + length;
int len = (int) length;
Object[] values = new Object[len];
for (int i = 0; i < len; ++i) {
values[i] = getIndexed(i);
}
return values;
}
/**
* [[Prototype]]
*
* @return the prototype object
*/
public final ScriptObject getPrototype() {
return prototype;
}
/**
* [[Prototype]]
*
* @param prototype
* the new prototype object
*/
protected final void setPrototype(ScriptObject prototype) {
this.prototype = prototype;
}
/**
* [[Extensible]]
*
* @return {@code true} if this object is extensible
*/
protected final boolean isExtensible() {
return extensible;
}
/**
* [[Extensible]]
*
* @param extensible
* the new extensible mode
*/
protected final void setExtensible(boolean extensible) {
assert this.extensible || !extensible;
this.extensible = extensible;
}
/**
* [[HasOwnProperty]] (P)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @return {@code true} if an own property was found
*/
protected boolean hasOwnProperty(ExecutionContext cx, long propertyKey) {
return ordinaryHasOwnProperty(propertyKey);
}
/**
* [[HasOwnProperty]] (P)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @return {@code true} if an own property was found
*/
protected boolean hasOwnProperty(ExecutionContext cx, String propertyKey) {
return ordinaryHasOwnProperty(propertyKey);
}
/**
* [[HasOwnProperty]] (P)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @return {@code true} if an own property was found
*/
protected boolean hasOwnProperty(ExecutionContext cx, Symbol propertyKey) {
return ordinaryHasOwnProperty(propertyKey);
}
/**
* OrdinaryHasOwnProperty (P) (not in spec)
*
* @param propertyKey
* the property key
* @return {@code true} if an own property was found
*/
protected final boolean ordinaryHasOwnProperty(long propertyKey) {
// optimized: HasOwnProperty(cx, this, propertyKey)
return indexedProperties.containsKey(propertyKey);
}
/**
* OrdinaryHasOwnProperty (P) (not in spec)
*
* @param propertyKey
* the property key
* @return {@code true} if an own property was found
*/
protected final boolean ordinaryHasOwnProperty(String propertyKey) {
// optimized: HasOwnProperty(cx, this, propertyKey)
return properties.containsKey(propertyKey);
}
/**
* OrdinaryHasOwnProperty (P) (not in spec)
*
* @param propertyKey
* the property key
* @return {@code true} if an own property was found
*/
protected final boolean ordinaryHasOwnProperty(Symbol propertyKey) {
// optimized: HasOwnProperty(cx, this, propertyKey)
return symbolProperties.containsKey(propertyKey);
}
/** 9.1.1 [[GetPrototypeOf]] ( ) */
@Override
public ScriptObject getPrototypeOf(ExecutionContext cx) {
/* step 1 */
return prototype;
}
/** 9.1.2 [[SetPrototypeOf]] (V) */
@Override
public boolean setPrototypeOf(ExecutionContext cx, ScriptObject prototype) {
/* steps 1-3 */
boolean extensible = this.extensible;
ScriptObject current = this.prototype;
/* step 4 */
if (prototype == current) { // SameValue(prototype, current)
return true;
}
/* step 5 */
if (!extensible) {
return false;
}
/* steps 6-8 */
for (ScriptObject p = prototype; p != null;) {
if (p == this) { // SameValue(p, O)
return false;
}
if (!(p instanceof OrdinaryObject)) {
break;
}
p = ((OrdinaryObject) p).prototype;
}
/* step 9 */
this.prototype = prototype;
/* step 10 */
return true;
}
/** 9.1.3 [[IsExtensible]] ( ) */
@Override
public boolean isExtensible(ExecutionContext cx) {
/* step 1 */
return extensible;
}
/** 9.1.4 [[PreventExtensions]] ( ) */
@Override
public boolean preventExtensions(ExecutionContext cx) {
/* step 1 */
this.extensible = false;
/* step 2 */
return true;
}
/** 9.1.5 [[GetOwnProperty]] (P) */
@Override
public final Property getOwnProperty(ExecutionContext cx, long propertyKey) {
if (IndexedMap.isIndex(propertyKey)) {
return getProperty(cx, propertyKey);
}
return getProperty(cx, ToString(propertyKey));
}
/** 9.1.5 [[GetOwnProperty]] (P) */
@Override
public final Property getOwnProperty(ExecutionContext cx, String propertyKey) {
long index = IndexedMap.toIndex(propertyKey);
if (IndexedMap.isIndex(index)) {
return getProperty(cx, index);
}
return getProperty(cx, propertyKey);
}
/** 9.1.5 [[GetOwnProperty]] (P) */
@Override
public final Property getOwnProperty(ExecutionContext cx, Symbol propertyKey) {
/* step 1 */
return getProperty(cx, propertyKey);
}
/**
* 9.1.5 [[GetOwnProperty]] (P)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @return the property or {@code null} if none found
*/
protected Property getProperty(ExecutionContext cx, long propertyKey) {
return ordinaryGetOwnProperty(propertyKey);
}
/**
* 9.1.5 [[GetOwnProperty]] (P)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @return the property or {@code null} if none found
*/
protected Property getProperty(ExecutionContext cx, String propertyKey) {
return ordinaryGetOwnProperty(propertyKey);
}
/**
* 9.1.5 [[GetOwnProperty]] (P)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @return the property or {@code null} if none found
*/
protected Property getProperty(ExecutionContext cx, Symbol propertyKey) {
return ordinaryGetOwnProperty(propertyKey);
}
/**
* 9.1.5.1 OrdinaryGetOwnProperty (O, P)
*
* @param propertyKey
* the property key
* @return the property record or {@code null} if none found
*/
protected final Property ordinaryGetOwnProperty(long propertyKey) {
/* steps 1-9 (altered: returns live view) */
return indexedProperties.get(propertyKey);
}
/**
* 9.1.5.1 OrdinaryGetOwnProperty (O, P)
*
* @param propertyKey
* the property key
* @return the property record or {@code null} if none found
*/
protected final Property ordinaryGetOwnProperty(String propertyKey) {
/* steps 1-9 (altered: returns live view) */
return properties.get(propertyKey);
}
/**
* 9.1.5.1 OrdinaryGetOwnProperty (O, P)
*
* @param propertyKey
* the property key
* @return the property record or {@code null} if none found
*/
protected final Property ordinaryGetOwnProperty(Symbol propertyKey) {
/* steps 1-9 (altered: returns live view) */
return symbolProperties.get(propertyKey);
}
/** 9.1.6 [[DefineOwnProperty]] (P, Desc) */
@Override
public final boolean defineOwnProperty(ExecutionContext cx, long propertyKey, PropertyDescriptor desc) {
if (IndexedMap.isIndex(propertyKey)) {
return defineProperty(cx, propertyKey, desc);
}
return defineProperty(cx, ToString(propertyKey), desc);
}
/** 9.1.6 [[DefineOwnProperty]] (P, Desc) */
@Override
public final boolean defineOwnProperty(ExecutionContext cx, String propertyKey, PropertyDescriptor desc) {
long index = IndexedMap.toIndex(propertyKey);
if (IndexedMap.isIndex(index)) {
return defineProperty(cx, index, desc);
}
return defineProperty(cx, propertyKey, desc);
}
/** 9.1.6 [[DefineOwnProperty]] (P, Desc) */
@Override
public final boolean defineOwnProperty(ExecutionContext cx, Symbol propertyKey, PropertyDescriptor desc) {
return defineProperty(cx, propertyKey, desc);
}
/**
* 9.1.6 [[DefineOwnProperty]] (P, Desc)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @param desc
* the property descriptor
* @return {@code true} if the property was successfully defined
*/
protected boolean defineProperty(ExecutionContext cx, long propertyKey, PropertyDescriptor desc) {
return ordinaryDefineOwnProperty(cx, propertyKey, desc);
}
/**
* 9.1.6 [[DefineOwnProperty]] (P, Desc)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @param desc
* the property descriptor
* @return {@code true} if the property was successfully defined
*/
protected boolean defineProperty(ExecutionContext cx, String propertyKey, PropertyDescriptor desc) {
return ordinaryDefineOwnProperty(cx, propertyKey, desc);
}
/**
* 9.1.6 [[DefineOwnProperty]] (P, Desc)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @param desc
* the property descriptor
* @return {@code true} if the property was successfully defined
*/
protected boolean defineProperty(ExecutionContext cx, Symbol propertyKey, PropertyDescriptor desc) {
return ordinaryDefineOwnProperty(cx, propertyKey, desc);
}
/**
* 9.1.6.1 OrdinaryDefineOwnProperty (O, P, Desc)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @param desc
* the property descriptor
* @return {@code true} on success
*/
protected final boolean ordinaryDefineOwnProperty(ExecutionContext cx, long propertyKey, PropertyDescriptor desc) {
/* steps 1-2 */
Property current = getProperty(cx, propertyKey);
/* step 3 */
boolean extensible = isExtensible();
/* step 4 */
return validateAndApplyPropertyDescriptor(indexedProperties, propertyKey, extensible, desc, current);
}
/**
* 9.1.6.1 OrdinaryDefineOwnProperty (O, P, Desc)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @param desc
* the property descriptor
* @return {@code true} on success
*/
protected final boolean ordinaryDefineOwnProperty(ExecutionContext cx, String propertyKey,
PropertyDescriptor desc) {
/* steps 1-2 */
Property current = getProperty(cx, propertyKey);
/* step 3 */
boolean extensible = isExtensible();
/* step 4 */
return validateAndApplyPropertyDescriptor(properties, propertyKey, extensible, desc, current);
}
/**
* 9.1.6.1 OrdinaryDefineOwnProperty (O, P, Desc)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @param desc
* the property descriptor
* @return {@code true} on success
*/
protected final boolean ordinaryDefineOwnProperty(ExecutionContext cx, Symbol propertyKey,
PropertyDescriptor desc) {
/* steps 1-2 */
Property current = getProperty(cx, propertyKey);
/* step 3 */
boolean extensible = isExtensible();
/* step 4 */
return validateAndApplyPropertyDescriptor(symbolProperties, propertyKey, extensible, desc, current);
}
/**
* 9.1.6.2 IsCompatiblePropertyDescriptor (Extensible, Desc, Current)
*
* @param extensible
* the extensible mode
* @param desc
* the property descriptor
* @param current
* the current property
* @return {@code true} if <var>desc</var> is compatible
*/
protected static final boolean IsCompatiblePropertyDescriptor(boolean extensible, PropertyDescriptor desc,
Property current) {
/* step 1 */
return validateAndApplyPropertyDescriptor(null, null, extensible, desc, current);
}
/**
* 9.1.6.3 ValidateAndApplyPropertyDescriptor (O, P, extensible, Desc, current)
*
* @param object
* the script object
* @param propertyKey
* the property key
* @param extensible
* the extensible mode
* @param desc
* the property descriptor
* @param current
* the current property
* @return {@code true} on success
*/
protected static final boolean ValidateAndApplyPropertyDescriptor(OrdinaryObject object, long propertyKey,
boolean extensible, PropertyDescriptor desc, Property current) {
return validateAndApplyPropertyDescriptor(object.indexedProperties, propertyKey, extensible, desc, current);
}
/**
* 9.1.6.3 ValidateAndApplyPropertyDescriptor (O, P, extensible, Desc, current)
*
* @param object
* the script object
* @param propertyKey
* the property key
* @param extensible
* the extensible mode
* @param desc
* the property descriptor
* @param current
* the current property
* @return {@code true} on success
*/
protected static final boolean ValidateAndApplyPropertyDescriptor(OrdinaryObject object, String propertyKey,
boolean extensible, PropertyDescriptor desc, Property current) {
return validateAndApplyPropertyDescriptor(object.properties, propertyKey, extensible, desc, current);
}
/**
* 9.1.6.3 ValidateAndApplyPropertyDescriptor (O, P, extensible, Desc, current)
*
* @param object
* the script object
* @param propertyKey
* the property key
* @param extensible
* the extensible mode
* @param desc
* the property descriptor
* @param current
* the current property
* @return {@code true} on success
*/
protected static final boolean ValidateAndApplyPropertyDescriptor(OrdinaryObject object, Symbol propertyKey,
boolean extensible, PropertyDescriptor desc, Property current) {
return validateAndApplyPropertyDescriptor(object.symbolProperties, propertyKey, extensible, desc, current);
}
/**
* 9.1.6.3 ValidateAndApplyPropertyDescriptor (O, P, extensible, Desc, current)
*
* @param object
* the script object
* @param propertyKey
* the property key
* @param extensible
* the extensible mode
* @param desc
* the property descriptor
* @param current
* the current property
* @return {@code true} on success
*/
private static final <KEY> boolean validateAndApplyPropertyDescriptor(PropertyMap<KEY, Property> object,
KEY propertyKey, boolean extensible, PropertyDescriptor desc, Property current) {
/* step 1 */
assert (object == null || propertyKey != null);
/* step 2 */
if (current == null) {
if (!extensible) {
return false;
}
if (object != null) {
object.put(propertyKey, desc.toProperty());
}
return true;
}
/* step 3 */
if (desc.isEmpty()) {
return true;
}
/* step 4 */
if (current.isSubset(desc)) {
return true;
}
/* step 5 */
if (!current.isConfigurable()) {
if (desc.isConfigurable()) {
return false;
}
if (desc.hasEnumerable() && desc.isEnumerable() != current.isEnumerable()) {
return false;
}
}
if (desc.isGenericDescriptor()) {
/* step 6 */
// no further validation required, proceed below...
} else if (desc.isDataDescriptor() != current.isDataDescriptor()) {
/* step 7 */
if (!current.isConfigurable()) {
return false;
}
if (current.isDataDescriptor()) {
if (object != null) {
object.get(propertyKey).toAccessorProperty();
}
} else {
if (object != null) {
object.get(propertyKey).toDataProperty();
}
}
} else if (desc.isDataDescriptor()) {
/* step 8 */
assert current.isDataDescriptor();
if (!current.isConfigurable() && !current.isWritable()) {
if (desc.isWritable()) {
return false;
}
if (desc.hasValue() && !SameValue(desc.getValue(), current.getValue())) {
return false;
}
}
} else {
/* step 9 */
assert desc.isAccessorDescriptor() && current.isAccessorDescriptor();
if (!current.isConfigurable()) {
if (desc.hasSetter() && desc.getSetter() != current.getSetter()) {
return false;
}
if (desc.hasGetter() && desc.getGetter() != current.getGetter()) {
return false;
}
}
}
/* step 10 */
if (object != null) {
object.get(propertyKey).apply(desc);
}
/* step 11 */
return true;
}
/**
* 9.1.6.3 ValidateAndApplyPropertyDescriptor (O, P, extensible, Desc, current)
*
* @param object
* the script object
* @param propertyKey
* the property key
* @param extensible
* the extensible mode
* @param desc
* the property descriptor
* @param current
* the current property
* @return {@code true} on success
*/
private static final boolean validateAndApplyPropertyDescriptor(IndexedMap<Property> object, long propertyKey,
boolean extensible, PropertyDescriptor desc, Property current) {
/* step 1 */
assert (object == null || IndexedMap.isIndex(propertyKey));
/* step 2 */
if (current == null) {
if (!extensible) {
return false;
}
if (object != null) {
object.put(propertyKey, desc.toProperty());
}
return true;
}
/* step 3 */
if (desc.isEmpty()) {
return true;
}
/* step 4 */
if (current.isSubset(desc)) {
return true;
}
/* step 5 */
if (!current.isConfigurable()) {
if (desc.isConfigurable()) {
return false;
}
if (desc.hasEnumerable() && desc.isEnumerable() != current.isEnumerable()) {
return false;
}
}
if (desc.isGenericDescriptor()) {
/* step 6 */
// no further validation required, proceed below...
} else if (desc.isDataDescriptor() != current.isDataDescriptor()) {
/* step 7 */
if (!current.isConfigurable()) {
return false;
}
if (current.isDataDescriptor()) {
if (object != null) {
object.get(propertyKey).toAccessorProperty();
}
} else {
if (object != null) {
object.get(propertyKey).toDataProperty();
}
}
} else if (desc.isDataDescriptor()) {
/* step 8 */
assert current.isDataDescriptor();
if (!current.isConfigurable() && !current.isWritable()) {
if (desc.isWritable()) {
return false;
}
if (desc.hasValue() && !SameValue(desc.getValue(), current.getValue())) {
return false;
}
}
} else {
/* step 9 */
assert desc.isAccessorDescriptor() && current.isAccessorDescriptor();
if (!current.isConfigurable()) {
if (desc.hasSetter() && desc.getSetter() != current.getSetter()) {
return false;
}
if (desc.hasGetter() && desc.getGetter() != current.getGetter()) {
return false;
}
}
}
/* step 10 */
if (object != null) {
object.get(propertyKey).apply(desc);
}
/* step 11 */
return true;
}
/**
* 9.1.7 [[HasProperty]](P)
*/
@Override
public final boolean hasProperty(ExecutionContext cx, long propertyKey) {
if (IndexedMap.isIndex(propertyKey)) {
return has(cx, propertyKey);
}
return has(cx, ToString(propertyKey));
}
/**
* 9.1.7 [[HasProperty]](P)
*/
@Override
public final boolean hasProperty(ExecutionContext cx, String propertyKey) {
long index = IndexedMap.toIndex(propertyKey);
if (IndexedMap.isIndex(index)) {
return has(cx, index);
}
return has(cx, propertyKey);
}
/**
* 9.1.7 [[HasProperty]](P)
*/
@Override
public final boolean hasProperty(ExecutionContext cx, Symbol propertyKey) {
return has(cx, propertyKey);
}
/**
* 9.1.7 [[HasProperty]](P)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @return {@code true} if the property was found
*/
protected boolean has(ExecutionContext cx, long propertyKey) {
return ordinaryHasProperty(cx, propertyKey);
}
/**
* 9.1.7 [[HasProperty]](P)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @return {@code true} if the property was found
*/
protected boolean has(ExecutionContext cx, String propertyKey) {
return ordinaryHasProperty(cx, propertyKey);
}
/**
* 9.1.7 [[HasProperty]](P)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @return {@code true} if the property was found
*/
protected boolean has(ExecutionContext cx, Symbol propertyKey) {
return ordinaryHasProperty(cx, propertyKey);
}
/**
* 9.1.7.1 OrdinaryHasProperty (O, P)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @return {@code true} if the property was found
*/
protected final boolean ordinaryHasProperty(ExecutionContext cx, long propertyKey) {
/* step 1 (implicit) */
/* step 2 */
boolean hasOwn = ordinaryHasOwnProperty(propertyKey);
/* step 3 */
if (hasOwn) {
return true;
}
/* steps 4-5 */
ScriptObject parent = getPrototypeOf(cx);
/* step 6 */
if (parent != null) {
return parent.hasProperty(cx, propertyKey);
}
/* step 7 */
return false;
}
/**
* 9.1.7.1 OrdinaryHasProperty (O, P)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @return {@code true} if the property was found
*/
protected final boolean ordinaryHasProperty(ExecutionContext cx, String propertyKey) {
/* step 1 (implicit) */
/* step 2 */
boolean hasOwn = ordinaryHasOwnProperty(propertyKey);
/* step 3 */
if (hasOwn) {
return true;
}
/* steps 4-5 */
ScriptObject parent = getPrototypeOf(cx);
/* step 6 */
if (parent != null) {
return parent.hasProperty(cx, propertyKey);
}
/* step 7 */
return false;
}
/**
* 9.1.7.1 OrdinaryHasProperty (O, P)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @return {@code true} if the property was found
*/
protected final boolean ordinaryHasProperty(ExecutionContext cx, Symbol propertyKey) {
/* step 1 (implicit) */
/* step 2 */
boolean hasOwn = ordinaryHasOwnProperty(propertyKey);
/* step 3 */
if (hasOwn) {
return true;
}
/* steps 4-5 */
ScriptObject parent = getPrototypeOf(cx);
/* step 6 */
if (parent != null) {
return parent.hasProperty(cx, propertyKey);
}
/* step 7 */
return false;
}
/** 9.1.8 [[Get]] (P, Receiver) */
@Override
public final Object get(ExecutionContext cx, long propertyKey, Object receiver) {
if (IndexedMap.isIndex(propertyKey)) {
return getValue(cx, propertyKey, receiver);
}
return getValue(cx, ToString(propertyKey), receiver);
}
/** 9.1.8 [[Get]] (P, Receiver) */
@Override
public final Object get(ExecutionContext cx, String propertyKey, Object receiver) {
long index = IndexedMap.toIndex(propertyKey);
if (IndexedMap.isIndex(index)) {
return getValue(cx, index, receiver);
}
return getValue(cx, propertyKey, receiver);
}
/** 9.1.8 [[Get]] (P, Receiver) */
@Override
public final Object get(ExecutionContext cx, Symbol propertyKey, Object receiver) {
return getValue(cx, propertyKey, receiver);
}
/**
* 9.1.8 [[Get]] (P, Receiver)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @param receiver
* the receiver object
* @return the property value
*/
protected Object getValue(ExecutionContext cx, long propertyKey, Object receiver) {
/* step 1 (implicit) */
/* steps 2-3 */
Property desc = getProperty(cx, propertyKey);
/* step 4 */
if (desc == null) {
ScriptObject parent = getPrototypeOf(cx);
if (parent == null) {
return UNDEFINED;
}
return parent.get(cx, propertyKey, receiver);
}
/* step 5 */
if (desc.isDataDescriptor()) {
return desc.getValue();
}
assert desc.isAccessorDescriptor();
/* step 6 */
Callable getter = desc.getGetter();
/* step 7 */
if (getter == null) {
return UNDEFINED;
}
/* step 8 */
return getter.call(cx, receiver, EMPTY_GETTER_ARGS);
}
/**
* 9.1.8 [[Get]] (P, Receiver)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @param receiver
* the receiver object
* @return the property value
*/
protected Object getValue(ExecutionContext cx, String propertyKey, Object receiver) {
/* step 1 (implicit) */
/* steps 2-3 */
Property desc = getProperty(cx, propertyKey);
/* step 4 */
if (desc == null) {
ScriptObject parent = getPrototypeOf(cx);
if (parent == null) {
return UNDEFINED;
}
return parent.get(cx, propertyKey, receiver);
}
/* step 5 */
if (desc.isDataDescriptor()) {
return desc.getValue();
}
assert desc.isAccessorDescriptor();
/* step 6 */
Callable getter = desc.getGetter();
/* step 7 */
if (getter == null) {
return UNDEFINED;
}
/* step 8 */
return getter.call(cx, receiver, EMPTY_GETTER_ARGS);
}
/**
* 9.1.8 [[Get]] (P, Receiver)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @param receiver
* the receiver object
* @return the property value
*/
protected Object getValue(ExecutionContext cx, Symbol propertyKey, Object receiver) {
/* step 1 (implicit) */
/* steps 2-3 */
Property desc = getProperty(cx, propertyKey);
/* step 4 */
if (desc == null) {
ScriptObject parent = getPrototypeOf(cx);
if (parent == null) {
return UNDEFINED;
}
return parent.get(cx, propertyKey, receiver);
}
/* step 5 */
if (desc.isDataDescriptor()) {
return desc.getValue();
}
assert desc.isAccessorDescriptor();
/* step 6 */
Callable getter = desc.getGetter();
/* step 7 */
if (getter == null) {
return UNDEFINED;
}
/* step 8 */
return getter.call(cx, receiver, EMPTY_GETTER_ARGS);
}
/** 9.1.9 [[Set] (P, V, Receiver) */
@Override
public final boolean set(ExecutionContext cx, long propertyKey, Object value, Object receiver) {
if (IndexedMap.isIndex(propertyKey)) {
return setValue(cx, propertyKey, value, receiver);
}
return setValue(cx, ToString(propertyKey), value, receiver);
}
/** 9.1.9 [[Set] (P, V, Receiver) */
@Override
public final boolean set(ExecutionContext cx, String propertyKey, Object value, Object receiver) {
long index = IndexedMap.toIndex(propertyKey);
if (IndexedMap.isIndex(index)) {
return setValue(cx, index, value, receiver);
}
return setValue(cx, propertyKey, value, receiver);
}
/** 9.1.9 [[Set] (P, V, Receiver) */
@Override
public final boolean set(ExecutionContext cx, Symbol propertyKey, Object value, Object receiver) {
return setValue(cx, propertyKey, value, receiver);
}
/**
* 9.1.9 [[Set] (P, V, Receiver)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @param value
* the new property value
* @param receiver
* the receiver object
* @return {@code true} on success
*/
protected boolean setValue(ExecutionContext cx, long propertyKey, Object value, Object receiver) {
/* step 1 (implicit) */
/* steps 2-3 */
Property ownDesc = getProperty(cx, propertyKey);
/* step 4 */
if (ownDesc == null) {
ScriptObject parent = getPrototypeOf(cx);
if (parent != null) {
return parent.set(cx, propertyKey, value, receiver);
} else {
ownDesc = new Property(UNDEFINED, true, true, true);
}
} else if (receiver == this && ownDesc.isWritable()) {
// Optimize the common case for own, writable properties
return setPropertyValue(cx, propertyKey, value, ownDesc);
}
/* step 5 */
if (ownDesc.isDataDescriptor()) {
if (!ownDesc.isWritable()) {
return false;
}
if (!Type.isObject(receiver)) {
return false;
}
ScriptObject _receiver = Type.objectValue(receiver);
Property existingDescriptor = _receiver.getOwnProperty(cx, propertyKey);
if (existingDescriptor != null) {
if (existingDescriptor.isAccessorDescriptor() || !existingDescriptor.isWritable()) {
return false;
}
PropertyDescriptor valueDesc = new PropertyDescriptor(value);
return _receiver.defineOwnProperty(cx, propertyKey, valueDesc);
} else {
return CreateDataProperty(cx, _receiver, propertyKey, value);
}
}
/* step 6 */
assert ownDesc.isAccessorDescriptor();
Callable setter = ownDesc.getSetter();
if (setter == null) {
return false;
}
setter.call(cx, receiver, value);
return true;
}
protected boolean setPropertyValue(ExecutionContext cx, long propertyKey, Object value, Property current) {
assert current.isDataDescriptor() && current.isWritable();
if (!SameValueNaNorSIMD(current.getValue(), value)) {
current.setValue(value);
}
return true;
}
/**
* 9.1.9 [[Set] (P, V, Receiver)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @param value
* the new property value
* @param receiver
* the receiver object
* @return {@code true} on success
*/
protected boolean setValue(ExecutionContext cx, String propertyKey, Object value, Object receiver) {
/* step 1 (implicit) */
/* steps 2-3 */
Property ownDesc = getProperty(cx, propertyKey);
/* step 4 */
if (ownDesc == null) {
ScriptObject parent = getPrototypeOf(cx);
if (parent != null) {
return parent.set(cx, propertyKey, value, receiver);
} else {
ownDesc = new Property(UNDEFINED, true, true, true);
}
} else if (receiver == this && ownDesc.isWritable()) {
// Optimize the common case for own, writable properties
return setPropertyValue(cx, propertyKey, value, ownDesc);
}
/* step 5 */
if (ownDesc.isDataDescriptor()) {
if (!ownDesc.isWritable()) {
return false;
}
if (!Type.isObject(receiver)) {
return false;
}
ScriptObject _receiver = Type.objectValue(receiver);
Property existingDescriptor = _receiver.getOwnProperty(cx, propertyKey);
if (existingDescriptor != null) {
if (existingDescriptor.isAccessorDescriptor() || !existingDescriptor.isWritable()) {
return false;
}
PropertyDescriptor valueDesc = new PropertyDescriptor(value);
return _receiver.defineOwnProperty(cx, propertyKey, valueDesc);
} else {
return CreateDataProperty(cx, _receiver, propertyKey, value);
}
}
/* step 6 */
assert ownDesc.isAccessorDescriptor();
Callable setter = ownDesc.getSetter();
if (setter == null) {
return false;
}
setter.call(cx, receiver, value);
return true;
}
protected boolean setPropertyValue(ExecutionContext cx, String propertyKey, Object value, Property current) {
assert current.isDataDescriptor() && current.isWritable();
if (!SameValueNaNorSIMD(current.getValue(), value)) {
current.setValue(value);
}
return true;
}
/**
* 9.1.9 [[Set] (P, V, Receiver)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @param value
* the new property value
* @param receiver
* the receiver object
* @return {@code true} on success
*/
protected boolean setValue(ExecutionContext cx, Symbol propertyKey, Object value, Object receiver) {
/* step 1 (implicit) */
/* steps 2-3 */
Property ownDesc = getProperty(cx, propertyKey);
/* step 4 */
if (ownDesc == null) {
ScriptObject parent = getPrototypeOf(cx);
if (parent != null) {
return parent.set(cx, propertyKey, value, receiver);
} else {
ownDesc = new Property(UNDEFINED, true, true, true);
}
} else if (receiver == this && ownDesc.isWritable()) {
// Optimize the common case for own, writable properties
return setPropertyValue(cx, propertyKey, value, ownDesc);
}
/* step 5 */
if (ownDesc.isDataDescriptor()) {
if (!ownDesc.isWritable()) {
return false;
}
if (!Type.isObject(receiver)) {
return false;
}
ScriptObject _receiver = Type.objectValue(receiver);
Property existingDescriptor = _receiver.getOwnProperty(cx, propertyKey);
if (existingDescriptor != null) {
if (existingDescriptor.isAccessorDescriptor() || !existingDescriptor.isWritable()) {
return false;
}
PropertyDescriptor valueDesc = new PropertyDescriptor(value);
return _receiver.defineOwnProperty(cx, propertyKey, valueDesc);
} else {
return CreateDataProperty(cx, _receiver, propertyKey, value);
}
}
/* step 6 */
assert ownDesc.isAccessorDescriptor();
Callable setter = ownDesc.getSetter();
if (setter == null) {
return false;
}
setter.call(cx, receiver, value);
return true;
}
protected boolean setPropertyValue(ExecutionContext cx, Symbol propertyKey, Object value, Property current) {
assert current.isDataDescriptor() && current.isWritable();
if (!SameValueNaNorSIMD(current.getValue(), value)) {
current.setValue(value);
}
return true;
}
/** 9.1.10 [[Delete]] (P) */
@Override
public final boolean delete(ExecutionContext cx, long propertyKey) {
if (IndexedMap.isIndex(propertyKey)) {
return deleteProperty(cx, propertyKey);
}
return deleteProperty(cx, ToString(propertyKey));
}
/** 9.1.10 [[Delete]] (P) */
@Override
public final boolean delete(ExecutionContext cx, String propertyKey) {
long index = IndexedMap.toIndex(propertyKey);
if (IndexedMap.isIndex(index)) {
return deleteProperty(cx, index);
}
return deleteProperty(cx, propertyKey);
}
/** 9.1.10 [[Delete]] (P) */
@Override
public final boolean delete(ExecutionContext cx, Symbol propertyKey) {
return deleteProperty(cx, propertyKey);
}
/**
* 9.1.10 [[Delete]] (P)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @return {@code true} if the property was successfully deleted
*/
protected boolean deleteProperty(ExecutionContext cx, long propertyKey) {
/* step 1 (not applicable) */
/* steps 2-3 */
Property desc = getProperty(cx, propertyKey);
/* step 4 */
if (desc == null) {
return true;
}
/* step 5 */
if (desc.isConfigurable()) {
indexedProperties.remove(propertyKey);
return true;
}
/* step 6 */
return false;
}
/**
* 9.1.10 [[Delete]] (P)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @return {@code true} if the property was successfully deleted
*/
protected boolean deleteProperty(ExecutionContext cx, String propertyKey) {
/* step 1 (not applicable) */
/* steps 2-3 */
Property desc = getProperty(cx, propertyKey);
/* step 4 */
if (desc == null) {
return true;
}
/* step 5 */
if (desc.isConfigurable()) {
properties.remove(propertyKey);
return true;
}
/* step 6 */
return false;
}
/**
* 9.1.10 [[Delete]] (P)
*
* @param cx
* the execution context
* @param propertyKey
* the property key
* @return {@code true} if the property was successfully deleted
*/
protected boolean deleteProperty(ExecutionContext cx, Symbol propertyKey) {
/* step 1 (not applicable) */
/* steps 2-3 */
Property desc = getProperty(cx, propertyKey);
/* step 4 */
if (desc == null) {
return true;
}
/* step 5 */
if (desc.isConfigurable()) {
symbolProperties.remove(propertyKey);
return true;
}
/* step 6 */
return false;
}
/** 9.1.11 [[Enumerate]] () */
@Override
public final ScriptObject enumerate(ExecutionContext cx) {
return CreateListIterator(cx, new EnumKeysIterator(cx, this));
}
/** 9.1.11 [[Enumerate]] () */
@Override
public final ScriptIterator<?> enumerateKeys(ExecutionContext cx) {
return new EnumKeysIterator(cx, this);
}
/**
* 9.1.11 [[Enumerate]] ()
*
* @param cx
* the execution context
* @return the list of enumerable string valued property keys
*/
protected List<String> getEnumerableKeys(ExecutionContext cx) {
int totalSize = countProperties(false);
if (totalSize == 0) {
return Collections.emptyList();
}
ArrayList<String> keys = new ArrayList<>(totalSize);
appendIndexedProperties(keys);
appendProperties(keys);
return keys;
}
/**
* Subclasses need to override this method if they have virtual, configurable properties.
*
* @param propertyKey
* the property key
* @return the property enumerable status
*/
protected Enumerability isEnumerableOwnProperty(String propertyKey) {
Property prop;
long index = IndexedMap.toIndex(propertyKey);
if (IndexedMap.isIndex(index)) {
prop = ordinaryGetOwnProperty(index);
} else {
prop = ordinaryGetOwnProperty(propertyKey);
}
if (prop == null) {
return Enumerability.Deleted;
}
return Enumerability.isEnumerable(prop.isEnumerable());
}
private static final class EnumKeysIterator extends com.github.anba.es6draft.runtime.internal.SimpleIterator<Object>
implements com.github.anba.es6draft.runtime.internal.ScriptIterator<Object> {
private final HashSet<Object> visitedKeys = new HashSet<>();
private final ExecutionContext cx;
private OrdinaryObject obj;
private Iterator<String> keys;
private ScriptIterator<?> protoKeys;
private ScriptObject scriptIter;
EnumKeysIterator(ExecutionContext cx, OrdinaryObject obj) {
this.cx = cx;
this.obj = obj;
this.keys = obj.getEnumerableKeys(cx).iterator();
}
@Override
protected Object findNext() {
HashSet<Object> visitedKeys = this.visitedKeys;
for (Iterator<String> keys; (keys = this.keys) != null;) {
assert protoKeys == null;
OrdinaryObject obj = this.obj;
while (keys.hasNext()) {
String key = keys.next();
Enumerability e = obj.isEnumerableOwnProperty(key);
if (e != Enumerability.Deleted) {
if (visitedKeys.add(key) && e == Enumerability.Enumerable) {
return key;
}
}
}
nextObject();
}
if (this.protoKeys != null) {
return findNextFromProtoKeys();
}
return null;
}
private Object findNextFromProtoKeys() {
HashSet<Object> visitedKeys = this.visitedKeys;
ScriptIterator<?> protoKeys = this.protoKeys;
try {
while (protoKeys.hasNext()) {
Object key = protoKeys.next();
if (visitedKeys.add(key)) {
return key;
}
}
// visited all inherited keys
this.protoKeys = null;
return null;
} catch (ScriptException e) {
this.protoKeys = null;
throw e;
}
}
private void nextObject() {
// switch to prototype enumerate
try {
ScriptObject proto = obj.getPrototypeOf(cx);
if (proto != null) {
if (proto instanceof OrdinaryObject) {
this.obj = (OrdinaryObject) proto;
this.keys = ((OrdinaryObject) proto).getEnumerableKeys(cx).iterator();
} else {
ScriptIterator<?> protoKeys = proto.enumerateKeys(cx);
if (protoKeys instanceof EnumKeysIterator) {
EnumKeysIterator keysIterator = (EnumKeysIterator) protoKeys;
assert keysIterator.visitedKeys.isEmpty();
assert keysIterator.keys != null && keysIterator.protoKeys == null;
this.obj = keysIterator.obj;
this.keys = keysIterator.keys;
} else {
this.obj = null;
this.keys = null;
this.protoKeys = protoKeys;
}
}
} else {
this.obj = null;
this.keys = null;
this.protoKeys = null;
}
} catch (ScriptException e) {
this.obj = null;
this.keys = null;
this.protoKeys = null;
throw e;
}
}
@Override
public void close() throws ScriptException {
if (!isDone() && hasReturn()) {
IteratorClose(cx, getScriptObject());
}
}
@Override
public void close(Throwable cause) throws ScriptException {
if (!isDone() && hasReturn()) {
IteratorClose(cx, getScriptObject(), cause);
}
}
private ScriptObject getScriptObject() {
if (scriptIter == null) {
scriptIter = CreateListIterator(cx, this);
}
return scriptIter;
}
private boolean isDone() {
return obj == null && keys == null && protoKeys == null;
}
private boolean hasReturn() {
if (scriptIter != null) {
return true;
}
OrdinaryObject iterProto = cx.getIntrinsic(Intrinsics.IteratorPrototype);
for (;;) {
if (iterProto.ordinaryHasOwnProperty("return")) {
return true;
}
ScriptObject proto = iterProto.getPrototype();
if (proto == null) {
return false;
}
if (!(proto instanceof OrdinaryObject)) {
return true;
}
iterProto = (OrdinaryObject) proto;
}
}
}
@Override
public final Iterator<String> ownEnumerablePropertyKeys(ExecutionContext cx) {
return getEnumerableKeys(cx).iterator();
}
@Override
public final Enumerability isEnumerableOwnProperty(ExecutionContext cx, String propertyKey) {
return isEnumerableOwnProperty(propertyKey);
}
/** 9.1.12 [[OwnPropertyKeys]] ( ) */
@Override
public final List<?> ownPropertyKeys(ExecutionContext cx) {
return getOwnPropertyKeys(cx);
}
/** 9.1.12 [[OwnPropertyKeys]] ( ) */
@Override
public final Iterator<?> ownKeys(ExecutionContext cx) {
return getOwnPropertyKeys(cx).iterator();
}
/**
* 9.1.12 [[OwnPropertyKeys]] ( )
*
* @param cx
* the execution context
* @return the list of own property keys
*/
protected List<Object> getOwnPropertyKeys(ExecutionContext cx) {
int totalSize = countProperties(true);
if (totalSize == 0) {
return Collections.emptyList();
}
/* step 1 */
ArrayList<Object> ownKeys = new ArrayList<>(totalSize);
/* step 2 */
appendIndexedProperties(ownKeys);
/* step 3 */
appendProperties(ownKeys);
/* step 4 */
appendSymbolProperties(ownKeys);
/* step 5 */
return ownKeys;
}
/**
* 9.1.13 ObjectCreate(proto, internalSlotsList)
*
* @param cx
* the execution context
* @param proto
* the prototype object
* @return the new object
*/
public static final OrdinaryObject ObjectCreate(ExecutionContext cx, ScriptObject proto) {
return new OrdinaryObject(cx.getRealm(), proto);
}
/**
* 9.1.13 ObjectCreate(proto, internalSlotsList)
*
* @param cx
* the execution context
* @param proto
* the prototype object
* @return the new object
*/
public static final OrdinaryObject ObjectCreate(ExecutionContext cx, Intrinsics proto) {
return new OrdinaryObject(cx.getRealm(), cx.getIntrinsic(proto));
}
/**
* 9.1.13 ObjectCreate(proto, internalSlotsList)
*
* @param <OBJECT>
* the object type
* @param cx
* the execution context
* @param proto
* the prototype object
* @param allocator
* the object allocator
* @return the new object
*/
public static final <OBJECT extends OrdinaryObject> OBJECT ObjectCreate(ExecutionContext cx, ScriptObject proto,
ObjectAllocator<OBJECT> allocator) {
OBJECT obj = allocator.newInstance(cx.getRealm());
obj.setPrototype(proto);
return obj;
}
/**
* 9.1.13 ObjectCreate(proto, internalSlotsList)
*
* @param <OBJECT>
* the object type
* @param cx
* the execution context
* @param proto
* the prototype object
* @param allocator
* the object allocator
* @return the new object
*/
public static final <OBJECT extends OrdinaryObject> OBJECT ObjectCreate(ExecutionContext cx, Intrinsics proto,
ObjectAllocator<OBJECT> allocator) {
OBJECT obj = allocator.newInstance(cx.getRealm());
obj.setPrototype(cx.getIntrinsic(proto));
return obj;
}
/**
* 9.1.13 ObjectCreate(proto, internalSlotsList)
*
* @param realm
* the realm instance
* @param proto
* the prototype object
* @return the new object
*/
public static final OrdinaryObject ObjectCreate(Realm realm, ScriptObject proto) {
return new OrdinaryObject(realm, proto);
}
/**
* 9.1.13 ObjectCreate(proto, internalSlotsList)
*
* @param realm
* the realm instance
* @param proto
* the prototype object
* @return the new object
*/
public static final OrdinaryObject ObjectCreate(Realm realm, Intrinsics proto) {
return new OrdinaryObject(realm, realm.getIntrinsic(proto));
}
/**
* 9.1.13 ObjectCreate(proto, internalSlotsList)
*
* @param <OBJECT>
* the object type
* @param realm
* the realm instance
* @param proto
* the prototype object
* @param allocator
* the object allocator
* @return the new object
*/
public static final <OBJECT extends OrdinaryObject> OBJECT ObjectCreate(Realm realm, ScriptObject proto,
ObjectAllocator<OBJECT> allocator) {
OBJECT obj = allocator.newInstance(realm);
obj.setPrototype(proto);
return obj;
}
/**
* 9.1.13 ObjectCreate(proto, internalSlotsList)
*
* @param <OBJECT>
* the object type
* @param realm
* the realm instance
* @param proto
* the prototype object
* @param allocator
* the object allocator
* @return the new object
*/
public static final <OBJECT extends OrdinaryObject> OBJECT ObjectCreate(Realm realm, Intrinsics proto,
ObjectAllocator<OBJECT> allocator) {
OBJECT obj = allocator.newInstance(realm);
obj.setPrototype(realm.getIntrinsic(proto));
return obj;
}
/**
* 9.1.14 OrdinaryCreateFromConstructor (constructor, intrinsicDefaultProto, internalSlotsList)
*
* @param cx
* the execution context
* @param constructor
* the constructor function
* @param intrinsicDefaultProto
* the default prototype
* @return the new object
*/
public static final OrdinaryObject OrdinaryCreateFromConstructor(ExecutionContext cx, Callable constructor,
Intrinsics intrinsicDefaultProto) {
/* step 1 (not applicable) */
/* steps 2-3 */
ScriptObject proto = GetPrototypeFromConstructor(cx, constructor, intrinsicDefaultProto);
/* step 4 */
return ObjectCreate(cx, proto);
}
/**
* 9.1.14 OrdinaryCreateFromConstructor (constructor, intrinsicDefaultProto, internalSlotsList)
*
* @param <OBJECT>
* the object type
* @param cx
* the execution context
* @param constructor
* the constructor function
* @param intrinsicDefaultProto
* the default prototype
* @param allocator
* the object allocator
* @return the new object
*/
public static final <OBJECT extends OrdinaryObject> OBJECT OrdinaryCreateFromConstructor(ExecutionContext cx,
Callable constructor, Intrinsics intrinsicDefaultProto, ObjectAllocator<OBJECT> allocator) {
/* step 1 (not applicable) */
/* steps 2-3 */
ScriptObject proto = GetPrototypeFromConstructor(cx, constructor, intrinsicDefaultProto);
/* step 4 */
return ObjectCreate(cx, proto, allocator);
}
/**
* 9.1.15 GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto )
*
* @param cx
* the execution context
* @param constructor
* the constructor object
* @param intrinsicDefaultProto
* the default prototype
* @return the prototype object
*/
public static final ScriptObject GetPrototypeFromConstructor(ExecutionContext cx, Callable constructor,
Intrinsics intrinsicDefaultProto) {
/* steps 1-2 (not applicable) */
/* steps 3-4 */
Object proto = Get(cx, constructor, "prototype");
/* step 5 */
if (!Type.isObject(proto)) {
/* steps 5.a-b */
Realm realm = GetFunctionRealm(cx, constructor);
/* step 5.c */
proto = realm.getIntrinsic(intrinsicDefaultProto);
}
/* step 6 */
return Type.objectValue(proto);
}
}