qjs解釋器qjsc編譯器qjscalc應用程序std模塊os模塊qjsc編譯器QuickJS是一個小型並且可嵌入的Javascript引擎,它支持ES2020規範,包括模塊,異步生成器和代理器。
它可選支持數學擴展,例如大整數(BigInt),大浮點數(BigFloat) 以及運算符重載。
官方站點:https://bellard.org/quickjs/
中文站點:https://github.com/quickjs-zh/
QuickJS QQ群1: 598609506 。
中文Wiki:https://github.com/quickjs-zh/QuickJS/wiki
點擊查看QuickJS基準測試具體內容
提供Makefile可以在Linux或者MacOS/X上編譯。通過使用MinGW工具在Linux主機上進行交叉編譯,可以獲得初步的Windows支持。
如果要選擇特定選項,請編輯Makefile頂部,然後運行make 。
使用root身份執行make install可以將編譯的二進製文件和支持文件安裝到/usr/local (這不是使用QuickJS所必需的).
注:可以參考QuickJS中文關於Windows下編譯安裝及Linux下編譯安裝相關文檔。
qjs是命令行解析器(Read-Eval-Print Loop). 您可以將Javascript文件和/或表達式作為參數傳遞以執行它們:
./qjs examples/hello.js
qjsc是命令行編譯器:
./qjsc -o hello examples/hello.js
./hello
生成一個沒有外部依賴的hello可執行文件。
qjsbn和qjscbn是具有數學擴展的相應解釋器和編譯器:
./qjsbn examples/pi.js 1000
顯示PI的1000位數字
./qjsbnc -o pi examples/pi.js
./pi 1000
編譯並執行PI程序。
qjs解釋器用法: qjs [options] [files]
選項:
-h
--help
選項列表。
-e `EXPR`
--eval `EXPR`
執行EXPR.
-i
--interactive
轉到交互模式(在命令行上提供文件時,它不是默認模式).
-m
--module
加載為ES6模塊(默認為.mjs文件擴展名)。
高級選項包括:
-d
--dump
轉存內存使用情況統計信息。
-q
--quit
只是實例化解釋器並退出。
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]
禁用所選語言功能以生成較小的可執行文件。
qjscalc應用程序該qjscalc應用程序是qjsbn命令行解釋器的超集,它實現了一個具有任意大整數和浮點數,分數,複數,多項式和矩陣的Javascript計算器。源代碼在qjscalc.js中。 http://numcalc.com上提供了更多文檔和Web版本。
運行make test以運行QuickJS存檔中包含的一些內置測試。
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.conf並test262bn.conf包含運行各種測試的選項。
包含Annex B (遺留Web兼容)和Unicode相關功能的ES2019規範2 已經基本支持。 目前尚未支持以下功能:
JSON解析器目前比規範支持範圍更寬.
ECMA402 (國際化API)尚未支持.
"use strip"不保留調試信息(包括函數源代碼) 以節省內存。 "use strict"指令可以應用全局腳本,或者特定函數。#!會被忽略。 數學擴展在qjsbn版本中可用,並且完全向後兼容標準Javascript. 查看jsbignum.pdf獲取更多信息。
BigInt (大整數) TC39已經支持。BigFloat支持: 基數2中任意大浮點數。"use bigint"啟用bigint模式, BigInt默認情況下為整數。"use math"啟用數學模式,其中整數上的除法和冪運算符產生分數。 BigFloat默認情況下,浮點文字是默認值,整數是BigInt默認值。ES6模塊完全支持。默認名稱解析規則如下:
.或..是相對於當前模塊的路徑。.或..是系統模塊,例如std或os 。.so結尾,是使用QuickJS C API的原生模塊。默認情況下,標準庫包含在命令行解釋器中。 它包含兩個模塊std和os以及一些全局對象.
scriptArgs
提供命令行參數。第一個參數是腳本名稱。
print(...args)
打印由空格和尾隨換行符分隔的參數。
console.log(...args)
與print()相同。
std模塊該std模塊為libc提供包裝器stdlib.h和stdio.h和其他一些實用程序。
可用出口:
exit(n)
退出進程。
evalScript(str)
將字符串str以腳本方式運行(全局eval)。
loadScript(filename)
將文件filename以腳本方式運行(全局eval)。
Error(errno)
std.Error構造函數。 錯誤實例包含字段errno (錯誤代碼)和message ( std.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字節到ArrayBuffer buffer (libc的包裝器fread )。
write(buffer, position, length)
將ArrayBuffer buffer中以字節位置position開始的length字節寫入文件(libc的包裝器fread ).
getline()
返回文件中的下一行,假設為UTF-8編碼,不包括尾隨換行符。
getByte()
返回文件中的下一個字節。
putByte(c)
將一個字節寫入文件。
os模塊os模塊提供操作系統特定功能:
如果是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字節到ArrayBuffer buffer 。返回讀取的字節數,若出現錯誤則返回小於0的值。
write(fd, buffer, offset, length)
將ArrayBuffer buffer中以字節位置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)
將讀處理程序添加到文件句柄fd 。 fd每次有數據待增加處理時調用func 。支持每個文件句柄的單個讀處理程序。使用func = null來刪除句柄。
setWriteHandler(fd, func)
將寫處理程序添加到文件句柄fd 。 fd每次有數據待寫入處理時調用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" .
C API的設計簡單而有效。 C API在quickjs.h標頭中定義。
JSRuntime表示與對象堆對應的JavaScript運行時。可以同時存在多個運行時,但它們不能交換對象。在給定的運行時內,不支持多線程。
JSContext表示JavaScript上下文(或領域)。每個JSContext都有自己的全局對象和系統對象。在JSRuntime中可以有多個JSContext,並且它們可以共享對象,類似於同一源的框架在Web瀏覽器中共享JavaScript對象。
JSValue表示一個JavaScript值,可以是原始類型或對象。使用引用計數,因此重要的是明確複製( JS_DupValue() ,增加引用計數)或釋放( JS_FreeValue() ,減少引用計數)JSValues。
使用JS_NewCFunction()可以創建C函數。 JS_SetPropertyFunctionList()是一種簡便的方法,可將函數、設置器和獲取器屬性輕鬆添加到給定對像中。
與其他嵌入式JavaScript引擎不同,QuickJS沒有隱式堆棧,因此C函數將其參數作為普通的C參數傳遞。一般規則是,C函數將JSValue作為參數(因此它們不需要釋放),並返回一個新分配的(活動的) JSValue 。
異常:大多數C函數可以返回一個Javascript異常。必須通過C代碼明確測試和處理它。特定的JSValue ,即JS_EXCEPTION ,表示發生了異常。實際的異常對象存儲在JSContext中,可以使用JS_GetException()檢索到。
使用JS_Eval()來評估腳本或模塊源代碼。
如果腳本或模塊已經使用qjsc編譯成字節碼,那麼使用JS_EvalBinary()可以實現相同的結果。優點是不需要編譯,因此速度更快、體積更小,因為如果不需要eval ,編譯器可以從可執行文件中刪除。
注意:字節碼格式與特定的QuickJS版本相關聯。此外,在執行之前沒有進行安全檢查。因此,字節碼不應從不受信任的來源加載。這就是為什麼qjsc中沒有將字節碼輸出到二進製文件的選項。
可以將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中提供了示例。
支持動態或者靜態鏈接的原生ES6模塊。查看test_bjson和bjson.so示例。標準庫js_libc.c也是原生模塊很好的一個例子。
使用JS_SetMemoryLimit()為給定的JSRuntime設置全局內存分配限制。
JS_NewRuntime2()可以提供自定義內存分配功能。
JS_SetMaxStackSize()可以使用設置最大系統堆棧大小
使用JS_SetInterruptHandler()來設置一個回調函數,當引擎執行代碼時,它會定期調用該回調函數。該回調函數可以用於實現執行超時。
命令行解釋器使用它來實現Ctrl-C處理程序。
編譯器直接生成字節碼,沒有中間表示(如解析樹),因此非常快速。在生成的字節碼上進行了多個優化步驟。
選擇了基於堆棧的字節碼,因為它簡單且生成的代碼緊湊。
對於每個函數,編譯時計算最大堆棧大小,因此不需要運行時堆棧溢出測試。
為調試信息維護了一個單獨的壓縮行號表。
對閉包變量的訪問進行了優化,並且幾乎與局部變量一樣快。
對嚴格模式下的直接eval進行了優化。
qjsc編譯器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 字符串不涉及復制。
對象形狀(對象原型、屬性名稱和標誌)在對象之間共享,以節省內存。
優化了沒有洞(除了數組末尾)的數組。
TypedArray訪問已優化。
對象屬性名稱和一些字符串被存儲為原子(唯一字符串),以節省內存並允許快速比較。原子表示為32位整數。原子範圍的一半保留給從0 到2^{31}-1 的立即整數字面值。
數字可以表示為32位有符號整數或64位IEEE-754浮點數值。大多數操作都針對32位整數情況有快速路徑。
引用計數用於自動和準確地釋放對象。當分配的內存變得過大時,會進行單獨的循環移除操作。循環移除算法僅使用引用計數和對象內容,因此在C代碼中不需要顯式操作垃圾收集根。
JSValue是一個Javascript值,可以是原始類型(例如Number、String等)或對象。在32位版本中,使用NaN裝箱來存儲64位浮點數。表示形式經過優化,可以高效地測試32位整數和引用計數值。
在64位代碼中,JSValue的大小為128位,並且不使用NaN裝箱。原因是在64位代碼中,內存使用不那麼關鍵。
在兩種情況下(32位或64位),JSValue恰好適應兩個CPU寄存器,因此可以通過C函數高效地返回。
引擎已經過優化,因此函數調用很快。系統堆棧包含Javascript參數和局部變量。
開發了一個特定的正則表達式引擎。它既小又高效,並支持所有ES2019功能,包括Unicode屬性。作為Javascript編譯器,它直接生成沒有解析樹的字節碼。
使用顯式堆棧的回溯使得系統堆棧上沒有遞歸。簡單的量化器經過專門優化,以避免遞歸。
來自具有空項的量化器的無限遞歸被避免。
完整的正則表達式文件庫的權重約為15 KiB(x86代碼),不包括Unicode庫。
開發了一個特定的Unicode庫,因此不依賴於外部大型Unicode庫,例如ICU。壓縮所有Unicode表,同時保持合理的訪問速度。
該庫支持大小寫轉換,Unicode規範化,Unicode腳本查詢,Unicode常規類別查詢和所有Unicode二進制屬性。
完整的Unicode庫大約重量為45 KiB(x86代碼)。
BigInt 和BigFloat 是用libbf庫libbf庫實現的4。 它大概有60 KiB (x86 代碼) 並提供任意精度的IEEE 754 浮點運算和具有精確舍入的超越函數。
QuickJS 在MIT協議下發布。
除非另有說明,否則QuickJS來源的版權歸Fabrice Bellard和Charlie Gordon所有。
https://github.com/tc39/test262
https://tc39.github.io/ecma262/
我們認為目前的尾部調用規範過於復雜,並且實際利益有限。
https://bellard.org/libbf
QuickJS-iOS iOS下的QuickJS庫
QuickJS-Android Android下的QuickJS庫
quickjs-rs Rust的QuickJS庫
quickjspp C++的QuickJS庫
go-quickjs Go的QuickJS庫
txiki.js The tiny JavaScript runtime built with QuickJS, libuv and ❤️
QuickJS-Pascal Quickjs FreePascal / Delphi Bindings