Wing 语言双重引用测试快照解析:从 lift 分析到 tf-aws 编译产物 编程语言编译器云原生语言运行时【免费下载链接】wingA programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡项目地址https://gitcode.com/gh_mirrors/wi/wing点击查看免费下载导读本文以 Wing 开源仓库中double_reference.test.w的tf-aws 编译快照double_reference.test.w_compile_tf-aws.md为解剖样本完整展示一条 Wing 测试用例从源码出发经过编译器 lift提升分析后产出的 inflight 闭包、Terraform 配置与 preflight 运行时代码。读者通过本文可以掌握Wing 如何为“同一对象被直接引用与间接引用”的双重引用场景生成 lift 依赖、如何将cloud.Counter映射为 AWS DynamoDB 表以及 hangar 快照测试工具链如何自动化验证编译结果。一、测试用例背景什么是 double_reference快照对应的 Wing 源码位于 tests/valid/double_reference.test.w该相对链接源自快照文档首行[double_reference.test.w](https://link.gitcode.com/i/638fa93017dad06a0fb3f76bf2d5b0f4)这里已按仓库根路径重写bring cloud; let initCount new cloud.Counter(); class Foo { inflight new() { initCount.inc(); } pub inflight method() { } } class Bar { pub foo: Foo; new() { this.foo new Foo(); } pub inflight callFoo() { this.foo.method(); } } let bar new Bar(); test hello { bar.callFoo(); bar.foo.method(); // TODO: https://github.com/winglang/wing/issues/3244 assert(initCount.peek() /*1*/ 2); }这段用例刻意构造了一个容易引发引用分析歧义的场景间接引用测试体内调用bar.callFoo()而callFoo()内部又调用this.foo.method()直接引用紧接着测试体又直接访问bar.foo.method()共享对象Bar的构造器在 preflight 阶段创建了Foo实例并存入字段this.foo因此bar与bar.foo两个 preflight 对象之间存在“拥有者—成员”关系计数校验Foo的inflight new()每次实例化都会initCount.inc()测试最终断言initCount.peek() 2。源码中的/*1*/ 2与 TODO 注释对应 winglang/wing#3244 这一历史问题早期实现中bar.callFoo()与bar.foo.method()两条路径可能重复请求同一对象的 lift导致initCount的初始化逻辑被重复触发。这个用例的目的就是确保编译器对“双重引用同一对象”的情况能正确去重。该文件位于tests/valid/目录下按 paths.ts 的定义validTestDir tests/valid其中的*.w文件都会由 hangar 自动生成编译与运行测试属于必须通过的合法用例集。二、快照文档的结构一份 tf-aws 编译产物全景快照文件 double_reference.test.w_compile_tf-aws.md 的文件名遵循 hangar 的命名约定源码名_测试类型_目标平台.md。其中compile表示编译测试tf-aws表示目标平台。文档共收录 5 个编译产物按文件名排序产物文件语言内容inflight.$Closure1-1.cjsCommonJS测试闭包test hello对应的 inflight 处理器inflight.Bar-1.cjsCommonJSBar类的 inflight 客户端inflight.Foo-1.cjsCommonJSFoo类的 inflight 客户端main.tf.jsonJSON面向 AWS 的 Terraform 资源配置DynamoDBpreflight.cjsCommonJSpreflight 阶段生成的构造代码与 lift 元数据这些文件的采集逻辑可以在 generated_test_targets.ts 中确认编译完成后hangar 遍历目标目录target/name.tfaws/.wing按[preflight., inflight., extern/, proc, .Struct.js]的前缀规则筛选产物排除.mapsourcemap最终由 createMarkdownSnapshot 将文件内容拼装为 Markdown 快照并用 vitest 的toMatchFileSnapshot进行比对。2.1 为什么会有三个 inflight 文件Wing 编译器会把源码中每个“会在 inflight 阶段运行”的类或闭包单独序列化为一个.cjs模块。本例中inflight.Foo-1.cjsFoo的method()与$inflight_init()inflight.Bar-1.cjsBar的callFoo()inflight.$Closure1-1.cjstest hello闭包的handle()。三个模块相互独立、通过 require 与参数注入建立联系这正体现了 Wing“preflight 与 inflight 代码分离、依赖显式传递”的编译模型。三、逐文件解读编译产物3.1inflight.$Closure1-1.cjs测试闭包的 inflight 实现module.exports function({ $bar, $bar_foo, $initCount }) { class $Closure1 { async handle() { (await $bar.callFoo()); (await $bar_foo.method()); $helpers.assert($helpers.eq((await $initCount.peek()), 2), initCount.peek() /*1*/ 2); } } return $Closure1; }这个模块的入口参数{ $bar, $bar_foo, $initCount }是编译器的lift 清单闭包在 inflight 阶段需要三个 preflight 对象——bar调用callFoo、bar.foo直接调用method以及initCount调用peek。三个$前缀变量在 inflight 运行时由宿主注入所有 inflight 方法调用都显式await保证异步语义。3.2inflight.Bar-1.cjs与inflight.Foo-1.cjs类的 inflight 客户端Bar的客户端把字段foo保存为this.$this_foomodule.exports function({ }) { class Bar { constructor($args) { const { $this_foo } $args; this.$this_foo $this_foo; } async callFoo() { (await this.$this_foo.method()); } } return Bar; }Foo的客户端则体现了inflight new()的语义——被转译为隐式初始化方法$inflight_init()module.exports function({ $initCount }) { class Foo { async method() { } async $inflight_init() { (await $initCount.inc()); } } return Foo; }这里出现了一个关键名字$inflight_init。它对应 lifting.ts 中定义的常量这是编译器在_liftMap矩阵中使用的特殊操作名表示某些传递依赖无论宿主请求什么操作都必然需要对用户而言该操作是隐藏的不会作为op传给onLift/onLiftType。换言之inflight new()是 Wing 为 inflight 类提供的“实例初始化钩子”编译器在 lift 分析中把它建模为一个隐式依赖操作。3.3main.tf.jsoncloud.Counter的 AWS 落地形态{ provider: { aws: [ {} ] }, resource: { aws_dynamodb_table: { Counter: { //: { metadata: { path: root/Default/Default/Counter/Default, uniqueId: Counter } }, attribute: [ { name: id, type: S } ], billing_mode: PAY_PER_REQUEST, hash_key: id, name: wing-counter-Counter-c824ef62 } } } }这份 Terraform JSON 是tf-aws 平台对cloud.Counter的具体映射结果。从源码看cloud/counter.ts 定义 Counter 为“分布式原子计数器”distributed atomic counter支持inc/dec/peek/set四个 inflight 操作构造参数initial默认0shared-aws/counter.ts 声明哈希键COUNTER_HASH_KEY id因此在 Terraform 中hash_key为id、attribute为字符串类型的id表名wing-counter-Counter-c824ef62由构造路径root/Default/Default/Counter/Default与唯一 ID 组合而成避免多栈冲突billing_mode: PAY_PER_REQUEST表示按量计费无需预置读写容量。同时shared-aws/counter.ts 的onLift实现会在 inflight 宿主上追加对应 DynamoDB 的 IAM 策略语句通过calculateCounterPermissions按操作计算权限并以环境变量DYNAMODB_TABLE_NAME_addr将表名注入宿主。也就是说一个cloud.Counter在 AWS 上 一张 DynamoDB 表 一套按需生成的 IAM 权限 一个环境变量注入。需要说明快照中的main.tf.json经过了 sanitize_json_paths 的清洗——绝对路径、source_hash、terraform 块与 cdktf 版本信息会被规范化或删除以保证快照跨机器、跨平台稳定可比。3.4preflight.cjslift 元数据的真正来源preflight 代码是理解整个机制的核心它定义了Foo、Bar与$Closure1三个 preflight 类并暴露了两类 lift 元数据1_toInflightType()inflight 客户端实例化模板class Bar extends $stdlib.std.Resource { static _toInflightType() { return require(${$helpers.normalPath(__dirname)}/inflight.Bar-1.cjs)({ }) ; } }Foo的模板中则注入了$initCount: ${$stdlib.core.liftObject(initCount)}$Closure1的模板同时注入了$bar、$bar_foo、$initCount三个对象——这与 3.1 节 inflight 闭包模块的入参一一对应。2_liftMap操作级依赖矩阵以$Closure1为例get _liftMap() { return ({ handle: [ [bar, [callFoo]], [bar.foo, [method]], [initCount, [peek]], ], $inflight_init: [ [bar, []], [bar.foo, []], [initCount, []], ], }); }操作handle即test hello的执行体依赖bar的callFoo、bar.foo的method、initCount的peek隐式操作$inflight_init空依赖但会触发闭包对三个对象的实例化准备。Bar的_liftMap则展示了依赖的传递性get _liftMap() { return ({ callFoo: [ [this.foo, [method]], ], $inflight_init: [ [this.foo, []], ], }); }callFoo自身没有额外捕获但会把请求转发给成员this.foo的method——这正是“双重引用”被正确展开的桥梁。3_liftedState()字段级状态提升_liftedState() { return { ...(super._liftedState?.() ?? {}), $this_foo: $stdlib.core.liftObject(this.foo), }; }Bar在 inflight 阶段需要的字段foo通过_liftedState序列化为$this_foo最终在inflight.Bar-1.cjs的构造函数中被还原为this.$this_foo。四、底层原理collectLifts 与对象引用去重快照中$Closure1的_liftMap同时列出[bar, [callFoo]]与[bar.foo, [method]]看起来是两条独立依赖。但 SDK 的 lift 引擎并不会“照单全收”地重复处理而是通过 lifting.ts 中的collectLifts()与parseMatrix()做对象身份去重。核心机制collectLifts(initialObj, initialOps)维护一个“已探索集合”explored与一个“待探索队列”queue以Map对象, Set操作的形式跟踪每个对象已被请求的操作集合对每个对象若已有同类操作被探索过则跳过lifting.tsparseMatrix()把编译器产出的[obj, [ops]]数组矩阵转换为Mapobj, Setops并明确注释编译器可能不知道 preflight 阶段obj1与obj2实际是同一对象去重会把[obj1, [op1]] [obj2, [op2]]合并为Map(obj1 → Set[op1, op2])lifting.ts;遍历每个操作连同隐式的$inflight_init时把矩阵中的依赖对象继续入队最终形成闭包式的传递依赖闭包最后Lifting.lift()对收集到的每个对象, 操作集调用onLift/onLiftType保证每个对象的onLift只被调用一次从而避免重复授权、重复初始化。这正是 double_reference 用例想要守护的行为虽然源码对bar.foo有两条引用路径经bar.callFoo()间接引用、经bar.foo.method()直接引用但去重机制保证Foo的初始化initCount.inc()只执行一次最终peek() 2一次new Foo() 一次断言前的显式计数。这份编译快照之所以被固化就是为了在后续编译器演进中防止回归——一旦 lift 分析出错快照比对立即失败。五、验证闭环从编译快照到 sim 运行hangar 为每个合法用例同时生成两类测试见 generate_tests.tstest(wing compile -t tf-aws, async () { await compileTest(sourceDir, filename, ...); }); test(wing test -t sim, async () { await testTest(sourceDir, filename, ...); });编译测试以-t tf-aws运行wing compile采集产物生成 Markdown 快照即本文解析的文档运行测试以-t sim运行wing test采集 stdout/stderr 生成运行快照。double_reference的 sim 运行快照位于 double_reference.test.w_test_sim.md内容为pass ─ double_reference.test.wsim » root/Default/test:hello Tests 1 passed (1) Snapshots 1 skipped Test Files 1 passed (1) Duration DURATIONpass表明测试闭包root/Default/test:hello在模拟器sim中真实执行并通过了initCount.peek() 2的断言与 tf-aws 编译快照形成“能编译 能运行”的双重验证。注意运行快照中的Duration DURATION是 sanitizeOutput 对不稳定输出时长、端口、时间戳、绝对路径等的归一化结果保证快照内容可重复比对。补充testTest只对入口文件以.main.w/.test.w/main.w结尾生成运行快照generated_test_targets.tsdouble_reference.test.w恰好满足.test.w后缀条件。六、实操如何查看与复现这份快照如果你希望在本仓库中复现或扩展验证可以按以下步骤操作仓库只读仅说明查看与运行方式不做任何修改查看源码与快照用例源码tests/valid/double_reference.test.wtf-aws 编译快照tools/hangar/snapshots/test_corpus/valid/double_reference.test.w_compile_tf-aws.mdsim 运行快照tools/hangar/snapshots/test_corpus/valid/double_reference.test.w_test_sim.md理解生成链路测试文件由 generate_tests.ts 从tests/valid自动生成快照由 generated_test_targets.ts 的compileTest/testTest调用wingCLI 后采集再由 utils.ts 的createMarkdownSnapshot写入__snapshots__/test_corpus/目录路径约定见 paths.tssnapshotDir tools/hangar/__snapshots__。本地运行测试需要先构建仓库并安装依赖# 在 tools/hangar 目录下执行 vitest仅运行 double_reference 相关用例 pnpm vitest run --testNamePattern double_reference若编译器行为发生变化导致产物不一致快照比对会直接失败并输出 diff这正是 hangar 把编译产物“固化为文档”的意义。七、总结从 double_reference.test.w_compile_tf-aws.md 这一份快照文档可以串联起 Wing 编译器与 SDK 的完整能力链条语言层preflight 类Foo、Bar与 inflight 闭包test hello如何被拆分序列化lift 层_liftMap操作级依赖矩阵 collectLifts/parseMatrix的对象引用去重保证双重引用不产生重复初始化对应 issue #3244平台层cloud.Counter在 tf-aws 平台上被具体化为 DynamoDB 表、IAM 权限与环境变量注入测试工程层hangar 通过编译快照 运行快照的双通道把这一切固化为可持续回归验证的基线。快照文档看似只是“生成的代码陈列”实则是 Wing 语言“同一份代码preflight 编排、inflight 执行、平台差异化落地”这一核心设计的最直接、最可验证的证据。赞分享编程语言编译器云原生语言运行时【免费下载链接】wingA programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡项目地址https://gitcode.com/gh_mirrors/wi/wing点击查看免费下载相关推荐Wing 语言 inflight class 定义与编译产物剖析从测试用例到 tf-aws 快照Wing 语言 inflight class 定义与编译产物剖析从测试用例到 tf aws 快照 本文以 Wing 仓库中 inflight_class_de编程语言编译器云原生语言运行时Wing 容器 Entrypoint 快照测试解析从 sim.Container 参数到 tf-aws 编译产物Wing 容器 Entrypoint 快照测试解析从 sim.Container 参数到 tf aws 编译产物 导读 本文围绕 Wing 仓库中 hanga编程语言编译器云原生语言运行时Wing 语言 math.atan 测试全解析从 tf-aws 编译快照到 SDK 实现Wing 语言 math.atan 测试全解析从 tf aws 编译快照到 SDK 实现 本篇围绕 Wing 仓库中 atan.test.w 的 Hangar编程语言编译器云原生语言运行时上一篇终极指南node-apn 证书与 Token 认证方式全面对比及选择策略下一篇Skill Seekers企业部署团队协作、私有配置库与500开发者规模管理终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考