编译扩展
编译扩展是高级的 Scratch 扩展,直接与 Bilup 的 JavaScript 编译器集成以提供优化性能。与通过 VM 解释器运行的常规扩展不同,编译扩展将代码直接注入编译输出中,实现原始 JavaScript 性能。
什么是编译扩展?
编译扩展通过修补编译器的代码生成阶段来修改 Bilup 的内部编译过程。当项目使用编译扩展时,扩展的积木被转换为优化的 JavaScript 代码,以原始速度运行而不是通过 Scratch 的虚拟机。
与常规扩展的主要区别
常规扩展
- 通过 Scratch 的 VM 解释器运行
- 每个积木调用经过多个抽象层
- 由于运行时解释,执行速度较慢
- 开发和调试更容易
- 与所有 Scratch 环境兼容
编译扩展
- 在编译期间生成直接 JavaScript 代码
- 完全绕过 VM 解释
- 接近原始 JavaScript 性能
- 开发过程更复杂
- 仅在支持编译的环境中工作(如 Bilup)
编译工作原理
当 Bilup 编译项目时,它经历多个阶段:
- 脚本树生成:将积木结构转换为中间表示
- IR 生成:将树转换为中间表示(IR)
- JavaScript 生成:将 IR 转换为可执行的 JavaScript 代码
编译扩展使用修补系统在每个阶段注入自定义行为。
性能优势
编译扩展可以比常规扩展快得多。例如:
- 常规数学扩展:每个数学运算需要 VM 调用、类型检查和解释消耗
- 编译数学扩展:生成直接 JavaScript 如
Math.pow(a, b),消耗最小
这种差异在循环或性能关键代码中尤其明显,其中操作可能每帧被调用数千次。
开发要求
创建编译扩展需要:
- 深入的 JavaScript 知识:理解高级 JavaScript 概念、闭包和代码生成
- Bilup 内部知识:熟悉编译器架构和内部 API
- 非沙箱环境:扩展必须在非沙箱环境中运行才能访问编译器内部
- 测试基础设施:适当的测试设置,因为调试更复杂
何时使用编译扩展
考虑使用编译扩展时:
- 性能至关重要:数学计算、数据处理或实时操作
- 大量计算:在紧凑循环中频繁运行的操作
- 原始 JavaScript 功能:访问常规扩展 API 不可用的 JavaScript 功能
- 优化要求:当 VM 消耗显著影响用户体验时
避免使用编译扩展时:
- 简单功能,不需要优化
- 原型设计或实验性功能
- 与其他 Scratch 环境的兼容性很重要
- 开发时间和复杂性超过性能优势
架构概述
编译扩展通过修补三个主要编译器组件工作:
JSGenerator
处理最终的 JavaScript 代码生成阶段。扩展定义其积木应如何转换为 JavaScript 代码。
IRGenerator
管理中间表示阶段,其中积木逻辑在 JavaScript 生成之前被转换为结构化格式。
ScriptTreeGenerator
处理初始积木树结构,识别扩展积木并为编译做准备。
安全性和限制
编译扩展具有强大的功能,但也有局限性:
功能
- 直接 JavaScript 代码注入
- 访问浏览器 API 和功能
- 性能优化机会
- 自定义编译行为
限制
- 仅在编译环境中工作
- 调试过程更复杂
- 代码注入存在潜在安全隐患
- 兼容性仅限于特定的 Bilup 版本
入门指南
开始开发编译扩展,您应该:
- 首先了解常规 Scratch 扩展开发
- 学习 Bilup 的内部架构和 API
- 研究现有的编译扩展,如 Mist 的 Utils
- 设置适当的开发和测试环境
- 在尝试复杂功能之前,从简单积木开始
以下章节将指导您创建自己的编译扩展的技术细节,从基本设置到高级优化技术。