diff --git a/boa_engine/src/builtins/eval/mod.rs b/boa_engine/src/builtins/eval/mod.rs index 9d7c4946fb0..ab3e25b1b8d 100644 --- a/boa_engine/src/builtins/eval/mod.rs +++ b/boa_engine/src/builtins/eval/mod.rs @@ -253,7 +253,10 @@ impl Eval { context.vm.environments.extend_outer_function_environment(); } - context.vm.push_frame(CallFrame::new(code_block)); + let env_fp = context.vm.environments.len() as u32; + context + .vm + .push_frame(CallFrame::new(code_block).with_env_fp(env_fp)); context.realm().resize_global_env(); let record = context.run(); context.vm.pop_frame(); diff --git a/boa_engine/src/builtins/json/mod.rs b/boa_engine/src/builtins/json/mod.rs index 20b80fef102..67fd9a41bce 100644 --- a/boa_engine/src/builtins/json/mod.rs +++ b/boa_engine/src/builtins/json/mod.rs @@ -128,7 +128,10 @@ impl Json { Gc::new(compiler.finish()) }; - context.vm.push_frame(CallFrame::new(code_block)); + let env_fp = context.vm.environments.len() as u32; + context + .vm + .push_frame(CallFrame::new(code_block).with_env_fp(env_fp)); context.realm().resize_global_env(); let record = context.run(); context.vm.pop_frame(); diff --git a/boa_engine/src/bytecompiler/declaration/declaration_pattern.rs b/boa_engine/src/bytecompiler/declaration/declaration_pattern.rs index 9801c63bd3d..98aa84b7fbf 100644 --- a/boa_engine/src/bytecompiler/declaration/declaration_pattern.rs +++ b/boa_engine/src/bytecompiler/declaration/declaration_pattern.rs @@ -180,11 +180,45 @@ impl ByteCompiler<'_, '_> { self.emit_opcode(Opcode::ValueNotNullOrUndefined); self.emit_opcode(Opcode::GetIterator); + // TODO: maybe, refactor this to be more condensed. + let handler_index = self.push_handler(); for element in pattern.bindings() { self.compile_array_pattern_element(element, def); } + let handler_end_address = self.next_opcode_location(); + self.emit_opcode(Opcode::PushFalse); + + let exit = self.jump(); + let handler_address = self.next_opcode_location(); + self.emit_opcode(Opcode::Exception); + self.emit_opcode(Opcode::PushTrue); + self.patch_jump(exit); + + self.handlers[handler_index as usize].handler = handler_address; + self.handlers[handler_index as usize].range.end = handler_end_address; + + let iterator_close_handler = self.push_handler(); self.iterator_close(false); + + let exit = self.jump(); + let iterator_close_handler_address = self.next_opcode_location(); + { + let jump = self.jump_if_false(); + self.emit_opcode(Opcode::Throw); + self.patch_jump(jump); + } + self.emit_opcode(Opcode::ReThrow); + self.patch_jump(exit); + + let jump = self.jump_if_false(); + self.emit_opcode(Opcode::Throw); + self.patch_jump(jump); + + self.handlers[iterator_close_handler as usize].handler = + iterator_close_handler_address; + self.handlers[iterator_close_handler as usize].range.end = + iterator_close_handler_address; } } } @@ -198,15 +232,15 @@ impl ByteCompiler<'_, '_> { match element { // ArrayBindingPattern : [ Elision ] Elision => { - self.emit_opcode(Opcode::IteratorNext); + self.emit_opcode(Opcode::IteratorNextWithoutPop); } // SingleNameBinding : BindingIdentifier Initializer[opt] SingleName { ident, default_init, } => { - self.emit_opcode(Opcode::IteratorNext); - self.emit_opcode(Opcode::IteratorValue); + self.emit_opcode(Opcode::IteratorNextWithoutPop); + self.emit_opcode(Opcode::IteratorValueWithoutPop); if let Some(init) = default_init { let skip = self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); self.compile_expr(init, true); @@ -216,8 +250,8 @@ impl ByteCompiler<'_, '_> { } PropertyAccess { access } => { self.access_set(Access::Property { access }, false, |compiler, _level| { - compiler.emit_opcode(Opcode::IteratorNext); - compiler.emit_opcode(Opcode::IteratorValue); + compiler.emit_opcode(Opcode::IteratorNextWithoutPop); + compiler.emit_opcode(Opcode::IteratorValueWithoutPop); }); } // BindingElement : BindingPattern Initializer[opt] @@ -225,8 +259,8 @@ impl ByteCompiler<'_, '_> { pattern, default_init, } => { - self.emit_opcode(Opcode::IteratorNext); - self.emit_opcode(Opcode::IteratorValue); + self.emit_opcode(Opcode::IteratorNextWithoutPop); + self.emit_opcode(Opcode::IteratorValueWithoutPop); if let Some(init) = default_init { let skip = self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); diff --git a/boa_engine/src/bytecompiler/env.rs b/boa_engine/src/bytecompiler/env.rs index fb3be0b9e75..c817b5b7944 100644 --- a/boa_engine/src/bytecompiler/env.rs +++ b/boa_engine/src/bytecompiler/env.rs @@ -7,6 +7,7 @@ use boa_ast::expression::Identifier; impl ByteCompiler<'_, '_> { /// Push either a new declarative or function environment on the compile time environment stack. pub(crate) fn push_compile_environment(&mut self, function_scope: bool) { + self.current_open_environments_count += 1; self.current_environment = Rc::new(CompileTimeEnvironment::new( self.current_environment.clone(), function_scope, @@ -16,6 +17,7 @@ impl ByteCompiler<'_, '_> { /// Pops the top compile time environment and returns its index in the compile time environments array. #[track_caller] pub(crate) fn pop_compile_environment(&mut self) -> u32 { + self.current_open_environments_count -= 1; let index = self.compile_environments.len() as u32; self.compile_environments .push(self.current_environment.clone()); diff --git a/boa_engine/src/bytecompiler/jump_control.rs b/boa_engine/src/bytecompiler/jump_control.rs index 0c86fbd0ba4..a200338cbe0 100644 --- a/boa_engine/src/bytecompiler/jump_control.rs +++ b/boa_engine/src/bytecompiler/jump_control.rs @@ -9,19 +9,103 @@ //! [try spec]: https://tc39.es/ecma262/#sec-try-statement //! [labelled spec]: https://tc39.es/ecma262/#sec-labelled-statements -use crate::bytecompiler::{ByteCompiler, Label}; +use crate::{ + bytecompiler::{ByteCompiler, Label}, + vm::{Handler, Opcode}, +}; use bitflags::bitflags; use boa_interner::Sym; +#[derive(Debug, Clone, Copy)] +pub(crate) enum JumpRecordAction { + CreateJump, + Transfter { index: u32 }, + PopEnvironments { count: u32 }, + HandleFinally { value: i32 }, + CloseIterator { r#async: bool }, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum JumpRecordKind { + Break, + Continue, + Return, +} + +#[derive(Debug, Clone)] +pub(crate) struct JumpRecord { + kind: JumpRecordKind, + label: Label, + actions: Vec, +} + +impl JumpRecord { + pub(crate) const fn new( + kind: JumpRecordKind, + label: Label, + actions: Vec, + ) -> Self { + Self { + kind, + label, + actions, + } + } + + pub(crate) fn perform_actions( + mut self, + start_address: u32, + compiler: &mut ByteCompiler<'_, '_>, + ) { + while let Some(action) = self.actions.pop() { + match action { + JumpRecordAction::Transfter { index } => { + compiler.jump_info[index as usize].jumps.push(self); + return; + } + JumpRecordAction::PopEnvironments { count } => { + for _ in 0..count { + compiler.emit_opcode(Opcode::PopEnvironment); + } + } + JumpRecordAction::HandleFinally { value } => { + compiler.emit_push_integer(value); + compiler.emit_opcode(Opcode::PushFalse); + } + JumpRecordAction::CreateJump => { + self.label = compiler.jump(); + } + JumpRecordAction::CloseIterator { r#async } => { + compiler.iterator_close(r#async); + } + } + } + + match self.kind { + JumpRecordKind::Break => compiler.patch_jump(self.label), + JumpRecordKind::Continue => compiler.patch_jump_with_target(self.label, start_address), + JumpRecordKind::Return if start_address != ByteCompiler::DUMMY_ADDRESS => { + compiler.returns_to_patch.push(Label { + index: start_address, + }); + } + JumpRecordKind::Return => { + compiler.emit_opcode(Opcode::Return); + } + } + } +} + /// Boa's `ByteCompiler` jump information tracking struct. #[derive(Debug, Clone)] pub(crate) struct JumpControlInfo { label: Option, start_address: u32, - flags: JumpControlInfoFlags, - set_jumps: Vec