目录

前言

正文

安装

运行

看看官网案例——Hello, world!

看看案例中使用的函数的含义

js_newstate

js_newcfunction

js_setglobal

js_dostring

总体流程

看看官网案例——Configuration file

看看函数含义

js_dofile

js_getglobal就不必细说了

js_tonumber

读取前面的index.js

自定义console

下一个案例——Object manipulation

js_setproperty函数


前言

看到油管上一位大佬使用MuJS,简单来试试,学习一下。

MuJS is a lightweight Javascript interpreter designed for embedding in other software to extend them with scripting capabilities.

视频网址如下,如果能访问到。

https://www.youtube.com/watch?v=4kuxeEnFVYw&list=PLpM-Dvs8t0VYMyfbX11yAd7VAHPILM0kvhttps://www.youtube.com/watch?v=4kuxeEnFVYw&list=PLpM-Dvs8t0VYMyfbX11yAd7VAHPILM0kv

MuJS下载网址。

Index of /downloadshttps://mujs.com/downloads/

MuJShttps://mujs.com/

对于环境,笔者其实很少使用linux,但是笔者windows中有wsl,就在wsl中使用

正文

安装

下载mujs-1.3.7.tar.gz,即

wget https://mujs.com/downloads/mujs-1.3.7.tar.gz

解压

tar -xzvf mujs-1.3.7.tar.gz

进入mujs-1.3.7目录,使用make构建

make

如果构建失败,很可能是网络问题。

在build/debug目录下,生成了三个文件

libmujs.o  mujs  mujs-pp

因为MuJS 只实现ECMA-262,直白的说,mujs可以看成一个ES5引擎。没有let之类的的东西。

运行

在mujs-1.3.7目录下新建一个test目录,然后在其中创建一个js文件

vim index.js

内容如下

let a=1;
let b=2;
console.log(a+b);

运行,结果如下

/mujs-1.3.7/test$ ../build/debug/mujs ./index.js
SyntaxError: ./index.js:1: unexpected token: (identifier) (expected ';')

报错。

修改文件内容

var a=1;
var b=2;
console.log(a+b);

没问题

/mujs-1.3.7/test$ ../build/debug/mujs ./index.js
3

看看官网案例——Hello, world!

在test目录下新建一个test.c文件,其中内容如下

#include <stdio.h>
#include <mujs.h>

static void hello(js_State *J)
{
	const char *name = js_tostring(J, 1);
	printf("Hello, %s!\n", name);
	js_pushundefined(J);
}

int main(int argc, char **argv)
{
	js_State *J = js_newstate(NULL, NULL, JS_STRICT);

	js_newcfunction(J, hello, "hello", 1);
	js_setglobal(J, "hello");

	js_dostring(J, "hello('world');");

	js_freestate(J);
}

这是官网的案例,具体含义先不慌,该如何运行这个test.c文件,

可以使用gcc编译。同时考虑到位置是在test目录下,结合前面的内容,因此,编译命令如下

gcc -I.. test.c ../build/debug/libmujs.o -o test -lm
片段 含义
gcc 调用 GNU 编译器集合。
-I.. 把上一级目录(..)加入头文件搜索路径,使 #include <mujs.h> 能找到。
test.c 要编译的源文件。
../build/debug/libmujs.o 已经编译好的 MuJS 目标文件,里面包含了 js_newstatejs_dostring 等函数的实现。
-o test 指定输出可执行文件名叫 test
-lm 链接数学库。

结果如下

/mujs-1.3.7/test$ gcc -I.. test.c ../build/debug/libmujs.o -o test -lm
/mujs-1.3.7/test$ ./test
Hello, world!

打印了hello world,没问题。

看看案例中使用的函数的含义

js_newstate

首先,先在mujs-1.3.7目录下搜索一下关于这个函数的定义

 grep -rn js_newst

...

mujs.h:52:js_State *js_newstate(js_Alloc alloc, void *actx, int flags);

...

可以发现其中的定义在mujs.h中,且在52行

