From e7c8330bab0ebf477019ce92a1d50cca012469c2 Mon Sep 17 00:00:00 2001 From: Haled Odat <8566042+HalidOdat@users.noreply.github.com> Date: Mon, 26 Jun 2023 09:13:34 +0200 Subject: [PATCH] Refactor environment, exception handling and jumping in VM --- boa_engine/src/builtins/eval/mod.rs | 5 +- boa_engine/src/builtins/json/mod.rs | 5 +- .../declaration/declaration_pattern.rs | 48 ++- boa_engine/src/bytecompiler/env.rs | 2 + boa_engine/src/bytecompiler/jump_control.rs | 307 +++++++++++------- boa_engine/src/bytecompiler/mod.rs | 35 +- .../src/bytecompiler/statement/break.rs | 106 +++--- .../src/bytecompiler/statement/continue.rs | 147 +++------ .../src/bytecompiler/statement/labelled.rs | 9 +- boa_engine/src/bytecompiler/statement/loop.rs | 56 ++-- boa_engine/src/bytecompiler/statement/mod.rs | 39 +++ .../src/bytecompiler/statement/switch.rs | 5 - boa_engine/src/bytecompiler/statement/try.rs | 83 +++-- boa_engine/src/module/source.rs | 3 +- boa_engine/src/script.rs | 5 +- boa_engine/src/vm/call_frame/abrupt_record.rs | 89 ----- boa_engine/src/vm/call_frame/env_stack.rs | 201 ------------ boa_engine/src/vm/call_frame/mod.rs | 47 +-- boa_engine/src/vm/code_block.rs | 105 +++++- boa_engine/src/vm/flowgraph/mod.rs | 235 ++++++-------- boa_engine/src/vm/mod.rs | 24 +- boa_engine/src/vm/opcode/await_stm/mod.rs | 3 +- .../src/vm/opcode/control_flow/break.rs | 54 --- .../src/vm/opcode/control_flow/continue.rs | 67 ---- .../src/vm/opcode/control_flow/finally.rs | 148 --------- .../src/vm/opcode/control_flow/labelled.rs | 55 ---- boa_engine/src/vm/opcode/control_flow/mod.rs | 10 - .../src/vm/opcode/control_flow/return.rs | 36 +- .../src/vm/opcode/control_flow/throw.rs | 224 ++++--------- boa_engine/src/vm/opcode/control_flow/try.rs | 68 ---- .../src/vm/opcode/generator/yield_stm.rs | 5 +- .../src/vm/opcode/iteration/iterator.rs | 57 ++++ .../src/vm/opcode/iteration/loop_ops.rs | 105 +----- boa_engine/src/vm/opcode/jump/mod.rs | 37 ++- boa_engine/src/vm/opcode/mod.rs | 120 +++---- boa_engine/src/vm/opcode/pop/mod.rs | 1 - boa_engine/src/vm/opcode/push/environment.rs | 2 - 37 files changed, 953 insertions(+), 1595 deletions(-) delete mode 100644 boa_engine/src/vm/call_frame/abrupt_record.rs delete mode 100644 boa_engine/src/vm/call_frame/env_stack.rs delete mode 100644 boa_engine/src/vm/opcode/control_flow/break.rs delete mode 100644 boa_engine/src/vm/opcode/control_flow/continue.rs delete mode 100644 boa_engine/src/vm/opcode/control_flow/finally.rs delete mode 100644 boa_engine/src/vm/opcode/control_flow/labelled.rs delete mode 100644 boa_engine/src/vm/opcode/control_flow/try.rs 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