过一段时间可能要用qjs,参考着github上的中文翻译再加上自己的机翻整理了一下,方便以后看
作为小白来说这东西真心看的一脸懵逼,很多地方看不懂
慢慢来吧~等变强了就看得懂了
QuickJS是一个小型并且可嵌入的Javascript引擎,它支持ES2020规范,包括模块,异步生成器和代理器。
它可选支持数学扩展,例如大整数 (BigInt),大浮点数 (BigFloat) 以及运算符重载。
提供Makefile可以在Linux或者MacOS/X上编译。通过使用MinGW工具在Linux主机上进行交叉编译,可以获得初步的Windows支持。
如果要选择特定选项,请编辑Makefile
顶部,然后运行make
。
使用root身份执行 make install
可以将编译的二进制文件和支持文件安装到 /usr/local
(这不是使用QuickJS所必需的).
qjs
是命令行解析器 (Read-Eval-Print Loop). 您可以将Javascript文件和/或表达式作为参数传递以执行它们:
./qjs examples/hello.js
qjsc
是命令行编译器:
./qjsc -o hello examples/hello.js
./hello
生成一个没有外部依赖的 hello
可执行文件。
用法: qjs [options] [files]
选项 | 说明 |
---|---|
-h / --help | 命令行选项列表 |
-e EXPR / --eval EXPR | 执行EXPR |
-i / --interactive | 转到交互模式(在命令行上提供文件时,它不是默认模式). |
-m / --module | 作为ES6模块加载(默认=自动检测)。如果一个模块的文件名的扩展名是.mjs,或者源文件的第一个关键字是import,则模块会被自动检测。 |
–script | 作为ES6脚本加载(默认=自动检测)。 |
–bignum | 启用bignum扩展。BigDecimal对象、BigFloat对象和 "use math "指令。 |
-I file / --include file | 包括一个附加的文件。 |
高级选项:
选项 | 说明 |
---|---|
–std | 使加载的脚本可以使用std和os模块,即使它不是一个模块。 |
-d / --dump | 转存内存使用情况统计信息。 |
-q / --quit | 只是实例化解释器并退出。 |
用法: qjsc [options] [files]
选项 | 说明 |
---|---|
-c | 只输出C文件中的字节码。默认是输出一个可执行文件。 |
-e | 在一个C文件中输出main()和字节码。默认是输出一个可执行文件 |
-o output | 设置输出文件名(默认= out.c或a.out)。 |
-N cname | 设置生成数据的C名称。 |
-m | 编译为Javascript模块(默认=自动检测)。 |
-D module_name | 编译一个动态加载的模块和它的依赖项。当你的代码使用import关键字或os.Worker构造函数时,需要这个选项,因为编译器不能静态地找到动态加载的模块的名称。 |
-M module_name[,cname] | 为一个外部C模块添加初始化代码。见c_module示例 |
-x | 字节交换输出(仅用于交叉编译)。 |
-flto | 使用链接时间优化。编译速度较慢,但可执行文件较小 和更快。当使用-fno-x选项时,这个选项会自动设置。 |
-fno-[eval|string-normalize|regexp|json|proxy|map|typedarray|promise|bigint] | 禁用选定的语言功能,以产生一个较小的可执行文件。 |
-fbignum | 启用bignum扩展:BigDecimal对象、BigFloat对象和 "use math "指令。 |
该qjscalc
应用程序是qjsbn
命令行解释器的超集,它实现了一个具有任意大整数和浮点数,分数,复数,多项式和矩阵的Javascript计算器。源代码在qjscalc.js中。http://numcalc.com上提供了更多文档和Web版本。
运行make test
以运行QuickJS存档中包含的一些内置测试。
test262是一个测试套件,旨在检查JavaScript实现和ECMA-262,ECMAScript语言规范(目前是5.1版)之间的一致性。该测试套件包含数千个单独的测试,每个测试都是对ECMAScript语言规范的一些具体要求进行测试。
一个test262运行器包含在QuickJS档案中。test262的测试可以安装在 QuickJS源代码目录中。
git clone https://github.com/tc39/test262.git test262
cd test262
patch -p1 < ../tests/test262.patch
cd ..
该补丁添加了特定的harness函数,并优化了低效的RegExp正则字符类和Unicode属性转义测试(测试本身没有被修改,只是优化了一个缓慢的字符串初始化函数)。
测试可以通过以下方式运行
make test2
欲了解更多信息,请运行./run-test262,查看test262的命令行选项。
ES2020规范几乎完全得到支持,包括附录B(传统的Web兼容性)和Unicode相关功能。
目前还不支持以下功能:
不支持ECMA402(国际化API)。
"use strip"
不保留调试信息 (包括函数源代码) 以节省内存。 与"use strict"
指令一样,它可以应用全局脚本,或者特定函数。#!
会被忽略数学扩展与标准Javascript完全向后兼容。参见 jsbignum.pdf获取更多信息。
"use math"
启用数学模式,其中整数上的除法和幂运算符产生分数。浮点符号默认为BigFloat,整数符号默认为BigInt。完全支持ES6模块。默认的名称解析规则如下:
.
或 ..
是相对于当前模块的路径。.
或..
是系统模块,例如std
或os
。.so
结尾,是使用QuickJS C API的原生模块默认情况下,标准库包含在命令行解释器中。它包含了两个模块std和os,以及一些全局对象。
scriptArgs
:
提供了命令行参数。第一个参数是脚本名称。
print(...args)
:
打印由空格和尾部换行符分隔的参数。
console.log(...args)
:
与print()
相同
std模块提供了对libc stdlib.h和stdio.h以及其他一些工具的封装。
可用的导出内容:
导出内容 | 描述 |
---|---|
exit(n) | 退出进程 |
evalScript(str, options = undefined) | 将字符串str作为一个脚本运行(全局eval)。 |
loadScript(filename) | 将文件filename以脚本方式运行(全局eval) |
loadFile(filename) | 加载文件名,并假设为UTF-8编码,将其作为一个字符串返回。如果出现I/O错误,则返回null。 |
open(filename, flags, errorObj = undefined) | 打开一个文件(对libc fopen()的封装)。返回FILE对象或在I/O错误的情况下返回空值null。如果errorObj不是undefined,则将其errno属性设置为错误代码,如果未发生错误,则设置为0。 |
popen(command, flags, errorObj = undefined) | 通过创建一个管道打开一个进程(对libc popen()的封装)。返回FILE对象或在I/O错误的情况下返回空值null。如果errorObj不是undefined,将其errno 属性为错误代码,如果没有发生错误则为0。 |
fdopen(fd, flags, errorObj = undefined) | 从一个文件句柄打开一个文件(对libc fdopen()的封装)。返回FILE对象或者在I/O错误的情况下返回空值null。如果errorObj不是undefined,将其errno 属性为错误代码,如果没有错误发生则为0。 |
tmpfile(errorObj = undefined) | 打开一个临时文件。返回FILE对象或在I/O错误的情况下返回空值null。如果 errorObj不是undefined,将其errno属性设置为错误代码,如果没有发生错误发生则为0。 |
puts(str) | 相当于std.out.puts(str)。 |
printf(fmt, …args) | 相当于std.out.printf(fmt, …args)。 |
sprintf(fmt, …args) | 相当于libc的sprintf()。 |
in / out / err | 对libc文件的stdin, stdout, stderr的封装 |
SEEK_SET / SEEK_CUR / SEEK_END | seek()的常量 |
Error | 枚举对象,包含常见错误的整数值(可定义额外的错误代码):EINVAL EIO EACCES EEXIST ENOSPC ENOSYS EBUSY ENOENT EPERM EPIPE |
strerror(errno) | 返回一个描述错误errno的字符串 |
gc() | 手动调用循环清除算法。循环清除算法在需要时自动启动,所以这个功能在特定的内存限制或测试时很有用。 |
getenv(name) | 返回环境变量名称的值,如果没有定义,则返回undefined |
setenv(name, value) | 将环境变量名称的值设置为字符串值 |
unsetenv(name) | 删除环境变量名称。 |
getenviron() | 返回一个包含环境变量键值对的对象。 |
urlGet(url, options = undefined) | 使用curl命令行工具下载url。 options是一个可选的对象,包含以下可选的属性:①binary:布尔值(默认=false)。如果为真,响应是一个ArrayBuffer,而不是字符串。当返回一个字符串时,数据被假定为UTF-8 编码的。 ②full:布尔值(默认=false)。如果为真,则返回一个包含以下属性的对象:response(响应内容)、responseHeaders(用CRLF分隔的头信息)、status(状态码)。当出现协议或网络错误时response为null。如果full为false,则仅在状态码在200到299之间时才返回响应。否则,将返回null。 |
parseExtJSON(str) | 使用JSON.parse的超集来解析str。 |
FILE对象原型的方法:
方法名 | 描述 |
---|---|
close() | 关闭该文件。如果OK则返回0,如果I/O错误则返回-errno。 |
puts(str) | 输出UTF-8编码的字符串。 |
printf(fmt, …args) | 格式化printf,与libc中的printf格式相同。 |
flush() | 刷新缓冲区的文件。 |
seek(offset, whence) | 寻找到一个给定的文件位置(即std.SEEK_*)。偏移量可以是一个数字或一个 bigint。如果OK则返回0,如果I/O错误则返回-errno。 |
tell() | 返回当前文件的位置。 |
tello() | 以bigint形式返回当前文件位置 |
eof() | 如果到达文件末尾,返回true。 |
fileno() | 返回关联的os句柄。 |
error() | 如果有错误,返回true。 |
clearerr() | 清除错误指示。 |
read(buffer, position, length) | 从文件中读取length个字节到ArrayBuffer缓冲区的字节位置(对libc库中fread的封装)。 |
write(buffer, position, length) | 从ArrayBuffer缓冲区的字节位置向文件写入length个字节(对libc库中fwrite的封装)。 |
getline() | 返回文件的下一行,假设为UTF-8编码。不包括尾部的换行。 |
readAsString(max_size = undefined) | 从文件中读取max_size字节,并假设为UTF-8编码,将其作为一个字符串返回。如果max_size不存在,文件就会被读到尽头。 |
getByte() | 返回文件中的下一个字节。如果到达文件的末端,则返回-1。 |
putByte© | 写一个字节到文件。 |
os模块提供操作系统的具体功能。
OS函数通常在OK时返回0,或者返回一个OS特定的负数错误代码。
可用导出内容:
导出内容 | 说明 |
---|---|
open(filename, flags, mode = 0o666) | 打开一个文件。返回一个文件句柄fd,如果出错则返回< 0。其中flags包括:O_RDONLY O_WRONLY O_RDWR O_APPEND O_CREAT O_EXCL O_TRUNC(POSIX打开标志) O_TEXT(Windows专用。以文本模式打开文件。默认是二进制模式) |
close(fd) | 关闭文件句柄fd。 |
seek(fd, offset, whence) | 在文件中寻找。whence参数使用std.SEEK_*表示从何处开始。offset是一个数字或一个bigint。 如果offset是一个bigint,也将返回一个bigint。 |
read(fd, buffer, offset, length) | 从文件句柄fd读取length个字节到ArrayBuffer缓冲区的指定字节位置offset。返回读取的字节数,如果出错则返回< 0。 |
write(fd, buffer, offset, length) | 从ArrayBuffer缓冲区的offset(字节位置)向文件句柄fd写入length个字节 。返回写入的字节数,如果出错则<0。 |
isatty(fd) | 如果fd是一个TTY(终端)句柄,则返回true。 |
ttyGetWinSize(fd) | 返回TTY尺寸为[width, height],如果没有则为空。 |
ttySetRaw(fd) | 将TTY设置为原始模式。 |
remove(filename) | 删除一个文件。如果OK则返回0,否则返回-errno。 |
rename(oldname, newname) | 重命名一个文件。如果OK则返回0,否则返回-errno。 |
realpath(path) | 返回 [str, err] 其中str是规范化绝对路径名,err是错误代码。 |
getcwd() | 返回 [str, err] 其中str是当前工作目录,err是错误代码。 |
chdir(path) | 改变当前目录。如果OK则返回0,否则返回-errno。 |
mkdir(path, mode = 0o777) | 在路径上创建一个目录。如果OK则返回0,否则返回-errno。 |
utimes(path, atime, mtime) | 改变文件路径的访问和修改时间。这些时间被指定为以毫秒为单位,从1970年开始。如果OK则返回0,否则返回-errno。 |
symlink(target, linkpath) | 在linkpath创建一个包含目标字符串的链接。如果OK则返回0,否则返回-errno。 |
readlink(path) | 返回 [str, err] 其中str是链接目标,err是错误代码。 |
readdir(path) | 返回 [array, err] 其中array是一个字符串数组,包含目录路径的文件名,err是错误代码。 |
setReadHandler(fd, func) | 向文件句柄fd中添加一个读取处理程序。 每次 fd 有待处理的数据时都会调用 func。 支持每个文件句柄拥有一个读取处理程序。 使用 func = null 删除读处理程序。 |
setWriteHandler(fd, func) | 向文件句柄 fd 添加一个写处理程序。 每次可以将数据写入 fd 时都会调用 func。 支持每个文件句柄拥有一个写入处理程序。 使用 func = null 删除写处理程序。 |
signal(signal, func) | 当信号发生时调用函数 func 。 每个信号编号仅支持一个处理程序。 使用 null 设置默认处理程序或使用 undefined 忽略信号。 信号处理程序只能在主线程中定义。 |
kill(pid, sig) | 向进程pid发送信号sig |
exec(args[, options]) | 执行一个带有参数args的进程。options是可选参数 |
waitpid(pid, options) | waitpid Unix系统调用。返回数组[ret, status],ret包含-errno 以防出现错误。 |
dup(fd) | dup Unix 系统调用 |
dup2(oldfd, newfd) | dup2 Unix 系统调用 |
pipe() | pipe Unix 系统调用,返回两个句柄[read_fd, write_fd],如果出错则为空 |
sleep(delay_ms) | 休眠delay_ms毫秒 |
setTimeout(func, delay) | 在延迟ms后调用函数func。返回定时器的句柄 |
clearTimeout(handle) | 取消一个定时器 |
platform | 返回一个代表平台的字符串。“linux”、“darwin”、"win32 "或 “js” |
Worker(module_filename) | 这是一个构造函数,该构造函数用于创建一个新的线程(工作者线程),其API与WebWorkers接近。 module_filename是一个字符串,指定在新创建的线程中执行的模块文件名。对于动态导入的模块,它是相对于当前脚本或模块的路径。线程通常不共享任何数据,并且互相之间用消息进行通信。不支持嵌套的工作者。test/test_worker.js中提供了一个例子。 |
其中工作者线程类拥有以下静态属性:
worker对象实例具有以下属性:
postMessage(msg):向相应的worker发送消息。 SharedArrayBuffer 在 worker 之间共享。 当前限制:尚不支持 Map 和 Set。
onmessage:获取器和设置器(Getter and setter)。 设置每次收到消息时调用的函数。该函数拥有一个参数, 该参数是一个对象,有一个包含收到的消息数据的属性。 如果至少有一个非空的 onmessage 处理程序,则线程不会终止。
C API 的设计在简单高效。 C API 在头文件 quickjs.h 中定义。
JSRuntime代表一个对应于对象堆的Javascript运行时。多个运行时可以同时存在,但它们不能交换对象。在一个给定的运行时内,不支持多线程。
JSContext 代表一个 Javascript 上下文(或 Realm)。 每个 JSContext 都有自己的全局对象和系统对象。 每个 JSRuntime 可以有多个 JSContext 并且它们可以共享对象,类似于在 Web 浏览器中共享 Javascript 对象的同源框架。
JSValue 表示一个 Javascript 值,它可以是原始类型或对象。 使用引用计数,因此显式复制(JS_DupValue(),增加引用计数)或释放(JS_FreeValue(),减少引用计数)JSValues 很重要。
C函数可以用JS_NewCFunction()创建。JS_SetPropertyFunctionList()是一个捷径,可以很容易地将函数、设置器和获取器(setters and getters)属性添加到一个给定的对象中。
与其他嵌入式Javascript引擎不同的是,这里没有隐式堆栈,因此 C 函数可以像普通 C 参数一样获取它们的参数。 一般来说,C 函数将常量 JSValues 作为参数(所以他们不需要释放它们),并返回一个新分配的(=live)JSValue。
异常:大多数 C 函数都可以返回 Javascript 异常。 它必须由 C 代码显式测试和处理。 具体的 JSValue JS_EXCEPTION 表示发生了异常。 实际的异常对象存储在 JSContext 中,可以使用 JS_GetException() 检索。
使用JS_Eval()来执行一个脚本或模块的源代码
如果脚本或模块使用 qjsc 编译为字节码,则可以通过调用 js_std_eval_binary()来执行它。 优点是不需要编译,所以它更快更小,因为如果不需要 eval,编译器可以从可执行文件中删除。
注意:字节码格式与特定的QuickJS版本相联系。此外,在执行前没有进行安全检查。这就是为什么在qjsc中没有选项可以将字节码输出到二进制文件。
C不透明数据可以附加到一个Javascript对象上。C不透明数据的类型是由该对象的类ID(JSClassID)决定的。因此,第一步是注册一个新的类ID和JS类(JS_NewClassID(), JS_NewClass())。然后,您可以使用JS_NewObjectClass()创建此类的对象,并使用JS_GetOpaque() / JS_SetOpaque()获取或设置C不透明点。
在定义一个新的 JS 类时,可以声明一个终结器,它会在对象被销毁时调用。 终结器应该用于释放 C 资源。 从中执行JS代码是无效的。 可以提供 gc_mark 方法,以便循环删除算法可以找到此对象引用的其他对象。 其他方法可用于定义奇异对象行为。
类的ID是全局分配的(即对所有运行时)。JSClass是按JSRuntime分配的。JS_SetClassProto()用于在一个给定的JSContext中为一个给定的类定义一个原型。JS_NewObjectClass()在创建的对象中设置这个原型。
在quickjs-libc.c中提供了一些示例。
支持原生 ES6 模块,可以动态或静态链接。 查看 test_bjson 和 bjson.so 示例。 标准库 quickjs-libc.c 也是原生模块的一个很好的例子。
使用JS_SetMemoryLimit()为给定的JSRuntime设置一个全局内存分配限制。
JS_NewRuntime2() 可以提供自定义内存分配函数
可以使用 JS_SetMaxStackSize() 设置最大系统堆栈大小。
使用 JS_SetInterruptHandler() 设置一个回调,引擎在执行代码时会定期调用该回调。 此回调可用于实现执行超时
命令行解释器使用它来实现 Ctrl-C 处理程序。
编译器直接生成字节码,没有解析树等中间表示,因此速度非常快。 对生成的字节码进行了多次优化。
我们选择了基于堆栈的字节码,因为它很简单,能生成紧凑的代码。
对于每个函数,最大堆栈大小是在编译时计算的,因此不需要在运行时进行堆栈溢出测试。
为调试信息维护一个单独的压缩行号表。
对闭包变量的访问进行了优化,几乎与局部变量一样快。
优化了严格模式下的直接eval
qjsc 编译器从 Javascript 文件生成 C 源码。 默认情况下,C 源代码是使用系统编译器(gcc 或 clang)编译的。
生成的 C 源码包含已编译函数或模块的字节码。 如果需要一个完整的可执行文件,它还包含一个 main() 函数和必要的 C 代码来初始化 Javascript 引擎并加载和执行编译的函数和模块。
Javascript代码可以与C模块混合使用
为了拥有更小的可执行文件,可以禁用特定的 Javascript 功能,特别是 eval 或正则表达式。 代码移除了依赖于系统编译器的链接时间优化。
qjsc的工作方式是编译脚本或模块,然后将其序列化为二进制格式。这种格式的一个子集(没有函数或模块)可以作为二进制JSON使用。test_bjson.js这个例子展示了如何使用它。
警告:二进制JSON格式可能会在没有通知的情况下改变,所以它不应该被用来存储持久性数据。test_bjson.js示例仅用于测试二进制对象格式的功能。
字符串存储为 8 位或 16 位字符数组。 因此随机访问字符速度很快。
C API提供了将Javascript字符串转换为C语言UTF-8编码字符串的函数。最常见的情况是,Javascript字符串只包含ASCII字符,不涉及复制。
对象的形状(对象的原型、属性名称和标志)在对象之间共享,以节省内存。
没有holes的数组(除了在数组末尾)已被优化。
TypedArray定型数组的访问已被优化。
对象属性名称和一些字符串存储为Atoms(唯一字符串)以节省内存并允许快速比较。 Atoms表示为 32 位整数。Atoms范围的一半用于保留从0到pow(2,31) - 1的整数
数字被表示为32位有符号整数或64位IEEE-754浮点值。对于 32 位整数,大多数操作都有快速方式。
引用计数用于自动且确定性地释放对象。 当分配的内存变得太大时,会执行单独的循环删除过程。 循环删除算法仅使用引用计数和对象内容,因此不需要在 C 代码中显式的进行垃圾回收。
它是一个Javascript值,可以是原始类型(如Number, String, …),也可以是一个Object。在 32 位版本中,NaN装箱用于存储64位浮点数。该表示法经过了优化,以便可以有效地测试 32 位整数和引用计数值。
在64位代码中,JSValue是128位大的,没有使用NaN编码。其理由是,在64位代码中,内存的使用不太重要。
在这两种情况下(32 位或 64 位),JSValue 正好适合两个 CPU 寄存器,因此它可以被 C 函数有效地返回。
引擎经过优化,因此函数调用速度很快。 系统堆栈保存 Javascript 参数和局部变量。
我们开发了一个特定的正则表达式引擎。它既小又高效,支持所有ES2020的功能,包括Unicode属性。作为Javascript的编译器,它直接生成没有解析树的字节码。
使用显式堆栈的回溯使得系统堆栈上没有递归。简单的量化器经过专门优化,以避免递归。
避免了来自空项的量化器的无限递归。
完整的正则表达式库约 15 LiB(x86 代码),不包括 Unicode 库。
开发了一个特定的 Unicode 库,因此不依赖于外部大型 Unicode 库,例如 ICU。 所有 Unicode 表都被压缩,同时保持合理的访问速度。
该库支持大小写转换、Unicode规范化、Unicode脚本查询、Unicode一般类别查询和所有Unicode二进制属性。
完整的 Unicode 库约 45 KiB(x86 代码)
BigInt、BigFloat和BigDecimal是用libbf库实现的。大小约为 90 KiB(x86代码),提供任意精度的IEEE 754浮点运算和具有精确四舍五入的超越函数。
QuickJS是在MIT许可下发布的。
除非另有说明,QuickJS的版权归Fabrice Bellard和Charlie Gordon所有。