进去看看,可以发现一些东西

js_State * js_newstate(js_Alloc alloc, void *context, int flags)

/* State constructor flags */
enum {
        JS_STRICT = 1,
};

根据英文意思,大致可以猜测参数的意思。

参数 说明
alloc 内存分配器
context 给分配器携带的用户数据
flags  ES5 的模式。

总之,说白了,这就是一个new,初始化

js_newcfunction

位置如下

mujs.h:175:void js_newcfunction(js_State *J, js_CFunction fun, const char *name, int length);

参数意思如下

参数 含义
J 解释器实例。
fun C 函数指针,必须符合签名 void fun(js_State *J)
name 函数名。
length 形参个数,传 0 表示“可变参数”,1 就告诉 MuJS 这个函数期望 1 个实参。

说白了,“C 函数 → JS 函数

js_setglobal

mujs.h:140:void js_setglobal(js_State *J, const char *name);

参数意思如下

参数 含义
J 解释器实例。
name 全局变量名。

说白了,将注册的函数变成全局的

js_dostring

mujs.h:60:int js_dostring(js_State *J, const char *source);

参数 含义
J 解释器实例。
source JS 源码字符串

说白了,执行js

总体流程

了解了几个函数的意思,大致明白代码在干什么了。

首先,自定义一个c语言函数

然后,变成js函数,

接着,注册成全局函数,

最后,执行。

哈哈哈哈,这个过程有点熟悉,和Webassembly类似。当然,二者还是不一样。

项目 本质 常见用途
MuJS 一个 轻量级 JavaScript 解释器(C 库)。 在 C/C++、嵌入式、桌面、服务端 脚本化,类似 Lua。
WebAssembly 一种 低级字节码格式(不是语言)。 让 C/C++/Rust 等代码以接近原生速度在 浏览器 或 Node.js 里运行。

看看官网案例——Configuration file

js_dofile(J, "config.js")

js_getglobal(J, "foo");
foo = js_tonumber(J, -1);
js_pop(J, 1);

从名字中,可以大致明白,应该是读取js文件,获取全局,转化成c变量,弹出栈顶。

表示疑惑???修改一些东西,运行一下再说

在test目录下新建file.c和config.js文件。

其中file.c的内容如下

#include<stdio.h>
#include<mujs.h>
int main(void){
        js_State *J = js_newstate(NULL, NULL, 0);
        int res=js_dofile(J, "config.js");
        printf("res = %d\n",res);
        js_getglobal(J, "foo");
        double foo = js_tonumber(J, -1);
        js_pop(J, 1);
        printf("foo = %.0f\n", foo);
}

其中config.js的内容如下

var foo=10;

编译并运行

/mujs-1.3.7/test$ gcc -I.. file.c ../build/debug/libmujs.o -o file -lm
/mujs-1.3.7/test$ ./file
res = 0
foo = 10

没问题。

看看函数含义

js_dofile

mujs.h:61:int js_dofile(js_State *J, const char *filename);

参数 说明
J 解释器实例。
filename 待执行的 .js 文件路径。

这个js_dofile包含“读文件 → 编译 → 执行” 。

返回值是int,说明可以知道执行的结果,从上面看来,0是成功了,1就是失败的

js_getglobal就不必细说了

js_tonumber

mujs.h:205:double js_tonumber(js_State *J, int idx);

具体含义

参数 含义
J 解释器实例。
idx 栈索引(正数从底往上,负数从顶往下;-1 就是栈顶)。

意思是把栈里idx位置的 JavaScript 值读出来,转成 C 。

还有js_pop,应该是弹出栈顶的意思,1代表个数。

mujs.h:221:void js_pop(js_State *J, int n);

读取前面的index.js

前面创建一个index.js,修改一下其中的内容,如下

var a=1;
var b=2;
console.log("====================")
console.log(a+b);
console.log("====================")

读取这个文件,看看打印结果,新建一个index.c文件

