progmune-runtime 3.7.43 → 3.7.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +271 -0
- package/dist/extract-ir-taint-structural.test.js +464 -3
- package/dist/extract-ir.js +1102 -75
- package/dist/sdk.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,276 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.7.46] — 2026-09-21
|
|
4
|
+
|
|
5
|
+
### C4j:载体收口的最后一批 + 一处既有缺陷的回归门
|
|
6
|
+
|
|
7
|
+
C4i 的备忘留了四条候选。按 R17 先写探针复现,同时塞进七条「没人提过但看着差不多」的
|
|
8
|
+
形状 —— 三轮探针:24 条覆盖面 → 17 条**防修过头**的边界 → 6 条给默认导出补真 import。
|
|
9
|
+
真缺口六条;另有一条**不是这轮引入**的缺陷被探针顺手挖出来(见下)。
|
|
10
|
+
|
|
11
|
+
| 形状 | 旧行为 | 机制 |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| `typeof m === "string" \|\| n + ".md"` | 整条 helper 落选 | `typeof` 被当成**未知自由标识符** ⇒ 判据不通过 |
|
|
14
|
+
| `((n) => …) as Fn` / `satisfies` / `<Fn>…` | 不收 | 初值是**断言表达式**不是箭头;成员位同理 |
|
|
15
|
+
| `get mk() { return (n) => … }` | 不收 | getter 没有形参,`getFunctions()` 取不到 |
|
|
16
|
+
| `lvl3("a")("b")(k)` 三/四跳 | 不展 | 旧机制只吃**两跳** |
|
|
17
|
+
| `factory(".md").toPath(k)` | 不展 | 真正塑形的是**返回对象里的成员方法** |
|
|
18
|
+
| `export default (n) => …` | 收不到 | 匿名默认导出**没有名字**可登记 |
|
|
19
|
+
|
|
20
|
+
**精度侧一条:运算符关键字。** `typeof` / `void` / `in` / `instanceof` 是运算符不是自由
|
|
21
|
+
标识符,出现在 return 表达式里不该让整条 helper 落选。**只放行运算符** —— `this` /
|
|
22
|
+
`super` 不放行,它们指向宿主状态,放行等于给「返回值依赖 this」的假塑形开口子。
|
|
23
|
+
|
|
24
|
+
**召回侧五条。**
|
|
25
|
+
|
|
26
|
+
- **断言解包**:`unwrapFns` 解 `as` / `satisfies` / `<T>` 类型断言,**变量位、属性位、
|
|
27
|
+
调用点 callee、返回位**都要用 —— `get mk() { return ((n) => …) as Fn }` 就是「返回位
|
|
28
|
+
没解」漏出来的。
|
|
29
|
+
- **getter 返回的箭头**:getter 自己的形参表是空的,判据要落在**它返回出来的那一层**,
|
|
30
|
+
与柯里化同一条路。
|
|
31
|
+
- **柯里化推广成「跳链」**:`CurrySpec` 从写死的「外层 + 一层内层」改成
|
|
32
|
+
`{ outerParams, hops: CurryHop[], allParams, rets }`。`lvl3 = (a) => (b) => (n) => n+a+b`
|
|
33
|
+
⇒ `outerParams=[a]`、`hops=[call[b], call[n]]`;工厂形态 ⇒ `hops=[member toPath [n]]`。
|
|
34
|
+
调用点从「两层实参代入」改成**逐跳消费**,跳数不设死上限(爬取有界 4 层),首跳实参
|
|
35
|
+
同样被闭包捕获(所以 `lvl3(k)("a")("b")` 与 `lvl3("a")("b")(k)` 都要对)。
|
|
36
|
+
- **工厂形态 = 一个名字多条链**:按**成员名**选链(`makeMixed(".md").mix(k)` 走 mix 那条),
|
|
37
|
+
脏链(返回常量 / 体内有 sink)单独剔掉,不许污染同名的另一条。
|
|
38
|
+
- **匿名默认导出**:没名字 ⇒ 用**导入方**起的本地名发布(`import toPath from "./helpers"`)。
|
|
39
|
+
|
|
40
|
+
### 探针挖出的一处既有缺陷(C4h-② 的洞)
|
|
41
|
+
|
|
42
|
+
C4h 把「同名成员唯一才登记」换成「全组确证」,但只把**通过判据**的实现算进组:判据不
|
|
43
|
+
成立的实现没进组 ⇒ 组看起来「全组确证」⇒ 成员调用位放行 ⇒ 误标。
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
同名三处 getter:无形参的(判据①不成立)/ 正常塑形 / 体内有 sink 的(判据③不成立)
|
|
47
|
+
后两处进组、第一处没进 ⇒ 组"干净" ⇒ .mk( 放行 ⇒ ONoParam.mk(k) 被误标
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
修法 `noteUnaccepted`:**失败的实现也记进组**(记 key + group,不进候选)。组一脏,整组
|
|
51
|
+
不放行。教训:**「全组」的分母必须包含被判据拒掉的那些**,否则「全组确证」会退化成
|
|
52
|
+
「通过者全体确证」—— 已升为方法学规则 **R19**。
|
|
53
|
+
|
|
54
|
+
### 又一条「探针期望写错」(第二次)
|
|
55
|
+
|
|
56
|
+
- **F9a**:探针忘了写 import。匿名默认导出的名字只能来自导入它的文件,两文件之间没有
|
|
57
|
+
import 就无从登记 —— 期望本身不可达。补上真 import 后通过。
|
|
58
|
+
- **F9f**:期望 unmarked、实测 mark。查清后是**期望写错**不是误报:`(0, (n) => n + ".md")(k)`
|
|
59
|
+
里那个**内联匿名箭头自己就是塑形函数**,与默认导出无关 —— 把 `helpers.ts` 删掉照样标。
|
|
60
|
+
|
|
61
|
+
⇒ 两条都不是机制错,是**预测错**。写期望前先问:这条标/不标,到底由**哪一个机制**决定。
|
|
62
|
+
|
|
63
|
+
### 三条「看着像缺口、这轮故意不修」的边界
|
|
64
|
+
|
|
65
|
+
探针里留着(F5 / F7 / A6 / F12),都退回保守侧:`switch` 分支与 `if` 早返回的返回值取
|
|
66
|
+
**并集**(常量分支和污点分支混在一起,判不出);`x != null` 这类**比较式**左值不折;
|
|
67
|
+
标识符位「先声明后赋值」(`let f; f = (n) => …`)不收。这三条不是漏,是「判不出就退回
|
|
68
|
+
保守」的固有代价 —— 要收得让调用点折叠长出手臂(按分支路径分别代入),属另一轮的量级。
|
|
69
|
+
|
|
70
|
+
### 验收
|
|
71
|
+
|
|
72
|
+
| 门 | 结果 |
|
|
73
|
+
|---|---|
|
|
74
|
+
| taintpath 闸门 | **160/160**(新增 K 族 22 条:12 mark / 9 no-taint / 1 suppressed) |
|
|
75
|
+
| fr-007 真实语料 | pre 5 / post 0 **维持** |
|
|
76
|
+
| fr-016 真实语料 | pre 7 / post 0 **维持** |
|
|
77
|
+
| 定向用例 | 提取器定向用例 **227 passed / 6 个测试文件**(其中本文件新增 C4j 组 23 条:12 正 11 负,含两条回归·⑦) |
|
|
78
|
+
| TS 盲测 | 111 项目 / perFunction 口径 **3201 条**:**LOST 0 / ADDED 18**,全部来自新增的 K 族 —— 其中 12 条 `path_traversal` 是本该标的,6 条 `data_integrity`(写文件无审计)属新项目的无关类别;基线那 3183 条**零漂移零丢失** |
|
|
79
|
+
| tsc | 零错误 |
|
|
80
|
+
|
|
81
|
+
### 反向验证七刀(归因)
|
|
82
|
+
|
|
83
|
+
每刀只把一个机制退回**旧行为**(R18:不许 `return null` 逼停):
|
|
84
|
+
|
|
85
|
+
| 刀 | 改动 | 转红 |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| 1 | 关掉运算符关键字放行 | emitKw |
|
|
88
|
+
| 2 | 摘掉 getter 收集 | emitGetterCls / emitGetterObj |
|
|
89
|
+
| 3 | 断言解包退回只脱括号 | emitAs / emitAsSat / emitAsMember |
|
|
90
|
+
| 4 | 柯里化退回两跳语义 | emitCurry3 / emitCurry4 / emitCurryFirst |
|
|
91
|
+
| 5 | 工厂形态不登记 | emitFactory / emitFactoryMix |
|
|
92
|
+
| 6 | 摘掉匿名默认导出收集 | emitDefault |
|
|
93
|
+
| 7 | 成员组退回「判据失败不进组」 | 定向用例 回归·⑦ 两条 |
|
|
94
|
+
|
|
95
|
+
闸门其余 148 条、定向用例 C4j 组其余 21 条全程不动 ⇒ 七处互不相干,归因干净。
|
|
96
|
+
|
|
97
|
+
### 实现坑三条
|
|
98
|
+
|
|
99
|
+
1. **解包要解到位**:`returnedValueOf` 也得解断言 —— `get mk() { return ((n) => …) as Fn }`
|
|
100
|
+
漏的就是这一处("返回位没解")。
|
|
101
|
+
2. **一条名字多条链时,脏链要单独剔掉**:`makeMixed` 的 `skip`(返回常量)不许把 `mix`
|
|
102
|
+
那条链带下水,否则「张冠李戴」在链层面复现。
|
|
103
|
+
3. **`expandCurried` 的实参位置**:首跳实参落在外层形参的第一个,不是内层 —— 从「两层
|
|
104
|
+
实参代入」改成「逐跳消费」时最容易写反。
|
|
105
|
+
|
|
106
|
+
## [3.7.45] — 2026-09-21
|
|
107
|
+
|
|
108
|
+
### C4i:又一批可判定分支 + 又一批 helper 载体
|
|
109
|
+
|
|
110
|
+
备忘里挂着两条(getter/computed 属性 helper、常量代入扩展到短路与模板串)。按 R17
|
|
111
|
+
先写探针复现,跑完 19 个形状:**备忘只覆盖了一半**,真缺口有六个,其中柯里化与 rest
|
|
112
|
+
两条备忘里根本没有。
|
|
113
|
+
|
|
114
|
+
| 形状 | 探针实测 | 结论 |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| 短路 `\|\|` / `&&` / `??` | `viaOr(k, "fixed.md")` 误标 | 真误报(精度侧) |
|
|
117
|
+
| 模板串内嵌三元 | `viaTpl(k, true)` 误标 | 真误报(精度侧) |
|
|
118
|
+
| 柯里化 `withExt(".md")(k)` | 不标 | 真漏报(备忘未列) |
|
|
119
|
+
| namespace 内函数声明 | `P.toPath(k)` 不标 | 真漏报 |
|
|
120
|
+
| rest 形参 `...parts` | `joinAll("out", k)` 不标 | 真漏报(备忘未列) |
|
|
121
|
+
| IIFE 定义 / 计算属性名 | `toPathIife(k)`、`Box.toPath(k)` 不标 | 真漏报 |
|
|
122
|
+
| getter(无形参) | 不标 | **不是缺口** —— 无形参 ⇒ 判据①本就不成立 |
|
|
123
|
+
|
|
124
|
+
**精度侧:可判定分支的扩展。** 三元之外,短路运算符与三元同形,只是判据从 cond 换成
|
|
125
|
+
**左操作数的真假**(`\|\|` 左真取左、`&&` 左真取右、`??` 左非空取左)。模板串的
|
|
126
|
+
`${...}` 是独立表达式,折叠要能进去 —— 现代代码里 `` `${flag ? "fixed" : n}.md` `` 比
|
|
127
|
+
裸三元更常见。判不出一律退回原串。
|
|
128
|
+
|
|
129
|
+
**召回侧:四个新载体。**
|
|
130
|
+
|
|
131
|
+
- **柯里化**:`withExt(ext)(n)` 真正塑形的是被返回出来的内层函数。收集时识别「return
|
|
132
|
+
箭头」,判据落在内层;调用点把 `NAME(a)(b)` 按两层实参代入后**原地摊开**
|
|
133
|
+
(⇒ `k + ".md"`),之后的污点检测直接看摊开后的串。
|
|
134
|
+
- **namespace**:旧收集走 `sf.getFunctions()`,实测取不到 namespace 里的函数 ⇒ 改遍历
|
|
135
|
+
声明节点,只收顶层 / namespace 链上的(函数体内嵌套的作用域与调用点不同,不收)。
|
|
136
|
+
- **rest 形参**:它吃掉的是**一批**实参,不是一位。原先按位对齐会把第二位当成「越界」
|
|
137
|
+
抹掉 ⇒ 漏报。现在 rest 位吃掉末尾所有位,且常量代入时跳过 rest 位。
|
|
138
|
+
- **IIFE / 计算属性名**:`(() => (n) => …)()` 的初值是调用不是箭头;`{ [KEY]: (n) => … }`
|
|
139
|
+
的属性名只有在 KEY 是**模块级字面量常量**时才解 —— 解不出就不登记。
|
|
140
|
+
|
|
141
|
+
### 又一条「用例自己写错了」
|
|
142
|
+
|
|
143
|
+
初版把「属性名算不出来」的 `DynBox` 和同名可解的 `Box` 放在同一个项目里,期望
|
|
144
|
+
unmarked 却实测 mark。原因不是机制错:成员调用位 `.name(` 的匹配**本就与宿主无关**
|
|
145
|
+
(C4g 的保守策略),`Box.toPath` 登记之后 `DynBox.toPath` 必然被顺带命中。教训:写负
|
|
146
|
+
对照前先确认「不标」到底是因为哪一个机制不成立 —— 这条已写进语料注释。
|
|
147
|
+
|
|
148
|
+
### 验收
|
|
149
|
+
|
|
150
|
+
| 门 | 结果 |
|
|
151
|
+
|---|---|
|
|
152
|
+
| taintpath 闸门 | **138/138**(新增 J 族 26 条:13 mark / 12 no-taint / 1 suppressed) |
|
|
153
|
+
| fr-007 真实语料 | pre 5 / post 0 **维持** |
|
|
154
|
+
| fr-016 真实语料 | pre 7 / post 0 **维持** |
|
|
155
|
+
| 定向用例 | 提取器定向用例 **204 passed / 6 个测试文件**(其中本文件新增 C4i 组 26 条:11 正 15 负) |
|
|
156
|
+
| TS 盲测 | 110 项目 / perFunction 口径 **3183 条**:**LOST 0 / ADDED 18**,全部来自新增的 J 族 —— 其中 12 条 `path_traversal` 是本该标的,6 条 `data_integrity`(写文件无审计)属新项目的无关类别;基线那 3165 条**零漂移零丢失** |
|
|
157
|
+
| tsc | 零错误 |
|
|
158
|
+
|
|
159
|
+
### 反向验证七刀
|
|
160
|
+
|
|
161
|
+
| 刀 | 改动 | 转红 |
|
|
162
|
+
|---|---|---|
|
|
163
|
+
| 1 | `topLevelLogical` 恒返回 null(关短路折叠) | `emitOrTakeLeft` / `emitAndTakeLeft` / `emitNullishTakeLeft`(no-taint 转误标) |
|
|
164
|
+
| 2 | `foldTemplateSubsts` 恒返回原串 | `emitTplConst`(转误标) |
|
|
165
|
+
| 3 | `currySpecOf` 恒返回 null(柯里化不登记) | `emitCurrySecond` / `emitCurryFirst` / `emitCurryFn`(转漏报) |
|
|
166
|
+
| 4 | `moduleQualifier` 恒返回 null(namespace 不发限定名) | `emitNsPath`(转漏报) |
|
|
167
|
+
| 5 | `paramIndexOf` 退回按位(rest 位截断) | `emitRestJoin`(转漏报) |
|
|
168
|
+
| 6 | 不解 IIFE(`functionLikeOf` 的 CallExpression 分支) | `emitIifePath`(转漏报) |
|
|
169
|
+
| 7 | 不解计算属性名(`propertyNameOf` 的 computed 分支) | `emitComputed`(转漏报) |
|
|
170
|
+
|
|
171
|
+
七刀各自只动自己那一组,没有交叉。第 2 刀和第 5 刀第一次都**打偏了**(详见下),
|
|
172
|
+
打偏的刀会连累无关用例 —— 已升为方法学规则 **R18**。
|
|
173
|
+
|
|
174
|
+
### 实现坑三条
|
|
175
|
+
|
|
176
|
+
1. **`substituteParams` 把模板串整段跳过了** —— 折叠逻辑写对了也没用,`${flag ? …}`
|
|
177
|
+
里的 flag 根本没被代进去。模板串要按 `${}` 逐段递归代入,字面量部分照抄。
|
|
178
|
+
2. **ts-morph 的两层包装**:`namespace P { … }` 里函数的父节点是 **ModuleBlock**
|
|
179
|
+
(ModuleDeclaration 在上一层);`(() => (n) => …)()` 里被括号包住的**不止**内层
|
|
180
|
+
箭头,连被调用的 callee 都是 `ParenthesizedExpression`。两处都得脱。
|
|
181
|
+
3. **模板串里写注释会提前终结模板串**(生成器里 `${}`、反引号都要转义)—— 报错信息
|
|
182
|
+
指向的是文件末尾,很容易误判成别处语法错。
|
|
183
|
+
|
|
184
|
+
### 反向验证的两刀打偏(R18)
|
|
185
|
+
|
|
186
|
+
反向验证不是"随便让一个函数失效"就行,退回必须是**旧行为**:
|
|
187
|
+
|
|
188
|
+
- 第 5 刀第一次写成 `paramIndexOf` 返回 `null` —— rest 位不是"退回按位",而是整个
|
|
189
|
+
失去对应形参 ⇒ 所有 helper 的实参被抹光 ⇒ **40 条漏报**。正确写法是 `return argIndex`。
|
|
190
|
+
- 第 2 刀第一次写成 `foldTemplateSubsts` 返回 `null` —— 调用方靠 `tpl !== s` 判断
|
|
191
|
+
"这一层有没有做事",返回 null 会被当成"已折叠" ⇒ 连带 11 条无关用例转红。
|
|
192
|
+
|
|
193
|
+
⇒ R18:**反向验证的刀要退回旧行为,不能随便 return null**;刀的转红清单必须落在
|
|
194
|
+
自己那一组,一旦出现跨组转红,先怀疑刀打偏了,而不是"机制有耦合"。
|
|
195
|
+
|
|
196
|
+
## [3.7.44] — 2026-09-20
|
|
197
|
+
|
|
198
|
+
### C4h:名录之外的三处收口
|
|
199
|
+
|
|
200
|
+
helper 名录这轮的起点是三个备忘候选,动工前先写探针复现(R12),结果只有两个是真
|
|
201
|
+
缺口,外加一个**探针顺手挖出来的既有缺陷**:
|
|
202
|
+
|
|
203
|
+
| 候选 | 探针实测 | 结论 |
|
|
204
|
+
|---|---|---|
|
|
205
|
+
| 调用点常量实参代入 | `viaFlag(k, true)` 误标 | 真误报,本轮修(精度侧) |
|
|
206
|
+
| 同名方法出现两次 | `u.toPath(k)` 不标 | 真漏报,本轮修(召回侧) |
|
|
207
|
+
| 属性承载的箭头 helper | `Util.toPath(k)` 不标 | 真漏报,本轮修(召回侧) |
|
|
208
|
+
| ——(非备忘项) | 方法名唯一但判据不成立时,成员调用位照样进名录 | **既有缺陷**,`r.lookup(k)` 误标 |
|
|
209
|
+
|
|
210
|
+
**① 调用点常量代入。** 名录里的依赖集是**跨调用点**的并集 —— 对所有实参形状都成立
|
|
211
|
+
的最宽结论,收不住个案:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
const viaFlag = (n: string, flag: boolean): string => (flag ? "fixed.md" : n + ".md");
|
|
215
|
+
const rel = viaFlag(k, true); // 本次调用的返回值就是字面量 —— n 位进了函数也出不来
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
按形参位把**字面量实参**代回 return 表达式,试着解掉 cond 可判定的三元,再看还有
|
|
219
|
+
哪些形参活着;判不出(实参不是常量、cond 代入后算不出真假)就一律退回名录结论 ——
|
|
220
|
+
保守侧,认不出就不收精度。
|
|
221
|
+
|
|
222
|
+
**② 同名成员多处。** 旧规则要求方法名全项目唯一才登记 `.method(` 成员调用位。唯一性
|
|
223
|
+
只是「不可能认错」的**充分条件**,不是必要条件:同名两处都被确证为纯塑形时,同样
|
|
224
|
+
不可能认错。换成「全组确证」之后依赖集按形参位取并集(任一实现在某位会流出,那位
|
|
225
|
+
就算会流出)。
|
|
226
|
+
|
|
227
|
+
同一处顺带补上一个坑:候选登记改成 `key / name / group` 三段,**确证之后**才发布到
|
|
228
|
+
成员名录 —— 旧代码在确证之前就把裸名登记进去,于是
|
|
229
|
+
`class Reg { lookup(n) { return registry[n]; } }`(registry 是模块级非字面量对象 ⇒
|
|
230
|
+
判据③不成立)之后的 `r.lookup(k)` 被当成塑形 ⇒ 误标。
|
|
231
|
+
|
|
232
|
+
**③ 属性承载的箭头。** `const Util = { toPath: (n) => … }` 与 `Util.toPath = (n) => …`
|
|
233
|
+
既不是方法声明也不是变量声明,旧名录一片都收不到。现在与方法同表登记(限定名 +
|
|
234
|
+
同组确证发布裸名)。
|
|
235
|
+
|
|
236
|
+
### R13 第二次的变种:假通过也会发生在**定向用例**里
|
|
237
|
+
|
|
238
|
+
C4g 组的静态类方法用例原本叫 `Renderer.stat(k)` —— `stat` 就在 fs sink 名单里,调用点
|
|
239
|
+
被 sink 兜底直接标中,**那条用例一直在假通过**(删掉整段方法收集它照样绿)。改名
|
|
240
|
+
`toFile` 后才真的在测 helper 传播。R16 原只约束盲测语料命名,本轮起同样适用于定向
|
|
241
|
+
用例:写 fixture 前先 grep `TS_FILE_SINK_NAMES`。
|
|
242
|
+
|
|
243
|
+
### 验收
|
|
244
|
+
|
|
245
|
+
| 门 | 结果 |
|
|
246
|
+
|---|---|
|
|
247
|
+
| taintpath 闸门 | **112/112**(新增 I 族 20 条:11 mark / 8 no-taint / 1 suppressed) |
|
|
248
|
+
| fr-007 真实语料 | pre 5 / post 0 **维持** |
|
|
249
|
+
| fr-016 真实语料 | pre 7 / post 0 **维持** |
|
|
250
|
+
| 定向用例 | 提取器定向用例 **178 passed / 6 个测试文件**(其中本文件 102 passed,新增 C4h 组 21 条:12 正 9 负) |
|
|
251
|
+
| TS 盲测 | 109 项目 / perFunction 口径 **3165 条**:**LOST 0 / ADDED 11**,全部是新增 I 族里那 11 条本该标的 path_traversal;基线那 3152 条**零漂移零丢失**(另 2 条 `data_integrity` 属新项目的无关类别) |
|
|
252
|
+
| tsc | 零错误 |
|
|
253
|
+
|
|
254
|
+
### 反向验证四刀
|
|
255
|
+
|
|
256
|
+
| 刀 | 改动 | 转红 |
|
|
257
|
+
|---|---|---|
|
|
258
|
+
| 1 | 关掉调用点折叠(`callSiteKeepFlags` 退回名录 deps) | `emitFlagTrue` / `emitPickMiss` / `emitFlagNested` / `emitWrapFlagTrue`(4 条由 no-taint 转误标) |
|
|
259
|
+
| 2 | 同名合并退回「全项目唯一」 | `emitSameNameA` / `emitSameNameB`(转漏报) |
|
|
260
|
+
| 3 | 摘掉属性箭头收集((d)(e) 两个分支) | `emitPropArrow` / `emitNestedPropArrow` / `emitPatchedArrow`(转漏报) |
|
|
261
|
+
| 4 | 成员名退回「确证之前就登记」 | `emitNonShaperMember` / `emitSameNameRejected`(转误标 —— 那个缺陷就是这么被探针逮到的) |
|
|
262
|
+
|
|
263
|
+
四刀各自只动自己那一组,没有交叉:归因是干净的。
|
|
264
|
+
|
|
265
|
+
### 实现坑两条
|
|
266
|
+
|
|
267
|
+
1. **整条括号会让 cond 切不干净**:return 表达式常被写成 `=> (flag ? a : b)`,在第一
|
|
268
|
+
个顶层 `?` 处切开得到的前缀 `((true)` 括号不配对 ⇒ 判不出真假 ⇒ 折叠静默失效。
|
|
269
|
+
要先脱掉包住整串的括号(`stripOuterParens`)。
|
|
270
|
+
2. **代入时别给字面量加括号**:`(true)` 会让 `maskNonDependentArgsOnce` 把实参串误判
|
|
271
|
+
成嵌套调用(它的判据是「实参里还有 `(`」),于是内层 helper 永远轮不到被处理 ——
|
|
272
|
+
两跳那条(`wrapFlag(k, true)`)就卡在这儿。
|
|
273
|
+
|
|
3
274
|
## [3.7.43] — 2026-09-20
|
|
4
275
|
|
|
5
276
|
### C4g:helper 换个载体(对象字面量方法 / 类方法 / 解构形参)
|