Skip to content
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

Switch from Nashorn js to Graal js #240

Merged
merged 17 commits into from
Aug 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
language: java
dist: trusty
jdk:
- oraclejdk8
- openjdk11
cache:
directories:
- "$HOME/.m2"
Expand Down Expand Up @@ -30,7 +30,7 @@ install:
- sudo apt-get update -qq
script:
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then if [ "${TRAVIS_PULL_REQUEST}" = "false"];
then mvn clean deploy --settings deploy.xml; else mvn clean package; fi fi
then mvn clean deploy --settings deploy.xml; else mvn --quiet clean package; fi fi
after_success:
- codecov
before_deploy:
Expand Down
4 changes: 1 addition & 3 deletions graphwalker-cli/src/main/java/org/graphwalker/cli/CLI.java
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,7 @@ private void run(String[] args) {
} catch (ParameterException e) {
System.err.println("An error occurred when running command: " + StringUtils.join(args, " "));
System.err.println(e.getMessage() + System.lineSeparator());
if (jc.getParsedCommand() != null) {
jc.usage(jc.getParsedCommand());
}
jc.usage();
} catch (Exception e) {
System.err.println("An error occurred when running command: " + StringUtils.join(args, " "));
System.err.println(e.getMessage() + System.lineSeparator());
Expand Down
2 changes: 1 addition & 1 deletion graphwalker-cli/src/test/resources/logback-test.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<pattern>%d{HH:mm:ss.SSS} [%thread] [%X{trace}] %-5level %logger{20} - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<root level="error">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
19 changes: 19 additions & 0 deletions graphwalker-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,23 @@
</developerConnection>
</scm>

<dependencies>
<dependency>
<groupId>org.graalvm.sdk</groupId>
<artifactId>graal-sdk</artifactId>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js</artifactId>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js-scriptengine</artifactId>
</dependency>
<dependency>
<groupId>org.graalvm.truffle</groupId>
<artifactId>truffle-api</artifactId>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
* #L%
*/

import org.graalvm.polyglot.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -46,17 +47,11 @@ public InternalState(String script) {

@Override
public boolean isFulfilled() {
try {
Object value = getContext().getScriptEngine().eval(script);
if (value instanceof Boolean) {
return (Boolean) value;
} else {
throw new StopConditionException("Wrong type of expression");
}
} catch (ScriptException e) {
LOG.error(e.getMessage());
return false;
Value value = getContext().getExecutionEnvironment().eval("js", script);
if (value.isBoolean()) {
return value.asBoolean();
}
throw new StopConditionException("Wrong type of expression");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Expand All @@ -26,6 +26,7 @@
* #L%
*/

import org.graalvm.polyglot.Value;
import org.graphwalker.core.algorithm.Algorithm;
import org.graphwalker.core.generator.PathGenerator;
import org.graphwalker.core.model.Action;
Expand All @@ -34,10 +35,8 @@
import org.graphwalker.core.model.Requirement;
import org.graphwalker.core.statistics.Profiler;

import javax.script.ScriptEngine;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import static org.graphwalker.core.model.Edge.RuntimeEdge;
import static org.graphwalker.core.model.Model.RuntimeModel;
Expand All @@ -51,7 +50,7 @@ public interface Context {

Context setExecutionStatus(ExecutionStatus executionStatus);

ScriptEngine getScriptEngine();
org.graalvm.polyglot.Context getExecutionEnvironment();

RuntimeModel getModel();

Expand Down Expand Up @@ -91,7 +90,11 @@ public interface Context {

void execute(Action action);

void execute(String name);
void execute(Element name);

Map<String, String> getKeys();
Value getAttribute(String name);

void setAttribute(String name, Value value);

void setGlobalExecutionEnvironment(org.graalvm.polyglot.Context executionEnvironment);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Expand All @@ -26,14 +26,14 @@
* #L%
*/

import org.graalvm.polyglot.Value;
import org.graphwalker.core.algorithm.Algorithm;
import org.graphwalker.core.generator.PathGenerator;
import org.graphwalker.core.model.*;
import org.graphwalker.core.statistics.Profiler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.script.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
Expand All @@ -54,12 +54,12 @@
*
* @author Nils Olsson
*/
public abstract class ExecutionContext extends SimpleScriptContext implements Context {
public abstract class ExecutionContext implements Context {

private static final Logger LOG = LoggerFactory.getLogger(ExecutionContext.class);

private final static String DEFAULT_SCRIPT_LANGUAGE = "JavaScript";
private ScriptEngine scriptEngine;
private org.graalvm.polyglot.Context executionEnvironment;
private org.graalvm.polyglot.Context globalExecutionEnvironment;

private RuntimeModel model;
private PathGenerator pathGenerator;
Expand All @@ -74,39 +74,8 @@ public abstract class ExecutionContext extends SimpleScriptContext implements Co
private final Map<Requirement, RequirementStatus> requirements = new HashMap<>();

public ExecutionContext() {
ScriptEngine engine = getEngineByName();
engine.setContext(this);
String script = "";
Compilable compiler = (Compilable) engine;
for (Method method : getClass().getMethods()) {
String arguments = "";
for (int i = 0; i < method.getParameterTypes().length; i++) {
if (i > 0) {
arguments += ",";
}
arguments += Character.toChars(65 + i)[0];
}
script += "function " + method.getName() + "(" + arguments;
script += ") { return impl." + method.getName() + "(" + arguments + ");};";
}
try {
CompiledScript compiledScript = compiler.compile(script);
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("impl", this);
compiledScript.eval(bindings);
scriptEngine = compiledScript.getEngine();
} catch (ScriptException e) {
LOG.error(e.getMessage());
throw new RuntimeException(e);
}
}

private ScriptEngine getEngineByName() {
ScriptEngine engine = new ScriptEngineManager().getEngineByName(DEFAULT_SCRIPT_LANGUAGE);
if (null == engine) {
throw new MachineException("Failed to create ScriptEngine");
}
return engine;
executionEnvironment = org.graalvm.polyglot.Context.newBuilder().allowAllAccess(true).build();
executionEnvironment.getBindings("js").putMember(getClass().getSimpleName(), this);
}

public ExecutionContext(Model model, PathGenerator pathGenerator) {
Expand All @@ -119,8 +88,8 @@ public ExecutionContext(RuntimeModel model, PathGenerator pathGenerator) {
setPathGenerator(pathGenerator);
}

public ScriptEngine getScriptEngine() {
return scriptEngine;
public org.graalvm.polyglot.Context getExecutionEnvironment() {
return executionEnvironment;
}

public RuntimeModel getModel() {
Expand Down Expand Up @@ -258,111 +227,64 @@ public <E> List<E> filter(Collection<E> elements) {

public boolean isAvailable(RuntimeEdge edge) {
if (edge.hasGuard()) {
LOG.debug("Execute guard: '{}' in model: '{}'", edge.getGuard().getScript(), getModel().getName());
try {
LOG.debug("Guard: '{}' is: '{}'", edge.getGuard().getScript(), getScriptEngine().eval(edge.getGuard().getScript()));
return (Boolean) getScriptEngine().eval(edge.getGuard().getScript());
} catch (ScriptException e) {
LOG.error(e.getMessage());
throw new MachineException(this, e);
LOG.debug("Execute guard: '{}' in edge {}, in model: '{}'", edge.getGuard().getScript(), edge.getName(), getModel().getName());
if (edge.getGuard().getScript().matches("^global\\..*")) {
return globalExecutionEnvironment.eval("js", edge.getGuard().getScript().replaceFirst("^global\\.", "")).asBoolean();
} else {
return executionEnvironment.eval("js", edge.getGuard().getScript()).asBoolean();
}
}
return true;
}

public void execute(Action action) {
LOG.debug("Execute action: '{}' in model: '{}'", action.getScript(), getModel().getName());
try {
getScriptEngine().eval(action.getScript());
} catch (ScriptException e) {
LOG.error(e.getMessage());
throw new MachineException(this, e);
if (action.getScript().matches("^global\\..*")) {
globalExecutionEnvironment.eval("js", action.getScript().replaceFirst("^global\\.", ""));
} else {
executionEnvironment.eval("js", action.getScript());
}
LOG.debug("Data: '{}'", getKeys().toString());
LOG.debug("Data: '{}'", data());
}

public void execute(String name) {
LOG.debug("Execute method: '{}' in model: '{}'", name, getModel().getName());
public void execute(Element element) {
if (!element.hasName()) {
return;
}
LOG.debug("Execute method: '{}' in model: '{}'", element.getName(), getModel().getName());
try {
getClass().getMethod(name); // provoke a NoSuchMethodException exception if the method doesn't exist
getScriptEngine().eval(name + "()");
Method method = getClass().getMethod(element.getName());
method.invoke(this);
} catch (NoSuchMethodException e) {
// ignore, method is not defined in the execution context
} catch (Throwable t) {
executionStatus = ExecutionStatus.FAILED;
LOG.error(t.getMessage());
throw new MachineException(this, t);
}
}

@SuppressWarnings("unchecked")
public Map<String, String> getKeys() {
Map<String, String> keys = new HashMap<>();
List<String> methods = new ArrayList<>();
for (Method method : getClass().getMethods()) {
methods.add(method.getName());
}
if (getBindings(ENGINE_SCOPE).containsKey("nashorn.global")) {
Map<String, Object> global = (Map<String, Object>) getBindings(ENGINE_SCOPE).get("nashorn.global");
for (String key : global.keySet()) {
if (isVariable(key, methods)) {
if (global.get(key) instanceof Double) {
keys.put(key, Long.toString(Math.round((double) global.get(key))));
} else {
keys.put(key, global.get(key).toString());
}
}
}
}

if (getBindings(ENGINE_SCOPE).containsKey("global")) {
Map<String, Object> global = (Map<String, Object>) getBindings(ENGINE_SCOPE).get("global");
for (String key : global.keySet()) {
if (isVariable(key, methods)) {
if (global.get(key) instanceof Double) {
keys.put(key, Long.toString(Math.round((double) global.get(key))));
} else {
keys.put(key, global.get(key).toString());
}
}
}
}

if (keys.isEmpty()) {
for (String key : getBindings(ENGINE_SCOPE).keySet()) {
if (!"nashorn.global".equals(key) && !"global".equals(key) && isVariable(key, methods)) {
Object value = getBindings(ENGINE_SCOPE).get(key);
if (value instanceof Double) {
keys.put(key, Long.toString(Math.round((double) value)));
} else {
keys.put(key, value.toString());
}
}
}
}
return keys;
public Value getAttribute(String name) {
return executionEnvironment.getBindings("js").getMember(name);
}

@SuppressWarnings("unchecked")
public Object getAttribute(String name) {
if (getBindings(ENGINE_SCOPE).containsKey("nashorn.global")) {
Map<String, Object> attributes = (Map<String, Object>) getBindings(ENGINE_SCOPE).get("nashorn.global");
return attributes.get(name);
} else {
return super.getAttribute(name);
}
public void setAttribute(String name, Value value) {
executionEnvironment.getBindings("js").putMember(name, value);
}

@SuppressWarnings("unchecked")
public void setAttribute(String name, Object value) {
if (getBindings(ENGINE_SCOPE).containsKey("global")) {
Map<String, Object> attributes = (Map<String, Object>) getBindings(ENGINE_SCOPE).get("nashorn.global");
attributes.put(name, value);
} else {
super.setAttribute(name, value, ENGINE_SCOPE);
public String data() {
StringBuilder data = new StringBuilder();
for (String member : executionEnvironment.getBindings("js").getMemberKeys()) {
data.append(member)
.append(": ")
.append(executionEnvironment.getBindings("js").getMember(member))
.append(", ");
}
return data.toString();
}

private boolean isVariable(String key, List<String> methods) {
return !"impl".equals(key) && !methods.contains(key) && !"print".equals(key) && !"println".equals(key);
@Override
public void setGlobalExecutionEnvironment(org.graalvm.polyglot.Context globalExecutionEnvironment) {
this.globalExecutionEnvironment = globalExecutionEnvironment;
}
}
Loading