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

Is there a grammatically way to execute Node.js scripts via the Context API #2

Closed
swaechter opened this Issue Apr 30, 2018 · 9 comments

Comments

Projects
None yet
5 participants
@swaechter

swaechter commented Apr 30, 2018

Hey there

Is it possible to execute/eval a Node.js script from the Context API? I can execute a hello world script via the GraalVM node binary:

const http = require('http');

const name = 'node-hello-world';
const port = '8888';

const app = new http.Server();

app.on('request', (req, res) => {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.write('Hello World');
    res.end('\n');
});

app.listen(port, () => {
    console.log(`${name} is listening on port ${port}`);
});

But that doesn't work for the Java API:

import org.apache.commons.io.IOUtils;
import org.graalvm.polyglot.Context;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;

public class NodeJsLauncher {

    public static void main(String[] args) throws Exception {
        new NodeJsLauncher();
    }

    public NodeJsLauncher() throws Exception {
        InputStream inputStream = getClass().getResourceAsStream("/HelloWorld.js");
        String helloWorldScript = IOUtils.toString(inputStream, StandardCharsets.UTF_8);

        Context context = Context.create();
        context.eval("js", helloWorldScript);
    }
}

Output:

Exception in thread "main" ReferenceError: require is not defined
	at <js> :program(Unnamed:1:13-19)
	at org.graalvm.polyglot.Context.eval(Context.java:336)
	at NodeJsLauncher.<init>(NodeJsLauncher.java:18)
	at NodeJsLauncher.main(NodeJsLauncher.java:10)

Process finished with exit code 1

Does the js language even support JavaScript with the Node.js module loading system? Or is it just not enabled:

Graal.js can execute Node.js applications. It provides high compatibility with existing npm packages, with high likelyhood that your application will run out of the box. This includes npm packages with native implementations. Note that you will need to re-compile from source with Graal.js if you want to run binaries that have beeen compiled for Node.js based on V8, or any other compatible engine.

Background: I am in the process of developing a Java solution that is able to server side render Angular SPA applications. The server side rendering requires some Node.js modules, so solutions like J2V8 (or maybe GraalVM) are required. For more information see https://github.com/swaechter/angularj-universal

@wirthi

This comment has been minimized.

Member

wirthi commented May 1, 2018

Hi Simon,

thanks for your interested in our engine.

Yes, what you ask for makes sense of course. However, consider that Node.js is a native application. Our approach is to base on Node.js sources directly - with only the JavaScript engine replaced - without major changes in the Node.js source code or its build process. This implies that, for Node.js support, you currently need to start Node.js as main (native) application. From there, you are free to call into Java and start whatever you want. Starting from Java and only then calling into Node.js would mean to deprive Node.js of all native access to e.g. signals, and it's unclear how the event loop would be started this way - we would have to significantly interfere the way it is started and managed.

This limitation is the reason why the Polyglot feature gives you a "pure" JavaScript (ECMAScript) engine, and not Node.js. Note that you can use load to eval external files, although that will only work for code not depending on the event loop and other core Node.js features.

We are currently working on APIs and code examples to do this nicely without running into threading problems. We could already show in experiments how to submit entries back into the Node.js event loop from Java. You want to avoid a scenario where your interop call (e.g., your Java application) blocks the event loop. We will address this in one of our next blog posts on Graal.js.

Best,
Christian

dougxc pushed a commit that referenced this issue May 25, 2018

http: remove adapter frame from onParserExecute
Remove a pointless adapter frame  by fixing up the function's formal
parameter count.  Before:

    frame #0: 0x000033257ea446d5 onParserExecute(...)
    frame #1: 0x000033257ea3b93f <adaptor>
    frame #2: 0x000033257ea41959 <internal>
    frame #3: 0x000033257e9840ff <entry>

After:

    frame #0: 0x00000956287446d5 onParserExecute(...)
    frame #1: 0x0000095628741959 <internal>
    frame #2: 0x00000956286840ff <entry>

PR-URL: nodejs/node#17693
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
Reviewed-By: Khaidi Chu <i@2333.moe>
@swaechter

This comment has been minimized.

swaechter commented May 28, 2018

Hey Christian

Thank you very much for the detailed explanation. This of course makes sense, so it's easier to keep up to date with the latest Node.js/V8 engine and profit from the work done there (Also you don't have to reinvent the wheel).

Best,
Simon

@edopalmieri

This comment has been minimized.

edopalmieri commented Sep 7, 2018

Hi,
Does enabling the v8-compat option when initialising a Context for JS execution bring any of the Node functionality (obviously not the core stuff like event loop, etc)?

I am wondering specifically about the require module. I have done a quick test:

Context ctx = Context.newBuilder().option("js.v8-compat", "true").build();
String jsCode = "const someLib = require('/someLib.js');";
ctx.eval("js", jsCode);
ctx.close();

But I get the same error that @swaechter was getting: require is not defined. Perhaps the require module is part of the native Node package and not the V8 engine?

Thanks
Edoardo.

@wirthi

This comment has been minimized.

Member

wirthi commented Sep 7, 2018

Hi Edoardo,

exactly, we don't implement require, that's provided by Node.js.

As an alternative, we offer load (https://github.com/graalvm/graaljs/blob/master/docs/user/JavaScriptCompatibility.md#loadsource), which might or might not work on npm modules.

Best,
Christian

@StephenOTT

This comment has been minimized.

StephenOTT commented Oct 15, 2018

@edopalmieri is there a way to run a command from within the context? such as node myJsFile.js --jvm ? So that we can pass the result of the command back into the Java application just as if it was normally run with context.eval("js", "....");

@edopalmieri

This comment has been minimized.

edopalmieri commented Oct 16, 2018

Hi @StephenOTT
From my understanding so far, what you are proposing is not possible. I think only two alternatives exist:

  1. Use load() inside the JS code to execute your myJsFile.js
  2. Execute myJsFile.js using ctx.eval() and then inject then result back into the ctx when running the code that needs it using the bindings.

Perhaps the Graal guys can suggest more approaches. I would be interested to know myself as I am trying to figure out a way to provide some sort of dependency importer/manager to my guest code.

Cheers
Edoardo.
PS: note that it is not possible to execute "node" code via the Polyglot API (ie. context) , only via the native node executable.

@StephenOTT

This comment has been minimized.

StephenOTT commented Oct 25, 2018

@edopalmieri, I had been playing around for using something like Process Builder to execute a node command as a CLI command and execute the specific .js file and then read back the result into a stream.
Had mixed results depending on argument usage, but generally it worked.

would be nice if this was built into the gralljs usage rather than having to jump through the hoops of processbuilder and streams. There is definitely the startup time factor of the node command, but if overall speed is not a concern and you are more concerned about specific usage of node modules, then this works well.

@edopalmieri

This comment has been minimized.

edopalmieri commented Oct 26, 2018

Hi @StephenOTT, I am guessing you mean java.lang.ProcessBuilder right?
That makes sense! It sounds like a nice work around to the problem, but I agree: it would be nice if it was offered as a feature of Graaljs. Perhaps it is in their roadmap ...

Thanks for sharing your solution
Edoardo.

@StephenOTT

This comment has been minimized.

StephenOTT commented Oct 26, 2018

@edopalmieri yes Java lang processbuilder. 👍🏻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment