Project Icon

porffor

从零构建的JavaScript到WebAssembly编译器和运行时

Porffor是一个从零开始构建的JavaScript到WebAssembly/C编译器和运行时。它采用全AOT编译,无常量运行时代码,最少化Wasm导入。Porffor支持部分JavaScript功能,包括基本语言特性、内置函数和一些提案。项目还包含Wasm引擎Asur、正则表达式引擎Rhemyn和Wasm到C编译器2c。Porffor在支持的功能范围内性能表现优异,尤其是编译为原生二进制文件时。

Porffor  /ˈpɔrfɔr/  (poor-for)

一个从零开始的实验性 AOT 优化 JS/TS -> Wasm/C 引擎/编译器/运行时,用 JS 编写。研究项目,目前尚不适合严肃使用。

终端截图显示 Porffor 运行和编译 hello world

设计

Porffor 是一个非常独特的 JS 引擎,采用了许多完全不同的方法。它有严重的限制,但它能做的事情,做得相当不错。主要区别:

  • 100% AOT 编译(无 JIT)
  • 无常量运行时/前置代码
  • 尽可能少的 Wasm 导入(仅 I/O)

Porffor 主要是从零开始构建的,唯一不是的部分是解析器(使用 Acorn)。不使用 Binaryen 等,我们自己制作最终的 wasm 二进制文件。你可以将其想象成编译一种是 JavaScript 的子集(某些功能不支持)和超集(新的/自定义 API)的语言。不基于任何特定的规范版本。

使用

不要期望一切都能正常工作!目前仅支持非常有限的 JS。请参见 bench 文件夹中的示例。

安装

npm install -g porffor@latest。就是这么简单(希望如此):)

尝试 REPL

porf。直接运行,不带脚本文件参数。

运行 JS 文件

porf path/to/script.js

编译为 Wasm

porf wasm path/to/script.js out.wasm。目前它不使用像 WASI 这样的导入标准,所以单独使用基本上是不可用的。

编译为原生二进制文件

[!警告] 编译为原生二进制文件使用 2c,Porffor 自己的 Wasm -> C 编译器,这是实验性的。

porf native path/to/script.js out(.exe)。你可以用 --compiler=clang|gcc|zig 指定编译器(默认为 clang),用 --cO=Ofast|O3|O2|O1|O0 指定使用的优化级别(默认为 Ofast)。输出的二进制文件默认也会被剥离。

编译为 C

[!警告] 编译为 C 使用 2c,Porffor 自己的 Wasm -> C 编译器,这是实验性的。

porf c path/to/script.js (out.c)。当不包含输出文件时,将会打印到标准输出。

分析 JS 文件

[!警告] 非常实验性的进行中功能!

porf profile path/to/script.js

调试 JS 文件

[!警告] 非常实验性的进行中功能!

porf debug path/to/script.js

调试 JS 文件编译后的 Wasm

[!警告] 非常实验性的进行中功能!

porf debug-wasm path/to/script.js

选项

  • --parser=acorn|@babel/parser|meriyah|hermes-parser(默认:acorn)设置使用哪个解析器
  • --parse-types 启用解析类型注解/typescript。如果未设置 -parser,默认更改为 @babel/parser。不进行类型检查
  • --opt-types 使用类型注解作为编译器提示进行优化。不进行类型检查
  • --valtype=i32|i64|f64(默认:f64)设置值类型
  • -O0 禁用优化
  • -O1(默认)启用基本优化(简化指令,树摇 wasm 导入)
  • -O2 启用高级优化(部分求值)。不稳定!

当前限制

  • 有限的异步支持
  • 作用域之间没有变量(除了参数和全局变量)
  • 不支持 eval()/Function() 等(因为是 AOT)

子引擎

Asur

Asur 是 Porffor 自己的 Wasm 引擎;它是一个用 JS 编写的有意简化的解释器。它还在开发中。更多详情请参见其自述文件

Rhemyn

