/**
* 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;
import static com.github.anba.es6draft.runtime.internal.Errors.newInternalError;
import static com.github.anba.es6draft.runtime.internal.Errors.newRangeError;
import static com.github.anba.es6draft.runtime.internal.Errors.newTypeError;
import static com.github.anba.es6draft.runtime.internal.ScriptRuntime.InstanceofOperator;
import static com.github.anba.es6draft.runtime.objects.BooleanObject.BooleanCreate;
import static com.github.anba.es6draft.runtime.objects.SymbolObject.SymbolCreate;
import static com.github.anba.es6draft.runtime.objects.number.NumberObject.NumberCreate;
import static com.github.anba.es6draft.runtime.objects.simd.SIMDObject.SIMDCreate;
import static com.github.anba.es6draft.runtime.types.builtins.ArrayObject.DenseArrayCreate;
import static com.github.anba.es6draft.runtime.types.builtins.OrdinaryObject.ObjectCreate;
import static com.github.anba.es6draft.runtime.types.builtins.StringObject.StringCreate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
import org.mozilla.javascript.ConsString;
import org.mozilla.javascript.DToA;
import org.mozilla.javascript.v8dtoa.FastDtoa;
import com.github.anba.es6draft.parser.NumberParser;
import com.github.anba.es6draft.runtime.internal.Messages;
import com.github.anba.es6draft.runtime.internal.ScriptException;
import com.github.anba.es6draft.runtime.internal.ScriptIterator;
import com.github.anba.es6draft.runtime.internal.ScriptIterators;
import com.github.anba.es6draft.runtime.internal.ScriptRuntime;
import com.github.anba.es6draft.runtime.internal.Strings;
import com.github.anba.es6draft.runtime.objects.FunctionPrototype;
import com.github.anba.es6draft.runtime.objects.iteration.ListIterator;
import com.github.anba.es6draft.runtime.objects.simd.SIMD;
import com.github.anba.es6draft.runtime.objects.simd.SIMDValue;
import com.github.anba.es6draft.runtime.objects.text.RegExpObject;
import com.github.anba.es6draft.runtime.types.*;
import com.github.anba.es6draft.runtime.types.builtins.ArgumentsObject;
import com.github.anba.es6draft.runtime.types.builtins.ArrayObject;
import com.github.anba.es6draft.runtime.types.builtins.BoundFunctionObject;
import com.github.anba.es6draft.runtime.types.builtins.OrdinaryObject;
import com.github.anba.es6draft.runtime.types.builtins.ProxyObject;
import com.google.doubleconversion.DoubleConversion;
/**
* <h1>7 Abstract Operations</h1>
* <ul>
* <li>7.1 Type Conversion
* <li>7.2 Testing and Comparison Operations
* <li>7.3 Operations on Objects
* <li>7.4 Operations on Iterator Objects
* </ul>
*/
public final class AbstractOperations {
private AbstractOperations() {
}
/**
* Hint string for
* {@link AbstractOperations#ToPrimitive(ExecutionContext, Object, ToPrimitiveHint)}
*/
public enum ToPrimitiveHint {
Default, String, Number;
@Override
public String toString() {
switch (this) {
case String:
return "string";
case Number:
return "number";
case Default:
return "default";
default:
throw new AssertionError();
}
}
}
/**
* 7.1.1 ToPrimitive ( input [, PreferredType] )
*
* @param cx
* the execution context
* @param argument
* the argument value
* @return the primitive value
*/
public static Object ToPrimitive(ExecutionContext cx, Object argument) {
if (!Type.isObject(argument)) {
return argument;
}
return ToPrimitive(cx, Type.objectValue(argument), ToPrimitiveHint.Default);
}
/**
* 7.1.1 ToPrimitive ( input [, PreferredType] )
*
* @param cx
* the execution context
* @param argument
* the argument value
* @return the primitive value
*/
public static Object ToPrimitive(ExecutionContext cx, ScriptObject argument) {
return ToPrimitive(cx, argument, ToPrimitiveHint.Default);
}
/**
* 7.1.1 ToPrimitive ( input [, PreferredType] )
*
* @param cx
* the execution context
* @param argument
* the argument value
* @param preferredType
* the preferred primitive type
* @return the primitive value
*/
public static Object ToPrimitive(ExecutionContext cx, Object argument,
ToPrimitiveHint preferredType) {
if (!Type.isObject(argument)) {
return argument;
}
return ToPrimitive(cx, Type.objectValue(argument), preferredType);
}
/**
* 7.1.1 ToPrimitive ( input [, PreferredType] )
* <p>
* ToPrimitive for the Object type
*
* @param cx
* the execution context
* @param argument
* the argument value
* @param preferredType
* the preferred primitive type
* @return the primitive value
*/
public static Object ToPrimitive(ExecutionContext cx, ScriptObject argument,
ToPrimitiveHint preferredType) {
/* steps 1-3 */
String hint = preferredType.toString();
/* steps 4-5 */
Callable exoticToPrim = GetMethod(cx, argument, BuiltinSymbol.toPrimitive.get());
/* step 6 */
if (exoticToPrim != null) {
Object result = exoticToPrim.call(cx, argument, hint);
if (!Type.isObject(result)) {
return result;
}
throw newTypeError(cx, Messages.Key.NotPrimitiveType);
}
/* step 7 */
if (preferredType == ToPrimitiveHint.Default) {
preferredType = ToPrimitiveHint.Number;
}
/* step 8 */
return OrdinaryToPrimitive(cx, argument, preferredType);
}
/**
* 7.1.1 ToPrimitive ( input [, PreferredType] )
* <p>
* OrdinaryToPrimitive
*
* @param cx
* the execution context
* @param object
* the argument object
* @param hint
* the preferred primitive type
* @return the primitive value
*/
public static Object OrdinaryToPrimitive(ExecutionContext cx, ScriptObject object,
ToPrimitiveHint hint) {
/* steps 1-2 */
assert hint == ToPrimitiveHint.String || hint == ToPrimitiveHint.Number;
/* steps 3-4 */
String tryFirst, trySecond;
if (hint == ToPrimitiveHint.String) {
tryFirst = "toString";
trySecond = "valueOf";
} else {
tryFirst = "valueOf";
trySecond = "toString";
}
/* step 5 (first try) */
Object first = Get(cx, object, tryFirst);
if (IsCallable(first)) {
Object result = ((Callable) first).call(cx, object);
if (!Type.isObject(result)) {
return result;
}
}
/* step 5 (second try) */
Object second = Get(cx, object, trySecond);
if (IsCallable(second)) {
Object result = ((Callable) second).call(cx, object);
if (!Type.isObject(result)) {
return result;
}
}
/* step 6 */
throw newTypeError(cx, Messages.Key.NoPrimitiveRepresentation);
}
/**
* 7.1.2 ToBoolean ( argument )
*
* @param value
* the argument value
* @return the boolean result
*/
public static boolean ToBoolean(Object value) {
switch (Type.of(value)) {
case Undefined:
return false;
case Null:
return false;
case Boolean:
return Type.booleanValue(value);
case Number:
double d = Type.numberValue(value);
return !(d == 0 || Double.isNaN(d));
case String:
return Type.stringValue(value).length() != 0;
case Symbol:
return true;
case SIMD:
return true;
case Object:
return true;
default:
throw new AssertionError();
}
}
/**
* 7.1.2 ToBoolean ( argument )
*
* @param value
* the argument value
* @return the boolean result
*/
public static boolean ToBoolean(double value) {
return !(value == 0 || Double.isNaN(value));
}
/**
* 7.1.3 ToNumber ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the number result
*/
public static double ToNumber(ExecutionContext cx, Object value) {
if (Type.isNumber(value)) {
return Type.numberValue(value);
}
return ToNumberSlow(cx, value);
}
/**
* 7.1.3 ToNumber ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the number result
*/
private static double ToNumberSlow(ExecutionContext cx, Object value) {
switch (Type.of(value)) {
case Undefined:
return Double.NaN;
case Null:
return +0;
case Boolean:
return Type.booleanValue(value) ? 1 : +0;
case Number:
return Type.numberValue(value);
case String:
return ToNumber(Type.stringValue(value));
case Symbol:
throw newTypeError(cx, Messages.Key.SymbolNumber);
case SIMD:
throw newTypeError(cx, Messages.Key.SIMDNumber);
case Object:
Object primValue = ToPrimitive(cx, Type.objectValue(value), ToPrimitiveHint.Number);
return ToNumber(cx, primValue);
default:
throw new AssertionError();
}
}
/**
* 7.1.3.1 ToNumber Applied to the String Type
*
* @param string
* the argument value
* @return the number result
*/
public static double ToNumber(CharSequence string) {
return ToNumberParser.parse(string.toString());
}
/**
* 7.1.4 ToInteger ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the integer result
*/
public static double ToInteger(ExecutionContext cx, Object value) {
/* steps 1-2 */
double number = ToNumber(cx, value);
/* step 3 */
if (Double.isNaN(number))
return +0d;
/* step 4 */
if (number == 0d || Double.isInfinite(number))
return number;
/* step 5 */
// return Math.signum(number) * Math.floor(Math.abs(number));
if (number < 0) {
return Math.ceil(number);
}
return Math.floor(number);
}
/**
* 7.1.4 ToInteger ( argument )
*
* @param number
* the argument value
* @return the integer result
*/
public static double ToInteger(double number) {
/* steps 1-2 (not applicable) */
/* step 3 */
if (Double.isNaN(number))
return +0d;
/* step 4 */
if (number == 0d || Double.isInfinite(number))
return number;
/* step 5 */
// return Math.signum(number) * Math.floor(Math.abs(number));
if (number < 0) {
return Math.ceil(number);
}
return Math.floor(number);
}
/**
* 7.1.5 ToInt32 ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the integer result
*/
public static int ToInt32(ExecutionContext cx, Object value) {
/* steps 1-2 */
double number = ToNumber(cx, value);
/* steps 3-6 */
return DoubleConversion.doubleToInt32(number);
}
/**
* 7.1.5 ToInt32 ( argument )
*
* @param number
* the argument value
* @return the integer result
*/
public static int ToInt32(double number) {
/* steps 1-2 (not applicable) */
/* steps 3-6 */
return DoubleConversion.doubleToInt32(number);
}
/**
* 7.1.6 ToUint32 ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the integer result
*/
public static long ToUint32(ExecutionContext cx, Object value) {
/* steps 1-2 */
double number = ToNumber(cx, value);
/* steps 3-6 */
return DoubleConversion.doubleToInt32(number) & 0xffffffffL;
}
/**
* 7.1.6 ToUint32 ( argument )
*
* @param number
* the argument value
* @return the integer result
*/
public static long ToUint32(double number) {
/* steps 1-2 (not applicable) */
/* steps 3-6 */
return DoubleConversion.doubleToInt32(number) & 0xffffffffL;
}
/**
* 7.1.7 ToInt16 ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the integer result
*/
public static short ToInt16(ExecutionContext cx, Object value) {
/* steps 1-2 */
double number = ToNumber(cx, value);
/* steps 3-6 */
return (short) DoubleConversion.doubleToInt32(number);
}
/**
* 7.1.7 ToInt16 ( argument )
*
* @param number
* the argument value
* @return the integer result
*/
public static short ToInt16(double number) {
/* steps 1-2 (not applicable) */
/* steps 3-6 */
return (short) DoubleConversion.doubleToInt32(number);
}
/**
* 7.1.8 ToUint16 ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the integer result
*/
public static char ToUint16(ExecutionContext cx, Object value) {
/* steps 1-2 */
double number = ToNumber(cx, value);
/* steps 3-6 */
return (char) DoubleConversion.doubleToInt32(number);
}
/**
* 7.1.8 ToUint16 ( argument )
*
* @param number
* the argument value
* @return the integer result
*/
public static char ToUint16(double number) {
/* steps 1-2 (not applicable) */
/* steps 3-6 */
return (char) DoubleConversion.doubleToInt32(number);
}
/**
* 7.1.9 ToInt8 ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the integer result
*/
public static byte ToInt8(ExecutionContext cx, Object value) {
/* steps 1-2 */
double number = ToNumber(cx, value);
/* steps 3-6 */
return (byte) DoubleConversion.doubleToInt32(number);
}
/**
* 7.1.9 ToInt8 ( argument )
*
* @param number
* the argument value
* @return the integer result
*/
public static byte ToInt8(double number) {
/* steps 1-2 (not applicable) */
/* steps 3-6 */
return (byte) DoubleConversion.doubleToInt32(number);
}
/**
* 7.1.10 ToUint8 ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the integer result
*/
public static int ToUint8(ExecutionContext cx, Object value) {
/* steps 1-2 */
double number = ToNumber(cx, value);
/* steps 3-6 */
return DoubleConversion.doubleToInt32(number) & 0xFF;
}
/**
* 7.1.10 ToUint8 ( argument )
*
* @param number
* the argument value
* @return the integer result
*/
public static int ToUint8(double number) {
/* steps 1-2 (not applicable) */
/* steps 3-6 */
return DoubleConversion.doubleToInt32(number) & 0xFF;
}
/**
* 7.1.11 ToUint8Clamp ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the integer result
*/
public static int ToUint8Clamp(ExecutionContext cx, Object value) {
/* steps 1-2 */
double number = ToNumber(cx, value);
/* step 4 */
if (number <= 0) {
return 0;
}
/* step 5 */
if (number >= 255) {
return 255;
}
/* steps 3, 6-10 */
return (int) Math.rint(number);
}
/**
* 7.1.11 ToUint8Clamp ( argument )
*
* @param number
* the argument value
* @return the integer result
*/
public static int ToUint8Clamp(double number) {
/* steps 1-2 (not applicable) */
/* step 4 */
if (number <= 0) {
return 0;
}
/* step 5 */
if (number >= 255) {
return 255;
}
/* steps 3, 6-10 */
return (int) Math.rint(number);
}
/**
* 7.1.12 ToString ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the string result
*/
public static String ToFlatString(ExecutionContext cx, Object value) {
return ToString(cx, value).toString();
}
/**
* 7.1.12 ToString ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the string result
*/
public static CharSequence ToString(ExecutionContext cx, Object value) {
if (Type.isString(value)) {
return Type.stringValue(value);
}
return ToStringSlow(cx, value);
}
/**
* 7.1.12 ToString ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the string result
*/
private static CharSequence ToStringSlow(ExecutionContext cx, Object value) {
switch (Type.of(value)) {
case Undefined:
return "undefined";
case Null:
return "null";
case Boolean:
return Type.booleanValue(value) ? "true" : "false";
case Number:
return ToString(Type.numberValue(value));
case String:
return Type.stringValue(value);
case Symbol:
throw newTypeError(cx, Messages.Key.SymbolString);
case SIMD:
return SIMD.ToString(Type.simdValue(value));
case Object:
Object primValue = ToPrimitive(cx, Type.objectValue(value), ToPrimitiveHint.String);
return ToString(cx, primValue);
default:
throw new AssertionError();
}
}
private static final String[] cachedIntegerStrings = { "0", "1", "2", "3", "4", "5", "6", "7",
"8", "9" };
/**
* 7.1.12.1 ToString Applied to the Number Type
*
* @param value
* the argument value
* @return the string result
*/
public static String ToString(int value) {
return Integer.toString(value);
}
/**
* 7.1.12.1 ToString Applied to the Number Type
*
* @param value
* the argument value
* @return the string result
*/
public static String ToString(long value) {
if ((int) value == value) {
return Integer.toString((int) value);
} else if (-0x1F_FFFF_FFFF_FFFFL <= value && value <= 0x1F_FFFF_FFFF_FFFFL) {
return Long.toString(value);
}
return ToString((double) value);
}
/**
* 7.1.12.1 ToString Applied to the Number Type
*
* @param value
* the argument value
* @return the string result
*/
public static String ToString(double value) {
/* steps 1-4 (+ shortcut for integer values) */
int intValue = (int) value;
if (intValue == value) {
if (0 <= intValue && intValue <= 9) {
return cachedIntegerStrings[intValue];
}
return Integer.toString(intValue);
} else if (value != value) {
return "NaN";
} else if (value == Double.POSITIVE_INFINITY) {
return "Infinity";
} else if (value == Double.NEGATIVE_INFINITY) {
return "-Infinity";
} else if (value == 0d) {
return "0";
}
// call DToA for general number-to-string
String result = FastDtoa.numberToString(value);
if (result != null) {
return result;
}
StringBuilder buffer = new StringBuilder();
DToA.JS_dtostr(buffer, DToA.DTOSTR_STANDARD, 0, value);
return buffer.toString();
}
/**
* 7.1.13 ToObject ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the object result
*/
public static ScriptObject ToObject(ExecutionContext cx, Object value) {
if (Type.isObject(value)) {
return Type.objectValue(value);
}
return ToObjectSlow(cx, value);
}
/**
* 7.1.13 ToObject ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the object result
*/
private static ScriptObject ToObjectSlow(ExecutionContext cx, Object value) {
switch (Type.of(value)) {
case Undefined:
case Null:
throw newTypeError(cx, Messages.Key.UndefinedOrNull);
case Boolean:
return BooleanCreate(cx, Type.booleanValue(value));
case Number:
return NumberCreate(cx, Type.numberValue(value));
case String:
return StringCreate(cx, Type.stringValue(value));
case Symbol:
return SymbolCreate(cx, Type.symbolValue(value));
case SIMD:
// FIXME: spec bug - unclear/invalid description.
return SIMDCreate(cx, Type.simdValue(value));
case Object:
return Type.objectValue(value);
default:
throw new AssertionError();
}
}
/**
* 7.1.14 ToPropertyKey ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the property key
*/
public static Object ToPropertyKey(ExecutionContext cx, Object value) {
/* steps 1-2 */
Object key = ToPrimitive(cx, value, ToPrimitiveHint.String);
/* step 3 */
if (key instanceof Symbol) {
return key;
}
/* step 4 */
return ToFlatString(cx, key);
}
/**
* 7.1.15 ToLength ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the length value
*/
public static long ToLength(ExecutionContext cx, Object value) {
/* step 1 (not applicable) */
/* steps 2-3 */
double len = ToInteger(cx, value);
/* step 4 */
if (len <= 0) {
return 0;
}
/* steps 5-6 */
return (long) Math.min(len, 0x1F_FFFF_FFFF_FFFFp0);
}
/**
* 7.1.15 ToLength ( argument )
*
* @param value
* the argument value
* @return the length value
*/
public static long ToLength(double value) {
/* steps 1-3 (not applicable) */
/* step 4 */
if (value <= 0) {
return 0;
}
/* steps 5-6 */
return (long) Math.min(value, 0x1F_FFFF_FFFF_FFFFp0);
}
/**
* 7.1.16 CanonicalNumericIndexString ( argument )
*
* @param value
* the argument value
* @return the canonical number or -1 if not canonical
*/
public static long CanonicalNumericIndexString(String value) {
// Shortcut if value does not start with a valid canonical numeric index character
if (value.isEmpty() || !isCanonicalNumericIndexStringPrefix(value.charAt(0))) {
return -1;
}
/* step 1 (not applicable) */
/* step 2 */
if ("-0".equals(value)) {
return Long.MAX_VALUE;
}
/* step 3 */
double n = ToNumberParser.readDecimalLiteral(value);
/* step 4 */
if (!value.equals(ToString(n))) {
return -1;
}
// Directly perform IsInteger() check and encode negative and non-integer indices as OOB.
if (n < 0 || !IsInteger(n)) {
return Long.MAX_VALUE;
}
/* step 5 */
return (long) n;
}
private static boolean isCanonicalNumericIndexStringPrefix(char c) {
return ('0' <= c && c <= '9') || c == '-' || c == 'I' || c == 'N';
}
/**
* 7.2.1 RequireObjectCoercible ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the input argument unless it is either <code>undefined</code> or <code>null</code>
*/
public static Object RequireObjectCoercible(ExecutionContext cx, Object value) {
if (Type.isUndefinedOrNull(value)) {
throw newTypeError(cx, Messages.Key.UndefinedOrNull);
}
return value;
}
/**
* 7.2.2 IsArray ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return {@code true} if the argument is an Array object
*/
public static boolean IsArray(ExecutionContext cx, Object value) {
/* step 1 (implicit) */
/* step 2 */
if (value instanceof ArrayObject) {
return true;
}
/* step 3 */
if (value instanceof ProxyObject) {
return ((ProxyObject) value).unwrap(cx) instanceof ArrayObject;
}
/* step 4 */
return false;
}
/**
* 7.2.3 IsCallable ( argument )
*
* @param value
* the argument value
* @return {@code true} if the value is a callable object
*/
public static boolean IsCallable(Object value) {
/* steps 1-4 */
return value instanceof Callable;
}
/**
* 7.2.4 IsConstructor ( argument )
*
* @param value
* the argument value
* @return {@code true} if the value is a constructor object
*/
public static boolean IsConstructor(Object value) {
/* steps 1-4 */
return value instanceof Constructor;
}
/**
* 7.2.5 IsExtensible (O)
*
* @param cx
* the execution context
* @param object
* the script object
* @return {@code true} if the object is extensible
*/
public static boolean IsExtensible(ExecutionContext cx, ScriptObject object) {
/* steps 1-2 */
return object.isExtensible(cx);
}
/**
* 7.2.6 IsInteger ( argument )
*
* @param value
* the argument value
* @return {@code true} if the value is a finite integer
*/
public static boolean IsInteger(Object value) {
/* steps 1-2 */
if (!Type.isNumber(value)) {
return false;
}
double d = Type.numberValue(value);
/* step 3 */
if (Double.isNaN(d) || Double.isInfinite(d)) {
return false;
}
/* step 4 */
if (Math.floor(Math.abs(d)) != Math.abs(d)) {
return false;
}
/* step 5 */
return true;
}
/**
* 7.2.6 IsInteger ( argument )
*
* @param value
* the argument value
* @return {@code true} if the value is a finite integer
*/
public static boolean IsInteger(double value) {
/* steps 1-2 (not applicable) */
double d = value;
/* step 3 */
if (Double.isNaN(d) || Double.isInfinite(d)) {
return false;
}
/* step 4 */
if (Math.floor(Math.abs(d)) != Math.abs(d)) {
return false;
}
/* step 5 */
return true;
}
/**
* 7.2.7 IsPropertyKey ( argument )
*
* @param value
* the argument value
* @return {@code true} if the value is a property key
*/
public static boolean IsPropertyKey(Object value) {
/* steps 1-4 */
return value instanceof String || value instanceof Symbol;
}
/**
* 7.2.8 IsRegExp ( argument )
*
* @param cx
* the execution context
* @param value
* the argument value
* @return {@code true} if the value is a regular expression object
*/
public static boolean IsRegExp(ExecutionContext cx, Object value) {
/* step 1 */
if (!Type.isObject(value)) {
return false;
}
ScriptObject object = Type.objectValue(value);
/* steps 2-3 */
Object isRegExp = Get(cx, object, BuiltinSymbol.match.get());
/* step 4 */
if (!Type.isUndefined(isRegExp)) {
return ToBoolean(isRegExp);
}
/* step 5 */
if (object instanceof RegExpObject) {
return true;
}
/* step 6 */
return false;
}
/**
* 7.2.9 SameValue(x, y)
*
* @param x
* the first operand
* @param y
* the second operand
* @return {@code true} if both operands have the same value
*/
public static boolean SameValue(Object x, Object y) {
/* same reference shortcuts */
if (x == y) {
return true;
}
Type tx = Type.of(x);
Type ty = Type.of(y);
/* steps 1-2 (not applicable) */
/* step 3 */
if (tx != ty) {
return false;
}
/* step 4 */
if (tx == Type.Undefined) {
return true;
}
/* step 5 */
if (tx == Type.Null) {
return true;
}
/* step 6 */
if (tx == Type.Number) {
double dx = Type.numberValue(x);
double dy = Type.numberValue(y);
return Double.compare(dx, dy) == 0;
}
/* step 7 */
if (tx == Type.String) {
CharSequence sx = Type.stringValue(x);
CharSequence sy = Type.stringValue(y);
return sx.length() == sy.length() && sx.toString().equals(sy.toString());
}
/* step 8 */
if (tx == Type.Boolean) {
return Type.booleanValue(x) == Type.booleanValue(y);
}
/* Extension: SIMD */
if (tx == Type.SIMD) {
return SIMD.SameValue(Type.simdValue(x), Type.simdValue(y));
}
/* steps 9-10 */
assert tx == Type.Object || tx == Type.Symbol;
return (x == y);
}
/**
* 7.2.9 SameValue(x, y)
*
* @param x
* the first operand
* @param y
* the second operand
* @return {@code true} if both operands have the same value
*/
public static boolean SameValue(double x, double y) {
/* steps 1-5, 7-10 (not applicable) */
/* step 6 */
return Double.compare(x, y) == 0;
}
/**
* 7.2.10 SameValueZero(x, y)
*
* @param x
* the first operand
* @param y
* the second operand
* @return {@code true} if both operands have the same value
*/
public static boolean SameValueZero(Object x, Object y) {
/* same reference shortcuts */
if (x == y) {
return true;
}
Type tx = Type.of(x);
Type ty = Type.of(y);
/* steps 1-2 (not applicable) */
/* step 3 */
if (tx != ty) {
return false;
}
/* step 4 */
if (tx == Type.Undefined) {
return true;
}
/* step 5 */
if (tx == Type.Null) {
return true;
}
/* step 6 */
if (tx == Type.Number) {
double dx = Type.numberValue(x);
double dy = Type.numberValue(y);
return dx == dy || (Double.isNaN(dx) && Double.isNaN(dy));
}
/* step 7 */
if (tx == Type.String) {
CharSequence sx = Type.stringValue(x);
CharSequence sy = Type.stringValue(y);
return sx.length() == sy.length() && sx.toString().equals(sy.toString());
}
/* step 8 */
if (tx == Type.Boolean) {
return Type.booleanValue(x) == Type.booleanValue(y);
}
/* Extension: SIMD */
if (tx == Type.SIMD) {
return SIMD.SameValueZero(Type.simdValue(x), Type.simdValue(y));
}
/* steps 9-10 */
assert tx == Type.Object || tx == Type.Symbol;
return (x == y);
}
/**
* 7.2.10 SameValueZero(x, y)
*
* @param x
* the first operand
* @param y
* the second operand
* @return {@code true} if both operands have the same value
*/
public static boolean SameValueZero(double x, double y) {
/* steps 1-5, 7-10 (not applicable) */
/* step 6 */
return x == y || (Double.isNaN(x) && Double.isNaN(y));
}
/**
* 7.2.11 Abstract Relational Comparison
*
* @param cx
* the execution context
* @param x
* the first operand
* @param y
* the second operand
* @param leftFirst
* the operation order flag
* @return the comparison result
*/
public static int RelationalComparison(ExecutionContext cx, Object x, Object y,
boolean leftFirst) {
// true -> 1
// false -> 0
// undefined -> -1
/* steps 1-2 (not applicable) */
/* steps 3-4 */
Object px, py;
if (leftFirst) {
px = ToPrimitive(cx, x, ToPrimitiveHint.Number);
py = ToPrimitive(cx, y, ToPrimitiveHint.Number);
} else {
py = ToPrimitive(cx, y, ToPrimitiveHint.Number);
px = ToPrimitive(cx, x, ToPrimitiveHint.Number);
}
/* step 5 */
if (Type.isString(px) && Type.isString(py)) {
int c = Type.stringValue(px).toString().compareTo(Type.stringValue(py).toString());
return c < 0 ? 1 : 0;
}
/* step 6 */
double nx = ToNumber(cx, px);
double ny = ToNumber(cx, py);
if (Double.isNaN(nx) || Double.isNaN(ny)) {
return -1;
}
if (nx == ny) {
return 0;
}
if (nx == Double.POSITIVE_INFINITY) {
return 0;
}
if (ny == Double.POSITIVE_INFINITY) {
return 1;
}
if (ny == Double.NEGATIVE_INFINITY) {
return 0;
}
if (nx == Double.NEGATIVE_INFINITY) {
return 1;
}
return nx < ny ? 1 : 0;
}
/**
* 7.2.12 Abstract Equality Comparison
*
* @param cx
* the execution context
* @param x
* the first operand
* @param y
* the second operand
* @return the comparison result
*/
public static boolean EqualityComparison(ExecutionContext cx, Object x, Object y) {
// Fast path for same reference.
if (x == y) {
if (x instanceof Double) {
return !((Double) x).isNaN();
}
if (!(x instanceof SIMDValue)) {
return true;
}
}
/* steps 1-2 (not applicable) */
Type tx = Type.of(x);
Type ty = Type.of(y);
/* step 3 */
if (tx == ty) {
return StrictEqualityComparison(x, y);
}
/* step 4 */
if (tx == Type.Null && ty == Type.Undefined) {
return true;
}
/* step 5 */
if (tx == Type.Undefined && ty == Type.Null) {
return true;
}
/* step 6 */
if (tx == Type.Number && ty == Type.String) {
// return EqualityComparison(cx, x, ToNumber(cx, y));
return Type.numberValue(x) == ToNumber(Type.stringValue(y));
}
/* step 7 */
if (tx == Type.String && ty == Type.Number) {
// return EqualityComparison(cx, ToNumber(cx, x), y);
return ToNumber(Type.stringValue(x)) == Type.numberValue(y);
}
/* step 8 */
if (tx == Type.Boolean) {
return EqualityComparison(cx, ToNumber(cx, x), y);
}
/* step 9 */
if (ty == Type.Boolean) {
return EqualityComparison(cx, x, ToNumber(cx, y));
}
/* step 10 */
if ((tx == Type.String || tx == Type.Number || tx == Type.Symbol || tx == Type.SIMD) && ty == Type.Object) {
return EqualityComparison(cx, x, ToPrimitive(cx, Type.objectValue(y)));
}
/* step 11 */
if (tx == Type.Object && (ty == Type.String || ty == Type.Number || ty == Type.Symbol || ty == Type.SIMD)) {
return EqualityComparison(cx, ToPrimitive(cx, Type.objectValue(x)), y);
}
/* step 12 */
return false;
}
/**
* 7.2.13 Strict Equality Comparison
*
* @param x
* the first operand
* @param y
* the second operand
* @return the comparison result
*/
public static boolean StrictEqualityComparison(Object x, Object y) {
// Fast path for same reference.
if (x == y) {
if (x instanceof Double) {
return !((Double) x).isNaN();
}
if (!(x instanceof SIMDValue)) {
return true;
}
}
Type tx = Type.of(x);
Type ty = Type.of(y);
/* step 1 */
if (tx != ty) {
return false;
}
/* step 2 */
if (tx == Type.Undefined) {
return true;
}
/* step 3 */
if (tx == Type.Null) {
return true;
}
/* step 4 */
if (tx == Type.Number) {
return Type.numberValue(x) == Type.numberValue(y);
}
/* step 5 */
if (tx == Type.String) {
CharSequence sx = Type.stringValue(x);
CharSequence sy = Type.stringValue(y);
return sx.length() == sy.length() && sx.toString().equals(sy.toString());
}
/* step 6 */
if (tx == Type.Boolean) {
return Type.booleanValue(x) == Type.booleanValue(y);
}
/* Extension: SIMD */
if (tx == Type.SIMD) {
return SIMD.StrictEquality(Type.simdValue(x), Type.simdValue(y));
}
assert tx == Type.Object || tx == Type.Symbol;
/* steps 7-9 */
return (x == y);
}
/**
* 7.3.1 Get (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return the property value
*/
public static Object Get(ExecutionContext cx, ScriptObject object, Object propertyKey) {
/* steps 1-3 */
if (propertyKey instanceof String) {
return Get(cx, object, (String) propertyKey);
} else {
return Get(cx, object, (Symbol) propertyKey);
}
}
/**
* 7.3.1 Get (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return the property value
*/
public static Object Get(ExecutionContext cx, ScriptObject object, long propertyKey) {
/* steps 1-3 */
return object.get(cx, propertyKey, object);
}
/**
* 7.3.1 Get (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return the property value
*/
public static Object Get(ExecutionContext cx, ScriptObject object, String propertyKey) {
/* steps 1-3 */
return object.get(cx, propertyKey, object);
}
/**
* 7.3.1 Get (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return the property value
*/
public static Object Get(ExecutionContext cx, ScriptObject object, Symbol propertyKey) {
/* steps 1-3 */
return object.get(cx, propertyKey, object);
}
/**
* 7.3.2 GetV (V, P)
*
* @param cx
* the execution context
* @param value
* the argument value
* @param propertyKey
* the property key
* @return the property value
*/
public static Object GetV(ExecutionContext cx, Object value, Object propertyKey) {
/* steps 1-3 */
if (propertyKey instanceof String) {
return GetV(cx, value, (String) propertyKey);
} else {
return GetV(cx, value, (Symbol) propertyKey);
}
}
/**
* 7.3.2 GetV (V, P)
*
* @param cx
* the execution context
* @param value
* the argument value
* @param propertyKey
* the property key
* @return the property value
*/
public static Object GetV(ExecutionContext cx, Object value, long propertyKey) {
/* step 1 (not applicable) */
/* steps 2-3 */
ScriptObject obj = ToObject(cx, value);
/* step 4 */
return obj.get(cx, propertyKey, value);
}
/**
* 7.3.2 GetV (V, P)
*
* @param cx
* the execution context
* @param value
* the argument value
* @param propertyKey
* the property key
* @return the property value
*/
public static Object GetV(ExecutionContext cx, Object value, String propertyKey) {
/* step 1 (not applicable) */
/* steps 2-3 */
ScriptObject obj = ToObject(cx, value);
/* step 4 */
return obj.get(cx, propertyKey, value);
}
/**
* 7.3.2 GetV (V, P)
*
* @param cx
* the execution context
* @param value
* the argument value
* @param propertyKey
* the property key
* @return the property value
*/
public static Object GetV(ExecutionContext cx, Object value, Symbol propertyKey) {
/* step 1 (not applicable) */
/* steps 2-3 */
ScriptObject obj = ToObject(cx, value);
/* step 4 */
return obj.get(cx, propertyKey, value);
}
/**
* 7.3.3 Set (O, P, V, Throw)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
* @param _throw
* the throw flag
*/
public static void Set(ExecutionContext cx, ScriptObject object, Object propertyKey,
Object value, boolean _throw) {
/* steps 1-7 */
if (propertyKey instanceof String) {
Set(cx, object, (String) propertyKey, value, _throw);
} else {
Set(cx, object, (Symbol) propertyKey, value, _throw);
}
}
/**
* 7.3.3 Set (O, P, V, Throw)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
* @param _throw
* the throw flag
*/
public static void Set(ExecutionContext cx, ScriptObject object, long propertyKey,
Object value, boolean _throw) {
/* steps 1-5 */
boolean success = object.set(cx, propertyKey, value, object);
/* step 6 */
if (!success && _throw) {
throw newTypeError(cx, Messages.Key.PropertyNotModifiable, ToString(propertyKey));
}
/* step 7 (not applicable) */
}
/**
* 7.3.3 Set (O, P, V, Throw)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
* @param _throw
* the throw flag
*/
public static void Set(ExecutionContext cx, ScriptObject object, String propertyKey,
Object value, boolean _throw) {
/* steps 1-5 */
boolean success = object.set(cx, propertyKey, value, object);
/* step 6 */
if (!success && _throw) {
throw newTypeError(cx, Messages.Key.PropertyNotModifiable, propertyKey);
}
/* step 7 (not applicable) */
}
/**
* 7.3.3 Set (O, P, V, Throw)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
* @param _throw
* the throw flag
*/
public static void Set(ExecutionContext cx, ScriptObject object, Symbol propertyKey,
Object value, boolean _throw) {
/* steps 1-5 */
boolean success = object.set(cx, propertyKey, value, object);
/* step 6 */
if (!success && _throw) {
throw newTypeError(cx, Messages.Key.PropertyNotModifiable, propertyKey.toString());
}
/* step 7 (not applicable) */
}
/**
* 7.3.4 CreateDataProperty (O, P, V)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
* @return {@code true} on success
*/
public static boolean CreateDataProperty(ExecutionContext cx, ScriptObject object,
Object propertyKey, Object value) {
if (propertyKey instanceof String) {
return CreateDataProperty(cx, object, (String) propertyKey, value);
} else {
return CreateDataProperty(cx, object, (Symbol) propertyKey, value);
}
}
/**
* 7.3.4 CreateDataProperty (O, P, V)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
* @return {@code true} on success
*/
public static boolean CreateDataProperty(ExecutionContext cx, ScriptObject object,
long propertyKey, Object value) {
/* steps 1-2 (not applicable) */
/* step 3 */
PropertyDescriptor newDesc = new PropertyDescriptor(value, true, true, true);
/* step 4 */
return object.defineOwnProperty(cx, propertyKey, newDesc);
}
/**
* 7.3.4 CreateDataProperty (O, P, V)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
* @return {@code true} on success
*/
public static boolean CreateDataProperty(ExecutionContext cx, ScriptObject object,
String propertyKey, Object value) {
/* steps 1-2 (not applicable) */
/* step 3 */
PropertyDescriptor newDesc = new PropertyDescriptor(value, true, true, true);
/* step 4 */
return object.defineOwnProperty(cx, propertyKey, newDesc);
}
/**
* 7.3.4 CreateDataProperty (O, P, V)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
* @return {@code true} on success
*/
public static boolean CreateDataProperty(ExecutionContext cx, ScriptObject object,
Symbol propertyKey, Object value) {
/* steps 1-2 (not applicable) */
/* step 3 */
PropertyDescriptor newDesc = new PropertyDescriptor(value, true, true, true);
/* step 4 */
return object.defineOwnProperty(cx, propertyKey, newDesc);
}
/**
* 7.3.5 CreateMethodProperty (O, P, V)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
* @return {@code true} on success
*/
public static boolean CreateMethodProperty(ExecutionContext cx, ScriptObject object,
Object propertyKey, Object value) {
if (propertyKey instanceof String) {
return CreateMethodProperty(cx, object, (String) propertyKey, value);
} else {
return CreateMethodProperty(cx, object, (Symbol) propertyKey, value);
}
}
/**
* 7.3.5 CreateMethodProperty (O, P, V)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
* @return {@code true} on success
*/
public static boolean CreateMethodProperty(ExecutionContext cx, ScriptObject object,
long propertyKey, Object value) {
/* steps 1-2 (not applicable) */
/* step 3 */
PropertyDescriptor newDesc = new PropertyDescriptor(value, true, false, true);
/* step 4 */
return object.defineOwnProperty(cx, propertyKey, newDesc);
}
/**
* 7.3.5 CreateMethodProperty (O, P, V)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
* @return {@code true} on success
*/
public static boolean CreateMethodProperty(ExecutionContext cx, ScriptObject object,
String propertyKey, Object value) {
/* steps 1-2 (not applicable) */
/* step 3 */
PropertyDescriptor newDesc = new PropertyDescriptor(value, true, false, true);
/* step 4 */
return object.defineOwnProperty(cx, propertyKey, newDesc);
}
/**
* 7.3.5 CreateMethodProperty (O, P, V)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
* @return {@code true} on success
*/
public static boolean CreateMethodProperty(ExecutionContext cx, ScriptObject object,
Symbol propertyKey, Object value) {
/* steps 1-2 (not applicable) */
/* step 3 */
PropertyDescriptor newDesc = new PropertyDescriptor(value, true, false, true);
/* step 4 */
return object.defineOwnProperty(cx, propertyKey, newDesc);
}
/**
* 7.3.6 CreateDataPropertyOrThrow (O, P, V)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
*/
public static void CreateDataPropertyOrThrow(ExecutionContext cx, ScriptObject object,
Object propertyKey, Object value) {
if (propertyKey instanceof String) {
CreateDataPropertyOrThrow(cx, object, (String) propertyKey, value);
} else {
CreateDataPropertyOrThrow(cx, object, (Symbol) propertyKey, value);
}
}
/**
* 7.3.6 CreateDataPropertyOrThrow (O, P, V)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
*/
public static void CreateDataPropertyOrThrow(ExecutionContext cx, ScriptObject object,
long propertyKey, Object value) {
/* steps 1-2 (not applicable) */
/* steps 3-4 */
boolean success = CreateDataProperty(cx, object, propertyKey, value);
/* step 5 */
if (!success) {
throw newTypeError(cx, Messages.Key.PropertyNotCreatable, ToString(propertyKey));
}
/* step 6 */
}
/**
* 7.3.6 CreateDataPropertyOrThrow (O, P, V)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
*/
public static void CreateDataPropertyOrThrow(ExecutionContext cx, ScriptObject object,
String propertyKey, Object value) {
/* steps 1-2 (not applicable) */
/* steps 3-4 */
boolean success = CreateDataProperty(cx, object, propertyKey, value);
/* step 5 */
if (!success) {
throw newTypeError(cx, Messages.Key.PropertyNotCreatable, propertyKey);
}
/* step 6 */
}
/**
* 7.3.6 CreateDataPropertyOrThrow (O, P, V)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param value
* the new property value
*/
public static void CreateDataPropertyOrThrow(ExecutionContext cx, ScriptObject object,
Symbol propertyKey, Object value) {
/* steps 1-2 (not applicable) */
/* steps 3-4 */
boolean success = CreateDataProperty(cx, object, propertyKey, value);
/* step 5 */
if (!success) {
throw newTypeError(cx, Messages.Key.PropertyNotCreatable, propertyKey.toString());
}
/* step 6 */
}
/**
* 7.3.7 DefinePropertyOrThrow (O, P, desc)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param desc
* the property descriptor
*/
public static void DefinePropertyOrThrow(ExecutionContext cx, ScriptObject object,
Object propertyKey, PropertyDescriptor desc) {
if (propertyKey instanceof String) {
DefinePropertyOrThrow(cx, object, (String) propertyKey, desc);
} else {
DefinePropertyOrThrow(cx, object, (Symbol) propertyKey, desc);
}
}
/**
* 7.3.7 DefinePropertyOrThrow (O, P, desc)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param desc
* the property descriptor
*/
public static void DefinePropertyOrThrow(ExecutionContext cx, ScriptObject object,
long propertyKey, PropertyDescriptor desc) {
/* steps 1-4 */
boolean success = object.defineOwnProperty(cx, propertyKey, desc);
/* step 5 */
if (!success) {
throw newTypeError(cx, Messages.Key.PropertyNotCreatable, ToString(propertyKey));
}
/* step 6 (not applicable) */
}
/**
* 7.3.7 DefinePropertyOrThrow (O, P, desc)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param desc
* the property descriptor
*/
public static void DefinePropertyOrThrow(ExecutionContext cx, ScriptObject object,
String propertyKey, PropertyDescriptor desc) {
/* steps 1-4 */
boolean success = object.defineOwnProperty(cx, propertyKey, desc);
/* step 5 */
if (!success) {
throw newTypeError(cx, Messages.Key.PropertyNotCreatable, propertyKey);
}
/* step 6 (not applicable) */
}
/**
* 7.3.7 DefinePropertyOrThrow (O, P, desc)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param desc
* the property descriptor
*/
public static void DefinePropertyOrThrow(ExecutionContext cx, ScriptObject object,
Symbol propertyKey, PropertyDescriptor desc) {
/* steps 1-4 */
boolean success = object.defineOwnProperty(cx, propertyKey, desc);
/* step 5 */
if (!success) {
throw newTypeError(cx, Messages.Key.PropertyNotCreatable, propertyKey.toString());
}
/* step 6 (not applicable) */
}
/**
* 7.3.8 DeletePropertyOrThrow (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
*/
public static void DeletePropertyOrThrow(ExecutionContext cx, ScriptObject object,
Object propertyKey) {
if (propertyKey instanceof String) {
DeletePropertyOrThrow(cx, object, (String) propertyKey);
} else {
DeletePropertyOrThrow(cx, object, (Symbol) propertyKey);
}
}
/**
* 7.3.8 DeletePropertyOrThrow (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
*/
public static void DeletePropertyOrThrow(ExecutionContext cx, ScriptObject object,
long propertyKey) {
/* steps 1-4 */
boolean success = object.delete(cx, propertyKey);
/* step 5 */
if (!success) {
throw newTypeError(cx, Messages.Key.PropertyNotDeletable, ToString(propertyKey));
}
/* step 6 (not applicable) */
}
/**
* 7.3.8 DeletePropertyOrThrow (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
*/
public static void DeletePropertyOrThrow(ExecutionContext cx, ScriptObject object,
String propertyKey) {
/* steps 1-4 */
boolean success = object.delete(cx, propertyKey);
/* step 5 */
if (!success) {
throw newTypeError(cx, Messages.Key.PropertyNotDeletable, propertyKey);
}
/* step 6 (not applicable) */
}
/**
* 7.3.8 DeletePropertyOrThrow (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
*/
public static void DeletePropertyOrThrow(ExecutionContext cx, ScriptObject object,
Symbol propertyKey) {
/* steps 1-4 */
boolean success = object.delete(cx, propertyKey);
/* step 5 */
if (!success) {
throw newTypeError(cx, Messages.Key.PropertyNotDeletable, propertyKey.toString());
}
/* step 6 (not applicable) */
}
/**
* 7.3.9 GetMethod (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return the method or {@code null} if not present
*/
public static Callable GetMethod(ExecutionContext cx, Object object, Object propertyKey) {
if (propertyKey instanceof String) {
return GetMethod(cx, object, (String) propertyKey);
} else {
return GetMethod(cx, object, (Symbol) propertyKey);
}
}
/**
* 7.3.9 GetMethod (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return the method or {@code null} if not present
*/
public static Callable GetMethod(ExecutionContext cx, Object object, String propertyKey) {
/* steps 1-3 */
Object func = GetV(cx, object, propertyKey);
/* step 4 */
if (Type.isUndefinedOrNull(func)) {
return null;
}
/* step 5 */
if (!IsCallable(func)) {
throw newTypeError(cx, Messages.Key.PropertyNotCallable, propertyKey);
}
/* step 6 */
return (Callable) func;
}
/**
* 7.3.9 GetMethod (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return the method or {@code null} if not present
*/
public static Callable GetMethod(ExecutionContext cx, Object object, Symbol propertyKey) {
/* steps 1-3 */
Object func = GetV(cx, object, propertyKey);
/* step 4 */
if (Type.isUndefinedOrNull(func)) {
return null;
}
/* step 5 */
if (!IsCallable(func)) {
throw newTypeError(cx, Messages.Key.PropertyNotCallable, propertyKey.toString());
}
/* step 6 */
return (Callable) func;
}
/**
* 7.3.9 GetMethod (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return the method or {@code null} if not present
*/
public static Callable GetMethod(ExecutionContext cx, ScriptObject object, Object propertyKey) {
if (propertyKey instanceof String) {
return GetMethod(cx, object, (String) propertyKey);
} else {
return GetMethod(cx, object, (Symbol) propertyKey);
}
}
/**
* 7.3.9 GetMethod (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return the method or {@code null} if not present
*/
public static Callable GetMethod(ExecutionContext cx, ScriptObject object, String propertyKey) {
/* steps 1-3 */
Object func = object.get(cx, propertyKey, object);
/* step 4 */
if (Type.isUndefinedOrNull(func)) {
return null;
}
/* step 5 */
if (!IsCallable(func)) {
throw newTypeError(cx, Messages.Key.PropertyNotCallable, propertyKey);
}
/* step 6 */
return (Callable) func;
}
/**
* 7.3.9 GetMethod (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return the method or {@code null} if not present
*/
public static Callable GetMethod(ExecutionContext cx, ScriptObject object, Symbol propertyKey) {
/* steps 1-3 */
Object func = object.get(cx, propertyKey, object);
/* step 4 */
if (Type.isUndefinedOrNull(func)) {
return null;
}
/* step 5 */
if (!IsCallable(func)) {
throw newTypeError(cx, Messages.Key.PropertyNotCallable, propertyKey.toString());
}
/* step 6 */
return (Callable) func;
}
/**
* 7.3.10 HasProperty (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return {@code true} if the property is present
*/
public static boolean HasProperty(ExecutionContext cx, ScriptObject object, Object propertyKey) {
if (propertyKey instanceof String) {
return HasProperty(cx, object, (String) propertyKey);
} else {
return HasProperty(cx, object, (Symbol) propertyKey);
}
}
/**
* 7.3.10 HasProperty (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return {@code true} if the property is present
*/
public static boolean HasProperty(ExecutionContext cx, ScriptObject object, long propertyKey) {
/* steps 1-3 */
return object.hasProperty(cx, propertyKey);
}
/**
* 7.3.10 HasProperty (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return {@code true} if the property is present
*/
public static boolean HasProperty(ExecutionContext cx, ScriptObject object, String propertyKey) {
/* steps 1-3 */
return object.hasProperty(cx, propertyKey);
}
/**
* 7.3.10 HasProperty (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return {@code true} if the property is present
*/
public static boolean HasProperty(ExecutionContext cx, ScriptObject object, Symbol propertyKey) {
/* steps 1-3 */
return object.hasProperty(cx, propertyKey);
}
/**
* 7.3.11 HasOwnProperty (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return {@code true} if the property is present
*/
public static boolean HasOwnProperty(ExecutionContext cx, ScriptObject object,
Object propertyKey) {
if (propertyKey instanceof String) {
return HasOwnProperty(cx, object, (String) propertyKey);
} else {
return HasOwnProperty(cx, object, (Symbol) propertyKey);
}
}
/**
* 7.3.11 HasOwnProperty (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return {@code true} if the property is present
*/
public static boolean HasOwnProperty(ExecutionContext cx, ScriptObject object, long propertyKey) {
/* steps 1-2 (not applicable) */
/* steps 3-4 */
Property desc = object.getOwnProperty(cx, propertyKey);
/* steps 5-6 */
return desc != null;
}
/**
* 7.3.11 HasOwnProperty (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return {@code true} if the property is present
*/
public static boolean HasOwnProperty(ExecutionContext cx, ScriptObject object,
String propertyKey) {
/* steps 1-2 (not applicable) */
/* steps 3-4 */
Property desc = object.getOwnProperty(cx, propertyKey);
/* steps 5-6 */
return desc != null;
}
/**
* 7.3.11 HasOwnProperty (O, P)
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @return {@code true} if the property is present
*/
public static boolean HasOwnProperty(ExecutionContext cx, ScriptObject object,
Symbol propertyKey) {
/* steps 1-2 (not applicable) */
/* steps 3-4 */
Property desc = object.getOwnProperty(cx, propertyKey);
/* steps 5-6 */
return desc != null;
}
/**
* 7.3.12 Call(F, V, [argumentsList])
*
* @param cx
* the execution context
* @param function
* the function object
* @param thisValue
* the this value
* @param argumentsList
* the function arguments
* @return the function call return value
*/
public static Object Call(ExecutionContext cx, Object function, Object thisValue,
Object... argumentsList) {
/* steps 1-2 (not applicable) */
/* step 3 */
if (!IsCallable(function)) {
throw newTypeError(cx, Messages.Key.NotCallable);
}
/* step 4 */
return ((Callable) function).call(cx, thisValue, argumentsList);
}
/**
* 7.3.12 Call(F, V, [argumentsList])
*
* @param cx
* the execution context
* @param function
* the function object
* @param thisValue
* the this value
* @param argumentsList
* the function arguments
* @return the function call return value
*/
public static Object Call(ExecutionContext cx, Callable function, Object thisValue,
Object... argumentsList) {
/* steps 1-3 (not applicable) */
/* step 4 */
return ((Callable) function).call(cx, thisValue, argumentsList);
}
/**
* 7.3.13 Construct (F, [argumentsList], [newTarget])
*
* @param cx
* the execution context
* @param f
* the constructor function
* @param argumentsList
* the constructor function arguments
* @return the new object
*/
public static ScriptObject Construct(ExecutionContext cx, Constructor f,
Object... argumentsList) {
/* steps 1-4 (not applicable) */
/* step 5 */
return f.construct(cx, f, argumentsList);
}
/**
* 7.3.13 Construct (F, [argumentsList], [newTarget])
*
* @param cx
* the execution context
* @param f
* the constructor function
* @param newTarget
* the newTarget constructor object
* @param argumentsList
* the constructor function arguments
* @return the new object
*/
public static ScriptObject Construct(ExecutionContext cx, Constructor f, Constructor newTarget,
Object... argumentsList) {
/* steps 1-4 (not applicable) */
/* step 5 */
return f.construct(cx, newTarget, argumentsList);
}
/**
* 7.3.14 SetIntegrityLevel (O, level)
*
* @param cx
* the execution context
* @param object
* the script object
* @param level
* the new integrity level
* @return {@code true} on success
*/
public static boolean SetIntegrityLevel(ExecutionContext cx, ScriptObject object,
IntegrityLevel level) {
/* steps 1-2 */
assert level == IntegrityLevel.Sealed || level == IntegrityLevel.Frozen;
/* steps 3-5 */
if (!object.preventExtensions(cx)) {
return false;
}
/* steps 6-7 */
List<?> keys = object.ownPropertyKeys(cx);
if (level == IntegrityLevel.Sealed) {
/* step 8 */
PropertyDescriptor nonConfigurable = new PropertyDescriptor();
nonConfigurable.setConfigurable(false);
for (Object key : keys) {
DefinePropertyOrThrow(cx, object, key, nonConfigurable);
}
} else {
/* step 9 */
PropertyDescriptor nonConfigurable = new PropertyDescriptor();
nonConfigurable.setConfigurable(false);
PropertyDescriptor nonConfigurableWritable = new PropertyDescriptor();
nonConfigurableWritable.setConfigurable(false);
nonConfigurableWritable.setWritable(false);
for (Object key : keys) {
Property currentDesc = object.getOwnProperty(cx, key);
if (currentDesc != null) {
PropertyDescriptor desc;
if (currentDesc.isAccessorDescriptor()) {
desc = nonConfigurable;
} else {
desc = nonConfigurableWritable;
}
DefinePropertyOrThrow(cx, object, key, desc);
}
}
}
/* step 10 */
return true;
}
/**
* 7.3.15 TestIntegrityLevel (O, level)
*
* @param cx
* the execution context
* @param object
* the script object
* @param level
* the integrity level to test
* @return {@code true} if the object conforms to the integrity level
*/
public static boolean TestIntegrityLevel(ExecutionContext cx, ScriptObject object,
IntegrityLevel level) {
/* steps 1-2 */
assert level == IntegrityLevel.Sealed || level == IntegrityLevel.Frozen;
boolean isFrozen = level == IntegrityLevel.Frozen;
/* steps 3-4 */
boolean status = IsExtensible(cx, object);
/* steps 5-6 */
if (status) {
return false;
}
/* steps 7-8 */
List<?> keys = object.ownPropertyKeys(cx);
/* step 9 */
for (Object key : keys) {
/* steps 9.a-b */
Property currentDesc = object.getOwnProperty(cx, key);
/* step 9.c */
if (currentDesc != null) {
if (currentDesc.isConfigurable()) {
return false;
}
if (isFrozen && currentDesc.isDataDescriptor() && currentDesc.isWritable()) {
return false;
}
}
}
/* step 10 */
return true;
}
/**
* 7.3.16 CreateArrayFromList (elements)
*
* @param cx
* the execution context
* @param elements
* the array elements
* @return the array object
*/
public static ArrayObject CreateArrayFromList(ExecutionContext cx, Collection<?> elements) {
/* steps 1-5 */
return DenseArrayCreate(cx, elements);
}
/**
* 7.3.16 CreateArrayFromList (elements)
*
* @param cx
* the execution context
* @param elements
* the array elements
* @return the array object
*/
public static ArrayObject CreateArrayFromList(ExecutionContext cx, List<?> elements) {
/* steps 1-5 */
return DenseArrayCreate(cx, elements);
}
/**
* 7.3.16 CreateArrayFromList (elements)
*
* @param cx
* the execution context
* @param elements
* the array elements
* @return the array object
*/
public static ArrayObject CreateArrayFromList(ExecutionContext cx, Stream<?> elements) {
/* steps 1-5 */
return DenseArrayCreate(cx, elements);
}
/**
* 7.3.16 CreateArrayFromList (elements)
*
* @param cx
* the execution context
* @param elements
* the array elements
* @return the array object
*/
public static ArrayObject CreateArrayFromList(ExecutionContext cx, Object... elements) {
/* steps 1-5 */
return DenseArrayCreate(cx, elements);
}
/**
* 7.3.17 CreateListFromArrayLike (obj [, elementTypes] )
*
* @param cx
* the execution context
* @param obj
* the array-like object
* @return the array elements
*/
public static Object[] CreateListFromArrayLike(ExecutionContext cx, Object obj) {
if (obj instanceof ArrayObject || obj instanceof ArgumentsObject) {
// Fast-path for dense arrays/arguments
OrdinaryObject array = (OrdinaryObject) obj;
long len = array.getLength();
if (array.isDenseArray(len)) {
if (len == 0) {
return ScriptRuntime.EMPTY_ARRAY;
}
// CreateListFromArrayLike() is (currently) only used for argument arrays
if (len > FunctionPrototype.getMaxArguments()) {
throw newRangeError(cx, Messages.Key.FunctionTooManyArguments);
}
return array.toArray(len);
}
}
/* steps 1-2 (not applicable) */
/* step 3 */
if (!Type.isObject(obj)) {
throw newTypeError(cx, Messages.Key.NotObjectType);
}
ScriptObject object = Type.objectValue(obj);
/* steps 4-5 */
long n = ToLength(cx, Get(cx, object, "length"));
// CreateListFromArrayLike() is (currently) only used for argument arrays
if (n > FunctionPrototype.getMaxArguments()) {
throw newRangeError(cx, Messages.Key.FunctionTooManyArguments);
}
int length = (int) n;
/* step 6 */
Object[] list = new Object[length];
/* steps 7-8 */
for (int index = 0; index < length; ++index) {
int indexName = index;
Object next = Get(cx, object, indexName);
list[index] = next;
}
/* step 9 */
return list;
}
/**
* 7.3.17 CreateListFromArrayLike (obj [, elementTypes] )
*
* @param cx
* the execution context
* @param obj
* the array-like object
* @param elementTypes
* the set of allowed element types
* @return the array elements
*/
public static List<Object> CreateListFromArrayLike(ExecutionContext cx, Object obj,
EnumSet<Type> elementTypes) {
assert elementTypes.size() == 2 && elementTypes.contains(Type.String)
&& elementTypes.contains(Type.Symbol) : elementTypes;
if (obj instanceof ArrayObject) {
// Fast-path for dense arrays
ArrayObject array = (ArrayObject) obj;
if (array.isDenseArray()) {
long len = array.getLength();
if (len == 0) {
return Collections.emptyList();
}
Object[] list = array.toArray();
for (int index = 0, length = list.length; index < length; ++index) {
Object next = list[index];
if (next instanceof String || next instanceof Symbol) {
// list[index] = next;
} else if (next instanceof ConsString) {
// enforce flat string
list[index] = ((ConsString) next).toString();
} else {
throw newTypeError(cx, Messages.Key.ProxyPropertyKey, Type.of(next)
.toString());
}
}
return Arrays.asList(list);
}
}
/* steps 1-2 (not applicable) */
/* step 3 */
if (!Type.isObject(obj)) {
throw newTypeError(cx, Messages.Key.ProxyNotObject);
}
ScriptObject object = Type.objectValue(obj);
/* steps 4-5 */
long n = ToLength(cx, Get(cx, object, "length"));
if (n > Integer.MAX_VALUE) {
throw newInternalError(cx, Messages.Key.OutOfMemory);
}
int length = (int) n;
/* step 6 */
Object[] list = new Object[length];
/* steps 7-8 */
for (int index = 0; index < length; ++index) {
int indexName = index;
Object next = Get(cx, object, indexName);
if (next instanceof String || next instanceof Symbol) {
list[index] = next;
} else if (next instanceof ConsString) {
// enforce flat string
list[index] = ((ConsString) next).toString();
} else {
throw newTypeError(cx, Messages.Key.ProxyPropertyKey, Type.of(next).toString());
}
}
/* step 9 */
return Arrays.asList(list);
}
/**
* 7.3.18 Invoke(O, P, [argumentsList])
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param argumentsList
* the method call arguments
* @return the method return value
*/
public static Object Invoke(ExecutionContext cx, Object object, Object propertyKey,
Object... argumentsList) {
if (propertyKey instanceof String) {
return Invoke(cx, object, (String) propertyKey, argumentsList);
} else {
return Invoke(cx, object, (Symbol) propertyKey, argumentsList);
}
}
/**
* 7.3.18 Invoke(O, P, [argumentsList])
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param argumentsList
* the method call arguments
* @return the method return value
*/
public static Object Invoke(ExecutionContext cx, Object object, String propertyKey,
Object... argumentsList) {
/* steps 1-2 (not applicable) */
/* step 3 */
Object func = GetV(cx, object, propertyKey);
/* Call - steps 1-3 */
if (!IsCallable(func)) {
throw newTypeError(cx, Messages.Key.PropertyNotCallable, propertyKey);
}
/* Call - step 4 */
return ((Callable) func).call(cx, object, argumentsList);
}
/**
* 7.3.18 Invoke(O, P, [argumentsList])
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param argumentsList
* the method call arguments
* @return the method return value
*/
public static Object Invoke(ExecutionContext cx, ScriptObject object, String propertyKey,
Object... argumentsList) {
/* steps 1-2 (not applicable) */
/* step 3 */
Object func = object.get(cx, propertyKey, object);
/* Call - steps 1-3 */
if (!IsCallable(func)) {
throw newTypeError(cx, Messages.Key.PropertyNotCallable, propertyKey);
}
/* Call - step 4 */
return ((Callable) func).call(cx, object, argumentsList);
}
/**
* 7.3.18 Invoke(O, P, [argumentsList])
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param argumentsList
* the method call arguments
* @return the method return value
*/
public static Object Invoke(ExecutionContext cx, Object object, Symbol propertyKey,
Object... argumentsList) {
/* steps 1-2 (not applicable) */
/* step 3 */
Object func = GetV(cx, object, propertyKey);
/* Call - steps 1-3 */
if (!IsCallable(func)) {
throw newTypeError(cx, Messages.Key.PropertyNotCallable, propertyKey.toString());
}
/* Call - step 4 */
return ((Callable) func).call(cx, object, argumentsList);
}
/**
* 7.3.18 Invoke(O, P, [argumentsList])
*
* @param cx
* the execution context
* @param object
* the script object
* @param propertyKey
* the property key
* @param argumentsList
* the method call arguments
* @return the method return value
*/
public static Object Invoke(ExecutionContext cx, ScriptObject object, Symbol propertyKey,
Object... argumentsList) {
/* steps 1-2 (not applicable) */
/* step 3 */
Object func = object.get(cx, propertyKey, object);
/* Call - steps 1-3 */
if (!IsCallable(func)) {
throw newTypeError(cx, Messages.Key.PropertyNotCallable, propertyKey.toString());
}
/* Call - step 4 */
return ((Callable) func).call(cx, object, argumentsList);
}
/**
* 7.3.19 OrdinaryHasInstance (C, O)
*
* @param cx
* the execution context
* @param c
* the constructor object
* @param o
* the instance object
* @return {@code true} on success
*/
public static boolean OrdinaryHasInstance(ExecutionContext cx, Object c, Object o) {
/* step 1 */
if (!IsCallable(c)) {
return false;
}
/* step 2 */
if (c instanceof BoundFunctionObject) {
Callable bc = ((BoundFunctionObject) c).getBoundTargetFunction();
return InstanceofOperator(o, bc, cx);
}
/* step 3 */
if (!Type.isObject(o)) {
return false;
}
/* steps 4-5 */
Object p = Get(cx, (ScriptObject) c, "prototype");
/* step 6 */
if (!Type.isObject(p)) {
throw newTypeError(cx, Messages.Key.PropertyNotObject, "prototype");
}
/* step 7 */
for (ScriptObject obj = Type.objectValue(o), proto = Type.objectValue(p);;) {
obj = obj.getPrototypeOf(cx);
if (obj == null) {
return false;
}
if (proto == obj) {
return true;
}
}
}
/**
* 7.3.20 SpeciesConstructor ( O, defaultConstructor )
*
* @param cx
* the execution context
* @param object
* the script object
* @param defaultConstructor
* the default constructor
* @return the constructor object
*/
public static Constructor SpeciesConstructor(ExecutionContext cx, ScriptObject object,
Intrinsics defaultConstructor) {
/* step 1 (not applicable) */
/* steps 2-3 */
Object constructor = Get(cx, object, "constructor");
/* step 4 */
if (Type.isUndefined(constructor)) {
return (Constructor) cx.getIntrinsic(defaultConstructor);
}
/* step 5 */
if (!Type.isObject(constructor)) {
throw newTypeError(cx, Messages.Key.PropertyNotObject, "constructor");
}
/* steps 6-7 */
Object species = Get(cx, Type.objectValue(constructor), BuiltinSymbol.species.get());
/* step 8 */
if (Type.isUndefinedOrNull(species)) {
return (Constructor) cx.getIntrinsic(defaultConstructor);
}
/* step 9 */
if (IsConstructor(species)) {
return (Constructor) species;
}
/* step 10 */
throw newTypeError(cx, Messages.Key.PropertyNotConstructor, "[Symbol.species]");
}
/**
* 7.3.21 EnumerableOwnNames (O)
*
* @param cx
* the execution context
* @param object
* the script object
* @return <var>object</var>'s own enumerable string-valued property keys
*/
public static List<String> EnumerableOwnNames(ExecutionContext cx, ScriptObject object) {
/* step 1 (not applicable) */
/* steps 2-3 */
List<?> ownKeys = object.ownPropertyKeys(cx);
/* step 4 */
int initialSize = Math.min(16, ownKeys.size());
ArrayList<String> names = new ArrayList<>(initialSize);
/* step 5 */
for (Object key : ownKeys) {
if (key instanceof String) {
String skey = (String) key;
Property desc = object.getOwnProperty(cx, skey);
if (desc != null && desc.isEnumerable()) {
names.add(skey);
}
}
}
/* step 6 (sort keys - not applicable) */
/* step 7 */
return names;
}
/**
* 7.3.22 GetFunctionRealm ( obj )
*
* @param cx
* the execution context
* @param function
* the callable object
* @return the function's realm
*/
public static Realm GetFunctionRealm(ExecutionContext cx, Callable function) {
/* steps 1-5 */
return function.getRealm(cx);
}
/**
* 7.4.1 GetIterator ( obj )
*
* @param cx
* the execution context
* @param obj
* the script object
* @return the script iterator object
*/
public static ScriptObject GetIterator(ExecutionContext cx, Object obj) {
/* step 1 (not applicable) */
/* step 2 */
Callable method = GetMethod(cx, obj, BuiltinSymbol.iterator.get());
/* steps 3-6 */
return GetIterator(cx, obj, method);
}
/**
* 7.4.1 GetIterator ( obj )
*
* @param cx
* the execution context
* @param obj
* the script object
* @param method
* the iterator method
* @return the script iterator object
*/
public static ScriptObject GetIterator(ExecutionContext cx, Object obj, Callable method) {
/* steps 1-2 (not applicable) */
/* steps 3-4 (inlined Call operation) */
if (method == null) {
throw newTypeError(cx, Messages.Key.PropertyNotCallable,
BuiltinSymbol.iterator.toString());
}
Object iterator = method.call(cx, obj);
/* step 5 */
if (!Type.isObject(iterator)) {
throw newTypeError(cx, Messages.Key.NotObjectTypeReturned, "[Symbol.iterator]");
}
/* step 6 */
return Type.objectValue(iterator);
}
/**
* 7.4.2 IteratorNext ( iterator, value )
*
* @param cx
* the execution context
* @param iterator
* the script iterator object
* @return the next value from the iterator
*/
public static ScriptObject IteratorNext(ExecutionContext cx, ScriptObject iterator) {
/* steps 1-3 */
Object result = Invoke(cx, iterator, "next");
/* step 4 */
if (!Type.isObject(result)) {
throw newTypeError(cx, Messages.Key.NotObjectTypeReturned, "next");
}
/* step 5 */
return Type.objectValue(result);
}
/**
* 7.4.2 IteratorNext ( iterator, value )
*
* @param cx
* the execution context
* @param iterator
* the script iterator object
* @param value
* the value to pass to the next() function
* @return the next value from the iterator
*/
public static ScriptObject IteratorNext(ExecutionContext cx, ScriptObject iterator, Object value) {
/* steps 1-3 */
Object result = Invoke(cx, iterator, "next", value);
/* step 4 */
if (!Type.isObject(result)) {
throw newTypeError(cx, Messages.Key.NotObjectTypeReturned, "next");
}
/* step 5 */
return Type.objectValue(result);
}
/**
* 7.4.3 IteratorComplete (iterResult)
*
* @param cx
* the execution context
* @param iterResult
* the iterator result object
* @return {@code true} if the iterator is completed
*/
public static boolean IteratorComplete(ExecutionContext cx, ScriptObject iterResult) {
/* step 1 (not applicable) */
/* step 2 */
return ToBoolean(Get(cx, iterResult, "done"));
}
/**
* 7.4.4 IteratorValue (iterResult)
*
* @param cx
* the execution context
* @param iterResult
* the iterator result object
* @return the iterator result value
*/
public static Object IteratorValue(ExecutionContext cx, ScriptObject iterResult) {
/* step 1 (not applicable) */
/* step 2 */
return Get(cx, iterResult, "value");
}
/**
* 7.4.5 IteratorStep ( iterator )
*
* @param cx
* the execution context
* @param iterator
* the script iterator object
* @return the next value from the iterator or {@code null}
*/
public static ScriptObject IteratorStep(ExecutionContext cx, ScriptObject iterator) {
/* steps 1-2 */
ScriptObject result = IteratorNext(cx, iterator);
/* steps 3-4 */
boolean done = IteratorComplete(cx, result);
/* step 5 */
if (done) {
return null;
}
/* step 6 */
return result;
}
/**
* 7.4.6 IteratorClose( iterator, completion )
*
* @param cx
* the execution context
* @param iterator
* the script iterator object
*/
public static void IteratorClose(ExecutionContext cx, ScriptObject iterator) {
/* steps 1-2 (not applicable) */
/* steps 3-4 */
Callable returnMethod = GetMethod(cx, iterator, "return");
/* step 5 */
if (returnMethod != null) {
/* steps 6, 8 */
Object innerResult = returnMethod.call(cx, iterator);
/* step 7 (not applicable) */
/* step 9 */
if (!Type.isObject(innerResult)) {
throw newTypeError(cx, Messages.Key.NotObjectTypeReturned, "return");
}
}
/* step 10 (not applicable) */
}
/**
* 7.4.6 IteratorClose( iterator, completion )
*
* @param cx
* the execution context
* @param iterator
* the script iterator object
* @param cause
* the exception cause
*/
public static void IteratorClose(ExecutionContext cx, ScriptObject iterator, Throwable cause) {
/* steps 1-2 (not applicable) */
/* steps 3-4 */
Callable returnMethod = GetMethod(cx, iterator, "return");
/* step 5 */
if (returnMethod != null) {
/* steps 6-7 */
try {
returnMethod.call(cx, iterator);
} catch (ScriptException e) {
// Ignore exceptions from "return" method.
if (cause != e) {
cause.addSuppressed(e);
}
}
}
/* steps 8-10 (not applicable) */
}
/**
* 7.4.7 CreateIterResultObject (value, done)
*
* @param cx
* the execution context
* @param value
* the iterator result value
* @param done
* the iterator result state
* @return the new iterator result object
*/
public static OrdinaryObject CreateIterResultObject(ExecutionContext cx, Object value,
boolean done) {
/* step 1 (not applicable) */
/* step 2 */
OrdinaryObject obj = ObjectCreate(cx, Intrinsics.ObjectPrototype);
/* step 3 */
CreateDataProperty(cx, obj, "value", value);
/* step 4 */
CreateDataProperty(cx, obj, "done", done);
/* step 5 */
return obj;
}
/**
* 7.4.8 CreateListIterator (list)
*
* @param <T>
* the element type
* @param cx
* the execution context
* @param list
* the source iterable
* @return a new script object iterator
*/
public static <T> ScriptObject CreateListIterator(ExecutionContext cx, Iterable<T> list) {
return ListIterator.CreateListIterator(cx, list.iterator());
}
/**
* 7.4.8 CreateListIterator (list)
*
* @param <T>
* the element type
* @param cx
* the execution context
* @param iterator
* the source iterator
* @return a new script object iterator
*/
public static <T> ScriptObject CreateListIterator(ExecutionContext cx, Iterator<T> iterator) {
return ListIterator.CreateListIterator(cx, iterator);
}
/**
* Returns a {@link ScriptIterator} for {@code iterable}.
*
* @param cx
* the execution context
* @param iterable
* the iterable object
* @return the iterator object
*/
public static ScriptIterator<?> GetScriptIterator(ExecutionContext cx, Object iterable) {
return ScriptIterators.GetScriptIterator(cx, iterable);
}
/**
* Returns a {@link ScriptIterator} for {@code iterable}.
*
* @param cx
* the execution context
* @param iterable
* the iterable object
* @param method
* the iterator method
* @return the iterator object
*/
public static ScriptIterator<?> GetScriptIterator(ExecutionContext cx, Object iterable, Callable method) {
return ScriptIterators.GetScriptIterator(cx, iterable, method);
}
/**
* Returns a {@link ScriptIterator} for {@code iterator}. {@code iterator} is expected to comply
* to the <code>"25.1.2 The Iterator Interface"</code>.
*
* @param cx
* the execution context
* @param iterator
* the script iterator object
* @return the iterator object
*/
public static ScriptIterator<?> ToScriptIterator(ExecutionContext cx, ScriptObject iterator) {
return ScriptIterators.ToScriptIterator(cx, iterator);
}
/**
* CopyDataProperties (target, source, excluded)
*
* @param <TARGET>
* the target type
* @param cx
* the execution context
* @param target
* the target script object
* @param source
* the source object
* @param excluded
* the excluded property names
* @return the <var>target</var> script object
*/
public static <TARGET extends ScriptObject> TARGET CopyDataProperties(ExecutionContext cx, TARGET target,
Object source, Set<?> excluded) {
/* step 1 (not applicable) */
/* steps 2, 5 */
if (Type.isUndefinedOrNull(source)) {
return target;
}
/* step 3.a */
ScriptObject from = ToObject(cx, source);
/* steps 3.b-c */
List<?> keys = from.ownPropertyKeys(cx);
/* step 4 */
for (Object nextKey : keys) {
if (!excluded.contains(nextKey)) {
/* steps 4.i.a-b */
Property desc = from.getOwnProperty(cx, nextKey);
/* step 4.i.c */
if (desc != null && desc.isEnumerable()) {
Object propValue = Get(cx, from, nextKey);
CreateDataProperty(cx, target, nextKey, propValue);
}
}
}
/* step 5 */
return target;
}
/**
* 7.1.3.1 ToNumber Applied to the String Type
*/
private static final class ToNumberParser {
private ToNumberParser() {
}
static double parse(String input) {
String s = Strings.trim(input);
int len = s.length();
if (len == 0) {
return 0;
}
if (s.charAt(0) == '0' && len > 2) {
char c = s.charAt(1);
if (c == 'x' || c == 'X') {
return readHexIntegerLiteral(s);
}
if (c == 'b' || c == 'B') {
return readBinaryIntegerLiteral(s);
}
if (c == 'o' || c == 'O') {
return readOctalIntegerLiteral(s);
}
}
return readDecimalLiteral(s);
}
private static double readHexIntegerLiteral(String s) {
assert s.length() > 2;
final int start = 2; // "0x" prefix
for (int index = start, end = s.length(); index < end; ++index) {
char c = s.charAt(index);
if (!(('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'))) {
return Double.NaN;
}
}
return NumberParser.parseHex(s);
}
private static double readBinaryIntegerLiteral(String s) {
assert s.length() > 2;
final int start = 2; // "0b" prefix
for (int index = start, end = s.length(); index < end; ++index) {
char c = s.charAt(index);
if (!(c == '0' || c == '1')) {
return Double.NaN;
}
}
return NumberParser.parseBinary(s);
}
private static double readOctalIntegerLiteral(String s) {
assert s.length() > 2;
final int start = 2; // "0o" prefix
for (int index = start, end = s.length(); index < end; ++index) {
char c = s.charAt(index);
if (!('0' <= c && c <= '7')) {
return Double.NaN;
}
}
return NumberParser.parseOctal(s);
}
static double readDecimalLiteral(String s) {
assert !s.isEmpty();
outOfBounds: invalidChar: {
final int end = s.length();
int index = 0;
int c = s.charAt(index++);
boolean isPos = true;
if (c == '+' || c == '-') {
if (index >= end)
break outOfBounds;
isPos = (c == '+');
c = s.charAt(index++);
}
if (c == 'I') {
// Infinity
final int Infinity_length = "Infinity".length();
if (index - 1 + Infinity_length == end
&& s.regionMatches(index - 1, "Infinity", 0, Infinity_length)) {
return isPos ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
}
break invalidChar;
}
boolean hasDot = (c == '.'), hasExp = false;
if (hasDot) {
if (index >= end)
break outOfBounds;
c = s.charAt(index);
}
if (!('0' <= c && c <= '9')) {
break invalidChar;
}
if (!hasDot) {
while (index < end) {
c = s.charAt(index++);
if ('0' <= c && c <= '9') {
continue;
}
if (c == '.') {
hasDot = true;
break;
}
if (c == 'e' || c == 'E') {
hasExp = true;
break;
}
break invalidChar;
}
}
if (hasDot) {
while (index < end) {
c = s.charAt(index++);
if ('0' <= c && c <= '9') {
continue;
}
if (c == 'e' || c == 'E') {
hasExp = true;
break;
}
break invalidChar;
}
}
if (hasExp) {
if (index >= end)
break outOfBounds;
c = s.charAt(index++);
if (c == '+' || c == '-') {
if (index >= end)
break outOfBounds;
c = s.charAt(index++);
}
if (!('0' <= c && c <= '9')) {
break invalidChar;
}
while (index < end) {
c = s.charAt(index++);
if ('0' <= c && c <= '9') {
continue;
}
break invalidChar;
}
}
if (index != end) {
break invalidChar;
}
if (!(hasDot || hasExp)) {
return NumberParser.parseInteger(s);
}
return NumberParser.parseDecimal(s);
}
return Double.NaN;
}
}
}