[Git][java-team/rhino][master] 4 commits: Backported the JSR 223 script engine to use Rhino as a replacement for Nashorn with OpenJDK 17
Emmanuel Bourg
gitlab at salsa.debian.org
Mon Feb 8 10:40:10 GMT 2021
Emmanuel Bourg pushed to branch master at Debian Java Maintainers / rhino
Commits:
7ef1f3ff by Emmanuel Bourg at 2021-02-08T11:39:12+01:00
Backported the JSR 223 script engine to use Rhino as a replacement for Nashorn with OpenJDK 17
- - - - -
5956b137 by Emmanuel Bourg at 2021-02-08T11:39:23+01:00
Standards-Version updated to 4.5.1
- - - - -
a825f850 by Emmanuel Bourg at 2021-02-08T11:39:27+01:00
Switch to debhelper level 13
- - - - -
1b685589 by Emmanuel Bourg at 2021-02-08T11:39:35+01:00
Upload to unstable
- - - - -
6 changed files:
- debian/changelog
- â debian/compat
- debian/control
- + debian/patches/script-engine.patch
- debian/patches/series
- debian/rules
Changes:
=====================================
debian/changelog
=====================================
@@ -1,3 +1,13 @@
+rhino (1.7.7.2-3) unstable; urgency=medium
+
+ * Team upload.
+ * Backported the JSR 223 script engine to use Rhino as a replacement
+ for Nashorn with OpenJDK 17
+ * Standards-Version updated to 4.5.1
+ * Switch to debhelper level 13
+
+ -- Emmanuel Bourg <ebourg at apache.org> Mon, 08 Feb 2021 11:39:29 +0100
+
rhino (1.7.7.2-2) UNRELEASED; urgency=medium
* Team upload.
=====================================
debian/compat deleted
=====================================
@@ -1 +0,0 @@
-10
=====================================
debian/control
=====================================
@@ -7,11 +7,11 @@ Uploaders:
Jakub Adam <jakub.adam at ktknet.cz>
Build-Depends:
ant,
- debhelper (>= 10),
+ debhelper-compat (= 13),
default-jdk,
javahelper,
maven-repo-helper
-Standards-Version: 4.5.0
+Standards-Version: 4.5.1
Vcs-Git: https://salsa.debian.org/java-team/rhino.git
Vcs-Browser: https://salsa.debian.org/java-team/rhino
Homepage: http://www.mozilla.org/rhino/
=====================================
debian/patches/script-engine.patch
=====================================
@@ -0,0 +1,1303 @@
+From f195514ffee1b759ba088883732e77b026b3a694 Mon Sep 17 00:00:00 2001
+From: Gregory Brail <gregbrail at google.com>
+Date: Fri, 5 Jun 2020 14:38:28 -0700
+Subject: [PATCH] Implement standard Java ScriptEngine
+
+This is not based on the now-removed JDK code but instead does a
+few things in a more modern way. See the comments for supported
+parameters (you can set language and optimization level via
+properties) and built-in functions (only print is supported right now.)
+
+This is built into a separate JAR called "rhino-engine" because
+otherwise, including it in any Java 8 JDK would break scripts that
+are expecting to see Nashorn instead.
+---
+ .circleci/config.yml | 2 +-
+ build.gradle | 86 ++++-
+ .../services/javax.script.ScriptEngineFactory | 1 +
+ .../javascript/engine/BindingsObject.java | 60 +++
+ .../mozilla/javascript/engine/Builtins.java | 59 +++
+ .../engine/RhinoCompiledScript.java | 33 ++
+ .../engine/RhinoInvocationHandler.java | 25 ++
+ .../javascript/engine/RhinoScriptEngine.java | 363 ++++++++++++++++++
+ .../engine/RhinoScriptEngineFactory.java | 140 +++++++
+ .../tests/scriptengine/BuiltinsTest.java | 54 +++
+ .../tests/scriptengine/FactoryTest.java | 56 +++
+ .../tests/scriptengine/InvocableTest.java | 158 ++++++++
+ .../tests/scriptengine/ScriptEngineTest.java | 276 +++++++++++++
+ 13 files changed, 1305 insertions(+), 8 deletions(-)
+ create mode 100644 src/META-INF/services/javax.script.ScriptEngineFactory
+ create mode 100644 src/org/mozilla/javascript/engine/BindingsObject.java
+ create mode 100644 src/org/mozilla/javascript/engine/Builtins.java
+ create mode 100644 src/org/mozilla/javascript/engine/RhinoCompiledScript.java
+ create mode 100644 src/org/mozilla/javascript/engine/RhinoInvocationHandler.java
+ create mode 100644 src/org/mozilla/javascript/engine/RhinoScriptEngine.java
+ create mode 100644 src/org/mozilla/javascript/engine/RhinoScriptEngineFactory.java
+ create mode 100644 testsrc/org/mozilla/javascript/tests/scriptengine/BuiltinsTest.java
+ create mode 100644 testsrc/org/mozilla/javascript/tests/scriptengine/FactoryTest.java
+ create mode 100644 testsrc/org/mozilla/javascript/tests/scriptengine/InvocableTest.java
+ create mode 100644 testsrc/org/mozilla/javascript/tests/scriptengine/ScriptEngineTest.java
+
+--- /dev/null
++++ b/src/META-INF/services/javax.script.ScriptEngineFactory
+@@ -0,0 +1 @@
++org.mozilla.javascript.engine.RhinoScriptEngineFactory
+--- /dev/null
++++ b/src/org/mozilla/javascript/engine/BindingsObject.java
+@@ -0,0 +1,60 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++package org.mozilla.javascript.engine;
++
++import org.mozilla.javascript.Context;
++import org.mozilla.javascript.Scriptable;
++import org.mozilla.javascript.ScriptableObject;
++import javax.script.Bindings;
++
++/**
++ * This class makes the Bindings object into a Scriptable. That way, we can query and modify
++ * the contents of the Bindings on demand.
++ */
++public class BindingsObject
++ extends ScriptableObject {
++ private final Bindings bindings;
++
++ BindingsObject(Bindings bindings) {
++ if (bindings == null) {
++ throw new IllegalArgumentException("Bindings must not be null");
++ }
++ this.bindings = bindings;
++ }
++
++ @Override
++ public String getClassName() {
++ return "BindingsObject";
++ }
++
++ @Override
++ public Object get(String name, Scriptable start) {
++ Object ret = bindings.get(name);
++ if (ret == null) {
++ return Scriptable.NOT_FOUND;
++ }
++ return Context.jsToJava(ret, Object.class);
++ }
++
++ @Override
++ public void put(String name, Scriptable start, Object value) {
++ bindings.put(name, Context.javaToJS(value, start));
++ }
++
++ @Override
++ public void delete(String name) {
++ bindings.remove(name);
++ }
++
++ @Override
++ public boolean has(String name, Scriptable start) {
++ return bindings.containsKey(name);
++ }
++
++ @Override
++ public Object[] getIds() {
++ return bindings.keySet().toArray();
++ }
++}
+--- /dev/null
++++ b/src/org/mozilla/javascript/engine/Builtins.java
+@@ -0,0 +1,59 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++package org.mozilla.javascript.engine;
++
++import java.io.IOException;
++import java.io.OutputStreamWriter;
++import java.io.Writer;
++import javax.script.ScriptContext;
++import org.mozilla.javascript.Context;
++import org.mozilla.javascript.Function;
++import org.mozilla.javascript.ScriptRuntime;
++import org.mozilla.javascript.Scriptable;
++import org.mozilla.javascript.ScriptableObject;
++
++/**
++ * <p>
++ * This class defines the following built-in functions for the RhinoScriptEngine.
++ * </p>
++ * <ul>
++ * <li>print(arg, arg, ...): Write each argument, concatenated to the ScriptEngine's
++ * "standard output" as a string.</li>
++ * </ul>
++ */
++public class Builtins {
++
++ static final Object BUILTIN_KEY = new Object();
++
++ private Writer stdout;
++
++ void register(Context cx, ScriptableObject scope, ScriptContext sc) {
++ if (sc.getWriter() == null) {
++ stdout = new OutputStreamWriter(System.out);
++ } else {
++ stdout = sc.getWriter();
++ }
++
++ scope.defineFunctionProperties(new String[]{"print"},
++ Builtins.class,
++ ScriptableObject.PERMANENT | ScriptableObject.DONTENUM);
++ }
++
++ public static void print(Context cx, Scriptable thisObj, Object[] args, Function f)
++ throws IOException {
++ Builtins self = getSelf(thisObj);
++ for (Object arg : args) {
++ self.stdout.write(ScriptRuntime.toString(arg));
++ }
++ self.stdout.write('\n');
++ }
++
++ private static Builtins getSelf(Scriptable scope) {
++ // Since this class is invoked as a set of anonymous functions, "this"
++ // in JavaScript does not point to "this" in Java. We set a key on the
++ // top-level scope to address this.
++ return (Builtins) ScriptableObject.getTopScopeValue(scope, BUILTIN_KEY);
++ }
++}
+--- /dev/null
++++ b/src/org/mozilla/javascript/engine/RhinoCompiledScript.java
+@@ -0,0 +1,33 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++package org.mozilla.javascript.engine;
++
++import javax.script.CompiledScript;
++import javax.script.ScriptContext;
++import javax.script.ScriptEngine;
++import javax.script.ScriptException;
++import org.mozilla.javascript.Script;
++
++public class RhinoCompiledScript
++ extends CompiledScript {
++
++ private final RhinoScriptEngine engine;
++ private final Script script;
++
++ RhinoCompiledScript(RhinoScriptEngine engine, Script script) {
++ this.engine = engine;
++ this.script = script;
++ }
++
++ @Override
++ public Object eval(ScriptContext context) throws ScriptException {
++ return engine.eval(script, context);
++ }
++
++ @Override
++ public ScriptEngine getEngine() {
++ return engine;
++ }
++}
+--- /dev/null
++++ b/src/org/mozilla/javascript/engine/RhinoInvocationHandler.java
+@@ -0,0 +1,25 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++package org.mozilla.javascript.engine;
++
++import java.lang.reflect.InvocationHandler;
++import java.lang.reflect.Method;
++
++public class RhinoInvocationHandler
++ implements InvocationHandler {
++
++ private final Object thiz;
++ private final RhinoScriptEngine engine;
++
++ RhinoInvocationHandler(RhinoScriptEngine engine, Object thiz) {
++ this.engine = engine;
++ this.thiz = thiz;
++ }
++
++ @Override
++ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
++ return engine.invokeMethodRaw(thiz, method.getName(), method.getReturnType(), args);
++ }
++}
+--- /dev/null
++++ b/src/org/mozilla/javascript/engine/RhinoScriptEngine.java
+@@ -0,0 +1,355 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++package org.mozilla.javascript.engine;
++
++import java.io.IOException;
++import java.io.Reader;
++import java.lang.reflect.Method;
++import java.lang.reflect.Proxy;
++import javax.script.AbstractScriptEngine;
++import javax.script.Bindings;
++import javax.script.Compilable;
++import javax.script.CompiledScript;
++import javax.script.Invocable;
++import javax.script.ScriptContext;
++import javax.script.ScriptEngine;
++import javax.script.ScriptEngineFactory;
++import javax.script.ScriptException;
++import javax.script.SimpleBindings;
++import org.mozilla.javascript.Callable;
++import org.mozilla.javascript.Context;
++import org.mozilla.javascript.ContextFactory;
++import org.mozilla.javascript.RhinoException;
++import org.mozilla.javascript.Script;
++import org.mozilla.javascript.Scriptable;
++import org.mozilla.javascript.ScriptableObject;
++
++/**
++ * <p>
++ * This is the implementation of the standard ScriptEngine interface for Rhino.
++ * </p>
++ * <p>
++ * An instance of the Rhino ScriptEngine is fully self-contained. Bindings at the GLOBAL_SCOPE may
++ * be set, but there is nothing special about them -- if both global and ENGINE_SCOPE bindings are
++ * set then the "engine" bindings override the global ones.
++ * </p>
++ * <p>
++ * The Rhino engine is not thread safe. Rhino does no synchronization of ScriptEngine instances and
++ * no synchronization of Bindings instances. It is up to the caller to ensure that the ScriptEngine
++ * and all its Bindings are used by a single thread at a time.
++ * </p>
++ * <p>
++ * The Rhino script engine includes some top-level built-in functions. See the Builtins class for
++ * more documentation.
++ * </p>
++ * <p>
++ * The engine supports a few configuration parameters that may be set at the "engine scope". Both
++ * are numbers that may be set to a String or Number object.
++ * </p>
++ * <ul>
++ * <li>javax.script.language_version: The version of the JavaScript language supported,
++ * which is an integer defined in the Context class. The default is the latest "ES6"
++ * version, defined as 200.</li>
++ * <li>org.mozilla.javascript.optimization_level: The level of optimization Rhino performs
++ * on the generated bytecode. Default is 9, which is the most. Set to -1 to use interpreted
++ * mode.</li>
++ * </ul>
++ */
++public class RhinoScriptEngine
++ extends AbstractScriptEngine
++ implements Compilable, Invocable {
++
++ /**
++ * Reserved key for the Rhino optimization level. Default is "9," for optimized and compiled code.
++ * Set this to "-1" to run Rhino in interpreted mode -- this is much much slower but the only
++ * option on platforms like Android that don't support class files.
++ */
++ public static final String OPTIMIZATION_LEVEL = "org.mozilla.javascript.optimization_level";
++
++ static final int DEFAULT_LANGUAGE_VERSION = Context.VERSION_ES6;
++ private static final int DEFAULT_OPT = 9;
++ private static final boolean DEFAULT_DEBUG = true;
++ private static final String DEFAULT_FILENAME = "eval";
++
++ private static final CtxFactory ctxFactory = new CtxFactory();
++
++ private final RhinoScriptEngineFactory factory;
++ private final Builtins builtins;
++ private ScriptableObject topLevelScope = null;
++
++ RhinoScriptEngine(RhinoScriptEngineFactory factory) {
++ this.factory = factory;
++ this.builtins = new Builtins();
++ }
++
++ private Scriptable initScope(Context cx, ScriptContext sc) throws ScriptException {
++ configureContext(cx);
++
++ if (topLevelScope == null) {
++ topLevelScope = cx.initStandardObjects();
++ // We need to stash this away so that the built in functions can find
++ // this engine's specific stuff that they need to work.
++ topLevelScope.associateValue(Builtins.BUILTIN_KEY, builtins);
++ builtins.register(cx, topLevelScope, sc);
++ }
++
++ Scriptable engineScope = new BindingsObject(
++ sc.getBindings(ScriptContext.ENGINE_SCOPE));
++ engineScope.setParentScope(null);
++ engineScope.setPrototype(topLevelScope);
++
++ if (sc.getBindings(ScriptContext.GLOBAL_SCOPE) != null) {
++ Scriptable globalScope = new BindingsObject(
++ sc.getBindings(ScriptContext.GLOBAL_SCOPE));
++ globalScope.setParentScope(null);
++ globalScope.setPrototype(topLevelScope);
++ engineScope.setPrototype(globalScope);
++ }
++
++ return engineScope;
++ }
++
++ @Override
++ public Object eval(String script, ScriptContext context) throws ScriptException {
++ Context cx = ctxFactory.enterContext();
++ try {
++ Scriptable scope = initScope(cx, context);
++ Object ret = cx.evaluateString(scope, script, getFilename(), 0, null);
++ return Context.jsToJava(ret, Object.class);
++ } catch (RhinoException re) {
++ throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
++ re.columnNumber());
++ } finally {
++ Context.exit();
++ }
++ }
++
++ @Override
++ public Object eval(Reader reader, ScriptContext context) throws ScriptException {
++ Context cx = ctxFactory.enterContext();
++ try {
++ Scriptable scope = initScope(cx, context);
++ Object ret = cx.evaluateReader(scope, reader, getFilename(), 0, null);
++ return Context.jsToJava(ret, Object.class);
++ } catch (RhinoException re) {
++ throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
++ re.columnNumber());
++ } catch (IOException ioe) {
++ throw new ScriptException(ioe);
++ } finally {
++ Context.exit();
++ }
++ }
++
++ @Override
++ public CompiledScript compile(String script) throws ScriptException {
++ Context cx = ctxFactory.enterContext();
++ try {
++ configureContext(cx);
++ Script s =
++ cx.compileString(script, getFilename(), 1, null);
++ return new RhinoCompiledScript(this, s);
++ } catch (RhinoException re) {
++ throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
++ re.columnNumber());
++ } finally {
++ Context.exit();
++ }
++ }
++
++ @Override
++ public CompiledScript compile(Reader script) throws ScriptException {
++ Context cx = ctxFactory.enterContext();
++ try {
++ configureContext(cx);
++ Script s =
++ cx.compileReader(script, getFilename(), 1, null);
++ return new RhinoCompiledScript(this, s);
++ } catch (RhinoException re) {
++ throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
++ re.columnNumber());
++ } catch (IOException ioe) {
++ throw new ScriptException(ioe);
++ } finally {
++ Context.exit();
++ }
++ }
++
++ Object eval(Script script, ScriptContext sc) throws ScriptException {
++ Context cx = ctxFactory.enterContext();
++ try {
++ Scriptable scope = initScope(cx, sc);
++ Object ret = script.exec(cx, scope);
++ return Context.jsToJava(ret, Object.class);
++ } catch (RhinoException re) {
++ throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
++ re.columnNumber());
++ } finally {
++ Context.exit();
++ }
++ }
++
++ @Override
++ public Object invokeFunction(String name, Object... args)
++ throws ScriptException, NoSuchMethodException {
++ return invokeMethod(null, name, args);
++ }
++
++ @Override
++ public Object invokeMethod(Object thiz, String name, Object... args)
++ throws ScriptException, NoSuchMethodException {
++ return invokeMethodRaw(thiz, name, Object.class, args);
++ }
++
++ Object invokeMethodRaw(Object thiz, String name, Class<?> returnType, Object... args)
++ throws ScriptException, NoSuchMethodException {
++ Context cx = ctxFactory.enterContext();
++ try {
++ Scriptable scope = initScope(cx, context);
++
++ Scriptable localThis;
++ if (thiz == null) {
++ localThis = scope;
++ } else {
++ localThis = Context.toObject(thiz, scope);
++ }
++
++ Object f = ScriptableObject.getProperty(localThis, name);
++ if (f == Scriptable.NOT_FOUND) {
++ throw new NoSuchMethodException(name);
++ }
++ if (!(f instanceof Callable)) {
++ throw new ScriptException("\"" + name + "\" is not a function");
++ }
++ Callable func = (Callable) f;
++
++ if (args != null) {
++ for (int i = 0; i < args.length; i++) {
++ args[i] = Context.javaToJS(args[i], scope);
++ }
++ }
++
++ Object ret = func.call(cx, scope, localThis, args);
++ if (returnType == Void.TYPE) {
++ return null;
++ }
++ return Context.jsToJava(ret, returnType);
++
++ } catch (RhinoException re) {
++ throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
++ re.columnNumber());
++ } finally {
++ Context.exit();
++ }
++ }
++
++ @Override
++ public <T> T getInterface(Class<T> clasz) {
++ if ((clasz == null) || !clasz.isInterface()) {
++ throw new IllegalArgumentException("Not an interface");
++ }
++ Context cx = ctxFactory.enterContext();
++ try {
++ Scriptable scope = initScope(cx, context);
++ if (methodsMissing(scope, clasz)) {
++ return null;
++ }
++ } catch (ScriptException se) {
++ return null;
++ } finally {
++ Context.exit();
++ }
++ return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
++ new Class<?>[]{clasz}, new RhinoInvocationHandler(this, null));
++ }
++
++ @Override
++ public <T> T getInterface(Object thiz, Class<T> clasz) {
++ if ((clasz == null) || !clasz.isInterface()) {
++ throw new IllegalArgumentException("Not an interface");
++ }
++ Context cx = ctxFactory.enterContext();
++ try {
++ Scriptable scope = initScope(cx, context);
++ Scriptable thisObj = Context.toObject(thiz, scope);
++ if (methodsMissing(thisObj, clasz)) {
++ return null;
++ }
++ } catch (ScriptException se) {
++ return null;
++ } finally {
++ Context.exit();
++ }
++ return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
++ new Class<?>[]{clasz}, new RhinoInvocationHandler(this, thiz));
++ }
++
++ @Override
++ public Bindings createBindings() {
++ return new SimpleBindings();
++ }
++
++ @Override
++ public ScriptEngineFactory getFactory() {
++ return factory;
++ }
++
++ private void configureContext(Context cx) throws ScriptException {
++ Object lv = get(ScriptEngine.LANGUAGE_VERSION);
++ if (lv != null) {
++ cx.setLanguageVersion(parseInteger(lv));
++ }
++ Object ol = get(OPTIMIZATION_LEVEL);
++ if (ol != null) {
++ cx.setOptimizationLevel(parseInteger(ol));
++ }
++ }
++
++ private int parseInteger(Object v) throws ScriptException {
++ if (v instanceof String) {
++ try {
++ return Integer.parseInt((String) v);
++ } catch (NumberFormatException nfe) {
++ throw new ScriptException("Invalid number " + v);
++ }
++ } else if (v instanceof Integer) {
++ return (Integer) v;
++ } else {
++ throw new ScriptException("Value must be a string or number");
++ }
++ }
++
++ private String getFilename() {
++ Object fn = get(ScriptEngine.FILENAME);
++ if (fn instanceof String) {
++ return (String) fn;
++ }
++ return DEFAULT_FILENAME;
++ }
++
++ private boolean methodsMissing(Scriptable scope, Class<?> clasz) {
++ for (Method m : clasz.getMethods()) {
++ if (m.getDeclaringClass() == Object.class) {
++ continue;
++ }
++ Object methodObj = ScriptableObject.getProperty(scope, m.getName());
++ if (!(methodObj instanceof Callable)) {
++ return true;
++ }
++ }
++ return false;
++ }
++
++ private static final class CtxFactory
++ extends ContextFactory {
++
++ @Override
++ protected void onContextCreated(Context cx) {
++ cx.setLanguageVersion(Context.VERSION_ES6);
++ cx.setOptimizationLevel(DEFAULT_OPT);
++ cx.setGeneratingDebug(DEFAULT_DEBUG);
++ }
++ }
++}
+--- /dev/null
++++ b/src/org/mozilla/javascript/engine/RhinoScriptEngineFactory.java
+@@ -0,0 +1,140 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++package org.mozilla.javascript.engine;
++
++import java.util.Arrays;
++import java.util.Collections;
++import java.util.List;
++import javax.script.ScriptEngine;
++import javax.script.ScriptEngineFactory;
++import org.mozilla.javascript.Context;
++
++/**
++ * <p>
++ * This is an implementation of the standard Java "ScriptEngine" for Rhino. If the Rhino engine
++ * (typically in the form of the "rhino-engine" JAR) is in the classpath, then this script
++ * engine will be activated.
++ * </p>
++ * <p>
++ * See the list of constants in this class for the list of language names, file extensions, and
++ * MIME types that this engine supports. This list is essentially the same as the list supported
++ * in the Nashorn script engine that was included in Java 8.
++ * </p>
++ * <p>
++ * Since this engine and Nashorn support the same language and file extensions, then unless
++ * you are sure you are running in an environment that has Nashorn, the best way to get this
++ * engine is to call ScriptEngine.getEngineByName("rhino") to ask for Rhino directly.
++ * </p>
++ */
++public class RhinoScriptEngineFactory
++ implements ScriptEngineFactory {
++
++ public static final String NAME = "rhino";
++ public static final String LANGUAGE = "javascript";
++ public static final List<String> NAMES =
++ Arrays.asList("rhino", "Rhino", "javascript", "JavaScript");
++ public static final List<String> EXTENSIONS =
++ Collections.singletonList("js");
++ public static final List<String> MIME_TYPES =
++ Arrays.asList("application/javascript", "application/ecmascript",
++ "text/javascript", "text/ecmascript");
++ public static final String LANGUAGE_VERSION =
++ String.valueOf(RhinoScriptEngine.DEFAULT_LANGUAGE_VERSION);
++
++ @Override
++ public String getEngineName() {
++ return NAME;
++ }
++
++ @Override
++ public String getEngineVersion() {
++ Context cx = Context.enter();
++ try {
++ String v = cx.getImplementationVersion();
++ return (v == null ? "unknown" : v);
++ } finally {
++ Context.exit();
++ }
++ }
++
++ @Override
++ public List<String> getExtensions() {
++ return EXTENSIONS;
++ }
++
++ @Override
++ public List<String> getMimeTypes() {
++ return MIME_TYPES;
++ }
++
++ @Override
++ public List<String> getNames() {
++ return NAMES;
++ }
++
++ @Override
++ public String getLanguageName() {
++ return LANGUAGE;
++ }
++
++ @Override
++ public String getLanguageVersion() {
++ return LANGUAGE_VERSION;
++ }
++
++ @Override
++ public Object getParameter(String key) {
++ switch (key) {
++ case ScriptEngine.ENGINE:
++ return getEngineName();
++ case ScriptEngine.ENGINE_VERSION:
++ return getEngineVersion();
++ case ScriptEngine.LANGUAGE:
++ return getLanguageName();
++ case ScriptEngine.LANGUAGE_VERSION:
++ return getLanguageVersion();
++ case ScriptEngine.NAME:
++ return NAME;
++ case "THREADING":
++ // Engines are explicitly not thread-safe
++ return null;
++ default:
++ return null;
++ }
++ }
++
++ @Override
++ public String getMethodCallSyntax(String obj, String m, String... args) {
++ StringBuilder sb = new StringBuilder();
++ sb.append(obj).append('.').append(m).append('(');
++ for (int i = 0; i < args.length; i++) {
++ if (i > 0) {
++ sb.append(',');
++ }
++ sb.append(args[i]);
++ }
++ sb.append(");");
++ return sb.toString();
++ }
++
++ @Override
++ public String getOutputStatement(String toDisplay) {
++ return "print('" + toDisplay + "');";
++ }
++
++ @Override
++ public String getProgram(String... statements) {
++ StringBuilder sb = new StringBuilder();
++ for (String stmt : statements) {
++ sb.append(stmt).append(";\n");
++ }
++ return sb.toString();
++ }
++
++ @Override
++ public ScriptEngine getScriptEngine() {
++ return new RhinoScriptEngine(this);
++ }
++}
+--- /dev/null
++++ b/testsrc/org/mozilla/javascript/tests/scriptengine/BuiltinsTest.java
+@@ -0,0 +1,54 @@
++package org.mozilla.javascript.tests.scriptengine;
++
++import java.io.StringWriter;
++import javax.script.ScriptContext;
++import javax.script.ScriptEngine;
++import javax.script.ScriptEngineManager;
++import javax.script.ScriptException;
++import javax.script.SimpleScriptContext;
++import org.junit.Before;
++import org.junit.BeforeClass;
++import org.junit.Test;
++import org.mozilla.javascript.engine.RhinoScriptEngineFactory;
++
++import static org.junit.Assert.*;
++
++public class BuiltinsTest {
++
++ private static ScriptEngineManager manager;
++
++ private ScriptEngine engine;
++
++ @BeforeClass
++ public static void init() {
++ manager = new ScriptEngineManager();
++ manager.registerEngineName("rhino", new RhinoScriptEngineFactory());
++ }
++
++ @Before
++ public void setup() {
++ engine = manager.getEngineByName("rhino");
++ }
++
++ @Test
++ public void testPrintStdout() throws ScriptException {
++ engine.eval("print('Hello, World!');");
++ }
++
++ @Test
++ public void testPrintWriter() throws ScriptException {
++ StringWriter sw = new StringWriter();
++ ScriptContext sc = new SimpleScriptContext();
++ sc.setWriter(sw);
++ engine.eval("print('one', 2, true);", sc);
++ assertEquals(sw.toString(), "one2true\n");
++ }
++
++ @Test
++ public void testPrintWriterGeneric() throws ScriptException {
++ StringWriter sw = new StringWriter();
++ engine.getContext().setWriter(sw);
++ engine.eval(engine.getFactory().getOutputStatement("Display This!"));
++ assertEquals(sw.toString(), "Display This!\n");
++ }
++}
+--- /dev/null
++++ b/testsrc/org/mozilla/javascript/tests/scriptengine/FactoryTest.java
+@@ -0,0 +1,56 @@
++package org.mozilla.javascript.tests.scriptengine;
++
++import javax.script.ScriptEngine;
++import javax.script.ScriptEngineFactory;
++import javax.script.ScriptEngineManager;
++import org.junit.Test;
++import org.mozilla.javascript.engine.RhinoScriptEngine;
++import org.mozilla.javascript.engine.RhinoScriptEngineFactory;
++
++import static org.junit.Assert.*;
++
++/*
++ * A series of tests that depend on us having our engine registered with the
++ * ScriptEngineManager by default.
++ */
++public class FactoryTest {
++
++ @Test
++ public void findRhinoFactory() {
++ ScriptEngineManager manager = new ScriptEngineManager();
++ for (ScriptEngineFactory factory : manager.getEngineFactories()) {
++ if (factory instanceof RhinoScriptEngineFactory) {
++ assertEquals("rhino", factory.getEngineName());
++ assertEquals("rhino", factory.getParameter(ScriptEngine.ENGINE));
++ assertEquals("rhino", factory.getParameter(ScriptEngine.NAME));
++ // This could be "unknown" if we're not running from a regular JAR
++ assertFalse(factory.getEngineVersion().isEmpty());
++ assertEquals("javascript", factory.getLanguageName());
++ assertEquals("javascript", factory.getParameter(ScriptEngine.LANGUAGE));
++ assertEquals("200", factory.getLanguageVersion());
++ assertEquals("200", factory.getParameter(ScriptEngine.LANGUAGE_VERSION));
++ assertNull(factory.getParameter("THREADING"));
++ assertTrue(factory.getExtensions().contains("js"));
++ assertTrue(factory.getMimeTypes().contains("application/javascript"));
++ assertTrue(factory.getMimeTypes().contains("application/ecmascript"));
++ assertTrue(factory.getMimeTypes().contains("text/javascript"));
++ assertTrue(factory.getMimeTypes().contains("text/ecmascript"));
++ assertTrue(factory.getNames().contains("rhino"));
++ assertTrue(factory.getNames().contains("Rhino"));
++ assertTrue(factory.getNames().contains("javascript"));
++ assertTrue(factory.getNames().contains("JavaScript"));
++ return;
++ }
++ }
++ fail("Expected to find Rhino script engine");
++ }
++
++ @Test
++ public void testRhinoFactory() {
++ // This will always uniquely return our engine.
++ // In Java 8, other ways to find it may return Nashorn.
++ ScriptEngine engine = new ScriptEngineManager().getEngineByName("rhino");
++ assertTrue(engine instanceof RhinoScriptEngine);
++
++ }
++}
+--- /dev/null
++++ b/testsrc/org/mozilla/javascript/tests/scriptengine/InvocableTest.java
+@@ -0,0 +1,158 @@
++package org.mozilla.javascript.tests.scriptengine;
++
++import java.io.FileNotFoundException;
++import java.io.FileReader;
++import javax.script.Invocable;
++import javax.script.ScriptEngine;
++import javax.script.ScriptEngineManager;
++import javax.script.ScriptException;
++import org.junit.Before;
++import org.junit.BeforeClass;
++import org.junit.Test;
++import org.mozilla.javascript.engine.RhinoScriptEngineFactory;
++
++import static org.junit.Assert.*;
++
++public class InvocableTest {
++
++ private static ScriptEngineManager manager;
++
++ private ScriptEngine engine;
++ private Invocable iEngine;
++
++ @BeforeClass
++ public static void init() {
++ manager = new ScriptEngineManager();
++ manager.registerEngineName("rhino", new RhinoScriptEngineFactory());
++ }
++
++ @Before
++ public void setup() {
++ engine = manager.getEngineByName("rhino");
++ iEngine = (Invocable) engine;
++ }
++
++ @Test
++ public void invokeFunctionTest() throws ScriptException, NoSuchMethodException {
++ engine.eval("function foo(a, b) { return a + b; }");
++ Object result = iEngine.invokeFunction("foo", 2, 2);
++ assertEquals(result, 4L);
++ }
++
++ @Test
++ public void invokeScriptFunctionTest() throws ScriptException, NoSuchMethodException {
++ Object scriptObj = engine.eval("let o = {};\n"
++ + "o.test = function(x) { return x + 2; }\n"
++ + "o;");
++ assertEquals(4L, iEngine.invokeMethod(scriptObj, "test", 2));
++ }
++
++ @Test
++ public void invokeGenericFunctionTest() throws ScriptException, NoSuchMethodException {
++ engine.eval("let o = {};\n"
++ + "o.test = function(x) { return x + 2; }\n");
++ Object result = engine.eval(engine.getFactory().getMethodCallSyntax("o", "test", "1"));
++ assertEquals(3L, result);
++ }
++
++ @Test
++ public void invokeGenericFunctionTest2() throws ScriptException, NoSuchMethodException {
++ engine.eval("let o = {};\n"
++ + "o.test = function(x, y) { return x + y; }\n");
++ Object result = engine.eval(engine.getFactory().getMethodCallSyntax("o", "test", "1", "7"));
++ assertEquals(8L, result);
++ }
++
++ @Test
++ public void invokeMethodTest()
++ throws ScriptException, NoSuchMethodException, FileNotFoundException {
++ engine.eval(new FileReader("testsrc/assert.js"));
++ engine.eval("function FooObj() { this.x = 0; }\n"
++ + "FooObj.prototype.set = function(a, b) { this.x = a + b; }");
++ engine.eval("let f = new FooObj();\n"
++ + "assertEquals(f.x, 0);\n"
++ + "f.set(2, 2);\n"
++ + "assertEquals(f.x, 4);");
++
++ Object fooObj = engine.eval("let y = new FooObj(); y");
++ assertNotNull(fooObj);
++ iEngine.invokeMethod(fooObj, "set", 3, 3);
++ Object result = engine.eval("y.x");
++ assertEquals(result, 6L);
++ }
++
++ @Test
++ public void interfaceFunctionTest()
++ throws ScriptException, FileNotFoundException {
++ engine.eval(new FileReader("testsrc/assert.js"));
++ engine.eval("var foo = 'initialized';\n"
++ + "function setFoo(v) { foo = v; }\n"
++ + "function getFoo() { return foo; }\n"
++ + "function addItUp(a, b) { return a + b; }");
++ I tester = iEngine.getInterface(I.class);
++ assertEquals(tester.getFoo(), "initialized");
++ tester.setFoo("tested");
++ assertEquals(tester.getFoo(), "tested");
++ assertEquals(tester.addItUp(100, 1), 101);
++ }
++
++ @Test
++ public void interfaceMethodTest()
++ throws ScriptException, FileNotFoundException {
++ engine.eval(new FileReader("testsrc/assert.js"));
++ Object foo = engine.eval("function Foo() { this.foo = 'initialized' }\n"
++ + "Foo.prototype.setFoo = function(v) { this.foo = v; };\n"
++ + "Foo.prototype.getFoo = function() { return this.foo; };\n"
++ + "Foo.prototype.addItUp = function(a, b) { return a + b; };\n"
++ + "new Foo();");
++ I tester = iEngine.getInterface(foo, I.class);
++ assertEquals(tester.getFoo(), "initialized");
++ tester.setFoo("tested");
++ assertEquals(tester.getFoo(), "tested");
++ assertEquals(tester.addItUp(100, 1), 101);
++ }
++
++ @Test
++ public void interfaceFunctionMissingTest() {
++ I tester = iEngine.getInterface(I.class);
++ assertNull(tester);
++ }
++
++ @Test
++ public void interfaceMethodMissingTest()
++ throws ScriptException {
++ // Functions defined, but not on the right object
++ Object foo = engine.eval("var foo = 'initialized';\n"
++ + "function setFoo(v) { foo = v; }\n"
++ + "function getFoo() { return foo; }\n"
++ + "function addItUp(a, b) { return a + b; }\n"
++ + "function Foo() {}\n"
++ + "new Foo();");
++ I tester = iEngine.getInterface(foo, I.class);
++ assertNull(tester);
++ }
++
++ @Test
++ public void invokeNotFoundTest() {
++ assertThrows(NoSuchMethodException.class, () -> {
++ iEngine.invokeFunction("foo", 2, 2);
++ });
++ }
++
++ @Test
++ public void invokeNotFunctionTest() {
++ assertThrows(ScriptException.class, () -> {
++ engine.eval("foo = 'bar';");
++ iEngine.invokeFunction("foo", 2, 2);
++ });
++ }
++
++ interface I {
++
++ void setFoo(String v);
++
++ String getFoo();
++
++ int addItUp(int a, int b);
++ }
++}
+--- /dev/null
++++ b/testsrc/org/mozilla/javascript/tests/scriptengine/ScriptEngineTest.java
+@@ -0,0 +1,276 @@
++package org.mozilla.javascript.tests.scriptengine;
++
++import java.io.File;
++import java.io.FileReader;
++import java.io.IOException;
++import java.io.StringReader;
++import javax.script.Bindings;
++import javax.script.Compilable;
++import javax.script.CompiledScript;
++import javax.script.ScriptContext;
++import javax.script.ScriptEngine;
++import javax.script.ScriptEngineFactory;
++import javax.script.ScriptEngineManager;
++import javax.script.ScriptException;
++import javax.script.SimpleBindings;
++import javax.script.SimpleScriptContext;
++import org.junit.Before;
++import org.junit.BeforeClass;
++import org.junit.Test;
++import org.mozilla.javascript.engine.RhinoScriptEngine;
++import org.mozilla.javascript.engine.RhinoScriptEngineFactory;
++
++import static org.junit.Assert.*;
++
++public class ScriptEngineTest {
++
++ private static ScriptEngineManager manager;
++ private ScriptEngine engine;
++ private Compilable cEngine;
++
++ @BeforeClass
++ public static void initManager() {
++ manager = new ScriptEngineManager();
++ manager.registerEngineName("rhino", new RhinoScriptEngineFactory());
++ }
++
++ @Before
++ public void init() {
++ engine = manager.getEngineByName("rhino");
++ cEngine = (Compilable) engine;
++ }
++
++ @Test
++ public void testHello() throws ScriptException {
++ Object result = engine.eval("'Hello, World!';");
++ assertEquals(result, "Hello, World!");
++ }
++
++ @Test
++ public void testHelloInterpreted() throws ScriptException {
++ engine.put(RhinoScriptEngine.OPTIMIZATION_LEVEL, -1);
++ Object result = engine.eval("'Hello, World!';");
++ assertEquals(result, "Hello, World!");
++ }
++
++
++ @Test
++ public void testHelloReader() throws ScriptException {
++ String src = "1 + 1;";
++ StringReader sr = new StringReader(src);
++ Object result = engine.eval(sr);
++ assertEquals(result, 2L);
++ }
++
++ @Test
++ public void testGenericStatements() throws ScriptException {
++ Object result = engine.eval(engine.getFactory().getProgram(
++ "let x = 1;",
++ "let y = 2",
++ "x + y"
++ ));
++ assertEquals(3L, result);
++ }
++
++ @Test
++ public void testThrows() {
++ assertThrows(ScriptException.class, () -> {
++ engine.eval("throw 'This is an error'");
++ });
++ }
++
++ @Test
++ public void testEngineBindings() throws IOException, ScriptException {
++ engine.put("string", "Hello");
++ engine.put("integer", 123);
++ engine.put("a", "a");
++ engine.put("b", "b");
++ engine.put("c", "c");
++
++ // Ensure that stuff we just stuck in bindings made it to a global
++ engine.eval(new FileReader("testsrc/assert.js"));
++ engine.eval("assertEquals(string, 'Hello');\n"
++ + "assertEquals(integer, 123);\n"
++ + "string = 'Goodbye';\n"
++ + "assertEquals(string, 'Goodbye');");
++ assertEquals(engine.get("string"), "Goodbye");
++
++ // Make sure we can delete
++ engine.getBindings(ScriptContext.ENGINE_SCOPE).remove("string");
++ // This will throw because string is undefined
++ assertThrows(ScriptException.class, () -> {
++ engine.eval("let failing = string + '123';");
++ });
++ }
++
++ @Test
++ public void testEngineScope() throws IOException, ScriptException {
++ engine.put("string", "Hello");
++ engine.put("integer", 123);
++ engine.eval(new FileReader("testsrc/assert.js"));
++ engine.eval("assertEquals(string, 'Hello');"
++ + "assertEquals(integer, 123);");
++
++ // Additional things added to the context but old stuff still there
++ engine.put("second", true);
++ engine.put("integer", 99);
++ engine.eval("assertEquals(string, 'Hello');"
++ + "assertEquals(integer, 99);"
++ + "assertTrue(second);");
++ }
++
++ @Test
++ public void testScopedBindings() throws IOException, ScriptException {
++ ScriptContext sc = new SimpleScriptContext();
++
++ // We treat engine and global scope the same -- if the user actually
++ // uses both, then engine scope overrides global scope.
++ Bindings eb = new SimpleBindings();
++ sc.setBindings(eb, ScriptContext.ENGINE_SCOPE);
++ eb.put("engine", Boolean.TRUE);
++ eb.put("level", 2);
++
++ Bindings gb = new SimpleBindings();
++ sc.setBindings(gb, ScriptContext.GLOBAL_SCOPE);
++ gb.put("global", Boolean.TRUE);
++ gb.put("level", 0);
++
++ engine.eval(new FileReader("testsrc/assert.js"), sc);
++ engine.eval("assertTrue(engine);"
++ + "assertTrue(global);"
++ + "assertEquals(level, 2);", sc);
++ }
++
++ @Test
++ public void testReservedBindings() throws ScriptException {
++ engine.put(ScriptEngine.ENGINE, "engine");
++ engine.put(ScriptEngine.ENGINE_VERSION, "123");
++ engine.put(ScriptEngine.LANGUAGE, "foo");
++ engine.put(ScriptEngine.NAME, "nothing");
++
++ // Can't actually test for those invalid property names -- but
++ // at least they didn't break the script.
++ assertEquals(engine.eval("'success'"), "success");
++ }
++
++ @Test
++ public void testCompiled() throws ScriptException, IOException {
++ CompiledScript asserts =
++ cEngine.compile(new FileReader("testsrc/assert.js"));
++ CompiledScript tests =
++ cEngine.compile("assertEquals(compiled, true);");
++
++ // Fails because asserts have not been loaded
++ assertThrows(ScriptException.class, tests::eval);
++
++ asserts.eval();
++ // Fails because value has not been set
++ assertThrows(ScriptException.class, tests::eval);
++
++ engine.put("compiled", Boolean.TRUE);
++ tests.eval();
++ }
++
++ @Test
++ public void testCompiled2() throws ScriptException, IOException {
++ CompiledScript asserts =
++ cEngine.compile(new FileReader("testsrc/assert.js"));
++ CompiledScript init =
++ cEngine.compile("value = 0;");
++ CompiledScript tests =
++ cEngine.compile("assertEquals(value, expectedValue);"
++ + "value += 1;");
++
++ asserts.eval();
++ init.eval();
++ for (int i = 0; i <= 10; i++) {
++ engine.put("expectedValue", i);
++ tests.eval();
++ }
++ }
++
++ @Test
++ public void testCompiledThrows() throws ScriptException {
++ engine.put(ScriptEngine.FILENAME, "throws1.js");
++ CompiledScript throw1 = cEngine.compile("throw 'one';");
++ engine.put(ScriptEngine.FILENAME, "throws2.js");
++ CompiledScript throw2 = cEngine.compile("throw 'two';");
++
++ try {
++ throw1.eval();
++ fail("Expected a throw");
++ } catch (ScriptException se) {
++ assertTrue(se.getMessage().startsWith("one"));
++ assertEquals("throws1.js", se.getFileName());
++ assertEquals(1, se.getLineNumber());
++ }
++
++ try {
++ throw2.eval();
++ fail("Expected a throw");
++ } catch (ScriptException se) {
++ assertTrue(se.getMessage().startsWith("two"));
++ assertEquals("throws2.js", se.getFileName());
++ assertEquals(1, se.getLineNumber());
++ }
++ }
++
++ @Test
++ public void testCantCompile() {
++ assertThrows(ScriptException.class, () -> {
++ cEngine.compile("This is not JavaScript at all!");
++ });
++ }
++
++ @Test
++ public void testLanguageVersion() throws ScriptException {
++ // Default language version is modernish
++ ScriptEngine newEngine = manager.getEngineByName("rhino");
++ assertEquals(newEngine.eval("Symbol() == Symbol()"), Boolean.FALSE);
++
++ // Older language versions
++ ScriptEngine oldEngine = manager.getEngineByName("rhino");
++ oldEngine.put(ScriptEngine.LANGUAGE_VERSION, 120);
++ assertThrows(ScriptException.class, () -> {
++ oldEngine.eval("Symbol() == Symbol()");
++ });
++
++ // The same with a string
++ ScriptEngine olderEngine = manager.getEngineByName("rhino");
++ olderEngine.put(ScriptEngine.LANGUAGE_VERSION, "100");
++ assertThrows(ScriptException.class, () -> {
++ olderEngine.eval("Symbol() == Symbol()");
++ });
++ }
++
++ @Test
++ public void testBadLanguageVersion() {
++ assertThrows(ScriptException.class, () -> {
++ engine.put(ScriptEngine.LANGUAGE_VERSION, "Not a number");
++ engine.eval("print('Hi!');");
++ });
++ assertThrows(ScriptException.class, () -> {
++ engine.put(ScriptEngine.LANGUAGE_VERSION, 3.14);
++ engine.eval("print('Hi!');");
++ });
++ }
++
++ @Test
++ public void testFilename() {
++ engine.put(ScriptEngine.FILENAME, "test.js");
++ try {
++ engine.eval("throw 'This is an exception';");
++ } catch (ScriptException se) {
++ assertEquals(se.getFileName(), "test.js");
++ }
++ }
++
++ @Test
++ public void testJavaObject() throws ScriptException {
++ File f = new File("testsrc/assert.js");
++ String absVal = f.getAbsolutePath();
++ engine.put("file", f);
++ Object result = engine.eval("file.getAbsolutePath();");
++ assertEquals(absVal, result);
++ }
++}
+--- a/build.xml
++++ b/build.xml
+@@ -87,7 +87,9 @@
+ basedir="${classes}"
+ manifest="src/manifest"
+ compress="${jar-compression}"
+- />
++ >
++ <fileset dir="src" includes="META-INF/**"/>
++ </jar>
+ </target>
+
+ <target name="console" depends="jar">
=====================================
debian/patches/series
=====================================
@@ -3,3 +3,5 @@
06_preserve-backward-compatibility.patch
07_fix-context-implementation-version.patch
08_fix-jar-version-number.patch
+script-engine.patch
+
=====================================
debian/rules
=====================================
@@ -7,7 +7,7 @@ BUILDDIR = build/rhino$(VERSION)
dh $@ --with javahelper --with maven-repo-helper
override_dh_auto_build:
- dh_auto_build -- jar javadoc
+ dh_auto_build -- jar javadoc -Dsource-level=7 -Dtarget-jvm=7
override_dh_installchangelogs:
dh_installchangelogs -- RELEASE-NOTES.md
View it on GitLab: https://salsa.debian.org/java-team/rhino/-/compare/c6de93800d99897e3535a600cea1db6aa5fcda0c...1b6855899385bb7a6561183976701c10616db41e
--
View it on GitLab: https://salsa.debian.org/java-team/rhino/-/compare/c6de93800d99897e3535a600cea1db6aa5fcda0c...1b6855899385bb7a6561183976701c10616db41e
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20210208/64aeb244/attachment.html>
More information about the pkg-java-commits
mailing list