Skip to content

控制流平坦化的还原思路:从调用点出发,而不是从 switch 出发

📝 这是一篇骨架。 每个 <!-- 写这里 --> 都是你要填的地方,填完把这段提示删掉。 没写完不想让它出现在列表里,就在 frontmatter 加一行 draft: true

写的时候记住三条:

  1. 写你自己踩的坑,不写网上抄得到的通识
  2. 写方法论,不写针对特定目标的可用绕过方案
  3. 每个结论后面跟一段代码或数据,空谈没有说服力

背景:为什么"全局展开 switch"行不通

换个方向:从真实调用点反向追踪

核心思路是不追求全局展开,只处理入口和参数能够静态证明的执行路径。

整条流水线是这样的:

text
识别 dispatcher 调用
→ 解析 case ID 与参数数组
→ 追踪真实 case 链
→ 规范化路径内代理调用
→ 递归处理嵌套 dispatcher
→ 绑定参数并选择平铺 / IIFE / 命名 helper
→ 将 dispatcher/helper 上下文交给字符串运行时

第一步:确认它真的是 dispatcher

只有绑定到带 switch 的函数才会被当作 dispatcher。

第二步:静态入口与静态参数容器

第三步:case 链追踪与环路保护

第四步:输出形态的选择

还原完的代码有三种落地方式,选哪种取决于 return 的位置、调用点所处的上下文,以及参数数组是否被当作对象身份使用:

形态适用场景
直接平铺
IIFE
命名 helper

职责边界:还原引擎不该做字符串解密

安全约束:宁可不还原,也不能还原错

这套东西最难的不是"能还原多少",而是"在什么情况下必须停手"。

  1. 只展开静态入口和静态参数容器
  2. 未知分支可能影响字符串栈时,回滚整次事务
  3. 未知循环只处理必然执行的部分,不模拟循环次数
  4. 延迟函数体不因为遍历到定义就提前执行
  5. 运行时对象和字符串栈在每次求值后恢复

目前的边界在哪

诚实地列出没做到的部分,比吹能做到什么更可信:

未完成原因
通用返回闭包上下文需要把创建时的上下文持久化到闭包绑定,并在真实调用点恢复
延迟回调事件、计时器、宿主回调的执行时机未知,必须等调用关系被证明
async / generator涉及挂起和恢复点,不能沿用同步模型
动态 case ID缺少可靠静态入口时保留原调用

小结


本文只讨论通用的 AST 还原方法,不涉及针对特定目标的可用方案。

内容仅供技术研究与学习交流