Fabrice 又一新作 QuickJs

作者简介

Fabrice Bellard 是个神一般的程序员,这是他的个人主页 https://bellard.org/ ,里面的每一个作品都惊为天人而且对计算机科学领域具有深远影响,以下是他的生平:

  • 1972年生于法国格勒诺布尔(Grenoble)。在高中就读期间开发了著名的可执行压缩程序LZEXE,这是当年DOS上第一个广泛使用的文件压缩程序。大学就读于巴黎综合理工学院,后在巴黎高等电信学校攻读。
  • 1996年,他编写了一个简洁但是完整的C编译器和一个Java虚拟机Harissa。Fabrice Bellard发明的TinyCC是GNU/Linux环境下最小的ANSI C语言编译器,是目前号称编译速度最快的C编译器。
  • 1997年他提出了最快速的计算圆周率的算法,是贝利-波尔温-普劳夫公式的变体。在计算圆周率的过程中,Fabrice Bellard使用改良后的查德诺夫斯基方程算法来进行圆周率的计算,并使用贝利-波尔温-普劳夫公式来验证计算的结果。为了纪念他对圆周率算法所作出的杰出贡献,Fabrice Bellard所使用的改良型算法被命名为Fabrice Bellard算法,这种算法是目前所有圆周率算法中最快的一种,这个计算N位PI的公式比传统的BBQ算法要快47%。
  • 1998年编写了一个简洁的OpenGL实现TinyGL。
  • 2000年,他化名Gérard Lantau,创建了FFmpeg项目。FFmpeg单词中的FF指的是Fast Forward,FFmpeg这个2000年发起著名的开源多媒体播放器项目,是MPlayer的姊妹项目。这是一个如此重要的成就。这个多平台、多功能的多媒体编码解码器由Fabrice Bellard发起并管理,现在是由Michael Niedermayer在进行维护。
  • 2003年,开发了Emacs克隆QEmacs。
  • 2004年,他编写了一个只有138KB的启动加载程序TCCBOOT,可以在15秒内从源代码编译并启动Linux系统。
  • 2005年,用普通PC和VGA卡设计了一个数字电视系统。
  • 2009年12月31日,他声称打破了圆周率计算的世界纪录,算出小数点后2.7万亿位,仅用一台普通个人电脑。他使用的个人PC价格不到2000欧元,仅用了116天,就计算出了PI的小数点后第2.7万亿位,超过了由目前排名世界第47位的T2K Open超级计算机于2009年8月17日创造的世界纪录。新纪录比原纪录多出1200亿位,然而,他使用的这台桌面电脑的配置仅为:2.93GHz Core i7 CPU,6GB内存,7.5TB硬盘。
  • 2011年,他使用JavaScript写了一个PC虚拟机Jslinux。这个虚拟机仿真了一个32位的x86兼容处理器,一个8259可编程中断控制器,一个8254可编程中断计时器,和一个16450 UART。
  • 2012年,在PC上用软件实现4G LTE基站。

QuickJs

最近,这位大佬的新作品 QuickJs 又火了,这个用 C89 写的只有几万行代码的 JS 引擎,居然通过了 Test262 96% 的测试,仅次于 V8 的 97%,ChakraCore (Edge) 只有 65%。新特性的通过率高达 89%, 仅次于 V8 的 90%,ChakraCore (又躺枪) 只有 16%。

QuickJs Runtime 使用的是 栈虚拟机,内部采用引用计数的方式实现 gc。

Quick Start

  1. 去大佬的主页下载压缩包(大佬的页面就是这么朴实无华且枯燥)
    https://bellard.org/quickjs/

  2. 根目录下有 Makefile,直接 make 编译即可

  3. 编译完生成了一个 qjs,一个 qjsc,中文说明可以参考这篇文档
    https://github.com/quickjs-zh/QuickJS/blob/master/README.md

qjs

qjs 本身可以直接执行 Javscript 代码:

$ ./qjs test.js

也可以像 d8、node 那样运行 REPL:

$ ./qjs
QuickJS - Type "\h" for help
qjs > const a = 5;
undefined
qjs > a++;
TypeError: 'a' is read-only
    at <eval> (<evalScript>)

qjsc

qjsc 可以把 JavaScript 编译成字节码,然后输出成 C 文件,像这样:

./qjsc -e -o hello.cc examples/hello.js
/* File generated automatically by the QuickJS compiler. */

#include "quickjs-libc.h"

const uint32_t qjsc_hello_size = 87;

const uint8_t qjsc_hello[87] = {
 0x02, 0x04, 0x0e, 0x63, 0x6f, 0x6e, 0x73, 0x6f,
 0x6c, 0x65, 0x06, 0x6c, 0x6f, 0x67, 0x16, 0x48,
 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72,
 0x6c, 0x64, 0x22, 0x65, 0x78, 0x61, 0x6d, 0x70,
 0x6c, 0x65, 0x73, 0x2f, 0x68, 0x65, 0x6c, 0x6c,
 0x6f, 0x2e, 0x6a, 0x73, 0x0e, 0x00, 0x06, 0x00,
 0xa0, 0x01, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00,
 0x14, 0x01, 0xa2, 0x01, 0x00, 0x00, 0x00, 0x38,
 0xe0, 0x00, 0x00, 0x00, 0x42, 0xe1, 0x00, 0x00,
 0x00, 0x04, 0xe2, 0x00, 0x00, 0x00, 0x24, 0x01,
 0x00, 0xce, 0x28, 0xc6, 0x03, 0x01, 0x00,
};

int main(int argc, char **argv)
{
  JSRuntime *rt;
  JSContext *ctx;
  rt = JS_NewRuntime();
  js_std_init_handlers(rt);
  ctx = JS_NewContextRaw(rt);
  JS_AddIntrinsicBaseObjects(ctx);
  js_std_add_helpers(ctx, argc, argv);
  js_std_eval_binary(ctx, qjsc_hello, qjsc_hello_size, 0);
  js_std_loop(ctx);
  JS_FreeContext(ctx);
  JS_FreeRuntime(rt);
  return 0;
}

或者直接编译成一个可执行文件:

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

详细参数参考这篇 中文文档