diff --git a/.travis.yml b/.travis.yml
index e06477754..6bcc07360 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,23 +25,28 @@ matrix:
# jdk: oraclejdk9
# - env: JDK='OpenJDK 9'
# install: . ./install-jdk.sh -F 9
- # 10
- - env: JDK='Oracle JDK 10'
- jdk: oraclejdk10
- - env: JDK='OpenJDK 10'
- install: . ./install-jdk.sh -F 10 -L GPL
- # 11
- - env: JDK='Oracle JDK 11'
- install: . ./install-jdk.sh -F 11 -L BCL
+# JDK 10 is EOL
+# # 10
+# - env: JDK='Oracle JDK 10'
+# jdk: oraclejdk10
+# - env: JDK='OpenJDK 10'
+# install: . ./install-jdk.sh -F 10 -L GPL
+# # 11 (Oracle and OpenJDK are pretty similar)
+# - env: JDK='Oracle JDK 11'
+# install: . ./install-jdk.sh -F 11 -L BCL
- env: JDK='OpenJDK 11'
install: . ./install-jdk.sh -F 11 -L GPL
# GraalVM
- env: JDK='GraalVM 1.0.0'
- install: . ./install-jdk.sh --url https://github.com/oracle/graal/releases/download/vm-1.0.0-rc7/graalvm-ce-1.0.0-rc7-linux-amd64.tar.gz
+ install: . ./install-jdk.sh --url https://github.com/oracle/graal/releases/download/vm-1.0.0-rc9/graalvm-ce-1.0.0-rc9-linux-amd64.tar.gz
+ # OpenJ9
+ - env: JDK='OpenJ9 13'
+ install: . ./install-jdk.sh --url https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.1%2B13/OpenJDK11-jdk_x64_linux_openj9_11.0.1_13.tar.gz
allow_failures:
- # Non LTS releases are OK to fail
- - env: JDK='Oracle JDK 10'
- - env: JDK='OpenJDK 10'
+# # Non LTS releases are OK to fail
+# - env: JDK='OpenJDK 9'
+# - env: JDK='OpenJDK 10'
+ - env: JDK='OpenJ9 13'
script:
- echo PATH = ${PATH}
@@ -50,7 +55,8 @@ script:
- mvn --settings settings.xml test -B
after_success:
-- bash <(curl -s https://codecov.io/bash)
+- '[[ "${JDK}" = "GraalVM 1.0.0" ]] &&
+ bash <(curl -s https://codecov.io/bash) || true'
- '[[ "${TRAVIS_BRANCH}" = "develop" ]] &&
[[ "${TRAVIS_PULL_REQUEST}" = "false" ]] &&
[[ "${JDK}" = "OpenJDK 11" ]] &&
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2903fa5ab..0d920444c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
+- Added a package command to package either fat jar or a JVMCI fat jar.
+- Fix issue preventing GraalJS running on OpenJ9
+- Allow specifying absolute path as start module
- implemented module aliases
- fixed node inspector paths for debugging
- fixed runtime definitions
diff --git a/README.md b/README.md
index adab897dc..43c7282a0 100644
--- a/README.md
+++ b/README.md
@@ -56,6 +56,26 @@ suite.run();
Profit!
-# Documentation
+## JDK11
+
+If you don't want to run on GraalVM JDK but prefer [OpenJDK11](https://adoptopenjdk.net/index.html?variant=openjdk11&jvmVariant=hotspot),
+or even [OpenJ9](https://adoptopenjdk.net/?variant=openjdk11&jvmVariant=openj9) all you need to do is **prefix**
+any `NPM` / `Maven` command with the variable `JVMCI=1`, e.g.:
+
+```bash
+JVMCI=1 npm install
+JVMCI=1 npm start
+```
+
+This will assume you're running on plain JDK11, if you don't want to type the variable all the time or set it to your
+environment, you can add it to your `package.json` as:
+
+```json
+{
+ "jvmci": true
+}
+```
+
+## Documentation
For more documentation please see [docs](./docs).
diff --git a/docs/README.md b/docs/README.md
index 910d24c87..6ed7e12dc 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -64,7 +64,8 @@ add the package [es4x-cli](https://www.npmjs.com/package/es4x-cli) to your
"postinstall": "es4x postinstall",
"test": "es4x launcher test",
"start": "es4x launcher run",
- "shell": "es4x shell"
+ "shell": "es4x shell",
+ "package": "es4x package"
},
"license": "ISC",
"private": true,
@@ -80,8 +81,8 @@ As of this moment you can follow the normal workflow. For example in order to cr
```json
{
"dependencies": {
- "@vertx/core": "3.5.3",
- "@vertx/web": "3.5.3"
+ "@vertx/core": "3.5.4",
+ "@vertx/web": "3.5.4"
}
}
```
@@ -163,7 +164,7 @@ It is common to package JVM applications as runnable `JAR` files, the `es4x-cli`
`maven-shade-plugin` configured for this:
```sh
-mvn clean package
+npm run package
```
And a new `JAR` file should be built in your `target` directory.
@@ -172,6 +173,34 @@ Packaging will re-arrange your application code to be moved to the directory `no
it can be used from other JARs. In order for this to work correctly, the current `node_modules` are also
bundled in the jar as well as all files listed under the `files` property of your `package.json`.
+When running this script you will see also the output command you need to use to run your application e.g.:
+
+```bash
+# Running on a GraalVM JVM
+Running: /home/plopes/Projects/reactiverse/es4x/examples/empty-project/mvnw ... package
+Run your application with:
+
+ java \
+ -jar target/empty-project-1.0.0-bin.jar
+```
+
+Note that if you run with the environment flag `JVMCI` then you can run your app on JDK11 too but the start command
+will be a little more complex, this is what the script tells you e.g.:
+
+```bash
+Running: /home/plopes/Projects/reactiverse/es4x/examples/empty-project/mvnw ... package
+Run your application with:
+
+ java \
+ --module-path=target/compiler \
+ -XX:+UnlockExperimentalVMOptions \
+ -XX:+EnableJVMCI \
+ --upgrade-module-path=target/compiler/compiler.jar \
+ -jar target/empty-project-1.0.0-bin-jvmci.jar
+```
+
+In this case you need not just the binary jar, but also the directory `target/compiler`. With this you can run using
+the GraalJS engine on any JDK11.
### Shell/ REPL
diff --git a/docs/graal/index.md b/docs/graal/index.md
index 579c5cc31..a13fc7635 100644
--- a/docs/graal/index.md
+++ b/docs/graal/index.md
@@ -1,7 +1,7 @@
# GraalVM Support
-ES4X has **experimental** GraalVM support. The same code will run either on Nashorn (JS Engine in JDK>=8) or GraalJS
-(if run on GraalVM).
+ES4X has GraalVM support. The same code will run either on Nashorn (JS Engine in JDK>=8) or GraalJS
+(if run on GraalVM or a JVMCI enabled JVM).
There are benefits on using GraalJS namely the updated language support >=ES6 and support out of the box for generators,
promises, etc....
diff --git a/es4x-cli/.pom.xml b/es4x-cli/.pom.xml
index 1c4d082d9..7d9acb074 100644
--- a/es4x-cli/.pom.xml
+++ b/es4x-cli/.pom.xml
@@ -24,8 +24,93 @@
1.8
1.8
1.8
+ bin
+
+
+ jvmci
+
+
+ env.JVMCI
+
+
+
+ bin-jvmci
+
+
+
+
+ org.graalvm.truffle
+ truffle-api
+ [{{graalVersion}},)
+ runtime
+
+
+ org.graalvm.js
+ js
+ [{{graalVersion}},)
+ runtime
+
+
+ org.graalvm.tools
+ profiler
+ [{{graalVersion}},)
+ runtime
+
+
+ org.graalvm.tools
+ chromeinspector
+ [{{graalVersion}},)
+ runtime
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-graalvm-compiler
+ initialize
+
+ copy
+
+
+
+
+ org.graalvm.compiler
+ compiler
+ {{graalVersion}}
+ jar
+ compiler.jar
+
+
+ org.graalvm.truffle
+ truffle-api
+ {{graalVersion}}
+ jar
+ truffle-api.jar
+
+
+ org.graalvm.sdk
+ graal-sdk
+ {{graalVersion}}
+ jar
+ graal-sdk.jar
+
+
+ target/compiler
+
+
+
+
+
+
+
+
+
{{#dependencies}}
@@ -117,6 +202,7 @@
META-INF/*.SF
META-INF/*.DSA
META-INF/*.RSA
+ **/module-info.class
@@ -133,7 +219,7 @@
- ${project.build.directory}/${project.artifactId}-${project.version}-fat.jar
+ ${project.build.directory}/${project.artifactId}-${project.version}-${shade.classifier}.jar
diff --git a/es4x-cli/bin/es4x.js b/es4x-cli/bin/es4x.js
index 7a756dfd9..68d148b0a 100755
--- a/es4x-cli/bin/es4x.js
+++ b/es4x-cli/bin/es4x.js
@@ -12,6 +12,7 @@ const mkdirp = require('mkdirp');
const version = require('../package.json').version;
const dir = process.cwd();
const isWindows = /^win/.test(process.platform);
+const graalVersion = '1.0.0-rc9';
// quickly abort if there's no package.json
if (!fs.existsSync(path.resolve(dir, 'package.json'))) {
@@ -66,7 +67,6 @@ function exec(command, args, env, options, callback) {
process.exit(err);
}
}
- callback && callback(err);
});
}
@@ -108,6 +108,25 @@ function getMaven() {
return path.resolve(dir, mvn);
}
+/**
+ * Helper to select java from JAVA_HOME or system maven
+ *
+ * @returns {string} the java command
+ */
+function getJava() {
+ let java = isWindows ? 'java.exe' : 'java';
+ let javaHome = process.env['JAVA_HOME'];
+
+ if (javaHome) {
+ let resolved = path.resolve(javaHome, 'bin/' + java);
+ if (fs.existsSync(resolved)) {
+ return resolved;
+ }
+ }
+
+ return java;
+}
+
function generateClassPath(callback) {
const readClassPath = function () {
let classPath = fs.readFileSync(path.resolve(dir, 'target/classpath.txt')).toString('UTF-8');
@@ -123,10 +142,13 @@ function generateClassPath(callback) {
};
if (!fs.existsSync(path.resolve(dir, 'target/classpath.txt'))) {
- let params = [
- '-f', path.resolve(dir, 'pom.xml'),
- 'generate-sources'
- ];
+ let params = [];
+
+ if (npm.jvmci) {
+ params.push('-Pjvmci');
+ }
+
+ params.push('-f', path.resolve(dir, 'pom.xml'), 'generate-sources');
return exec(getMaven(), params, process.env, { verbose: false, stopOnError: true }, readClassPath);
}
@@ -250,13 +272,22 @@ program
files: files,
dependencies: Object.values(dependencies),
- packageJson: npm
+ packageJson: npm,
+ graalVersion: npm.graal || graalVersion
};
try {
fs.writeFileSync(path.resolve(dir, 'pom.xml'), Mustache.render(template, data));
// init the maven bits
- exec(getMaven(), [], process.env, {stopOnError: true, verbose: options.verbose});
+ let params = [];
+
+ if (npm.jvmci) {
+ params.push('-Pjvmci');
+ }
+
+ params.push('-f', path.resolve(dir, 'pom.xml'), 'clean');
+
+ exec(getMaven(), params, process.env, {stopOnError: true, verbose: options.verbose});
} catch (e) {
console.error(c.red.bold(e));
process.exit(1);
@@ -278,6 +309,12 @@ program
process.exit(1);
}
+ // debug validation and they overlap
+ if (options.debug && options.inspect) {
+ console.error(c.red.bold('--debug and --inspect options are exclusive (choose one)'));
+ process.exit(1);
+ }
+
const test = ('test' === cmd);
if (!args || args.length === 0) {
@@ -303,6 +340,16 @@ program
generateClassPath(function (classPath) {
let params = [];
+ if (npm.jvmci || process.env['JVMCI']) {
+ // enable modules
+ params.push('--module-path=target/compiler');
+ // enable JVMCI
+ params.push('-XX:+UnlockExperimentalVMOptions');
+ params.push('-XX:+EnableJVMCI');
+ // upgrade graal compiler
+ params.push('--upgrade-module-path=target/compiler/compiler.jar');
+ }
+
if (options.debug) {
if (options.debug === true) {
console.log(c.yellow.bold('Debug socket listening at port: 9229'));
@@ -348,7 +395,7 @@ program
params = params.concat(args);
// run the command
- exec('java', params, process.env, {verbose: true});
+ exec(getJava(), params, process.env, {verbose: true});
});
});
@@ -358,10 +405,20 @@ program
.action(function (args) {
generateClassPath(function (classPath) {
- let params = [
- '-cp',
- classPath
- ];
+ let params = [];
+
+ if (npm.jvmci || process.env['JVMCI']) {
+ // enable modules
+ params.push('--module-path=target/compiler');
+ // enable JVMCI
+ params.push('-XX:+UnlockExperimentalVMOptions');
+ params.push('-XX:+EnableJVMCI');
+ // upgrade graal compiler
+ params.push('--upgrade-module-path=target/compiler/compiler.jar');
+ }
+
+ params.push('-cp');
+ params.push(classPath);
params.push('io.reactiverse.es4x.Shell');
@@ -372,7 +429,7 @@ program
// Releasing stdin
process.stdin.setRawMode(false);
- spawn('java', params, {stdio: [0, 1, 2]})
+ spawn(getJava(), params, {stdio: [0, 1, 2]})
.on("exit", function (code) {
// Don't forget to switch pseudo terminal on again
process.stdin.setRawMode(true);
@@ -409,6 +466,7 @@ program
npm.scripts.start = 'es4x launcher run';
npm.scripts.test = 'es4x launcher test';
npm.scripts.shell = 'es4x shell';
+ npm.scripts.package = 'es4x package';
try {
fs.writeFileSync(path.resolve(dir, 'package.json'), JSON.stringify(npm, null, 2));
@@ -418,6 +476,55 @@ program
}
});
+program
+ .command('package')
+ .description('Packages the application as a runnable jar')
+ .action(function(options) {
+
+ // if it doesn't exist stop
+ if (!fs.existsSync(path.resolve(dir, 'pom.xml'))) {
+ console.error(c.red.bold('No \'pom.xml\' found, please init it first.'));
+ process.exit(1);
+ }
+
+ try {
+ // init the maven bits
+ let params = [];
+
+ if (npm.jvmci || process.env['JVMCI']) {
+ params.push('-Pjvmci');
+ }
+
+ params.push('-f', path.resolve(dir, 'pom.xml'), 'clean', 'package');
+
+ exec(getMaven(), params, process.env, {stopOnError: true, verbose: options.verbose}, function (code) {
+ if (code !== 0) {
+ console.error(c.red.bold('Maven exited with code: ' + code));
+ process.exit(1);
+ }
+
+ console.log(c.green.bold('Run your application with:'));
+ console.log();
+
+ var classifier = 'bin';
+
+ console.log(c.bold(' ' + getJava() + ' \\'));
+ if (npm.jvmci || process.env['JVMCI']) {
+ classifier = 'bin-jvmci';
+ console.log(c.bold(' --module-path=target/compiler \\'));
+ console.log(c.bold(' -XX:+UnlockExperimentalVMOptions \\'));
+ console.log(c.bold(' -XX:+EnableJVMCI \\'));
+ console.log(c.bold(' --upgrade-module-path=target/compiler/compiler.jar \\'));
+ }
+ console.log(c.bold(' -jar target/' + (npm.artifactId || npm.name) + '-' + npm.version + '-' + classifier + '.jar'));
+ console.log();
+ });
+ } catch (e) {
+ console.error(c.red.bold(e));
+ process.exit(1);
+ }
+ });
+
program
.command('*')
.description('Prints this help')
diff --git a/es4x-cli/package.json b/es4x-cli/package.json
index b4daaddab..c20f155e9 100644
--- a/es4x-cli/package.json
+++ b/es4x-cli/package.json
@@ -1,6 +1,6 @@
{
"name": "es4x-cli",
- "version": "0.0.2",
+ "version": "0.0.3",
"readme": "README.md",
"description": "Utilities to work with Eclipse Vert.x",
"scripts": {
diff --git a/examples/empty-project/package.json b/examples/empty-project/package.json
index 01529a7b7..2658e6dab 100644
--- a/examples/empty-project/package.json
+++ b/examples/empty-project/package.json
@@ -4,21 +4,21 @@
"private": true,
"description": "A barebones application",
"main": "index.js",
- "scripts": {
- "postinstall": "vertx-scripts init",
- "test": "vertx-scripts launcher test -v",
- "start": "vertx-scripts launcher run",
- "package": "vertx-scripts package",
- "repl": "vertx-scripts repl"
- },
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"vertx-scripts": "latest",
- "@vertx/unit": "latest"
+ "@vertx/unit": "latest",
+ "es4x-cli": "latest"
},
"dependencies": {
"@vertx/core": "latest"
+ },
+ "scripts": {
+ "postinstall": "es4x postinstall",
+ "start": "es4x launcher run",
+ "test": "es4x launcher test",
+ "shell": "es4x shell"
}
}
diff --git a/examples/techempower/pom.xml b/examples/techempower/pom.xml
index 0ae511793..5d62f983b 100644
--- a/examples/techempower/pom.xml
+++ b/examples/techempower/pom.xml
@@ -66,27 +66,27 @@
io.vertx
vertx-web-templ-handlebars
- 3.5.2
+ 3.5.4
io.vertx
vertx-core
- 3.5.3
+ 3.5.4
io.vertx
vertx-web
- 3.5.3
+ 3.5.4
io.vertx
vertx-auth-common
- 3.5.3
+ 3.5.4
io.vertx
vertx-web-common
- 3.5.3
+ 3.5.4
io.reactiverse
@@ -96,7 +96,7 @@
io.vertx
vertx-unit
- 3.5.3
+ 3.5.4
test
diff --git a/pom.xml b/pom.xml
index 49e84801f..265899126 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,11 +13,11 @@
io.reactiverse
es4x
- 0.5.5
+ 0.5.6
3.5.4
- 1.0.0-rc7
+ 1.0.0-rc9
UTF-8
@@ -108,7 +108,7 @@
provided
- org.graalvm
+ org.graalvm.sdk
graal-sdk
${graal.version}
provided
diff --git a/src/main/java/io/reactiverse/es4x/FatalException.java b/src/main/java/io/reactiverse/es4x/FatalException.java
deleted file mode 100644
index 16cbb3966..000000000
--- a/src/main/java/io/reactiverse/es4x/FatalException.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2018 Red Hat, Inc.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * and Apache License v2.0 which accompanies this distribution.
- *
- * The Eclipse Public License is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * The Apache License v2.0 is available at
- * http://www.opensource.org/licenses/apache2.0.php
- *
- * You may elect to redistribute this code under either of these licenses.
- */
-package io.reactiverse.es4x;
-
-import org.graalvm.polyglot.PolyglotException;
-
-public final class FatalException extends Exception {
- public FatalException(PolyglotException cause) {
- super(cause);
- }
-}
diff --git a/src/main/java/io/reactiverse/es4x/IncompleteSourceException.java b/src/main/java/io/reactiverse/es4x/IncompleteSourceException.java
deleted file mode 100644
index 633312c20..000000000
--- a/src/main/java/io/reactiverse/es4x/IncompleteSourceException.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2018 Red Hat, Inc.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * and Apache License v2.0 which accompanies this distribution.
- *
- * The Eclipse Public License is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * The Apache License v2.0 is available at
- * http://www.opensource.org/licenses/apache2.0.php
- *
- * You may elect to redistribute this code under either of these licenses.
- */
-package io.reactiverse.es4x;
-
-import org.graalvm.polyglot.PolyglotException;
-
-public final class IncompleteSourceException extends Exception {
- public IncompleteSourceException(PolyglotException cause) {
- super(cause);
- }
-}
diff --git a/src/main/java/io/reactiverse/es4x/Loader.java b/src/main/java/io/reactiverse/es4x/Loader.java
deleted file mode 100644
index 6af3a8537..000000000
--- a/src/main/java/io/reactiverse/es4x/Loader.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2018 Red Hat, Inc.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * and Apache License v2.0 which accompanies this distribution.
- *
- * The Eclipse Public License is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * The Apache License v2.0 is available at
- * http://www.opensource.org/licenses/apache2.0.php
- *
- * You may elect to redistribute this code under either of these licenses.
- */
-package io.reactiverse.es4x;
-
-import io.vertx.core.json.JsonObject;
-
-public interface Loader {
-
- String name();
-
- void config(final JsonObject config);
-
- T require(String main);
-
- T main(String main);
-
- T worker(String main, String address);
-
- T eval(String script) throws Exception;
-
- default T evalLiteral(CharSequence script) throws Exception {
- return eval(script.toString());
- }
-
- boolean hasMember(T thiz, String key);
-
- T invokeMethod(T thiz, String method, Object... args);
-
- T invokeFunction(String function, Object... args);
-
- void put(String name, Object value);
-
- default void enter() {
- }
-
- default void leave() {
- }
-
- void close();
-}
diff --git a/src/main/java/io/reactiverse/es4x/Runtime.java b/src/main/java/io/reactiverse/es4x/Runtime.java
index 85368eaea..ee00b2c72 100644
--- a/src/main/java/io/reactiverse/es4x/Runtime.java
+++ b/src/main/java/io/reactiverse/es4x/Runtime.java
@@ -25,46 +25,70 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
-public interface Runtime {
+public interface Runtime {
- static Runtime getCurrent() {
+ static Runtime getCurrent(Vertx vertx) {
String rtName = System.getProperty("es4x.engine");
// rt name takes precedence in the choice
if (rtName == null) {
String vmName = System.getProperty("java.vm.name");
if (vmName != null && vmName.startsWith("GraalVM")) {
- rtName = "GraalJS";
+ // we're running on a GraalVM JDK
+ rtName = "graaljs";
} else {
- rtName = "Nashorn";
+ // it is not GraalVM JDK but if it is JDK11 and the graal components
+ // are available graaljs can be available too
+
+ try {
+ double spec = Double.parseDouble(System.getProperty("java.specification.version"));
+ if (spec < 11) {
+ rtName = "nashorn";
+ }
+ } catch (NumberFormatException nfe) {
+ rtName = "nashorn";
+ }
}
+ } else {
+ rtName = rtName.toLowerCase();
}
- if (rtName.equalsIgnoreCase("GraalJS")) {
- System.setProperty("es4x.engine", "GraalJS");
- return new GraalRuntime();
+ if (rtName == null || "graaljs".equals(rtName)) {
+ try {
+ System.setProperty("es4x.engine", "graaljs");
+ return new GraalRuntime(vertx);
+ } catch (NoClassDefFoundError | IllegalStateException e) {
+ // in the case classes are missing, the graal bits are missing
+ // so fallback to Nashorn
+
+ // we could also have an illegal state when the graal is missing
+ // the language bits, in that case also try to fallback to nashorn
+ rtName = "nashorn";
+ }
}
- if (rtName.equalsIgnoreCase("Nashorn")) {
- System.setProperty("es4x.engine", "Nashorn");
- return new NashornRuntime();
+ if ("nashorn".equals(rtName)) {
+ System.setProperty("es4x.engine", rtName);
+ return new NashornRuntime(vertx);
}
- System.clearProperty("es4x.engine");
+ // no nashorn or graal available on the system!
throw new RuntimeException("Unsupported runtime: " + rtName);
}
/**
* return the runtime name
+ *
* @return runtime name.
*/
String name();
/**
* Bootstraps a Vert.x instance
+ *
* @param arguments arguments
* @return a vertx instance
*/
- default Vertx vertx(Map arguments) {
+ static Vertx vertx(Map arguments) {
final VertxOptions options = arguments == null ? new VertxOptions() : new VertxOptions(new JsonObject(arguments));
@@ -101,12 +125,110 @@ default Vertx vertx(Map arguments) {
}
}
- Runtime registerCodec(Vertx vertx);
+ /**
+ * passes the given configuration to the runtime.
+ *
+ * @param config given configuration.
+ */
+ void config(final JsonObject config);
+
+ /**
+ * Require a module following the commonjs spec
+ *
+ * @param module a module
+ * @return return the module
+ */
+ T require(String module);
/**
- * Returns a module loader for the given runtime.
+ * Requires the main module as a commonjs module, the
+ * module returned will be flagged as a main module.
*
- * @return loader
+ * @param main the main module
+ * @return the module
+ */
+ T main(String main);
+
+ /**
+ * Loads a JS Worker, meaning it will become a Vert.x Worker.
+ *
+ * @param main the main entry script
+ * @param address the eventbus address
+ * @return the module
+ */
+ T worker(String main, String address);
+
+ /**
+ * Evals a given sript string.
+ *
+ * @param script string containing code.
+ * @return returns the evaluation result.
+ * @throws Exception on error
+ */
+ T eval(String script) throws Exception;
+
+ /**
+ * Evals a script literal. Script literals are hidden from the
+ * chrome inspector loaded scripts.
+ *
+ * @param script string containing code.
+ * @return returns the evaluation result.
+ * @throws Exception on error
+ */
+ default T evalLiteral(CharSequence script) throws Exception {
+ return eval(script.toString());
+ }
+
+ /**
+ * Performs property lookups on a given evaluated object.
+ *
+ * @param thiz the evaluated object
+ * @param key the key to lookup
+ * @return the value associated with the key
+ */
+ boolean hasMember(T thiz, String key);
+
+ /**
+ * Invokes a method on an evaluated object.
+ *
+ * @param thiz the evaluated object
+ * @param method the method name to invoke
+ * @param args the vararg arguments list
+ * @return the call result
+ */
+ T invokeMethod(T thiz, String method, Object... args);
+
+ /**
+ * Invokes function on the global scope.
+ *
+ * @param function the function name to invoke
+ * @param args the vararg arguments list
+ * @return the call result
+ */
+ T invokeFunction(String function, Object... args);
+
+ /**
+ * Puts a value to the global scope.
+ *
+ * @param name the key to identify the value in the global scope
+ * @param value the value to store.
+ */
+ void put(String name, Object value);
+
+ /**
+ * explicitly enter the script engine scope.
+ */
+ default void enter() {
+ }
+
+ /**
+ * explicitly leave the script engine scope.
+ */
+ default void leave() {
+ }
+
+ /**
+ * close the current runtime and shutdown all the engine related resources.
*/
- Loader loader(Vertx vertx);
+ void close();
}
diff --git a/src/main/java/io/reactiverse/es4x/ScriptException.java b/src/main/java/io/reactiverse/es4x/ScriptException.java
new file mode 100644
index 000000000..6f500bef3
--- /dev/null
+++ b/src/main/java/io/reactiverse/es4x/ScriptException.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2018 Red Hat, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * The Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php
+ *
+ * You may elect to redistribute this code under either of these licenses.
+ */
+package io.reactiverse.es4x;
+
+/**
+ * Exception wrapper to avoid depend on Graal exception types
+ * when dealing with VMs running on Nashorn only environments
+ */
+public class ScriptException extends RuntimeException {
+
+ private final boolean isExit;
+ private final boolean isIncompleteSource;
+
+ public ScriptException(Throwable other, boolean isIncompleteSource, boolean isExit) {
+ super(other.getMessage(), other.getCause());
+ this.isIncompleteSource = isIncompleteSource;
+ this.isExit = isExit;
+ }
+
+ public boolean isIncompleteSource() {
+ return isIncompleteSource;
+ }
+
+ public boolean isExit() {
+ return isExit;
+ }
+}
diff --git a/src/main/java/io/reactiverse/es4x/Shell.java b/src/main/java/io/reactiverse/es4x/Shell.java
index e0f263201..eb5864bc0 100644
--- a/src/main/java/io/reactiverse/es4x/Shell.java
+++ b/src/main/java/io/reactiverse/es4x/Shell.java
@@ -15,6 +15,7 @@
*/
package io.reactiverse.es4x;
+import io.vertx.core.Context;
import io.vertx.core.Vertx;
import java.io.*;
@@ -48,8 +49,6 @@ private static String toCamelCase(String arg) {
public static void main(String[] args) {
- final Runtime runtime = Runtime.getCurrent();
-
final Map options = new HashMap<>();
String script = null;
@@ -94,47 +93,101 @@ public static void main(String[] args) {
}
// create the vertx instance that will boostrap the whole process
- final Vertx vertx = runtime.vertx(options);
+ final Vertx vertx = Runtime.vertx(options);
final String main = script;
+ if (main == null && System.console() == null) {
+ // invalid state, no script and no console
+ System.err.println("\u001B[1m\u001B[31mNo Script provided in non interactive shell!\u001B[0m");
+ System.exit(1);
+ }
+
// move the context to the event loop
vertx.runOnContext(v -> {
- runtime.registerCodec(vertx);
-
- final Loader loader = runtime.loader(vertx);
+ final Runtime runtime = Runtime.getCurrent(vertx);
if (main != null) {
- loader.main(main);
+ runtime.main(main);
+ } else {
+ new REPL(vertx.getOrCreateContext(), runtime).start();
+ }
+ });
+ }
+
+ private static class REPL extends Thread {
+
+ final StringBuilder buffer = new StringBuilder();
+
+ final Context context;
+ final Runtime runtime;
+
+ private REPL(Context context, Runtime runtime) {
+ this.context = context;
+ this.runtime = runtime;
+ }
+
+ private synchronized String updateBuffer(String line, boolean resetOrPrepend) {
+ if (resetOrPrepend) {
+ buffer.append(line);
+ final String statement = buffer.toString();
+ // reset the buffer
+ buffer.setLength(0);
+ return statement;
} else {
- try (BufferedReader input = new BufferedReader(new InputStreamReader(System.in))) {
- final StringBuilder buffer = new StringBuilder();
- for (; ; ) {
+ // incomplete source, do not handle as error and
+ // continue appending to the previous buffer
+ buffer.insert(0, line);
+ return null;
+ }
+ }
+
+ @Override
+ public void run() {
+ try (BufferedReader input = new BufferedReader(new InputStreamReader(System.in))) {
+ System.out.print("> ");
+ System.out.flush();
+
+ for (; ; ) {
+ String line = input.readLine();
+ if (line == null) {
+ break;
+ }
+
+ final String statement = updateBuffer(line, true);
+
+ // ensure the statement is run on the right context
+ context.runOnContext(v -> {
try {
- if (buffer.length() == 0) {
- System.out.print("> ");
- } else {
- System.out.print("| ");
+ System.out.println("\u001B[1;90m" + runtime.evalLiteral(statement) + "\u001B[0m");
+ System.out.print("> ");
+ System.out.flush();
+ } catch (ScriptException t) {
+ if (t.isIncompleteSource()) {
+ updateBuffer(statement, false);
+ return;
}
- String line = input.readLine();
- if (line == null) {
- break;
+ System.out.println("\u001B[1m\u001B[31m" + t.getMessage() + "\u001B[0m\n");
+
+ if (t.isExit()) {
+ // polyglot engine is requesting to exit
+ // REPL is cancelled, close the loader
+ try {
+ runtime.close();
+ System.exit(1);
+ } catch (RuntimeException e) {
+ // ignore...
+ }
+ // force a error code out
+ System.exit(1);
}
- buffer.append(line);
-
- System.out.println("\u001B[1;90m" + loader.evalLiteral(buffer) + "\u001B[0m");
- // reset the buffer
- buffer.setLength(0);
- } catch (IncompleteSourceException t) {
- // incomplete source, do not handle as error and
- // continue appending to the previous buffer
- } catch (FatalException t) {
- // polyglot engine is requesting to exit
- break;
- } catch (Exception t) {
- // reset the buffer as the source is complete
- // but there's an error anyway
- buffer.setLength(0);
+
+ System.out.print("> ");
+ System.out.flush();
+ } catch (Throwable t) {
+ String message = null;
+ String trace = null;
+
// collect the trace back to a string
try (StringWriter sw = new StringWriter()) {
PrintWriter pw = new PrintWriter(sw);
@@ -142,25 +195,30 @@ public static void main(String[] args) {
String sStackTrace = sw.toString(); // stack trace as a string
int idx = sStackTrace.indexOf("\n\tat");
if (idx != -1) {
- System.out.print("\u001B[1m\u001B[31m" + sStackTrace.substring(0, idx) + "\u001B[0m");
- System.out.println(sStackTrace.substring(idx));
+ message = sStackTrace.substring(0, idx);
+ trace = sStackTrace.substring(idx);
} else {
- System.out.println(sStackTrace);
+ trace = sStackTrace;
}
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+
+ if (message != null) {
+ System.out.print("\u001B[1m\u001B[31m" + message + "\u001B[0m");
}
+
+ System.out.println(trace);
+ System.out.print("> ");
+ System.out.flush();
}
- }
- // REPL is cancelled, close the loader
- try {
- loader.close();
- } catch (RuntimeException e) {
- // ignore...
- }
- } catch (IOException e) {
- e.printStackTrace();
- System.exit(1);
+ });
}
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.exit(1);
}
- });
+ }
}
}
diff --git a/src/main/java/io/reactiverse/es4x/VerticleFactory.java b/src/main/java/io/reactiverse/es4x/VerticleFactory.java
index 20d6a5773..a944d96d8 100644
--- a/src/main/java/io/reactiverse/es4x/VerticleFactory.java
+++ b/src/main/java/io/reactiverse/es4x/VerticleFactory.java
@@ -22,8 +22,6 @@
public class VerticleFactory implements io.vertx.core.spi.VerticleFactory {
- private final Runtime runtime = Runtime.getCurrent();
-
private Vertx vertx;
@Override
@@ -39,15 +37,10 @@ public String prefix() {
@Override
public Verticle createVerticle(String verticleName, ClassLoader classLoader) {
- final Loader engine;
+ final Runtime runtime;
synchronized (this) {
- engine = runtime
- // now that the vertx instance fully initialized and available,
- // register the custom JS codec for the eventbus
- .registerCodec(vertx)
- // create a new CommonJS loader
- .loader(vertx);
+ runtime = Runtime.getCurrent(vertx);
}
return new Verticle() {
@@ -86,7 +79,7 @@ public void start(Future startFuture) throws Exception {
worker = context.isWorkerContext() || context.isMultiThreadedWorkerContext();
// expose config
if (context.config() != null) {
- engine.config(context.config());
+ runtime.config(context.config());
}
} else {
worker = false;
@@ -96,32 +89,32 @@ public void start(Future startFuture) throws Exception {
// this can take some time to load so it might block the event loop
// this is usually not a issue as it is a one time operation
try {
- engine.enter();
+ runtime.enter();
if (worker) {
- self = engine.worker(fsVerticleName, address);
+ self = runtime.worker(fsVerticleName, address);
} else {
- self = engine.main(fsVerticleName);
+ self = runtime.main(fsVerticleName);
}
} catch (RuntimeException e) {
startFuture.fail(e);
return;
} finally {
- engine.leave();
+ runtime.leave();
}
if (self != null) {
if (worker) {
// if it is a worker and there is a onmessage handler we need to bind it to the eventbus
- if (engine.hasMember(self, "onmessage")) {
+ if (runtime.hasMember(self, "onmessage")) {
try {
// if the worker has specified a onmessage function we need to bind it to the eventbus
- final Object JSON = engine.eval("JSON");
+ final Object JSON = runtime.eval("JSON");
vertx.eventBus().consumer(address + ".out", msg -> {
// parse the json back to the engine runtime type
- Object json = engine.invokeMethod(JSON, "parse", msg.body());
+ Object json = runtime.invokeMethod(JSON, "parse", msg.body());
// deliver it to the handler
- engine.invokeMethod(self, "onmessage", json);
+ runtime.invokeMethod(self, "onmessage", json);
});
} catch (RuntimeException e) {
startFuture.fail(e);
@@ -130,15 +123,15 @@ public void start(Future startFuture) throws Exception {
}
} else {
// if the main module exports 2 function we bind those to the verticle lifecycle
- if (engine.hasMember(self, "start")) {
+ if (runtime.hasMember(self, "start")) {
try {
- engine.enter();
- engine.invokeMethod(self, "start");
+ runtime.enter();
+ runtime.invokeMethod(self, "start");
} catch (RuntimeException e) {
startFuture.fail(e);
return;
} finally {
- engine.leave();
+ runtime.leave();
}
}
}
@@ -151,21 +144,21 @@ public void start(Future startFuture) throws Exception {
@Override
public void stop(Future stopFuture) {
if (self != null) {
- if (engine.hasMember(self, "stop")) {
+ if (runtime.hasMember(self, "stop")) {
try {
- engine.enter();
- engine.invokeMethod(self, "stop");
+ runtime.enter();
+ runtime.invokeMethod(self, "stop");
} catch (RuntimeException e) {
stopFuture.fail(e);
return;
} finally {
// done!
- engine.leave();
+ runtime.leave();
}
}
}
// close the loader
- engine.close();
+ runtime.close();
stopFuture.complete();
}
};
diff --git a/src/main/java/io/reactiverse/es4x/impl/graal/GraalLoader.java b/src/main/java/io/reactiverse/es4x/impl/graal/GraalLoader.java
deleted file mode 100644
index 8d21c6e48..000000000
--- a/src/main/java/io/reactiverse/es4x/impl/graal/GraalLoader.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright 2018 Red Hat, Inc.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * and Apache License v2.0 which accompanies this distribution.
- *
- * The Eclipse Public License is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * The Apache License v2.0 is available at
- * http://www.opensource.org/licenses/apache2.0.php
- *
- * You may elect to redistribute this code under either of these licenses.
- */
-package io.reactiverse.es4x.impl.graal;
-
-import io.reactiverse.es4x.FatalException;
-import io.reactiverse.es4x.IncompleteSourceException;
-import io.reactiverse.es4x.Loader;
-import io.vertx.core.Vertx;
-import io.vertx.core.json.JsonObject;
-import org.graalvm.polyglot.*;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.Map;
-import java.util.function.Function;
-
-public class GraalLoader implements Loader {
-
- private final Context context;
- private final Value bindings;
- private final Value module;
-
- public GraalLoader(final Vertx vertx) {
- this(
- vertx,
- // create the default context
- Context
- .newBuilder("js")
- .allowHostAccess(true)
- .allowCreateThread(true)
- .build()
- );
- }
-
- private static String getCWD() {
- // clean up the current working dir
- String cwdOverride = System.getProperty("vertx.cwd");
- String cwd;
- // are the any overrides?
- if (cwdOverride != null) {
- cwd = new File(cwdOverride).getAbsolutePath();
- } else {
- cwd = System.getProperty("user.dir");
- }
- // ensure it's not null
- if (cwd == null) {
- cwd = "";
- }
-
- // all paths are unix paths
- cwd = cwd.replace('\\', '/');
- // ensure it ends with /
- if (cwd.charAt(cwd.length() - 1) != '/') {
- cwd += '/';
- }
-
- // append the required prefix
- return "file://" + cwd;
- }
-
- public GraalLoader(final Vertx vertx, Context context) {
-
- this.context = context;
- this.bindings = this.context.getBindings("js");
-
- // remove the exit and quit functions
- bindings.removeMember("exit");
- bindings.removeMember("quit");
- // add vertx as a global
- bindings.putMember("vertx", vertx);
-
- // clean up the current working dir
- final String cwd = getCWD();
-
- // override the default load function to allow proper mapping of file for debugging
- bindings.putMember("load", new Function