Rhemyn 是 Porffor 自己的正则表达式引擎;它将字面正则表达式 AOT 编译为 Wasm 字节码(想起什么了吗?)。它相当基础且还在开发中。更多详情请参见其自述文件

2c

2c 是 Porffor 自己的 Wasm -> C 编译器,使用生成的 Wasm 字节码和内部信息来生成特定且高效/快速的 C 代码。很少的样板/前置代码或所需的外部文件,仅用于 CLI 二进制文件(与 wasm2c 非常不同)。

支持的功能

有关已实现/支持的优化,请参见优化部分。

提案

这些包括一些早期(阶段 1/0)和/或已停止(最后提交已多年)的提案,但认为它们相当不错,所以。

Math 提案(阶段 1/0)

  • Math.clamp 提案Math.clamp(阶段 0 - 最后提交于 2023 年 4 月)
  • Math 扩展提案Math.scaleMath.radiansMath.degreesMath.RAD_PER_DEGMath.DEG_PER_RAD(阶段 1 - 最后提交于 2020 年 9 月)
  • Math.signbit 提案Math.signbit(阶段 1 - 最后提交于 2020 年 2 月)

语言

  • 数字字面量
  • 声明函数
  • 调用函数
  • return
  • 基本声明(let/const/var
  • 一些基本的整数运算符(+-/*%
  • 一些基本的整数位运算符(&|
  • 相等运算符(==!=等)
  • 大于/小于运算符(><>=等)
  • 一些一元运算符(!+-
  • 逻辑运算符(&&||
  • 一次声明多个变量(let a, b = 0
  • 数组解构(let [a, ...b] = foo
  • 全局变量(顶层作用域中的var/无修饰符)
  • 布尔值
  • ifif ... else
  • 匿名函数
  • 使用变量设置函数(const foo = function() { ... }
  • 箭头函数
  • undefined/null
  • 更新表达式(a++++bc--等)
  • for循环(for (let i = 0; i < N; i++)等)
  • 基本对象(无原型)
  • console.log
  • while循环
  • breakcontinue
  • 命名导出函数
  • IIFE支持
  • 赋值运算符(+=-=>>=&&=等)
  • 条件/三元运算符(cond ? a : b
  • 递归函数
  • 裸返回(return
  • throw(仅支持字面量,用于new Error的hack)
  • 基本的try { ... } catch { ... }(不提供错误信息)
  • 调用参数不匹配的函数(如f(a, b); f(0); f(1, 2, 3);
  • typeof
  • 未声明变量的运行时错误(ReferenceError),非函数的运行时错误(TypeError
  • 通过[]创建数组(如let arr = [ 1, 2, 3 ]
  • 通过arr[ind]访问数组成员(如arr[0]
  • 字符串字面量('hello world'
  • 通过str[ind]访问字符串成员(字符)(如str[0]
  • 字符串拼接(+)(如'a' + 'b'
  • 真值/假值(如!'' == true
  • 字符串比较(如'a' == 'a''a' != 'b'
  • 空值合并运算符(??
  • for...of(数组和字符串)
  • for...in
  • 数组成员赋值(arr[0] = 2arr[0] += 2等)
  • 数组构造函数(Array(5)new Array(1, 2, 3)
  • 标签语句(foo: while (...)
  • do...while循环
  • 可选参数((foo = 'bar') => { ... }
  • 剩余参数((...foo) => { ... }
  • this
  • 构造函数(new Foo
  • 类(class A {}
  • Await(await promise

内置功能

  • NaNInfinity
  • isNaN()isFinite()
  • Number的大部分功能(MAX_VALUEMIN_VALUEMAX_SAFE_INTEGERMIN_SAFE_INTEGERPOSITIVE_INFINITYNEGATIVE_INFINITYEPSILONNaNisNaNisFiniteisIntegerisSafeInteger
  • 大多数Math函数(sqrtabsfloorsignroundtruncclz32froundrandomexploglog2log10powexpm1log1psqrtcbrthypotsincostansinhcoshtanhasinhacoshatanhasinacosatanatan2
  • 基本的globalThis支持
  • 基本的BooleanNumber
  • 基本的eval用于字面量
  • 使用自制的xorshift128+ PRNG实现Math.random()
  • performance的部分功能(now()timeOrigin
  • Array.prototype的大部分方法(atpushpopshiftfillsliceindexOflastIndexOfincludeswithreversetoReversedforEachfiltermapfindfindLastfindIndexfindLastIndexeverysomereducereduceRightjointoString
  • Array的大部分方法(ofisArray
  • String.prototype的大部分方法(atcharAtcharCodeAttoUpperCasetoLowerCasestartsWithendsWithindexOflastIndexOfincludespadStartpadEndsubstringsubstrslicetrimStarttrimEndtrimtoStringbigblinkboldfixeditalicssmallstrikesubsuptrimLefttrimRighttrim
  • crypto的部分功能(randomUUID
  • escape
  • btoa
  • Number.prototype的大部分方法(toStringtoFixedtoExponential
  • parseInt
  • 符合规范的Date
  • 正在开发的类型化数组(Uint8ArrayInt32Array等)
  • 同步Promise

自定义功能

  • 支持i32、i64和f64作为值类型
  • 内部函数(见下文)
  • 通过``asm`...```"宏"内联wasm

版本控制

Porffor使用独特的版本控制系统,示例如下:0.18.2+2aa3f0589。让我们来分解一下:

  1. 0 - 主版本号,始终为0,因为Porffor还未准备就绪
  2. 18 - 次版本号,总Test262通过百分比(向下取整到最接近的整数)
  3. 2 - 微版本号,该次版本的构建编号(每次发布/git推送时递增)
  4. 2aa3f0589 - 提交哈希值

性能

对于大多数支持的功能,Porffor与大多数解释器和常见引擎(在不使用JIT的情况下)相比速度极快。对于那些使用JIT的引擎,默认情况下通常较慢,但可以通过编译器参数和类型化输入赶上,在编译为本机二进制文件时更是如此。

优化

主要用于减小体积。我不太关心编译器性能/时间,只要合理即可。我们不使用/依赖外部优化工具(如wasm-opt等),而是在编译器内部进行优化,创建比wasm-opt本身产生的代码体积更小的代码,因为我们拥有更多内部信息。

传统优化

  • 内联函数(正在开发中,有限支持)
  • 内联常量数学运算
  • 尾调用(在--tail-call标志后面)

Wasm 转换

  • local.setlocal.get -> local.tee
  • i32.const 0i32.eq -> i32.eqz
  • i64.extend_i32_si32.wrap_i64 -> ``
  • f64.convert_i32_ui32.trunc_sat_f64_s -> ``
  • returnend -> end
  • 更改常量,转换为转换后的值类型的常量(例如 f64.consti32.trunc_sat_f64_s -> i32.const
  • 移除一些冗余的设置/获取操作
  • 移除不需要的仅使用一次的变量
  • 移除不需要的块(内部没有 br 指令)
  • 移除未使用的导入
  • 使用数据段初始化数组/字符串
  • (可能还有更多未记录的转换,待完成)

Wasm 模块

  • 类型缓存/索引(无重复类型)
  • 如果主函数为空则不包含(以及其他导出)
  • 如果未使用或已优化掉,则不包含标签

Test262

Porffor 可以通过一些技巧/转换来运行 Test262,这些技巧/转换移除了不支持的特性,同时仍然执行相同的断言(例如使用仅包含字面量的简化错误消息)。目前通过率超过 14%(具体数据和详情请查看最新提交描述)。使用 node test262 进行测试,它还会显示上次提交和当前结果之间的整体差异。

图片

代码库

  • compiler:包含编译器本身

    • 2c.js:porffor 的自定义 wasm-to-c 引擎
    • allocators.js:支持各种语言特性的静态和动态分配器
    • assemble.js:将 wasm 操作和元数据组装成 wasm 模块/文件
    • builtins.js:引擎的所有手动编写的内置功能(规范、自定义、变量、函数)
    • builtins_object.js:所有各种内置对象(如 StringglobalThis 等)
    • builtins_precompiled.js:从 builtins/ 文件夹动态生成的内置功能
    • codegen.js:代码(wasm)生成,ast -> wasm。主要工作量在此
    • cyclone.js:wasm 部分常量求值器(快速且危险,因此称为"cyclone")
    • decompile.js:用于调试信息的基本 wasm 反编译器
    • diagram.js:生成 Mermaid 图表
    • embedding.js:嵌入常量的工具
    • encoding.js:将内容编码为 wasm 所需字节的工具
    • expression.js:将大多数运算符映射到操作码(高级运算符作为内置功能,如 f64_%
    • havoc.js:wasm 重写库(它对 wasm 字节码造成破坏,因此称为"havoc")
    • index.js:执行所有编译器步骤,输入代码,输出 wasm
    • opt.js:自制的 wasm 字节码优化器
    • parse.js:简单封装 acorn 的解析器
    • pgo.js:基于概要引导的优化器
    • precompile.js:生成 builtins_precompied.js 的工具
    • prefs.js:读取命令行参数的工具
    • prototype.js:一些内置原型函数
    • types.js:每个内置类型的定义
    • wasmSpec.js:来自 wasm 规范的"枚举"/信息
    • wrap.js:编译器的包装器,实例化并生成友好的导出
  • runner:包含使用编译器运行 JS 的工具

    • index.js:主文件,您可能想使用这个
    • info.js:运行时打印额外信息
    • repl.js:基本的 repl(使用 node:repl
  • rhemyn:包含 Rhemyn - 我们的正则表达式引擎(由 Porffor 使用)

    • compile.js:将正则表达式 ast 编译为 wasm 字节码
    • parse.js:自己的正则表达式解析器
  • test:包含许多测试文件,用于测试大多数支持的功能

  • test262:test262 运行器和工具

用途

目前,Porffor 在功能和特性上有严重限制,但它有一些关键优势:

  • 安全性。由于 Porffor 是用 JS 编写的,JS 是一种内存安全的语言*,并将 JS 编译为 Wasm,Wasm 是一个完全沙箱化的环境*,因此它相当安全。(* 这些依赖于底层实现的安全性。您也可以使用解释器而不是 JIT 来运行 Wasm,甚至是 Porffor 本身,以获得额外的安全性。)
  • 将 JS 编译为本机二进制文件。这仍处于非常早期的阶段!
  • 内联 Wasm,用于当您想在性能上超越编译器,或只是想要精细的功能时。
  • SIMD 操作和其他低级概念的潜力。
  • 未来可能会有更多用途?

待办事项

没有特定顺序,也不做保证,只是可能很快发生的事情™

VSCode 扩展

vscode-ext 中有一个 vscode 扩展,它调整了 JS 语法高亮,使其更适合 porffor 特性(例如,在内联汇编中高亮 wasm)。

使用的 Wasm 提案

Porffor 有意不使用尚未广泛实现的 Wasm 提案(例如 GC),以便它可以在尽可能多的地方使用。

  • 多值返回 (必需)
  • 非陷阱浮点数到整数的转换 (必需)
  • 批量内存操作(可选,有时可以不使用)
  • 异常处理(可选,仅用于错误)
  • 尾调用(可选,默认关闭)

常见问题

1. 为什么叫这个名字?

威尔士语中的"紫色"是 porffor。为什么选择紫色?

  • 没有其他 JS 引擎使用紫色
  • 紫色很酷
  • 据说紫色代表"雄心",这是描述这个项目的一个词

2. 为什么要做这个项目?

是的!

3. 这与AssemblyScript或其他Wasm语言不是一样的吗?

不,它们在内部完全不同,并且有着非常不同的目标和理念:

  • Porffor是作为一个通用的JS引擎开发的,并不专门针对Wasm
  • Porffor主要处理JS
  • Porffor是用纯JS编写的,并且能自我编译,不使用Binaryen等工具
  • (而且说实话,我开始做这个项目时都不知道它的存在,哈哈)
项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号