[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
@@ -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 <T> void set(String name, Class<T> 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;
}
}