quickjs到底能干什么

又来讨论QuickJS!!!

QuickJS刚出来就引起了很多人的关注及讨论,但是毕竟比较新而且底层构建又是C语言,所以入门门槛还是挺高的,后面跟进下来的人估计也没那么多。

但是帕奇我还是很敢兴趣的,所以有继续关注下来。

首先QuickJS在知乎也有个个大神发声,有讲解到底是干嘛用的,而已是否是日后趋势:

  • JavaScript 之父也已经钦定,这个在后端很实用。感觉能要拳打 Node.js,脚踢 Deno.ts 的节奏
  • C 实现的 JS 引擎,API 比较舒服一点,有点像 JSC,用起来比较直接,没 V8 那么绕
  • QuickJS的文档说Hello World编译的可执行文件在x86只有190KB,strip hello后hello文件就只有604K了
  • 这就意味着我们写的JavaScript代码可以在各种嵌入式系统中使用,这也是这个项目最大的意义吧。

嗯嗯,上面部分内容节选不要怪帕奇断章取义(笑,大家完全可以去知乎围观:传送门

QuickJS到底能干什么

就目前关注下来,撇开其他因素他能干很多事情,你可以用它做后端服务、嵌入式应用、命令工具等等。但是就C语言环境来看,作为IOT领域的生态工具相对会比较有看头。毕竟C语言的生态不及JS简单,开发效率上也高出许多。从招人成本处考虑,能做成一套体系对公司来说不是会起到很大的作用吗?大概QuickJS就是给我这种感觉🤔。

QuickJS主要功能

  • 轻量而且易于嵌入:只需几个C文件,没有外部依赖,一个x86下的简单的“hello world”程序只要180 KiB。
  • 具有极低启动时间的快速解释器: 在一台单核的台式PC上,大约在100秒内运行ECMAScript 测试套件1 56000次。运行时实例的完整生命周期在不到300微秒的时间内完成。
  • 几乎完整实现ES2019支持,包括: 模块,异步生成器和和完整Annex B支持 (传统的Web兼容性)。
  • 通过100%的ECMAScript Test Suite测试。
  • 可以将Javascript源编译为没有外部依赖的可执行文件。
  • 使用引用计数(以减少内存使用并具有确定性行为)的垃圾收集与循环删除。
  • 数学扩展:BigInt, BigFloat, 运算符重载, bigint模式, math模式.
  • 在Javascript中实现的具有上下文着色和完成的命令行解释器。
  • 采用C包装库构建的内置标准库。

没错,帕奇根本写不出这些东西,上面内容是从quickjs-zh/QuickJS拷贝来的😛。

这里主要提一下的是QuickJS C API,可以从这个文件作为例子:

QuickJS/bjson.c:一个可读取可写入JSON文件的插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include "quickjs-libc.h"
#include "cutils.h"

static JSValue js_bjson_read(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
uint8_t *buf;
uint64_t pos, len;
JSValue obj;
size_t size;

if (JS_ToIndex(ctx, &pos, argv[1]))
return JS_EXCEPTION;
if (JS_ToIndex(ctx, &len, argv[2]))
return JS_EXCEPTION;
buf = JS_GetArrayBuffer(ctx, &size, argv[0]);
if (!buf)
return JS_EXCEPTION;
if (pos + len > size)
return JS_ThrowRangeError(ctx, "array buffer overflow");
obj = JS_ReadObject(ctx, buf + pos, len, 0);
return obj;
}

static JSValue js_bjson_write(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
size_t len;
uint8_t *buf;
JSValue array;

buf = JS_WriteObject(ctx, &len, argv[0], 0);
if (!buf)
return JS_EXCEPTION;
array = JS_NewArrayBufferCopy(ctx, buf, len);
js_free(ctx, buf);
return array;
}

static const JSCFunctionListEntry js_bjson_funcs[] = {
JS_CFUNC_DEF("read", 3, js_bjson_read ),
JS_CFUNC_DEF("write", 1, js_bjson_write ),
};

static int js_bjson_init(JSContext *ctx, JSModuleDef *m)
{
return JS_SetModuleExportList(ctx, m, js_bjson_funcs,
countof(js_bjson_funcs));
}

#ifdef JS_SHARED_LIBRARY
#define JS_INIT_MODULE js_init_module
#else
#define JS_INIT_MODULE js_init_module_bjson
#endif

JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
{
JSModuleDef *m;
m = JS_NewCModule(ctx, module_name, js_bjson_init);
if (!m)
return NULL;
JS_AddModuleExportList(ctx, m, js_bjson_funcs, countof(js_bjson_funcs));
return m;
}

下面是调用的代码:

QuickJS/tests/test_bjson.js:测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import * as bjson from "../bjson.so";

function assert(b, str)
{
if (b) {
return;
} else {
throw Error("assertion failed: " + str);
}
}

function toHex(a)
{
var i, s = "", tab, v;
tab = new Uint8Array(a);
for(i = 0; i < tab.length; i++) {
v = tab[i].toString(16);
if (v.length < 2)
v = "0" + v;
if (i !== 0)
s += " ";
s += v;
}
return s;
}

function toStr(a)
{
var s, i, props, prop;

switch(typeof(a)) {
case "object":
if (a === null)
return "null";
if (Array.isArray(a)) {
s = "[";
for(i = 0; i < a.length; i++) {
if (i != 0)
s += ",";
s += toStr(a[i]);
}
s += "]";
} else {
props = Object.keys(a);
s = "{";
for(i = 0; i < props.length; i++) {
if (i != 0)
s += ",";
prop = props[i];
s += prop + ":" + toStr(a[prop]);
}
s += "}";
}
return s;
case "undefined":
return "undefined";
case "string":
return a.__quote();
case "number":
case "bigfloat":
if (a == 0 && 1 / a < 0)
return "-0";
else
return a.toString();
break;
default:
return a.toString();
}
}

function bjson_test(a)
{
var buf, r, a_str, r_str;
a_str = toStr(a);
buf = bjson.write(a);
if (0) {
print(a_str, "->", toHex(buf));
}
r = bjson.read(buf, 0, buf.byteLength);
r_str = toStr(r);
if (a_str != r_str) {
print(a_str);
print(r_str);
assert(false);
}
}

function bjson_test_all()
{
var obj;

bjson_test({x:1, y:2, if:3});
bjson_test([1, 2, 3]);
bjson_test([1.0, "aa", true, false, undefined, null, NaN, -Infinity, -0.0]);
if (typeof BigInt !== "undefined") {
bjson_test([BigInt("1"), -BigInt("0x123456789"),
BigInt("0x123456789abcdef123456789abcdef")]);
}
if (typeof BigFloat !== "undefined") {
BigFloatEnv.setPrec(function () {
bjson_test([BigFloat("0.1"), BigFloat("-1e30"), BigFloat("0"),
BigFloat("-0"), BigFloat("Infinity"), BigFloat("-Infinity"),
0.0 / BigFloat("0"), BigFloat.MAX_VALUE,
BigFloat.MIN_VALUE]);
}, 113, 15);
}

/* tested with a circular reference */
obj = {};
obj.x = obj;
try {
bjson.write(obj);
assert(false);
} catch(e) {
assert(e instanceof TypeError);
}
}

bjson_test_all();

能用C语言基于系统级别进行编码,系统级别功能在这里处理了,然后让不懂C的另一位盆友在JS层面调用这套方式简直不要太棒了🌻。

当然目前基于Quickjs的生态还没有起来,某些基层需求还需要慢慢累积或者自行开发才会有。

再深入的研究帕奇也不是很懂,就不继续嫌丑下次文章再见👋。