Skip to content

Commit

Permalink
play nice with polyglot
Browse files Browse the repository at this point in the history
  • Loading branch information
ekmett committed Jan 14, 2022
1 parent 1cf4ab4 commit 6e7c669
Show file tree
Hide file tree
Showing 5 changed files with 342 additions and 15 deletions.
@@ -0,0 +1,218 @@
package org.enso.interpreter.node.callable;

import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.dsl.*;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.callable.dispatch.IndirectInvokeFunctionNode;
import org.enso.interpreter.node.callable.resolver.AnyResolverNode;
import org.enso.interpreter.node.callable.resolver.DataflowErrorResolverNode;
import org.enso.interpreter.node.callable.resolver.HostMethodCallNode;
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
import org.enso.interpreter.runtime.state.Stateful;

@GenerateUncached
@ReportPolymorphism
@ImportStatic({HostMethodCallNode.PolyglotCallType.class, HostMethodCallNode.class})
public abstract class IndirectInvokeConversionNode extends Node {

/** @return a new indirect method invocation node */
public static IndirectInvokeConversionNode build() {
return IndirectInvokeConversionNodeGen.create();
}

public abstract Stateful execute(
MaterializedFrame frame,
Object state,
UnresolvedConversion conversion,
Object _this,
Object that,
Object[] arguments,
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail);

@Specialization(guards = "dispatch.canConvertFrom(that)")
Stateful doConvertFrom(
MaterializedFrame frame,
Object state,
UnresolvedConversion conversion,
Object _this,
Object that,
Object[] arguments,
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx,
@Cached ConditionProfile atomProfile,
@Cached ConditionProfile atomConstructorProfile,
@Cached IndirectInvokeFunctionNode indirectInvokeFunctionNode) {
try {
Function function =
dispatch.getConversionFunction(
that,
InvokeConversionNode.extractConstructor(
this, _this, ctx, atomConstructorProfile, atomProfile),
conversion);
return indirectInvokeFunctionNode.execute(
function,
frame,
state,
arguments,
schema,
defaultsExecutionMode,
argumentsExecutionMode,
isTail);
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
throw new PanicException(
ctx.get().getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
}
}

@Specialization
Stateful doDataflowError(
MaterializedFrame frame,
Object state,
UnresolvedConversion conversion,
Object _this,
DataflowError that,
Object[] arguments,
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx,
@Cached BranchProfile profile,
@Cached ConditionProfile atomProfile,
@Cached ConditionProfile atomConstructorProfile,
@Cached IndirectInvokeFunctionNode indirectInvokeFunctionNode) {
try {
Function function =
dispatch.getConversionFunction(
that,
InvokeConversionNode.extractConstructor(
this, _this, ctx, atomConstructorProfile, atomProfile),
conversion);
return indirectInvokeFunctionNode.execute(
function,
frame,
state,
arguments,
schema,
defaultsExecutionMode,
argumentsExecutionMode,
isTail);
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
profile.enter();
return new Stateful(state, that);
}
}

@Specialization
Stateful doPanicSentinel(
MaterializedFrame frame,
Object state,
UnresolvedConversion conversion,
Object _this,
PanicSentinel that,
Object[] arguments,
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail) {
throw that;
}

@Specialization(guards = "interop.isString(that)")
Stateful doConvertText(
MaterializedFrame frame,
Object state,
UnresolvedConversion conversion,
Object _this,
Object that,
Object[] arguments,
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
@CachedLibrary(limit = "1") MethodDispatchLibrary textDispatch,
@CachedLibrary(limit = "10") InteropLibrary interop,
@Cached ConditionProfile atomProfile,
@Cached ConditionProfile atomConstructorProfile,
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx,
@Cached IndirectInvokeFunctionNode indirectInvokeFunctionNode) {
try {
String str = interop.asString(that);
Text txt = Text.create(str);
Function function =
textDispatch.getConversionFunction(
txt,
InvokeConversionNode.extractConstructor(
this, _this, ctx, atomConstructorProfile, atomProfile),
conversion);
arguments[0] = txt;
return indirectInvokeFunctionNode.execute(
function,
frame,
state,
arguments,
schema,
defaultsExecutionMode,
argumentsExecutionMode,
isTail);
} catch (UnsupportedMessageException e) {
throw new IllegalStateException("Impossible, that is guaranteed to be a string.");
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
throw new PanicException(
ctx.get().getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
}
}

@Specialization(
guards = {
"!methods.canConvertFrom(that)",
"!interop.isString(that)",
"!methods.hasSpecialConversion(that)"
})
Stateful doFallback(
MaterializedFrame frame,
Object state,
UnresolvedConversion conversion,
Object _this,
Object that,
Object[] arguments,
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
@CachedLibrary(limit = "10") InteropLibrary interop,
@CachedContext(Language.class) Context ctx) {
throw new PanicException(
ctx.getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
}
}
@@ -0,0 +1,102 @@
package org.enso.interpreter.node.callable;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.Constants;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.BaseNode.TailStatus;
import org.enso.interpreter.node.callable.InvokeCallableNode.ArgumentsExecutionMode;
import org.enso.interpreter.node.callable.InvokeCallableNode.DefaultsExecutionMode;
import org.enso.interpreter.node.callable.InvokeConversionNode;
import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;

/** A helper node to handle conversion application for the interop library. */
@GenerateUncached
@NodeInfo(description = "Helper node to handle conversion application through the interop library.")
public abstract class InteropConversionCallNode extends Node {

public static InteropConversionCallNode build() {
return InteropConversionCallNodeGen.create();
}

public abstract Object execute(UnresolvedConversion conversion, Object state, Object[] arguments)
throws ArityException;

@CompilerDirectives.TruffleBoundary
CallArgumentInfo[] buildSchema(int length) {
CallArgumentInfo[] args = new CallArgumentInfo[length];
for (int i = 0; i < length; i++) {
args[i] = new CallArgumentInfo();
}
return args;
}

@CompilerDirectives.TruffleBoundary
InvokeConversionNode buildInvoker(int length) {
CallArgumentInfo[] args = buildSchema(length);
return InvokeConversionNode.build(
args,
DefaultsExecutionMode.EXECUTE,
ArgumentsExecutionMode.PRE_EXECUTED);
}

@Specialization(
guards = {"!context.isInlineCachingDisabled()", "arguments.length == cachedArgsLength"},
limit = Constants.CacheSizes.FUNCTION_INTEROP_LIBRARY)
@ExplodeLoop
Object callCached(
UnresolvedConversion conversion,
Object state,
Object[] arguments,
@CachedContext(Language.class) Context context,
@Cached("arguments.length") int cachedArgsLength,
@Cached("buildInvoker(cachedArgsLength)") InvokeConversionNode invokerNode,
@Cached("build()") HostValueToEnsoNode hostValueToEnsoNode)
throws ArityException {
Object[] args = new Object[cachedArgsLength];
for (int i = 0; i < cachedArgsLength; i++) {
args[i] = hostValueToEnsoNode.execute(arguments[i]);
}
if (cachedArgsLength < 2) throw ArityException.create(2, cachedArgsLength);
return invokerNode.execute(null, state, conversion, args[0], args[1], args).getValue();
}

@Specialization(replaces = "callCached")
Object callUncached(
UnresolvedConversion conversion,
Object state,
Object[] arguments,
@Cached IndirectInvokeConversionNode indirectInvokeConversionNode,
@Cached("build()") HostValueToEnsoNode hostValueToEnsoNode)
throws ArityException {
Object[] args = new Object[arguments.length];
for (int i = 0; i < arguments.length; i++) {
args[i] = hostValueToEnsoNode.execute(arguments[i]);
}
if (arguments.length < 2) throw ArityException.create(2, arguments.length);
return indirectInvokeConversionNode
.execute(
null,
state,
conversion,
args[0],
args[1],
args,
buildSchema(arguments.length),
DefaultsExecutionMode.EXECUTE,
ArgumentsExecutionMode.PRE_EXECUTED,
TailStatus.NOT_TAIL)
.getValue();
}
}
Expand Up @@ -71,19 +71,28 @@ public abstract Stateful execute(
Object that,
Object[] arguments);

AtomConstructor extractConstructor(Object _this, TruffleLanguage.ContextReference<Context> ctx) {
static AtomConstructor extractConstructor(
Node thisNode,
Object _this,
TruffleLanguage.ContextReference<Context> ctx,
ConditionProfile atomConstructorProfile,
ConditionProfile atomProfile) {
if (atomConstructorProfile.profile(_this instanceof AtomConstructor)) {
return (AtomConstructor) _this;
} else if (atomProfile.profile(_this instanceof Atom)) {
return ((Atom) _this).getConstructor();
} else {
throw new PanicException(
ctx.get().getBuiltins().error().makeInvalidConversionTargetError(_this), this);
ctx.get().getBuiltins().error().makeInvalidConversionTargetError(_this), thisNode);
}
}

AtomConstructor extractConstructor(Object _this, TruffleLanguage.ContextReference<Context> ctx) {
return extractConstructor(this, _this, ctx, atomConstructorProfile, atomProfile);
}

@Specialization(guards = "dispatch.canConvertFrom(that)")
Stateful doFunctionalDispatch(
Stateful doConvertFrom(
VirtualFrame frame,
Object state,
UnresolvedConversion conversion,
Expand All @@ -102,7 +111,6 @@ Stateful doFunctionalDispatch(
}
}

// TODO: be smarter here
@Specialization
Stateful doDataflowError(
VirtualFrame frame,
Expand All @@ -126,12 +134,12 @@ Stateful doDataflowError(

@Specialization
Stateful doPanicSentinel(
VirtualFrame frame,
Object state,
UnresolvedConversion conversion,
Object _this,
PanicSentinel that,
Object[] arguments) {
VirtualFrame frame,
Object state,
UnresolvedConversion conversion,
Object _this,
PanicSentinel that,
Object[] arguments) {
throw that;
}

Expand Down Expand Up @@ -177,7 +185,6 @@ Stateful doFallback(
Object[] arguments,
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
@CachedLibrary(limit = "10") InteropLibrary interop,
@Cached AnyResolverNode anyResolverNode,
@CachedContext(Language.class) Context ctx) {
throw new PanicException(
ctx.getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
Expand Down

0 comments on commit 6e7c669

Please sign in to comment.