Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package io.roastedroot.quickjs4j.core;

import java.nio.charset.StandardCharsets;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;

public class BasicScriptCache implements ScriptCache, AutoCloseable {
private static final String DEFAULT_MESSAGE_DIGEST_ALGORITHM = "SHA-256";

private final HashMap<String, byte[]> cache;
private final HashMap<ByteBuffer, byte[]> cache;
private final MessageDigest messageDigest;

public BasicScriptCache() {
Expand All @@ -24,19 +24,20 @@ public BasicScriptCache(String messageDigestAlgorithm) {
}
}

private ByteBuffer key(byte[] code) {
return ByteBuffer.wrap(messageDigest.digest(code));
}

public boolean exists(byte[] code) {
var key = messageDigest.digest(code);
return cache.containsKey(new String(key, StandardCharsets.UTF_8));
return cache.containsKey(key(code));
}

public void set(byte[] code, byte[] compiled) {
var key = messageDigest.digest(code);
cache.put(new String(key, StandardCharsets.UTF_8), compiled);
cache.put(key(code), compiled);
}

public byte[] get(byte[] code) {
var key = messageDigest.digest(code);
return cache.get(new String(key, StandardCharsets.UTF_8));
return cache.get(key(code));
}

public void close() {
Expand Down
56 changes: 31 additions & 25 deletions core/src/main/java/io/roastedroot/quickjs4j/core/Engine.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
@WasmModuleInterface(WasmResource.absoluteFile)
public final class Engine implements AutoCloseable {
private static final int ALIGNMENT = 1;

private static final byte[] NULL_BYTES = "null".getBytes(UTF_8);
public static final ObjectMapper DEFAULT_OBJECT_MAPPER = new ObjectMapper();

private final ByteArrayOutputStream stdout;
Expand Down Expand Up @@ -120,11 +122,16 @@ private String readJavyString(int ptr, int len) {

public Object invokeGuestFunction(
String moduleName, String name, List<Object> args, String libraryCode) {
return invokeGuestFunction(moduleName, name, args, libraryCode.getBytes(UTF_8));
}

public Object invokeGuestFunction(
String moduleName, String name, List<Object> args, byte[] libraryCode) {
return invokePrecompiledGuestFunction(
moduleName, name, args, compilePortableGuestFunction(libraryCode));
}

public String invokeFunction() {
private String invokeFunction() {
var funInvoke =
"globalThis[quickjs4j_engine.module_name()][quickjs4j_engine.function_name()](...JSON.parse(quickjs4j_engine.args()))";
Function<String, String> setResult =
Expand All @@ -146,24 +153,24 @@ public String invokeFunction() {
// we compile a static version of the module and we invoke it parametrically using a basic
// protocol
// { moduleName: "..", functionName: "..", args: "stringified args" }
public byte[] compilePortableGuestFunction(byte[] libraryCode) {
return compilePortableGuestFunction(new String(libraryCode, UTF_8));
}

public byte[] compilePortableGuestFunction(String libraryCode) {
int codePtr = 0;
try {
var invokeFunction = invokeFunction();

String jsCode =
new String(jsPrelude(), UTF_8)
+ "\n"
+ libraryCode
+ "\n"
+ new String(jsSuffix(), UTF_8)
+ "\n"
+ invokeFunction
+ ";\n";

// System.out.println(jsCode);

codePtr = compileRaw(jsCode.getBytes(UTF_8));
var buf = new StringBuilder();
buf.append(jsPrelude());
buf.append('\n');
buf.append(libraryCode);
buf.append('\n');
buf.append(jsSuffix());
buf.append('\n');
buf.append(invokeFunction());
buf.append(";\n");

codePtr = compileRaw(buf.toString().getBytes(UTF_8));
return readCompiled(codePtr);
} finally {
if (codePtr != 0) {
Expand Down Expand Up @@ -267,11 +274,10 @@ private long[] invokeBuiltin(Instance instance, long[] args) {
}
}

var returnStr =
var returnBytes =
(returnType == Void.class)
? "null"
: mapper.writerFor(returnType).writeValueAsString(res);
var returnBytes = returnStr.getBytes();
? NULL_BYTES
: mapper.writerFor(returnType).writeValueAsBytes(res);

var returnPtr =
exports.canonicalAbiRealloc(
Expand Down Expand Up @@ -315,7 +321,7 @@ private long[] invokeBuiltin(Instance instance, long[] args) {
this::invokeBuiltin);

// This function dynamically generates the global functions defined by the Builtins
private byte[] jsPrelude() {
private String jsPrelude() {
var preludeBuilder = new StringBuilder();
for (Map.Entry<String, Builtins> builtin : builtins.entrySet()) {
preludeBuilder.append("globalThis." + builtin.getKey() + " = {};\n");
Expand All @@ -332,11 +338,11 @@ private byte[] jsPrelude() {
+ "\", JSON.stringify(args))) };\n");
}
}
return preludeBuilder.toString().getBytes();
return preludeBuilder.toString();
}

// This function dynamically generates the js handlers for Invokables
private byte[] jsSuffix() {
private String jsSuffix() {
var suffixBuilder = new StringBuilder();
for (Map.Entry<String, Invokables> invokable : invokables.entrySet()) {
// The object is already defined by the set_result, just add the handlers
Expand All @@ -352,15 +358,15 @@ private byte[] jsSuffix() {
+ ";\n");
}
}
return suffixBuilder.toString().getBytes();
return suffixBuilder.toString();
}

public int compile(String js) {
return compile(js.getBytes(UTF_8));
}

public int compile(byte[] js) {
byte[] prelude = jsPrelude();
byte[] prelude = jsPrelude().getBytes(UTF_8);
byte[] jsCode = new byte[prelude.length + js.length];
System.arraycopy(prelude, 0, jsCode, 0, prelude.length);
System.arraycopy(js, 0, jsCode, prelude.length, js.length);
Expand Down
7 changes: 5 additions & 2 deletions core/src/main/java/io/roastedroot/quickjs4j/core/Runner.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@ private Runner(Engine engine, int timeout, int compilationTimeout, ExecutorServi
}

public byte[] compile(String code) {
return compile(code.getBytes(StandardCharsets.UTF_8));
}

public byte[] compile(byte[] code) {
return submitWithTimeout(
() -> {
byte[] codeBytes = code.getBytes(StandardCharsets.UTF_8);
int codePtr = engine.compile(codeBytes);
int codePtr = engine.compile(code);
try {
return engine.readCompiled(codePtr);
} finally {
Expand Down
Loading