package com.oracle.truffle.js.nodes.interop;
import com.oracle.truffle.api.TruffleLanguage.LanguageReference;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.CachedLanguage;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.js.lang.JavaScriptLanguage;
import com.oracle.truffle.js.nodes.access.PropertyGetNode;
import com.oracle.truffle.js.nodes.access.ReadElementNode;
import com.oracle.truffle.js.nodes.function.JSFunctionCallNode;
import com.oracle.truffle.js.nodes.unary.IsCallableNode;
import com.oracle.truffle.js.runtime.JSArguments;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.objects.JSObject;
@GenerateUncached
public abstract class JSInteropInvokeNode extends JSInteropCallNode {
JSInteropInvokeNode() {
}
public static JSInteropInvokeNode create() {
return JSInteropInvokeNodeGen.create();
}
public abstract Object execute(DynamicObject receiver, String name, Object[] arguments) throws UnknownIdentifierException, UnsupportedMessageException;
@Specialization(guards = {"cachedName.equals(name)"}, limit = "1")
Object doCached(DynamicObject receiver, @SuppressWarnings("unused") String name, Object[] arguments,
@Cached("name") String cachedName,
@CachedLanguage @SuppressWarnings("unused") LanguageReference<JavaScriptLanguage> languageRef,
@Cached("createGetProperty(cachedName, languageRef)") PropertyGetNode functionPropertyGetNode,
@Shared("isCallable") @Cached IsCallableNode isCallableNode,
@Shared("call") @Cached(value = "createCall()", uncached = "getUncachedCall()") JSFunctionCallNode callNode,
@Shared("importValue") @Cached ImportValueNode importValueNode) throws UnknownIdentifierException, UnsupportedMessageException {
Object function = functionPropertyGetNode.getValueOrDefault(receiver, null);
if (function == null) {
throw UnknownIdentifierException.create(cachedName);
}
if (isCallableNode.executeBoolean(function)) {
return callNode.executeCall(JSArguments.create(receiver, function, prepare(arguments, importValueNode)));
} else {
throw UnsupportedMessageException.create();
}
}
@Specialization(replaces = "doCached")
Object doUncached(DynamicObject receiver, String name, Object[] arguments,
@CachedLanguage @SuppressWarnings("unused") LanguageReference<JavaScriptLanguage> languageRef,
@Cached(value = "create(languageRef.get().getJSContext())", uncached = "getUncachedRead()") ReadElementNode readNode,
@Shared("isCallable") @Cached IsCallableNode isCallableNode,
@Shared("call") @Cached(value = "createCall()", uncached = "getUncachedCall()") JSFunctionCallNode callNode,
@Shared("importValue") @Cached ImportValueNode importValueNode) throws UnknownIdentifierException, UnsupportedMessageException {
Object function;
if (readNode == null) {
function = JSObject.getOrDefault(receiver, name, receiver, null);
} else {
function = readNode.executeWithTargetAndIndexOrDefault(receiver, name, null);
}
if (function == null) {
throw UnknownIdentifierException.create(name);
}
if (isCallableNode.executeBoolean(function)) {
Object[] preparedArgs = prepare(arguments, importValueNode);
if (callNode == null) {
return JSRuntime.call(function, receiver, preparedArgs);
} else {
return callNode.executeCall(JSArguments.create(receiver, function, preparedArgs));
}
} else {
throw UnsupportedMessageException.create();
}
}
PropertyGetNode createGetProperty(String name, LanguageReference<JavaScriptLanguage> languageRef) {
return PropertyGetNode.create(name, false, languageRef.get().getJSContext());
}
static ReadElementNode getUncachedRead() {
return null;
}
}