diff --git a/build.gradle b/build.gradle index f7899ae822..5c979afd98 100644 --- a/build.gradle +++ b/build.gradle @@ -3572,6 +3572,7 @@ project(":systemTests") { testapp5 testapp6 testscriptapp1 + testscriptapp2 } def nonModSrcSets = [ @@ -3585,7 +3586,8 @@ project(":systemTests") { sourceSets.testapp4, sourceSets.testapp5, sourceSets.testapp6, - sourceSets.testscriptapp1 + sourceSets.testscriptapp1, + sourceSets.testscriptapp2 ] project.ext.buildModule = false @@ -3685,7 +3687,7 @@ project(":systemTests") { } test.dependsOn(createTestApps); - def modtestapps = [ "testapp2", "testapp3", "testapp4", "testapp5", "testapp6", "testscriptapp1" ] + def modtestapps = [ "testapp2", "testapp3", "testapp4", "testapp5", "testapp6", "testscriptapp1", "testscriptapp2" ] modtestapps.each { testapp -> def testappCapital = testapp.capitalize() def copyTestAppTask = task("copy${testappCapital}", type: Copy) { diff --git a/modules/javafx.fxml/src/main/java/javafx/fxml/FXMLLoader.java b/modules/javafx.fxml/src/main/java/javafx/fxml/FXMLLoader.java index 313f158095..71e3775ed6 100644 --- a/modules/javafx.fxml/src/main/java/javafx/fxml/FXMLLoader.java +++ b/modules/javafx.fxml/src/main/java/javafx/fxml/FXMLLoader.java @@ -63,6 +63,8 @@ import javafx.util.Callback; import javax.script.Bindings; +import javax.script.Compilable; +import javax.script.CompiledScript; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; @@ -1563,7 +1565,12 @@ public void processStartElement() throws IOException { InputStreamReader scriptReader = null; try { scriptReader = new InputStreamReader(location.openStream(), charset); - engine.eval(scriptReader); + if (engine instanceof Compilable) { + ((Compilable) engine).compile(scriptReader).eval(); + } + else { + engine.eval(scriptReader); + } } catch(ScriptException exception) { exception.printStackTrace(); } finally { @@ -1587,7 +1594,13 @@ public void processEndElement() throws IOException { Bindings engineBindings = scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE); engineBindings.put(scriptEngine.FILENAME, location.getPath() + "-script_starting_at_line_" + (getLineNumber() - (int) ((String) value).codePoints().filter(c -> c == '\n').count())); - scriptEngine.eval((String)value); + + if (scriptEngine instanceof Compilable) { + ((Compilable) scriptEngine).compile((String)value).eval(); + } + else { + scriptEngine.eval((String)value); + } } catch (ScriptException exception) { System.err.println(exception.getMessage()); } @@ -1681,11 +1694,23 @@ public void handle(T event) { public final String script; public final ScriptEngine scriptEngine; public final String filename; + public CompiledScript compiledScript; + public boolean isCompiled=false; public ScriptEventHandler(String script, ScriptEngine scriptEngine, String filename) { this.script = script; this.scriptEngine = scriptEngine; this.filename = filename; + if (scriptEngine instanceof Compilable) { + try { + // supply the filename to the scriptEngine engine scope Bindings in case it is needed for compilation + scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE).put(scriptEngine.FILENAME, filename); + this.compiledScript = ((Compilable) scriptEngine).compile(script); + this.isCompiled = true; + } catch (ScriptException exception){ + throw new RuntimeException(exception); + } + } } @Override @@ -1699,7 +1724,12 @@ public void handle(Event event) { localBindings.put(scriptEngine.FILENAME, filename); // Execute the script try { - scriptEngine.eval(script, localBindings); + if (isCompiled) { + compiledScript.eval(localBindings); + } + else { + scriptEngine.eval(script, localBindings); + } } catch (ScriptException exception){ throw new RuntimeException(exception); } diff --git a/tests/system/src/test/java/test/launchertest/ModuleLauncherTest.java b/tests/system/src/test/java/test/launchertest/ModuleLauncherTest.java index 346bf78c4e..273593843e 100644 --- a/tests/system/src/test/java/test/launchertest/ModuleLauncherTest.java +++ b/tests/system/src/test/java/test/launchertest/ModuleLauncherTest.java @@ -44,6 +44,7 @@ private static final String modulePath5 = System.getProperty("launchertest.testapp5.module.path"); private static final String modulePath6 = System.getProperty("launchertest.testapp6.module.path"); private static final String modulePathScript1 = System.getProperty("launchertest.testscriptapp1.module.path"); + private static final String modulePathScript2 = System.getProperty("launchertest.testscriptapp2.module.path"); private static final String moduleName = "mymod"; @@ -277,8 +278,12 @@ public void testModuleFXMLQualOpened() throws Exception { } @Test (timeout = 15000) - public void testFXMLScriptDeployment() throws Exception { + public void testFXMLScriptDeployment1() throws Exception { doTestLaunchModule(modulePathScript1, "myapp1.FXMLScriptDeployment"); } + @Test (timeout = 15000) + public void testFXMLScriptDeployment2() throws Exception { + doTestLaunchModule(modulePathScript2, "myapp2.FXMLScriptDeployment"); + } } diff --git a/tests/system/src/testscriptapp1/resources/mymod/META-INF/services/javax.script.ScriptEngineFactory b/tests/system/src/testscriptapp1/resources/mymod/META-INF/services/javax.script.ScriptEngineFactory index b2ef7c5bac..8baed7c6dd 100644 --- a/tests/system/src/testscriptapp1/resources/mymod/META-INF/services/javax.script.ScriptEngineFactory +++ b/tests/system/src/testscriptapp1/resources/mymod/META-INF/services/javax.script.ScriptEngineFactory @@ -1,2 +1 @@ pseudoScriptEngine.RgfPseudoScriptEngineFactory - diff --git a/tests/system/src/testscriptapp2/java/mymod/module-info.java b/tests/system/src/testscriptapp2/java/mymod/module-info.java new file mode 100644 index 0000000000..004112036c --- /dev/null +++ b/tests/system/src/testscriptapp2/java/mymod/module-info.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +module mymod { + requires javafx.controls; + requires javafx.fxml; + + requires java.scripting; + provides javax.script.ScriptEngineFactory with pseudoScriptEngineCompilable.RgfPseudoScriptEngineCompilableFactory; + exports pseudoScriptEngineCompilable; + exports myapp2; +} diff --git a/tests/system/src/testscriptapp2/java/mymod/myapp2/Constants.java b/tests/system/src/testscriptapp2/java/mymod/myapp2/Constants.java new file mode 100644 index 0000000000..b2aa4b2b61 --- /dev/null +++ b/tests/system/src/testscriptapp2/java/mymod/myapp2/Constants.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package myapp2; // verbatim from myapp6 + +public class Constants { + + public static final int SHOWTIME = 2500; + + // NOTE: these constants must match those in test.launchertest.Constants + public static final int ERROR_NONE = 2; + public static final int ERROR_UNEXPECTED_EXCEPTION = 4; + + public static final int ERROR_ASSERTION_FAILURE = 28; +} diff --git a/tests/system/src/testscriptapp2/java/mymod/myapp2/FXMLScriptDeployment.java b/tests/system/src/testscriptapp2/java/mymod/myapp2/FXMLScriptDeployment.java new file mode 100644 index 0000000000..3c45e9aec0 --- /dev/null +++ b/tests/system/src/testscriptapp2/java/mymod/myapp2/FXMLScriptDeployment.java @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package myapp2; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.TreeMap; + +import javafx.application.Application; +import javafx.application.Platform; +import javafx.event.ActionEvent; +import javafx.fxml.FXMLLoader; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; + +import javax.script.Bindings; +import javax.script.ScriptContext; + +import static myapp2.Constants.*; +import pseudoScriptEngineCompilable.InvocationInfos; +import pseudoScriptEngineCompilable.RgfPseudoScriptEngineCompilable; + +/** + * Modular test application for testing FXML. + * This is launched by ModuleLauncherTest. + */ +public class FXMLScriptDeployment extends Application { + + static boolean bDebug = false; // true; // display invocation list + + /** Runs the application and invokes the tests. + * @param args the command line arguments, if any given the RgfPseudoScriptEngine invocation logs get displayed + * which are used in the asserCorrectInvocations() method + */ + public static void main(String[] args) { + try { + // any argument will cause the bDebug flag to be set to true + if (args.length > 0) { + bDebug = true; + } + new FXMLScriptDeployment().launch(); + // for debugging, allows to study invocation logs in detail + if (bDebug) { dumpEvalInformation(); } + assertCorrectInvocations(); + } catch (AssertionError ex) { + System.err.println("ASSERTION ERROR: caught unexpected exception: " + ex); + ex.printStackTrace(System.err); + System.exit(ERROR_ASSERTION_FAILURE); + } catch (Error | Exception ex) { + System.err.println("ERROR: caught unexpected exception: " + ex); + ex.printStackTrace(System.err); + System.exit(ERROR_UNEXPECTED_EXCEPTION); + } + System.exit(ERROR_NONE); // not in stop() method as we need to run the assertions first + } + + @Override + public void start(Stage mainStage) { + URL fxmlUrl = null; + Parent rootNode = null; + Scene scene = null; + Button btn = null; + try { + fxmlUrl = Util.getURL(FXMLScriptDeployment.class, "demo_02"); + rootNode = FXMLLoader.load(fxmlUrl); + scene = new Scene(rootNode); + btn = (Button) scene.lookup("#idButton"); + } + catch (Exception ioe) { + ioe.printStackTrace(); + System.exit(ERROR_UNEXPECTED_EXCEPTION); + } + // fire three events on the button + btn.fire(); + btn.fireEvent(new ActionEvent()); + btn.fireEvent(new MouseEvent(MouseEvent.MOUSE_CLICKED, + 0, // double x, + 0, // double y, + 0, // double screenX, + 0, // double screenY, + MouseButton.PRIMARY, // MouseButton button, + 0, // int clickCount, + false, // boolean shiftDown, + false, // boolean controlDown, + false, // boolean altDown, + false, // boolean metaDown, + true, // boolean primaryButtonDown, + false, // boolean middleButtonDown, + false, // boolean secondaryButtonDown, + false, // boolean synthesized, + false, // boolean popupTrigger, + false, // boolean stillSincePress, + null // PickResult pickResult + ) + ); + + // mainStage.setScene(scene); + // mainStage.show(); + Platform.exit(); + } + + // show engine invocations with script text and their Bindings + static void dumpEvalInformation() { + System.err.println("\nListing eval() invocation information (invocationList):"); + + Iterator it = RgfPseudoScriptEngineCompilable.getEnginesUsed().iterator(); + while (it.hasNext()) { + RgfPseudoScriptEngineCompilable rpse = it.next(); + ArrayList invocationList = rpse.getInvocationList(); + System.err.println("ScriptEngine: [" + rpse + "]"); + + Iterator itEval = invocationList.iterator(); + int count = 1; + while (itEval.hasNext()) { + System.err.println("\teval() invocation # " + count + ": "); + InvocationInfos entry = itEval.next(); + System.err.println(entry.toDebugFormat("\t\t")); // indentation + count++; + System.err.println(); + } + } + } + + static void assertCorrectInvocations() { + // test only creates one engine for a script controller + Util.assertTrue("exactly one pseudo script engine instance", + RgfPseudoScriptEngineCompilable.getEnginesUsed().size() == 1); + RgfPseudoScriptEngineCompilable rpse = RgfPseudoScriptEngineCompilable.getEnginesUsed().get(0); + + ArrayList invocationList = rpse.getInvocationList(); + Util.assertTrue("exactly nine script engine invocations", invocationList.size() == 9); + + final String FILENAME = "javax.script.filename"; + final String ARGV = "javax.script.argv"; + final String EVENT = "event"; + final String IDBUTTON = "idButton"; + final String IDROOT = "idRoot"; + final String LOCATION = "location"; // always FXML File hosting script controller code + final String RESOURCES = "resources"; // always null in this test + + for (Integer invocation = 1; invocation <= invocationList.size(); invocation++) { + InvocationInfos entry = (InvocationInfos) invocationList.get(invocation - 1); + String script = entry.script; + TreeMap scopes = (TreeMap) entry.bindings; + + TreeMap engineBindings = scopes.get(100); + TreeMap globalBindings = scopes.get(200); + + Object obj = null; + Button btn = null; + + // global Bindings + Util.assertExists(IDROOT + " in global scope Bindings", globalBindings.containsKey(IDROOT)); + obj = globalBindings.get(IDROOT); + Util.assertType(IDROOT, AnchorPane.class, obj); + + Util.assertExists(LOCATION + " in global scope Bindings", globalBindings.containsKey(LOCATION)); + obj = globalBindings.get(LOCATION); + Util.assertType(LOCATION, URL.class, obj); + + Util.assertExists(RESOURCES + " in global scope Bindings", globalBindings.containsKey(RESOURCES)); + obj = globalBindings.get(RESOURCES); + Util.assertNull(RESOURCES,obj); + + if (invocation == 1) { + Util.assertNotExists(IDBUTTON + " in global scope Bindings", globalBindings.containsKey(IDBUTTON)); + } + else { + Util.assertExists(IDBUTTON + " in global scope Bindings", globalBindings.containsKey(IDBUTTON)); + obj = globalBindings.get(IDBUTTON); + Util.assertType(IDBUTTON, Button.class, obj); + btn = (Button) obj; + } + + // engine Bindings + Util.assertExists(FILENAME + " in engine scope Bindings", engineBindings.containsKey(FILENAME)); + if (invocation < 7) { // no event objects, no arguments + Util.assertNotExists(ARGV + " in engine scope Bindings", engineBindings.containsKey(ARGV)); + Util.assertNotExists(EVENT + " in engine scope Bindings", engineBindings.containsKey(EVENT)); + } + else { // this has events on the Button + Util.assertExists(ARGV + " in engine scope Bindings", engineBindings.containsKey(ARGV)); + Object[] argv = (Object[]) engineBindings.get(ARGV); + + Util.assertExists(EVENT + " in engine scope Bindings", engineBindings.containsKey(EVENT)); + obj = engineBindings.get(EVENT); + + Util.assertSame("argv[0] == event", argv[0], obj); + + if (invocation == 9) { + Util.assertType(EVENT, MouseEvent.class, obj); + MouseEvent ev = (MouseEvent) obj; + Util.assertSame("MouseEvent.getSource() == btn", ev.getSource(), btn); + Util.assertSame("MouseEvent.MOUSE_CLICKED", MouseEvent.MOUSE_CLICKED, ev.getEventType()); + } else { + Util.assertType(EVENT, ActionEvent.class, obj); + ActionEvent ev = (ActionEvent) obj; + Util.assertSame("ActionEvent.getSource() == btn", ev.getSource(), btn); + } + } + + // check filename and script + String filename = (String) engineBindings.get(FILENAME); + boolean ok = false; + switch (invocation) { + case 1: + Util.assertEndsWith ("demo_02_topscript.sqtmc", filename); + Util.assertStartsWith("RgfPseudoCompiledScript.eval(): RgfPseudoCompiledScript=[demo_02_topscript.sqtmc file - pseudo script", script); + break; + + case 2: + Util.assertEndsWith ("demo_02_middlescript.sqtmc", filename); + Util.assertStartsWith("RgfPseudoCompiledScript.eval(): RgfPseudoCompiledScript=[demo_02_middlescript.sqtmc file - pseudo script", script); + break; + + case 3: + Util.assertEndsWith("demo_02.fxml-script_starting_at_line_52", filename); + Util.assertStartsWith("RgfPseudoCompiledScript.eval(): RgfPseudoCompiledScript=[demo_02.fxml embedded script sqtmc - line # 52", script); + break; + + case 4: + Util.assertEndsWith ("demo_02_bottomscript.sqtmc", filename); + Util.assertStartsWith("RgfPseudoCompiledScript.eval(): RgfPseudoCompiledScript=[demo_02_bottomscript.sqtmc file - pseudo script", script); + break; + + case 5: + Util.assertEndsWith("demo_02.fxml-script_starting_at_line_56", filename); + Util.assertStartsWith("RgfPseudoCompiledScript.eval(): RgfPseudoCompiledScript=[something (line # 56)", script); + break; + + case 6: + Util.assertEndsWith("demo_02.fxml-script_starting_at_line_59", filename); + Util.assertStartsWith("RgfPseudoCompiledScript.eval(): RgfPseudoCompiledScript=[demo_02.fxml (line # 59):", script); + break; + + case 7: // same as case 8 (same button clicked) + Util.assertEndsWith("demo_02.fxml-onAction_attribute_in_element_ending_at_line_46", filename); + Util.assertStartsWith("RgfPseudoCompiledScript.eval(Bindings bindings): RgfPseudoCompiledScript=[demo_02.fxml embedded event - ActionEvent - line # 45 -", script); + break; + + case 8: // same as case 7 (same button clicked) + Util.assertEndsWith("demo_02.fxml-onAction_attribute_in_element_ending_at_line_46", filename); + Util.assertStartsWith("RgfPseudoCompiledScript.eval(Bindings bindings): RgfPseudoCompiledScript=[demo_02.fxml embedded event - ActionEvent - line # 45 -", script); + break; + + case 9: + Util.assertEndsWith("demo_02.fxml-onMouseClicked_attribute_in_element_ending_at_line_46", filename); + Util.assertStartsWith("RgfPseudoCompiledScript.eval(Bindings bindings): RgfPseudoCompiledScript=[demo_02.fxml embedded event - MouseClicked - line # 44", script); + break; + } + } + } +} diff --git a/tests/system/src/testscriptapp2/java/mymod/myapp2/Util.java b/tests/system/src/testscriptapp2/java/mymod/myapp2/Util.java new file mode 100644 index 0000000000..a1c6cfd646 --- /dev/null +++ b/tests/system/src/testscriptapp2/java/mymod/myapp2/Util.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package myapp2; + +import java.net.URL; + +public class Util { + + public static final URL getURL(Class clazz, String name) { + final String theName = name + ".fxml"; + final URL fxmlFile = clazz.getResource(theName); + if (fxmlFile == null) { + throw new AssertionError("(getURL()) unable to open: " + theName); + } + return fxmlFile; + } + + public static void assertNull(String message, Object o) { + if (o != null) { + throw new AssertionError("(assertNull) " + message + ", expected null object, but was non-null"); + } + } + + public static void assertNotNull(Object o) { + if (o == null) { + throw new AssertionError("(assertNotNull) expected non-null object, but was null"); + } + } + + public static void assertEndsWith(String expected, String observed) { + if ((expected == null && observed != null) || !observed.endsWith(expected)) { + throw new AssertionError("(assertEndsWith) " + "expected: <" + expected + "> but was: <" + observed + ">"); + } + } + + public static void assertStartsWith(String expected, String observed) { + if ((expected == null && observed != null) || !observed.startsWith(expected)) { + throw new AssertionError("(assertStartsWith) " + "expected: <" + expected + "> but was: <" + observed + ">"); + } + } + + + public static void assertSame(String message, Object expected, Object observed) { + if (expected != observed) { + throw new AssertionError("(assertSame) "+ message + ", expected: <" + expected + "> but was: <" + observed + ">"); + } + } + + public static void assertTrue(String message, boolean cond) { + if (!cond) { + throw new AssertionError("(assertTrue): " + message); + } + } + + public static void assertFalse(String message, boolean cond) { + if (cond) { + throw new AssertionError("(assertFalse): " + message); + } + } + + public static void assertExists(String message, boolean cond) { + if (!cond) { + throw new AssertionError("(assertExists): " + message); + } + } + + public static void assertNotExists(String message, boolean cond) { + if (cond) { + throw new AssertionError("(assertNotExists): " + message); + } + } + + public static void assertType(String message, Class clz, Object obj) { + if (obj == null) { + throw new AssertionError("(assertType): " + message+": \"obj\" is null"); + } + else if (clz == null) { + throw new AssertionError("(assertType): " + message+": \"clz\" is null"); + } + + if (! clz.isInstance(obj)) { + throw new AssertionError("(assertType): " + message + + ", object " + obj + + " is not an instance of class " + + clz + " -> " + clz.getName() + "]"); + } + } + + private Util() { + } +} diff --git a/tests/system/src/testscriptapp2/java/mymod/pseudoScriptEngineCompilable/InvocationInfos.java b/tests/system/src/testscriptapp2/java/mymod/pseudoScriptEngineCompilable/InvocationInfos.java new file mode 100644 index 0000000000..dd62c8562a --- /dev/null +++ b/tests/system/src/testscriptapp2/java/mymod/pseudoScriptEngineCompilable/InvocationInfos.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package pseudoScriptEngineCompilable; + +import javax.script.Bindings; +import javax.script.ScriptContext; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Set; +import java.util.TreeMap; + + +/** Stores PseudoScriptEngine related invocation information for asserting and debugging. */ +public class InvocationInfos { + public String script; + public TreeMap> bindings; + public Instant dateTime; + + InvocationInfos(String script, ScriptContext context) { + this.dateTime = Instant.now(); + this.script = script; + this.bindings = new TreeMap(); + // get and save each Bindings + for (Integer scope : context.getScopes()) { + Bindings binding = context.getBindings(scope); + bindings.put(scope, binding == null ? new TreeMap() : new TreeMap(binding)); + } + } + + /** + * Creates and returns a string having all information formatted to ease debugging. + * @return string formatted to ease debugging + */ + public String toDebugFormat(String indentation) { + StringBuilder sb = new StringBuilder(); + String indent = (indentation == null ? "\t\t" : indentation); + sb.append(indent).append("at: [").append(dateTime.toString()).append("]\n"); + sb.append(indent).append("script: [").append(script) .append("]\n"); + + for (Integer scope : (Set) bindings.keySet()) { + sb.append(indent).append("Bindings for scope # ").append(scope); + if (scope == 100) sb.append(" (ENGINE_SCOPE):"); + else if (scope == 200) sb.append(" (GLOBAL_SCOPE):"); + else sb.append(':'); + sb.append('\n'); + + TreeMap treeMap = bindings.get(scope); + for (String k : (Set) treeMap.keySet()) { + sb.append(indent).append("\t[").append(k).append("]:\t[").append(treeMap.get(k)).append("]\n"); + } + } + return sb.toString(); + } +} + diff --git a/tests/system/src/testscriptapp2/java/mymod/pseudoScriptEngineCompilable/RgfPseudoCompiledScript.java b/tests/system/src/testscriptapp2/java/mymod/pseudoScriptEngineCompilable/RgfPseudoCompiledScript.java new file mode 100644 index 0000000000..a310ddf9ed --- /dev/null +++ b/tests/system/src/testscriptapp2/java/mymod/pseudoScriptEngineCompilable/RgfPseudoCompiledScript.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package pseudoScriptEngineCompilable; + +import javax.script.Bindings; +import javax.script.CompiledScript; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptException; + +public class RgfPseudoCompiledScript extends CompiledScript +{ + String code = null; + ScriptEngine scriptEngine = null; + + RgfPseudoCompiledScript(String code, ScriptEngine scriptEngine) { + this.code = code; + this.scriptEngine = scriptEngine; + } + + public Object eval(Bindings bindings) throws ScriptException { + return scriptEngine.eval("RgfPseudoCompiledScript.eval(Bindings bindings): " + code, bindings); + } + + public Object eval(ScriptContext context) throws ScriptException { + return scriptEngine.eval("RgfPseudoCompiledScript.eval(ScriptContext context): " + code, context); + } + + public Object eval() throws ScriptException { + return scriptEngine.eval("RgfPseudoCompiledScript.eval(): " + code ); + } + + public ScriptEngine getEngine() { + return scriptEngine; + } +} + diff --git a/tests/system/src/testscriptapp2/java/mymod/pseudoScriptEngineCompilable/RgfPseudoScriptEngineCompilable.java b/tests/system/src/testscriptapp2/java/mymod/pseudoScriptEngineCompilable/RgfPseudoScriptEngineCompilable.java new file mode 100644 index 0000000000..4c2fdb1c44 --- /dev/null +++ b/tests/system/src/testscriptapp2/java/mymod/pseudoScriptEngineCompilable/RgfPseudoScriptEngineCompilable.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package pseudoScriptEngineCompilable; + +import javax.script.Bindings; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; + +import javax.script.Compilable; +import javax.script.CompiledScript; + +import javax.script.AbstractScriptEngine; +import javax.script.SimpleScriptContext; +import javax.script.SimpleBindings; + +import java.util.ArrayList; +import java.util.Set; +import java.util.TreeMap; +import java.io.Reader; +import java.io.BufferedReader; +import java.io.IOException; + +import java.time.Instant; + +public class RgfPseudoScriptEngineCompilable extends AbstractScriptEngine implements Compilable { + static final boolean bDebug = false; // true; + + /** Allows to log and access the ScriptEngine instances with their evalDataList. */ + static final ArrayList enginesUsed = new ArrayList(); + public static ArrayList getEnginesUsed() { + return enginesUsed; + } + + public RgfPseudoScriptEngineCompilable() { + enginesUsed.add(this); + } + + public ScriptEngineFactory getFactory() { + return new RgfPseudoScriptEngineCompilableFactory(); + } + + /** ArrayList of eval() (invocation) information. */ + final ArrayList invocationList = new ArrayList(); + + /** + * Returns ArrayList of eval() (invocation) information. + * @return invocationList + */ + public ArrayList getInvocationList() { + return invocationList; + } + + public Bindings createBindings() { + return new SimpleBindings(); + } + + public Object eval(Reader reader, ScriptContext context) { + if (bDebug) System.err.println("[debug: " + this + ".eval(Reader,ScriptContext), ScriptContext=" + context + "]"); + + return eval(readReader(reader), context); + } + + public Object eval(String script, ScriptContext context) { + if (bDebug) System.err.print("[debug: " + this + ".eval(String,ScriptContext), ScriptContext=" + context + "]"); + + // create copies of the Bindings for later inspection as they may + // get reused and changed on each eval() invocation + TreeMap bindings = new TreeMap(); + for (Integer scope : context.getScopes()) { + Bindings binding = context.getBindings(scope); + bindings.put(scope, binding == null ? new TreeMap() : new TreeMap(binding)); + } + invocationList.add(new InvocationInfos(script,context)); + if (bDebug) System.err.println(" | invocationList.size()=" + invocationList.size()); + return invocationList; + } + + public CompiledScript compile(Reader script) { + return compile (readReader(script)); + } + + public CompiledScript compile(String script) { + String code = "RgfPseudoCompiledScript=[" + script + "]"; + RgfPseudoCompiledScript rpcs = new RgfPseudoCompiledScript(code, this); + return rpcs; + } + + String readReader(Reader reader) { + if (reader == null) { + return ""; + } + + BufferedReader bufferedReader = new BufferedReader(reader); + StringBuilder sb = new StringBuilder(); + // caters for possible IOException in read() and close() + try { + try { + char[] charBuffer = new char[1024]; + int r = 0; + + while ((r = bufferedReader.read(charBuffer)) != -1) { + sb.append(charBuffer, 0, r); + } + } finally { + bufferedReader.close(); + } + } catch (IOException ioe) { + throw new RuntimeException(ioe.getMessage(), ioe); + } + return sb.toString(); + } +} + diff --git a/tests/system/src/testscriptapp2/java/mymod/pseudoScriptEngineCompilable/RgfPseudoScriptEngineCompilableFactory.java b/tests/system/src/testscriptapp2/java/mymod/pseudoScriptEngineCompilable/RgfPseudoScriptEngineCompilableFactory.java new file mode 100644 index 0000000000..acf09f54ff --- /dev/null +++ b/tests/system/src/testscriptapp2/java/mymod/pseudoScriptEngineCompilable/RgfPseudoScriptEngineCompilableFactory.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package pseudoScriptEngineCompilable; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; + +import java.lang.StringBuilder; +import java.util.Arrays; +import java.util.List; + + +public class RgfPseudoScriptEngineCompilableFactory implements ScriptEngineFactory { + static final String ENGINE_NAME = "RgfPseudoScriptLanguageCompilable (SQTMC) 1.0.0"; + static final String SHORT_ENGINE_NAME = "sqtmc"; + static final String ENGINE_VERSION = "100.20200228"; + static final List EXTENSIONS = Arrays.asList("sqtmc", "SQTMC"); + static final String LANGUAGE_NAME = "RgfPseudoScriptLanguageCompilable"; + static final String LANGUAGE_VERSION = "1.0.0.100.20200228"; + static final List MIME_TYPES = Arrays.asList("text/sqtmc", "application/x-sqtmc"); + static final String THREADING = "MULTITHREADED"; + static final List ENGINE_NAMES = Arrays.asList(SHORT_ENGINE_NAME, "RgfPseudoSLCompilable"); + + public String getEngineName() { + return ENGINE_NAME; + } + + public String getEngineVersion() { + return ENGINE_VERSION; + } + + public List getExtensions() { + return EXTENSIONS; + } + + public String getLanguageName() { + return LANGUAGE_NAME; + } + + public String getLanguageVersion() { + return LANGUAGE_VERSION; + } + + public String getName() { + return ENGINE_NAME; + } + + public String getMethodCallSyntax(String obj, String m, String... args) { + return "obj~(m, ...) /* ooRexx style */ "; + } + + public List getMimeTypes() { + return MIME_TYPES; + } + + public List getNames() { + return ENGINE_NAMES; + } + + public String getOutputStatement(String toDisplay) { + String tmpDisplay = toStringLiteral(toDisplay); + return "say " + tmpDisplay + " /* Rexx style (duplicate quotes within string) */ "; + } + + String toStringLiteral(String toDisplay) { + if (toDisplay == null) { + return "\"\""; + } + return '"' + toDisplay.replace("\"","\"\"") + '"'; + } + + public Object getParameter(final String key) { + switch (key) { + case "THREADING": + return THREADING; + + case ScriptEngine.NAME: + return SHORT_ENGINE_NAME; + + case ScriptEngine.ENGINE: + return ENGINE_NAME; + + case ScriptEngine.ENGINE_VERSION: + return ENGINE_VERSION; + + case ScriptEngine.LANGUAGE: + return LANGUAGE_NAME; + + case ScriptEngine.LANGUAGE_VERSION: + return LANGUAGE_VERSION; + + default: + return null; + } + } + + public String getProgram(String... statements) { + if (statements == null) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < statements.length; i++) { + if (statements[i] == null) { + sb.append("\tsay 'null'; /* Rexx style */ \n"); + } + else { + sb.append("\t" + statements[i] + ";\n"); + } + } + return sb.toString(); + } + + public ScriptEngine getScriptEngine() { + return new RgfPseudoScriptEngineCompilable(); + } +} diff --git a/tests/system/src/testscriptapp2/resources/mymod/META-INF/services/javax.script.ScriptEngineFactory b/tests/system/src/testscriptapp2/resources/mymod/META-INF/services/javax.script.ScriptEngineFactory new file mode 100644 index 0000000000..018b592990 --- /dev/null +++ b/tests/system/src/testscriptapp2/resources/mymod/META-INF/services/javax.script.ScriptEngineFactory @@ -0,0 +1 @@ +pseudoScriptEngineCompilable.RgfPseudoScriptEngineCompilableFactory diff --git a/tests/system/src/testscriptapp2/resources/mymod/myapp2/demo_02.fxml b/tests/system/src/testscriptapp2/resources/mymod/myapp2/demo_02.fxml new file mode 100644 index 0000000000..1f2e9756d0 --- /dev/null +++ b/tests/system/src/testscriptapp2/resources/mymod/myapp2/demo_02.fxml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + +