Skip to content

Commit 167d606

Browse files
committed
Merge branch 'nashorn'
Java 8 removed the Rhino implementation of JavaScript in favor of the newer, faster, better, shinier, and backwards-incompatible Nashorn implementation. Fortunately, Nashorn provides a backwards compatibility script called mozilla_compat.js which we can source to regain the most common Mozilla-specific behavior. Fixes imagej/ImageJ#116.
2 parents 965b097 + 8c05f67 commit 167d606

File tree

2 files changed

+86
-14
lines changed

2 files changed

+86
-14
lines changed

src/main/java/org/scijava/plugins/scripting/javascript/JavaScriptScriptLanguage.java

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,47 @@ public JavaScriptScriptLanguage() {
5959
super("javascript");
6060
}
6161

62+
// -- JavaScriptScriptLanguage methods --
63+
64+
/**
65+
* Returns true iff this script language is using the <a
66+
* href="http://openjdk.java.net/projects/nashorn/">Nashorn</a> JavaScript
67+
* engine. This is the case for Java 8.
68+
*/
69+
public boolean isNashorn() {
70+
return getEngineName().contains("Nashorn");
71+
}
72+
73+
/**
74+
* Returns true iff this script language is using the <a
75+
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino"
76+
* >Rhino</a> JavaScript engine. This is the case for Java 6 and Java 7.
77+
*/
78+
public boolean isRhino() {
79+
return getEngineName().contains("Rhino");
80+
}
81+
82+
// -- ScriptEngineFactory methods --
83+
6284
@Override
6385
public ScriptEngine getScriptEngine() {
6486
final ScriptEngine engine = super.getScriptEngine();
6587
try {
66-
engine.eval("function load(path) {\n"
67-
+ " importClass(Packages.sun.org.mozilla.javascript.internal.Context);\n"
68-
+ " importClass(Packages.java.io.FileReader);\n"
69-
+ " var cx = Context.getCurrentContext();\n"
70-
+ " cx.evaluateReader(this, new FileReader(path), path, 1, null);\n"
71-
+ "}");
88+
if (isNashorn()) {
89+
// for Rhino compatibility, importClass and importPackage in particular
90+
engine.eval("load(\"nashorn:mozilla_compat.js\");");
91+
}
92+
if (isRhino()) {
93+
// for the load function, which is somehow otherwise unavailable (?)
94+
engine.eval("function load(path) {\n" +
95+
" importClass(Packages." + contextClass(engine) + ");\n" +
96+
" importClass(Packages.java.io.FileReader);\n" +
97+
" var cx = Context.getCurrentContext();\n" +
98+
" cx.evaluateReader(this, new FileReader(path), path, 1, null);\n" +
99+
"}");
100+
}
72101
}
73-
catch (ScriptException e) {
102+
catch (final ScriptException e) {
74103
e.printStackTrace();
75104
}
76105
return engine;
@@ -112,4 +141,24 @@ public Object decode(final Object object) {
112141
return null;
113142
}
114143

144+
// -- Helper methods --
145+
146+
private String contextClass(final ScriptEngine engine) {
147+
if (isNashorn()) return "jdk.nashorn.internal.runtime.Context";
148+
149+
final String engineClassName = engine.getClass().getName();
150+
151+
if (isRhino()) {
152+
if (engineClassName.startsWith("com.sun.")) {
153+
// assume JDK-flavored Rhino script engine
154+
return "sun.org.mozilla.javascript.internal.Context";
155+
}
156+
// assume vanilla Mozilla-flavored Rhino script engine
157+
return "org.mozilla.javascript.Context";
158+
}
159+
160+
throw new UnsupportedOperationException("Unknown JavaScript flavor: " +
161+
engineClassName);
162+
}
163+
115164
}

src/test/java/org/scijava/plugins/scripting/javascript/JavaScriptTest.java

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,7 @@ public void testBasic() throws InterruptedException, ExecutionException,
6767
final Context context = new Context(ScriptService.class);
6868
final ScriptService scriptService = context.getService(ScriptService.class);
6969
final String script = "$x = 1 + 2;";
70-
// NB: Some JVMs return Integer, others Double. Let's be careful here.
71-
final ScriptModule m = scriptService.run("add.js", script, true).get();
72-
final Number result = (Number) m.getReturnValue();
73-
assertEquals(3.0, result.doubleValue(), 0.0);
70+
assertResult(3.0, scriptService.run("add.js", script, true).get());
7471
}
7572

7673
@Test
@@ -80,7 +77,9 @@ public void testLocals() throws ScriptException {
8077

8178
final ScriptLanguage language = scriptService.getLanguageByExtension("js");
8279
final ScriptEngine engine = language.getScriptEngine();
83-
assertTrue(engine.getClass().getName().endsWith(".RhinoScriptEngine"));
80+
final String engineClassName = engine.getClass().getName();
81+
assertTrue(engineClassName.endsWith(".RhinoScriptEngine") ||
82+
engineClassName.endsWith(".NashornScriptEngine"));
8483
engine.put("$hello", 17);
8584
assertEquals("17", engine.eval("$hello").toString());
8685
assertEquals("17", engine.get("$hello").toString());
@@ -122,8 +121,32 @@ public void testLoad() throws IOException, InterruptedException, ExecutionExcept
122121
final Context context = new Context(ScriptService.class);
123122
final ScriptService scriptService = context.getService(ScriptService.class);
124123
final String script = "load('" + tmp.getPath() + "'); three();";
125-
final Object result = scriptService.run("three.js", script, false).get().getReturnValue();
126-
assertEquals(4.0, (Number) result);
124+
assertResult(4.0, scriptService.run("three.js", script, false).get());
127125
assertTrue(tmp.delete());
128126
}
127+
128+
@Test
129+
public void testJavaAPI() throws InterruptedException, ExecutionException,
130+
IOException, ScriptException
131+
{
132+
final Context context = new Context(ScriptService.class);
133+
final ScriptService scriptService = context.getService(ScriptService.class);
134+
final String script = //
135+
"importClass(Packages.java.util.ArrayList);\n" //
136+
+ "var list = new ArrayList();\n" //
137+
+ "list.add(3);\n" //
138+
+ "list.add(5.5);\n" //
139+
+ "list.add(7);\n" //
140+
+ "list.get(1);\n";
141+
assertResult(5.5, scriptService.run("javaAPI.js", script, true).get());
142+
}
143+
144+
// -- Helper methods --
145+
146+
private void assertResult(final double expected, final ScriptModule m) {
147+
// NB: Some JVMs return Integer, others Double. Let's be careful here.
148+
final Number result = (Number) m.getReturnValue();
149+
assertEquals(expected, result.doubleValue(), 0.0);
150+
}
151+
129152
}

0 commit comments

Comments
 (0)