为 Duktape 添加 JavaScript 模块化加载能力

Duktape 是一个体积小巧、可移植性高、适合嵌入到各种环境中的 JavaScript 引擎。

最近需要将 protobuf.js 移植到 Duktape 里边运行起来,所以需要解决 JavaScript 模块化加载问题,也就是要支持 require, module.exports 语法。我们通过 modSearch 函数来实现模块化加载:

实现 modSearch 函数

Implementing a native modSearch() function这篇 guide 里边有说通过在 native 实现 modSearch 函数就可以在 JavaScript 里通过require的时候加载到别的模块。

我在 c 层实现 modSearch 函数如下:

//
//  main.c
//  duktape
//
//  Created by faywong on 16/3/18.
//  Copyright © 2016年 faywong. All rights reserved.
//

#include <stdio.h>
#include "duktape.h"
#include "fileio.h"

duk_ret_t my_mod_search(duk_context *ctx) {
    /*
     *   index 0: id (string)
     *   index 1: require (object)
     *   index 2: exports (object)
     *   index 3: module (object)
     */
    printf("fun: %s in, id: %s\n", __FUNCTION__, duk_require_string(ctx, 0));

    const char *id = duk_require_string(ctx, 0);
    duk_pop_n(ctx, duk_get_top(ctx));

    const int FILE_PATH_LEN = 1024;
    char file[FILE_PATH_LEN];
    memset(file, 0, FILE_PATH_LEN);
    snprintf(file, FILE_PATH_LEN, "/Users/faywong/%s.js", id);

    duk_push_string_file(ctx, file);
    return 1;
}

/*
 * Register Duktape.modSearch
 */
void register_mod_search(duk_context *ctx) {
    duk_eval_string(ctx, "(function (fun) { Duktape.modSearch = fun; })");
    duk_push_c_function(ctx, my_mod_search, 4 /*nargs*/);
    duk_call(ctx, 1);
    duk_pop(ctx);
}

int main(int argc, const char * argv[]) {

    duk_context *ctx = duk_create_heap_default();

    if (ctx) {

        register_mod_search(ctx);

        register_fileio(ctx);

        duk_eval_file(ctx, "/Users/faywong/test.js");
        printf("result is: %s\n", duk_safe_to_string(ctx, -1));
        duk_pop(ctx);
    }

    return 0;
}

test.js 用以验证实现的模块化加载功能是否正常,内容如下:

var ByteBuffer = require('bytebuffer');
var test = new ByteBuffer(10);
print('step 1, ByteBuffer ok: ' + test.toString());
var ProtoBuf = require('protobuf');
print('step 2, ProtoBuf ok: ' + (typeof ProtoBuf));
print('step 3, typeof ProtoBuf.loadProtoFile: ' + (typeof ProtoBuf.loadProtoFile));
var builder = ProtoBuf.loadProtoFile('/Users/faywong/complex.proto');
print('step 4, typeof builder: ' + (typeof builder));

Game = builder.build("Game"),
Car = Game.Cars.Car;

// OR: Construct with values from an object, implicit message creation (address) and enum values as strings:
var car = new Car({
    "model": "Rustywq",
    "vendor": {
        "name": "Iron Inc.",
        "address": {
            "country": "US"
        }
    },
    "speed": "SUPERFAST" // also equivalent to "speed": 2
});

// OR: It's also possible to mix all of this!

// Afterwards, just encode your message:
var buffer = car.encode();

print('step 5, typeof buffer: ' + (typeof buffer) + ' toString(): ' + buffer.toString());
 

其中:

  • register_mod_search 函数用于向 Duktape 注册一个用于加载 JavaScript 模块的函数 my_mod_search,该函数有四个入参,分别为模块 id、发起 require 的模块、本模块的 exports 对象、本模块的 module 对象,该函数加载 /Users/faywong 目录下以 id 为主文件名(比如在 test.js 中 require 到的 bytebuffer, protobuf)的 JavaScript 文件并将文件内容返回给 Duktape
  • 为了方便,test.js 中 require 的其他 JavaScript 模块被笔者放在了自己的家目录下:
    /Users/faywong/bytebuffer.js
    /Users/faywong/protobuf.js
    /Users/faywong/test.js
上一篇:git pull --rebase


下一篇:乍暖还寒也不怕 浅谈物联网智能温度控制器