QuickJS wrapper for JVM & Android. It is based on HarlonWang's QuickJS.
- Replace QuickJS with QuickJS-NG;
- Correct some bugs.
- Add NativeLibraryLoader;
- Add Logger support;
- Support ESMoudle;
- Cross compile in Linux|Windows|Termux|Android;
- Add junit test cases;
- Java types are supported with JavaScript
- Support promise execute
- JavaScript exception handler
- Compile bytecode
- Supports converting JS object types to Java HashMap.
- ESModule (import, export)
- Support 16KB page size
Experimental Features Stability not guaranteed.
- Supports ArrayBuffer to a byte array type.
QuickJSContext context = QuickJSContext.create();
// evaluating JavaScript
context.evaluate("1 + 2;");
// destroy QuickJSContext
context.close();Or try-with-resource
try(QuickJSContext context = QuickJSContext.create()) {
// evaluating JavaScript
context.evaluate("1 + 2;");
}context.setConsole(your console implementation);
DefaultConsole will be used if not set. DefaultConsole print information to logger if set.| JavaScript | Java |
|---|---|
| null | null |
| undefined | null |
| boolean | Boolean |
| Number | Long/Int/Double |
| string | String |
| Array | JSArray |
| object | JSObject |
| Function | JSFunction |
| ArrayBuffer | byte[](Deep copy) |
Since JavaScript doesn't have a long type, additional information about long:
Java --> JavaScript
- The Long value <= Number.MAX_SAFE_INTEGER, will be convert to Number type.
- The Long value > Number.MAX_SAFE_INTEGER, will be convert to BigInt type.
- Number.MIN_SAFE_INTEGER is the same to above.
JavaScript --> Java
- Number(Int64) or BigInt --> Long type
Java
QuickJSContext context = QuickJSContext.create();
JSObject globalObj = context.getGlobalObject();
JSObject repository = context.createNewJSObject();
obj1.setProperty("name", "QuickJS Wrapper");
obj1.setProperty("created", 2022);
obj1.setProperty("version", 1.1);
obj1.setProperty("signing_enabled", true);
obj1.setProperty("getUrl", (JSCallFunction) args -> {
return "https://github.com/ZhiJianMesh/QuickJS";
});
globalObj.setProperty("repository", repository);
repository.release();JavaScript
repository.name; // QuickJS Wrapper
repository.created; // 2022
repository.version; // 1.1
repository.signing_enabled; // true
repository.getUrl(); // https://github.com/ZhiJianMesh/QuickJSJavaScript
var repository = {
name: 'QuickJS Wrapper',
created: 2022,
version: 1.1,
signing_enabled: true,
getUrl: (name) => { return 'https://github.com/ZhiJianMesh/QuickJS'; }
}Java
QuickJSContext context = QuickJSContext.create();
JSObject globalObject = context.getGlobalObject();
JSObject repository = globalObject.getJSObject("repository");
repository.getString("name"); // QuickJS Wrapper
repository.getInteger("created"); // 2022
repository.getDouble("version"); // 1.1
repository.getBoolean("signing_enabled"); // true
JSFunction fn = repository.getJSFunction("getUrl");
String url = fn.call(); // https://github.com/ZhiJianMesh/QuickJS
fn.release();
repository.release();QuickJSContext context = QuickJSContext.create();
JSObject obj = context.createNewJSObject();
// When not in use, it needs to be released, otherwise it will cause a memory leak.
obj.release();QuickJSContext context = QuickJSContext.create();
JSArray array = context.createNewJSArray();
array.release();QuickJSContext context = createContext();
context.getGlobalObject().setProperty("test", args -> (JSCallFunction) args1 -> "123");
context.evaluate("console.log(test()());");Also, you can view it in QuickJSTest.testReturnJSCallback code
byte[] code = context.compile("'hello, world!'.toUpperCase();");
context.execute(code);Java
// 1. load with string code mode
String js = "export var name = 'Jack';\n"
+ "export var age = 18;\n"
+ "export function report() { return name + ':' + age};"
context.setModuleLoader(new QuickJSContext.DefaultModuleLoader() {
@Override
public String getModuleStringCode(String moduleName) {
if (moduleName.equals("a.js")) {
return js;
}
return null;
}
});
// 2. load with bytecode mode
context.setModuleLoader(new QuickJSContext.BytecodeModuleLoader() {
@Override
public byte[] getModuleBytecode(String moduleName) {
if (moduleName.equals("a.js")) {
return context.compileModule(js, moduleName);
}
return null;
}
});
// 3. use module script with 'evaluate'
Object msg = context.evaluate("import('a.js').then(m => m.name+':'+m.age)"); //Jack:18
// 4. use module script with 'evaluateModule'
Object o = context.evaluateModule("import {name, age, report} from 'a.js'; export {name, age, report}", "c.js");
JSObject module = (JSObject)o;
String name = (String) module.getProperty("name"); //Jack
int age = (Integer)module.getInteger("age"); //18
JSFunction f = module.getJSFunction("report");
String result = (String) f.call(); //Jack:18
//===be sure to release them after using===
f.release();
module.release();
// 5. load module with 'evaluateModule' directly
Object o = context.evaluateModule(
"export var name = 'Jack';\n" +
"export var age = 18;\n" +
"export function report() { return name + ':' + age};",
"a.js");
JSObject module = (JSObject) o;
...We typically recommend releasing reference relationships actively after using Java objects to avoid memory leaks. Additionally, the engine will release unreleased objects when destroy, but it may be a bit later.
JSFunction func = xxx.getJSFunction("test");
func.call();
func.release();
JSObject obj = xxx.getJSObject("test");
int a = obj.getString("123");
obj.release();
// If the return value is an object, it also needs to be released,
JSObject ret = jsFunction.call();
ret.release();
// If you don't need to handle the return value, it is recommended to call the following method.
jsFunction.callVoid(xxx);It's important that if the result is being used in JavaScript, needn't release.
context.getGlobalObject().setProperty("test", new JSCallFunction() {
@Override
public Object call(Object... args) {
JSObject ret = context.createNewJSObject();
// There is no need to call the release method here.
// ret.release();
return ret;
}
});If you are using R8 the shrinking and obfuscation rules are included automatically.
ProGuard users must manually add the options from consumer-rules.pro.
JavaScript runtime context must be used in a single thread at the same time. All execution in JavaScript runtime is guaranteed thread safe. You should isolate contexts within different threads. For example save contexts in a resource pool. When any thread need to use, just apply from the pool.
Support it by joining stargazers for this repository.