Aptos move-model Builder 模块解析:Legacy 模式与 Compiler 模式的双轨构建机制
2026/9/19 1:37:17 网站建设 项目流程

Aptos move-model Builder 模块解析:Legacy 模式与 Compiler 模式的双轨构建机制

【免费下载链接】aptos-coreAptos is a layer 1 blockchain built to support the widespread use of blockchain through better technology and user experience.项目地址: https://gitcode.com/GitHub_Trending/ap/aptos-core

导读

本文聚焦 Aptos 仓库中 Move 规范验证体系的核心前置模块——third_party/move/move-model/src/builder,它是将一组 Move 模块"构建(编译)"为全局环境(GlobalEnv)的入口。文章以 builder/README.md 定义的legacy 模式compiler 模式两条主线展开,结合模块源码剖析两种模式在"源码信息与 bytecode 信息如何组合"上的本质差异、三阶段翻译流水线、符号表结构与关键 API 调用链,帮助读者理解 Move Prover / 规范检查工具从 Move 代码到可分析模型的完整构建路径。

builder 模块的定位:为 Move 模块构建全局环境

在 Aptos 的 move-model 中,builder是一个负责"前端构建"的子模块,其职责在 README 开头一句话即已点明:

This module handles building (compiling) a global environment for a set of Move modules.

这里的"构建"并不是指生成链上部署的字节码,而是指:把一组 Move 模块(可能同时包含 Move 源码与已编译的 bytecode)翻译成规范验证系统可以查询、遍历、做类型检查的全局环境GlobalEnvGlobalEnv是 move-model 的核心数据结构(定义于 model.rs),承载模块、结构体、函数、规格(spec)、常量等全部模型信息,是 Move Prover 后续所有分析阶段的数据基础。

从目录结构看,builder由以下文件组成(见 builder 目录):

