From 22798b185b1d8b5e48db4740f3ed66702f099bbe Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 24 Apr 2023 16:10:29 -0500 Subject: [PATCH 01/20] Bump to next development cycle Signed-off-by: Curtis Rueden --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a258a05..4ff0d57 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ scripting-scala - 0.3.0-SNAPSHOT + 0.3.1-SNAPSHOT SciJava Scripting: Scala JSR-223-compliant Scala scripting language plugin. From 80d25aee73a96fc620b8d2d8d2cf484b7d3a5f67 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 24 Apr 2023 16:17:36 -0500 Subject: [PATCH 02/20] Add JSR-223 declaration Closes #1. --- .../resources/META-INF/services/javax.script.ScriptEngineFactory | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/main/resources/META-INF/services/javax.script.ScriptEngineFactory diff --git a/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory b/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory new file mode 100644 index 0000000..45807c2 --- /dev/null +++ b/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory @@ -0,0 +1 @@ +org.scijava.plugins.scripting.scala.ScalaScriptLanguage From 32d4c5f7cbb45c17fd3c0657ee7ac678abe8a326 Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Tue, 9 May 2023 17:17:04 -0400 Subject: [PATCH 03/20] Fixed: Internal ServiceConfigurationError when Script Engine is created #8 --- .../scripting/scala/ScalaScriptLanguage.java | 26 +++----- .../ScalaAdaptedScriptEngineFactory.scala | 66 +++++++++++++++++++ 2 files changed, 76 insertions(+), 16 deletions(-) create mode 100644 src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngineFactory.scala diff --git a/src/main/java/org/scijava/plugins/scripting/scala/ScalaScriptLanguage.java b/src/main/java/org/scijava/plugins/scripting/scala/ScalaScriptLanguage.java index 035cbed..21b24f0 100644 --- a/src/main/java/org/scijava/plugins/scripting/scala/ScalaScriptLanguage.java +++ b/src/main/java/org/scijava/plugins/scripting/scala/ScalaScriptLanguage.java @@ -6,13 +6,13 @@ * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -29,17 +29,17 @@ package org.scijava.plugins.scripting.scala; -import javax.script.ScriptEngine; - import org.scijava.log.LogService; import org.scijava.plugin.Parameter; import org.scijava.plugin.Plugin; import org.scijava.script.AdaptedScriptLanguage; import org.scijava.script.ScriptLanguage; +import javax.script.ScriptEngine; + /** * An adapter of the Scala interpreter to the SciJava scripting interface. - * + * * @author Curtis Rueden * @author Keith Schulze * @author Johannes Schindelin @@ -49,16 +49,10 @@ @Plugin(type = ScriptLanguage.class, name = "Scala") public class ScalaScriptLanguage extends AdaptedScriptLanguage { - @Parameter - private LogService log; - - public ScalaScriptLanguage() { - super("scala"); - } + @Parameter + private LogService log; - @Override - public ScriptEngine getScriptEngine() { - final ScriptEngine eng = new dotty.tools.repl.ScriptEngine(); - return new ScalaAdaptedScriptEngine(eng); - } + public ScalaScriptLanguage() { + super(new ScalaAdaptedScriptEngineFactory()); + } } diff --git a/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngineFactory.scala b/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngineFactory.scala new file mode 100644 index 0000000..16ebfb3 --- /dev/null +++ b/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngineFactory.scala @@ -0,0 +1,66 @@ +/* + * #%L + * JSR-223-compliant Scala scripting language plugin. + * %% + * Copyright (C) 2013 - 2023 SciJava developers. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.plugins.scripting.scala + +import org.scijava.plugins.scripting.scala.ScalaAdaptedScriptEngine + +import java.util +import javax.script.{ScriptEngine, ScriptEngineFactory} + +/** + * A factory for ScalaAdaptedScriptEngine. + * + * @author Jarek Sacha + * @see ScriptEngineFactory + */ +class ScalaAdaptedScriptEngineFactory extends ScriptEngineFactory: + + private val factory = new dotty.tools.repl.ScriptEngine.Factory + + /** + * Returns an instance of the `ScalaAdaptedScriptEngine`. + * A new instance is returned. + * + * @return A new `ScalaAdaptedScriptEngine` instance. + */ + override def getScriptEngine = new ScalaAdaptedScriptEngine(factory.getScriptEngine) + + override def getEngineName: String = factory.getEngineName + override def getEngineVersion: String = factory.getEngineVersion + override def getExtensions: util.List[String] = factory.getExtensions + override def getLanguageName: String = factory.getLanguageName + override def getLanguageVersion: String = factory.getLanguageVersion + override def getMimeTypes: util.List[String] = factory.getMimeTypes + override def getNames: util.List[String] = factory.getNames + override def getOutputStatement(toDisplay: String): String = factory.getOutputStatement(toDisplay) + override def getParameter(key: String): AnyRef = factory.getParameter(key) + override def getMethodCallSyntax(obj: String, m: String, args: String*): String = + factory.getMethodCallSyntax(obj, m, args*) + override def getProgram(statements: String*): String = factory.getProgram(statements*) From 33f3b1b19cb88711a0db2cf2cf5a2ed26726aec3 Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Tue, 9 May 2023 17:24:20 -0400 Subject: [PATCH 04/20] Add test for issue #5: "eval should sometimes make an entry in the ENGINE_SCOPE bindings" --- .../plugins/scripting/scala/ScalaTest.java | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java b/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java index fee834e..addd2dd 100644 --- a/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java +++ b/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java @@ -6,13 +6,13 @@ * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -35,12 +35,12 @@ import org.scijava.script.ScriptModule; import org.scijava.script.ScriptService; +import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.SimpleScriptContext; import java.io.StringWriter; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * Scala unit tests. @@ -210,7 +210,7 @@ public void testLocals() throws Exception { assertEquals("17", engine.eval("hello").toString()); assertEquals("17", engine.get("hello").toString()); -// With Scala 3.2.2 cannot reset bindings correctly, will skip the ret of the test +// With Scala 3.2.2 cannot reset bindings correctly, will skip the rest of the test // final Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); // bindings.clear(); // assertNull(engine.get("hello")); @@ -266,4 +266,24 @@ public void testImportsRetained() throws Exception { assertEquals(result, result2); } } + + /** + * Test for issue #5: "eval should sometimes make an entry in the ENGINE_SCOPE bindings" + */ + @Test + public void issue5() { + try (final Context context = new Context(ScriptService.class)) { + + final ScriptService scriptService = context.getService(ScriptService.class); + final ScriptEngine engine = scriptService.getLanguageByName("scala").getScriptEngine(); + + assertFalse(engine.getBindings(ScriptContext.ENGINE_SCOPE).containsKey("ten")); + engine.put("ten", 10); + assertEquals(10, engine.get("ten")); + assertTrue(engine.getBindings(ScriptContext.ENGINE_SCOPE).containsKey("ten")); + + engine.put("twenty", 20); + assertEquals(20, engine.get("twenty")); + } + } } From ac8d086f6ea2b71fc06efbd4fed9301f83bd4688 Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Tue, 9 May 2023 17:42:41 -0400 Subject: [PATCH 05/20] Bump scala-maven-plugin to latest version 4.8.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4ff0d57..374f2f6 100644 --- a/pom.xml +++ b/pom.xml @@ -104,7 +104,7 @@ net.alchim31.maven scala-maven-plugin - 4.8.0 + 4.8.1 -unchecked From 2773bce67c929b313a2b4adcff9df32eaf26c9ca Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Sat, 13 May 2023 12:21:24 -0400 Subject: [PATCH 06/20] Ignore IDEA files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 08c660b..a4fd068 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ # Maven # target/ + +# IntelliJ IDEA # +/.idea/ From eb277c88482a2aa7f3cbe98e2895168fd17fd19a Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Sat, 13 May 2023 15:32:12 -0400 Subject: [PATCH 07/20] Fixed: ScriptREPL is not usable - problem with variables named as Scala keywords #9 --- .../scala/ScalaAdaptedScriptEngine.scala | 124 +++++++++++++++--- .../plugins/scripting/scala/ScalaTest.java | 41 ++++++ 2 files changed, 145 insertions(+), 20 deletions(-) diff --git a/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngine.scala b/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngine.scala index 8f39b4e..148feb8 100644 --- a/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngine.scala +++ b/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngine.scala @@ -1,3 +1,32 @@ +/* + * #%L + * JSR-223-compliant Scala scripting language plugin. + * %% + * Copyright (C) 2013 - 2023 SciJava developers. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + package org.scijava.plugins.scripting.scala import java.io.{OutputStream, Reader, StringWriter, Writer} @@ -5,6 +34,7 @@ import javax.script.* import scala.collection.mutable import scala.jdk.CollectionConverters.* import scala.util.Try +import scala.util.matching.Regex /** * Adapted Scala ScriptEngine @@ -43,26 +73,30 @@ class ScalaAdaptedScriptEngine(engine: ScriptEngine) extends AbstractScriptEngin bindings <- Option(context.getBindings(scope)).map(_.asScala) // bindings in context can be null yield { for (name, value) <- bindings yield { - value match - case v: Double => s"val $name : Double = ${v}d" - case v: Float => s"val $name : Float = ${v}f" - case v: Long => s"val $name : Long = ${v}L" - case v: Int => s"val $name : Int = $v" - case v: Char => s"val $name : Char = '$v'" - case v: Short => s"val $name : Short = $v" - case v: Byte => s"val $name : Byte = $v" - case v: Boolean => s"val $name : Int = $v" - case o: AnyRef if isValidVariableName(name) => - _transfer = o - val typeName = Option(o).map(_.getClass.getCanonicalName).getOrElse("AnyRef") - s""" - |val $name : $typeName = { - | val t = org.scijava.plugins.scripting.scala.ScalaAdaptedScriptEngine._transfer - | t.asInstanceOf[$typeName] - |}""".stripMargin - case _: AnyRef => "" // ignore if name is not a variable - case v: Unit => - throw ScriptException(s"Unsupported type for bind variable $name: ${v.getClass}") + if isValidVariableName(name) then + val validName = addBackticksIfNeeded(name) + value match + case v: Double => s"val $validName : Double = ${v}d" + case v: Float => s"val $validName : Float = ${v}f" + case v: Long => s"val $validName : Long = ${v}L" + case v: Int => s"val $validName : Int = $v" + case v: Char => s"val $validName : Char = '$v'" + case v: Short => s"val $validName : Short = $v" + case v: Byte => s"val $validName : Byte = $v" + case v: Boolean => s"val $validName : Int = $v" + case v: AnyRef => + _transfer = v + val typeName = Option(v).map(_.getClass.getCanonicalName).getOrElse("AnyRef") + val validTypeName = addBackticksIfNeeded(typeName) + s""" + |val $validName : $validTypeName = { + | val t = org.scijava.plugins.scripting.scala.ScalaAdaptedScriptEngine._transfer + | t.asInstanceOf[$validTypeName] + |}""".stripMargin + case v: Unit => + throw ScriptException(s"Unsupported type for bind variable $name: ${v.getClass}") + else + "" // ignore if name is not a variable } } @@ -126,10 +160,60 @@ end ScalaAdaptedScriptEngine object ScalaAdaptedScriptEngine: private lazy val variableNamePattern = """^[a-zA-Z_$][a-zA-Z_$0-9]*$""".r + private val scala3Keywords = Seq( + "abstract", + "case", + "catch", + "class", + "def", + "do", + "else", + "enum", + "export", + "extends", + "false", + "final", + "finally", + "for", + "given", + "if", + "implicit", + "import", + "lazy", + "match", + "new", + "null", + "object", + "override", + "package", + "private", + "protected", + "return", + "sealed", + "super", + "then", + "throw", + "trait", + "true", + "try", + "type", + "val", + "var", + "while", + "with", + "yield" + ) /** Do not use externally despite it is declared public. IT is public so it is accessible from scripts */ // noinspection ScalaWeakerAccess var _transfer: Object = _ private def isValidVariableName(name: String): Boolean = variableNamePattern.matches(name) + + private[scala] def addBackticksIfNeeded(referenceName: String): String = + referenceName + .split("\\.") + .map(n => if scala3Keywords.contains(n) then s"`$n`" else n) + .mkString(".") + end ScalaAdaptedScriptEngine diff --git a/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java b/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java index addd2dd..80ab6a0 100644 --- a/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java +++ b/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java @@ -37,6 +37,7 @@ import javax.script.ScriptContext; import javax.script.ScriptEngine; +import javax.script.ScriptException; import javax.script.SimpleScriptContext; import java.io.StringWriter; @@ -286,4 +287,44 @@ public void issue5() { assertEquals(20, engine.get("twenty")); } } + + /** + * Test for issue #9: "ScriptREPL is not usable - problem with variables named as Scala keywords" + */ + @Test + public void issue9() { + try (final Context context = new Context(ScriptService.class)) { + + final ScriptService scriptService = context.getService(ScriptService.class); + final ScriptEngine engine = scriptService.getLanguageByName("scala").getScriptEngine(); + + final String name = "object"; + final int expectedValue = 7; + + assertFalse(engine.getBindings(ScriptContext.ENGINE_SCOPE).containsKey(name)); + engine.put(name, expectedValue); + assertTrue(engine.getBindings(ScriptContext.ENGINE_SCOPE).containsKey(name)); + assertEquals(expectedValue, engine.get(name)); + + final String script1 = "val abc = `" + name + "`"; + try { + engine.eval(script1); + } catch (ScriptException e) { + fail("Failed to execute valid script: \"" + script1 + "\""); + } + + final Object r = engine.get("abc"); + assertEquals(expectedValue, r); + + } + } + + @Test + public void addBackticksIfNeededTest() { + final String expected = "org.scijava.`object`.DefaultObjectService"; + final String name = "org.scijava.object.DefaultObjectService"; + final String correctedName = ScalaAdaptedScriptEngine.addBackticksIfNeeded(name); + + assertEquals(expected, correctedName); + } } From 14c826f390159ae88689c96084658082c4f71a07 Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Sat, 13 May 2023 23:58:29 -0400 Subject: [PATCH 08/20] Add Scala code formatting configuration --- .scalafmt.conf | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .scalafmt.conf diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..36dc7b5 --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,14 @@ +# [[https://github.com/scalameta/scalafmt]] + +version = 3.7.3 + +runner.dialect = scala3 + +preset = IntelliJ +align.preset = more +maxColumn = 120 +docstrings.style = Asterisk +docstrings.blankFirstLine = yes +docstrings.wrap = no +importSelectors = singleLine +newlines.source = keep \ No newline at end of file From 730a93b3ec41ab32dae689d57e897a4ae808a493 Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Sun, 14 May 2023 19:39:41 -0400 Subject: [PATCH 09/20] Fixed: MissingCoreLibraryException when running script in FIJI #10 --- .../tools/repl/SciJavaScriptEngine.scala | 104 ++++++++++++++++++ .../ScalaAdaptedScriptEngineFactory.scala | 12 +- 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/main/scala/dotty/tools/repl/SciJavaScriptEngine.scala diff --git a/src/main/scala/dotty/tools/repl/SciJavaScriptEngine.scala b/src/main/scala/dotty/tools/repl/SciJavaScriptEngine.scala new file mode 100644 index 0000000..f2f215a --- /dev/null +++ b/src/main/scala/dotty/tools/repl/SciJavaScriptEngine.scala @@ -0,0 +1,104 @@ +package dotty.tools.repl + +// This file is based on dotty.tools.repl.ScriptEngine in Scala Compiler +// It is using some package private classes from `dotty.tools.repl` +// Only necessary changes are made, no corrects existing compiler warnings, etc. + +import dotty.tools.dotc +import dotty.tools.dotc.core.StdNames.str + +import java.io.{Reader, StringWriter} +import javax.script.{AbstractScriptEngine, Bindings, ScriptContext, ScriptEngineFactory, ScriptException, SimpleBindings, ScriptEngine as JScriptEngine} +import scala.language.unsafeNulls + +/** + * A JSR 223 (Scripting API) compatible wrapper around the REPL for improved + * interoperability with software that supports it. + * + * It works by instantiating a new script engine through the script engine manager. + * The script engine provides a eval method to evaluate scripts in string form. + * Example use: + * + * val m = new javax.script.ScriptEngineManager() + * val e = m.getEngineByName("scala") + * println(e.eval("42")) + */ +class SciJavaScriptEngine(classPath: String) extends AbstractScriptEngine { + new javax.script.ScriptEngineManager() + private val driver = new ReplDriver( + Array( + "-classpath", + classPath, + "-usejavacp", + "-color:never", + "-Xrepl-disable-display" + ), + Console.out, + None + ) + private val rendering = new Rendering(Some(getClass.getClassLoader)) + private var state: State = driver.initialState + + def getFactory: ScriptEngineFactory = new ScriptEngine.Factory + + def createBindings: Bindings = new SimpleBindings + + /* Evaluate with the given context. */ + @throws[ScriptException] + def eval(script: String, context: ScriptContext): Object = { + val vid = state.valIndex + state = driver.run(script)(using state) + val oid = state.objectIndex + Class.forName(s"${Rendering.REPL_WRAPPER_NAME_PREFIX}$oid", true, rendering.classLoader()(using state.context)) + .getDeclaredMethods.find(_.getName == s"${str.REPL_RES_PREFIX}$vid") + .map(_.invoke(null)) + .getOrElse(null) + } + + @throws[ScriptException] + def eval(reader: Reader, context: ScriptContext): Object = eval(stringFromReader(reader), context) + + private val buffer = new Array[Char](8192) + + def stringFromReader(in: Reader) = { + val out = new StringWriter + var n = in.read(buffer) + while (n > -1) { + out.write(buffer, 0, n) + n = in.read(buffer) + } + in.close + out.toString + } +} + +object SciJavaScriptEngine { + import java.util.Arrays + import scala.util.Properties + class Factory(classPath: String) extends ScriptEngineFactory { + def getEngineName = "Scala REPL for SciJava" + def getEngineVersion = "3.0" + def getExtensions = Arrays.asList("scala") + def getLanguageName = "Scala" + def getLanguageVersion = Properties.versionString + def getMimeTypes = Arrays.asList("application/x-scala") + def getNames = Arrays.asList("scala") + + def getMethodCallSyntax(obj: String, m: String, args: String*) = s"$obj.$m(${args.mkString(", ")})" + + def getOutputStatement(toDisplay: String) = s"""print("$toDisplay")""" + + def getParameter(key: String): Object = key match { + case JScriptEngine.ENGINE => getEngineName + case JScriptEngine.ENGINE_VERSION => getEngineVersion + case JScriptEngine.LANGUAGE => getLanguageName + case JScriptEngine.LANGUAGE_VERSION => getLanguageVersion + case JScriptEngine.NAME => getNames.get(0) + case _ => null + } + + def getProgram(statements: String*) = statements.mkString("; ") + + def getScriptEngine: JScriptEngine = new SciJavaScriptEngine(classPath) + } +} diff --git a/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngineFactory.scala b/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngineFactory.scala index 16ebfb3..a0aaf52 100644 --- a/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngineFactory.scala +++ b/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngineFactory.scala @@ -31,6 +31,7 @@ package org.scijava.plugins.scripting.scala import org.scijava.plugins.scripting.scala.ScalaAdaptedScriptEngine +import java.net.URLClassLoader import java.util import javax.script.{ScriptEngine, ScriptEngineFactory} @@ -42,7 +43,7 @@ import javax.script.{ScriptEngine, ScriptEngineFactory} */ class ScalaAdaptedScriptEngineFactory extends ScriptEngineFactory: - private val factory = new dotty.tools.repl.ScriptEngine.Factory + private val factory = new dotty.tools.repl.SciJavaScriptEngine.Factory(classPath) /** * Returns an instance of the `ScalaAdaptedScriptEngine`. @@ -64,3 +65,12 @@ class ScalaAdaptedScriptEngineFactory extends ScriptEngineFactory: override def getMethodCallSyntax(obj: String, m: String, args: String*): String = factory.getMethodCallSyntax(obj, m, args*) override def getProgram(statements: String*): String = factory.getProgram(statements*) + + /** + * Retrieves the current classpath as a string. + */ + def classPath: String = ClassLoader.getSystemClassLoader match + case cl: URLClassLoader => + cl.getURLs.map(_.getPath).mkString(System.getProperty("path.separator")) + case _ => + System.getProperty("java.class.path") From 8fd596409c32727ba418f809d73cc1d8a8743b51 Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Sun, 14 May 2023 20:25:44 -0400 Subject: [PATCH 10/20] Make possible to bind/transfer multiple AnyRef variables AnyRef bound variables maintain reference to transfer variable. Even if they are `val`'s. Need to use separate transfer variable for each bound variable. This implementation is not production ready. It never releases transfer variables and limits their number. Good enough for scripts that are called only limited times. --- .../scala/ScalaAdaptedScriptEngine.scala | 102 ++++++++++++++---- .../plugins/scripting/scala/ScalaTest.java | 41 +++++++ 2 files changed, 122 insertions(+), 21 deletions(-) diff --git a/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngine.scala b/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngine.scala index 148feb8..4854b4a 100644 --- a/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngine.scala +++ b/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngine.scala @@ -34,6 +34,7 @@ import javax.script.* import scala.collection.mutable import scala.jdk.CollectionConverters.* import scala.util.Try +import scala.util.control.NonFatal import scala.util.matching.Regex /** @@ -67,12 +68,12 @@ class ScalaAdaptedScriptEngine(engine: ScriptEngine) extends AbstractScriptEngin // Scala 3.2.2 ignores bindings, emulate binding using setup script // Create a line with variable declaration for each binding item - val lines = + val transfers: mutable.Seq[Seq[String]] = for scope <- context.getScopes.asScala bindings <- Option(context.getBindings(scope)).map(_.asScala) // bindings in context can be null yield { - for (name, value) <- bindings yield { + for (name, value) <- bindings.toSeq yield { if isValidVariableName(name) then val validName = addBackticksIfNeeded(name) value match @@ -85,12 +86,22 @@ class ScalaAdaptedScriptEngine(engine: ScriptEngine) extends AbstractScriptEngin case v: Byte => s"val $validName : Byte = $v" case v: Boolean => s"val $validName : Int = $v" case v: AnyRef => - _transfer = v - val typeName = Option(v).map(_.getClass.getCanonicalName).getOrElse("AnyRef") + val transferIndex = BindingSupport.nextTransferIndex + BindingSupport.__transfer(transferIndex) = v + val typeName = Option(v) + .map { oo => + val tt: Array[_] = oo.getClass.getTypeParameters + tt.foreach(t => log(s"${oo.getClass.getCanonicalName} TYPE PARAM: ${t.getClass.getName}")) + val p = tt.map(_ => "_").mkString("[", ",", "]") + val n = oo.getClass.getCanonicalName + if tt.nonEmpty then n + p else n + } + .getOrElse("AnyRef") val validTypeName = addBackticksIfNeeded(typeName) s""" |val $validName : $validTypeName = { - | val t = org.scijava.plugins.scripting.scala.ScalaAdaptedScriptEngine._transfer + | val t = org.scijava.plugins.scripting.scala.ScalaAdaptedScriptEngine.BindingSupport + | ._transfer($transferIndex) | t.asInstanceOf[$validTypeName] |}""".stripMargin case v: Unit => @@ -100,26 +111,45 @@ class ScalaAdaptedScriptEngine(engine: ScriptEngine) extends AbstractScriptEngin } } - val script = lines + val script = transfers .flatten .filter(_.nonEmpty) .mkString("\n") - if script.nonEmpty then - evalInner(script, context) + evalInner(script, context) end emulateBinding - private def evalInner(script: String, context: ScriptContext) = - class WriterOutputStream(w: Writer) extends OutputStream: - override def write(b: Int): Unit = w.write(b) + private def evalInner(script: String, context: ScriptContext): AnyRef = + log( + s""" + |LOG[evalInner] script + |BEGIN + |--------------------------- + |$script + |--------------------------- + |END + |""".stripMargin + ) + if script.trim.isEmpty then + log("LOG[evalInner] script is empty, skipping evaluation") + null + else + class WriterOutputStream(w: Writer) extends OutputStream: + override def write(b: Int): Unit = w.write(b) - // Redirect output to writes provided by context - Console.withOut(WriterOutputStream(context.getWriter)) { - Console.withErr(WriterOutputStream(context.getErrorWriter)) { - engine.eval(script, context) - } - } + try + // Redirect output to writes provided by context + Console.withOut(WriterOutputStream(context.getWriter)) { + Console.withErr(WriterOutputStream(context.getErrorWriter)) { + engine.eval(script, context) + } + } + catch + case NonFatal(t) => + log(s"LOG[evalInner] in eval: $t") + t.printStackTrace() + throw t private def stringFromReader(in: Reader) = val out = new StringWriter() @@ -156,9 +186,15 @@ class ScalaAdaptedScriptEngine(engine: ScriptEngine) extends AbstractScriptEngin value end get + private def log(msg: String): Unit = { + if ScalaAdaptedScriptEngine.DEBUG then + Console.out.println(msg) + } + end ScalaAdaptedScriptEngine object ScalaAdaptedScriptEngine: + private val DEBUG: Boolean = false private lazy val variableNamePattern = """^[a-zA-Z_$][a-zA-Z_$0-9]*$""".r private val scala3Keywords = Seq( "abstract", @@ -204,10 +240,6 @@ object ScalaAdaptedScriptEngine: "yield" ) - /** Do not use externally despite it is declared public. IT is public so it is accessible from scripts */ - // noinspection ScalaWeakerAccess - var _transfer: Object = _ - private def isValidVariableName(name: String): Boolean = variableNamePattern.matches(name) private[scala] def addBackticksIfNeeded(referenceName: String): String = @@ -216,4 +248,32 @@ object ScalaAdaptedScriptEngine: .map(n => if scala3Keywords.contains(n) then s"`$n`" else n) .mkString(".") + /** + * Temporary support for implementing binding in the script engine. + * It has limited capacity and does not free memory. + * Access to storage is public, so it is visible from scripts. + */ + //noinspection ScalaWeakerAccess + object BindingSupport: + private val MaxTransfers: Int = 1024 * 1024 + + /** + * Do not use externally despite it is declared public. + * It is public so it is accessible from scripts that are used to emulate variable binding + */ + // noinspection ScalaWeakerAccess,ScalaUnusedSymbol + def _transfer: Seq[AnyRef] = __transfer.toSeq + + private[scala] val __transfer: mutable.ListBuffer[AnyRef] = mutable.ListBuffer.empty[AnyRef] + + private var lastTransferIndex = -1 + + private[scala] def nextTransferIndex: Int = + if lastTransferIndex + 1 >= MaxTransfers then + throw new IllegalStateException("ScalaAdaptedScriptEngine: maximum transfer limit reached") + + lastTransferIndex += 1 + __transfer.append(null) + lastTransferIndex + end ScalaAdaptedScriptEngine diff --git a/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java b/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java index 80ab6a0..65fb75e 100644 --- a/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java +++ b/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java @@ -40,6 +40,8 @@ import javax.script.ScriptException; import javax.script.SimpleScriptContext; import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; import static org.junit.Assert.*; @@ -183,6 +185,28 @@ public void testPutString() throws Exception { } } + @Test + public void testPut3Strings() throws Exception { + // Check that multiple AnyRef variable are bound correctly + try (final Context context = new Context(ScriptService.class)) { + final ScriptEngine engine = getEngine(context); + final String expected1 = "Ala ma kota"; + final String expected2 = "Kot ma Ale"; + final String expected3 = "Reksio nie ma butow"; + engine.put("v1", expected1); + engine.put("v2", expected2); + engine.put("v3", expected3); + final String script = "\n" + + "val o1:String = v1\n" + + "val o2:String = v2\n" + + "val o3:String = v3\n"; + engine.eval(script); + assertEquals(expected1, engine.get("o1")); + assertEquals(expected2, engine.get("o2")); + assertEquals(expected3, engine.get("o3")); + } + } + @Test public void testPutInt() throws Exception { @@ -198,6 +222,23 @@ public void testPutInt() throws Exception { } } + @Test + public void testPutSeqInt() throws Exception { + try (final Context context = new Context(ScriptService.class)) { + final ScriptEngine engine = getEngine(context); + + final List expected = new ArrayList<>(); + expected.add(7); + expected.add(13); + expected.add(-1); + engine.put("v", expected); + final String script = "val v1 = v"; + engine.eval(script); + final Object actual = engine.get("v1"); + assertEquals(expected, actual); + } + } + @Test public void testLocals() throws Exception { From 4d3c0bccb929b5ef8d23b9235711502e4dd1c9d4 Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Sun, 14 May 2023 20:34:02 -0400 Subject: [PATCH 11/20] Bump pom-scijava to 35.0.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 374f2f6..dd7d799 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.scijava pom-scijava - 34.1.0 + 35.0.0 From 183fb447095d948389ce17132791291e79a6b4a8 Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Sun, 14 May 2023 20:46:42 -0400 Subject: [PATCH 12/20] Add "sc" to Scala file extensions --- src/main/scala/dotty/tools/repl/SciJavaScriptEngine.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/dotty/tools/repl/SciJavaScriptEngine.scala b/src/main/scala/dotty/tools/repl/SciJavaScriptEngine.scala index f2f215a..429639e 100644 --- a/src/main/scala/dotty/tools/repl/SciJavaScriptEngine.scala +++ b/src/main/scala/dotty/tools/repl/SciJavaScriptEngine.scala @@ -78,7 +78,7 @@ object SciJavaScriptEngine { class Factory(classPath: String) extends ScriptEngineFactory { def getEngineName = "Scala REPL for SciJava" def getEngineVersion = "3.0" - def getExtensions = Arrays.asList("scala") + def getExtensions = Arrays.asList("scala", "sc") def getLanguageName = "Scala" def getLanguageVersion = Properties.versionString def getMimeTypes = Arrays.asList("application/x-scala") From 265c42b51972d75b33a49455e5551494fec707f7 Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Sun, 14 May 2023 20:53:49 -0400 Subject: [PATCH 13/20] Copyright blurbs updated --- .../scijava/plugins/scripting/scala/ScalaScriptLanguage.java | 4 ++-- .../java/org/scijava/plugins/scripting/scala/ScalaTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/scijava/plugins/scripting/scala/ScalaScriptLanguage.java b/src/main/java/org/scijava/plugins/scripting/scala/ScalaScriptLanguage.java index 21b24f0..65deb83 100644 --- a/src/main/java/org/scijava/plugins/scripting/scala/ScalaScriptLanguage.java +++ b/src/main/java/org/scijava/plugins/scripting/scala/ScalaScriptLanguage.java @@ -6,13 +6,13 @@ * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE diff --git a/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java b/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java index 65fb75e..1d4e14a 100644 --- a/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java +++ b/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java @@ -6,13 +6,13 @@ * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE From 29dd569942c37b8061379e3867f812275de21002 Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Sun, 14 May 2023 20:56:28 -0400 Subject: [PATCH 14/20] Bump to next development cycle Signed-off-by: Jarek Sacha --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dd7d799..cdff448 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ scripting-scala - 0.3.1-SNAPSHOT + 0.3.2-SNAPSHOT SciJava Scripting: Scala JSR-223-compliant Scala scripting language plugin. From 653b60131c48ae18796d79bc63421c5a9c14513a Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Tue, 23 May 2023 17:27:53 -0400 Subject: [PATCH 15/20] Fixed: Test on Windows fail with: "InvalidPathException: Illegal char <:> at index 2: /C:/Users/..." #11 --- .../scripting/scala/ScalaAdaptedScriptEngineFactory.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngineFactory.scala b/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngineFactory.scala index a0aaf52..967baf9 100644 --- a/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngineFactory.scala +++ b/src/main/scala/org/scijava/plugins/scripting/scala/ScalaAdaptedScriptEngineFactory.scala @@ -32,6 +32,7 @@ package org.scijava.plugins.scripting.scala import org.scijava.plugins.scripting.scala.ScalaAdaptedScriptEngine import java.net.URLClassLoader +import java.nio.file.Paths import java.util import javax.script.{ScriptEngine, ScriptEngineFactory} @@ -71,6 +72,8 @@ class ScalaAdaptedScriptEngineFactory extends ScriptEngineFactory: */ def classPath: String = ClassLoader.getSystemClassLoader match case cl: URLClassLoader => - cl.getURLs.map(_.getPath).mkString(System.getProperty("path.separator")) + cl.getURLs + .map(url => Paths.get(url.toURI).toString) + .mkString(System.getProperty("path.separator")) case _ => System.getProperty("java.class.path") From a5c714e94adcba657263b63bd9b3c86b997cf503 Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Tue, 23 May 2023 17:29:27 -0400 Subject: [PATCH 16/20] Bump to Scala 3.3.0 LTS #12 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cdff448..614a0ed 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ sign,deploy-to-scijava - 3.2.2 + 3.3.0 From 80e5a3216427cb4a3f2e61e05b60bd46e101f910 Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Tue, 23 May 2023 17:30:47 -0400 Subject: [PATCH 17/20] Bump pom-scijava to 35.1.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 614a0ed..2311621 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.scijava pom-scijava - 35.0.0 + 35.1.0 From 64681bf00789bf0950f760a694cec1b741fe32e3 Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Tue, 23 May 2023 21:45:50 -0400 Subject: [PATCH 18/20] Bump to next development cycle Signed-off-by: Jarek Sacha --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2311621..ac96131 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ scripting-scala - 0.3.2-SNAPSHOT + 0.3.3-SNAPSHOT SciJava Scripting: Scala JSR-223-compliant Scala scripting language plugin. From 8c733d7e09a9c419962d22b1e80b9d31c0c77251 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 1 Apr 2024 22:48:02 -0500 Subject: [PATCH 19/20] POM: tidy up configuration blocks And factor out version strings to properties. --- pom.xml | 66 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/pom.xml b/pom.xml index ac96131..6d7955b 100644 --- a/pom.xml +++ b/pom.xml @@ -96,15 +96,39 @@ sign,deploy-to-scijava 3.3.0 + 4.8.1 + + + + org.scijava + scijava-common + + + + + org.scala-lang + scala3-compiler_3 + ${scala.version} + + + - - - - net.alchim31.maven - scala-maven-plugin - 4.8.1 + + + junit + junit + test + + + + + + + net.alchim31.maven + scala-maven-plugin + ${scala-maven-plugin.version} -unchecked @@ -132,31 +156,7 @@ - - - - - - - - org.scijava - scijava-common - - - - - org.scala-lang - scala3-compiler_3 - ${scala.version} - - - - - - - junit - junit - test - - + + + From 3e38b3323ed657054e617f94cf70c19bf288ee54 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 1 Apr 2024 22:48:22 -0500 Subject: [PATCH 20/20] Happy New Year 2024 --- LICENSE.txt | 2 +- src/main/java/org/scijava/plugins/scripting/scala/Main.java | 2 +- .../scijava/plugins/scripting/scala/ScalaScriptLanguage.java | 2 +- .../java/org/scijava/plugins/scripting/scala/ScalaTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 08db1a8..8082288 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2013 - 2023, SciJava developers. +Copyright (c) 2013 - 2024, SciJava developers. All rights reserved. Redistribution and use in source and binary forms, with or without modification, diff --git a/src/main/java/org/scijava/plugins/scripting/scala/Main.java b/src/main/java/org/scijava/plugins/scripting/scala/Main.java index 2d729cc..e272aa8 100644 --- a/src/main/java/org/scijava/plugins/scripting/scala/Main.java +++ b/src/main/java/org/scijava/plugins/scripting/scala/Main.java @@ -2,7 +2,7 @@ * #%L * JSR-223-compliant Scala scripting language plugin. * %% - * Copyright (C) 2013 - 2023 SciJava developers. + * Copyright (C) 2013 - 2024 SciJava developers. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/java/org/scijava/plugins/scripting/scala/ScalaScriptLanguage.java b/src/main/java/org/scijava/plugins/scripting/scala/ScalaScriptLanguage.java index 65deb83..1d863e8 100644 --- a/src/main/java/org/scijava/plugins/scripting/scala/ScalaScriptLanguage.java +++ b/src/main/java/org/scijava/plugins/scripting/scala/ScalaScriptLanguage.java @@ -2,7 +2,7 @@ * #%L * JSR-223-compliant Scala scripting language plugin. * %% - * Copyright (C) 2013 - 2023 SciJava developers. + * Copyright (C) 2013 - 2024 SciJava developers. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java b/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java index 1d4e14a..504bfa2 100644 --- a/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java +++ b/src/test/java/org/scijava/plugins/scripting/scala/ScalaTest.java @@ -2,7 +2,7 @@ * #%L * JSR-223-compliant Scala scripting language plugin. * %% - * Copyright (C) 2013 - 2023 SciJava developers. + * Copyright (C) 2013 - 2024 SciJava developers. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: