Giter Site home page Giter Site logo

quickjs-zh / quickjs Goto Github PK

View Code? Open in Web Editor NEW
3.0K 86.0 289.0 2.87 MB

QuickJS是一个小型并且可嵌入的Javascript引擎,它支持ES2020规范,包括模块,异步生成器和代理器。

License: Other

Makefile 0.41% C 92.30% JavaScript 7.17% Shell 0.12%
quickjs compiler javascript c zh javascript-engine es2020 js ecmascript vm

quickjs's Introduction

QuickJS Javascript引擎

目录

1 简介

QuickJS是一个小型并且可嵌入的Javascript引擎,它支持ES2020规范,包括模块,异步生成器和代理器。

它可选支持数学扩展,例如大整数 (BigInt),大浮点数 (BigFloat) 以及运算符重载。


官方站点:https://bellard.org/quickjs/

中文站点:https://github.com/quickjs-zh/

QuickJS QQ群:598609506

中文Wiki:https://github.com/quickjs-zh/QuickJS/wiki

1.1 主要功能

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

1.2 基准测试

点击查看QuickJS基准测试具体内容

2 用法

2.1 安装

提供Makefile可以在Linux或者MacOS/X上编译。通过使用MinGW工具在Linux主机上进行交叉编译,可以获得初步的Windows支持。

如果要选择特定选项,请编辑Makefile顶部,然后运行make

使用root身份执行 make install 可以将编译的二进制文件和支持文件安装到 /usr/local (这不是使用QuickJS所必需的).

:可以参考QuickJS中文关于Windows下编译安装Linux下编译安装相关文档。

2.2 快速入门

qjs 是命令行解析器 (Read-Eval-Print Loop). 您可以将Javascript文件和/或表达式作为参数传递以执行它们:

./qjs examples/hello.js

qjsc 是命令行编译器:

./qjsc -o hello examples/hello.js
./hello

生成一个没有外部依赖的 hello 可执行文件。

qjsbnqjscbn 是具有数学扩展的相应解释器和编译器:

./qjsbn examples/pi.js 1000

显示PI的1000位数字

./qjsbnc -o pi examples/pi.js
./pi 1000

编译并执行PI程序。

2.3 命令行选项

2.3.1 qjs 解释器

用法: qjs [options] [files]

选项:

-h

--help

选项列表。

-e `EXPR`

--eval `EXPR`

执行EXPR.

-i

--interactive

转到交互模式 (在命令行上提供文件时,它不是默认模式).

-m

--module

加载为ES6模块(默认为.mjs文件扩展名)。

高级选项包括:

-d

--dump

转存内存使用情况统计信息。

-q

--quit

只是实例化解释器并退出。

2.3.2 qjsc 编译器

用法: qjsc [options] [files]

选项:

-c

仅输出C文件中的字节码,默认是输出可执行文件。

-e

main() C文件中的输出和字节码,默认是输出可执行文件。

-o output

设置输出文件名(默认= out.c或a.out)。

-N cname

设置生成数据的C名称。

-m

编译为Javascript模块(默认为.mjs扩展名)。

-M module_name[,cname]

添加外部C模块的初始化代码。查看c_module示例。

-x

字节交换输出(仅用于交叉编译)。

-flto

使用链接时间优化。编译速度较慢,但可执行文件更小更快。使用选项时会自动设置此选项-fno-x

-fno-[eval|string-normalize|regexp|json|proxy|map|typedarray|promise]

禁用所选语言功能以生成较小的可执行文件。

2.4 qjscalc 应用程序

qjscalc应用程序是qjsbn命令行解释器的超集,它实现了一个具有任意大整数和浮点数,分数,复数,多项式和矩阵的Javascript计算器。源代码在qjscalc.js中。http://numcalc.com上提供了更多文档和Web版本。

2.5 内置测试

运行make test以运行QuickJS存档中包含的一些内置测试。

