package com.oracle.truffle.js.nodes.cast;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.object.HiddenKey;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.js.nodes.JSGuards;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.Symbol;
public abstract class ToArrayIndexNode extends JavaScriptBaseNode {
protected final boolean convertToPropertyKey;
public abstract Object execute(Object operand);
public abstract long executeLong(Object operand) throws UnexpectedResultException;
@SuppressWarnings("static-method")
public final boolean isResultArrayIndex(Object result) {
return result instanceof Long;
}
protected ToArrayIndexNode(boolean convertToPropertyKey) {
this.convertToPropertyKey = convertToPropertyKey;
}
public static ToArrayIndexNode create() {
return ToArrayIndexNodeGen.create(true);
}
public static ToArrayIndexNode createNoToPropertyKey() {
return ToArrayIndexNodeGen.create(false);
}
@Specialization(guards = "isIntArrayIndex(value)")
protected static long doInteger(int value) {
return value;
}
@Specialization(guards = "isLongArrayIndex(value)")
protected static long doLong(long value) {
return JSRuntime.castArrayIndex(value);
}
protected static boolean doubleIsIntIndex(double d) {
return JSRuntime.doubleIsRepresentableAsInt(d) && d >= 0;
}
@Specialization(guards = "doubleIsIntIndex(value)")
protected static long doDoubleAsIntIndex(double value) {
return (long) value;
}
protected static boolean doubleIsUintIndex(double d) {
return JSRuntime.doubleIsRepresentableAsUnsignedInt(d, true) && d >= 0 && d < 0xffff_ffffL;
}
@Specialization(guards = "doubleIsUintIndex(value)")
protected static long doDoubleAsUintIndex(double value) {
return JSRuntime.castArrayIndex(value);
}
@Specialization
protected static Symbol doSymbol(Symbol value) {
return value;
}
@Specialization
protected static HiddenKey doHiddenKey(HiddenKey value) {
assert false : value;
return value;
}
@Specialization(guards = "isArrayIndexLengthInRange(index)")
protected static Object convertFromString(String index,
@Cached("create()") BranchProfile startsWithDigitBranch,
@Cached("create()") BranchProfile isArrayIndexBranch,
@Cached("create()") BranchProfile needPassStringBranch) {
if (JSRuntime.isAsciiDigit(index.charAt(0))) {
startsWithDigitBranch.enter();
long longValue = JSRuntime.parseArrayIndexRaw(index);
if (JSRuntime.isArrayIndex(longValue)) {
isArrayIndexBranch.enter();
return JSRuntime.castArrayIndex(longValue);
}
}
needPassStringBranch.enter();
return index;
}
@Specialization(guards = "!isArrayIndexLengthInRange(index)")
protected static Object convertFromStringNotInRange(String index) {
return index;
}
protected static boolean notArrayIndex(Object o) {
assert !(o instanceof HiddenKey);
return (!(o instanceof Integer) || !JSGuards.isIntArrayIndex((int) o)) && (!(o instanceof Double) || !doubleIsUintIndex((double) o)) &&
(!(o instanceof Long) || !JSGuards.isLongArrayIndex((long) o)) && !(o instanceof String) && !(o instanceof Symbol);
}
@Specialization(guards = {"!convertToPropertyKey", "notArrayIndex(value)"})
protected static Object doNonArrayIndex(Object value) {
return value;
}
@Specialization(guards = {"convertToPropertyKey", "notArrayIndex(value)"})
protected static Object doNonArrayIndex(Object value,
@Cached("create()") JSToPropertyKeyNode toPropertyKey) {
return toPropertyKey.execute(value);
}
}