Browse Source

Decouple bytecompiler from CodeBlock (#2669)

Hopefully this is a PR in a series of PRs to implement a bytecode optimizer, before that can happen there needs to be a lot of refactoring in the way we store and compile it.

This also give us some memory benefits, it reduces `CodeBlock` size from `264` **=>** `208` (removes `56` bytes).

Additionally when calling `into_boxed_slice`, If the vector has excess capacity, its items will be moved into a newly-allocated buffer with exactly the right capacity removing wasted space.
pull/2678/head
Haled Odat 2 years ago
parent
commit
da866cae64
  1. 107
      boa_engine/src/bytecompiler/class.rs
  2. 35
      boa_engine/src/bytecompiler/function.rs
  3. 153
      boa_engine/src/bytecompiler/mod.rs
  4. 28
      boa_engine/src/vm/code_block.rs
  5. 2
      boa_engine/src/vm/flowgraph/mod.rs

107
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);

35
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,23 +94,14 @@ 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;
}
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,
};
compiler.this_mode = ThisMode::Lexical;
}
if let Some(class_name) = self.class_name {
compiler.context.push_compile_time_environment(false);
@ -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, &[]);

153
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<u8>,
/// Literals
pub(crate) literals: Vec<JsValue>,
/// Property field names.
pub(crate) names: Vec<Identifier>,
/// Private names.
pub(crate) private_names: Vec<PrivateName>,
/// Locators for all bindings in the codeblock.
pub(crate) bindings: Vec<BindingLocator>,
/// Number of binding for the function environment.
pub(crate) num_bindings: usize,
/// Functions inside this function
pub(crate) functions: Vec<Gc<CodeBlock>>,
/// The `arguments` binding location of the function, if set.
pub(crate) arguments_binding: Option<BindingLocator>,
/// Compile time environments in this function.
pub(crate) compile_environments: Vec<Gc<GcRefCell<CompileTimeEnvironment>>>,
/// The `[[IsClassConstructor]]` internal slot.
pub(crate) is_class_constructor: bool,
/// The `[[ClassFieldInitializerName]]` internal slot.
pub(crate) class_field_initializer_name: Option<Sym>,
/// 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<Literal, u32>,
names_map: FxHashMap<Identifier, u32>,
private_names_map: FxHashMap<PrivateName, u32>,
@ -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<GcRefCell<CompileTimeEnvironment>>,
) -> 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) {

28
boa_engine/src/vm/code_block.rs

@ -80,35 +80,35 @@ pub struct CodeBlock {
pub(crate) params: FormalParameterList,
/// Bytecode
pub(crate) bytecode: Vec<u8>,
pub(crate) bytecode: Box<[u8]>,
/// Literals
pub(crate) literals: Vec<JsValue>,
pub(crate) literals: Box<[JsValue]>,
/// Property field names.
#[unsafe_ignore_trace]
pub(crate) names: Vec<Identifier>,
pub(crate) names: Box<[Identifier]>,
/// Private names.
#[unsafe_ignore_trace]
pub(crate) private_names: Vec<PrivateName>,
pub(crate) private_names: Box<[PrivateName]>,
/// Locators for all bindings in the codeblock.
#[unsafe_ignore_trace]
pub(crate) bindings: Vec<BindingLocator>,
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<Gc<Self>>,
pub(crate) functions: Box<[Gc<Self>]>,
/// The `arguments` binding location of the function, if set.
#[unsafe_ignore_trace]
pub(crate) arguments_binding: Option<BindingLocator>,
/// Compile time environments in this function.
pub(crate) compile_environments: Vec<Gc<GcRefCell<CompileTimeEnvironment>>>,
pub(crate) compile_environments: Box<[Gc<GcRefCell<CompileTimeEnvironment>>]>,
/// 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,

2
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);
}

Loading…
Cancel
Save