2.6 Test262 (ECMAScript 测试套件))

QuickJS存档中包含test262运行程序。

作为参考,完整的test262测试在档案qjs-tests-yyyy-mm-dd.tar.xz中提供。您只需将其解压缩到QuickJS源代码目录中即可。

或者,test262测试可以安装:

git clone https://github.com/tc39/test262.git test262
cd test262
git checkout 94b1e80ab3440413df916cd56d29c5a2fa2ac451
patch -p1 < ../tests/test262.patch
cd ..

补丁添加了特定于实现的harness函数,并优化了低效的RegExp字符类和Unicode属性转义测试(测试本身不会被修改,只有慢速字符串初始化函数被优化)。

测试可以运行

make test2

有关更多信息,请运行./run-test262以查看test262 runner的选项。配置文件test262.conftest262bn.conf包含运行各种测试的选项。

3 技术规范

3.1 语言支持

3.1.1 ES2019支持

包含Annex B (遗留Web兼容)和Unicode相关功能的ES2019规范 2 已经基本支持。 目前尚未支持以下功能:

  • Realms (尽管C API支持不同的运行时和上下文)
  • Tail calls3

3.1.2 JSON

JSON解析器目前比规范支持范围更宽.

3.1.3 ECMA402

ECMA402 (国际化API)尚未支持.

3.1.4 扩展

  • 指令 "use strip" 不保留调试信息 (包括函数源代码) 以节省内存。 "use strict"指令可以应用全局脚本,或者特定函数。
  • 脚本开头第一行 #! 会被忽略。

3.1.5 数学扩展

数学扩展在qjsbn 版本中可用,并且完全向后兼容标准Javascript. 查看jsbignum.pdf获取更多信息。

  • BigInt (大整数) TC39已经支持。
  • BigFloat 支持: 基数2中任意大浮点数。
  • 运算符重载。
  • 指令"use bigint"启用bigint模式, BigInt默认情况下为整数。
  • 指令"use math"启用数学模式,其中整数上的除法和幂运算符产生分数。BigFloat默认情况下,浮点文字是默认值,整数是BigInt默认值。

3.2 模块

ES6模块完全支持。默认名称解析规则如下:

  • 模块名称带有前导...是相对于当前模块的路径。
  • 模块名称没有前导...是系统模块,例如stdos
  • 模块名称以.so结尾,是使用QuickJS C API的原生模块。

3.3 标准库

默认情况下,标准库包含在命令行解释器中。 它包含两个模块stdos以及一些全局对象.

3.3.1 全局对象

scriptArgs

提供命令行参数。第一个参数是脚本名称。

print(...args)

打印由空格和尾随换行符分隔的参数。

console.log(...args)

与print()相同。

3.3.2 std 模块

std模块为libc提供包装器stdlib.h和stdio.h和其他一些实用程序。

可用出口:

exit(n)

退出进程。

evalScript(str)

将字符串str以脚本方式运行(全局eval)。

loadScript(filename)

将文件filename以脚本方式运行(全局eval)。

Error(errno)

std.Error 构造函数。 错误实例包含字段errno(错误代码)和messagestd.Error.strerror(errno)的结果)。

构造函数包含以下字段:

EINVAL

EIO

EACCES

EEXIST

ENOSPC

ENOSYS

EBUSY

ENOENT

EPERM

EPIPE

常见错误的整数值 (可以定义附加错误代码)。

strerror(errno)

返回描述错误的字符串errno.

open(filename, flags)

打开一个文件(libc的包装器fopen())。在I/O错误时抛出 std.Error

tmpfile()

打开一个临时文件。在I/O错误时抛出std.Error

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()的常量

global

引用全局对象。

gc()

手动调用循环删除算法。循环移除算法在需要时自动启动,因此该功能在特定内存限制或测试时非常有用。

getenv(name)

返回环境变量的值 name ,或未定义时返回 undefined .

