如何解决webassembly 运行时是否应该创建指令的 AST?
我正在用 Java 构建一个 Web 程序集解释器,现在正在考虑如何最好地对指令建模。这是我第一次尝试构建编译器/运行时。
目前,我将 function 建模为一个类,其中包含从二进制存储格式中检索到的类型、局部变量列表和作为指令 byte[]
的代码。
在执行过程中,我只是遍历指令的每个字节(我是从 ByteBuffer 获取的,它包装了 byte[]
)并根据值进行切换,如下所示:
public void execute() {
int opCode = code.get();
// 0x0b is the end of the current block
while (opCode != 0x0b) {
if (opCode == 0x20) {
localGet();
} else if (opCode == 0x7c) {
i64add();
} else if (opCode == 0x6a) {
i32add();
} else {
throw new UnsupportedOperationException(String.format("Opcode %d not yet supported",opCode));
}
opCode = code.get();
}
}
这是一个“好”的方法吗?我还可以想象在模块加载期间解析每条指令并为每条指令创建单独的类(例如 LocalGet
),然后创建一个 AST。但我想知道这是否真的有必要在这里,因为指令似乎只是一个线性列表(除了块,我认为我必须创建一个辅助堆栈来跟踪嵌套级别)。
此外,webassembly 参考根据主堆栈列出了每条指令的确切步骤,所以我不确定 AST 会带来什么(因为我不是从终端节点开始解析,而是线性工作通过说明)。
解决方法
经过反复试验,我的方法最终是这样的:
-
创建
Instruction
接口public interface Instruction { void execute(Store store,WasmStack stack); }
-
为每条指令创建类,将立即值保存为一个字段(例如
LocalGet
指令)并包含来自 the webassembly documentation 的实际逻辑。 -
在实例化之前解析模块的字节码并将每个解析的指令附加到
List<Instruction>
。 -
在运行时,只需通过从列表中检索当前指令然后执行方法来顺序执行每条指令(如函数指针)。
最难的问题是分支结构,特别是block
指令,需要知道块尾的位置。因此,在解析过程中,我创建了一个简单的堆栈 Deque<Instruction>
,我将在其中推送每个控制指令(即每个以 end
终止的指令)以及当遇到 end
时,我修改了原来的控制指令来设置end
的位置(因为它在栈顶但也已经添加到指令列表中了)。
在运行时,end
指令然后更改函数指针(这只是一个简单的 for (int i = 0; i < instructions.size(); i++)
,通过从 i
方法内部更改 execute
。这实际上是一种实现控制流的简单方法,因为可以跳过指令或返回。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。