uBlock Origin 的 Public Suffix List 模块:从 WAT 源码到 WASM 加速的编译与加载全解
uBlock Origin 的 Public Suffix List 模块:从 WAT 源码到 WASM 加速的编译与加载全解【免费下载链接】uBlockuBlock Origin - An efficient blocker for Chromium and Firefox. Fast and lean.项目地址: https://gitcode.com/GitHub_Trending/ub/uBlock本文以 wasm 目录的 README 为核心,完整讲解 uBlock Origin 中 Public Suffix List(PSL,公共后缀列表)查询模块的 WASM 加速组件:如何用wat2wasm将publicsuffixlist.wat编译为publicsuffixlist.wasm、WAT 源码的模块结构与内存布局约定,以及该 WASM 模块在扩展运行时中的可选加载链路与降级策略。读完之后,你能够独立复现该.wasm文件的构建过程,并理解它在 uBlock Origin 域名解析体系中扮演的角色。这个 wasm 目录解决什么问题目录 src/lib/publicsuffixlist/wasm/ 下只有三类文件:一份说明文档README.md、WebAssembly 文本格式源码 publicsuffixlist.wat 与它的二进制编译产物 publicsuffixlist.wasm。README 开头就明确了它的定位:For code reviewersAllwasmfiles in that directory were created by compiling the correspondingwatfile using the command ...也就是说,该文档主要面向代码审查者,回答一个具体问题:目录里的.wasm二进制文件是怎么来的、用什么工具链构建、如何复现。其对应的宿主实现是 publicsuffixlist.js,文件头注明这是 Raymond Hill 的 publicsuffixlist.js 库的移植,用于高效处理 Mozilla 基金会维护的 Public Suffix List(用于按getDomain、getPublicSuffix、suffixInPSL等 API 解析可注册域名)。复现编译:wat2wasm工具链README 给出的完整编译步骤如下(以publicsuffixlist.wat/publicsuffixlist.wasm为例):wat2wasm publicsuffixlist.wat -o publicsuffixlist.wasm配套的前提条件与替代方案,原文档逐条列出了:命令必须在当前目录内执行,即src/lib/publicsuffixlist/wasm/目录下运行,输入输出文件均为相对该目录的路径;wat2wasm工具的获取:从 WebAssembly 官方项目 wabt 的发行版(releases)下载;publicsuffixlist.wat 文件头部的注释块(How to compile部分,见 第 15-21 行)同样内嵌了这条编译命令,保证了源码与文档的一致;在线编译替代方案:WebAssembly 官方提供 wat2wasm 在线 demo,操作方式是把整个wat文件的内容粘贴进 WAT 编辑区,点击 Download 按钮即可下载编译出的.wasm文件——适合不想本地安装 wabt 的场景;延伸阅读:README 在 See also 一节还提到,对感兴趣的人可以用 WasmExplorer 这类在线工具查看 WASM 编译出的机器码,便于反汇编级别地审查这份小模块。值得注意的细节:publicsuffixlist.wat全文约 320 行,是一份完全手写、带中文式逐行注释(每条指令旁标注对应 JS 伪代码)的 WebAssembly Text 格式源码,并非由 C/Rust 等语言转译而来。这正契合 uBlock Origin 小而精 的实现风格——审查者可以直接阅读 WAT 源码本身,而不必理解任何上游语言。WAT 源码结构:一个只导出单一函数的模块publicsuffixlist.wat 的模块结构极其精简,可以拆解为三个部分。1. 导入宿主内存(module (memory (import imports memory) 1)模块在 第 29 行 声明:内存不是模块自建的,而是从imports对象导入,初始 1 页(64 KiB)。这与 JS 侧的实例化逻辑严格对应——publicsuffixlist.js 第 566-569 行 中,WebAssembly.instantiate(module, { imports: { memory } })传入的正是 JS 侧预先创建、并按需memory.grow的WebAssembly.Memory,即 WASM 与 JS 共享同一块线性内存,wasm代码直接读写 JS 数组视图中的树形数据结构。2. 唯一导出函数getPublicSuffixPos(func (export getPublicSuffixPos) (result i32) ;; result match index, -1 miss ...整个模块只导出 第 61-62 行 的getPublicSuffixPos(),返回i32:命中的后缀在主机名缓冲区中的位置偏移,未命中返回-1。JS 侧在启用 WASM 时正是取instance.exports.getPublicSuffixPos替换纯 JS 版本(见 publicsuffixlist.js 第 588-589 行)。函数声明了十余个局部变量($iNode、$iLabel、$cursorPos、$l/$r等,见 第 63-81 行),其算法骨架与 publicsuffixlist.js 中的getPublicSuffixPosJS逐行对应:标签遍历循环(WAT 第 101 行起的block $labelLookupDone loop $labelLookup):从LABEL_INDICES_SLOT(偏移 256) 处读取当前标签的[长度, 起始位置]字节对,逐个标签自右向左匹配;二进制搜索内层循环(WAT 第 132 行起的block $binarySearchDone loop $binarySearch):对当前节点的子节点数组做二分查找,按长度差 → 逐字节比较的字典序收敛;注释中保留了const iCandidateNode iCandidates iCandidate (iCandidate 1)这样的 JS 原始表达式,说明每个子节点在数组中占 3 个 i32 槽位(12 字节);PSL 算法规则映射:WAT 第 248-267 行实现规则 2——若无匹配规则,生效规则为*(检查首个候选是否为0x2A并写入SUFFIX_NOT_FOUND_SLOT);第 279-294 行实现规则 5——例外规则去掉最左标签;第 295-304 行用flags 0x01记录is_publicsuffix命中位置,作为最终返回的cursorPos。3. 与 JS 共享的缓冲区布局约定WAT 文件头注释(第 32-49 行)与 JS 源码(publicsuffixlist.js 第 47-71 行)给出了完全一致的内存契约:Node: u8: length of char data u8: flags bit 0: is_publicsuffix, bit 1: is_exception u16: length of array of children u32: char data or offset to char data u32: offset to array of children 12 bytes以及固定槽位(注释列出了 i32/i8 双视角下同一地址的不同偏移):常量i32 偏移对应字节偏移用途HOSTNAME_SLOT00主机名(小写)字符数据区RULES_PTR_SLOT100400规则树根节点指针CHARDATA_PTR_SLOT101404长标签(4 字符)字符数据区指针LABEL_INDICES_SLOT256256标签索引表(最多 128 个标签的 [len, beg] 字节对)SUFFIX_NOT_FOUND_SLOT—399*兜底命中标志位WAT 中对这些偏移的使用可以在指令中直接看到:如 第 84-92 行i32.const 404 / i32.const 400分别对应CHARDATA_PTR_SLOT与RULES_PTR_SLOT的字节偏移。这种JS 与 WASM 共用一套魔数的设计,使得 WASM 只是热点函数(getPublicSuffixPos)的语言替换,数据编码零转换、零拷贝。运行时:WASM 是可选项,JS 是兜底publicsuffixlist.js 第 543-544 行 的注释点明了架构基调:The WASM module is entirely optional, the JS implementation will be used should the WASM module be unavailable for whatever reason.即.wasm只是对getPublicSuffixPosJS的透明加速;任何环节失败(不支持 WebAssembly、CPU 非小端、fetch 失败等)都会静默回落到纯 JS 路径,功能不受影响。enableWASM的启用流程(第 546-604 行)包含几道前置检查:typeof WebAssembly ! object直接返回 false;端序探测:写入Uint32Array [1]后检查首字节是否为 1(第 553-556 行)。注释解释了原因——WASM 代码依赖 JS 侧的原生uint32数组视图,只有原生小端 CPU 上两者布局才一致;成功后把 WASM 内存增长到覆盖现有数据,把pslBuffer32内容拷入 WASM 内存,并将getPublicSuffixPos切换为instance.exports.getPublicSuffixPos。在 uBlock Origin 主程序中,这一调用发生在后台的 PSL 加载链路 storage.js 的µb.loadPublicSuffixList:// WASM is nice but not critical if ( vAPI.canWASM this.hiddenSettings.disableWebAssembly ! true ) { const wasmModuleFetcher function(path) { return fetch( ${path}.wasm, { mode: same-origin }).then( WebAssembly.compileStreaming ).catch(reason { ubolog(reason); }); }; let result false; try { result await psl.enableWASM(wasmModuleFetcher, ./lib/publicsuffixlist/wasm/ ); } catch(reason) { ubolog(reason); } ... }可见三个工程细节:双重开关:只有浏览器能力检测vAPI.canWASM通过、且用户没有在隐藏设置里打开disableWebAssembly时,才会尝试加载 WASM;流式编译:fetcher 用fetch(..., { mode: same-origin }).then(WebAssembly.compileStreaming),直接以 Response 流交给 WebAssembly 编译器,请求的正是本文档所在目录下的publicsuffixlist.wasm(路径拼接为./lib/publicsuffixlist/wasm/publicsuffixlist.wasm);日志留痕:成功时打印WASM PSL ready ... ms after launch,方便在控制台观察 WASM 何时就位。WASM 就绪后,PSL 本体仍按正常流程加载:优先从缓存读取序列化快照(selfie/assetKey,经psl.fromSelfie还原),否则从 PSL 资产取文本并用punycode.toASCII转换后psl.parse编译入库,同时把toSelfie()结果写回缓存(见 storage.js 第 1273-1294 行)。因为数据布局完全一致,这套流程对 WASM 是否启用无感。如何验证与审查这份 WASM单元测试:npm 平台的测试 platform/npm/tests/wasm.js 覆盖了 README 隐含的两条路径——在WebAssembly可用时enableWASM()应兑现为true(第 36-40 行),在被显式置为undefined的环境(模拟不支持 WASM 的运行时)中应兑现为false(第 43-52 行),验证了可选加速、优雅降级这一契约;构建一致性审查:按前文的wat2wasm publicsuffixlist.wat -o publicsuffixlist.wasm从.wat重新编译一份,与仓库中提交的publicsuffixlist.wasm对比,即可确认二进制产物确实由文本源码生成,这是 README For code reviewers 一节的原始意图;反汇编审查:如 README See also 所述,可用 WasmExplorer 类工具将publicsuffixlist.wasm反汇编,与 publicsuffixlist.wat 的指令序列逐条对照,确认编译产物没有偏离源码。小结这份 wasm 目录的 README 篇幅虽小,却把 uBlock Origin 一个典型的微加速组件交代得完整:.wat是手写源码、.wasm是wat2wasm的直接产物、模块只导出一个依赖共享内存的查询函数、JS 永远是可用的兜底实现。对维护者而言,审查这份 WASM 不需要任何编译工具链之外的知识——读 300 多行带注释的 WAT 即可覆盖全部逻辑;对使用者而言,disableWebAssembly隐藏设置与运行时自动降级保证了即使 WASM 加载失败,PSL 域名解析功能依然完整。【免费下载链接】uBlockuBlock Origin - An efficient blocker for Chromium and Firefox. Fast and lean.项目地址: https://gitcode.com/GitHub_Trending/ub/uBlock创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

