QuickJS 快速入門 (QuickJS QuickStart)


1. QuickJS 快速入門 (QuickJS QuickStart)

1.1. 簡介

QuickJS是一個小型的可嵌入Javascript引擎。它支持ES2020規范,包括模塊、異步生成器和代理。它還支持數學擴展,比如大整數(BigInt)、大浮點數(BigFloat)和操作符重載。

1.2. 安裝

make && make install
  • MacOS X 下 makefile 有 Bug ,可以直接使用 homebrew 安裝
brew install quickjs 
  • 執行 qjs 驗證安裝成功

1.3. 簡單使用

1.3.1. 控制台執行

qjs 進入quickjs環境,-h 獲取幫助,-q 退出環境。
直接執行js:

console.log(new Date())

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

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

輸出:2

1.3.2. js腳本執行

新建一個js腳本,名為hello.js,內容為console.log('hello world !'), 在js目錄下執行

qjs hello.js

輸出:hello world !

1.3.3. 編譯二進制文件

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

qjsc -o hello hello.js
ls
./hello

輸出:hello world !
編譯出來的可執行文件的大小只有569K(2019-9-18版本為900K),沒有任何外部依賴,非常適合嵌入式設備使用。

1.4. 全局對象

  • scriptArgs 輸入的命令行參數,第一個參數為腳本的名稱。
  • print(...args)console.log(...args)打印由空格和尾隨換行符分隔的參數。

新建js腳本globle_obj.js

(function(){
    if(typeof scriptArgs != 'undefined'){
        print(scriptArgs);
        console.log(scriptArgs[1]);
    }
})()
qjs globle_obj.js -a 123 1234

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

1.5. std 模塊

std模塊為quickjs-libc提供包裝器stdlib.hstdio.h和其他一些實用程序。
std代碼示例:
創建文件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();

執行qjs std_m.js ,目錄下會生成2個新文件std_open_file.js std_open_file.txt
控制台輸出:
std_open_file line1
std_open_file line2
null

1.6. os 模塊

os 模塊提供操作系統特定功能:底層文件訪問、信號、計時器、異步 I/O。
代碼示例:

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的原生模塊

使用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;

此結構定義在 quickjs.h 中。

1.7.2. c模塊編寫

流程如下:

  1. 自定義原生C函數
  2. 定義 QuickJS C 函數
  3. 定義API的函數入口名稱及列表
  4. 定義初始化回調方法,將函數入口列表在模塊中暴露
  5. 定義初始化模塊方法,由系統自動調用,且函數名稱不可更改

創建編寫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;
}

quickjs.hquickjs-libc.hlibquickjs.a 拷貝到當前工程目錄下。
執行命令

gcc c_test_m.c libquickjs.a  -fPIC -shared -o 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')
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




項目地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM