[WIP] Switch to GraalJS Engine (#793)

* Switch to GraalJS Engine

* Update Polygot
This commit is contained in:
Mitchell Syer
2024-11-17 15:24:08 -05:00
committed by GitHub
parent 9a7344ccbe
commit 746f9f1a11
3 changed files with 67 additions and 38 deletions

View File

@@ -36,8 +36,8 @@ dependencies {
// AndroidX annotations // AndroidX annotations
compileOnly(libs.android.annotations) compileOnly(libs.android.annotations)
// substitute for duktape-android // substitute for duktape-android/quickjs
implementation(libs.bundles.rhino) implementation(libs.bundles.polyglot)
// Kotlin wrapper around Java Preferences, makes certain things easier // Kotlin wrapper around Java Preferences, makes certain things easier
implementation(libs.bundles.settings) implementation(libs.bundles.settings)

View File

@@ -1,69 +1,98 @@
package app.cash.quickjs; package app.cash.quickjs;
import org.mozilla.javascript.ConsString; import org.graalvm.polyglot.*;
import org.mozilla.javascript.NativeArray;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.io.Closeable; import java.io.Closeable;
import java.math.BigInteger;
import java.util.Arrays;
public final class QuickJs implements Closeable { public final class QuickJs implements Closeable {
private ScriptEngine engine; private Context context;
public static QuickJs create() { public static QuickJs create() {
return new QuickJs(new ScriptEngineManager()); return new QuickJs();
} }
public QuickJs(ScriptEngineManager manager) { public QuickJs() {
this.engine = manager.getEngineByName("rhino"); 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); return this.evaluate(script);
} }
public Object evaluate(String script) { public Object evaluate(String script) {
try { try {
Object value = engine.eval(script); Value value = context.eval("js", script);
return translateType(value); return translateType(value);
} catch (Exception exception) { } catch (Exception exception) {
throw new QuickJsException(exception.getMessage(), exception); throw new QuickJsException(exception.getMessage(), exception);
} }
} }
private Object translateType(Object obj) { private Object translateType(Value obj) {
if (obj instanceof NativeArray) { if (obj.isBoolean()) {
NativeArray array = (NativeArray) obj; return obj.asBoolean();
long length = array.getLength(); } else if (obj.hasArrayElements()) {
Object[] objects = new Object[(int) length]; if (obj.getArraySize() == 0) {
for (int i = 0; i < (int) length; i++) { return new int[0];
objects[i] = translateType(array.get(i)); } 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; } else if (obj.isNumber()) {
} if (obj.fitsInInt()) {
if (obj instanceof ConsString) { return obj.asInt();
ConsString consString = (ConsString) obj; } else if (obj.fitsInBigInteger()) {
return consString.toString(); return obj.asBigInteger().longValue();
} } else {
if (obj instanceof Long) { return obj.asDouble();
Long value = (Long) obj; }
return value.intValue(); } else if (obj.isHostObject()) {
return obj.asHostObject();
} else if (obj.isString()) {
return obj.asString();
} }
return obj; return obj;
} }
public byte[] compile(String sourceCode, String fileName) { public byte[] compile(String sourceCode, String ignoredFileName) {
return sourceCode.getBytes(); return sourceCode.getBytes();
} }
public Object execute(byte[] bytecode) { public Object execute(byte[] bytecode) {
return this.evaluate(new String(bytecode)); return this.evaluate(new String(bytecode));
} }
public <T> void set(String name, Class<T> ignoredType, T object) {
context.getBindings("js").putMember(name, object);
}
@Override @Override
public void close() { public void close() {
this.engine = null; this.context.leave();
this.context.close();
this.context = null;
} }
} }

View File

@@ -3,11 +3,11 @@ kotlin = "2.0.21"
coroutines = "1.9.0" coroutines = "1.9.0"
serialization = "1.7.3" serialization = "1.7.3"
okhttp = "5.0.0-alpha.14" # Major version is locked by Tachiyomi extensions 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` jackson = "2.18.1" # jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency`
exposed = "0.40.1" exposed = "0.40.1"
dex2jar = "v64" # Stuck until https://github.com/ThexXTURBOXx/dex2jar/issues/27 is fixed 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" settings = "1.2.0"
twelvemonkeys = "3.12.0" twelvemonkeys = "3.12.0"
graphqlkotlin = "8.2.1" graphqlkotlin = "8.2.1"
@@ -116,8 +116,8 @@ bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.79"
android-annotations = "androidx.annotation:annotation:1.9.1" android-annotations = "androidx.annotation:annotation:1.9.1"
# Substitute for duktape-android # Substitute for duktape-android
rhino-runtime = { module = "org.mozilla:rhino-runtime", version.ref = "rhino" } # slimmer version of 'org.mozilla:rhino' polyglot-core = { module = "org.graalvm.polyglot:polyglot", version.ref = "polyglot" }
rhino-engine = { module = "org.mozilla:rhino-engine", version.ref = "rhino" } # provides the same interface as 'javax.script' a.k.a Nashorn polyglot-graaljs = { module = "org.graalvm.polyglot:js-community", version.ref = "polyglot" } # provides the same interface as 'javax.script' a.k.a Nashorn
# Settings # Settings
settings-core = { module = "com.russhwolf:multiplatform-settings-jvm", version.ref = "settings" } settings-core = { module = "com.russhwolf:multiplatform-settings-jvm", version.ref = "settings" }
@@ -219,9 +219,9 @@ systemtray = [
"systemtray-utils", "systemtray-utils",
"systemtray-desktop" "systemtray-desktop"
] ]
rhino = [ polyglot = [
"rhino-runtime", "polyglot-core",
"rhino-engine", "polyglot-graaljs",
] ]
settings = [ settings = [
"settings-core", "settings-core",