Giter Club home page Giter Club logo

quickjs-wrapper's Introduction

QuickJS For Android/JVM

QuickJS wrapper for Android/JVM.

Feature

  • Java types are supported with JavaScript
  • Support promise execute
  • JavaScript exception handler
  • Optimize not a function with its name when type error
  • Compile bytecode

Experimental Features Stability not guaranteed.

  • ESModule (import, export)

Download

Maven Central

repositories {
  mavenCentral()
}

dependencies {
  // Pick one:

  // 1. Android - Use wrapper in your public API:
  api 'wang.harlon.quickjs:wrapper-android:latest.version'

  // 2. JVM - Use wrapper in your implementation only:
  implementation 'wang.harlon.quickjs:wrapper-java:latest.version'
}

SNAPSHOT

Wrapper

See how to import the snapshot

Including the SNAPSHOT

Snapshots of the current development version of Wrapper are available, which track the latest versions.

To import snapshot versions on your project, add the code snippet below on your gradle file:

repositories {
   maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' }
}

Next, add the dependency below to your module's build.gradle file:

dependencies {
    // For Android
    implementation "wang.harlon.quickjs:wrapper-android:latest-SNAPSHOT"
    // For JVM
    implementation "wang.harlon.quickjs:wrapper-java:latest-SNAPSHOT"
}

Building the Project

This repository use git submodules and so when you are checking out the app, you'll need to ensure the submodules are initialized properly. You can use the --recursive flag when cloning the project to do this.

git clone --recursive https://github.com/HarlonWang/quickjs-wrapper.git

Alternatively, if you already have the project checked out, you can initialize the submodules manually.

git submodule update --init

Usage

Initialization

In Android Platforms:

// It is usually init in the application.
QuickJSLoader.init();

Refer to here for other platforms.

Create QuickJSContext

QuickJSContext context = QuickJSContext.create();

Destroy QuickJSContext

QuickJSContext context = QuickJSContext.create();
context.destroy();

Evaluating JavaScript

QuickJSContext context = QuickJSContext.create();
context.evaluate("var a = 1 + 2;");

Console Support

QuickJSContext context = QuickJSContext.create();
QuickJSLoader.initConsoleLog(context);
// or custom console.
// QuickJSLoader.initConsoleLog(context, your console implementation.);

Supported Types

Java and JavaScript can directly convert to each other for the following basic types

  • boolean
  • int
  • long
  • double
  • String
  • null

Mutual conversion of JS object types

  • JSObject represents a JavaScript object
  • JSFunction represents a JavaScript function
  • JSArray represents a JavaScript Array

About Long type

There is no Long type in JavaScript, the conversion of Long type is special.

  • 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

Set Property

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/HarlonWang/quickjs-wrapper";
});
globalObj.setProperty("repository", repository);

JavaScript

repository.name; // QuickJS Wrapper
repository.created; // 2022
repository.version; // 1.1
repository.signing_enabled; // true
repository.getUrl(); // https://github.com/HarlonWang/quickjs-wrapper

Get Property

JavaScript

var repository = {
	name: 'QuickJS Wrapper',
	created: 2022,
	version: 1.1,
	signing_enabled: true,
	getUrl: (name) => { return 'https://github.com/HarlonWang/quickjs-wrapper'; }
}

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
repository.getJSFunction("getUrl").call(); // https://github.com/HarlonWang/quickjs-wrapper

Create JSObject in Java

QuickJSContext context = QuickJSContext.create();
JSObject obj = context.createNewJSObject();

Create JSArray in Java

QuickJSContext context = QuickJSContext.create();
JSArray array = context.createNewJSArray();

How to return Function to JavaScript in Java

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

Compile ByteCode

byte[] code = context.compile("'hello, world!'.toUpperCase();");
context.execute(code);

ESModule

Java

// 1. string code mode
context.setModuleLoader(new QuickJSContext.DefaultModuleLoader() {
    @Override
    public String getModuleStringCode(String moduleName) {
       if (moduleName.equals("a.js")) {
           return "export var name = 'Jack';\n" +
                   "export var age = 18;";
       }
       return null;
    }
});

