QuickJS 快速入门 (QuickJS QuickStart)_橘里_前端开发者

1. QuickJS 快速入门 (QuickJS QuickStart)

  • 1. QuickJS 快速入门 (QuickJS QuickStart)
  • 1. QuickJS 快速入门 (QuickJS QuickStart)

  • 1.1. 简介
  • 1.1. 简介

  • 1.2. 安装
  • 1.2. 安装

  • 1.3. 简单使用
  • 1.3. 简单使用

  • 1.3.1. 控制台执行
  • 1.3.1. 控制台执行

  • 1.3.2. js脚本执行
  • 1.3.2. js脚本执行

  • 1.3.3. 编译二进制文件
  • 1.3.3. 编译二进制文件

  • 1.4. 全局对象
  • 1.4. 全局对象

  • 1.5. std 模块
  • 1.5. std 模块std

  • 1.6. os 模块
  • 1.6. os 模块os

  • 1.7. 自定义C模块
  • 1.7. 自定义C模块

  • 1.7.1. js数据类型在C中的定义
  • 1.7.1. js数据类型在C中的定义

  • 1.7.2. c模块编写
  • 1.7.2. c模块编写

  • 1.7.3. 使用.so模块
  • 1.7.3. 使用.so模块

    1.1. 简介

    QuickJS是一个小型的可嵌入Javascript引擎。它支持ES2020规范,包括模块、异步生成器和代理。它还支持数学扩展,比如大整数(BigInt)、大浮点数(BigFloat)和操作符重载。
    

    QuickJS是一个小型的可嵌入Javascript引擎。它支持ES2020规范,包括模块、异步生成器和代理。它还支持数学扩展,比如大整数(BigInt)、大浮点数(BigFloat)和操作符重载。

    1.2. 安装

  • Linux 直接下载 源码
  • 源码

    make && make install
    

    make && make install

    • MacOS X 下 makefile 有 Bug ,可以直接使用 homebrew 安装
  • MacOS X 下 makefile 有 Bug ,可以直接使用 homebrew 安装
  • Bug

    brew install quickjs 
    

    brew install quickjs

    • 执行 qjs 验证安装成功
  • 执行 qjs 验证安装成功
  • qjs

    1.3. 简单使用

    1.3.1. 控制台执行

    qjs 进入quickjs环境,-h 获取帮助,-q 退出环境。
    直接执行js

    qjs-h-q

    console.log(new Date())
    

    console.log(new Date())

    输出:Wed Aug 14 2019 23:51:43 GMT+0800
       undefined


    (function(){ return 1+1;})()
    

    (function(){ return 1+1;})()

    输出:2


    1.3.2. js脚本执行

    新建一个js脚本,名为hello.js,内容为console.log('hello world !'), 在js目录下执行

    hello.jsconsole.log('hello world !')

    qjs hello.js
    

    qjs hello.js

    输出:hello world !


    1.3.3. 编译二进制文件

    quickjs.hquickjs-libc.hlibquickjs.a 拷贝到js文件同目录下。

    quickjs.hquickjs-libc.hlibquickjs.a

    qjsc -o hello hello.js
    ls
    ./hello
    

    qjsc -o hello hello.js
    ls
    ./hello

    输出:hello world !
    编译出来的可执行文件的大小只有569K(2019-9-18版本为900K),没有任何外部依赖,非常适合嵌入式设备使用。

    1.4. 全局对象

    • scriptArgs 输入的命令行参数,第一个参数为脚本的名称。
    • print(...args)console.log(...args)打印由空格和尾随换行符分隔的参数。
  • scriptArgs 输入的命令行参数,第一个参数为脚本的名称。
  • scriptArgs

  • print(...args)console.log(...args)打印由空格和尾随换行符分隔的参数。
  • print(...args)console.log(...args)

    新建js脚本globle_obj.js

    globle_obj.js

    (function(){
        if(typeof scriptArgs != 'undefined'){
            print(scriptArgs);
            console.log(scriptArgs[1]);
        }
    })()
    

    (function(){
    if(typeof scriptArgs != 'undefined'){
    print(scriptArgs);
    console.log(scriptArgs[1]);
    }
    })()

    qjs globle_obj.js -a 123 1234
    

    qjs globle_obj.js -a 123 1234

    输出:
    globle_obj.js,-a,123,1234
    -a

    1.5. std 模块

    std

    std模块为quickjs-libc提供包装器stdlib.hstdio.h和其他一些实用程序。
    std代码示例:
    创建文件std_m.js

    stdquickjs-libcstdlib.hstdio.h

    std_m.js

    import * as std from 'std';
    var file = std.open('std_open_file.js','w');
    file.puts('var file = std.open(\"std_open_file.txt\",\"w\");\n');
    file.puts('file.puts(\'std_open_file line1\\n\');\n');
    file.puts('file.puts(\'std_open_file line2\\n\');\n');
    file.puts('file.close();\n');
    file.close();
    std.loadScript('std_open_file.js');
    var rdfile = std.open("std_open_file.txt","r");
    do{
        console.log(rdfile.getline());
    }while(!rdfile.eof());
    rdfile.close();
    

    import * as std from 'std';
    var file = std.open('std_open_file.js','w');
    file.puts('var file = std.open(\"std_open_file.txt\",\"w\");\n');
    file.puts('file.puts(\'std_open_file line1\\n\');\n');
    file.puts('file.puts(\'std_open_file line2\\n\');\n');
    file.puts('file.close();\n');
    file.close();
    std.loadScript('std_open_file.js');
    var rdfile = std.open("std_open_file.txt","r");
    do{
    console.log(rdfile.getline());
    }while(!rdfile.eof());
    rdfile.close();

    执行qjs std_m.js ,目录下会生成2个新文件std_open_file.js std_open_file.txt
    控制台输出:
    std_open_file line1
    std_open_file line2
    null

    qjs std_m.jsstd_open_file.jsstd_open_file.txt

    std_open_file line1
    std_open_file line2
    null

    1.6. os 模块

    os

    os 模块提供操作系统特定功能:底层文件访问、信号、计时器、异步 I/O。
    代码示例:

    os

    import * as os from 'os';
    os.remove('hello');
    os.remove('std_open_file.js');
    os.remove('std_open_file.txt');
    

    import * as os from 'os';
    os.remove('hello');
    os.remove('std_open_file.js');
    os.remove('std_open_file.txt');

    删除生成的测试文件

    1.7. 自定义C模块

    ES6模块完全支持。默认名称解析规则如下:

    • 模块名称带有前导.或..是相对于当前模块的路径
    • 模块名称没有前导.或..是系统模块,例如std或os
    • 模块名称以.so结尾,是使用QuickJS C API的原生模块
  • 模块名称带有前导.或..是相对于当前模块的路径
  • 模块名称没有前导.或..是系统模块,例如std或os
  • 模块名称以.so结尾,是使用QuickJS C API的原生模块
  • 使用js文件模块和系统模块,参照引用原生js模块和上面的例子即可,这里就不多赘述。
    这里着重讲解如何编写自己的原生C模块,并且以导入so文件的方式在js代码中使用。

    1.7.1. js数据类型在C中的定义

    typedef union JSValueUnion {
        int32_t int32;          //整数值
        double float64;         //double值
        void *ptr;              //QuickJS引用类型的指针
    } JSValueUnion;             //存放于同一地址,且互斥
    
    typedef struct JSValue {
        JSValueUnion u;         //存放真实数值或着其指针
        int64_t tag;            //JSValue类型的标示符(如 undefined 其 tag == JS_TAG_UNDEFINED)
    } JSValue;
    

    typedef union JSValueUnion {
    int32_t int32; //整数值
    double float64; //double值
    void *ptr; //QuickJS引用类型的指针
    } JSValueUnion; //存放于同一地址,且互斥

    typedef struct JSValue {
    JSValueUnion u; //存放真实数值或着其指针
    int64_t tag; //JSValue类型的标示符(如 undefined 其 tag == JS_TAG_UNDEFINED)
    } JSValue;

    此结构定义在 quickjs.h 中。

    quickjs.h

    1.7.2. c模块编写

    流程如下:

    1. 自定义原生C函数
    2. 定义 QuickJS C 函数
    3. 定义API的函数入口名称及列表
    4. 定义初始化回调方法,将函数入口列表在模块中暴露
    5. 定义初始化模块方法,由系统自动调用,且函数名称不可更改
  • 自定义原生C函数
  • 定义 QuickJS C 函数
  • 定义API的函数入口名称及列表
  • 定义初始化回调方法,将函数入口列表在模块中暴露
  • 定义初始化模块方法,由系统自动调用,且函数名称不可更改
  • 创建编写c_test_m.c文件:

    #include "quickjs.h"
    #include "stdio.h"
    #include "stdlib.h"
    #include "string.h"
    
    #define JS_INIT_MODULE js_init_module
    #define countof(x) (sizeof(x) / sizeof((x)[0]))
    
    /* 自定义原生C函数 */
    static double test_add(int a, double b)
    {
        return a + b;
    }
    
    static char *test_add_str(const char *a, double b)
    {
        /* 要有足够的空间来容纳要拼接的字符串,否则可能会造成缓冲溢出的错误情况 */
        char instr[64];
        sprintf(instr, "%.2f", b);
        char *dest = malloc(128);
        memset(dest, 0, 128);
        strcpy(dest, a);
        char *retdest = strcat(dest, instr);
        return dest;
    }
    
    /* 
        定义 QuickJS C 函数 
        *ctx     : 运行时上下文
        this_val : this对象
        argc     : 入参个数
        *argv    : 入参列表
    */
    static JSValue js_test_add(JSContext *ctx, JSValueConst this_val,
                               int argc, JSValueConst *argv)
    {
        int a;
        double b;
        if (JS_ToInt32(ctx, &a, argv[0]))
            return JS_EXCEPTION;
        if (JS_ToFloat64(ctx, &b, argv[1]))
            return JS_EXCEPTION;
        printf("argc = %d \n", argc);
        printf("a = %d \n", a);
        printf("b = %lf \n", b);
        printf("argv[1].u.float64 = %lf \n", argv[1].u.float64);
        return JS_NewFloat64(ctx, test_add(a, b));
    }
    
    static JSValue js_test_add_str(JSContext *ctx, JSValueConst this_val,
                                   int argc, JSValueConst *argv)
    {
        if (!JS_IsString(argv[0]))
        {
            return JS_EXCEPTION;
        }
        double d;
        if (JS_ToFloat64(ctx, &d, argv[1]))
            return JS_EXCEPTION;
        const char *jscstr = JS_ToCString(ctx, argv[0]);
        printf("JS_ToCString(ctx, argv[0]) = %s \n", jscstr);
        printf("argv[1].u.float64 = %lf \n", argv[1].u.float64);
        char *jsret = test_add_str(jscstr, d);
        return JS_NewString(ctx, jsret);
    }
    
    /* 定义API的函数入口名称及列表 */
    static const JSCFunctionListEntry js_test_funcs[] = {
        /* JS_CFUNC_DEF(函数入口名称,入参个数,QuickJS C 函数) */
        JS_CFUNC_DEF("testAdd", 2, js_test_add),
        JS_CFUNC_DEF("testAddStr", 2, js_test_add_str),
    };
    
    /* 定义初始化回调方法(由系统调用,入参格式固定),将函数入口列表 在模块中暴露 */
    static int js_test_init(JSContext *ctx, JSModuleDef *m)
    {
        return JS_SetModuleExportList(ctx, m, js_test_funcs,
                                      countof(js_test_funcs));
    }
    
    /* 定义初始化模块方法,由系统自动调用,且函数名称不可更改 */
    JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
    {
        JSModuleDef *m;
        m = JS_NewCModule(ctx, module_name, js_test_init);
        if (!m)
            return NULL;
        JS_AddModuleExportList(ctx, m, js_test_funcs, countof(js_test_funcs));
        return m;
    }
    

    #include "quickjs.h"
    #include "stdio.h"
    #include "stdlib.h"
    #include "string.h"

    #define JS_INIT_MODULE js_init_module
    #define countof(x) (sizeof(x) / sizeof((x)[0]))

    /* 自定义原生C函数 */
    static double test_add(int a, double b)
    {
    return a + b;
    }

    static char *test_add_str(const char *a, double b)
    {
    /* 要有足够的空间来容纳要拼接的字符串,否则可能会造成缓冲溢出的错误情况 */
    char instr[64];
    sprintf(instr, "%.2f", b);
    char *dest = malloc(128);
    memset(dest, 0, 128);
    strcpy(dest, a);
    char *retdest = strcat(dest, instr);
    return dest;
    }

    /*
    定义 QuickJS C 函数
    *ctx : 运行时上下文
    this_val : this对象
    argc : 入参个数
    *argv : 入参列表
    */
    static JSValue js_test_add(JSContext *ctx, JSValueConst this_val,
    int argc, JSValueConst *argv)
    {
    int a;
    double b;
    if (JS_ToInt32(ctx, &a, argv[0]))
    return JS_EXCEPTION;
    if (JS_ToFloat64(ctx, &b, argv[1]))
    return JS_EXCEPTION;
    printf("argc = %d \n", argc);
    printf("a = %d \n", a);
    printf("b = %lf \n", b);
    printf("argv[1].u.float64 = %lf \n", argv[1].u.float64);
    return JS_NewFloat64(ctx, test_add(a, b));
    }

    static JSValue js_test_add_str(JSContext *ctx, JSValueConst this_val,
    int argc, JSValueConst *argv)
    {
    if (!JS_IsString(argv[0]))
    {
    return JS_EXCEPTION;
    }
    double d;
    if (JS_ToFloat64(ctx, &d, argv[1]))
    return JS_EXCEPTION;
    const char *jscstr = JS_ToCString(ctx, argv[0]);
    printf("JS_ToCString(ctx, argv[0]) = %s \n", jscstr);
    printf("argv[1].u.float64 = %lf \n", argv[1].u.float64);
    char *jsret = test_add_str(jscstr, d);
    return JS_NewString(ctx, jsret);
    }

    /* 定义API的函数入口名称及列表 */
    static const JSCFunctionListEntry js_test_funcs[] = {
    /* JS_CFUNC_DEF(函数入口名称,入参个数,QuickJS C 函数) */
    JS_CFUNC_DEF("testAdd", 2, js_test_add),
    JS_CFUNC_DEF("testAddStr", 2, js_test_add_str),
    };

    /* 定义初始化回调方法(由系统调用,入参格式固定),将函数入口列表 在模块中暴露 */
    static int js_test_init(JSContext *ctx, JSModuleDef *m)
    {
    return JS_SetModuleExportList(ctx, m, js_test_funcs,
    countof(js_test_funcs));
    }

    /* 定义初始化模块方法,由系统自动调用,且函数名称不可更改 */
    JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
    {
    JSModuleDef *m;
    m = JS_NewCModule(ctx, module_name, js_test_init);
    if (!m)
    return NULL;
    JS_AddModuleExportList(ctx, m, js_test_funcs, countof(js_test_funcs));
    return m;
    }

    quickjs.hquickjs-libc.hlibquickjs.a 拷贝到当前工程目录下。
    执行命令

    quickjs.hquickjs-libc.hlibquickjs.a

    gcc c_test_m.c libquickjs.a  -fPIC -shared -o libtest.so
    

    gcc c_test_m.c libquickjs.a -fPIC -shared -o libtest.so

    生成libtest.so文件。

    libtest.so

    1.7.3. 使用.so模块

    创建js文件 c_test_m.js

    import { testAdd , testAddStr} from 'libtest.so'
    console.log('\n')
    console.log(`testAdd: ${testAdd(1, 0.5)}`)
    console.log('\n')
    console.log(`testAddStr: ${testAddStr('Pi equal to about ', 3.14159)}`)
    console.log('\n')
    

    import { testAdd , testAddStr} from 'libtest.so'
    console.log('\n')
    console.log(`testAdd: ${testAdd(1, 0.5)}`)
    console.log('\n')
    console.log(`testAddStr: ${testAddStr('Pi equal to about ', 3.14159)}`)
    console.log('\n')

    qjs c_test_m.js
    

    qjs c_test_m.js

    输出:
    argc = 2
    a = 1
    b = 0.500000
    argv[1].u.float64 = 0.500000
    testAdd: 1.5

    JS_ToCString(ctx, argv[0]) = Pi equal to about
    argv[1].u.float64 = 3.141590
    testAddStr: Pi equal to about 3.14

    项目地址

    项目地址

    » 本文来自:前端开发者 » 《QuickJS 快速入门 (QuickJS QuickStart)_橘里_前端开发者》
    » 本文链接地址:https://www.rokub.com/73732.html
    » 您也可以订阅本站:https://www.rokub.com
    赞(0)
    64K

    评论 抢沙发

    评论前必须登录!