Browse Source

Remove unneded `num_bindings` in `Opcode`s and `CodeBlock` (#2967)

* Remove redundant `num_bindings` field from `CodeBlock`

* Remove redundant num_bindings parameter from push_function_inherits

* Remove redundant num_bindings operand from environment opcodes

* Make pop_compile_environment() return an index

* Move boolean `CodeBlock` flags to bitflags

* Fix ci

* Add doc
pull/2970/head
Haled Odat 1 year ago committed by GitHub
parent
commit
67c5652911
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      boa_cli/src/debug/function.rs
  2. 9
      boa_engine/src/builtins/eval/mod.rs
  3. 42
      boa_engine/src/bytecompiler/class.rs
  4. 14
      boa_engine/src/bytecompiler/declarations.rs
  5. 25
      boa_engine/src/bytecompiler/env.rs
  6. 19
      boa_engine/src/bytecompiler/function.rs
  7. 53
      boa_engine/src/bytecompiler/mod.rs
  8. 7
      boa_engine/src/bytecompiler/statement/block.rs
  9. 43
      boa_engine/src/bytecompiler/statement/loop.rs
  10. 7
      boa_engine/src/bytecompiler/statement/switch.rs
  11. 7
      boa_engine/src/bytecompiler/statement/try.rs
  12. 9
      boa_engine/src/environments/runtime/mod.rs
  13. 146
      boa_engine/src/vm/code_block.rs
  14. 1
      boa_engine/src/vm/flowgraph/mod.rs
  15. 3
      boa_engine/src/vm/mod.rs
  16. 4
      boa_engine/src/vm/opcode/call/mod.rs
  17. 6
      boa_engine/src/vm/opcode/define/mod.rs
  18. 4
      boa_engine/src/vm/opcode/delete/mod.rs
  19. 4
      boa_engine/src/vm/opcode/mod.rs
  20. 9
      boa_engine/src/vm/opcode/push/environment.rs
  21. 14
      boa_engine/src/vm/opcode/set/name.rs
  22. 6
      boa_engine/src/vm/opcode/set/property.rs

2
boa_cli/src/debug/function.rs

@ -135,7 +135,7 @@ fn set_trace_flag_in_function_object(object: &JsObject, value: bool) -> JsResult
let code = function.codeblock().ok_or_else(|| {
JsNativeError::typ().with_message("native functions do not have bytecode")
})?;
code.set_trace(value);
code.set_traceable(value);
Ok(())
}

9
boa_engine/src/builtins/eval/mod.rs

@ -232,14 +232,15 @@ impl Eval {
);
compiler.push_compile_environment(strict);
let push_env = compiler.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
let push_env = compiler.emit_opcode_with_operand(Opcode::PushDeclarativeEnvironment);
compiler.eval_declaration_instantiation(&body, strict)?;
compiler.compile_statement_list(body.statements(), true, false);
let env_info = compiler.pop_compile_environment();
compiler.patch_jump_with_target(push_env.0, env_info.num_bindings);
compiler.patch_jump_with_target(push_env.1, env_info.index);
let env_index = compiler.pop_compile_environment();
compiler.patch_jump_with_target(push_env, env_index);
compiler.emit_opcode(Opcode::PopEnvironment);
let code_block = Gc::new(compiler.finish());

42
boa_engine/src/bytecompiler/class.rs