文件职责
mod.rs子模块声明与若干文本辅助函数(复数化等)
model_builder.rsModelBuilder主状态机:维护全部符号表,负责将各模块按无环依赖顺序登记进模型
module_builder.rsModuleBuilder:单个模块的翻译器,实现声明分析、定义分析、环境填充三阶段
exp_builder.rsExpTranslator:Move 表达式(含 spec 语言表达式)到模型 AST 的翻译与类型检查
builtins.rs注册内建函数与操作符(ModelBuilder::new时调用builtins::declare_builtins
binary_module_loader.rs二进制模块加载器:将CompiledModule+SourceMap载入环境
xir_loader.rsXIR 加载器(字节码中间表示相关)
macros.rs内建宏展开辅助

两种构建模式:README 定义的双轨机制

README 指出该模块可以以两种模式运行,这是全文的核心骨架:

  • legacy 模式(遗留模式):合并 bytecode 与 Move 源码中代表"表达式语言构造"的部分。由此得到的模型同时具备源码信息与 bytecode 信息
  • compiler 模式(编译器模式):完整分析 Move 源码。由此得到的模型默认不包含 bytecode 相关信息;但 bytecode 可以在后续阶段通过GlobalEnv::attach_compiled_module附加进来。

两种模式的差异本质,在于"模型的信息来源":legacy 模式以已编译的CompiledModule为主体、以源码补全表达式层信息;compiler 模式则以源码为主体、bytecode 是可选的后置附件。下文分别深入。

Legacy 模式:bytecode 为主、源码补充表达式信息

Legacy 模式的实现集中在 binary_module_loader.rs。其入口是GlobalEnv::load_compiled_module(binary_module_loader.rs):

pub fn load_compiled_module( &mut self, with_dep_closure: bool, module: CompiledModule, source_map: SourceMap, ) -> ModuleId { let mut loader = BinaryModuleLoader::new(self, with_dep_closure, &module, &source_map); loader.load(); let BinaryModuleLoader { module_id, .. } = loader; self.attach_compiled_module(module_id, module, source_map); self.update_loaded_modules(); module_id }

其注释清楚地说明了实现策略:

we leverage the already existing and battle-testedenv.attach_compiled_modulefunction... This function here basically simulates populating the initial env from bytecode instead of AST, and then calls the bytecode attach.

即:legacy 模式先"假装"从 AST 填充环境(实际上从 bytecode 反推),再调用字节码附加逻辑,从而让最终模型同时拥有两类信息。几个值得注意的实现细节:

  • 依赖处理with_dep_closurefalse时,模块的全部依赖必须已存在于环境中;为true时,会按使用表为缺失依赖创建 "stub"(占位)定义,形成"部分"模块,并可在后续load_compiled_module调用中被逐步精化(binary_module_loader.rs)。
  • 0x1::vector特殊处理:加载时若发现vector模块尚未加载,会调用add_well_known_vector_funs(binary_module_loader.rs)把emptylengthborrowborrow_mutpush_backpop_backdestroy_emptyswap等以字节码指令形式存在、没有 handle 的 vector 函数以 native public 函数的形式补进环境——这是"合并 bytecode 信息"的直接体现。
  • 结构体/函数签名一致性校验:若同一结构体或函数已被加载,load过程会做逻辑签名比对(type_params_logical_equalparams_logical_equal),不一致时报错并保证环境在无错前提下自洽。

Compiler 模式:完全分析源码,bytecode 后置附加

Compiler 模式的入口在 lib.rs 的run_model_builder_in_compiler_mode

Builds the Move model for the v2 compiler. This builds the model, compiling both code and specs from sources into typed-checked AST. No bytecode is attached to the model. This currently uses the v1 compiler as the parser (up to expansion AST), after that a new type checker.

其流程(见run_model_builder_with_options_and_compilation_flags,lib.rs)大致如下:

  1. 解析:用编译器(Compiler::from_package_paths)运行到PASS_PARSER,收集源码文件、注释与命名地址映射。
  2. 扩展:运行到PASS_EXPANSION,得到 expansion AST;期间把vectorcmpstringstring_utilssigner等隐式模块及其依赖闭包纳入编译集。
  3. 模型构建:调用run_move_checker(lib.rs),这是 compiler 模式的核心——不做 bytecode 附加("The expansion AST will be type checked. No bytecode is attached.")。

因此,compiler 模式产出的模型只含源码层信息(结构体、函数签名、spec 块、宏展开结果等),bytecode 相关的派生数据(如字节码指令序列)默认缺失,需要在后续阶段调用GlobalEnv::attach_compiled_module(model.rs)手动附加。该方法要求module_data[module_id]已通过GlobalEnv::add初始化:

/// Attaches a bytecode module to the module in the environment. This functions expects /// the `self.module_data[module_id]` to be already initialized using the `self.add` /// function. pub fn attach_compiled_module( &mut self, module_id: ModuleId, module: CompiledModule, source_map: SourceMap, )

这一设计的意义在于:compiler 模式可以在不依赖链上/本地字节码的情况下先行完成源码级检查(类型检查、spec 检查),需要字节码级分析(如字节码指令层面的验证)时再按需附加,实现了"源码检查"与"字节码分析"的解耦。

模块翻译的三阶段流水线

无论哪种模式,单个模块的翻译都由ModuleBuilder::translate驱动(module_builder.rs),分为三个阶段:

pub fn translate(&mut self, loc: Loc, module_def: EA::ModuleDefinition) { self.decl_ana(&module_def); // 阶段 1:声明分析 self.def_ana(&module_def); // 阶段 2:定义分析 self.synthesize_validity_slots(); self.collect_spec_block_infos(&module_def); let attrs = self.translate_attributes(&module_def.attributes); self.populate_and_finalize_env(loc, attrs); // 阶段 3:填充环境 }
  1. 声明分析(declaration analysis):收集模块内结构体、函数、spec 函数、spec 变量与 schema 的全部声明信息,但暂不分析函数体、条件与不变量。原因在于全局声明是"顺序无关"的,且可能存在循环引用——必须先让所有声明可见,才能正确解析相互引用。
  2. 定义分析(definition analysis):回头处理阶段 1 跳过的定义体,对表达式与 schema 包含(inclusion)做完整分析与类型检查。此阶段由ExpTranslator(exp_builder.rs)完成表达式到模型 AST 的翻译,并支持old表达式、ghost字段等规格语言特性。
  3. 填充阶段(population phase):将本模块的信息正式登记进GlobalEnv,并完成环境级后处理(如常量访问器注入、package 可见性的 friend 声明补全等)。

在 compiler 模式下,run_move_checker会先把模块按dependency_order排序,保证依赖先于使用者被翻译;同时通过pre_register_lemma_decls(lib.rs)预注册所有模块的 lemma 声明,使跨模块 lemma 引用不依赖模块处理顺序即可解析。

ModelBuilder:环境构建的状态核心

ModelBuilder(model_builder.rs)是构建过程的"总账本",以多张符号表维护增量状态,每翻译一个模块就扩展一次。其核心字段包括:

  • spec_fun_table:spec 函数符号表,因支持重载,一项可对应多个函数;
  • spec_var_table/spec_schema_table:spec 变量与 schema 符号表,后者附带unused_schema_set用于生成未使用 schema 的告警;
  • struct_table/reverse_struct_table:结构体符号表及其反向映射(供错误消息中类型可视化);
  • fun_table:函数符号表,并维护builtin_receiver_functionsreceiver_functions用于 receiver 风格方法调用(如v.length()分发到0x1::vector::length);
  • const_table:常量符号表,支持跨模块常量使用者追踪(sync_const_users);
  • intrinsics:内建声明列表,populate_env时统一注册进GlobalEnv
  • lemma_decl_table:全局 lemma 查询表,首次扫描时预填充,保证跨模块引用可解析。

ModelBuilder::new(model_builder.rs)在初始化时会调用builtins::declare_builtins,把语言内建的操作符与 spec 函数预先登记,这是表达式翻译的前提。

两种模式的选择与适用场景

  • 需要完整字节码信息的场景(legacy 模式):例如对已部署模块做验证、需要访问指令级信息的分析、或者依赖SourceMap做源码↔字节码映射的场景。此时通过load_compiled_module(with_dep_closure, module, source_map)一次性获得"源码 + bytecode"双全模型。
  • 纯源码分析场景(compiler 模式):例如在编译早期对包内源码做类型检查、spec 检查、宏展开验证。此时使用run_model_builder_in_compiler_mode(或带选项/编译标志的run_model_builder_with_options_and_compilation_flags),产出纯源码模型;若后续阶段需要字节码,再调用GlobalEnv::attach_compiled_module按需附加。

从代码结构可以推断,两者并非互斥的封闭体系,而是共享同一套GlobalEnv/ModelBuilder/ModuleBuilder基础设施:legacy 模式通过BinaryModuleLoader从 bytecode 反推环境初始状态,compiler 模式则从 expansion AST 正向翻译;最终都收敛到attach_compiled_module所定义的"源码信息 + 字节码信息"统一模型表示上。

源码地图与延伸阅读

  • 模式定义与模块定位:builder/README.md
  • 环境构建主状态机:model_builder.rs
  • 单模块三阶段翻译:module_builder.rs
  • 表达式与 spec 翻译器:exp_builder.rs
  • bytecode 加载(legacy 模式核心):binary_module_loader.rs
  • 内建函数注册:builtins.rs
  • Compiler 模式入口与run_move_checker:lib.rs
  • 全局环境与attach_compiled_module:model.rs

综上,builder模块以"legacy 合并 bytecode"与"compiler 纯源码分析"两种模式覆盖了从 Move 代码到可验证模型的全部构建路径,是理解 Aptos move-model 乃至 Move Prover 前端流水线的关键入口。

【免费下载链接】aptos-coreAptos is a layer 1 blockchain built to support the widespread use of blockchain through better technology and user experience.项目地址: https://gitcode.com/GitHub_Trending/ap/aptos-core

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询