package com.oracle.truffle.js.nodes.array;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.nodes.access.PropertyNode;
import com.oracle.truffle.js.nodes.array.ArrayLengthNode.ArrayLengthReadNode;
import com.oracle.truffle.js.nodes.cast.JSToLengthNode;
import com.oracle.truffle.js.nodes.cast.JSToUInt32Node;
import com.oracle.truffle.js.nodes.interop.ImportValueNode;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.builtins.JSAbstractArray;
import com.oracle.truffle.js.runtime.builtins.JSArray;
import com.oracle.truffle.js.runtime.interop.JSInteropUtil;
@ImportStatic(JSInteropUtil.class)
abstract class GetLengthHelperNode extends JavaScriptBaseNode {
private final JSContext context;
@Child private JSToUInt32Node toUInt32Node;
@Child private JSToLengthNode toLengthNode;
private final boolean toLength;
GetLengthHelperNode(JSContext context) {
this.context = context;
this.toLength = context.getEcmaScriptVersion() >= 6;
}
public static GetLengthHelperNode create(JSContext context) {
return GetLengthHelperNodeGen.create(context);
}
public abstract Object execute(Object value, boolean isArray);
public final long executeLong(Object value, boolean isArray) {
return toLengthLong(execute(value, isArray));
}
@Specialization(guards = "isArray", rewriteOn = UnexpectedResultException.class)
public int getArrayLengthInt(DynamicObject target, @SuppressWarnings("unused") boolean isArray,
@Cached("create()") ArrayLengthReadNode arrayLengthReadNode) throws UnexpectedResultException {
return arrayLengthReadNode.executeInt(target);
}
@Specialization(guards = "isArray")
public double getArrayLength(DynamicObject target, @SuppressWarnings("unused") boolean isArray,
@Cached("create()") ArrayLengthReadNode arrayLengthReadNode) {
return arrayLengthReadNode.executeDouble(target);
}
@Specialization(guards = "!isArray")
public double getLengthDynamicObject(DynamicObject target, @SuppressWarnings("unused") boolean isArray,
@Cached("createLengthProperty()") PropertyNode getLengthPropertyNode) {
return toLengthDouble(getLengthPropertyNode.executeWithTarget(target));
}
@Specialization(guards = "!isDynamicObject(target)", limit = "3")
public double getLengthForeign(Object target, @SuppressWarnings("unused") boolean isArray,
@CachedLibrary("target") InteropLibrary interop,
@Cached("create()") ImportValueNode importValueNode) {
if (interop.hasArrayElements(target)) {
return JSInteropUtil.getArraySize(target, interop, this);
} else {
return toLengthDouble(JSInteropUtil.readMemberOrDefault(target, JSAbstractArray.LENGTH, 0, interop, importValueNode, this));
}
}
protected PropertyNode createLengthProperty() {
return PropertyNode.createProperty(context, null, JSArray.LENGTH);
}
private double toUInt32Double(Object target) {
return JSRuntime.doubleValue((Number) getUInt32Node().execute(target));
}
private long toUInt32Long(Object target) {
return JSRuntime.longValue((Number) getUInt32Node().execute(target));
}
private double toLengthDouble(Object target) {
if (toLength) {
return getToLengthNode().executeLong(target);
} else {
return toUInt32Double(target);
}
}
private long toLengthLong(Object target) {
if (toLength) {
return getToLengthNode().executeLong(target);
} else {
return toUInt32Long(target);
}
}
private JSToLengthNode getToLengthNode() {
if (toLengthNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toLengthNode = insert(JSToLengthNode.create());
}
return toLengthNode;
}
private JSToUInt32Node getUInt32Node() {
if (toUInt32Node == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toUInt32Node = insert(JSToUInt32Node.create());
}
return toUInt32Node;
}
}