From 746f9f1a11e2691e88f1b21bfaa7b63c1aa0d66f Mon Sep 17 00:00:00 2001 From: Mitchell Syer Date: Sun, 17 Nov 2024 15:24:08 -0500 Subject: [PATCH] [WIP] Switch to GraalJS Engine (#793) * Switch to GraalJS Engine * Update Polygot --- AndroidCompat/build.gradle.kts | 4 +- .../main/java/app/cash/quickjs/QuickJs.java | 87 ++++++++++++------- gradle/libs.versions.toml | 14 +-- 3 files changed, 67 insertions(+), 38 deletions(-) diff --git a/AndroidCompat/build.gradle.kts b/AndroidCompat/build.gradle.kts index 590285c9..df6d19dd 100644 --- a/AndroidCompat/build.gradle.kts +++ b/AndroidCompat/build.gradle.kts @@ -36,8 +36,8 @@ dependencies { // AndroidX annotations compileOnly(libs.android.annotations) - // substitute for duktape-android - implementation(libs.bundles.rhino) + // substitute for duktape-android/quickjs + implementation(libs.bundles.polyglot) // Kotlin wrapper around Java Preferences, makes certain things easier implementation(libs.bundles.settings) diff --git a/AndroidCompat/src/main/java/app/cash/quickjs/QuickJs.java b/AndroidCompat/src/main/java/app/cash/quickjs/QuickJs.java index 8b89f99e..02b42efe 100644 --- a/AndroidCompat/src/main/java/app/cash/quickjs/QuickJs.java +++ b/AndroidCompat/src/main/java/app/cash/quickjs/QuickJs.java @@ -1,69 +1,98 @@ package app.cash.quickjs; -import org.mozilla.javascript.ConsString; -import org.mozilla.javascript.NativeArray; +import org.graalvm.polyglot.*; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; import java.io.Closeable; +import java.math.BigInteger; +import java.util.Arrays; public final class QuickJs implements Closeable { - private ScriptEngine engine; + private Context context; public static QuickJs create() { - return new QuickJs(new ScriptEngineManager()); + return new QuickJs(); } - public QuickJs(ScriptEngineManager manager) { - this.engine = manager.getEngineByName("rhino"); + public QuickJs() { + this.context = Context + .newBuilder("js") + .allowHostAccess(HostAccess.ALL) + .allowPolyglotAccess(PolyglotAccess.NONE) + .allowHostClassLoading(false) + .build(); + context.enter(); } - public Object evaluate(String script, String fileName) { + public Object evaluate(String script, String ignoredFileName) { return this.evaluate(script); } public Object evaluate(String script) { try { - Object value = engine.eval(script); + Value value = context.eval("js", script); return translateType(value); } catch (Exception exception) { throw new QuickJsException(exception.getMessage(), exception); } } - private Object translateType(Object obj) { - if (obj instanceof NativeArray) { - NativeArray array = (NativeArray) obj; - long length = array.getLength(); - Object[] objects = new Object[(int) length]; - for (int i = 0; i < (int) length; i++) { - objects[i] = translateType(array.get(i)); + private Object translateType(Value obj) { + if (obj.isBoolean()) { + return obj.asBoolean(); + } else if (obj.hasArrayElements()) { + if (obj.getArraySize() == 0) { + return new int[0]; + } else { + Value element = obj.getArrayElement(0); + if (element.isBoolean()) { + return obj.as(boolean[].class); + } else if (element.isNumber()) { + if (element.fitsInInt()) { + return obj.as(int[].class); + } else if (element.fitsInBigInteger()) { + return Arrays.stream(obj.as(BigInteger[].class)).map(BigInteger::longValue).toArray(); + } else { + return obj.as(double[].class); + } + } else if (element.isHostObject()) { + return obj.as(Object[].class); + } else if (element.isString()) { + return obj.as(String[].class); + } } - return objects; - } - if (obj instanceof ConsString) { - ConsString consString = (ConsString) obj; - return consString.toString(); - } - if (obj instanceof Long) { - Long value = (Long) obj; - return value.intValue(); + } else if (obj.isNumber()) { + if (obj.fitsInInt()) { + return obj.asInt(); + } else if (obj.fitsInBigInteger()) { + return obj.asBigInteger().longValue(); + } else { + return obj.asDouble(); + } + } else if (obj.isHostObject()) { + return obj.asHostObject(); + } else if (obj.isString()) { + return obj.asString(); } return obj; } - public byte[] compile(String sourceCode, String fileName) { + public byte[] compile(String sourceCode, String ignoredFileName) { return sourceCode.getBytes(); } - public Object execute(byte[] bytecode) { return this.evaluate(new String(bytecode)); } + public void set(String name, Class ignoredType, T object) { + context.getBindings("js").putMember(name, object); + } + @Override public void close() { - this.engine = null; + this.context.leave(); + this.context.close(); + this.context = null; } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d8932dc4..7f77cb25 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,11 +3,11 @@ kotlin = "2.0.21" coroutines = "1.9.0" serialization = "1.7.3" okhttp = "5.0.0-alpha.14" # Major version is locked by Tachiyomi extensions -javalin = "6.3.0" # Javalin 5.0.0+ requires Java 11 +javalin = "6.3.0" jackson = "2.18.1" # jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency` exposed = "0.40.1" dex2jar = "v64" # Stuck until https://github.com/ThexXTURBOXx/dex2jar/issues/27 is fixed -rhino = "1.7.15" +polyglot = "24.1.1" settings = "1.2.0" twelvemonkeys = "3.12.0" graphqlkotlin = "8.2.1" @@ -116,8 +116,8 @@ bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.79" android-annotations = "androidx.annotation:annotation:1.9.1" # Substitute for duktape-android -rhino-runtime = { module = "org.mozilla:rhino-runtime", version.ref = "rhino" } # slimmer version of 'org.mozilla:rhino' -rhino-engine = { module = "org.mozilla:rhino-engine", version.ref = "rhino" } # provides the same interface as 'javax.script' a.k.a Nashorn +polyglot-core = { module = "org.graalvm.polyglot:polyglot", version.ref = "polyglot" } +polyglot-graaljs = { module = "org.graalvm.polyglot:js-community", version.ref = "polyglot" } # provides the same interface as 'javax.script' a.k.a Nashorn # Settings settings-core = { module = "com.russhwolf:multiplatform-settings-jvm", version.ref = "settings" } @@ -219,9 +219,9 @@ systemtray = [ "systemtray-utils", "systemtray-desktop" ] -rhino = [ - "rhino-runtime", - "rhino-engine", +polyglot = [ + "polyglot-core", + "polyglot-graaljs", ] settings = [ "settings-core",