diff --git a/boa_engine/src/bytecompiler/class.rs b/boa_engine/src/bytecompiler/class.rs index bd36e5d2ff..5ced760b7f 100644 --- a/boa_engine/src/bytecompiler/class.rs +++ b/boa_engine/src/bytecompiler/class.rs @@ -1,5 +1,5 @@ use super::{ByteCompiler, Literal, NodeKind}; -use crate::vm::{BindingOpcode, CodeBlock, Opcode}; +use crate::vm::{BindingOpcode, Opcode}; use boa_ast::{ declaration::Binding, expression::Identifier, @@ -9,7 +9,6 @@ use boa_ast::{ }; use boa_gc::Gc; use boa_interner::Sym; -use rustc_hash::FxHashMap; impl ByteCompiler<'_, '_> { /// This function compiles a class declaration or expression. @@ -20,22 +19,11 @@ impl ByteCompiler<'_, '_> { pub(crate) fn compile_class(&mut self, class: &Class, expression: bool) { let class_name = class.name().map_or(Sym::EMPTY_STRING, Identifier::sym); - let code = CodeBlock::new(class_name, 0, true); - let mut compiler = ByteCompiler { - code_block: code, - literals_map: FxHashMap::default(), - names_map: FxHashMap::default(), - private_names_map: FxHashMap::default(), - bindings_map: FxHashMap::default(), - jump_info: Vec::new(), - in_async_generator: false, - json_parse: self.json_parse, - context: self.context, - }; + let mut compiler = ByteCompiler::new(class_name, true, self.json_parse, self.context); if let Some(class_name) = class.name() { if class.has_binding_identifier() { - compiler.code_block.has_binding_identifier = true; + compiler.has_binding_identifier = true; compiler.context.push_compile_time_environment(false); compiler.context.create_immutable_binding(class_name, true); } @@ -44,12 +32,12 @@ impl ByteCompiler<'_, '_> { compiler.context.push_compile_time_environment(true); if let Some(expr) = class.constructor() { - compiler.code_block.length = expr.parameters().length(); - compiler.code_block.params = expr.parameters().clone(); + compiler.length = expr.parameters().length(); + compiler.params = expr.parameters().clone(); compiler .context .create_mutable_binding(Sym::ARGUMENTS.into(), false, false); - compiler.code_block.arguments_binding = Some( + compiler.arguments_binding = Some( compiler .context .initialize_mutable_binding(Sym::ARGUMENTS.into(), false), @@ -84,10 +72,9 @@ impl ByteCompiler<'_, '_> { compiler.emit_opcode(Opcode::RestParameterPop); } let env_label = if expr.parameters().has_expressions() { - compiler.code_block.num_bindings = compiler.context.get_binding_number(); + compiler.num_bindings = compiler.context.get_binding_number(); compiler.context.push_compile_time_environment(true); - compiler.code_block.function_environment_push_location = - compiler.next_opcode_location(); + compiler.function_environment_push_location = compiler.next_opcode_location(); Some(compiler.emit_opcode_with_two_operands(Opcode::PushFunctionEnvironment)) } else { None @@ -107,8 +94,8 @@ impl ByteCompiler<'_, '_> { let (num_bindings, compile_environment) = compiler.context.pop_compile_time_environment(); compiler.push_compile_environment(compile_environment); - compiler.code_block.num_bindings = num_bindings; - compiler.code_block.is_class_constructor = true; + compiler.num_bindings = num_bindings; + compiler.is_class_constructor = true; } } else { if class.super_ref().is_some() { @@ -117,8 +104,8 @@ impl ByteCompiler<'_, '_> { let (num_bindings, compile_environment) = compiler.context.pop_compile_time_environment(); compiler.push_compile_environment(compile_environment); - compiler.code_block.num_bindings = num_bindings; - compiler.code_block.is_class_constructor = true; + compiler.num_bindings = num_bindings; + compiler.is_class_constructor = true; } if class.name().is_some() && class.has_binding_identifier() { @@ -130,8 +117,8 @@ impl ByteCompiler<'_, '_> { compiler.emit_opcode(Opcode::Return); let code = Gc::new(compiler.finish()); - let index = self.code_block.functions.len() as u32; - self.code_block.functions.push(code); + let index = self.functions.len() as u32; + self.functions.push(code); self.emit(Opcode::GetFunction, &[index]); self.emit_u8(0); @@ -279,18 +266,8 @@ impl ByteCompiler<'_, '_> { self.compile_expr(name, true); } } - let field_code = CodeBlock::new(Sym::EMPTY_STRING, 0, true); - let mut field_compiler = ByteCompiler { - code_block: field_code, - literals_map: FxHashMap::default(), - names_map: FxHashMap::default(), - private_names_map: FxHashMap::default(), - bindings_map: FxHashMap::default(), - jump_info: Vec::new(), - in_async_generator: false, - json_parse: self.json_parse, - context: self.context, - }; + let mut field_compiler = + ByteCompiler::new(Sym::EMPTY_STRING, true, self.json_parse, self.context); field_compiler.context.push_compile_time_environment(false); field_compiler .context @@ -307,14 +284,14 @@ impl ByteCompiler<'_, '_> { let (_, compile_environment) = field_compiler.context.pop_compile_time_environment(); field_compiler.push_compile_environment(compile_environment); - field_compiler.code_block.num_bindings = num_bindings; + field_compiler.num_bindings = num_bindings; field_compiler.emit_opcode(Opcode::Return); let mut code = field_compiler.finish(); code.class_field_initializer_name = Some(Sym::EMPTY_STRING); let code = Gc::new(code); - let index = self.code_block.functions.len() as u32; - self.code_block.functions.push(code); + let index = self.functions.len() as u32; + self.functions.push(code); self.emit(Opcode::GetFunction, &[index]); self.emit_u8(0); self.emit_opcode(Opcode::PushClassField); @@ -322,18 +299,8 @@ impl ByteCompiler<'_, '_> { ClassElement::PrivateFieldDefinition(name, field) => { self.emit_opcode(Opcode::Dup); let name_index = self.get_or_insert_private_name(*name); - let field_code = CodeBlock::new(Sym::EMPTY_STRING, 0, true); - let mut field_compiler = ByteCompiler { - code_block: field_code, - literals_map: FxHashMap::default(), - names_map: FxHashMap::default(), - private_names_map: FxHashMap::default(), - bindings_map: FxHashMap::default(), - jump_info: Vec::new(), - in_async_generator: false, - json_parse: self.json_parse, - context: self.context, - }; + let mut field_compiler = + ByteCompiler::new(class_name, true, self.json_parse, self.context); field_compiler.context.push_compile_time_environment(false); field_compiler .context @@ -350,14 +317,14 @@ impl ByteCompiler<'_, '_> { let (_, compile_environment) = field_compiler.context.pop_compile_time_environment(); field_compiler.push_compile_environment(compile_environment); - field_compiler.code_block.num_bindings = num_bindings; + field_compiler.num_bindings = num_bindings; field_compiler.emit_opcode(Opcode::Return); let mut code = field_compiler.finish(); code.class_field_initializer_name = Some(Sym::EMPTY_STRING); let code = Gc::new(code); - let index = self.code_block.functions.len() as u32; - self.code_block.functions.push(code); + let index = self.functions.len() as u32; + self.functions.push(code); self.emit(Opcode::GetFunction, &[index]); self.emit_u8(0); self.emit(Opcode::PushClassFieldPrivate, &[name_index]); @@ -375,18 +342,8 @@ impl ByteCompiler<'_, '_> { None } }; - let field_code = CodeBlock::new(Sym::EMPTY_STRING, 0, true); - let mut field_compiler = ByteCompiler { - code_block: field_code, - literals_map: FxHashMap::default(), - names_map: FxHashMap::default(), - private_names_map: FxHashMap::default(), - bindings_map: FxHashMap::default(), - jump_info: Vec::new(), - in_async_generator: false, - json_parse: self.json_parse, - context: self.context, - }; + let mut field_compiler = + ByteCompiler::new(class_name, true, self.json_parse, self.context); field_compiler.context.push_compile_time_environment(false); field_compiler .context @@ -403,14 +360,14 @@ impl ByteCompiler<'_, '_> { let (_, compile_environment) = field_compiler.context.pop_compile_time_environment(); field_compiler.push_compile_environment(compile_environment); - field_compiler.code_block.num_bindings = num_bindings; + field_compiler.num_bindings = num_bindings; field_compiler.emit_opcode(Opcode::Return); let mut code = field_compiler.finish(); code.class_field_initializer_name = Some(Sym::EMPTY_STRING); let code = Gc::new(code); - let index = self.code_block.functions.len() as u32; - self.code_block.functions.push(code); + let index = self.functions.len() as u32; + self.functions.push(code); self.emit(Opcode::GetFunction, &[index]); self.emit_u8(0); self.emit_opcode(Opcode::SetHomeObject); @@ -447,11 +404,11 @@ impl ByteCompiler<'_, '_> { compiler.push_compile_environment(compile_environment); let (_, compile_environment) = compiler.context.pop_compile_time_environment(); compiler.push_compile_environment(compile_environment); - compiler.code_block.num_bindings = num_bindings; + compiler.num_bindings = num_bindings; let code = Gc::new(compiler.finish()); - let index = self.code_block.functions.len() as u32; - self.code_block.functions.push(code); + let index = self.functions.len() as u32; + self.functions.push(code); self.emit(Opcode::GetFunction, &[index]); self.emit_u8(0); self.emit_opcode(Opcode::SetHomeObject); diff --git a/boa_engine/src/bytecompiler/function.rs b/boa_engine/src/bytecompiler/function.rs index f91f4c37c7..c19226d5c0 100644 --- a/boa_engine/src/bytecompiler/function.rs +++ b/boa_engine/src/bytecompiler/function.rs @@ -9,7 +9,6 @@ use boa_ast::{ }; use boa_gc::Gc; use boa_interner::Sym; -use rustc_hash::FxHashMap; /// `FunctionCompiler` is used to compile AST functions to bytecode. #[derive(Debug, Clone, Copy)] @@ -95,24 +94,15 @@ impl FunctionCompiler { self.strict = self.strict || body.strict(); let length = parameters.length(); - let mut code = CodeBlock::new(self.name, length, self.strict); + + let mut compiler = ByteCompiler::new(self.name, self.strict, false, context); + compiler.length = length; + compiler.in_async_generator = self.generator && self.r#async; if self.arrow { - code.this_mode = ThisMode::Lexical; + compiler.this_mode = ThisMode::Lexical; } - let mut compiler = ByteCompiler { - code_block: code, - literals_map: FxHashMap::default(), - names_map: FxHashMap::default(), - private_names_map: FxHashMap::default(), - bindings_map: FxHashMap::default(), - jump_info: Vec::new(), - in_async_generator: self.generator && self.r#async, - json_parse: false, - context, - }; - if let Some(class_name) = self.class_name { compiler.context.push_compile_time_environment(false); compiler @@ -121,7 +111,7 @@ impl FunctionCompiler { } if let Some(binding_identifier) = self.binding_identifier { - compiler.code_block.has_binding_identifier = true; + compiler.has_binding_identifier = true; compiler.context.push_compile_time_environment(false); compiler .context @@ -139,7 +129,7 @@ impl FunctionCompiler { compiler .context .create_mutable_binding(Sym::ARGUMENTS.into(), false, false); - compiler.code_block.arguments_binding = Some( + compiler.arguments_binding = Some( compiler .context .initialize_mutable_binding(Sym::ARGUMENTS.into(), false), @@ -184,10 +174,9 @@ impl FunctionCompiler { } let env_label = if parameters.has_expressions() { - compiler.code_block.num_bindings = compiler.context.get_binding_number(); + compiler.num_bindings = compiler.context.get_binding_number(); compiler.context.push_compile_time_environment(true); - compiler.code_block.function_environment_push_location = - compiler.next_opcode_location(); + compiler.function_environment_push_location = compiler.next_opcode_location(); Some(compiler.emit_opcode_with_two_operands(Opcode::PushFunctionEnvironment)) } else { None @@ -215,7 +204,7 @@ impl FunctionCompiler { let (num_bindings, compile_environment) = compiler.context.pop_compile_time_environment(); compiler.push_compile_environment(compile_environment); - compiler.code_block.num_bindings = num_bindings; + compiler.num_bindings = num_bindings; } if self.binding_identifier.is_some() { @@ -228,7 +217,7 @@ impl FunctionCompiler { compiler.push_compile_environment(compile_environment); } - compiler.code_block.params = parameters.clone(); + compiler.params = parameters.clone(); // TODO These are redundant if a function returns so may need to check if a function returns and adding these if it doesn't compiler.emit(Opcode::PushUndefined, &[]); diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 98fbcfe207..b10232878c 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -9,6 +9,7 @@ mod module; mod statement; use crate::{ + builtins::function::ThisMode, environments::{BindingLocator, CompileTimeEnvironment}, vm::{BindingOpcode, CodeBlock, Opcode}, Context, JsBigInt, JsString, JsValue, @@ -210,8 +211,65 @@ impl Access<'_> { /// The [`ByteCompiler`] is used to compile ECMAScript AST from [`boa_ast`] to bytecode. #[derive(Debug)] +#[allow(clippy::struct_excessive_bools)] pub struct ByteCompiler<'b, 'host> { - code_block: CodeBlock, + /// Name of this function. + pub(crate) function_name: Sym, + + /// Indicates if the function is an expression and has a binding identifier. + pub(crate) has_binding_identifier: bool, + + /// The number of arguments expected. + pub(crate) length: u32, + + /// Is this function in strict mode. + pub(crate) strict: bool, + + /// \[\[ThisMode\]\] + pub(crate) this_mode: ThisMode, + + /// Parameters passed to this function. + pub(crate) params: FormalParameterList, + + /// Bytecode + pub(crate) bytecode: Vec, + + /// Literals + pub(crate) literals: Vec, + + /// Property field names. + pub(crate) names: Vec, + + /// Private names. + pub(crate) private_names: Vec, + + /// Locators for all bindings in the codeblock. + pub(crate) bindings: Vec, + + /// Number of binding for the function environment. + pub(crate) num_bindings: usize, + + /// Functions inside this function + pub(crate) functions: Vec>, + + /// The `arguments` binding location of the function, if set. + pub(crate) arguments_binding: Option, + + /// Compile time environments in this function. + pub(crate) compile_environments: Vec>>, + + /// The `[[IsClassConstructor]]` internal slot. + pub(crate) is_class_constructor: bool, + + /// The `[[ClassFieldInitializerName]]` internal slot. + pub(crate) class_field_initializer_name: Option, + + /// Marks the location in the code where the function environment in pushed. + /// This is only relevant for functions with expressions in the parameters. + /// We execute the parameter expressions in the function code and push the function environment afterward. + /// When the execution of the parameter expressions throws an error, we do not need to pop the function environment. + pub(crate) function_environment_push_location: u32, + literals_map: FxHashMap, names_map: FxHashMap, private_names_map: FxHashMap, @@ -226,7 +284,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { /// Represents a placeholder address that will be patched later. const DUMMY_ADDRESS: u32 = u32::MAX; - /// Creates a new `ByteCompiler`. + /// Creates a new [`ByteCompiler`]. #[inline] pub fn new( name: Sym, @@ -235,7 +293,25 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { context: &'b mut Context<'host>, ) -> ByteCompiler<'b, 'host> { Self { - code_block: CodeBlock::new(name, 0, strict), + function_name: name, + strict, + length: 0, + bytecode: Vec::default(), + literals: Vec::default(), + names: Vec::default(), + private_names: Vec::default(), + bindings: Vec::default(), + num_bindings: 0, + functions: Vec::default(), + has_binding_identifier: false, + this_mode: ThisMode::Global, + params: FormalParameterList::default(), + arguments_binding: None, + compile_environments: Vec::default(), + is_class_constructor: false, + class_field_initializer_name: None, + function_environment_push_location: 0, + literals_map: FxHashMap::default(), names_map: FxHashMap::default(), private_names_map: FxHashMap::default(), @@ -256,8 +332,8 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { &mut self, environment: Gc>, ) -> usize { - let index = self.code_block.compile_environments.len(); - self.code_block.compile_environments.push(environment); + let index = self.compile_environments.len(); + self.compile_environments.push(environment); index } @@ -271,8 +347,8 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { Literal::BigInt(value) => JsValue::new(value), }; - let index = self.code_block.literals.len() as u32; - self.code_block.literals.push(value); + let index = self.literals.len() as u32; + self.literals.push(value); self.literals_map.insert(literal, index); index } @@ -282,8 +358,8 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { return *index; } - let index = self.code_block.names.len() as u32; - self.code_block.names.push(name); + let index = self.names.len() as u32; + self.names.push(name); self.names_map.insert(name, index); index } @@ -294,8 +370,8 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { return *index; } - let index = self.code_block.private_names.len() as u32; - self.code_block.private_names.push(name); + let index = self.private_names.len() as u32; + self.private_names.push(name); self.private_names_map.insert(name, index); index } @@ -306,8 +382,8 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { return *index; } - let index = self.code_block.bindings.len() as u32; - self.code_block.bindings.push(binding); + let index = self.bindings.len() as u32; + self.bindings.push(binding); self.bindings_map.insert(binding, index); index } @@ -357,8 +433,8 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { } fn next_opcode_location(&mut self) -> u32 { - assert!(self.code_block.bytecode.len() < u32::MAX as usize); - self.code_block.bytecode.len() as u32 + assert!(self.bytecode.len() < u32::MAX as usize); + self.bytecode.len() as u32 } fn emit(&mut self, opcode: Opcode, operands: &[u32]) { @@ -369,15 +445,15 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { } fn emit_u64(&mut self, value: u64) { - self.code_block.bytecode.extend(value.to_ne_bytes()); + self.bytecode.extend(value.to_ne_bytes()); } fn emit_u32(&mut self, value: u32) { - self.code_block.bytecode.extend(value.to_ne_bytes()); + self.bytecode.extend(value.to_ne_bytes()); } fn emit_u16(&mut self, value: u16) { - self.code_block.bytecode.extend(value.to_ne_bytes()); + self.bytecode.extend(value.to_ne_bytes()); } fn emit_opcode(&mut self, opcode: Opcode) { @@ -385,7 +461,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { } fn emit_u8(&mut self, value: u8) { - self.code_block.bytecode.push(value); + self.bytecode.push(value); } fn emit_push_integer(&mut self, value: i32) { @@ -473,10 +549,10 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { let index = index as usize; let bytes = target.to_ne_bytes(); - self.code_block.bytecode[index + 1] = bytes[0]; - self.code_block.bytecode[index + 2] = bytes[1]; - self.code_block.bytecode[index + 3] = bytes[2]; - self.code_block.bytecode[index + 4] = bytes[3]; + self.bytecode[index + 1] = bytes[0]; + self.bytecode[index + 2] = bytes[1]; + self.bytecode[index + 3] = bytes[2]; + self.bytecode[index + 4] = bytes[3]; } fn patch_jump(&mut self, label: Label) { @@ -1042,13 +1118,13 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { .name(name.map(Identifier::sym)) .generator(generator) .r#async(r#async) - .strict(self.code_block.strict) + .strict(self.strict) .arrow(arrow) .binding_identifier(binding_identifier) .compile(parameters, body, self.context); - let index = self.code_block.functions.len() as u32; - self.code_block.functions.push(code); + let index = self.functions.len() as u32; + self.functions.push(code); if r#async && generator { self.emit(Opcode::GetGeneratorAsync, &[index]); @@ -1121,8 +1197,8 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { .class_name(class_name) .compile(parameters, body, self.context); - let index = self.code_block.functions.len() as u32; - self.code_block.functions.push(code); + let index = self.functions.len() as u32; + self.functions.push(code); if r#async && generator { self.emit(Opcode::GetGeneratorAsync, &[index]); @@ -1228,7 +1304,26 @@ impl<'b, 'host> ByteCompiler<'b, 'host> { #[must_use] #[allow(clippy::missing_const_for_fn)] pub fn finish(self) -> CodeBlock { - self.code_block + CodeBlock { + name: self.function_name, + has_binding_identifier: self.has_binding_identifier, + length: self.length, + strict: self.strict, + this_mode: self.this_mode, + params: self.params, + bytecode: self.bytecode.into_boxed_slice(), + literals: self.literals.into_boxed_slice(), + names: self.names.into_boxed_slice(), + private_names: self.private_names.into_boxed_slice(), + bindings: self.bindings.into_boxed_slice(), + num_bindings: self.num_bindings, + functions: self.functions.into_boxed_slice(), + arguments_binding: self.arguments_binding, + compile_environments: self.compile_environments.into_boxed_slice(), + is_class_constructor: self.is_class_constructor, + class_field_initializer_name: self.class_field_initializer_name, + function_environment_push_location: self.function_environment_push_location, + } } fn compile_declaration_pattern(&mut self, pattern: &Pattern, def: BindingOpcode) { diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index 117944eac2..347ab3976d 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -80,35 +80,35 @@ pub struct CodeBlock { pub(crate) params: FormalParameterList, /// Bytecode - pub(crate) bytecode: Vec, + pub(crate) bytecode: Box<[u8]>, /// Literals - pub(crate) literals: Vec, + pub(crate) literals: Box<[JsValue]>, /// Property field names. #[unsafe_ignore_trace] - pub(crate) names: Vec, + pub(crate) names: Box<[Identifier]>, /// Private names. #[unsafe_ignore_trace] - pub(crate) private_names: Vec, + pub(crate) private_names: Box<[PrivateName]>, /// Locators for all bindings in the codeblock. #[unsafe_ignore_trace] - pub(crate) bindings: Vec, + pub(crate) bindings: Box<[BindingLocator]>, /// Number of binding for the function environment. pub(crate) num_bindings: usize, /// Functions inside this function - pub(crate) functions: Vec>, + pub(crate) functions: Box<[Gc]>, /// The `arguments` binding location of the function, if set. #[unsafe_ignore_trace] pub(crate) arguments_binding: Option, /// Compile time environments in this function. - pub(crate) compile_environments: Vec>>, + pub(crate) compile_environments: Box<[Gc>]>, /// The `[[IsClassConstructor]]` internal slot. pub(crate) is_class_constructor: bool, @@ -129,13 +129,13 @@ impl CodeBlock { #[must_use] pub fn new(name: Sym, length: u32, strict: bool) -> Self { Self { - bytecode: Vec::new(), - literals: Vec::new(), - names: Vec::new(), - private_names: Vec::new(), - bindings: Vec::new(), + bytecode: Box::default(), + literals: Box::default(), + names: Box::default(), + private_names: Box::default(), + bindings: Box::default(), num_bindings: 0, - functions: Vec::new(), + functions: Box::default(), name, has_binding_identifier: false, length, @@ -143,7 +143,7 @@ impl CodeBlock { this_mode: ThisMode::Global, params: FormalParameterList::default(), arguments_binding: None, - compile_environments: Vec::new(), + compile_environments: Box::default(), is_class_constructor: false, class_field_initializer_name: None, function_environment_push_location: 0, diff --git a/boa_engine/src/vm/flowgraph/mod.rs b/boa_engine/src/vm/flowgraph/mod.rs index daf71db679..bb959017cb 100644 --- a/boa_engine/src/vm/flowgraph/mod.rs +++ b/boa_engine/src/vm/flowgraph/mod.rs @@ -550,7 +550,7 @@ impl CodeBlock { graph.add_node(pc, NodeShape::Diamond, "End".into(), Color::Red); - for function in &self.functions { + for function in self.functions.as_ref() { let subgraph = graph.subgraph(String::new()); function.to_graph(interner, subgraph); }