Skip to content

Commit 3d22954

Browse files
committed
fix: wrap function bodies with using declarations in try-finally for proper disposal on return
Signed-off-by: Abhinav Sharma <abhinavs1920bpl@gmail.com>
1 parent a438cee commit 3d22954

1 file changed

Lines changed: 107 additions & 1 deletion

File tree

core/engine/src/bytecompiler/function.rs

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,113 @@ impl FunctionCompiler {
219219

220220
{
221221
let mut compiler = compiler.position_guard(body);
222-
compiler.compile_statement_list(body.statement_list(), false, false);
222+
223+
// Check if the function body contains `using` declarations
224+
#[cfg(feature = "experimental")]
225+
let using_count: u32 = {
226+
use boa_ast::{
227+
declaration::LexicalDeclaration,
228+
operations::{LexicallyScopedDeclaration, lexically_scoped_declarations},
229+
};
230+
231+
lexically_scoped_declarations(body.statement_list())
232+
.iter()
233+
.filter_map(|decl| {
234+
if let LexicallyScopedDeclaration::LexicalDeclaration(
235+
LexicalDeclaration::Using(u) | LexicalDeclaration::AwaitUsing(u),
236+
) = decl
237+
{
238+
Some(u.as_ref().len() as u32)
239+
} else {
240+
None
241+
}
242+
})
243+
.sum()
244+
};
245+
246+
#[cfg(not(feature = "experimental"))]
247+
let using_count: u32 = 0;
248+
249+
if using_count > 0 {
250+
#[cfg(feature = "experimental")]
251+
{
252+
use crate::bytecompiler::jump_control::JumpControlInfoFlags;
253+
254+
// Function body with `using` declarations needs try-finally semantics
255+
let finally_re_throw = compiler.register_allocator.alloc();
256+
let finally_jump_index = compiler.register_allocator.alloc();
257+
258+
compiler.bytecode.emit_store_true(finally_re_throw.variable());
259+
compiler.bytecode.emit_store_zero(finally_jump_index.variable());
260+
261+
// Push jump control info to handle break/continue/return through disposal
262+
compiler.push_try_with_finally_control_info(
263+
&finally_re_throw,
264+
&finally_jump_index,
265+
false,
266+
);
267+
268+
// Push exception handler
269+
let handler = compiler.push_handler();
270+
271+
// Compile the function body
272+
compiler.compile_statement_list(body.statement_list(), false, false);
273+
274+
// Normal exit: mark that we don't need to re-throw
275+
compiler.bytecode.emit_store_false(finally_re_throw.variable());
276+
277+
let finally_jump = compiler.jump();
278+
279+
// Exception path: patch the handler
280+
compiler.patch_handler(handler);
281+
282+
// Push a second handler for exceptions during exception handling
283+
let catch_handler = compiler.push_handler();
284+
let error = compiler.register_allocator.alloc();
285+
compiler.bytecode.emit_exception(error.variable());
286+
compiler.bytecode.emit_store_true(finally_re_throw.variable());
287+
288+
let no_throw = compiler.jump();
289+
compiler.patch_handler(catch_handler);
290+
291+
compiler.patch_jump(no_throw);
292+
compiler.patch_jump(finally_jump);
293+
294+
// Finally block: dispose resources
295+
let finally_start = compiler.next_opcode_location();
296+
compiler.jump_info
297+
.last_mut()
298+
.expect("there should be a jump control info")
299+
.flags |= JumpControlInfoFlags::IN_FINALLY;
300+
301+
// Save accumulator
302+
let value = compiler.register_allocator.alloc();
303+
compiler.bytecode
304+
.emit_set_register_from_accumulator(value.variable());
305+
306+
// Emit disposal logic
307+
compiler.bytecode.emit_dispose_resources(using_count.into());
308+
309+
// Restore accumulator
310+
compiler.bytecode.emit_set_accumulator(value.variable());
311+
compiler.register_allocator.dealloc(value);
312+
313+
// Re-throw if there was an exception
314+
let do_not_throw_exit = compiler.jump_if_false(&finally_re_throw);
315+
compiler.bytecode.emit_throw(error.variable());
316+
compiler.register_allocator.dealloc(error);
317+
compiler.patch_jump(do_not_throw_exit);
318+
319+
// Pop jump control info (this handles break/continue/return via jump table)
320+
compiler.pop_try_with_finally_control_info(finally_start);
321+
322+
compiler.register_allocator.dealloc(finally_re_throw);
323+
compiler.register_allocator.dealloc(finally_jump_index);
324+
}
325+
} else {
326+
// Normal function body compilation (no using declarations)
327+
compiler.compile_statement_list(body.statement_list(), false, false);
328+
}
223329
}
224330

225331
compiler.params = parameters.clone();

0 commit comments

Comments
 (0)