// 2. bytecode mode
context.setModuleLoader(new QuickJSContext.BytecodeModuleLoader() {
    @Override
    public byte[] getModuleBytecode(String moduleName) {
        return context.compileModule("export var name = 'Jack';export var age = 18;", moduleName);
    }
});

JavaScript

import {name, age} from './a.js';

console.log('name:' + name); // Jack
console.log('age:' + age); // 18

R8 / ProGuard

If you are using R8 the shrinking and obfuscation rules are included automatically.

ProGuard users must manually add the options from consumer-rules.pro.

Concurrency

JavaScript runtimes are single threaded. All execution in the JavaScript runtime is guaranteed thread safe, by way of Java synchronization.

Find this repository useful?

Support it by joining stargazers for this repository.
Also, follow me on GitHub for my next creations!

Stargazers over time

Stargazers over time

Reference

quickjs-wrapper's People

Contributors

harlonwang avatar ijuniorfu avatar yubaokang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

quickjs-wrapper's Issues

能否支持JSRuntime创建多个JSContext上下文?

能否支持JSRuntime运行时 创建多个JSContext上下文?

如果只有一个JSContext的话加载多个脚本存在同名属性方法,会导致全局GlobalObjec冲突。

我看quickjs文档是支持一个JSRuntime多个JSContext,每个JSContext都有自己的全局对象和系统对象。

java中什么获取 js Promise 和 async function 的结果呢?

javascript 函数

async function getData(){
	const data = await new Promise((resolve, reject) => {
		const complete = (ok, data) => ok ? resolve(data) : reject(data);
		http(url, complete)
	})
	return data;
}

java方法

client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(@NotNull Call call, @NotNull IOException e) {
        handler().post(() -> complete.call(false, "error"));
    }

    @Override
    public void onResponse(@NotNull Call call, @NotNull Response response) {
        handler().post(() -> complete.call(true, "ok"));
    }
});

JSFunction func = jsObject.getJSFunction("getData");
Object data = func.call();

//什么获取 js 函数 getData 的返回值?

Throw Java exception when JNI error occurred.

Todo: c 层异常后需要抛回到 Java 层,不然外部依赖的是 so 库,无法识别实际错误信息
测试代码如下:

public void testNullExceptionWithProperty() {
        QuickJSContext context = QuickJSContext.create();
        JSObject object = context.createNewJSObject();
        object.getJSObject(null);
        context.destroyContext();
    }

QuickJS fork

Hello! I've seen that you have applied some bugfixes and improvements to QuickJS, which seems to be abandoned. Would you please consider forking the original repository yourself and applying your changes on top? I would be happy to use your fork and possibly contribute with more improvements.

Thanks!

Console 沒有顯示 JS 錯誤訊息

您好,我執行以下代發發現沒有出現錯誤訊息

console.log("666")
console.log(gg())
console.log("路飞开始调试了!!!");

第 2 行應該要顯示 'gg' is not defined

再麻煩了,謝謝。

問題詢問

您好,想請教怎麼讓 QuickJSContext 的 getGlobalObject 可以呼叫 init、home 的 func
image

我有將 js 內容丟進去了
image

Ready to convert Java Class to JS Object

Like this :

In Java

    class TestJava {
        public void log(String message) {
            System.out.println(message);
        }
    }
    
    @Test
    public void testSetJavaClass() {
        QuickJSContext context = QuickJSContext.create();
        context.setProperty(context.getGlobalObject(), "test", TestJava.class);
    }

In JavaScript

test.log("123");
// print 123

Eval Script support ESModule check and load

Reference code: in qjs.c

static int eval_file(JSContext *ctx, const char *filename, int module)
{
    uint8_t *buf;
    int ret, eval_flags;
    size_t buf_len;
    
    buf = js_load_file(ctx, &buf_len, filename);
    if (!buf) {
        perror(filename);
        exit(1);
    }

    if (module < 0) {
        module = (has_suffix(filename, ".mjs") ||
                  JS_DetectModule((const char *)buf, buf_len));
    }
    if (module)
        eval_flags = JS_EVAL_TYPE_MODULE;
    else
        eval_flags = JS_EVAL_TYPE_GLOBAL;
    ret = eval_buf(ctx, buf, buf_len, filename, eval_flags);
    js_free(ctx, buf);
    return ret;
}