FILE 原型:

close()

关闭文件。

puts(str)

使用UTF-8编码输出字符串。

printf(fmt, ...args)

格式化printf,与libc printf格式相同。

flush()

刷新缓冲的文件。

seek(offset, whence)

寻找特定文件位置 (从哪里std.SEEK_*)。在I/O错误时抛出 std.Error

tell()

返回当前文件位置。

eof()

如果文件结束,则返回true。

fileno()

返回关联的OS句柄。

read(buffer, position, length)

从文件中以字节位置position,读取length字节到ArrayBufferbuffer(libc的包装器fread)。

write(buffer, position, length)

将ArrayBufferbuffer中以字节位置position开始的length字节写入文件 (libc的包装器fread).

getline()

返回文件中的下一行,假设为UTF-8编码,不包括尾随换行符。

getByte()

返回文件中的下一个字节。

putByte(c)

将一个字节写入文件。

3.3.3 os 模块

os 模块提供操作系统特定功能:

  • 底层文件访问
  • 信号
  • 计时器
  • 异步 I/O

如果是OK,OS函数通常返回0,或者OS返回特定的错误代码。

可用导出函数:

open(filename, flags, mode = 0o666)

打开一个文件。如果错误,返回句柄或<0。

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)

寻找文件。使用 std.SEEK_*whence.

read(fd, buffer, offset, length)

从文件句柄fd中以字节位置offset开始,读取length字节到ArrayBufferbuffer。返回读取的字节数,若出现错误则返回小于0的值。

write(fd, buffer, offset, length)

将ArrayBufferbuffer中以字节位置offset开始的length字节写入文件句柄fd。返回写入的字节数,若出现错误则返回小于0的值。

isatty(fd)

fd 是一个TTY (终端)句柄返回 true

ttyGetWinSize(fd)

返回TTY大小 [width, height] 或者如果不可用返回 null

ttySetRaw(fd)

在原始模式下设置TTY。

remove(filename)

删除文件。如果正常则返回0,如果错误则返回<0

rename(oldname, newname)

重命名文件。如果正常则返回0,如果错误则返回<0

setReadHandler(fd, func)

将读处理程序添加到文件句柄fdfd每次有数据待增加处理时调用func 。支持每个文件句柄的单个读处理程序。使用 func = null 来删除句柄。

setWriteHandler(fd, func)

