-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support a low-level PolyglotEngine execution mode #183
Comments
@jtulach Any thoughts? Most of the language implementations I've seen to date work around these issues with bootstrapping tricks. It would be nice to have a better solution. |
I haven't responded as I didn't know what to say. The text in this issue is so vague that it cannot be treated as a real bug report. Unless I see the real problems, I can't discuss solutions. |
@jtulach I've tried my best to give an initial problem statement, but I'll try again: When language-specific code (e.g. a language-specific REPL implementation) wants to interface with its language, it's natural that it wants to work directly with the value types, AST nodes, and exceptions used by the language implementation. In such a case, Here is how SOMns and JRuby/Truffle work around this: https://github.com/smarr/SOMns/blob/master/src/som/interpreter/SomLanguage.java#L150 I believe these constructions are only necessary because there is no supported way to execute anything other than a Has @chrisseaton talked to you about this? We had a discussion in the Gitter channel, and he wanted to propose the "lower-level execution mode" idea to you. |
My idea was vm.executeInLanguageContext('application/x-my-language', () -> {
whatever you want here
}) It would just be syntactic sugar for the current workarounds in the other languages. That was my proposed solution to what you wanted, but really I'm happy with the current system - I only have the workaround in a couple of places so I don't see it as a huge burden. |
@chrisseaton Keep in mind that your understanding is way above that of the average Truffle user, and that other language implementations may have additional constraints that make such workarounds more complicated. |
...they want to take shortcuts. Yes, in such system it may seem that the overhead of
Using these tricks is dangerous - unless you are careful, you may find out things like debugger don't work - to guarantee that I would need to tighten things up, not relax them. |
@pniederw I think we should be able to implement the method I proposed, using the existing Truffle API stuff. We could that in your code, so you don't need a modification to the API. Let me know if you want to try that. |
The point I'm trying to make is that something seems to be missing from To give one example from my own language, my tooling sometimes needs to programmatically construct an AST and execute that. It's not clear to me how to achieve this with By now I've implemented a complete Truffle interpreter for my language (which is roughly as complex as SOMns) and some APIs/tooling around it. How to fit in |
@chrisseaton I'd love to try that. The only constraint I have is that (unfortunately) I can't share any of my code at this time. |
I thought I could do this cleanly, because I thought that one of the public diff --git a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java
index 1f52d88..f6e86ca 100644
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java
@@ -52,9 +52,11 @@ import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.debug.DebuggerTags;
import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.ProvidedTags;
import com.oracle.truffle.api.instrumentation.StandardTags;
import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.sl.nodes.SLEvalRootNode;
import com.oracle.truffle.sl.nodes.SLRootNode;
@@ -89,6 +91,20 @@ public final class SLLanguage extends TruffleLanguage<SLContext> {
@Override
protected CallTarget parse(Source source, Node node, String... argumentNames) throws IOException {
+ if (source.getCode().equals("@callback")) {
+ final Runnable runnableWhenCreated = SLMain.runnable;
+
+ return Truffle.getRuntime().createCallTarget(new RootNode(SLLanguage.class, null, null) {
+
+ @Override
+ public Object execute(VirtualFrame frame) {
+ runnableWhenCreated.run();
+ return null;
+ }
+
+ });
+ }
+
Map<String, SLRootNode> functions;
try {
/*
diff --git a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java
index ab994d5..9cff745 100644
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java
@@ -208,6 +208,14 @@ public final class SLMain {
PolyglotEngine engine = PolyglotEngine.newBuilder().setIn(in).setOut(out).build();
assert engine.getLanguages().containsKey(SLLanguage.MIME_TYPE);
+ runInSLContext(engine, new Runnable() {
+
+ public void run() {
+ System.err.println("hello");
+ }
+
+ });
+
try {
Value result = engine.eval(source);
@@ -290,4 +298,20 @@ public final class SLMain {
}
return result.toString();
}
+
+ public static Runnable runnable;
+
+ private static void runInSLContext(PolyglotEngine engine, Runnable runnable) {
+ SLMain.runnable = runnable;
+
+ final Source source = Source.newBuilder("@callback").name("(callback)").mimeType(SLLanguage.MIME_TYPE).build();
+
+ try {
+ engine.eval(source);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
} |
Hello Chris, Runnable r = // your own code and then any time later: enter.execute(null, callback); // null is JavaScript specifiec thing I believe I am using this style in my own work at -jt Chris Seaton : 10. 7. 2016 @ 05:52
|
Yes that works. And I guess if you had a language where you couldn't natively write that, you could introduce a special form for it. |
We are going to continue to support to use the Truffle core APIs without PolyglotEngine. However the instrumentation and multi-language interop will only work embedded in PolyglotEngine. @pniederw : @jtulach has explained how PolyglotEngine can be used to achieve your goals. Can you work with that? Or do you want to make other concrete suggestions on how to improve? Please close this issue if all your questions are answered. |
ok, so, I should report NullPointerExceptions for stuff that breaks when not initializing the |
Without
My language doesn't currently support language interop, and supporting it is not a priority. The "special form" solution suggested by @chrisseaton should work, but feels like an obscure workaround for a common need. However, it does demonstrate that running non-program code within a context is already possible, which means that adding a public API for this wouldn't lead to any loss of control (which was used as an argument for not adding such an API).
@chrisseaton's suggestion would solve my problem immediately: #183 (comment) |
Is there any consensus on whether something like @chrisseaton's |
It's up to @jtulach whether the new method is added, and from his previous comments it looks like he's not keen on it. All I can suggest is helping you to implement the tiny amount of interop needed to make the eval pattern work for you. |
I believe that common things should be easy, complex possible. Here is a working code that gets anyone into the language context: public final class Main {
public void callMeInContext() {
Thread.dumpStack();
}
public static void main(String[] args) throws IOException {
PolyglotEngine engine = PolyglotEngine.newBuilder().build();
final Source source = Source.
newBuilder("function() { return this.callMeInContext(); }").
name("callback.js").
mimeType("text/javascript").
build();
final PolyglotEngine.Value invoke = engine.eval(source);
final TruffleObject callback = JavaInterop.asTruffleObject(new Main());
invoke.execute(callback);
invoke.execute(callback);
}
} I don't think the solution is that complex and the request that common to justify addition of new method into the API. |
@pniederw PolyglotEngine is not mandatory if you are not using TruffleLanguage. Only TruffleRuntime, CallTarget, RootNode, Node can be used as well. TruffleLanguage is the SPI side of PolyglotEngine. To execute your language you would parse on your own and then call into a CallTarget. Thats what PolyglotEngine is currently doing for you, but if you, as you say, don't care about instrumentation/interop feel free skip PolyglotEngine and TruffleLanguage. |
@jtulach There's no OSS Truffle JS implementation at this point in time, and shipping a JS implementation with my language just to solve this problem doesn't make sense. From my perspective, language interop is a complex solution to this common problem. @chrisseaton I'm keen on giving this a go. Can you give me some pointers on how to implement the necessary amount of language interop? Unfortunately I can't share my code at this point. @chumer Good point. Ditching all of |
You need to subclass If you are just using this for Jaroslav's pattern, you could put simple call logic here: Catch me on Gitter and I'll walk you through it. |
…/truffle:UseLinks to master Using hyperlinks in CHANGELOG.md * commit '090f138ed9ec194ec28b3dde88a80cb2fe1d58b5': Prefer use of . instead of # Direct link to initializeContext Adding links to Javadoc for some important API changes
Seems like the issue is resolved. Low-level execution without any polyglot support is possible for a custom language. I would however not recommend it as it eliminates many benefits, like interop/instruments/debugging. |
Use of
PolyglotEngine
is required to make certain parts of Truffle work (e.g. retrieval ofExecutionContext
). From what I've heard,PolyglotEngine
may even become mandatory in the future.However, I found that when writing tools tailored to a specific language,
PolyglotEngine
isn't a good fit: Values have to be unwrapped, exceptions have to be unwrapped, onlySource
s (but not ASTs) can be evaluated, etc. After a lot of fighting, I gave up and decided to bypassPolyglotEngine
for the time being.To solve this problem, I propose that
PolyglotEngine
should provide an additional lower-level execution mode that allows to run any callback within an engine context, without the baggage ofValue
,Source
, etc. This would greatly simplify writing of language-specific tools.Thanks to Chris Seaton for bringing up the idea of a lower-level execution mode.
The text was updated successfully, but these errors were encountered: