Skip to content

Commit

Permalink
R Interop (#1559)
Browse files Browse the repository at this point in the history
  • Loading branch information
kustosz authored Mar 9, 2021
1 parent 03fa549 commit f298fbd
Show file tree
Hide file tree
Showing 13 changed files with 610 additions and 10 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/scala.yml
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,11 @@ jobs:
go get -v github.com/ahmetb/go-httpbin/cmd/httpbin
$(go env GOPATH)/bin/httpbin -host :8080 &
- name: Install Graalpython
- name: Install Graalpython & FastR
if: runner.os != 'Windows'
run: |
gu install python
gu install r
- name: Test Engine Distribution (Unix)
shell: bash
Expand Down
5 changes: 5 additions & 0 deletions distribution/std-lib/Test/src/Test.enso
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ type Verbs

be subject argument = this.equal subject argument

contain subject argument =
if subject.contains argument then Success else
msg = subject.to_text + " did not contain " + argument.to_text + "."
here.fail msg

Any.should verb argument = verb Verbs this argument

## Fail a test with the given message.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ public class EpbParser {
/** Lists all the languages supported in polyglot eval. */
public enum ForeignLanguage {
JS("js", "js"),
PY("python", "python");
PY("python", "python"),
R("R", "r");

private final String truffleId;
private final String syntacticTag;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.ReportPolymorphism;
import com.oracle.truffle.api.dsl.Specialization;
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.Node;
import org.enso.interpreter.epb.runtime.GuardedTruffleContext;
import org.enso.interpreter.epb.runtime.PolyglotProxy;
Expand Down Expand Up @@ -61,6 +64,47 @@ Object doAlreadyProxied(
return proxy;
}


@Specialization(guards = "bools.isBoolean(b)")
boolean doWrappedBoolean(
Object b,
GuardedTruffleContext origin,
GuardedTruffleContext target,
@CachedLibrary(limit = "5") InteropLibrary bools) {
try {
return bools.asBoolean(b);
} catch (UnsupportedMessageException e) {
throw new IllegalStateException("Impossible, `b` is checked to be a boolean");
}
}

@Specialization(guards = {"numbers.isNumber(l)", "numbers.fitsInLong(l)"})
long doWrappedLong(
Object l,
GuardedTruffleContext origin,
GuardedTruffleContext target,
@CachedLibrary(limit = "5") InteropLibrary numbers) {
try {
return numbers.asLong(l);
} catch (UnsupportedMessageException e) {
throw new IllegalStateException("Impossible, `l` is checked to be a long");
}
}

@Specialization(
guards = {"numbers.isNumber(d)", "!numbers.fitsInLong(d)", "numbers.fitsInDouble(d)"})
double doWrappedDouble(
Object d,
GuardedTruffleContext origin,
GuardedTruffleContext target,
@CachedLibrary(limit = "5") InteropLibrary numbers) {
try {
return numbers.asDouble(d);
} catch (UnsupportedMessageException e) {
throw new IllegalStateException("Impossible, `l` is checked to be a long");
}
}

@Fallback
Object doWrapProxy(Object o, GuardedTruffleContext origin, GuardedTruffleContext target) {
return new PolyglotProxy(o, origin, target);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.oracle.truffle.api.TruffleLanguage.ContextReference;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.exception.AbstractTruffleException;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
Expand All @@ -25,6 +26,9 @@ public abstract class ForeignEvalNode extends RootNode {
private final EpbParser.Result code;
private @Child ForeignFunctionCallNode foreign;
private @Child ContextRewrapNode rewrapNode = ContextRewrapNodeGen.create();
private @Child ContextRewrapExceptionNode rewrapExceptionNode =
ContextRewrapExceptionNodeGen.create();
private @CompilerDirectives.CompilationFinal AbstractTruffleException parseError;
private final String[] argNames;

ForeignEvalNode(EpbLanguage language, EpbParser.Result code, List<String> arguments) {
Expand All @@ -51,11 +55,15 @@ Object doExecute(
VirtualFrame frame,
@CachedContext(EpbLanguage.class) ContextReference<EpbContext> contextRef) {
ensureParsed(contextRef);
return foreign.execute(frame.getArguments());
if (foreign != null) {
return foreign.execute(frame.getArguments());
} else {
throw parseError;
}
}

private void ensureParsed(ContextReference<EpbContext> ctxRef) {
if (foreign == null) {
if (foreign == null && parseError == null) {
getLock().lock();
try {
if (foreign == null) {
Expand All @@ -67,6 +75,9 @@ private void ensureParsed(ContextReference<EpbContext> ctxRef) {
case PY:
parsePy(ctxRef);
break;
case R:
parseR(ctxRef);
break;
default:
throw new IllegalStateException("Unsupported language resulted from EPB parsing");
}
Expand All @@ -91,9 +102,16 @@ private void parseJs(ContextReference<EpbContext> ctxRef) {
+ code.getForeignSource()
+ "\n};poly_enso_eval";
Source source = Source.newBuilder(code.getLanguage().getTruffleId(), wrappedSrc, "").build();

CallTarget ct = ctxRef.get().getEnv().parseInternal(source);
Object fn = rewrapNode.execute(ct.call(), inner, outer);
foreign = insert(JsForeignNode.build(argNames.length, fn));
} catch (Throwable e) {
if (InteropLibrary.getUncached().isException(e)) {
parseError = rewrapExceptionNode.execute((AbstractTruffleException) e, inner, outer);
} else {
throw e;
}
} finally {
inner.leave(this, p);
}
Expand Down Expand Up @@ -124,6 +142,35 @@ private void parsePy(ContextReference<EpbContext> ctxRef) {
Object fn = ctxRef.get().getEnv().importSymbol("poly_enso_py_eval");
Object contextWrapped = rewrapNode.execute(fn, inner, outer);
foreign = insert(PyForeignNodeGen.create(contextWrapped));
} catch (Throwable e) {
if (InteropLibrary.getUncached().isException(e)) {
parseError = rewrapExceptionNode.execute((AbstractTruffleException) e, inner, outer);
} else {
throw e;
}
} finally {
inner.leave(this, p);
}
}

private void parseR(ContextReference<EpbContext> ctxRef) {
EpbContext context = ctxRef.get();
GuardedTruffleContext outer = context.getCurrentContext();
GuardedTruffleContext inner = context.getInnerContext();
Object p = inner.enter(this);
try {
String args = String.join(",", argNames);
String wrappedSrc = "function(" + args + "){\n" + code.getForeignSource() + "\n}";
Source source = Source.newBuilder(code.getLanguage().getTruffleId(), wrappedSrc, "").build();
CallTarget ct = ctxRef.get().getEnv().parseInternal(source);
Object fn = rewrapNode.execute(ct.call(), inner, outer);
foreign = insert(RForeignNodeGen.create(fn));
} catch (Throwable e) {
if (InteropLibrary.getUncached().isException(e)) {
parseError = rewrapExceptionNode.execute((AbstractTruffleException) e, inner, outer);
} else {
throw e;
}
} finally {
inner.leave(this, p);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.enso.interpreter.epb.node;

import com.oracle.truffle.api.dsl.NodeField;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.library.CachedLibrary;

@NodeField(name = "foreignFunction", type = Object.class)
public abstract class RForeignNode extends ForeignFunctionCallNode {

abstract Object getForeignFunction();

@Specialization
public Object doExecute(
Object[] arguments, @CachedLibrary("foreignFunction") InteropLibrary interopLibrary) {
try {
return interopLibrary.execute(getForeignFunction(), arguments);
} catch (UnsupportedMessageException | UnsupportedTypeException | ArityException e) {
throw new IllegalStateException("R parser returned a malformed object", e);
}
}
}
Loading

0 comments on commit f298fbd

Please sign in to comment.