Browse Source

Refactor compile time environment handling (#3365)

* Refactor compile time environment handling

* Apply review

* Apply review
pull/3374/head
raskad 8 months ago committed by GitHub
parent
commit
f654ad1784
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      boa_engine/src/builtins/eval/mod.rs
  2. 3
      boa_engine/src/builtins/function/mod.rs
  3. 1
      boa_engine/src/builtins/json/mod.rs
  4. 60
      boa_engine/src/bytecompiler/class.rs
  5. 271
      boa_engine/src/bytecompiler/declarations.rs
  6. 110
      boa_engine/src/bytecompiler/env.rs
  7. 11
      boa_engine/src/bytecompiler/expression/assign.rs
  8. 6
      boa_engine/src/bytecompiler/expression/unary.rs
  9. 11
      boa_engine/src/bytecompiler/expression/update.rs
  10. 33
      boa_engine/src/bytecompiler/function.rs
  11. 102
      boa_engine/src/bytecompiler/mod.rs
  12. 6
      boa_engine/src/bytecompiler/module.rs
  13. 5
      boa_engine/src/bytecompiler/statement/block.rs
  14. 79
      boa_engine/src/bytecompiler/statement/loop.rs
  15. 5
      boa_engine/src/bytecompiler/statement/switch.rs
  16. 11
      boa_engine/src/bytecompiler/statement/try.rs
  17. 2
      boa_engine/src/bytecompiler/statement/with.rs
  18. 207
      boa_engine/src/environments/compile.rs
  19. 11
      boa_engine/src/environments/runtime/declarative/mod.rs
  20. 62
      boa_engine/src/environments/runtime/mod.rs
  21. 36
      boa_engine/src/module/source.rs
  22. 6
      boa_engine/src/script.rs
  23. 2
      boa_engine/src/vm/opcode/define/mod.rs
  24. 3
      boa_engine/src/vm/opcode/mod.rs

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

@ -224,31 +224,35 @@ impl Eval {
}
});
let var_environment = context.vm.environments.outer_function_environment();
let mut var_env = var_environment.compile_env();
let mut compiler = ByteCompiler::new(
Sym::MAIN,
body.strict(),
false,
var_env.clone(),
context.vm.environments.current_compile_environment(),
context,
);
let env_index = compiler.push_compile_environment(strict);
compiler.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index);
let lex_env = compiler.lexical_environment.clone();
if strict {
var_env = lex_env.clone();
compiler.variable_environment = lex_env.clone();
}
compiler.eval_declaration_instantiation(&body, strict)?;
compiler.eval_declaration_instantiation(&body, strict, &var_env, &lex_env)?;
compiler.compile_statement_list(body.statements(), true, false);
compiler.pop_compile_environment();
compiler.emit_opcode(Opcode::PopEnvironment);
let code_block = Gc::new(compiler.finish());
// Indirect calls don't need extensions, because a non-strict indirect call modifies only
// the global object.
// Strict direct calls also don't need extensions, since all strict eval calls push a new
// Strict calls don't need extensions, since all strict eval calls push a new
// function environment before evaluating.
if direct && !strict {
context.vm.environments.extend_outer_function_environment();
if !strict {
var_environment.extend_from_compile();
}
let env_fp = context.vm.environments.len() as u32;

3
boa_engine/src/builtins/function/mod.rs

@ -636,6 +636,7 @@ impl BuiltInFunctionObject {
&parameters,
&body,
context.realm().environment().compile_env(),
context.realm().environment().compile_env(),
context,
);
@ -659,6 +660,7 @@ impl BuiltInFunctionObject {
&FormalParameterList::default(),
&FunctionBody::default(),
context.realm().environment().compile_env(),
context.realm().environment().compile_env(),
context,
);
@ -680,6 +682,7 @@ impl BuiltInFunctionObject {
&FormalParameterList::default(),
&FunctionBody::default(),
context.realm().environment().compile_env(),
context.realm().environment().compile_env(),
context,
);

1
boa_engine/src/builtins/json/mod.rs

@ -122,6 +122,7 @@ impl Json {
script.strict(),
true,
context.realm().environment().compile_env(),
context.realm().environment().compile_env(),
context,
);
compiler.compile_statement_list(script.statements(), true, false);

60
boa_engine/src/bytecompiler/class.rs

@ -26,24 +26,29 @@ 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 class_env = match class.name() {
let old_lex_env = match class.name() {
Some(name) if class.has_binding_identifier() => {
let old_lex_env = self.lexical_environment.clone();
let env_index = self.push_compile_environment(false);
self.create_immutable_binding(name, true);
self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index);
true
self.lexical_environment
.create_immutable_binding(name, true);
Some(old_lex_env)
}
_ => false,
_ => None,
};
let mut compiler = ByteCompiler::new(
class_name,
true,
self.json_parse,
self.current_environment.clone(),
self.variable_environment.clone(),
self.lexical_environment.clone(),
self.context,
);
compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR;
// Function environment
let _ = compiler.push_compile_environment(true);
@ -51,7 +56,7 @@ impl ByteCompiler<'_, '_> {
compiler.length = expr.parameters().length();
compiler.params = expr.parameters().clone();
let (env_label, _) = compiler.function_declaration_instantiation(
compiler.function_declaration_instantiation(
expr.body(),
expr.parameters(),
false,
@ -61,23 +66,14 @@ impl ByteCompiler<'_, '_> {
compiler.compile_statement_list(expr.body().statements(), false, false);
if env_label {
compiler.pop_compile_environment();
} else {
compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR;
}
compiler.emit_opcode(Opcode::PushUndefined);
} else if class.super_ref().is_some() {
compiler.emit_opcode(Opcode::SuperCallDerived);
} else {
if class.super_ref().is_some() {
compiler.emit_opcode(Opcode::SuperCallDerived);
} else {
compiler.emit_opcode(Opcode::RestParameterPop);
compiler.emit_opcode(Opcode::PushUndefined);
}
compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR;
compiler.emit_opcode(Opcode::RestParameterPop);
compiler.emit_opcode(Opcode::PushUndefined);
}
compiler.emit_opcode(Opcode::SetReturnValue);
compiler.pop_compile_environment();
let code = Gc::new(compiler.finish());
let index = self.functions.len() as u32;
@ -117,9 +113,9 @@ impl ByteCompiler<'_, '_> {
let mut static_elements = Vec::new();
let mut static_field_name_count = 0;
if class_env {
if old_lex_env.is_some() {
self.emit_opcode(Opcode::Dup);
self.emit_binding(BindingOpcode::InitConst, class_name.into());
self.emit_binding(BindingOpcode::InitLexical, class_name.into());
}
// TODO: set function name for getter and setters
@ -277,7 +273,8 @@ impl ByteCompiler<'_, '_> {
Sym::EMPTY_STRING,
true,
self.json_parse,
self.current_environment.clone(),
self.variable_environment.clone(),
self.lexical_environment.clone(),
self.context,
);
@ -290,8 +287,6 @@ impl ByteCompiler<'_, '_> {
}
field_compiler.emit_opcode(Opcode::SetReturnValue);
field_compiler.pop_compile_environment();
field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER;
let code = field_compiler.finish();
@ -311,7 +306,8 @@ impl ByteCompiler<'_, '_> {
class_name,
true,
self.json_parse,
self.current_environment.clone(),
self.variable_environment.clone(),
self.lexical_environment.clone(),
self.context,
);
let _ = field_compiler.push_compile_environment(true);
@ -320,8 +316,6 @@ impl ByteCompiler<'_, '_> {
} else {
field_compiler.emit_opcode(Opcode::PushUndefined);
}
field_compiler.pop_compile_environment();
field_compiler.emit_opcode(Opcode::SetReturnValue);
field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER;
@ -355,7 +349,8 @@ impl ByteCompiler<'_, '_> {
class_name,
true,
self.json_parse,
self.current_environment.clone(),
self.variable_environment.clone(),
self.lexical_environment.clone(),
self.context,
);
let _ = field_compiler.push_compile_environment(true);
@ -366,8 +361,6 @@ impl ByteCompiler<'_, '_> {
}
field_compiler.emit_opcode(Opcode::SetReturnValue);
field_compiler.pop_compile_environment();
field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER;
let code = field_compiler.finish();
@ -390,7 +383,8 @@ impl ByteCompiler<'_, '_> {
Sym::EMPTY_STRING,
true,
false,
self.current_environment.clone(),
self.variable_environment.clone(),
self.lexical_environment.clone(),
self.context,
);
let _ = compiler.push_compile_environment(true);
@ -404,7 +398,6 @@ impl ByteCompiler<'_, '_> {
);
compiler.compile_statement_list(body.statements(), false, false);
compiler.pop_compile_environment();
let code = Gc::new(compiler.finish());
static_elements.push(StaticElement::StaticBlock(code));
@ -589,8 +582,9 @@ impl ByteCompiler<'_, '_> {
self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::Pop);
if class_env {
if let Some(old_lex_env) = old_lex_env {
self.pop_compile_environment();
self.lexical_environment = old_lex_env;
self.emit_opcode(Opcode::PopEnvironment);
}

271
boa_engine/src/bytecompiler/declarations.rs

@ -1,6 +1,8 @@
use std::rc::Rc;
use crate::{
bytecompiler::{ByteCompiler, FunctionCompiler, FunctionSpec, NodeKind},
environments::BindingLocatorError,
environments::CompileTimeEnvironment,
vm::{
create_function_object_fast, create_generator_function_object, BindingOpcode,
CodeBlockFlags, Opcode,
@ -32,7 +34,11 @@ impl ByteCompiler<'_, '_> {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
pub(crate) fn global_declaration_instantiation(&mut self, script: &Script) -> JsResult<()> {
pub(crate) fn global_declaration_instantiation(
&mut self,
script: &Script,
env: &Rc<CompileTimeEnvironment>,
) -> JsResult<()> {
// 1. Let lexNames be the LexicallyDeclaredNames of script.
let lex_names = lexically_declared_names(script);
@ -43,9 +49,8 @@ impl ByteCompiler<'_, '_> {
for name in lex_names {
// Note: Our implementation differs from the spec here.
// a. If env.HasVarDeclaration(name) is true, throw a SyntaxError exception.
// b. If env.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.
if self.has_binding(name) {
if env.has_binding(name) {
return Err(JsNativeError::syntax()
.with_message("duplicate lexical declaration")
.into());
@ -65,7 +70,7 @@ impl ByteCompiler<'_, '_> {
// 4. For each element name of varNames, do
for name in var_names {
// a. If env.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.
if self.has_binding(name) {
if env.has_lex_binding(name) {
return Err(JsNativeError::syntax()
.with_message("duplicate lexical declaration")
.into());
@ -176,7 +181,7 @@ impl ByteCompiler<'_, '_> {
// would not produce any Early Errors for script, then
if !lex_names.contains(&f) {
// a. If env.HasLexicalDeclaration(F) is false, then
if !self.current_environment.has_lex_binding(f) {
if !env.has_lex_binding(f) {
// i. Let fnDefinable be ? env.CanDeclareGlobalVar(F).
let fn_definable = self.context.can_declare_global_function(f)?;
@ -222,17 +227,17 @@ impl ByteCompiler<'_, '_> {
match declaration {
Declaration::Class(class) => {
for name in bound_names(class) {
self.create_mutable_binding(name, false);
env.create_mutable_binding(name, false);
}
}
Declaration::Lexical(LexicalDeclaration::Let(declaration)) => {
for name in bound_names(declaration) {
self.create_mutable_binding(name, false);
env.create_mutable_binding(name, false);
}
}
Declaration::Lexical(LexicalDeclaration::Const(declaration)) => {
for name in bound_names(declaration) {
self.create_immutable_binding(name, true);
env.create_immutable_binding(name, true);
}
}
_ => {}
@ -271,7 +276,8 @@ impl ByteCompiler<'_, '_> {
.compile(
parameters,
body,
self.current_environment.clone(),
self.variable_environment.clone(),
self.lexical_environment.clone(),
self.context,
);
@ -306,12 +312,13 @@ impl ByteCompiler<'_, '_> {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-blockdeclarationinstantiation
pub(crate) fn block_declaration_instantiation<'a, N>(&mut self, block: &'a N)
where
pub(crate) fn block_declaration_instantiation<'a, N>(
&mut self,
block: &'a N,
env: &Rc<CompileTimeEnvironment>,
) where
&'a N: Into<NodeRef<'a>>,
{
let env = &self.current_environment;
// 1. Let declarations be the LexicallyScopedDeclarations of code.
let declarations = lexically_scoped_declarations(block);
@ -386,14 +393,9 @@ impl ByteCompiler<'_, '_> {
&mut self,
body: &Script,
strict: bool,
var_env: &Rc<CompileTimeEnvironment>,
lex_env: &Rc<CompileTimeEnvironment>,
) -> JsResult<()> {
let var_environment_is_global = self
.context
.vm
.environments
.is_next_outer_function_environment_global()
&& !strict;
// 2. Let varDeclarations be the VarScopedDeclarations of body.
let var_declarations = var_scoped_declarations(body);
@ -403,30 +405,47 @@ impl ByteCompiler<'_, '_> {
let var_names = var_declared_names(body);
// a. If varEnv is a Global Environment Record, then
// i. For each element name of varNames, do
// 1. If varEnv.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.
// 2. NOTE: eval will not create a global var declaration that would be shadowed by a global lexical declaration.
if var_env.is_global() {
// i. For each element name of varNames, do
for name in &var_names {
// 1. If varEnv.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.
// 2. NOTE: eval will not create a global var declaration that would be shadowed by a global lexical declaration.
if var_env.has_lex_binding(*name) {
return Err(JsNativeError::syntax()
.with_message("duplicate lexical declaration")
.into());
}
}
}
// b. Let thisEnv be lexEnv.
let mut this_env = lex_env.clone();
// c. Assert: The following loop will terminate.
// d. Repeat, while thisEnv is not varEnv,
// i. If thisEnv is not an Object Environment Record, then
// 1. NOTE: The environment of with statements cannot contain any lexical
// declaration so it doesn't need to be checked for var/let hoisting conflicts.
// 2. For each element name of varNames, do
// a. If ! thisEnv.HasBinding(name) is true, then
// i. Throw a SyntaxError exception.
// ii. NOTE: Annex B.3.4 defines alternate semantics for the above step.
// b. NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration.
// ii. Set thisEnv to thisEnv.[[OuterEnv]].
if let Some(name) = self
.context
.vm
.environments
.has_lex_binding_until_function_environment(&var_names)
{
let name = self.context.interner().resolve_expect(name.sym());
let msg = format!("variable declaration {name} in eval function already exists as a lexical variable");
return Err(JsNativeError::syntax().with_message(msg).into());
while this_env.environment_index() != var_env.environment_index() {
// i. If thisEnv is not an Object Environment Record, then
// 1. NOTE: The environment of with statements cannot contain any lexical
// declaration so it doesn't need to be checked for var/let hoisting conflicts.
// 2. For each element name of varNames, do
for name in &var_names {
// a. If ! thisEnv.HasBinding(name) is true, then
if this_env.has_binding(*name) {
// i. Throw a SyntaxError exception.
// ii. NOTE: Annex B.3.4 defines alternate semantics for the above step.
let name = self.context.interner().resolve_expect(name.sym());
let msg = format!("variable declaration {name} in eval function already exists as a lexical variable");
return Err(JsNativeError::syntax().with_message(msg).into());
}
// b. NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration.
}
// ii. Set thisEnv to thisEnv.[[OuterEnv]].
if let Some(outer) = this_env.outer() {
this_env = outer;
} else {
break;
}
}
}
@ -482,7 +501,7 @@ impl ByteCompiler<'_, '_> {
// a.iv. If declaredFunctionNames does not contain fn, then
if !declared_function_names.contains(&name) {
// 1. If varEnv is a Global Environment Record, then
if var_environment_is_global {
if var_env.is_global() {
// a. Let fnDefinable be ? varEnv.CanDeclareGlobalFunction(fn).
let fn_definable = self.context.can_declare_global_function(name)?;
@ -519,20 +538,35 @@ impl ByteCompiler<'_, '_> {
// as a BindingIdentifier would not produce any Early Errors for body, then
if !lexically_declared_names.contains(&f) {
// 1. Let bindingExists be false.
let mut binding_exists = false;
// 2. Let thisEnv be lexEnv.
let mut this_env = lex_env.clone();
// 3. Assert: The following loop will terminate.
// 4. Repeat, while thisEnv is not varEnv,
// a. If thisEnv is not an Object Environment Record, then
// i. If ! thisEnv.HasBinding(F) is true, then
// i. Let bindingExists be true.
// b. Set thisEnv to thisEnv.[[OuterEnv]].
let binding_exists = self.has_binding_until_var(f);
while this_env.environment_index() != lex_env.environment_index() {
// a. If thisEnv is not an Object Environment Record, then
// i. If ! thisEnv.HasBinding(F) is true, then
if this_env.has_binding(f) {
// i. Let bindingExists be true.
binding_exists = true;
break;
}
// b. Set thisEnv to thisEnv.[[OuterEnv]].
if let Some(outer) = this_env.outer() {
this_env = outer;
} else {
break;
}
}
// 5. If bindingExists is false and varEnv is a Global Environment Record, then
let fn_definable = if !binding_exists && var_environment_is_global {
let fn_definable = if !binding_exists && var_env.is_global() {
// a. If varEnv.HasLexicalDeclaration(F) is false, then
// b. Else,
if self.current_environment.has_lex_binding(f) {
if self.variable_environment.has_lex_binding(f) {
// i. Let fnDefinable be false.
false
} else {
@ -556,7 +590,7 @@ impl ByteCompiler<'_, '_> {
&& !function_names.contains(&f)
{
// i. If varEnv is a Global Environment Record, then
if var_environment_is_global {
if var_env.is_global() {
// i. Perform ? varEnv.CreateGlobalVarBinding(F, true).
self.context.create_global_var_binding(f, true)?;
}
@ -564,12 +598,10 @@ impl ByteCompiler<'_, '_> {
else {
// i. Let bindingExists be ! varEnv.HasBinding(F).
// ii. If bindingExists is false, then
if !self.has_binding(f) {
if !var_env.has_binding(f) {
// i. Perform ! varEnv.CreateMutableBinding(F, true).
self.create_mutable_binding(f, true);
// ii. Perform ! varEnv.InitializeBinding(F, undefined).
let binding = self.initialize_mutable_binding(f, true);
let binding = var_env.create_mutable_binding(f, true);
let index = self.get_or_insert_binding(binding);
self.emit_opcode(Opcode::PushUndefined);
self.emit_with_varying_operand(Opcode::DefInitVar, index);
@ -608,7 +640,7 @@ impl ByteCompiler<'_, '_> {
// 1. If declaredFunctionNames does not contain vn, then
if !declared_function_names.contains(&name) {
// a. If varEnv is a Global Environment Record, then
if var_environment_is_global {
if var_env.is_global() {
// i. Let vnDefinable be ? varEnv.CanDeclareGlobalVar(vn).
let vn_definable = self.context.can_declare_global_var(name)?;
@ -645,17 +677,17 @@ impl ByteCompiler<'_, '_> {
match declaration {
Declaration::Class(class) => {
for name in bound_names(class) {
self.create_mutable_binding(name, false);
lex_env.create_mutable_binding(name, false);
}
}
Declaration::Lexical(LexicalDeclaration::Let(declaration)) => {
for name in bound_names(declaration) {
self.create_mutable_binding(name, false);
lex_env.create_mutable_binding(name, false);
}
}
Declaration::Lexical(LexicalDeclaration::Const(declaration)) => {
for name in bound_names(declaration) {
self.create_immutable_binding(name, true);
lex_env.create_immutable_binding(name, true);
}
}
_ => {}
@ -693,12 +725,13 @@ impl ByteCompiler<'_, '_> {
.compile(
parameters,
body,
self.context.vm.environments.current_compile_environment(),
self.variable_environment.clone(),
self.lexical_environment.clone(),
self.context,
);
// c. If varEnv is a Global Environment Record, then
if var_environment_is_global {
if var_env.is_global() {
// Ensures global functions are printed when generating the global flowgraph.
self.functions.push(code.clone());
@ -735,31 +768,20 @@ impl ByteCompiler<'_, '_> {
}
// i. Let bindingExists be ! varEnv.HasBinding(fn).
let binding_exists = self.has_binding_eval(name, strict);
let binding_exists = var_env.has_binding(name);
// ii. If bindingExists is false, then
// iii. Else,
if binding_exists {
// 1. Perform ! varEnv.SetMutableBinding(fn, fo, false).
match self.set_mutable_binding(name) {
Ok(binding) => {
let index = self.get_or_insert_binding(binding);
self.emit_with_varying_operand(Opcode::SetName, index);
}
Err(BindingLocatorError::MutateImmutable) => {
let index = self.get_or_insert_name(name);
self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index);
}
Err(BindingLocatorError::Silent) => {
self.emit_opcode(Opcode::Pop);
}
}
let binding = var_env.set_mutable_binding(name).expect("must not fail");
let index = self.get_or_insert_binding(binding);
self.emit_with_varying_operand(Opcode::SetName, index);
} else {
// 1. NOTE: The following invocation cannot return an abrupt completion because of the validation preceding step 14.
// 2. Perform ! varEnv.CreateMutableBinding(fn, true).
// 3. Perform ! varEnv.InitializeBinding(fn, fo).
self.create_mutable_binding(name, !strict);
let binding = self.initialize_mutable_binding(name, !strict);
let binding = var_env.create_mutable_binding(name, !strict);
let index = self.get_or_insert_binding(binding);
self.emit_with_varying_operand(Opcode::DefInitVar, index);
}
@ -769,22 +791,21 @@ impl ByteCompiler<'_, '_> {
// 18. For each String vn of declaredVarNames, do
for name in declared_var_names {
// a. If varEnv is a Global Environment Record, then
if var_environment_is_global {
if var_env.is_global() {
// i. Perform ? varEnv.CreateGlobalVarBinding(vn, true).
self.context.create_global_var_binding(name, true)?;
}
// b. Else,
else {
// i. Let bindingExists be ! varEnv.HasBinding(vn).
let binding_exists = self.has_binding_eval(name, strict);
let binding_exists = var_env.has_binding(name);
// ii. If bindingExists is false, then
if !binding_exists {
// 1. NOTE: The following invocation cannot return an abrupt completion because of the validation preceding step 14.
// 2. Perform ! varEnv.CreateMutableBinding(vn, true).
// 3. Perform ! varEnv.InitializeBinding(vn, undefined).
self.create_mutable_binding(name, !strict);
let binding = self.initialize_mutable_binding(name, !strict);
let binding = var_env.create_mutable_binding(name, true);
let index = self.get_or_insert_binding(binding);
self.emit_opcode(Opcode::PushUndefined);
self.emit_with_varying_operand(Opcode::DefInitVar, index);
@ -809,10 +830,7 @@ impl ByteCompiler<'_, '_> {
arrow: bool,
strict: bool,
generator: bool,
) -> (bool, bool) {
let mut env_label = false;
let mut additional_env = false;
) {
// 1. Let calleeContext be the running execution context.
// 2. Let code be func.[[ECMAScriptCode]].
// 3. Let strict be func.[[Strict]].
@ -899,11 +917,13 @@ impl ByteCompiler<'_, '_> {
}
// 19. If strict is true or hasParameterExpressions is false, then
// a. NOTE: Only a single Environment Record is needed for the parameters,
// since calls to eval in strict mode code cannot create new bindings which are visible outside of the eval.
// b. Let env be the LexicalEnvironment of calleeContext.
if strict || !has_parameter_expressions {
// a. NOTE: Only a single Environment Record is needed for the parameters,
// since calls to eval in strict mode code cannot create new bindings which are visible outside of the eval.
// b. Let env be the LexicalEnvironment of calleeContext.
}
// 20. Else,
if !strict && has_parameter_expressions {
else {
// a. NOTE: A separate Environment Record is needed to ensure that bindings created by
// direct eval calls in the formal parameter list are outside the environment where parameters are declared.
// b. Let calleeEnv be the LexicalEnvironment of calleeContext.
@ -911,8 +931,10 @@ impl ByteCompiler<'_, '_> {
// d. Assert: The VariableEnvironment of calleeContext is calleeEnv.
// e. Set the LexicalEnvironment of calleeContext to env.
let _ = self.push_compile_environment(false);
additional_env = true;
}
self.code_block_flags |= CodeBlockFlags::PARAMETERS_ENV_BINDINGS;
};
let env = self.lexical_environment.clone();
// 22. If argumentsObjectNeeded is true, then
//
@ -933,12 +955,12 @@ impl ByteCompiler<'_, '_> {
// i. Perform ! env.CreateImmutableBinding("arguments", false).
// ii. NOTE: In strict mode code early errors prevent attempting to assign
// to this binding, so its mutability is not observable.
self.create_immutable_binding(arguments, false);
env.create_immutable_binding(arguments, false);
}
// d. Else,
else {
// i. Perform ! env.CreateMutableBinding("arguments", false).
self.create_mutable_binding(arguments, false);
env.create_mutable_binding(arguments, false);
}
self.code_block_flags |= CodeBlockFlags::NEEDS_ARGUMENTS_OBJECT;
@ -947,7 +969,7 @@ impl ByteCompiler<'_, '_> {
// 21. For each String paramName of parameterNames, do
for param_name in &parameter_names {
// a. Let alreadyDeclared be ! env.HasBinding(paramName).
let already_declared = self.has_binding(*param_name);
let already_declared = env.has_binding(*param_name);
// b. NOTE: Early errors ensure that duplicate parameter names can only occur in non-strict
// functions that do not have parameter default values or rest parameters.
@ -955,7 +977,7 @@ impl ByteCompiler<'_, '_> {
// c. If alreadyDeclared is false, then
if !already_declared {
// i. Perform ! env.CreateMutableBinding(paramName, false).
self.create_mutable_binding(*param_name, false);
env.create_mutable_binding(*param_name, false);
// Note: These steps are not necessary in our implementation.
// ii. If hasDuplicates is true, then
@ -991,24 +1013,20 @@ impl ByteCompiler<'_, '_> {
}
match parameter.variable().binding() {
Binding::Identifier(ident) => {
self.create_mutable_binding(*ident, false);
if let Some(init) = parameter.variable().init() {
let skip = self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined);
self.compile_expr(init, true);
self.patch_jump(skip);
}
self.emit_binding(BindingOpcode::InitLet, *ident);
self.emit_binding(BindingOpcode::InitLexical, *ident);
}
Binding::Pattern(pattern) => {
for ident in bound_names(pattern) {
self.create_mutable_binding(ident, false);
}
if let Some(init) = parameter.variable().init() {
let skip = self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined);
self.compile_expr(init, true);
self.patch_jump(skip);
}
self.compile_declaration_pattern(pattern, BindingOpcode::InitLet);
self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical);
}
}
}
@ -1024,7 +1042,7 @@ impl ByteCompiler<'_, '_> {
// 27. If hasParameterExpressions is false, then
// 28. Else,
#[allow(unused_variables, unused_mut)]
let mut instantiated_var_names = if has_parameter_expressions {
let (mut instantiated_var_names, mut var_env) = if has_parameter_expressions {
// a. NOTE: A separate Environment Record is needed to ensure that closures created by
// expressions in the formal parameter list do not have
// visibility of declarations in the function body.
@ -1032,7 +1050,8 @@ impl ByteCompiler<'_, '_> {
// c. Set the VariableEnvironment of calleeContext to varEnv.
let env_index = self.push_compile_environment(false);
self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index);
env_label = true;
let mut var_env = self.lexical_environment.clone();
// d. Let instantiatedVarNames be a new empty List.
let mut instantiated_var_names = Vec::new();
@ -1045,7 +1064,7 @@ impl ByteCompiler<'_, '_> {
instantiated_var_names.push(n);
// 2. Perform ! varEnv.CreateMutableBinding(n, false).
self.create_mutable_binding(n, true);
let binding = var_env.create_mutable_binding(n, false);
// 3. If parameterBindings does not contain n, or if functionNames contains n, then
if !parameter_bindings.contains(&n) || function_names.contains(&n) {
@ -1055,13 +1074,12 @@ impl ByteCompiler<'_, '_> {
// 4. Else,
else {
// a. Let initialValue be ! env.GetBindingValue(n, false).
let binding = self.get_binding_value(n);
let binding = env.get_binding(n).expect("must have binding");
let index = self.get_or_insert_binding(binding);
self.emit_with_varying_operand(Opcode::GetName, index);
}
// 5. Perform ! varEnv.InitializeBinding(n, initialValue).
let binding = self.initialize_mutable_binding(n, true);
let index = self.get_or_insert_binding(binding);
self.emit_opcode(Opcode::PushUndefined);
self.emit_with_varying_operand(Opcode::DefInitVar, index);
@ -1071,7 +1089,7 @@ impl ByteCompiler<'_, '_> {
}
}
instantiated_var_names
(instantiated_var_names, var_env)
} else {
// a. NOTE: Only a single Environment Record is needed for the parameters and top-level vars.
// b. Let instantiatedVarNames be a copy of the List parameterBindings.
@ -1085,10 +1103,8 @@ impl ByteCompiler<'_, '_> {
instantiated_var_names.push(n);
// 2. Perform ! env.CreateMutableBinding(n, false).
self.create_mutable_binding(n, true);
// 3. Perform ! env.InitializeBinding(n, undefined).
let binding = self.initialize_mutable_binding(n, true);
let binding = env.create_mutable_binding(n, true);
let index = self.get_or_insert_binding(binding);
self.emit_opcode(Opcode::PushUndefined);
self.emit_with_varying_operand(Opcode::DefInitVar, index);
@ -1096,7 +1112,7 @@ impl ByteCompiler<'_, '_> {
}
// d. Let varEnv be env.
instantiated_var_names
(instantiated_var_names, env)
};
// 29. NOTE: Annex B.3.2.1 adds additional steps at this point.
@ -1117,10 +1133,8 @@ impl ByteCompiler<'_, '_> {
// 2. If initializedBindings does not contain F and F is not "arguments", then
if !instantiated_var_names.contains(&f) && f != arguments {
// a. Perform ! varEnv.CreateMutableBinding(F, false).
self.create_mutable_binding(f, true);
// b. Perform ! varEnv.InitializeBinding(F, undefined).
let binding = self.initialize_mutable_binding(f, true);
let binding = var_env.create_mutable_binding(f, false);
let index = self.get_or_insert_binding(binding);
self.emit_opcode(Opcode::PushUndefined);
self.emit_with_varying_operand(Opcode::DefInitVar, index);
@ -1142,14 +1156,22 @@ impl ByteCompiler<'_, '_> {
}
// 30. If strict is false, then
// 30.a. Let lexEnv be NewDeclarativeEnvironment(varEnv).
// 30.b. NOTE: Non-strict functions use a separate Environment Record for top-level lexical
// declarations so that a direct eval can determine whether any var scoped declarations
// introduced by the eval code conflict with pre-existing top-level lexically scoped declarations.
// This is not needed for strict functions because a strict direct eval always
// places all declarations into a new Environment Record.
// 31. Else,
// a. Let lexEnv be varEnv.
let lex_env = if strict {
// a. Let lexEnv be varEnv.
var_env
} else {
// a. Let lexEnv be NewDeclarativeEnvironment(varEnv).
// b. NOTE: Non-strict functions use a separate Environment Record for top-level lexical
// declarations so that a direct eval can determine whether any var scoped declarations
// introduced by the eval code conflict with pre-existing top-level lexically scoped declarations.
// This is not needed for strict functions because a strict direct eval always
// places all declarations into a new Environment Record.
let env_index = self.push_compile_environment(false);
self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index);
self.lexical_environment.clone()
};
// 32. Set the LexicalEnvironment of calleeContext to lexEnv.
// 33. Let lexDeclarations be the LexicallyScopedDeclarations of code.
@ -1166,17 +1188,17 @@ impl ByteCompiler<'_, '_> {
match declaration {
Declaration::Class(class) => {
for name in bound_names(class) {
self.create_mutable_binding(name, false);
lex_env.create_mutable_binding(name, false);
}
}
Declaration::Lexical(LexicalDeclaration::Let(declaration)) => {
for name in bound_names(declaration) {
self.create_mutable_binding(name, false);
lex_env.create_mutable_binding(name, false);
}
}
Declaration::Lexical(LexicalDeclaration::Const(declaration)) => {
for name in bound_names(declaration) {
self.create_immutable_binding(name, true);
lex_env.create_immutable_binding(name, true);
}
}
_ => {}
@ -1194,6 +1216,5 @@ impl ByteCompiler<'_, '_> {
}
// 37. Return unused.
(env_label, additional_env)
}
}

110
boa_engine/src/bytecompiler/env.rs

@ -1,8 +1,6 @@
use std::rc::Rc;
use super::ByteCompiler;
use crate::environments::{BindingLocator, BindingLocatorError, CompileTimeEnvironment};
use boa_ast::expression::Identifier;
use crate::environments::CompileTimeEnvironment;
use std::rc::Rc;
impl ByteCompiler<'_, '_> {
/// Push either a new declarative or function environment on the compile time environment stack.
@ -10,111 +8,25 @@ impl ByteCompiler<'_, '_> {
pub(crate) fn push_compile_environment(&mut self, function_scope: bool) -> u32 {
self.current_open_environments_count += 1;
self.current_environment = Rc::new(CompileTimeEnvironment::new(
self.current_environment.clone(),
let env = Rc::new(CompileTimeEnvironment::new(
self.lexical_environment.clone(),
function_scope,
));
let index = self.compile_environments.len() as u32;
self.compile_environments
.push(self.current_environment.clone());
self.compile_environments.push(env.clone());
if function_scope {
self.variable_environment = env.clone();
}
self.lexical_environment = env;
index
}
/// 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) {
self.current_open_environments_count -= 1;
let outer = self
.current_environment
.outer()
.expect("cannot pop the global environment");
self.current_environment = outer;
}
/// Get the binding locator of the binding at bytecode compile time.
pub(crate) fn get_binding_value(&self, name: Identifier) -> BindingLocator {
self.current_environment.get_binding_recursive(name)
}
/// Return if a declarative binding exists at bytecode compile time.
/// This does not include bindings on the global object.
pub(crate) fn has_binding(&self, name: Identifier) -> bool {
self.current_environment.has_binding_recursive(name)
}
/// Check if a binding name exists in a environment.
/// If strict is `false` check until a function scope is reached.
pub(crate) fn has_binding_eval(&self, name: Identifier, strict: bool) -> bool {
self.current_environment.has_binding_eval(name, strict)
}
#[cfg(feature = "annex-b")]
/// Check if a binding name exists in a environment.
/// Stop when a function scope is reached.
pub(crate) fn has_binding_until_var(&self, name: Identifier) -> bool {
self.current_environment.has_binding_until_var(name)
}
/// Create a mutable binding at bytecode compile time.
/// This function returns a syntax error, if the binding is a redeclaration.
///
/// # Panics
///
/// Panics if the global environment is not function scoped.
pub(crate) fn create_mutable_binding(&mut self, name: Identifier, function_scope: bool) {
assert!(self
.current_environment
.create_mutable_binding(name, function_scope));
}
/// Initialize a mutable binding at bytecode compile time and return its binding locator.
pub(crate) fn initialize_mutable_binding(
&self,
name: Identifier,
function_scope: bool,
) -> BindingLocator {
self.current_environment
.initialize_mutable_binding(name, function_scope)
}
/// Create an immutable binding at bytecode compile time.
/// This function returns a syntax error, if the binding is a redeclaration.
///
/// # Panics
///
/// Panics if the global environment does not exist.
pub(crate) fn create_immutable_binding(&mut self, name: Identifier, strict: bool) {
self.current_environment
.create_immutable_binding(name, strict);
}
/// Initialize an immutable binding at bytecode compile time and return it's binding locator.
///
/// # Panics
///
/// Panics if the global environment does not exist or a the binding was not created on the current environment.
pub(crate) fn initialize_immutable_binding(&self, name: Identifier) -> BindingLocator {
self.current_environment.initialize_immutable_binding(name)
}
/// Return the binding locator for a set operation on an existing binding.
pub(crate) fn set_mutable_binding(
&self,
name: Identifier,
) -> Result<BindingLocator, BindingLocatorError> {
self.current_environment.set_mutable_binding_recursive(name)
}
#[cfg(feature = "annex-b")]
/// Return the binding locator for a set operation on an existing var binding.
pub(crate) fn set_mutable_binding_var(
&self,
name: Identifier,
) -> Result<BindingLocator, BindingLocatorError> {
self.current_environment
.set_mutable_binding_var_recursive(name)
}
}

11
boa_engine/src/bytecompiler/expression/assign.rs

@ -55,11 +55,10 @@ impl ByteCompiler<'_, '_> {
match access {
Access::Variable { name } => {
let binding = self.get_binding_value(name);
let index = self.get_or_insert_binding(binding);
let lex = self.current_environment.is_lex_binding(name);
let binding = self.lexical_environment.get_identifier_reference(name);
let index = self.get_or_insert_binding(binding.locator());
if lex {
if binding.is_lexical() {
self.emit_with_varying_operand(Opcode::GetName, index);
} else {
self.emit_with_varying_operand(Opcode::GetNameAndLocator, index);
@ -75,8 +74,8 @@ impl ByteCompiler<'_, '_> {
if use_expr {
self.emit_opcode(Opcode::Dup);
}
if lex {
match self.set_mutable_binding(name) {
if binding.is_lexical() {
match self.lexical_environment.set_mutable_binding(name) {
Ok(binding) => {
let index = self.get_or_insert_binding(binding);
self.emit_with_varying_operand(Opcode::SetName, index);

6
boa_engine/src/bytecompiler/expression/unary.rs

@ -27,8 +27,10 @@ impl ByteCompiler<'_, '_> {
UnaryOp::TypeOf => {
match unary.target().flatten() {
Expression::Identifier(identifier) => {
let binding = self.get_binding_value(*identifier);
let index = self.get_or_insert_binding(binding);
let binding = self
.lexical_environment
.get_identifier_reference(*identifier);
let index = self.get_or_insert_binding(binding.locator());
self.emit_with_varying_operand(Opcode::GetNameOrUndefined, index);
}
expr => self.compile_expr(expr, true),

11
boa_engine/src/bytecompiler/expression/update.rs

@ -23,11 +23,10 @@ impl ByteCompiler<'_, '_> {
match Access::from_update_target(update.target()) {
Access::Variable { name } => {
let binding = self.get_binding_value(name);
let index = self.get_or_insert_binding(binding);
let lex = self.current_environment.is_lex_binding(name);
let binding = self.lexical_environment.get_identifier_reference(name);
let index = self.get_or_insert_binding(binding.locator());
if lex {
if binding.is_lexical() {
self.emit_with_varying_operand(Opcode::GetName, index);
} else {
self.emit_with_varying_operand(Opcode::GetNameAndLocator, index);
@ -40,8 +39,8 @@ impl ByteCompiler<'_, '_> {
self.emit_opcode(Opcode::Dup);
}
if lex {
match self.set_mutable_binding(name) {
if binding.is_lexical() {
match self.lexical_environment.set_mutable_binding(name) {
Ok(binding) => {
let index = self.get_or_insert_binding(binding);
self.emit_with_varying_operand(Opcode::SetName, index);

33
boa_engine/src/bytecompiler/function.rs

@ -82,14 +82,22 @@ impl FunctionCompiler {
mut self,
parameters: &FormalParameterList,
body: &FunctionBody,
outer_env: Rc<CompileTimeEnvironment>,
variable_environment: Rc<CompileTimeEnvironment>,
lexical_environment: Rc<CompileTimeEnvironment>,
context: &mut Context<'_>,
) -> Gc<CodeBlock> {
self.strict = self.strict || body.strict();
let length = parameters.length();
let mut compiler = ByteCompiler::new(self.name, self.strict, false, outer_env, context);
let mut compiler = ByteCompiler::new(
self.name,
self.strict,
false,
variable_environment,
lexical_environment,
context,
);
compiler.length = length;
compiler.in_async = self.r#async;
compiler.in_generator = self.generator;
@ -101,7 +109,9 @@ impl FunctionCompiler {
if let Some(binding_identifier) = self.binding_identifier {
compiler.code_block_flags |= CodeBlockFlags::HAS_BINDING_IDENTIFIER;
let _ = compiler.push_compile_environment(false);
compiler.create_immutable_binding(binding_identifier.into(), self.strict);
compiler
.lexical_environment
.create_immutable_binding(binding_identifier.into(), self.strict);
}
// Function environment
@ -132,7 +142,7 @@ impl FunctionCompiler {
compiler.async_handler = Some(compiler.push_handler());
}
let (env_label, additional_env) = compiler.function_declaration_instantiation(
compiler.function_declaration_instantiation(
body,
parameters,
self.arrow,
@ -155,21 +165,6 @@ impl FunctionCompiler {
compiler.compile_statement_list(body.statements(), false, false);
if env_label {
compiler.pop_compile_environment();
}
if additional_env {
compiler.pop_compile_environment();
compiler.code_block_flags |= CodeBlockFlags::PARAMETERS_ENV_BINDINGS;
}
compiler.pop_compile_environment();
if self.binding_identifier.is_some() {
compiler.pop_compile_environment();
}
compiler.params = parameters.clone();
Gc::new(compiler.finish())

102
boa_engine/src/bytecompiler/mod.rs

@ -263,8 +263,11 @@ pub struct ByteCompiler<'ctx, 'host> {
/// Compile time environments in this function.
pub(crate) compile_environments: Vec<Rc<CompileTimeEnvironment>>,
/// The environment that is currently active.
pub(crate) current_environment: Rc<CompileTimeEnvironment>,
/// The current variable environment.
pub(crate) variable_environment: Rc<CompileTimeEnvironment>,
/// The current lexical environment.
pub(crate) lexical_environment: Rc<CompileTimeEnvironment>,
current_open_environments_count: u32,
current_stack_value_count: u32,
@ -301,7 +304,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
name: Sym,
strict: bool,
json_parse: bool,
current_environment: Rc<CompileTimeEnvironment>,
variable_environment: Rc<CompileTimeEnvironment>,
lexical_environment: Rc<CompileTimeEnvironment>,
// TODO: remove when we separate scripts from the context
context: &'ctx mut Context<'host>,
) -> ByteCompiler<'ctx, 'host> {
@ -332,7 +336,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
in_generator: false,
async_handler: None,
json_parse,
current_environment,
variable_environment,
lexical_environment,
context,
#[cfg(feature = "annex-b")]
@ -408,42 +413,29 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
fn emit_binding(&mut self, opcode: BindingOpcode, name: Identifier) {
match opcode {
BindingOpcode::Var => {
let binding = self.initialize_mutable_binding(name, true);
let index = self.get_or_insert_binding(binding);
let binding = self.variable_environment.get_identifier_reference(name);
let index = self.get_or_insert_binding(binding.locator());
self.emit_with_varying_operand(Opcode::DefVar, index);
}
BindingOpcode::InitVar => {
if self.has_binding(name) {
match self.set_mutable_binding(name) {
Ok(binding) => {
let index = self.get_or_insert_binding(binding);
self.emit_with_varying_operand(Opcode::DefInitVar, index);
}
Err(BindingLocatorError::MutateImmutable) => {
let index = self.get_or_insert_name(name);
self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index);
}
Err(BindingLocatorError::Silent) => {
self.emit_opcode(Opcode::Pop);
}
}
} else {
let binding = self.initialize_mutable_binding(name, true);
BindingOpcode::InitVar => match self.lexical_environment.set_mutable_binding(name) {
Ok(binding) => {
let index = self.get_or_insert_binding(binding);
self.emit_with_varying_operand(Opcode::DefInitVar, index);
};
}
BindingOpcode::InitLet => {
let binding = self.initialize_mutable_binding(name, false);
let index = self.get_or_insert_binding(binding);
self.emit_with_varying_operand(Opcode::PutLexicalValue, index);
}
BindingOpcode::InitConst => {
let binding = self.initialize_immutable_binding(name);
let index = self.get_or_insert_binding(binding);
}
Err(BindingLocatorError::MutateImmutable) => {
let index = self.get_or_insert_name(name);
self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index);
}
Err(BindingLocatorError::Silent) => {
self.emit_opcode(Opcode::Pop);
}
},
BindingOpcode::InitLexical => {
let binding = self.lexical_environment.get_identifier_reference(name);
let index = self.get_or_insert_binding(binding.locator());
self.emit_with_varying_operand(Opcode::PutLexicalValue, index);
}
BindingOpcode::SetName => match self.set_mutable_binding(name) {
BindingOpcode::SetName => match self.lexical_environment.set_mutable_binding(name) {
Ok(binding) => {
let index = self.get_or_insert_binding(binding);
self.emit_with_varying_operand(Opcode::SetName, index);
@ -698,8 +690,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
fn access_get(&mut self, access: Access<'_>, use_expr: bool) {
match access {
Access::Variable { name } => {
let binding = self.get_binding_value(name);
let index = self.get_or_insert_binding(binding);
let binding = self.lexical_environment.get_identifier_reference(name);
let index = self.get_or_insert_binding(binding.locator());
self.emit_with_varying_operand(Opcode::GetName, index);
}
Access::Property { access } => match access {
@ -763,11 +755,10 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
{
match access {
Access::Variable { name } => {
let binding = self.get_binding_value(name);
let index = self.get_or_insert_binding(binding);
let lex = self.current_environment.is_lex_binding(name);
let binding = self.lexical_environment.get_identifier_reference(name);
let index = self.get_or_insert_binding(binding.locator());
if !lex {
if !binding.is_lexical() {
self.emit_with_varying_operand(Opcode::GetLocator, index);
}
@ -776,8 +767,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
self.emit(Opcode::Dup, &[]);
}
if lex {
match self.set_mutable_binding(name) {
if binding.is_lexical() {
match self.lexical_environment.set_mutable_binding(name) {
Ok(binding) => {
let index = self.get_or_insert_binding(binding);
self.emit_with_varying_operand(Opcode::SetName, index);
@ -876,8 +867,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
}
},
Access::Variable { name } => {
let binding = self.get_binding_value(name);
let index = self.get_or_insert_binding(binding);
let binding = self.lexical_environment.get_identifier_reference(name);
let index = self.get_or_insert_binding(binding.locator());
self.emit_with_varying_operand(Opcode::DeleteName, index);
}
Access::This => {
@ -1130,7 +1121,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
} else {
self.emit_opcode(Opcode::PushUndefined);
}
self.emit_binding(BindingOpcode::InitLet, *ident);
self.emit_binding(BindingOpcode::InitLexical, *ident);
}
Binding::Pattern(pattern) => {
if let Some(init) = variable.init() {
@ -1139,7 +1130,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
self.emit_opcode(Opcode::PushUndefined);
};
self.compile_declaration_pattern(pattern, BindingOpcode::InitLet);
self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical);
}
}
}
@ -1152,7 +1143,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
.init()
.expect("const declaration must have initializer");
self.compile_expr(init, true);
self.emit_binding(BindingOpcode::InitConst, *ident);
self.emit_binding(BindingOpcode::InitLexical, *ident);
}
Binding::Pattern(pattern) => {
if let Some(init) = variable.init() {
@ -1161,7 +1152,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
self.emit_opcode(Opcode::PushUndefined);
};
self.compile_declaration_pattern(pattern, BindingOpcode::InitConst);
self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical);
}
}
}
@ -1189,11 +1180,11 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
.name()
.expect("function declaration must have name");
if self.annex_b_function_names.contains(&name) {
let binding = self.get_binding_value(name);
let index = self.get_or_insert_binding(binding);
let binding = self.lexical_environment.get_identifier_reference(name);
let index = self.get_or_insert_binding(binding.locator());
self.emit_with_varying_operand(Opcode::GetName, index);
match self.set_mutable_binding_var(name) {
match self.variable_environment.set_mutable_binding_var(name) {
Ok(binding) => {
let index = self.get_or_insert_binding(binding);
self.emit_with_varying_operand(Opcode::SetName, index);
@ -1250,7 +1241,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
.compile(
parameters,
body,
self.current_environment.clone(),
self.variable_environment.clone(),
self.lexical_environment.clone(),
self.context,
);
@ -1347,7 +1339,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
.compile(
parameters,
body,
self.current_environment.clone(),
self.variable_environment.clone(),
self.lexical_environment.clone(),
self.context,
);
@ -1410,7 +1403,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
.compile(
parameters,
body,
self.current_environment.clone(),
self.variable_environment.clone(),
self.lexical_environment.clone(),
self.context,
);

6
boa_engine/src/bytecompiler/module.rs

@ -46,14 +46,14 @@ impl ByteCompiler<'_, '_> {
self.class(cl, cl.name().is_none());
if cl.name().is_none() {
self.emit_binding(
BindingOpcode::InitLet,
BindingOpcode::InitLexical,
Identifier::from(Sym::DEFAULT_EXPORT),
);
}
}
ExportDeclaration::DefaultAssignmentExpression(expr) => {
let name = Identifier::from(Sym::DEFAULT_EXPORT);
self.create_mutable_binding(name, false);
self.lexical_environment.create_mutable_binding(name, false);
self.compile_expr(expr, true);
if expr.is_anonymous_function_definition() {
@ -66,7 +66,7 @@ impl ByteCompiler<'_, '_> {
self.emit(Opcode::SetFunctionName, &[Operand::U8(0)]);
}
self.emit_binding(BindingOpcode::InitLet, name);
self.emit_binding(BindingOpcode::InitLexical, name);
}
}
}

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

@ -4,13 +4,16 @@ use boa_ast::statement::Block;
impl ByteCompiler<'_, '_> {
/// Compile a [`Block`] `boa_ast` node
pub(crate) fn compile_block(&mut self, block: &Block, use_expr: bool) {
let old_lex_env = self.lexical_environment.clone();
let env_index = self.push_compile_environment(false);
self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index);
let env = self.lexical_environment.clone();
self.block_declaration_instantiation(block);
self.block_declaration_instantiation(block, &env);
self.compile_statement_list(block.statement_list(), use_expr, true);
self.pop_compile_environment();
self.lexical_environment = old_lex_env;
self.emit_opcode(Opcode::PopEnvironment);
}
}

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

@ -22,7 +22,7 @@ impl ByteCompiler<'_, '_> {
use_expr: bool,
) {
let mut let_binding_indices = None;
let mut has_lexical_environment_binding = false;
let mut old_lex_env = None;
if let Some(init) = for_loop.init() {
match init {
@ -31,20 +31,22 @@ impl ByteCompiler<'_, '_> {
self.compile_var_decl(decl);
}
ForLoopInitializer::Lexical(decl) => {
old_lex_env = Some(self.lexical_environment.clone());
let env_index = self.push_compile_environment(false);
self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index);
has_lexical_environment_binding = true;
let names = bound_names(decl);
if decl.is_const() {
for name in &names {
self.create_immutable_binding(*name, true);
self.lexical_environment
.create_immutable_binding(*name, true);
}
} else {
let mut indices = Vec::new();
for name in &names {
self.create_mutable_binding(*name, false);
let binding = self.initialize_mutable_binding(*name, false);
let binding = self
.lexical_environment
.create_mutable_binding(*name, false);
let index = self.get_or_insert_binding(binding);
indices.push(index);
}
@ -101,8 +103,9 @@ impl ByteCompiler<'_, '_> {
self.patch_jump(exit);
self.pop_loop_control_info();
if has_lexical_environment_binding {
if let Some(old_lex_env) = old_lex_env {
self.pop_compile_environment();
self.lexical_environment = old_lex_env;
self.emit_opcode(Opcode::PopEnvironment);
}
}
@ -130,15 +133,18 @@ impl ByteCompiler<'_, '_> {
if initializer_bound_names.is_empty() {
self.compile_expr(for_in_loop.target(), true);
} else {
let old_lex_env = self.lexical_environment.clone();
let env_index = self.push_compile_environment(false);
self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index);
for name in &initializer_bound_names {
self.create_mutable_binding(*name, false);
self.lexical_environment
.create_mutable_binding(*name, false);
}
self.compile_expr(for_in_loop.target(), true);
self.pop_compile_environment();
self.lexical_environment = old_lex_env;
self.emit_opcode(Opcode::PopEnvironment);
}
@ -155,7 +161,10 @@ impl ByteCompiler<'_, '_> {
self.emit_opcode(Opcode::IteratorValue);
let mut old_lex_env = None;
if !initializer_bound_names.is_empty() {
old_lex_env = Some(self.lexical_environment.clone());
let env_index = self.push_compile_environment(false);
self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index);
}
@ -181,26 +190,30 @@ impl ByteCompiler<'_, '_> {
},
IterableLoopInitializer::Let(declaration) => match declaration {
Binding::Identifier(ident) => {
self.create_mutable_binding(*ident, false);
self.emit_binding(BindingOpcode::InitLet, *ident);
self.lexical_environment
.create_mutable_binding(*ident, false);
self.emit_binding(BindingOpcode::InitLexical, *ident);
}
Binding::Pattern(pattern) => {
for ident in bound_names(pattern) {
self.create_mutable_binding(ident, false);
self.lexical_environment
.create_mutable_binding(ident, false);
}
self.compile_declaration_pattern(pattern, BindingOpcode::InitLet);
self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical);
}
},
IterableLoopInitializer::Const(declaration) => match declaration {
Binding::Identifier(ident) => {
self.create_immutable_binding(*ident, true);
self.emit_binding(BindingOpcode::InitConst, *ident);
self.lexical_environment
.create_immutable_binding(*ident, true);
self.emit_binding(BindingOpcode::InitLexical, *ident);
}
Binding::Pattern(pattern) => {
for ident in bound_names(pattern) {
self.create_immutable_binding(ident, true);
self.lexical_environment
.create_immutable_binding(ident, true);
}
self.compile_declaration_pattern(pattern, BindingOpcode::InitConst);
self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical);
}
},
IterableLoopInitializer::Pattern(pattern) => {
@ -210,8 +223,9 @@ impl ByteCompiler<'_, '_> {
self.compile_stmt(for_in_loop.body(), use_expr, true);
if !initializer_bound_names.is_empty() {
if let Some(old_lex_env) = old_lex_env {
self.pop_compile_environment();
self.lexical_environment = old_lex_env;
self.emit_opcode(Opcode::PopEnvironment);
}
@ -242,15 +256,18 @@ impl ByteCompiler<'_, '_> {
if initializer_bound_names.is_empty() {
self.compile_expr(for_of_loop.iterable(), true);
} else {
let old_lex_env = self.lexical_environment.clone();
let env_index = self.push_compile_environment(false);
self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index);
for name in &initializer_bound_names {
self.create_mutable_binding(*name, false);
self.lexical_environment
.create_mutable_binding(*name, false);
}
self.compile_expr(for_of_loop.iterable(), true);
self.pop_compile_environment();
self.lexical_environment = old_lex_env;
self.emit_opcode(Opcode::PopEnvironment);
}
@ -279,7 +296,10 @@ impl ByteCompiler<'_, '_> {
let exit = self.jump_if_true();
self.emit_opcode(Opcode::IteratorValue);
let mut old_lex_env = None;
if !initializer_bound_names.is_empty() {
old_lex_env = Some(self.lexical_environment.clone());
let env_index = self.push_compile_environment(false);
self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index);
};
@ -287,7 +307,7 @@ impl ByteCompiler<'_, '_> {
let mut handler_index = None;
match for_of_loop.initializer() {
IterableLoopInitializer::Identifier(ref ident) => {
match self.set_mutable_binding(*ident) {
match self.lexical_environment.set_mutable_binding(*ident) {
Ok(binding) => {
let index = self.get_or_insert_binding(binding);
self.emit_with_varying_operand(Opcode::DefInitVar, index);
@ -323,26 +343,30 @@ impl ByteCompiler<'_, '_> {
}
IterableLoopInitializer::Let(declaration) => match declaration {
Binding::Identifier(ident) => {
self.create_mutable_binding(*ident, false);
self.emit_binding(BindingOpcode::InitLet, *ident);
self.lexical_environment
.create_mutable_binding(*ident, false);
self.emit_binding(BindingOpcode::InitLexical, *ident);
}
Binding::Pattern(pattern) => {
for ident in bound_names(pattern) {
self.create_mutable_binding(ident, false);
self.lexical_environment
.create_mutable_binding(ident, false);
}
self.compile_declaration_pattern(pattern, BindingOpcode::InitLet);
self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical);
}
},
IterableLoopInitializer::Const(declaration) => match declaration {
Binding::Identifier(ident) => {
self.create_immutable_binding(*ident, true);
self.emit_binding(BindingOpcode::InitConst, *ident);
self.lexical_environment
.create_immutable_binding(*ident, true);
self.emit_binding(BindingOpcode::InitLexical, *ident);
}
Binding::Pattern(pattern) => {
for ident in bound_names(pattern) {
self.create_immutable_binding(ident, true);
self.lexical_environment
.create_immutable_binding(ident, true);
}
self.compile_declaration_pattern(pattern, BindingOpcode::InitConst);
self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical);
}
},
IterableLoopInitializer::Pattern(pattern) => {
@ -375,8 +399,9 @@ impl ByteCompiler<'_, '_> {
self.compile_stmt(for_of_loop.body(), use_expr, true);
if !initializer_bound_names.is_empty() {
if let Some(old_lex_env) = old_lex_env {
self.pop_compile_environment();
self.lexical_environment = old_lex_env;
self.emit_opcode(Opcode::PopEnvironment);
}

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

@ -6,10 +6,12 @@ impl ByteCompiler<'_, '_> {
pub(crate) fn compile_switch(&mut self, switch: &Switch, use_expr: bool) {
self.compile_expr(switch.val(), true);
let old_lex_env = self.lexical_environment.clone();
let env_index = self.push_compile_environment(false);
self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index);
let env = self.lexical_environment.clone();
self.block_declaration_instantiation(switch);
self.block_declaration_instantiation(switch, &env);
let start_address = self.next_opcode_location();
self.push_switch_control_info(None, start_address, use_expr);
@ -51,6 +53,7 @@ impl ByteCompiler<'_, '_> {
self.pop_switch_control_info();
self.pop_compile_environment();
self.lexical_environment = old_lex_env;
self.emit_opcode(Opcode::PopEnvironment);
}
}

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

@ -111,20 +111,22 @@ impl ByteCompiler<'_, '_> {
pub(crate) fn compile_catch_stmt(&mut self, catch: &Catch, _has_finally: bool, use_expr: bool) {
// stack: exception
let old_lex_env = self.lexical_environment.clone();
let env_index = self.push_compile_environment(false);
self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index);
let env = self.lexical_environment.clone();
if let Some(binding) = catch.parameter() {
match binding {
Binding::Identifier(ident) => {
self.create_mutable_binding(*ident, false);
self.emit_binding(BindingOpcode::InitLet, *ident);
env.create_mutable_binding(*ident, false);
self.emit_binding(BindingOpcode::InitLexical, *ident);
}
Binding::Pattern(pattern) => {
for ident in bound_names(pattern) {
self.create_mutable_binding(ident, false);
env.create_mutable_binding(ident, false);
}
self.compile_declaration_pattern(pattern, BindingOpcode::InitLet);
self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical);
}
}
} else {
@ -134,6 +136,7 @@ impl ByteCompiler<'_, '_> {
self.compile_catch_finally_block(catch.block(), use_expr);
self.pop_compile_environment();
self.lexical_environment = old_lex_env;
self.emit_opcode(Opcode::PopEnvironment);
}

2
boa_engine/src/bytecompiler/statement/with.rs

@ -6,12 +6,14 @@ impl ByteCompiler<'_, '_> {
pub(crate) fn compile_with(&mut self, with: &With, use_expr: bool) {
self.compile_expr(with.expression(), true);
let old_lex_env = self.lexical_environment.clone();
let _ = self.push_compile_environment(false);
self.emit_opcode(Opcode::PushObjectEnvironment);
self.compile_stmt(with.statement(), use_expr, true);
self.pop_compile_environment();
self.lexical_environment = old_lex_env;
self.emit_opcode(Opcode::PopEnvironment);
}
}

207
boa_engine/src/environments/compile.rs

@ -65,20 +65,23 @@ impl CompileTimeEnvironment {
.map_or(false, |binding| binding.lex)
}
#[cfg(feature = "annex-b")]
/// Check if the environment has a binding with the given name.
pub(crate) fn has_binding(&self, name: Identifier) -> bool {
self.bindings.borrow().contains_key(&name)
}
/// Checks if `name` is a lexical binding.
pub(crate) fn is_lex_binding(&self, name: Identifier) -> bool {
/// Get the binding locator for a binding with the given name.
/// Fall back to the global environment if the binding is not found.
pub(crate) fn get_identifier_reference(&self, name: Identifier) -> IdentifierReference {
if let Some(binding) = self.bindings.borrow().get(&name) {
binding.lex
IdentifierReference::new(
BindingLocator::declarative(name, self.environment_index, binding.index),
binding.lex,
)
} else if let Some(outer) = &self.outer {
outer.is_lex_binding(name)
outer.get_identifier_reference(name)
} else {
false
IdentifierReference::new(BindingLocator::global(name), false)
}
}
@ -87,11 +90,21 @@ impl CompileTimeEnvironment {
self.bindings.borrow().len() as u32
}
/// Returns the index of this environment.
pub(crate) fn environment_index(&self) -> u32 {
self.environment_index
}
/// Check if the environment is a function environment.
pub(crate) const fn is_function(&self) -> bool {
self.function_scope
}
/// Check if the environment is a global environment.
pub(crate) const fn is_global(&self) -> bool {
self.outer.is_none()
}
/// Get the locator for a binding name.
pub(crate) fn get_binding(&self, name: Identifier) -> Option<BindingLocator> {
self.bindings
@ -100,105 +113,31 @@ impl CompileTimeEnvironment {
.map(|binding| BindingLocator::declarative(name, self.environment_index, binding.index))
}
/// Get the locator for a binding name in this and all outer environments.
pub(crate) fn get_binding_recursive(&self, name: Identifier) -> BindingLocator {
if let Some(binding) = self.bindings.borrow().get(&name) {
BindingLocator::declarative(name, self.environment_index, binding.index)
} else if let Some(outer) = &self.outer {
outer.get_binding_recursive(name)
} else {
BindingLocator::global(name)
}
}
/// Check if a binding name exists in this and all outer environments.
pub(crate) fn has_binding_recursive(&self, name: Identifier) -> bool {
if self.bindings.borrow().contains_key(&name) {
true
} else if let Some(outer) = &self.outer {
outer.has_binding_recursive(name)
} else {
false
}
}
/// Check if a binding name exists in a environment.
/// If strict is `false` check until a function scope is reached.
pub(crate) fn has_binding_eval(&self, name: Identifier, strict: bool) -> bool {
let exists = self.bindings.borrow().contains_key(&name);
if exists || strict {
return exists;
}
if self.function_scope {
return false;
}
if let Some(outer) = &self.outer {
outer.has_binding_eval(name, false)
} else {
false
}
}
#[cfg(feature = "annex-b")]
/// Check if a binding name exists in a environment.
/// Stop when a function scope is reached.
pub(crate) fn has_binding_until_var(&self, name: Identifier) -> bool {
if self.function_scope {
return false;
}
if self.bindings.borrow().contains_key(&name) {
return true;
}
if let Some(outer) = &self.outer {
outer.has_binding_until_var(name)
} else {
false
}
}
/// Create a mutable binding.
///
/// If the binding is a function scope binding and this is a declarative environment, try the outer environment.
pub(crate) fn create_mutable_binding(&self, name: Identifier, function_scope: bool) -> bool {
if let Some(outer) = &self.outer {
if !function_scope || self.function_scope {
if !self.bindings.borrow().contains_key(&name) {
let binding_index = self.bindings.borrow().len() as u32;
self.bindings.borrow_mut().insert(
name,
CompileTimeBinding {
index: binding_index,
mutable: true,
lex: !function_scope,
strict: false,
},
);
}
true
} else {
outer.create_mutable_binding(name, function_scope)
}
} else if function_scope {
false
} else {
if !self.bindings.borrow().contains_key(&name) {
let binding_index = self.bindings.borrow().len() as u32;
self.bindings.borrow_mut().insert(
name,
CompileTimeBinding {
index: binding_index,
mutable: true,
lex: !function_scope,
strict: false,
},
);
}
true
}
pub(crate) fn create_mutable_binding(
&self,
name: Identifier,
function_scope: bool,
) -> BindingLocator {
let binding_index = self.bindings.borrow().len() as u32;
self.bindings.borrow_mut().insert(
name,
CompileTimeBinding {
index: binding_index,
mutable: true,
lex: !function_scope,
strict: false,
},
);
BindingLocator::declarative(name, self.environment_index, binding_index)
}
/// Crate an immutable binding.
pub(crate) fn create_immutable_binding(&self, name: Identifier, strict: bool) {
pub(crate) fn create_immutable_binding(
&self,
name: Identifier,
strict: bool,
) -> BindingLocator {
let binding_index = self.bindings.borrow().len() as u32;
self.bindings.borrow_mut().insert(
name,
@ -209,42 +148,11 @@ impl CompileTimeEnvironment {
strict,
},
);
}
/// Return the binding locator for a mutable binding with the given binding name and scope.
pub(crate) fn initialize_mutable_binding(
&self,
name: Identifier,
function_scope: bool,
) -> BindingLocator {
if let Some(outer) = &self.outer {
if function_scope && !self.function_scope {
return outer.initialize_mutable_binding(name, function_scope);
}
self.bindings.borrow().get(&name).map_or_else(
|| outer.initialize_mutable_binding(name, function_scope),
|binding| BindingLocator::declarative(name, self.environment_index, binding.index),
)
} else if let Some(binding) = self.bindings.borrow().get(&name) {
BindingLocator::declarative(name, self.environment_index, binding.index)
} else {
BindingLocator::global(name)
}
}
/// Return the binding locator for an immutable binding.
///
/// # Panics
///
/// Panics if the binding is not in the current environment.
pub(crate) fn initialize_immutable_binding(&self, name: Identifier) -> BindingLocator {
let bindings = self.bindings.borrow();
let binding = bindings.get(&name).expect("binding must exist");
BindingLocator::declarative(name, self.environment_index, binding.index)
BindingLocator::declarative(name, self.environment_index, binding_index)
}
/// Return the binding locator for a mutable binding.
pub(crate) fn set_mutable_binding_recursive(
pub(crate) fn set_mutable_binding(
&self,
name: Identifier,
) -> Result<BindingLocator, BindingLocatorError> {
@ -256,21 +164,21 @@ impl CompileTimeEnvironment {
Some(_) => return Err(BindingLocatorError::Silent),
None => self.outer.as_ref().map_or_else(
|| Ok(BindingLocator::global(name)),
|outer| outer.set_mutable_binding_recursive(name),
|outer| outer.set_mutable_binding(name),
)?,
})
}
#[cfg(feature = "annex-b")]
/// Return the binding locator for a set operation on an existing var binding.
pub(crate) fn set_mutable_binding_var_recursive(
pub(crate) fn set_mutable_binding_var(
&self,
name: Identifier,
) -> Result<BindingLocator, BindingLocatorError> {
if !self.is_function() {
return self.outer.as_ref().map_or_else(
|| Ok(BindingLocator::global(name)),
|outer| outer.set_mutable_binding_var_recursive(name),
|outer| outer.set_mutable_binding_var(name),
);
}
@ -282,7 +190,7 @@ impl CompileTimeEnvironment {
Some(_) => return Err(BindingLocatorError::Silent),
None => self.outer.as_ref().map_or_else(
|| Ok(BindingLocator::global(name)),
|outer| outer.set_mutable_binding_var_recursive(name),
|outer| outer.set_mutable_binding_var(name),
)?,
})
}
@ -292,3 +200,26 @@ impl CompileTimeEnvironment {
self.outer.clone()
}
}
/// A reference to an identifier in a compile time environment.
pub(crate) struct IdentifierReference {
locator: BindingLocator,
lexical: bool,
}
impl IdentifierReference {
/// Create a new identifier reference.
pub(crate) fn new(locator: BindingLocator, lexical: bool) -> Self {
Self { locator, lexical }
}
/// Get the binding locator for this identifier reference.
pub(crate) fn locator(&self) -> BindingLocator {
self.locator
}
/// Check if this identifier reference is lexical.
pub(crate) fn is_lexical(&self) -> bool {
self.lexical
}
}

11
boa_engine/src/environments/runtime/declarative/mod.rs

@ -127,6 +127,17 @@ impl DeclarativeEnvironment {
pub(crate) fn poison(&self) {
self.kind.poison();
}
/// Extends the environment with the bindings from the compile time environment.
pub(crate) fn extend_from_compile(&self) {
if let Some(env) = self.kind().as_function() {
let compile_bindings_number = self.compile_env().num_bindings() as usize;
let mut bindings = env.poisonable_environment().bindings().borrow_mut();
if compile_bindings_number > bindings.len() {
bindings.resize(compile_bindings_number, None);
}
}
}
}
/// The kind of the declarative environment.

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

@ -7,7 +7,6 @@ use crate::{
};
use boa_ast::expression::Identifier;
use boa_gc::{empty_trace, Finalize, Gc, Trace};
use rustc_hash::FxHashSet;
mod declarative;
mod private;
@ -90,69 +89,19 @@ impl EnvironmentStack {
}
}
/// Extends the length of the next outer function environment to the number of compiled bindings.
///
/// This is only useful when compiled bindings are added after the initial compilation (eval).
pub(crate) fn extend_outer_function_environment(&mut self) {
for env in self
.stack
.iter()
.filter_map(Environment::as_declarative)
.rev()
{
if let DeclarativeEnvironmentKind::Function(fun) = &env.kind() {
let compile_bindings_number = env.compile_env().num_bindings() as usize;
let mut bindings = fun.poisonable_environment().bindings().borrow_mut();
if compile_bindings_number > bindings.len() {
bindings.resize(compile_bindings_number, None);
}
break;
}
}
}
/// Check if any of the provided binding names are defined as lexical bindings.
///
/// Start at the current environment.
/// Stop at the next outer function environment.
pub(crate) fn has_lex_binding_until_function_environment(
&self,
names: &FxHashSet<Identifier>,
) -> Option<Identifier> {
/// Gets the next outer function environment.
pub(crate) fn outer_function_environment(&self) -> Gc<DeclarativeEnvironment> {
for env in self
.stack
.iter()
.filter_map(Environment::as_declarative)
.rev()
{
let compile = env.compile_env();
for name in names {
if compile.has_lex_binding(*name) {
return Some(*name);
}
}
if compile.is_function() {
break;
}
}
None
}
/// Check if the next outer function environment is the global environment.
pub(crate) fn is_next_outer_function_environment_global(&self) -> bool {
for env in self
.stack
.iter()
.rev()
.filter_map(Environment::as_declarative)
{
let compile = env.compile_env();
if compile.is_function() {
return compile.outer().is_none();
if let DeclarativeEnvironmentKind::Function(_) = &env.kind() {
return env.clone();
}
}
true
self.global()
}
/// Pop all current environments except the global environment.
@ -527,6 +476,7 @@ impl BindingLocator {
}
/// Action that is returned when a fallible binding operation.
#[derive(Debug)]
pub(crate) enum BindingLocatorError {
/// Trying to mutate immutable binding,
MutateImmutable,

36
boa_engine/src/module/source.rs

@ -1410,10 +1410,10 @@ impl SourceTextModule {
// 6. Set module.[[Environment]] to env.
let global_env = realm.environment().clone();
let global_compile_env = global_env.compile_env();
let module_compile_env = Rc::new(CompileTimeEnvironment::new(global_compile_env, true));
let env = Rc::new(CompileTimeEnvironment::new(global_compile_env, true));
let mut compiler =
ByteCompiler::new(Sym::MAIN, true, false, module_compile_env.clone(), context);
ByteCompiler::new(Sym::MAIN, true, false, env.clone(), env.clone(), context);
compiler.in_async = true;
compiler.async_handler = Some(compiler.push_handler());
@ -1449,8 +1449,7 @@ impl SourceTextModule {
// 2. Perform ! env.CreateImmutableBinding(in.[[LocalName]], true).
// 3. Perform ! env.InitializeBinding(in.[[LocalName]], namespace).
compiler.create_immutable_binding(entry.local_name(), true);
let locator = compiler.initialize_immutable_binding(entry.local_name());
let locator = env.create_immutable_binding(entry.local_name(), true);
if let BindingName::Name(_) = resolution.binding_name {
// 1. Perform env.CreateImportBinding(in.[[LocalName]], resolution.[[Module]],
@ -1471,9 +1470,8 @@ impl SourceTextModule {
} else {
// b. If in.[[ImportName]] is namespace-object, then
// ii. Perform ! env.CreateImmutableBinding(in.[[LocalName]], true).
compiler.create_immutable_binding(entry.local_name(), true);
// iii. Perform ! env.InitializeBinding(in.[[LocalName]], namespace).
let locator = compiler.initialize_immutable_binding(entry.local_name());
let locator = env.create_immutable_binding(entry.local_name(), true);
// i. Let namespace be GetModuleNamespace(importedModule).
// deferred to initialization below
@ -1496,12 +1494,12 @@ impl SourceTextModule {
// i. If declaredVarNames does not contain dn, then
if !declared_var_names.contains(&name) {
// 1. Perform ! env.CreateMutableBinding(dn, false).
compiler.create_mutable_binding(name, false);
// 2. Perform ! env.InitializeBinding(dn, undefined).
let binding = compiler.initialize_mutable_binding(name, false);
let binding = env.create_mutable_binding(name, false);
let index = compiler.get_or_insert_binding(binding);
compiler.emit_opcode(Opcode::PushUndefined);
compiler.emit_with_varying_operand(Opcode::DefInitVar, index);
// 3. Append dn to declaredVarNames.
declared_var_names.push(name);
}
@ -1527,35 +1525,31 @@ impl SourceTextModule {
let (spec, locator): (FunctionSpec<'_>, _) = match declaration {
LexicallyScopedDeclaration::Function(f) => {
let name = bound_names(f)[0];
compiler.create_mutable_binding(name, false);
let locator = compiler.initialize_mutable_binding(name, false);
let locator = env.create_mutable_binding(name, false);
(f.into(), locator)
}
LexicallyScopedDeclaration::Generator(g) => {
let name = bound_names(g)[0];
compiler.create_mutable_binding(name, false);
let locator = compiler.initialize_mutable_binding(name, false);
let locator = env.create_mutable_binding(name, false);
(g.into(), locator)
}
LexicallyScopedDeclaration::AsyncFunction(af) => {
let name = bound_names(af)[0];
compiler.create_mutable_binding(name, false);
let locator = compiler.initialize_mutable_binding(name, false);
let locator = env.create_mutable_binding(name, false);
(af.into(), locator)
}
LexicallyScopedDeclaration::AsyncGenerator(ag) => {
let name = bound_names(ag)[0];
compiler.create_mutable_binding(name, false);
let locator = compiler.initialize_mutable_binding(name, false);
let locator = env.create_mutable_binding(name, false);
(ag.into(), locator)
}
LexicallyScopedDeclaration::Class(class) => {
for name in bound_names(class) {
compiler.create_mutable_binding(name, false);
env.create_mutable_binding(name, false);
}
continue;
}
@ -1566,19 +1560,19 @@ impl SourceTextModule {
// a. For each element dn of the BoundNames of d, do
for name in bound_names(c) {
// 1. Perform ! env.CreateImmutableBinding(dn, true).
compiler.create_immutable_binding(name, true);
env.create_immutable_binding(name, true);
}
continue;
}
LexicallyScopedDeclaration::LexicalDeclaration(LexicalDeclaration::Let(l)) => {
for name in bound_names(l) {
compiler.create_mutable_binding(name, false);
env.create_mutable_binding(name, false);
}
continue;
}
LexicallyScopedDeclaration::AssignmentExpression(expr) => {
for name in bound_names(expr) {
compiler.create_mutable_binding(name, false);
env.create_mutable_binding(name, false);
}
continue;
}
@ -1605,7 +1599,7 @@ impl SourceTextModule {
// 8. Let moduleContext be a new ECMAScript code execution context.
let mut envs = EnvironmentStack::new(global_env);
envs.push_module(module_compile_env);
envs.push_module(env);
// 9. Set the Function of moduleContext to null.
// 12. Set the ScriptOrModule of moduleContext to module.

6
boa_engine/src/script.rs

@ -113,10 +113,14 @@ impl Script {
self.inner.source.strict(),
false,
self.inner.realm.environment().compile_env(),
self.inner.realm.environment().compile_env(),
context,
);
// TODO: move to `Script::evaluate` to make this operation infallible.
compiler.global_declaration_instantiation(&self.inner.source)?;
compiler.global_declaration_instantiation(
&self.inner.source,
&self.inner.realm.environment().compile_env(),
)?;
compiler.compile_statement_list(self.inner.source.statements(), true, false);
let cb = Gc::new(compiler.finish());

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

@ -66,9 +66,7 @@ impl DefInitVar {
fn operation(context: &mut Context<'_>, index: usize) -> JsResult<CompletionType> {
let value = context.vm.pop();
let mut binding_locator = context.vm.frame().code_block.bindings[index];
context.find_runtime_binding(&mut binding_locator)?;
context.set_binding(
binding_locator,
value,

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

@ -2182,8 +2182,7 @@ generate_opcodes! {
pub(crate) enum BindingOpcode {
Var,
InitVar,
InitLet,
InitConst,
InitLexical,
SetName,
}

Loading…
Cancel
Save