From 7e4930b085779b02f6d169b5461680e4e274072b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20D=C3=B6ring?= Date: Mon, 16 Nov 2020 14:48:44 +0100 Subject: [PATCH] #96: add support for GraalJS script engine on RegExp checks --- .../core/util/RegexECMA262Helper.java | 60 +++++++++++++++---- .../core/util/RegexECMA262HelperTest.java | 2 +- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/github/fge/jsonschema/core/util/RegexECMA262Helper.java b/src/main/java/com/github/fge/jsonschema/core/util/RegexECMA262Helper.java index 4e2a1856..dc6eb55a 100644 --- a/src/main/java/com/github/fge/jsonschema/core/util/RegexECMA262Helper.java +++ b/src/main/java/com/github/fge/jsonschema/core/util/RegexECMA262Helper.java @@ -49,10 +49,13 @@ * the full story. And if you don't yet have Jeffrey Friedl's "Mastering regular * expressions", just buy it :p

* - *

As script engine is used either Nashorn or Rhino as its fallback. - * Nashorn is only available on Java 8 up to 14.

+ *

As script engine is used either GraalJS, Nashorn or Rhino as their + * fallback. Nashorn is only available on Java 8 up to 14.

* - *

Rhino is the fallback as it is tremendously slower.

+ *

GraalJS is the first choice as it supports more RegExp features, e.g. + * lookbehind assertions, than both alternatives.

+ * + *

Rhino is the fallback as it is tremendously slower than Nashorn.

*/ @ThreadSafe public final class RegexECMA262Helper @@ -89,10 +92,15 @@ private RegexECMA262Helper() private static RegexScript determineRegexScript() { + try { + return new GraalJsScript(); + } catch(final ScriptException e) { + // most probably GraalJS is simply not available + } try { return new NashornScript(); } catch(final ScriptException e) { - // either Nashorn is not available or the JavaScript can't be parsed + // most probably Nashorn is simply not available } return new RhinoScript(); } @@ -134,26 +142,25 @@ private interface RegexScript boolean regMatch(String regex, String input); } - private static class NashornScript implements RegexScript + private static abstract class ScriptEngineScript implements RegexScript { /** * Script engine */ private final Invocable scriptEngine; - private NashornScript() throws ScriptException + private ScriptEngineScript(final String engineName) throws ScriptException { final ScriptEngine engine = new ScriptEngineManager() - .getEngineByName("nashorn"); - if (engine == null) { - throw new ScriptException("ScriptEngine 'nashorn' not found."); + .getEngineByName(engineName); + if(engine == null) { + throw new ScriptException("ScriptEngine '" + engineName + "' not found."); } engine.eval(jsAsString); this.scriptEngine = (Invocable) engine; } - private boolean invokeScriptEngine(final String function, - final Object... values) + boolean invoke(final String function, final Object... values) { try { return (Boolean) scriptEngine.invokeFunction(function, @@ -178,6 +185,37 @@ public boolean regMatch(final String regex, final String input) { return invokeScriptEngine(REG_MATCH_FUNCTION_NAME, regex, input); } + + abstract boolean invokeScriptEngine(final String function, final Object... values); + } + + private static class GraalJsScript extends ScriptEngineScript + { + private GraalJsScript() throws ScriptException + { + super("graal.js"); + } + + // GraalJS works single-threaded. The synchronized ensures this. + @Override + synchronized boolean invokeScriptEngine(final String function, + final Object... values) { + return invoke(function, values); + } + } + + private static class NashornScript extends ScriptEngineScript + { + private NashornScript() throws ScriptException + { + super("nashorn"); + } + + @Override + boolean invokeScriptEngine(final String function, + final Object... values) { + return invoke(function, values); + } } private static class RhinoScript implements RegexScript diff --git a/src/test/java/com/github/fge/jsonschema/core/util/RegexECMA262HelperTest.java b/src/test/java/com/github/fge/jsonschema/core/util/RegexECMA262HelperTest.java index cda0538e..c4df4d64 100644 --- a/src/test/java/com/github/fge/jsonschema/core/util/RegexECMA262HelperTest.java +++ b/src/test/java/com/github/fge/jsonschema/core/util/RegexECMA262HelperTest.java @@ -71,7 +71,7 @@ public Iterator ecma262regexes() { return ImmutableList.of( new Object[] { "[^]", true }, - new Object[] { "(?<=foo)bar", false }, + new Object[] { "(?<=foobar", false }, new Object[] { "", true }, new Object[] { "[a-z]+(?!foo)(?=bar)", true } ).iterator();