@ -1,5 +1,5 @@
use super::{ByteCompiler, Literal};
use crate::vm::{BindingOpcode, Opcode};
use crate::vm::{BindingOpcode, CodeBlockFlags, Opcode};
use boa_ast::{
expression::Identifier,
function::{Class, ClassElement, FormalParameterList},
@ -27,7 +27,7 @@ impl ByteCompiler<'_, '_> {
if let Some(class_name) = class.name() {
if class.has_binding_identifier() {
compiler.has_binding_identifier = true;
compiler.code_block_flags |= CodeBlockFlags::HAS_BINDING_IDENTIFIER;
compiler.push_compile_environment(false);
compiler.create_immutable_binding(class_name, true);
}
@ -39,7 +39,7 @@ impl ByteCompiler<'_, '_> {
compiler.length = expr.parameters().length();
compiler.params = expr.parameters().clone();
let (env_labels, _) = compiler.function_declaration_instantiation(
let (env_label, _) = compiler.function_declaration_instantiation(
expr.body(),
expr.parameters(),
false,
@ -49,23 +49,20 @@ impl ByteCompiler<'_, '_> {
compiler.compile_statement_list(expr.body().statements(), false, false);
let env_info = compiler.pop_compile_environment();
let env_index = compiler.pop_compile_environment();
if let Some(env_labels) = env_labels {
compiler.patch_jump_with_target(env_labels.0, env_info.num_bindings);
compiler.patch_jump_with_target(env_labels.1, env_info.index);
if let Some(env_label) = env_label {
compiler.patch_jump_with_target(env_label, env_index);
compiler.pop_compile_environment();
} else {
compiler.num_bindings = env_info.num_bindings;
compiler.is_class_constructor = true;
compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR;
}
} else {
if class.super_ref().is_some() {
compiler.emit_opcode(Opcode::SuperCallDerived);
}
let env_info = compiler.pop_compile_environment();
compiler.num_bindings = env_info.num_bindings;
compiler.is_class_constructor = true;
compiler.pop_compile_environment();
compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR;
}
if class.name().is_some() && class.has_binding_identifier() {
@ -81,11 +78,11 @@ impl ByteCompiler<'_, '_> {
self.emit(Opcode::GetFunction, &[index]);
self.emit_u8(0);
let class_env: Option<(super::Label, super::Label)> = match class.name() {
let class_env: Option<super::Label> = match class.name() {
Some(name) if class.has_binding_identifier() => {
self.push_compile_environment(false);
self.create_immutable_binding(name, true);
Some(self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment))
Some(self.emit_opcode_with_operand(Opcode::PushDeclarativeEnvironment))
}
_ => None,
};
@ -266,9 +263,8 @@ impl ByteCompiler<'_, '_> {
} else {
field_compiler.emit_opcode(Opcode::PushUndefined);
}
let env_info = field_compiler.pop_compile_environment();
field_compiler.pop_compile_environment();
field_compiler.num_bindings = env_info.num_bindings;
field_compiler.pop_compile_environment();
field_compiler.emit_opcode(Opcode::Return);
let mut code = field_compiler.finish();
@ -298,9 +294,8 @@ impl ByteCompiler<'_, '_> {
} else {
field_compiler.emit_opcode(Opcode::PushUndefined);
}
let env_info = field_compiler.pop_compile_environment();
field_compiler.pop_compile_environment();
field_compiler.num_bindings = env_info.num_bindings;
field_compiler.pop_compile_environment();
field_compiler.emit_opcode(Opcode::Return);
let mut code = field_compiler.finish();
@ -340,9 +335,8 @@ impl ByteCompiler<'_, '_> {
} else {
field_compiler.emit_opcode(Opcode::PushUndefined);
}
let env_info = field_compiler.pop_compile_environment();
field_compiler.pop_compile_environment();
field_compiler.num_bindings = env_info.num_bindings;
field_compiler.pop_compile_environment();
field_compiler.emit_opcode(Opcode::Return);
let mut code = field_compiler.finish();
@ -392,9 +386,8 @@ impl ByteCompiler<'_, '_> {
);
compiler.compile_statement_list(body.statements(), false, false);
let env_info = compiler.pop_compile_environment();
compiler.pop_compile_environment();
compiler.num_bindings = env_info.num_bindings;
compiler.pop_compile_environment();
let code = Gc::new(compiler.finish());
let index = self.functions.len() as u32;
@ -547,9 +540,8 @@ impl ByteCompiler<'_, '_> {
self.emit_opcode(Opcode::Pop);
if let Some(class_env) = class_env {
let env_info = self.pop_compile_environment();
self.patch_jump_with_target(class_env.0, env_info.num_bindings);
self.patch_jump_with_target(class_env.1, env_info.index);
let env_index = self.pop_compile_environment();
self.patch_jump_with_target(class_env, env_index);
self.emit_opcode(Opcode::PopEnvironment);
}

14
boa_engine/src/bytecompiler/declarations.rs

@ -260,7 +260,7 @@ impl ByteCompiler<'_, '_> {
.name(name.sym())
.generator(generator)
.r#async(r#async)
.strict(self.strict)
.strict(self.strict())
.binding_identifier(Some(name.sym()))
.compile(
parameters,
@ -672,7 +672,7 @@ impl ByteCompiler<'_, '_> {
.name(name.sym())
.generator(generator)
.r#async(r#async)
.strict(self.strict)
.strict(self.strict())
.binding_identifier(Some(name.sym()))
.compile(
parameters,
@ -777,8 +777,8 @@ impl ByteCompiler<'_, '_> {
arrow: bool,
strict: bool,
generator: bool,
) -> (Option<(Label, Label)>, bool) {
let mut env_labels = None;
) -> (Option<Label>, bool) {
let mut env_label = None;
let mut additional_env = false;
// 1. Let calleeContext be the running execution context.
@ -987,8 +987,7 @@ impl ByteCompiler<'_, '_> {
// b. Let varEnv be NewDeclarativeEnvironment(env).
// c. Set the VariableEnvironment of calleeContext to varEnv.
self.push_compile_environment(true);
self.function_environment_push_location = self.next_opcode_location();
env_labels = Some(self.emit_opcode_with_two_operands(Opcode::PushFunctionEnvironment));
env_label = Some(self.emit_opcode_with_operand(Opcode::PushFunctionEnvironment));
// d. Let instantiatedVarNames be a new empty List.
let mut instantiated_var_names = Vec::new();
@ -1052,7 +1051,6 @@ impl ByteCompiler<'_, '_> {
}
// d. Let varEnv be env.
instantiated_var_names
};
@ -1151,6 +1149,6 @@ impl ByteCompiler<'_, '_> {
}
// 37. Return unused.
(env_labels, additional_env)
(env_label, additional_env)
}
}

25
boa_engine/src/bytecompiler/env.rs

@ -3,15 +3,6 @@ use crate::environments::{BindingLocator, CompileTimeEnvironment};
use boa_ast::expression::Identifier;
use boa_gc::{Gc, GcRefCell};
/// Info returned by the [`ByteCompiler::pop_compile_environment`] method.
#[derive(Debug, Clone, Copy)]
pub(crate) struct PopEnvironmentInfo {
/// Number of bindings declared.
pub(crate) num_bindings: u32,
/// Index in the compile time envs array.
pub(crate) index: u32,
}
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) {
@ -21,26 +12,20 @@ impl ByteCompiler<'_, '_> {
)));
}
/// Pops the top compile time environment and returns its index and number of bindings.
/// 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) -> PopEnvironmentInfo {
pub(crate) fn pop_compile_environment(&mut self) -> u32 {
let index = self.compile_environments.len() as u32;
self.compile_environments
.push(self.current_environment.clone());
let (num_bindings, outer) = {
let outer = {
let env = self.current_environment.borrow();
(
env.num_bindings(),
env.outer().expect("cannot pop the global environment"),
)
env.outer().expect("cannot pop the global environment")
};
self.current_environment = outer;
PopEnvironmentInfo {
num_bindings,
index,
}
index
}
/// Get the binding locator of the binding at bytecode compile time.

19
boa_engine/src/bytecompiler/function.rs

@ -2,7 +2,7 @@ use crate::{
builtins::function::ThisMode,
bytecompiler::ByteCompiler,
environments::CompileTimeEnvironment,
vm::{CodeBlock, Opcode},
vm::{CodeBlock, CodeBlockFlags, Opcode},
Context,
};
use boa_ast::function::{FormalParameterList, FunctionBody};
@ -109,7 +109,7 @@ impl FunctionCompiler {
}
if let Some(binding_identifier) = self.binding_identifier {
compiler.has_binding_identifier = true;
compiler.code_block_flags |= CodeBlockFlags::HAS_BINDING_IDENTIFIER;
compiler.push_compile_environment(false);
compiler.create_immutable_binding(binding_identifier.into(), self.strict);
}
@ -117,7 +117,7 @@ impl FunctionCompiler {
// Function environment
compiler.push_compile_environment(true);
let (env_labels, additional_env) = compiler.function_declaration_instantiation(
let (env_label, additional_env) = compiler.function_declaration_instantiation(
body,
parameters,
self.arrow,
@ -127,18 +127,17 @@ impl FunctionCompiler {
compiler.compile_statement_list(body.statements(), false, false);
if let Some(env_labels) = env_labels {
let env_info = compiler.pop_compile_environment();
compiler.patch_jump_with_target(env_labels.0, env_info.num_bindings);
compiler.patch_jump_with_target(env_labels.1, env_info.index);
if let Some(env_labels) = env_label {
let env_index = compiler.pop_compile_environment();
compiler.patch_jump_with_target(env_labels, env_index);
}
if additional_env {
compiler.parameters_env_bindings =
Some(compiler.pop_compile_environment().num_bindings);
compiler.pop_compile_environment();
compiler.code_block_flags |= CodeBlockFlags::PARAMETERS_ENV_BINDINGS;
}
compiler.num_bindings = compiler.pop_compile_environment().num_bindings;
compiler.pop_compile_environment();
if self.binding_identifier.is_some() {
compiler.pop_compile_environment();

53
boa_engine/src/bytecompiler/mod.rs

@ -11,11 +11,13 @@ mod module;
mod statement;
mod utils;
use std::cell::Cell;
use crate::{
builtins::function::ThisMode,
environments::{BindingLocator, CompileTimeEnvironment},
js_string,
vm::{BindingOpcode, CodeBlock, Opcode},
vm::{BindingOpcode, CodeBlock, CodeBlockFlags, Opcode},
Context, JsBigInt, JsString, JsValue,
};
use boa_ast::{
@ -213,15 +215,9 @@ pub struct ByteCompiler<'ctx, 'host> {
/// 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,
@ -243,9 +239,6 @@ pub struct ByteCompiler<'ctx, 'host> {
/// Locators for all bindings in the codeblock.
pub(crate) bindings: Vec<BindingLocator>,
/// Number of binding for the function environment.
pub(crate) num_bindings: u32,
/// Functions inside this function
pub(crate) functions: Vec<Gc<CodeBlock>>,
@ -255,23 +248,13 @@ pub struct ByteCompiler<'ctx, 'host> {
/// 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,
/// The environment that is currently active.
pub(crate) current_environment: Gc<GcRefCell<CompileTimeEnvironment>>,
/// The number of bindings in the parameters environment.
pub(crate) parameters_env_bindings: Option<u32>,
pub(crate) code_block_flags: CodeBlockFlags,
literals_map: FxHashMap<Literal, u32>,
names_map: FxHashMap<Identifier, u32>,
@ -303,26 +286,23 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
// TODO: remove when we separate scripts from the context
context: &'ctx mut Context<'host>,
) -> ByteCompiler<'ctx, 'host> {
let mut code_block_flags = CodeBlockFlags::empty();
code_block_flags.set(CodeBlockFlags::STRICT, strict);
Self {
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,
parameters_env_bindings: None,
code_block_flags,
literals_map: FxHashMap::default(),
names_map: FxHashMap::default(),
@ -339,6 +319,10 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
}
}
pub(crate) const fn strict(&self) -> bool {
self.code_block_flags.contains(CodeBlockFlags::STRICT)
}
pub(crate) fn interner(&self) -> &Interner {
self.context.interner()
}
@ -525,7 +509,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
/// Emit an opcode with a dummy operand.
/// Return the `Label` of the operand.
fn emit_opcode_with_operand(&mut self, opcode: Opcode) -> Label {
pub(crate) fn emit_opcode_with_operand(&mut self, opcode: Opcode) -> Label {
let index = self.next_opcode_location();
self.emit(opcode, &[Self::DUMMY_ADDRESS]);
Label { index }
@ -1091,7 +1075,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
.name(name.map(Identifier::sym))
.generator(generator)
.r#async(r#async)
.strict(self.strict)
.strict(self.strict())
.arrow(arrow)
.binding_identifier(binding_identifier)
.compile(
@ -1185,7 +1169,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
.name(name.map(Identifier::sym))
.generator(generator)
.r#async(r#async)
.strict(self.strict)
.strict(self.strict())
.arrow(arrow)
.binding_identifier(binding_identifier)
.compile(
@ -1356,9 +1340,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
pub fn finish(self) -> CodeBlock {
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(),
@ -1366,16 +1348,11 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
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,
parameters_env_bindings: self.parameters_env_bindings,
#[cfg(feature = "trace")]
trace: std::cell::Cell::new(false),
flags: Cell::new(self.code_block_flags),
}
}

7
boa_engine/src/bytecompiler/statement/block.rs

@ -5,14 +5,13 @@ impl ByteCompiler<'_, '_> {
/// Compile a [`Block`] `boa_ast` node
pub(crate) fn compile_block(&mut self, block: &Block, use_expr: bool) {
self.push_compile_environment(false);
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
let push_env = self.emit_opcode_with_operand(Opcode::PushDeclarativeEnvironment);
self.block_declaration_instantiation(block);
self.compile_statement_list(block.statement_list(), use_expr, true);
let env_info = self.pop_compile_environment();
self.patch_jump_with_target(push_env.0, env_info.num_bindings);
self.patch_jump_with_target(push_env.1, env_info.index);
let env_index = self.pop_compile_environment();
self.patch_jump_with_target(push_env, env_index);
self.emit_opcode(Opcode::PopEnvironment);
}

43
boa_engine/src/bytecompiler/statement/loop.rs

@ -32,9 +32,8 @@ impl ByteCompiler<'_, '_> {
}
ForLoopInitializer::Lexical(decl) => {
self.push_compile_environment(false);
env_labels = Some(
self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment),
);
env_labels =
Some(self.emit_opcode_with_operand(Opcode::PushDeclarativeEnvironment));
let names = bound_names(decl);
if decl.is_const() {
@ -74,7 +73,7 @@ impl ByteCompiler<'_, '_> {
}
self.emit_opcode(Opcode::PopEnvironment);
iteration_env_labels =
Some(self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment));
Some(self.emit_opcode_with_operand(Opcode::PushDeclarativeEnvironment));
for index in let_binding_indices.iter().rev() {
self.emit(Opcode::PutLexicalValue, &[*index]);
}
@ -114,12 +113,10 @@ impl ByteCompiler<'_, '_> {
}
if let Some(env_labels) = env_labels {
let env_info = self.pop_compile_environment();
self.patch_jump_with_target(env_labels.0, env_info.num_bindings);
self.patch_jump_with_target(env_labels.1, env_info.index);
let env_index = self.pop_compile_environment();
self.patch_jump_with_target(env_labels, env_index);
if let Some(iteration_env_labels) = iteration_env_labels {
self.patch_jump_with_target(iteration_env_labels.0, env_info.num_bindings);
self.patch_jump_with_target(iteration_env_labels.1, env_info.index);
self.patch_jump_with_target(iteration_env_labels, env_index);
}
self.emit_opcode(Opcode::PopEnvironment);
}
@ -149,16 +146,15 @@ impl ByteCompiler<'_, '_> {
self.compile_expr(for_in_loop.target(), true);
} else {
self.push_compile_environment(false);
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
let push_env = self.emit_opcode_with_operand(Opcode::PushDeclarativeEnvironment);
for name in &initializer_bound_names {
self.create_mutable_binding(*name, false);
}
self.compile_expr(for_in_loop.target(), true);
let env_info = self.pop_compile_environment();
self.patch_jump_with_target(push_env.0, env_info.num_bindings);
self.patch_jump_with_target(push_env.1, env_info.index);
let env_index = self.pop_compile_environment();
self.patch_jump_with_target(push_env, env_index);
self.emit_opcode(Opcode::PopEnvironment);
}
@ -180,7 +176,7 @@ impl ByteCompiler<'_, '_> {
None
} else {
self.push_compile_environment(false);
Some(self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment))
Some(self.emit_opcode_with_operand(Opcode::PushDeclarativeEnvironment))
};
match for_in_loop.initializer() {
@ -238,9 +234,8 @@ impl ByteCompiler<'_, '_> {
self.emit_opcode(Opcode::LoopUpdateReturnValue);
if let Some(iteration_environment) = iteration_environment {
let env_info = self.pop_compile_environment();
self.patch_jump_with_target(iteration_environment.0, env_info.num_bindings);
self.patch_jump_with_target(iteration_environment.1, env_info.index);
let env_index = self.pop_compile_environment();
self.patch_jump_with_target(iteration_environment, env_index);
self.emit_opcode(Opcode::PopEnvironment);
}
@ -281,16 +276,15 @@ impl ByteCompiler<'_, '_> {
self.compile_expr(for_of_loop.iterable(), true);
} else {
self.push_compile_environment(false);
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
let push_env = self.emit_opcode_with_operand(Opcode::PushDeclarativeEnvironment);
for name in &initializer_bound_names {
self.create_mutable_binding(*name, false);
}
self.compile_expr(for_of_loop.iterable(), true);
let env_info = self.pop_compile_environment();
self.patch_jump_with_target(push_env.0, env_info.num_bindings);
self.patch_jump_with_target(push_env.1, env_info.index);
let env_index = self.pop_compile_environment();
self.patch_jump_with_target(push_env, env_index);
self.emit_opcode(Opcode::PopEnvironment);
}
@ -319,7 +313,7 @@ impl ByteCompiler<'_, '_> {
None
} else {
self.push_compile_environment(false);
Some(self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment))
Some(self.emit_opcode_with_operand(Opcode::PushDeclarativeEnvironment))
};
match for_of_loop.initializer() {
@ -383,9 +377,8 @@ impl ByteCompiler<'_, '_> {
self.emit_opcode(Opcode::LoopUpdateReturnValue);
if let Some(iteration_environment) = iteration_environment {
let env_info = self.pop_compile_environment();
self.patch_jump_with_target(iteration_environment.0, env_info.num_bindings);
self.patch_jump_with_target(iteration_environment.1, env_info.index);
let env_index = self.pop_compile_environment();
self.patch_jump_with_target(iteration_environment, env_index);
self.emit_opcode(Opcode::PopEnvironment);
}

7
boa_engine/src/bytecompiler/statement/switch.rs

@ -7,7 +7,7 @@ impl ByteCompiler<'_, '_> {
self.compile_expr(switch.val(), true);
self.push_compile_environment(false);
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
let push_env = self.emit_opcode_with_operand(Opcode::PushDeclarativeEnvironment);
self.block_declaration_instantiation(switch);
@ -60,9 +60,8 @@ impl ByteCompiler<'_, '_> {
self.emit_opcode(Opcode::Pop);
}
let env_info = self.pop_compile_environment();
self.patch_jump_with_target(push_env.0, env_info.num_bindings);
self.patch_jump_with_target(push_env.1, env_info.index);
let env_index = self.pop_compile_environment();
self.patch_jump_with_target(push_env, env_index);
self.emit_opcode(Opcode::PopEnvironment);
}
}

7
boa_engine/src/bytecompiler/statement/try.rs

@ -60,7 +60,7 @@ impl ByteCompiler<'_, '_> {
let catch_end = self.emit_opcode_with_operand(Opcode::CatchStart);
self.push_compile_environment(false);
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
let push_env = self.emit_opcode_with_operand(Opcode::PushDeclarativeEnvironment);
if let Some(binding) = catch.parameter() {
match binding {
@ -87,9 +87,8 @@ impl ByteCompiler<'_, '_> {
self.emit_opcode(Opcode::Pop);
}
let env_info = self.pop_compile_environment();
self.patch_jump_with_target(push_env.0, env_info.num_bindings);
self.patch_jump_with_target(push_env.1, env_info.index);
let env_index = self.pop_compile_environment();
self.patch_jump_with_target(push_env, env_index);
self.emit_opcode(Opcode::PopEnvironment);
if finally {

9
boa_engine/src/environments/runtime/mod.rs

@ -222,9 +222,10 @@ impl EnvironmentStack {
#[track_caller]
pub(crate) fn push_lexical(
&mut self,
num_bindings: u32,
compile_environment: Gc<GcRefCell<CompileTimeEnvironment>>,
) -> u32 {
let num_bindings = compile_environment.borrow().num_bindings();
let (poisoned, with) = {
let with = self
.stack
@ -266,10 +267,11 @@ impl EnvironmentStack {
#[track_caller]
pub(crate) fn push_function(
&mut self,
num_bindings: u32,
compile_environment: Gc<GcRefCell<CompileTimeEnvironment>>,
function_slots: FunctionSlots,
) {
let num_bindings = compile_environment.borrow().num_bindings();
let (poisoned, with) = {
let with = self
.stack
@ -309,9 +311,10 @@ impl EnvironmentStack {
#[track_caller]
pub(crate) fn push_function_inherit(
&mut self,
num_bindings: u32,
compile_environment: Gc<GcRefCell<CompileTimeEnvironment>>,
) {
let num_bindings = compile_environment.borrow().num_bindings();
debug_assert!(
self.stack.len() as u32 == compile_environment.borrow().environment_index(),
"tried to push an invalid compile environment"

146
boa_engine/src/vm/code_block.rs

@ -18,11 +18,12 @@ use crate::{
vm::CallFrame,
Context, JsError, JsResult, JsString, JsValue,
};
use bitflags::bitflags;
use boa_ast::function::{FormalParameterList, PrivateName};
use boa_gc::{Finalize, Gc, GcRefCell, Trace};
use boa_gc::{empty_trace, Finalize, Gc, GcRefCell, Trace};
use boa_interner::Sym;
use boa_profiler::Profiler;
use std::{collections::VecDeque, mem::size_of};
use std::{cell::Cell, collections::VecDeque, mem::size_of};
use thin_vec::ThinVec;
#[cfg(any(feature = "trace", feature = "flowgraph"))]
@ -52,6 +53,33 @@ unsafe impl Readable for i64 {}
unsafe impl Readable for f32 {}
unsafe impl Readable for f64 {}
bitflags! {
/// Flags for [`CodeBlock`].
#[derive(Clone, Copy, Debug, Finalize)]
pub(crate) struct CodeBlockFlags: u8 {
/// Is this function in strict mode.
const STRICT = 0b0000_0001;
/// Indicates if the function is an expression and has a binding identifier.
const HAS_BINDING_IDENTIFIER = 0b0000_0010;
/// The `[[IsClassConstructor]]` internal slot.
const IS_CLASS_CONSTRUCTOR = 0b0000_0100;
/// Does this function have a parameters environment.
const PARAMETERS_ENV_BINDINGS = 0b0000_1000;
/// Trace instruction execution to `stdout`.
#[cfg(feature = "trace")]
const TRACEABLE = 0b1000_0000;
}
}
// SAFETY: Nothing in CodeBlockFlags needs tracing, so this is safe.
unsafe impl Trace for CodeBlockFlags {
empty_trace!();
}
/// The internal representation of a JavaScript function.
///
/// A `CodeBlock` is generated for each function compiled by the
@ -63,15 +91,12 @@ pub struct CodeBlock {
#[unsafe_ignore_trace]
pub(crate) name: Sym,
/// Indicates if the function is an expression and has a binding identifier.
pub(crate) has_binding_identifier: bool,
#[unsafe_ignore_trace]
pub(crate) flags: Cell<CodeBlockFlags>,
/// 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,
@ -96,9 +121,6 @@ pub struct CodeBlock {
#[unsafe_ignore_trace]
pub(crate) bindings: Box<[BindingLocator]>,
/// Number of binding for the function environment.
pub(crate) num_bindings: u32,
/// Functions inside this function
pub(crate) functions: Box<[Gc<Self>]>,
@ -109,26 +131,9 @@ pub struct CodeBlock {
/// Compile time environments in this function.
pub(crate) compile_environments: Box<[Gc<GcRefCell<CompileTimeEnvironment>>]>,
/// The `[[IsClassConstructor]]` internal slot.
pub(crate) is_class_constructor: bool,
/// The `[[ClassFieldInitializerName]]` internal slot.
#[unsafe_ignore_trace]
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,
/// The number of bindings in the parameters environment.
pub(crate) parameters_env_bindings: Option<u32>,
#[cfg(feature = "trace")]
/// Trace instruction execution to `stdout`.
#[unsafe_ignore_trace]
pub(crate) trace: std::cell::Cell<bool>,
}
/// ---- `CodeBlock` public API ----
@ -136,28 +141,23 @@ impl CodeBlock {
/// Creates a new `CodeBlock`.
#[must_use]
pub fn new(name: Sym, length: u32, strict: bool) -> Self {
let mut flags = CodeBlockFlags::empty();
flags.set(CodeBlockFlags::STRICT, strict);
Self {
bytecode: Box::default(),
literals: Box::default(),
names: Box::default(),
private_names: Box::default(),
bindings: Box::default(),
num_bindings: 0,
functions: Box::default(),
name,
has_binding_identifier: false,
flags: Cell::new(flags),
length,
strict,
this_mode: ThisMode::Global,
params: FormalParameterList::default(),
arguments_binding: None,
compile_environments: Box::default(),
is_class_constructor: false,
class_field_initializer_name: None,
function_environment_push_location: 0,
parameters_env_bindings: None,
#[cfg(feature = "trace")]
trace: std::cell::Cell::new(false),
}
}
@ -167,11 +167,44 @@ impl CodeBlock {
self.name
}
/// Check if the function is traced.
#[cfg(feature = "trace")]
pub(crate) fn traceable(&self) -> bool {
self.flags.get().contains(CodeBlockFlags::TRACEABLE)
}
/// Enable or disable instruction tracing to `stdout`.
#[cfg(feature = "trace")]
#[inline]
pub fn set_trace(&self, value: bool) {
self.trace.set(value);
pub fn set_traceable(&self, value: bool) {
let mut flags = self.flags.get();
flags.set(CodeBlockFlags::TRACEABLE, value);
self.flags.set(flags);
}
/// Check if the function is a class constructor.
pub(crate) fn is_class_constructor(&self) -> bool {
self.flags
.get()
.contains(CodeBlockFlags::IS_CLASS_CONSTRUCTOR)
}
/// Check if the function is in strict mode.
pub(crate) fn strict(&self) -> bool {
self.flags.get().contains(CodeBlockFlags::STRICT)
}
/// Indicates if the function is an expression and has a binding identifier.
pub(crate) fn has_binding_identifier(&self) -> bool {
self.flags
.get()
.contains(CodeBlockFlags::HAS_BINDING_IDENTIFIER)
}
/// Does this function have a parameters environment.
pub(crate) fn has_parameters_env_bindings(&self) -> bool {
self.flags
.get()
.contains(CodeBlockFlags::PARAMETERS_ENV_BINDINGS)
}
}
@ -285,9 +318,12 @@ impl CodeBlock {
*pc += size_of::<u32>();
result
}
Opcode::PushDeclarativeEnvironment
| Opcode::PushFunctionEnvironment
| Opcode::CopyDataProperties
Opcode::PushDeclarativeEnvironment | Opcode::PushFunctionEnvironment => {
let operand = self.read::<u32>(*pc);
*pc += size_of::<u32>();
format!("{operand}")
}
Opcode::CopyDataProperties
| Opcode::Break
| Opcode::Continue
| Opcode::LoopStart
@ -914,7 +950,7 @@ impl JsObject {
..
} => {
let code = code.clone();
if code.is_class_constructor {
if code.is_class_constructor() {
return Err(JsNativeError::typ()
.with_message("class constructor cannot be invoked without 'new'")
.with_realm(context.realm().clone())
@ -991,7 +1027,7 @@ impl JsObject {
let this = if lexical_this_mode {
ThisBindingStatus::Lexical
} else if code.strict {
} else if code.strict() {
ThisBindingStatus::Initialized(this.clone())
} else if this.is_null_or_undefined() {
ThisBindingStatus::Initialized(context.realm().global_this().clone().into())
@ -1009,7 +1045,7 @@ impl JsObject {
let index = context
.vm
.environments
.push_lexical(1, code.compile_environments[last_env].clone());
.push_lexical(code.compile_environments[last_env].clone());
context
.vm
.environments
@ -1017,11 +1053,11 @@ impl JsObject {
last_env -= 1;
}
if code.has_binding_identifier {
if code.has_binding_identifier() {
let index = context
.vm
.environments
.push_lexical(1, code.compile_environments[last_env].clone());
.push_lexical(code.compile_environments[last_env].clone());
context
.vm
.environments
@ -1030,21 +1066,20 @@ impl JsObject {
}
context.vm.environments.push_function(
code.num_bindings,
code.compile_environments[last_env].clone(),
FunctionSlots::new(this, self.clone(), None),
);
if let Some(bindings) = code.parameters_env_bindings {
if code.has_parameters_env_bindings() {
last_env -= 1;
context
.vm
.environments
.push_lexical(bindings, code.compile_environments[last_env].clone());
.push_lexical(code.compile_environments[last_env].clone());
}
if let Some(binding) = code.arguments_binding {
let arguments_obj = if code.strict || !code.params.is_simple() {
let arguments_obj = if code.strict() || !code.params.is_simple() {
Arguments::create_unmapped_arguments_object(args, context)
} else {
let env = context.vm.environments.current();
@ -1271,11 +1306,11 @@ impl JsObject {
let mut last_env = code.compile_environments.len() - 1;
if code.has_binding_identifier {
if code.has_binding_identifier() {
let index = context
.vm
.environments
.push_lexical(1, code.compile_environments[last_env].clone());
.push_lexical(code.compile_environments[last_env].clone());
context
.vm
.environments
@ -1284,7 +1319,6 @@ impl JsObject {
}
context.vm.environments.push_function(
code.num_bindings,
code.compile_environments[last_env].clone(),
FunctionSlots::new(
this.clone().map_or(ThisBindingStatus::Uninitialized, |o| {
@ -1295,15 +1329,15 @@ impl JsObject {
),
);
if let Some(bindings) = code.parameters_env_bindings {
if code.has_parameters_env_bindings() {
context
.vm
.environments
.push_lexical(bindings, code.compile_environments[0].clone());
.push_lexical(code.compile_environments[0].clone());
}
if let Some(binding) = code.arguments_binding {
let arguments_obj = if code.strict || !code.params.is_simple() {
let arguments_obj = if code.strict() || !code.params.is_simple() {
Arguments::create_unmapped_arguments_object(args, context)
} else {
let env = context.vm.environments.current();
@ -1340,7 +1374,7 @@ impl JsObject {
context.vm.push(arg.clone());
}
let has_binding_identifier = code.has_binding_identifier;
let has_binding_identifier = code.has_binding_identifier();
std::mem::swap(&mut context.vm.active_runnable, &mut script_or_module);

1
boa_engine/src/vm/flowgraph/mod.rs

@ -358,7 +358,6 @@ impl CodeBlock {
let random = rand::random();
environments.push((previous_pc, random));
pc += size_of::<u32>();
pc += size_of::<u32>();
graph.add_node(

3
boa_engine/src/vm/mod.rs

@ -40,6 +40,7 @@ pub(crate) use {
call_frame::GeneratorResumeKind,
code_block::{
create_function_object, create_function_object_fast, create_generator_function_object,
CodeBlockFlags,
},
completion_record::CompletionRecord,
opcode::BindingOpcode,
@ -239,7 +240,7 @@ impl Context<'_> {
// 1. Run the next instruction.
#[cfg(feature = "trace")]
let result = if self.vm.trace || self.vm.frame().code_block.trace.get() {
let result = if self.vm.trace || self.vm.frame().code_block.traceable() {
let mut pc = self.vm.frame().pc as usize;
let opcode: Opcode = self
.vm

4
boa_engine/src/vm/opcode/call/mod.rs

@ -58,7 +58,7 @@ impl Operation for CallEval {
.map(|f| matches!(f.kind(), FunctionKind::Native { .. }))
.unwrap_or_default();
let strict = context.vm.frame().code_block.strict;
let strict = context.vm.frame().code_block.strict();
if eval {
if let Some(x) = arguments.get(0) {
@ -132,7 +132,7 @@ impl Operation for CallEvalSpread {
.map(|f| matches!(f.kind(), FunctionKind::Native { .. }))
.unwrap_or_default();
let strict = context.vm.frame().code_block.strict;
let strict = context.vm.frame().code_block.strict();
if eval {
if let Some(x) = arguments.get(0) {

6
boa_engine/src/vm/opcode/define/mod.rs

@ -60,7 +60,11 @@ impl Operation for DefInitVar {
context.find_runtime_binding(&mut binding_locator)?;
context.set_binding(binding_locator, value, context.vm.frame().code_block.strict)?;
context.set_binding(
binding_locator,
value,
context.vm.frame().code_block.strict(),
)?;
Ok(CompletionType::Normal)
}

4
boa_engine/src/vm/opcode/delete/mod.rs

@ -23,7 +23,7 @@ impl Operation for DeletePropertyByName {
.clone()
.into();
let result = object.__delete__(&key, context)?;
if !result && context.vm.frame().code_block.strict {
if !result && context.vm.frame().code_block.strict() {
return Err(JsNativeError::typ()
.with_message("Cannot delete property")
.into());
@ -50,7 +50,7 @@ impl Operation for DeletePropertyByValue {
let object = value.to_object(context)?;
let property_key = key_value.to_property_key(context)?;
let result = object.__delete__(&property_key, context)?;
if !result && context.vm.frame().code_block.strict {
if !result && context.vm.frame().code_block.strict() {
return Err(JsNativeError::typ()
.with_message("Cannot delete property")
.into());

4
boa_engine/src/vm/opcode/mod.rs

@ -1348,7 +1348,7 @@ generate_impl! {
/// Push a declarative environment.
///
/// Operands: num_bindings: `u32`, compile_environments_index: `u32`
/// Operands: compile_environments_index: `u32`
///
/// Stack: **=>**
PushDeclarativeEnvironment,
@ -1362,7 +1362,7 @@ generate_impl! {
/// Push a function environment.
///
/// Operands: num_bindings: `u32`, compile_environments_index: `u32`
/// Operands: compile_environments_index: `u32`
///
/// Stack: **=>**
PushFunctionEnvironment,

9
boa_engine/src/vm/opcode/push/environment.rs

@ -17,15 +17,11 @@ impl Operation for PushDeclarativeEnvironment {
const INSTRUCTION: &'static str = "INST - PushDeclarativeEnvironment";
fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
let num_bindings = context.vm.read::<u32>();
let compile_environments_index = context.vm.read::<u32>();
let compile_environment = context.vm.frame().code_block.compile_environments
[compile_environments_index as usize]
.clone();
context
.vm
.environments
.push_lexical(num_bindings, compile_environment);
context.vm.environments.push_lexical(compile_environment);
context.vm.frame_mut().inc_frame_env_stack();
Ok(CompletionType::Normal)
}
@ -43,7 +39,6 @@ impl Operation for PushFunctionEnvironment {
const INSTRUCTION: &'static str = "INST - PushFunctionEnvironment";
fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
let num_bindings = context.vm.read::<u32>();
let compile_environments_index = context.vm.read::<u32>();
let compile_environment = context.vm.frame().code_block.compile_environments
[compile_environments_index as usize]
@ -51,7 +46,7 @@ impl Operation for PushFunctionEnvironment {
context
.vm
.environments
.push_function_inherit(num_bindings, compile_environment);
.push_function_inherit(compile_environment);
Ok(CompletionType::Normal)
}
}

14
boa_engine/src/vm/opcode/set/name.rs

@ -28,7 +28,11 @@ impl Operation for SetName {
verify_initialized(binding_locator, context)?;
context.set_binding(binding_locator, value, context.vm.frame().code_block.strict)?;
context.set_binding(
binding_locator,
value,
context.vm.frame().code_block.strict(),
)?;
Ok(CompletionType::Normal)
}
@ -60,7 +64,11 @@ impl Operation for SetNameByLocator {
verify_initialized(binding_locator, context)?;
context.set_binding(binding_locator, value, context.vm.frame().code_block.strict)?;
context.set_binding(
binding_locator,
value,
context.vm.frame().code_block.strict(),
)?;
Ok(CompletionType::Normal)
}
@ -70,7 +78,7 @@ impl Operation for SetNameByLocator {
fn verify_initialized(locator: BindingLocator, context: &mut Context<'_>) -> JsResult<()> {
if !context.is_initialized_binding(&locator)? {
let key = context.interner().resolve_expect(locator.name().sym());
let strict = context.vm.frame().code_block.strict;
let strict = context.vm.frame().code_block.strict();
let message = if locator.is_global() {
strict.then(|| format!("cannot assign to uninitialized global property `{key}`"))

6
boa_engine/src/vm/opcode/set/property.rs

@ -35,7 +35,7 @@ impl Operation for SetPropertyByName {
.into();
let succeeded = object.__set__(name.clone(), value.clone(), receiver, context)?;
if !succeeded && context.vm.frame().code_block.strict {
if !succeeded && context.vm.frame().code_block.strict() {
return Err(JsNativeError::typ()
.with_message(format!("cannot set non-writable property: {name}"))
.into());
@ -126,7 +126,7 @@ impl Operation for SetPropertyByValue {
.configurable(false)
.build(),
);
} else if context.vm.frame().code_block.strict {
} else if context.vm.frame().code_block.strict() {
return Err(JsNativeError::typ().with_message("TypeError: Cannot assign to read only property 'length' of array object").into());
}
return Ok(CompletionType::Normal);
@ -140,7 +140,7 @@ impl Operation for SetPropertyByValue {
object.set(
key,
value.clone(),
context.vm.frame().code_block.strict,
context.vm.frame().code_block.strict(),
context,
)?;
context.vm.stack.push(value);

Loading…
Cancel
Save