porffor

porffor

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

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

PorfforJavaScript编译器WebAssemblyAOT编译性能优化Github开源项目

Porffor  <sup><sub>/ˈpɔrfɔr/  (poor-for)</sup></sub>

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

<img src="https://github.com/CanadaHonk/porffor/assets/19228318/de8ad753-8ce3-4dcd-838e-f4d49452f8f8" alt="终端截图显示 Porffor 运行和编译 hello world" width="60%">

设计

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等工具
  • (而且说实话,我开始做这个项目时都不知道它的存在,哈哈)

编辑推荐精选

Keevx

Keevx

AI数字人视频创作平台

Keevx 一款开箱即用的AI数字人视频创作平台,广泛适用于电商广告、企业培训与社媒宣传,让全球企业与个人创作者无需拍摄剪辑,就能快速生成多语言、高质量的专业视频。

即梦AI

即梦AI

一站式AI创作平台

提供 AI 驱动的图片、视频生成及数字人等功能,助力创意创作

扣子-AI办公

扣子-AI办公

AI办公助手,复杂任务高效处理

AI办公助手,复杂任务高效处理。办公效率低?扣子空间AI助手支持播客生成、PPT制作、网页开发及报告写作,覆盖科研、商业、舆情等领域的专家Agent 7x24小时响应,生活工作无缝切换,提升50%效率!

TRAE编程

TRAE编程

AI辅助编程,代码自动修复

Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。

AI工具TraeAI IDE协作生产力转型热门
蛙蛙写作

蛙蛙写作

AI小说写作助手,一站式润色、改写、扩写

蛙蛙写作—国内先进的AI写作平台,涵盖小说、学术、社交媒体等多场景。提供续写、改写、润色等功能,助力创作者高效优化写作流程。界面简洁,功能全面,适合各类写作者提升内容品质和工作效率。

AI辅助写作AI工具蛙蛙写作AI写作工具学术助手办公助手营销助手AI助手
问小白

问小白

全能AI智能助手,随时解答生活与工作的多样问题

问小白,由元石科技研发的AI智能助手,快速准确地解答各种生活和工作问题,包括但不限于搜索、规划和社交互动,帮助用户在日常生活中提高效率,轻松管理个人事务。

热门AI助手AI对话AI工具聊天机器人
Transly

Transly

实时语音翻译/同声传译工具

Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等等,都可以满足你所有需要同传的场景需求,线上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。

讯飞智文

讯飞智文

一键生成PPT和Word,让学习生活更轻松

讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。

AI办公办公工具AI工具讯飞智文AI在线生成PPTAI撰写助手多语种文档生成AI自动配图热门
讯飞星火

讯飞星火

深度推理能力全新升级,全面对标OpenAI o1

科大讯飞的星火大模型,支持语言理解、知识问答和文本创作等多功能,适用于多种文件和业务场景,提升办公和日常生活的效率。讯飞星火是一个提供丰富智能服务的平台,涵盖科技资讯、图像创作、写作辅助、编程解答、科研文献解读等功能,能为不同需求的用户提供便捷高效的帮助,助力用户轻松获取信息、解决问题,满足多样化使用场景。

热门AI开发模型训练AI工具讯飞星火大模型智能问答内容创作多语种支持智慧生活
Spark-TTS

Spark-TTS

一种基于大语言模型的高效单流解耦语音令牌文本到语音合成模型

Spark-TTS 是一个基于 PyTorch 的开源文本到语音合成项目,由多个知名机构联合参与。该项目提供了高效的 LLM(大语言模型)驱动的语音合成方案,支持语音克隆和语音创建功能,可通过命令行界面(CLI)和 Web UI 两种方式使用。用户可以根据需求调整语音的性别、音高、速度等参数,生成高质量的语音。该项目适用于多种场景,如有声读物制作、智能语音助手开发等。

下拉加载更多