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)

Read length bytes from the file to the ArrayBuffer buffer at byte position position (libc的包装器fread)。

write(buffer, position, length)

Write length bytes to the file from the ArrayBuffer buffer at byte position position (wrapper to the 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)

Read length bytes from the file handle fd to the ArrayBuffer buffer at byte position offset. Return the number of read bytes or < 0 if error.

write(fd, buffer, offset, length)

Write length bytes to the file handle fd from the ArrayBuffer buffer at byte position offset. Return the number of written bytes or < 0 if error.

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 represents a Javascript runtime corresponding to an object heap. Several runtimes can exist at the same time but they cannot exchange objects. Inside a given runtime, no multi-threading is supported.

JSContext represents a Javascript context (or Realm). Each JSContext has its own global objects and system objects. There can be several JSContexts per JSRuntime and they can share objects, similary to frames of the same origin sharing Javascript objects in a web browser.

3.4.2 JSValue

JSValue represents a Javascript value which can be a primitive type or an object. Reference counting is used, so it is important to explicitely duplicate (JS_DupValue(), increment the reference count) or free (JS_FreeValue(), decrement the reference count) JSValues.

3.4.3 C函数

C functions can be created with JS_NewCFunction(). JS_SetPropertyFunctionList() is a shortcut to easily add functions, setters and getters properties to a given object.

Unlike other embedded Javascript engines, there is no implicit stack, so C functions get their parameters as normal C parameters. As a general rule, C functions take constant JSValues as parameters (so they don’t need to free them) and return a newly allocated (=live) JSValue.

3.4.4 错误异常

Exceptions: most C functions can return a Javascript exception. It must be explicitely tested and handled by the C code. The specific JSValue JS_EXCEPTION indicates that an exception occured. The actual exception object is stored in the JSContext and can be retrieved with JS_GetException().

3.4.5 Script代码执行

Use JS_Eval() to evaluate a script or module source.

If the script or module was compiled to bytecode with qjsc, JS_EvalBinary() achieves the same result. The advantage is that no compilation is needed so it is faster and smaller because the compiler can be removed from the executable if no eval is required.

Note: the bytecode format is linked to a given QuickJS version. Moreover, no security check is done before its execution. Hence the bytecode should not be loaded from untrusted sources. That’s why there is no option to output the bytecode to a binary file in qjsc.

3.4.6 JS类

C opaque data can be attached to a Javascript object. The type of the C opaque data is determined with the class ID (JSClassID) of the object. Hence the first step is to register a new class ID and JS class (JS_NewClassID(), JS_NewClass()). Then you can create objects of this class with JS_NewObjectClass() and get or set the C opaque point with JS_GetOpaque()/JS_SetOpaque().

When defining a new JS class, it is possible to declare a finalizer which is called when the object is destroyed. A gc_mark method can be provided so that the cycle removal algorithm can find the other objects referenced by this object. Other methods are available to define exotic object behaviors.

The Class ID are globally allocated (i.e. for all runtimes). The JSClass are allocated per JSRuntime. JS_SetClassProto() is used to define a prototype for a given class in a given JSContext. JS_NewObjectClass() sets this prototype in the created object.

Examples are available in 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 执行超时和中断

Use JS_SetInterruptHandler() to set a callback which is regularly called by the engine when it is executing code. This callback can be used to implement an execution timeout.

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