3636
3737/**
3838 * This class runs a VStar script.
39+ *
40+ * JavaScript is executed by the standalone OpenJDK Nashorn engine
41+ * (nashorn-core on the classpath). Nashorn was removed from the JDK
42+ * in Java 15, which caused Tool -> Run Script... to fail (issue #601).
3943 */
4044public class ScriptRunner {
4145
@@ -54,11 +58,38 @@ public class ScriptRunner {
5458 * Constructor
5559 */
5660 public ScriptRunner (boolean fromFileChooser ) {
57- manager = new ScriptEngineManager ();
58- jsEngine = manager .getEngineByName ("javascript" );
59- compilable = (Compilable ) jsEngine ;
60- bindings = jsEngine .getBindings (ScriptContext .GLOBAL_SCOPE );
61- bindings .put ("vstar" , VStarScriptingAPI .getInstance ());
61+ try {
62+ // Use this class's loader so the standalone Nashorn engine (required
63+ // since the JDK removed it in Java 15) is found when running from a jar.
64+ manager = new ScriptEngineManager (ScriptRunner .class .getClassLoader ());
65+ jsEngine = manager .getEngineByName ("javascript" );
66+ if (jsEngine == null ) {
67+ jsEngine = manager .getEngineByName ("nashorn" );
68+ }
69+ if (jsEngine == null ) {
70+ error = "No JavaScript engine is available. "
71+ + "Nashorn was removed from the JDK in Java 15; "
72+ + "ensure nashorn-core and its ASM jars are on the classpath." ;
73+ } else {
74+ compilable = (Compilable ) jsEngine ;
75+ bindings = jsEngine .getBindings (ScriptContext .GLOBAL_SCOPE );
76+ if (bindings == null ) {
77+ bindings = jsEngine .getBindings (ScriptContext .ENGINE_SCOPE );
78+ }
79+ if (bindings == null ) {
80+ bindings = jsEngine .createBindings ();
81+ jsEngine .setBindings (bindings , ScriptContext .ENGINE_SCOPE );
82+ }
83+ bindings .put ("vstar" , VStarScriptingAPI .getInstance ());
84+ // Nashorn provides print() but not println(); some sample scripts use println.
85+ jsEngine .eval ("if (typeof println === 'undefined') { println = print; }" );
86+ }
87+ } catch (Throwable t ) {
88+ jsEngine = null ;
89+ compilable = null ;
90+ error = "Failed to initialise JavaScript engine: "
91+ + t .getLocalizedMessage ();
92+ }
6293 if (fromFileChooser ) {
6394 scriptFileChooser = new JFileChooser ();
6495 }
@@ -75,6 +106,13 @@ public static ScriptRunner getInstance() {
75106 * Run script from a chosen file.
76107 */
77108 public void runScript () {
109+ if (jsEngine == null || compilable == null ) {
110+ MessageBox .showErrorDialog ("Script Error" ,
111+ error != null ? error
112+ : "No JavaScript engine is available." );
113+ return ;
114+ }
115+
78116 File scriptFile = null ;
79117
80118 int returnVal = scriptFileChooser .showOpenDialog (DocumentManager
@@ -92,6 +130,13 @@ public void runScript(File scriptFile) {
92130 FileReader reader = null ;
93131
94132 try {
133+ if (jsEngine == null || compilable == null ) {
134+ MessageBox .showErrorDialog ("Script Error" ,
135+ error != null ? error
136+ : "No JavaScript engine is available." );
137+ return ;
138+ }
139+
95140 setError (null );
96141 setWarning (null );
97142
@@ -161,6 +206,20 @@ public void setWarning(String warning) {
161206 * The value to which to bind.
162207 */
163208 public void bind (String name , Object value ) {
164- bindings .put (name , value );
209+ if (bindings != null ) {
210+ bindings .put (name , value );
211+ }
212+ }
213+
214+ /**
215+ * Evaluate a JavaScript snippet in the current engine context.
216+ * Package-private for unit tests.
217+ */
218+ Object eval (String script ) throws ScriptException {
219+ if (jsEngine == null || compilable == null ) {
220+ throw new ScriptException (error != null ? error
221+ : "No JavaScript engine is available." );
222+ }
223+ return compilable .compile (script ).eval ();
165224 }
166225}
0 commit comments