将写处理程序添加到文件句柄fdfd每次有数据待写入处理时调用func . 支持每个文件句柄一个写处理程序。使用 `func = null来删除句柄。

signal(signal, func)

当信号 signal 发生时调用 func 。 每个信号编号只支持一个处理程序。使用 null 设定的默认处理或 undefined 忽略的信号。

SIGINT

SIGABRT

SIGFPE

SIGILL

SIGSEGV

SIGTERM

POSIX 信号编号。

setTimeout(func, delay)

delay 毫秒之后调用函数 func 。返回计时器的句柄。

clearTimer(handle)

取消计时器。

platform

返回表示该平台的字符串: "linux", "darwin", "win32" or "js".

3.4 QuickJS C API

C API的设计简单而有效。C API在quickjs.h标头中定义。

3.4.1 运行时和上下文

JSRuntime表示与对象堆对应的JavaScript运行时。可以同时存在多个运行时,但它们不能交换对象。在给定的运行时内,不支持多线程。

JSContext表示JavaScript上下文(或领域)。每个JSContext都有自己的全局对象和系统对象。在JSRuntime中可以有多个JSContext,并且它们可以共享对象,类似于同一源的框架在Web浏览器**享JavaScript对象。

3.4.2 JSValue

JSValue表示一个JavaScript值,可以是原始类型或对象。使用引用计数,因此重要的是明确复制(JS_DupValue(),增加引用计数)或释放(JS_FreeValue(),减少引用计数)JSValues。

3.4.3 C函数

使用JS_NewCFunction()可以创建C函数。JS_SetPropertyFunctionList()是一种简便的方法,可将函数、设置器和获取器属性轻松添加到给定对象中。

与其他嵌入式JavaScript引擎不同,QuickJS没有隐式堆栈,因此C函数将其参数作为普通的C参数传递。一般规则是,C函数将JSValue作为参数(因此它们不需要释放),并返回一个新分配的(活动的)JSValue

3.4.4 错误异常

异常:大多数C函数可以返回一个Javascript异常。必须通过C代码明确测试和处理它。特定的JSValue,即JS_EXCEPTION,表示发生了异常。实际的异常对象存储在JSContext中,可以使用JS_GetException()检索到。

3.4.5 Script代码执行

使用JS_Eval()来评估脚本或模块源代码。

如果脚本或模块已经使用qjsc编译成字节码,那么使用JS_EvalBinary()可以实现相同的结果。优点是不需要编译,因此速度更快、体积更小,因为如果不需要eval,编译器可以从可执行文件中删除。

注意:字节码格式与特定的QuickJS版本相关联。此外,在执行之前没有进行安全检查。因此,字节码不应从不受信任的来源加载。这就是为什么qjsc中没有将字节码输出到二进制文件的选项。

3.4.6 JS类

可以将C的不透明数据附加到JavaScript对象上。C不透明数据的类型由对象的类ID(JSClassID)确定。因此,第一步是注册一个新的类ID和JS类(JS_NewClassID()JS_NewClass())。然后,可以使用JS_NewObjectClass()创建该类的对象,并使用JS_GetOpaque()/JS_SetOpaque()获取或设置C的不透明指针。

在定义新的JS类时,可以声明一个析构函数,在对象销毁时调用该函数。可以提供一个gc_mark方法,以便循环移除算法可以找到被该对象引用的其他对象。还有其他方法可用于定义异类对象行为。

类ID在全局范围内分配(即适用于所有运行时)。JSClass在每个JSRuntime中分配。JS_SetClassProto()用于在给定JSContext中为给定类定义原型。JS_NewObjectClass()在创建的对象中设置此原型。

在js_libc.c中提供了示例。

3.4.7 C模块

支持动态或者静态链接的原生ES6模块。查看test_bjson和bjson.so示例。标准库js_libc.c也是原生模块很好的一个例子。

3.4.8 内存处理

使用 JS_SetMemoryLimit() 为给定的JSRuntime设置全局内存分配限制。

JS_NewRuntime2()可以提供自定义内存分配功能。

JS_SetMaxStackSize()可以使用设置最大系统堆栈大小

3.4.9 执行超时和中断

使用JS_SetInterruptHandler()来设置一个回调函数,当引擎执行代码时,它会定期调用该回调函数。该回调函数可以用于实现执行超时。

命令行解释器使用它来实现 Ctrl-C 处理程序。

4 内部实现

4.1 Bytecode

编译器直接生成字节码,没有中间表示(如解析树),因此非常快速。在生成的字节码上进行了多个优化步骤。

选择了基于堆栈的字节码,因为它简单且生成的代码紧凑。

对于每个函数,编译时计算最大堆栈大小,因此不需要运行时堆栈溢出测试。

为调试信息维护了一个单独的压缩行号表。

对闭包变量的访问进行了优化,并且几乎与局部变量一样快。

对严格模式下的直接eval进行了优化。

4.2 Executable generation

4.2.1 qjsc 编译器

qjsc编译器从Javascript文件生成C源代码。默认情况下,C源代码使用系统编译器(gccclang)进行编译。

生成的C源代码包含已编译函数或模块的字节码。如果需要完整的可执行文件,它还包含一个main()函数,其中包含必要的C代码来初始化Javascript引擎,并加载和执行已编译的函数和模块。

可以将Javascript代码与C模块混合使用。

为了生成更小的可执行文件,可以禁用特定的Javascript功能,特别是eval或正则表达式。代码删除依赖于系统编译器的链接时优化。

4.2.2 二进制 JSON

qjsc通过编译脚本或模块,然后将它们序列化为二进制格式来工作。该格式的一个子集(不包括函数或模块)可以用作二进制JSON。示例test_bjson.js展示了如何使用它。

警告:二进制JSON格式可能会在不经通知的情况下更改,因此不应将其用于存储持久数据。test_bjson.js示例仅用于测试二进制对象格式的函数。

4.3 运行时

4.3.1 Strings

字符串存储为8位或16位字符数组。因此,随机访问字符总是很快。

C API提供将Javascript字符串转换为C UTF-8编码字符串的函数。最常见情况是 Javascript字符串仅包含ASCII 字符串不涉及复制。

4.3.2 Objects

对象形状(对象原型、属性名称和标志)在对象之间共享,以节省内存。

优化了没有洞(除了数组末尾)的数组。

TypedArray访问已优化。

4.3.3 Atoms

对象属性名称和一些字符串被存储为原子(唯一字符串),以节省内存并允许快速比较。原子表示为32位整数。原子范围的一半保留给从 0 到 2^{31}-1 的立即整数字面值。

4.3.4 Numbers

数字可以表示为32位有符号整数或64位IEEE-754浮点数值。大多数操作都针对32位整数情况有快速路径。

4.3.5 垃圾回收

引用计数用于自动和准确地释放对象。当分配的内存变得过大时,会进行单独的循环移除操作。循环移除算法仅使用引用计数和对象内容,因此在C代码中不需要显式操作垃圾收集根。

4.3.6 JSValue

JSValue是一个Javascript值,可以是原始类型(例如Number、String等)或对象。在32位版本中,使用NaN装箱来存储64位浮点数。表示形式经过优化,可以高效地测试32位整数和引用计数值。

在64位代码中,JSValue的大小为128位,并且不使用NaN装箱。原因是在64位代码中,内存使用不那么关键。

在两种情况下(32位或64位),JSValue恰好适应两个CPU寄存器,因此可以通过C函数高效地返回。

4.3.7 函数调用

引擎已经过优化,因此函数调用很快。系统堆栈包含Javascript参数和局部变量。

4.4 RegExp

开发了一个特定的正则表达式引擎。它既小又高效,并支持所有ES2019功能,包括Unicode属性。作为Javascript编译器,它直接生成没有解析树的字节码。

使用显式堆栈的回溯使得系统堆栈上没有递归。简单的量化器经过专门优化,以避免递归。

来自具有空项的量化器的无限递归被避免。

完整的正则表达式文件库的权重约为15 KiB(x86代码),不包括Unicode库。

4.5 Unicode

开发了一个特定的Unicode库,因此不依赖于外部大型Unicode库,例如ICU。压缩所有Unicode表,同时保持合理的访问速度。

该库支持大小写转换,Unicode规范化,Unicode脚本查询,Unicode常规类别查询和所有Unicode二进制属性。

完整的Unicode库大约重量为45 KiB(x86代码)。

4.6 BigInt 和 BigFloat

BigInt 和 BigFloat 是用libbflibbf 库实现的4。 它大概有60 KiB (x86 代码) 并提供任意精度的IEEE 754 浮点运算和具有精确舍入的超越函数。

5 许可协议

QuickJS 在MIT协议下发布。

除非另有说明,否则QuickJS来源的版权归Fabrice Bellard和Charlie Gordon所有。


脚注

https://github.com/tc39/test262

https://tc39.github.io/ecma262/

我们认为目前的尾部调用规范过于复杂,并且实际利益有限。

https://bellard.org/libbf


6 相关项目

quickjs's People

Contributors

doodlewind avatar frostmiku avatar harlonwang avatar llgoer avatar luwuer avatar tqfx avatar undefined-moe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.