Skip to content

Commit 57a48c3

Browse files
committed
[JSC] Nested using blocks lose outer disposals
https://bugs.webkit.org/show_bug.cgi?id=310116 Reviewed by Yusuke Suzuki. emitUsingBodyScope holds a reference into m_usingScopeStack across the emitBody call that may recursively append. Once nesting exceeds the initial Vector capacity, the append reallocates and the stale reference reads a moved-from UsingScope whose slots vector is empty, so the finally emits no dispose calls for those outer blocks. ASAN catches the freed read directly. Test: JSTests/stress/nested-using-blocks.js * JSTests/stress/nested-using-blocks.js: Added. (shouldBe): (eval.string_appeared_here.string_appeared_here.repeat.depth.1.string_appeared_here.string_appeared_here.repeat): (shouldBe.async then): * Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h: Canonical link: https://commits.webkit.org/309457@main
1 parent 4971587 commit 57a48c3

2 files changed

Lines changed: 63 additions & 1 deletion

File tree

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//@ requireOptions("--useExplicitResourceManagement=1")
2+
3+
function shouldBe(actual, expected) {
4+
if (actual !== expected)
5+
throw new Error(`Expected ${expected} but got ${actual}`);
6+
}
7+
8+
for (let depth of [5, 8, 9, 16, 17, 20, 32, 64, 128]) {
9+
eval(
10+
"{using a=null;" +
11+
"{using a=null;".repeat(depth - 1) +
12+
"0;" +
13+
"}".repeat(depth)
14+
);
15+
}
16+
17+
{
18+
let n = 0;
19+
eval(
20+
"{using a={[Symbol.dispose](){n++}};" +
21+
"{using a={[Symbol.dispose](){n++}};".repeat(19) +
22+
"0;" +
23+
"}".repeat(20)
24+
);
25+
shouldBe(n, 20);
26+
}
27+
28+
{
29+
let order = [];
30+
eval(
31+
"{using a={[Symbol.dispose](){order.push(0)}};" +
32+
Array.from({length: 15}, (_, i) =>
33+
`{using a={[Symbol.dispose](){order.push(${i + 1})}};`
34+
).join("") +
35+
"0;" +
36+
"}".repeat(16)
37+
);
38+
shouldBe(order.join(","), "15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0");
39+
}
40+
41+
for (let depth = 1; depth <= 20; depth++) {
42+
let n = 0;
43+
let code = "";
44+
for (let i = 0; i < depth; i++)
45+
code += `{using a${i}={[Symbol.dispose](){n++}};using b${i}={[Symbol.dispose](){n++}};`;
46+
code += "0;" + "}".repeat(depth);
47+
eval(code);
48+
shouldBe(n, depth * 2);
49+
}
50+
51+
(async () => {
52+
for (let depth of [8, 9, 16, 20]) {
53+
let n = 0;
54+
let code = "(async()=>{";
55+
for (let i = 0; i < depth; i++)
56+
code += `{await using a${i}={[Symbol.asyncDispose](){n++}};`;
57+
code += "0;" + "}".repeat(depth) + "})()";
58+
await eval(code);
59+
shouldBe(n, depth);
60+
}
61+
})().then(() => {}, e => { print(e); throw e; });
62+
drainMicrotasks();

Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1375,7 +1375,7 @@ namespace JSC {
13751375
Vector<SwitchInfo> m_switchContextStack;
13761376
Vector<Ref<ForInContext>> m_forInContextStack;
13771377
Vector<TryContext> m_tryContextStack;
1378-
Vector<UsingScope> m_usingScopeStack;
1378+
SegmentedVector<UsingScope, 8> m_usingScopeStack;
13791379
unsigned m_yieldPoints { 0 };
13801380
bool m_needsGeneratorification { false };
13811381

0 commit comments

Comments
 (0)