RAG-Anything 接入 LightRAG:3 步给老项目升级多模态 RAG

RAG-Anything 接入 LightRAG:3 步给老项目升级多模态 RAG

RAG-Anything 接入 LightRAG:3 步给老项目升级多模态 RAG 【免费下载链接】RAG-Anything "RAG-Anything: All-in-One RAG Framework" 项目地址: https://gitcode.com/GitHub_Trending/ra/RAG-Anything 你的 LightRAG 项目现在只能读纯文本&#xf…

2026/11/12 10:33:31 阅读更多 →
3000元预算为5轴重托配舵机:扭矩选型与稳定性调校全记录

3000元预算为5轴重托配舵机:扭矩选型与稳定性调校全记录

如果你接手一台真正算得上“重托”的 5 轴设备,第一反应往往是先考虑用多大功率的电机。等把预算拆开,你会发现最不省心的其实不是电机本体,而是整套传动结构在低速、大悬臂、重心随时变化的场景里能不能稳定输出。这也是给 5 轴重托配舵机时…

2026/11/12 10:33:31 阅读更多 →
偏激暴食者的审美误区:重建身体信号,走出暴饮暴食循环

偏激暴食者的审美误区:重建身体信号,走出暴饮暴食循环

偏激暴食者的审美误区,常常在同一个晚上完成两次切换。晚餐前,一切碳水、油脂和甜味都被描述成需要消灭的敌人;晚餐后,当那盘被用力拒绝过的食物终于进了口,原本的“严格计划”反而瞬间瓦解:反正已经破功&a…

2026/11/1 23:03:42 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/12 5:00:00 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/12 5:13:20 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/12 5:13:20 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/11 17:16:37 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/11 17:16:37 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/12 5:13:20 阅读更多 →

日新闻

周新闻

月新闻