ModuleLoader 加载ByteCode后的模块那么路径就会有问题

module_a.js

export function name(){ }

module_b.js

import name from "./module_a.js"

export function name(){ return name() }

然后用 qjsc 编译得到 a 和 b 的 ByteCode。
加载 模块

context.setModuleLoader(new ModuleLoader() {
     @Override
     public boolean isBytecodeMode() {
         return true;
     }

     @Override
     public String getModuleStringCode(String moduleName) {
         return Es6ModuleLoader.get().getModuleStringCode(moduleName);
     }

     @Override
     public byte[] getModuleBytecode(String moduleName) {
		//这里 moduleName 都是相对路径
		//module_a.js  是 http://……/module_a.js 正确
		//module_b.js 是 ./module_b.js 这里就变成了相对路径了、由于没有 base_module_name 所以没有办法合并路径了
     }
});
context.evaluateModule("import * as all from 'http://……/module_a.js'")

不支持long类型

No implementation found for long com.whl.quickjs.wrapper.QuickJSContext.createContext() (tried Java_com_whl_quickjs_wrapper_QuickJSContext_createContext and Java_com_whl_quickjs_wrapper_QuickJSContext_createContext__)

能否支持Java byte写入js的ArrayBuffer?

有没有办法直接创建一个 Uint8Array 或 ArrayBuffer 的 JS 值?这个ArrayBuffer可以通过java来写入和读取?

目前只有JSArray ,只能

JSArray array = context.createNewJSArray();
for (int i = 0; i < bytes.length; i++) array.set((int) bytes[i], i);

这样大数据会非常慢。

Promise 内部执行JS调用Java方法,在JS的callback中执行reject会crash

实际项目中用了类似的逻辑,Promise内部执行JS,这段JS会调用到Java,然后在回调里执行reject(res); so库会崩溃。

下面是模拟实际场景写出的demo,也会出现崩溃,但是错误信息不一致。

//附测试代码
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        QuickJSLoader.init();
        testPromiseCrash();
    }

    //运行app执行,会崩溃
    public void testPromiseCrash() {
        QuickJSContext jsContext = QuickJSContext.create();
        JSObject pofeng = jsContext.createNewJSObject();
        JSObject gol = jsContext.getGlobalObject();
        gol.setProperty("pofeng", pofeng);
        pofeng.setProperty("getSystemInfo", new JSCallFunction() {
            @Override
            public Object call(Object... args) {
                ((JSFunction) ((JSObject) args[0]).getJSObject("success")).call("我来自Java的值");
                return "我来自Java的值";
            }
        });

        String js = "new Promise((resolve, reject) => {\n" +
                "            pofeng.getSystemInfo({\n" +
                "                success: res => {\n" +
                "                    reject(res);\n" +
                "                }\n" +
                "            })\n" +
                "        })";

        try {
            jsContext.evaluate(js);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Native receive promise function with exception

复现代码如下:

    @Test
    public void testPromiseUnHandledExceptionWithCall() {
        QuickJSContext context = QuickJSContext.create();
        context.getGlobalObject().setProperty("nativeCall", new JSCallFunction() {
            @Override
            public Object call(Object... args) {
                JSFunction function = (JSFunction) args[0];
                function.call();
                return null;
            }
        });
        context.evaluate("const getData = async () => {\n" +
                "\tconst res = await new Promise(() => {\n" +
                "\t\twindow.setTimeout();\n" +
                "\t});\n" +
                "}\n" +
                "\n" +
                "// function nativeCall(fn) {\n" +
                "// \tfn.call();\n" +
                "// }\n" +
                "\n" +
                "// console.log(getData);\n" +
                "\n" +
                "nativeCall(getData);");
        context.destroyContext();
    }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.