Ruffle AVM1/AMF0 序列化回归测试套件用 SharedObject、LocalConnection 与 NetConnection 三通道逐字节校验 Flash 数据序列化【免费下载链接】ruffleA Flash Player emulator written in Rust项目地址: https://gitcode.com/GitHub_Trending/ru/ruffle本文以 tests/tests/swfs/avm1/amf0_serde_suite 目录下的 AMF0 序列化回归测试为核心讲解 Ruffle 如何通过一套 ActionScript 2 测试脚本把同一份覆盖原语、Date/XML 原生对象、稀疏数组、注册类、循环引用与 DAG 引用缓存的“压力载荷”分别经由 SharedObject磁盘、LocalConnection内存和 NetConnection网络三条通道序列化并与 Flash Player 的真实输出逐字节对比。读完本篇你将掌握该测试的完整运行方法含时区要求与 Flash Player 网络信任配置、测试配置项含义以及 Ruffle 在 core/src/avm1/amf.rs 中的 AMF0 序列化实现细节。一、测试套件定位AVM1/AMF0 协议回归AMF0 是 Flash 用于跨进程、跨网络传输数据的序列化协议。Ruffle 需要保证自己读写的 AMF0 字节流与 Adobe Flash Player 完全一致否则与真实 Flash 服务互操作如NetConnection调远程网关、SharedObject读写磁盘就会失败。test.as 文件头部自述为 “THE AVM1 / AMF0 PROTOCOL REGRESSION SUITE”即 AVM1/AMF0 协议回归套件。套件的核心思路是构造一份尽量“刁钻”的载荷对象通过三条通道各走一遍序列化/反序列化往返再用trace输出逐项断言与 Flash Player 的输出快照逐行对比。1.1 三条数据通道test.as 将载荷分别送入三个通道通道机制测试重点Channel ASharedObjectSharedObject.getLocal(avm1_test_suite, /)flush()写盘再重新getLocal读回AMF0 磁盘持久化格式.sol 文件Channel BLocalConnectionlcSend.send(test_lc_pipe, onMainPacket, payload, topFunc, topMc)内存内序列化另测试顶层参数传 function / MovieClip 时的中止行为Channel CNetConnectionnc.call(test.avm1, responder, payload)向http://localhost:8000/发 POST网络传输的原始 AMF0 字节流由 server.py 捕获并以十六进制打印注意 Channel C 前的注释在发送前delete掉了arr_strict_ref/arr_mixed_ref/t_reg_ref三个 DAG 引用字段注释明确说明 Ruffle 的 AMF0 引用计数器reference bytes在原始值上目前与 Flash Player 存在失步会造成逐字节不匹配——这是套件当前标记known_failure的已知原因之一。1.2 载荷覆盖的数据类型test.as 第 112 行起构造payload共八组原语Primitives正负浮点、正负整数、0/-0并用1/x输出Infinity验证符号零语义、Infinity/-Infinity、普通字符串、空串、非 ASCII 串Jalapeño Mjölnir、代理对\uD83D\uDE00、未配对代理项\uD83D、含不可打印字符的串、true/false、null、undefined。原生对象Nativesnew Date(1672531200000)2023-01-01 UTC验证 Date 的时间戳与时区偏移字段new XML(...)验证 XML 节点以字符串形式传输后仍可解析出nodeName与属性。问题类型Problematic Typesfunction 与 MovieClip。AMF0 规范下 function 应被丢弃或变成undefinedMovieClip 的自定义变量行为因通道而异见第四节结果对比。数组Arrays严格数组稀疏数组索引取i32_max、i32_max1、u32_max、u32_max1、i64_max、i64_max1、u64_max、u64_max1等极端值混合数组数值下标 字符串键负数与小数索引以及手工伪造的“假数组”普通 Object 带length2和0/1键。类型化对象Typed Objects未注册类、Object.registerClass(com.tests.RegClass, RegClass)注册类、注册名不匹配的WeirdAliasString、同一构造器注册两次Alias.One/Alias.Two、伪造constructor属性与伪造__proto__的两种“冒充”对象。深层嵌套Deep Nestingo.prop o循环引用对象RegClass 实例内再嵌含 Date、XML 的严格数组和稀疏数组。AMF0 引用缓存DAGspayload.arr_strict_ref payload.arr_strict等三组自引用利用 AS2 中复杂类型的做内存同一性检查验证 AMF0 的 object id 引用机制是否让两次出现解析为同一对象。嵌套显示对象普通对象中塞入movie_clip_prop验证 DisplayObject 在序列化时被剥离应为undefined。验证函数verifyPayload(prefix, d)test.as 第 11–104 行对八组数据逐条trace输出形成每通道 60 余行的可 diff 断言。二、运行测试完整继承原 README以下内容直接继承自 README.md是该套件手动运行的权威说明时区前置条件需要将系统时区或 Flash Player 的时区设置为 UTC才能获得正确输出。原因是 AMF0 的 Date 数据中带有 timezone offset 信息时区不同会导致输出差异。验证网络通道的原始字节在 tests/tests/swfs/avm1/amf0_serde_suite 目录下运行server.py然后分别用 Flash Player 或 Ruffle Desktop 播放器运行test.swf。server.py 是一个 29 行的 Python HTTP 服务监听 8000 端口收到 POST 后打印Navigator::fetch的 URL、方法、Content-Type应为application/x-amf并把请求体按大写字节十六进制逐字节打印出来返回空 200 响应——这正是 output.txt 中Body: [...]那一大串字节的来源。Flash Player 网络信任配置Linux在 Flash Player 下运行时需要允许该 SWF 发起网络请求。在 Linux 上创建/etc/adobe/FlashPlayerTrust/test.cfg文件内容为/ancestor/of/swf/path其中/ancestor/of/swf/path是test.swf路径的任意祖先目录例如/home/username/。三、回归框架配置test.toml 与共享对象快照test.toml 是该测试在 Ruffle 回归测试框架tests/framework中的声明num_ticks 10 log_fetch true known_failure true [[compilers]] type Rascal target test.swf scripts [test.as] swf_version 8 [shared_objects.localhost//avm1_test_suite] expected avm1_test_suite known_failure true逐项说明num_ticks 10测试运行 10 个时钟 tick 后收集结果给异步通道LocalConnection/NetConnection留出执行时间。log_fetch true捕获Navigator::fetch即 NetConnection 发出的网络请求日志对应框架选项 log_fetch 字段使server.py捕获的字节流进入输出对比。[[compilers]]声明由test.as经 Rascal 编译器编译生成 test.swfSWF 版本 8说明该套件支持从源码重新编译验证。[shared_objects.localhost//avm1_test_suite]断言运行后磁盘上的共享对象avm1_test_suite与期望文件avm1_test_suite即 avm1_test_suite.sol逐字节一致由框架的 check_shared_objects 实现校验。顶层与 shared_object 均标记known_failure true表明当前 Ruffle 输出与 Flash Player 快照尚不完全一致框架会把它记为“已知的失败”而非 CI 阻塞与 test.as 中关于引用计数字节失步的注释相互印证。3.1 两份 1579 字节的 .sol 快照目录内同时存放 Flash Player 写出的 avm1_test_suite.sol 和 Ruffle 写出的 avm1_test_suite.ruffle.sol二者文件大小均为 1579 字节与 test.as 中so.getSize()打印的 1579 一致。用od查看两份文件头部前 120 字节完全相同000000 00 bf 00 00 06 25 54 43 53 4f 00 04 00 00 00 00 000010 00 0f 61 76 6d 31 5f 74 65 73 74 5f 73 75 69 74 000020 65 00 00 00 00 00 07 70 61 79 6c 6f 61 64 03 00即 AMF0 魔数00 BF 00 00其后对象结构里可见avm1_test_suite键名15 字节 0x0f、payload属性及p_num的 IEEE 754 双精度40 94 E6 ...1337.5。这说明 SharedObject 磁盘格式这一“最严格的通道”上Ruffle 与 Flash Player 当前至少在本快照范围内达到了逐字节一致。3.2 输出快照对比output.txt 是 Flash Player 的基准输出output.ruffle.txt 是 Ruffle 的实际输出。两者 diff 出的差异点正是当前待修复的行为差异例如ref_strict_is_exact等三条 DAG 引用断言在 Ruffle 输出中为falseFlash 为trueRuffle 的 LocalConnection 通道引用缓存尚未让重复对象解析到同一内存实例sp_func_type/sp_mc_var在 Ruffle 的 LocalConnection 通道为undefinedFlash 保留了 function 类型与自定义变量两条Body: [...]十六进制流在arr_strict与nest_deep段存在字节差异如 Flash 版本中引用字节0A与 Ruffle 版本中重复编码00/01的差异这正是known_failure的字节级证据。四、Ruffle 侧实现AMF0 序列化路径套件断言的行为其 Ruffle 实现位于 core/src/avm1/amf.rs共 343 行底层依赖独立的flash-lsocrateAMF0Decoder、Amf0Writer、ObjWriter。关键逻辑与测试载荷一一对应通道判定点serialize 函数注释明确其服务于 “NetConnection.addHeader, NetConnection.call, and LocalConnection.send” 三类调用。它把Value::Undefined/Null/Bool/Number/String直接映射为 AMF0 值MovieClip映射为UndefinedDisplayObject 属性也被替换为Undefined——这解释了 Channel A 中sp_mc_var: i_am_a_display_object保留自定义变量MovieClip 作为对象属性被序列化其属性表而 Channel B 中nested_mc_type: undefined显示对象在属性遍历中被剥离的行为分岔。类型化对象类名解析object_class_name 复刻了 Flash 的构造函数解析规则先查自身constructor属性再回退到隐藏的__constructor__属性可能是继承来的然后查registerClass注册表。这正对应测试中t_forged_ctor伪造constructor、t_forged_proto伪造__proto__与t_mismatch注册名不匹配三组“冒充”用例——从快照输出看这些对象最终都回退为匿名对象序列化instanceof断言为falseRuffle 与该行为一致。数组序列化serialize_array 按 Flash 规则处理“任何数值键都是数组元素、超出 length 的数值键会扩展序列化长度”并区分纯密集数组StrictArray空洞补undefined与含字符串键的混合数组ECMAArray——对应arr_strict/arr_sparse/arr_mixed/arr_fake各组的断言以及 16/32/64 位边界索引在稀疏数组中的降级表现快照中arr_sparse[9223372036854775807]被字符串键9.2233720368547758e18承接。引用计数Amf0Writer通过CacheKey/ObjectId跟踪已写对象commit_lso生成最终元素表DAG 引用断言在 Ruffle 输出中的false即该计数器与 Flash Player 语义尚未对齐的部分。五、小结与使用建议该套件是一份典型的“行为快照 字节快照”双层回归测试output*.txt对比逻辑层行为哪些值保留、哪些类型被丢弃、引用是否同一*.sol与server.py的十六进制体对比字节层格式磁盘 AMF0 与网络 AMF0。复现步骤归纳为将系统时区设为 UTC在 tests/tests/swfs/avm1/amf0_serde_suite 下运行python3 server.py监听 8000 端口Linux 上按 README 建立/etc/adobe/FlashPlayerTrust/test.cfg信任test.swf的祖先路径仅 Flash Player 需要分别用 Flash Player 与 Ruffle Desktop 打开 test.swf对照 output.txt / output.ruffle.txt 与两份 .sol 快照定位差异在 CI 中则直接由回归框架按 test.toml 执行known_failure项会在对应行为修复后转为强断言。对于希望贡献修复尤其是ref_*引用同一性与 NetConnection 字节对齐的开发者入口就是 core/src/avm1/amf.rs 的 serialize/deserialize 两条路径与flash-lso的Amf0Writer计数器逻辑本目录的三份快照文件则提供了随时可复验的基准。【免费下载链接】ruffleA Flash Player emulator written in Rust项目地址: https://gitcode.com/GitHub_Trending/ru/ruffle创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
