Duktape VM使用

2018-10-26  本文已影响0人  袁俊亮技术博客

Duktape使用

下载并编译源码

下载地址:https://www.duktape.org/download.html

本示例使用2.3.0版本

wget https://www.duktape.org/duktape-2.3.0.tar.xz
tar xvf duktape-2.3.0.tar.xz
cd duktape-2.3.0/
make -f Makefile.cmdline

编译完成以后会在根目录下生成一个duk的可执行文件。我们尝试运行duk并编写一个HelloWord的JS代码。如果出现以下结果。说明编译成功

$ ./duk
((o) Duktape 2.3.0 (v2.3.0)
duk> print('Hello world!')
Hello world!
= undefined

JS调C方法

我们在其项目示例的基础上做修改来演示,修改文件在examples/hello/目录下

make -f Makefile.hello

编译完成后会在根目录生成一个hello的可执行文件,然后直接运行hello可执行文件,运行会有如下结果

./hello
Hello world!
2+3=5

修改hello.c文件如下

/*
 *  Very simple example program
 */

#include "duktape.h"
#include <stddef.h>

static duk_ret_t native_print(duk_context *ctx) {
    duk_push_string(ctx, " ");
    duk_insert(ctx, 0);
    duk_join(ctx, duk_get_top(ctx) - 1);
    printf("%s\n", duk_safe_to_string(ctx, -1));
    return 0;
}

// 定义了一个动态参数加法器,可以输入N个参数,最后得到N个参数的和
static duk_ret_t native_adder(duk_context *ctx) {
    int i;
    int n = duk_get_top(ctx);  /* #args */
    double res = 0.0;

    for (i = 0; i < n; i++) {
        res += duk_to_number(ctx, i);
    }

    duk_push_number(ctx, res);
    return 1;  /* one return value */
}

// 此处为多个参数入参,与返回多个参数给JS的实现方式
static duk_ret_t native_person(duk_context *ctx){
    // 当输入多个参数时,参数按照数组下标顺序依次对应取出
    const char *name = duk_safe_to_string(ctx,0);
    const char *sex = duk_safe_to_string(ctx,1);
    size_t age = duk_to_uint32(ctx,2);

    // 多个参数返回可以包装成Object对象,依次存入key/value值返回给JS调用
    duk_idx_t person;
    person = duk_push_object(ctx);

    duk_push_string(ctx,name);
    duk_put_prop_string(ctx,person,"name");

    duk_push_string(ctx,sex);
    duk_put_prop_string(ctx,person,"sex");

    duk_push_int(ctx,age);
    duk_put_prop_string(ctx,person,"age");

    return 1;
}

// 加载JS脚本文件方法
char *loadJS(const char *filepath, size_t *size) {
  if (size != NULL) {
    *size = 0;
  }

  FILE *f = fopen(filepath, "r");
  if (f == NULL) {
    return NULL;
  }

  // get file size.
  fseek(f, 0L, SEEK_END);
  size_t file_size = ftell(f);
  rewind(f);

  char *data = (char *)malloc(file_size + 1);
  size_t idx = 0;

  size_t len = 0;
  while ((len = fread(data + idx, sizeof(char), file_size + 1 - idx, f)) > 0) {
    idx += len;
  }
  *(data + idx) = '\0';

  if (feof(f) == 0) {
    // free(static_cast<void *>(data));
    free(data);
    return NULL;
  }

  fclose(f);

  if (size != NULL) {
    *size = file_size;
  }

  return data;
}

int main(int argc, char *argv[]) {
    // 初始化上下文
    duk_context *ctx = duk_create_heap_default();
    // 初始化参数
    (void) argc; (void) argv;  /* suppress warning */
    // 将自定义C方法压入栈中
    duk_push_c_function(ctx, native_print, DUK_VARARGS);
    // 将print方法注册到global对象中,暴露print方法给JS调用(这里print为对外的方法名)
    duk_put_global_string(ctx, "print");

    // 注册adder方法
    duk_push_c_function(ctx, native_adder, DUK_VARARGS);
    duk_put_global_string(ctx, "adder");

    // 注册person实例化方法
    duk_push_c_function(ctx,native_person,DUK_VARARGS);
    duk_put_global_string(ctx,"newPerson");

    // 使用加载JS脚本的方式
    size_t size = 0;
    // 注意:此处JS脚本文件路径是相对于编译出来的可执行文件的。
    char *jsFile = loadJS("./examples/hello/hello.js",&size);
    duk_eval_string(ctx, jsFile);

    duk_pop(ctx);  /* pop eval result */

    // 释放上下文
    duk_destroy_heap(ctx);
    // 释放文件
    free(jsFile);
    return 0;
}

hello.js内容如下

// JS调用print方法
print('JS call C function')

// JS调用adder方法
var adder_res = adder(2, 3)
print("adder function result = ",adder_res)

// person实例化方法(主要是多参数传入,接收,与返回)
var person = newPerson("john","男",19);
print("person function result = ",JSON.stringify(person))

cd到项目根目录

make -f Makefile.hello
./hello

如果返回如下内容说明调用成功

JS call C function
adder function result =  5
person function result =  {"name":"john","sex":"男","age":19}

C调用JS

修改examples/guide/目录

/* processlines.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "duktape.h"

/* For brevity assumes a maximum file length of 16kB. */
static void push_file_as_string(duk_context *ctx, const char *filename) {
    FILE *f;
    size_t len;
    char buf[16384];

    f = fopen(filename, "rb");
    if (f) {
        len = fread((void *) buf, 1, sizeof(buf), f);
        fclose(f);
        duk_push_lstring(ctx, (const char *) buf, (duk_size_t) len);
    } else {
        duk_push_undefined(ctx);
    }
}

int main(int argc, const char *argv[]) {
    duk_context *ctx = NULL;
    (void) argc; (void) argv;

    ctx = duk_create_heap_default();
    if (!ctx) {
        printf("Failed to create a Duktape heap.\n");
        exit(1);
    }

    push_file_as_string(ctx, "./examples/guide/process.js");
    if (duk_peval(ctx) != 0) {
        printf("Error: %s\n", duk_safe_to_string(ctx, -1));
        goto finished;
    }
    duk_pop(ctx);  /* ignore result */
    duk_push_global_object(ctx);
    duk_get_prop_string(ctx, -1 /*index*/, "processLine");
    // 按照顺序传递参数(对应:line,name,age的参数)
    duk_push_string(ctx, "line value");
    duk_push_string(ctx, "john");
    duk_push_int(ctx, 19);

    // 注意这里中间参数3表示对应上面JS方法要传3个参数(具体根据自定义的JS方法参数个数来确定)
    if (duk_pcall(ctx, 3 /*nargs*/) != 0) {
        printf("Error: %s\n", duk_safe_to_string(ctx, -1));
    } else {
        // 这里将JS代码return的字符串打印出来
        printf("%s\n", duk_safe_to_string(ctx, -1));
    }
    duk_pop(ctx);

 finished:
    duk_destroy_heap(ctx);

    exit(0);
}
// process.js
function processLine(line,name,age) {
    var obj = {line,name,age}
    return JSON.stringify(obj)
}
#
#  Example Makefile for building a program with embedded Duktape.
#
#  There are two source sets in the distribution: (1) combined sources where
#  you only need duktape.c, duktape.h, and duk_config.h, and (2) separate
#  sources where you have a bunch of source and header files.  Whichever
#  you use, simply include the relevant sources into your C project.  This
#  Makefile uses the combined source file.
#

DUKTAPE_SOURCES = src/duktape.c

# Compiler options are quite flexible.  GCC versions have a significant impact
# on the size of -Os code, e.g. gcc-4.6 is much worse than gcc-4.5.

CC = gcc
CCOPTS = -Os -pedantic -std=c99 -Wall -fstrict-aliasing -fomit-frame-pointer
CCOPTS += -I./src  # for combined sources
CCLIBS = -lm
DEFINES =

# If you want a 32-bit build on a 64-bit host
#CCOPTS += -m32

# Use the tools/configure.py utility to modify Duktape default configuration:
# http://duktape.org/guide.html#compiling
# http://wiki.duktape.org/Configuring.html

# For debugging, use -O0 -g -ggdb, and don't add -fomit-frame-pointer

processlines: $(DUKTAPE_SOURCES) examples/guide/processlines.c
    $(CC) -o $@ $(DEFINES) $(CCOPTS) $(DUKTAPE_SOURCES) examples/guide/processlines.c $(CCLIBS)

make -f Makefile.guide

编译完成以后再项目根目录会生成一个processlines可执行文件

在项目根目录执行可执行文件

./processlines

如果返回一下结果,说明C调JS方法成功

{"line":"line value","name":"john","age":19}
上一篇下一篇

猜你喜欢

热点阅读