/**
* 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.CanonicalNumericIndexString;
import java.util.List;
import com.github.anba.es6draft.runtime.ExecutionContext;
import com.github.anba.es6draft.runtime.Realm;
import com.github.anba.es6draft.runtime.internal.CompoundList;
import com.github.anba.es6draft.runtime.internal.IndexedPropertyKeyList;
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.Type;
/**
* <h1>9 Ordinary and Exotic Objects Behaviours</h1><br>
* <h2>9.4 Built-in Exotic Object Internal Methods and Data Fields</h2>
* <ul>
* <li>9.4.5 Integer Indexed Exotic Objects
* </ul>
*/
public abstract class IntegerIndexedObject extends OrdinaryObject {
/**
* Constructs a new Integer Indexed object.
*
* @param realm
* the realm object
*/
public IntegerIndexedObject(Realm realm) {
super(realm);
}
private static boolean isCanonicalNumericIndex(long numericIndex) {
return numericIndex >= 0;
}
@Override
public final boolean hasSpecialIndexedProperties() {
return true;
}
/** [[HasOwnProperty]] (P) */
@Override
protected final boolean hasOwnProperty(ExecutionContext cx, long propertyKey) {
return elementHas(cx, propertyKey);
}
/** [[HasOwnProperty]] (P) */
@Override
protected final boolean hasOwnProperty(ExecutionContext cx, String propertyKey) {
/* steps 1-2 (not applicable) */
/* step 3 */
long numericIndex = CanonicalNumericIndexString(propertyKey);
if (isCanonicalNumericIndex(numericIndex)) {
return elementHas(cx, numericIndex);
}
/* step 4 */
return super.hasOwnProperty(cx, propertyKey);
}
/** 9.4.5.1 [[GetOwnProperty]] (P) */
@Override
protected final Property getProperty(ExecutionContext cx, long propertyKey) {
Object value = elementGet(cx, propertyKey);
if (Type.isUndefined(value)) {
return null;
}
return new Property(value, true, true, false);
}
/** 9.4.5.1 [[GetOwnProperty]] (P) */
@Override
protected final Property getProperty(ExecutionContext cx, String propertyKey) {
/* steps 1-2 (not applicable) */
/* step 3 */
long numericIndex = CanonicalNumericIndexString(propertyKey);
if (isCanonicalNumericIndex(numericIndex)) {
Object value = elementGet(cx, numericIndex);
if (Type.isUndefined(value)) {
return null;
}
return new Property(value, true, true, false);
}
/* step 4 */
return ordinaryGetOwnProperty(propertyKey);
}
/** 9.4.5.2 [[HasProperty]](P) */
@Override
protected final boolean has(ExecutionContext cx, long propertyKey) {
return elementHas(cx, propertyKey);
}
/** 9.4.5.2 [[HasProperty]](P) */
@Override
protected final boolean has(ExecutionContext cx, String propertyKey) {
long numericIndex = CanonicalNumericIndexString(propertyKey);
if (isCanonicalNumericIndex(numericIndex)) {
return elementHas(cx, numericIndex);
}
return ordinaryHasProperty(cx, propertyKey);
}
/** 9.4.5.3 [[DefineOwnProperty]] (P, Desc) */
@Override
protected final boolean defineProperty(ExecutionContext cx, long propertyKey, PropertyDescriptor desc) {
/* steps 3.c.i-3.c.vi */
if (propertyKey >= getLength()) {
return false;
}
/* step 3.c.vii */
if (desc.isAccessorDescriptor()) {
return false;
}
/* step 3.c.viii */
if (desc.hasConfigurable() && desc.isConfigurable()) {
return false;
}
/* step 3.c.ix */
if (desc.hasEnumerable() && !desc.isEnumerable()) {
return false;
}
/* step 3.c.x */
if (desc.hasWritable() && !desc.isWritable()) {
return false;
}
/* step 3.c.xi */
if (desc.hasValue()) {
Object value = desc.getValue();
return elementSet(cx, propertyKey, value);
}
/* step 3.c.xii */
return true;
}
/** 9.4.5.3 [[DefineOwnProperty]] (P, Desc) */
@Override
protected final boolean defineProperty(ExecutionContext cx, String propertyKey, PropertyDescriptor desc) {
/* steps 1-2 (not applicable) */
/* step 3 */
long numericIndex = CanonicalNumericIndexString(propertyKey);
if (isCanonicalNumericIndex(numericIndex)) {
return defineProperty(cx, numericIndex, desc);
}
/* step 4 */
return ordinaryDefineOwnProperty(cx, propertyKey, desc);
}
/** 9.4.5.4 [[Get]] (P, Receiver) */
@Override
protected final Object getValue(ExecutionContext cx, long propertyKey, Object receiver) {
/* step 1 (not applicable) */
/* step 2 */
if (this == receiver) { // SameValue(this, receiver)
return elementGet(cx, propertyKey);
}
/* step 3 */
return super.getValue(cx, propertyKey, receiver);
}
/** 9.4.5.4 [[Get]] (P, Receiver) */
@Override
protected final Object getValue(ExecutionContext cx, String propertyKey, Object receiver) {
/* step 1 (not applicable) */
/* step 2 */
if (this == receiver) { // SameValue(this, receiver)
long numericIndex = CanonicalNumericIndexString(propertyKey);
if (isCanonicalNumericIndex(numericIndex)) {
return elementGet(cx, numericIndex);
}
}
/* step 3 */
return super.getValue(cx, propertyKey, receiver);
}
/** 9.4.5.5 [[Set]] (P, V, Receiver) */
@Override
protected final boolean setValue(ExecutionContext cx, long propertyKey, Object value, Object receiver) {
/* step 1 (not applicable) */
/* step 2 */
if (this == receiver) { // SameValue(this, receiver)
return elementSet(cx, propertyKey, value);
}
/* step 3 */
return super.setValue(cx, propertyKey, value, receiver);
}
/** 9.4.5.5 [[Set]] (P, V, Receiver) */
@Override
protected final boolean setValue(ExecutionContext cx, String propertyKey, Object value, Object receiver) {
/* step 1 (not applicable) */
/* step 2 */
if (this == receiver) { // SameValue(this, receiver)
long numericIndex = CanonicalNumericIndexString(propertyKey);
if (isCanonicalNumericIndex(numericIndex)) {
return elementSet(cx, numericIndex, value);
}
}
/* step 3 */
return super.setValue(cx, propertyKey, value, receiver);
}
/** 9.4.5.6 [[OwnPropertyKeys]] () */
@Override
protected final List<Object> getOwnPropertyKeys(ExecutionContext cx) {
/* steps 1-7 */
return new CompoundList<>(new IndexedPropertyKeyList(getLength()), super.getOwnPropertyKeys(cx));
}
@Override
protected final List<String> getEnumerableKeys(ExecutionContext cx) {
return new CompoundList<>(new IndexedPropertyKeyList(getLength()), super.getEnumerableKeys(cx));
}
@Override
protected final Enumerability isEnumerableOwnProperty(String propertyKey) {
long numericIndex = CanonicalNumericIndexString(propertyKey);
if (isCanonicalNumericIndex(numericIndex)) {
return Enumerability.isEnumerable(numericIndex < getLength());
}
return super.isEnumerableOwnProperty(propertyKey);
}
/**
* 9.4.5.7 IntegerIndexedObjectCreate (prototype, internalSlotsList)
*
* @param cx
* the execution context
* @param prototype
* the prototype object
* @return the new integer indexed object
*/
public static ScriptObject IntegerIndexedObjectCreate(ExecutionContext cx, ScriptObject prototype) {
// the operation is not supported in this implementation
throw new UnsupportedOperationException();
}
/**
* Not in spec
*
* @return the length property
*/
@Override
public abstract long getLength();
/**
* Not in spec
*
* @param cx
* the execution context
* @param index
* the integer index
* @return {@code true} if the element is present
*/
protected abstract boolean elementHas(ExecutionContext cx, long index);
/**
* 9.4.5.8 IntegerIndexedElementGet (O, index)
*
* @param cx
* the execution context
* @param index
* the integer index
* @return the element value
*/
protected abstract Object elementGet(ExecutionContext cx, long index);
/**
* 9.4.5.9 IntegerIndexedElementSet (O, index, value)
*
* @param cx
* the execution context
* @param index
* the integer index
* @param value
* the new element value
* @return {@code true} on success
*/
protected abstract boolean elementSet(ExecutionContext cx, long index, Object value);
}