#include<stdio.h>
#include<mujs.h>
int main(void)
{
        js_State *J = js_newstate(NULL, NULL, 0);
        int res=js_dofile(J, "index.js");
        printf("res = %d\n",res);
}

结果如下

mujs-1.3.7/test$ gcc -I.. index.c ../build/debug/libmujs.o -o index -lm
mujs-1.3.7/test$ ./index
ReferenceError: 'console' is not defined
res = 1

没有定义console!!!!!!!!!!!!!!!!!!!!!!!!

自定义console

在最开始的那位油管上的大佬——使用的js_dostring和js_newcfunction自定义了console

首先,先搜一下console,然后就可以发现这样一个东西

main.c:233:static const char *console_js =
main.c:234:     "var console = { log: print, debug: print, warn: print, error: print };"
main.c:348:     js_dostring(J, console_js);

什么意思?

使用js_dostring定义了console,log对应于print这个函数

所以,只需要自定义print函数就可以了,即,使用前面的js_newcfunction。

而print函数也是可以搜索到的

grep -rn print main.c
.....
118:static void jsB_print(js_State *J)
.....

在main.c的118行,这个jsB_print就是注册的print。

因此,直接最终的代码,

#include<stdio.h>
#include<mujs.h>
static const char *console_js ="var console = { log: print, debug: print, warn: print, error: print };";
static void jsB_print(js_State *J)
{
        int i, n = js_gettop(J);
        for (i = 1; i < n; ++i) {
                const char *s = js_tostring(J, i);
                if (i > 1) putchar(' ');
                fputs(s, stdout);
        }
        putchar('\n');
        js_pushundefined(J);
}
int main(void)
{
        js_State *J = js_newstate(NULL, NULL, 0);
        js_newcfunction(J,jsB_print,"print",0);
        js_setglobal(J,"print");
        js_dostring(J,console_js);
        int res=js_dofile(J, "index.js");
        printf("res = %d\n",res);
}

编译并运行,结果如下

/mujs-1.3.7/test$ gcc -I.. index.c ../build/debug/libmujs.o -o index -lm

/mujs-1.3.7/test$ ./index
====================
3
====================
res = 0

没问题

下一个案例——Object manipulation

// t = { foo: 42, bar: true }

js_newobject(J);
{
	js_pushnumber(J, 42);
	js_setproperty(J, -2, "foo");
	js_pushboolean(J, 1);
	js_setproperty(J, -2, "bar");
}
js_setglobal(J, "t");

意思是很明显的,初始化一个对象,挂到全局。

运行一下,新建一个obj.c文件,结果如下

#include <stdio.h>
#include <mujs.h>
static void jsB_print(js_State *J)
{
        int i, n = js_gettop(J);
        for (i = 1; i < n; ++i) {
                const char *s = js_tostring(J, i);
                if (i > 1) putchar(' ');
                fputs(s, stdout);
        }
        putchar('\n');
        js_pushundefined(J);
}
int main(void)
{
    js_State *J = js_newstate(NULL, NULL, JS_STRICT);
    js_newcfunction(J,jsB_print,"print",0);
    js_setglobal(J,"print");
    js_newobject(J);
    js_pushnumber(J, 42);
    js_setproperty(J, -2, "foo");
    js_pushboolean(J, 1);
    js_setproperty(J, -2, "bar");
    js_setglobal(J, "t");
    js_dostring(J, "print('inline:'); print(JSON.stringify(t));");
    js_freestate(J);
    return 0;
}

js_setproperty函数

mujs.h:146:void js_setproperty(js_State *J, int idx, const char *name);

把栈顶值写到指定对象的指定属性,然后弹出栈顶

参数 含义
J 解释器实例。
idx 目标对象在栈中的索引(负数从顶往下数)。
name 要写入的属性名。

为什么是-2???

因为先把对象压栈,然后把属性压栈,属性就在栈顶,属性是-1,对象是-2,

编译并运行,结果如下

mujs-1.3.7/test$ ./obj
inline:
{"bar":true,"foo":42}

没问题。

行,就到这儿吧,有点意思。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