diff --git a/Cargo.lock b/Cargo.lock index 9ed25a9bc1c..dbfdb04164c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -415,7 +415,6 @@ version = "0.17.0" dependencies = [ "boa_engine", "boa_gc", - "boa_interner", "boa_parser", "boa_runtime", "clap", diff --git a/boa_cli/Cargo.toml b/boa_cli/Cargo.toml index 7c1e34b3c3b..c4f6880a779 100644 --- a/boa_cli/Cargo.toml +++ b/boa_cli/Cargo.toml @@ -15,7 +15,6 @@ rust-version.workspace = true boa_engine = { workspace = true, features = ["deser", "flowgraph", "trace"] } boa_parser.workspace = true boa_gc.workspace = true -boa_interner.workspace = true boa_runtime.workspace = true rustyline = { version = "12.0.0", features = ["derive"]} clap = { workspace = true, features = ["derive"] } diff --git a/boa_cli/src/debug/function.rs b/boa_cli/src/debug/function.rs index 2f3212e8112..4f8769ea3e0 100644 --- a/boa_cli/src/debug/function.rs +++ b/boa_cli/src/debug/function.rs @@ -4,7 +4,6 @@ use boa_engine::{ vm::flowgraph::{Direction, Graph}, Context, JsArgs, JsNativeError, JsObject, JsResult, JsValue, NativeFunction, }; -use boa_interner::ToInternedString; use crate::FlowgraphFormat; @@ -92,7 +91,7 @@ fn flowgraph(_this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResu let code = function.codeblock(); let mut graph = Graph::new(direction); - code.to_graph(context.interner(), graph.subgraph(String::default())); + code.to_graph(graph.subgraph(String::default())); let result = match format { FlowgraphFormat::Graphviz => graph.to_graphviz_format(), FlowgraphFormat::Mermaid => graph.to_mermaid_format(), @@ -101,7 +100,7 @@ fn flowgraph(_this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResu Ok(JsValue::new(js_string!(result))) } -fn bytecode(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { +fn bytecode(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { let Some(value) = args.get(0) else { return Err(JsNativeError::typ() .with_message("expected function argument") @@ -121,7 +120,7 @@ fn bytecode(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult JsResult<()> { diff --git a/boa_cli/src/main.rs b/boa_cli/src/main.rs index f2fa0c5f9ce..3c6a796fc01 100644 --- a/boa_cli/src/main.rs +++ b/boa_cli/src/main.rs @@ -241,7 +241,7 @@ fn generate_flowgraph( }; let mut graph = Graph::new(direction); - code.to_graph(context.interner(), graph.subgraph(String::default())); + code.to_graph(graph.subgraph(String::default())); let result = match format { FlowgraphFormat::Graphviz => graph.to_graphviz_format(), FlowgraphFormat::Mermaid => graph.to_mermaid_format(), diff --git a/boa_engine/src/builtins/eval/mod.rs b/boa_engine/src/builtins/eval/mod.rs index 3a239f3be3e..d0d714c1796 100644 --- a/boa_engine/src/builtins/eval/mod.rs +++ b/boa_engine/src/builtins/eval/mod.rs @@ -11,7 +11,7 @@ use crate::{ builtins::BuiltInObject, - bytecompiler::ByteCompiler, + bytecompiler::{ByteCompiler, ToJsString}, context::intrinsics::Intrinsics, environments::Environment, error::JsNativeError, @@ -228,7 +228,7 @@ impl Eval { let mut var_env = var_environment.compile_env(); let mut compiler = ByteCompiler::new( - Sym::MAIN, + Sym::MAIN.to_js_string(context.interner()), body.strict(), false, var_env.clone(), diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index 7b12288f168..3e8b84f2cfd 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -13,7 +13,7 @@ use crate::{ builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, - bytecompiler::FunctionCompiler, + bytecompiler::{FunctionCompiler, ToJsString}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, environments::{EnvironmentStack, PrivateEnvironment}, error::JsNativeError, @@ -557,7 +557,7 @@ impl BuiltInFunctionObject { }; let code = FunctionCompiler::new() - .name(Sym::ANONYMOUS) + .name(Sym::ANONYMOUS.to_js_string(context.interner())) .generator(generator) .r#async(r#async) .compile( diff --git a/boa_engine/src/builtins/json/mod.rs b/boa_engine/src/builtins/json/mod.rs index 5902987a6e5..4f466009d11 100644 --- a/boa_engine/src/builtins/json/mod.rs +++ b/boa_engine/src/builtins/json/mod.rs @@ -19,7 +19,7 @@ use itertools::Itertools; use crate::{ builtins::BuiltInObject, - bytecompiler::ByteCompiler, + bytecompiler::{ByteCompiler, ToJsString}, context::intrinsics::Intrinsics, error::JsNativeError, js_string, @@ -114,7 +114,7 @@ impl Json { let script = parser.parse_script(context.interner_mut())?; let code_block = { let mut compiler = ByteCompiler::new( - Sym::MAIN, + Sym::MAIN.to_js_string(context.interner()), script.strict(), true, context.realm().environment().compile_env(), diff --git a/boa_engine/src/bytecompiler/class.rs b/boa_engine/src/bytecompiler/class.rs index 10e5d1f4292..27e134595b2 100644 --- a/boa_engine/src/bytecompiler/class.rs +++ b/boa_engine/src/bytecompiler/class.rs @@ -1,5 +1,8 @@ -use super::{ByteCompiler, Literal, Operand}; -use crate::vm::{BindingOpcode, CodeBlock, CodeBlockFlags, Opcode}; +use super::{ByteCompiler, Literal, Operand, ToJsString}; +use crate::{ + js_string, + vm::{BindingOpcode, CodeBlock, CodeBlockFlags, Opcode}, +}; use boa_ast::{ expression::Identifier, function::{Class, ClassElement, FormalParameterList}, @@ -29,22 +32,24 @@ impl ByteCompiler<'_> { let strict = self.strict(); self.code_block_flags |= CodeBlockFlags::STRICT; - let class_name = class.name().map_or(Sym::EMPTY_STRING, Identifier::sym); - - 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.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); - self.lexical_environment - .create_immutable_binding(name, true); - Some(old_lex_env) - } - _ => None, + let class_name = class + .name() + .map_or(Sym::EMPTY_STRING, Identifier::sym) + .to_js_string(self.interner()); + + let old_lex_env = if class.has_binding_identifier() { + 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); + self.lexical_environment + .create_immutable_binding(class_name.clone(), true); + Some(old_lex_env) + } else { + None }; let mut compiler = ByteCompiler::new( - class_name, + class_name.clone(), true, self.json_parse, self.variable_environment.clone(), @@ -122,7 +127,7 @@ impl ByteCompiler<'_> { if old_lex_env.is_some() { self.emit_opcode(Opcode::Dup); - self.emit_binding(BindingOpcode::InitLexical, class_name.into()); + self.emit_binding(BindingOpcode::InitLexical, class_name.clone()); } // TODO: set function name for getter and setters @@ -277,7 +282,7 @@ impl ByteCompiler<'_> { } } let mut field_compiler = ByteCompiler::new( - Sym::EMPTY_STRING, + js_string!(), true, self.json_parse, self.variable_environment.clone(), @@ -305,7 +310,7 @@ impl ByteCompiler<'_> { self.emit_opcode(Opcode::Dup); let name_index = self.get_or_insert_private_name(*name); let mut field_compiler = ByteCompiler::new( - class_name, + class_name.clone(), true, self.json_parse, self.variable_environment.clone(), @@ -343,7 +348,7 @@ impl ByteCompiler<'_> { } }; let mut field_compiler = ByteCompiler::new( - class_name, + class_name.clone(), true, self.json_parse, self.variable_environment.clone(), @@ -377,7 +382,7 @@ impl ByteCompiler<'_> { } ClassElement::StaticBlock(body) => { let mut compiler = ByteCompiler::new( - Sym::EMPTY_STRING, + Sym::EMPTY_STRING.to_js_string(self.interner()), true, false, self.variable_environment.clone(), @@ -580,7 +585,7 @@ impl ByteCompiler<'_> { self.emit_opcode(Opcode::PopPrivateEnvironment); if !expression { - self.emit_binding(BindingOpcode::InitVar, class_name.into()); + self.emit_binding(BindingOpcode::InitVar, class_name); } // NOTE: Reset strict mode to before class declaration/expression evalutation. diff --git a/boa_engine/src/bytecompiler/declaration/declaration_pattern.rs b/boa_engine/src/bytecompiler/declaration/declaration_pattern.rs index 5200f3982d3..375fb3e8312 100644 --- a/boa_engine/src/bytecompiler/declaration/declaration_pattern.rs +++ b/boa_engine/src/bytecompiler/declaration/declaration_pattern.rs @@ -1,5 +1,5 @@ use crate::{ - bytecompiler::{Access, ByteCompiler, Literal, Operand}, + bytecompiler::{Access, ByteCompiler, Literal, Operand, ToJsString}, vm::{BindingOpcode, Opcode}, }; use boa_ast::{ @@ -61,7 +61,7 @@ impl ByteCompiler<'_> { self.compile_expr(init, true); self.patch_jump(skip); } - self.emit_binding(def, *ident); + self.emit_binding(def, ident.to_js_string(self.interner())); if rest_exits && name.computed().is_some() { self.emit_opcode(Opcode::Swap); @@ -88,7 +88,7 @@ impl ByteCompiler<'_> { Operand::Varying(additional_excluded_keys_count), ], ); - self.emit_binding(def, *ident); + self.emit_binding(def, ident.to_js_string(self.interner())); } AssignmentRestPropertyAccess { access, @@ -250,7 +250,7 @@ impl ByteCompiler<'_> { self.compile_expr(init, true); self.patch_jump(skip); } - self.emit_binding(def, *ident); + self.emit_binding(def, ident.to_js_string(self.interner())); } PropertyAccess { access } => { self.access_set(Access::Property { access }, false, |compiler, _level| { @@ -277,7 +277,7 @@ impl ByteCompiler<'_> { // BindingRestElement : ... BindingIdentifier SingleNameRest { ident } => { self.emit_opcode(Opcode::IteratorToArray); - self.emit_binding(def, *ident); + self.emit_binding(def, ident.to_js_string(self.interner())); } PropertyAccessRest { access } => { self.access_set(Access::Property { access }, false, |compiler, _level| { diff --git a/boa_engine/src/bytecompiler/declarations.rs b/boa_engine/src/bytecompiler/declarations.rs index 5edbc9888b3..f60eb2d6673 100644 --- a/boa_engine/src/bytecompiler/declarations.rs +++ b/boa_engine/src/bytecompiler/declarations.rs @@ -3,7 +3,6 @@ use std::rc::Rc; use crate::{ bytecompiler::{ByteCompiler, FunctionCompiler, FunctionSpec, NodeKind}, environments::CompileTimeEnvironment, - js_string, vm::{create_function_object_fast, BindingOpcode, Opcode}, JsNativeError, JsResult, }; @@ -23,7 +22,7 @@ use boa_interner::Sym; #[cfg(feature = "annex-b")] use boa_ast::operations::annex_b_function_declarations_names; -use super::Operand; +use super::{Operand, ToJsString}; impl ByteCompiler<'_> { /// `GlobalDeclarationInstantiation ( script, env )` @@ -45,17 +44,24 @@ impl ByteCompiler<'_> { // 3. For each element name of lexNames, do for name in lex_names { + let name = self + .context + .interner() + .resolve_expect(name.sym()) + .utf16() + .into(); + // 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 env.has_binding(name) { + if env.has_binding(&name) { return Err(JsNativeError::syntax() .with_message("duplicate lexical declaration") .into()); } // c. Let hasRestrictedGlobal be ? env.HasRestrictedGlobalProperty(name). - let has_restricted_global = self.context.has_restricted_global_property(name)?; + let has_restricted_global = self.context.has_restricted_global_property(&name)?; // d. If hasRestrictedGlobal is true, throw a SyntaxError exception. if has_restricted_global { @@ -67,8 +73,15 @@ impl ByteCompiler<'_> { // 4. For each element name of varNames, do for name in var_names { + let name = self + .context + .interner() + .resolve_expect(name.sym()) + .utf16() + .into(); + // a. If env.HasLexicalDeclaration(name) is true, throw a SyntaxError exception. - if env.has_lex_binding(name) { + if env.has_lex_binding(&name) { return Err(JsNativeError::syntax() .with_message("duplicate lexical declaration") .into()); @@ -106,7 +119,9 @@ impl ByteCompiler<'_> { // a.iv. If declaredFunctionNames does not contain fn, then if !declared_function_names.contains(&name) { // 1. Let fnDefinable be ? env.CanDeclareGlobalFunction(fn). - let fn_definable = self.context.can_declare_global_function(name)?; + let fn_definable = self + .context + .can_declare_global_function(&name.to_js_string(self.interner()))?; // 2. If fnDefinable is false, throw a TypeError exception. if !fn_definable { @@ -140,7 +155,9 @@ impl ByteCompiler<'_> { // 1. If declaredFunctionNames does not contain vn, then if !declared_function_names.contains(&name) { // a. Let vnDefinable be ? env.CanDeclareGlobalVar(vn). - let definable = self.context.can_declare_global_var(name)?; + let definable = self + .context + .can_declare_global_var(&name.to_js_string(self.interner()))?; // b. If vnDefinable is false, throw a TypeError exception. if !definable { @@ -178,10 +195,12 @@ impl ByteCompiler<'_> { // 2. If replacing the FunctionDeclaration f with a VariableStatement that has F as a BindingIdentifier // would not produce any Early Errors for script, then if !lex_names.contains(&f) { + let f_string = self.resolve_identifier_expect(f); + // a. If env.HasLexicalDeclaration(F) is false, then - if !env.has_lex_binding(f) { + if !env.has_lex_binding(&f_string) { // i. Let fnDefinable be ? env.CanDeclareGlobalVar(F). - let fn_definable = self.context.can_declare_global_function(f)?; + let fn_definable = self.context.can_declare_global_function(&f_string)?; // ii. If fnDefinable is true, then if fn_definable { @@ -192,7 +211,7 @@ impl ByteCompiler<'_> { && !declared_var_names.contains(&f) { // i. Perform ? env.CreateGlobalVarBinding(F, false). - self.context.create_global_var_binding(f, false)?; + self.context.create_global_var_binding(f_string, false)?; // ii. Append F to declaredFunctionOrVarNames. declared_function_names.push(f); @@ -225,16 +244,19 @@ impl ByteCompiler<'_> { match declaration { Declaration::Class(class) => { for name in bound_names(class) { + let name = name.to_js_string(self.interner()); env.create_mutable_binding(name, false); } } Declaration::Lexical(LexicalDeclaration::Let(declaration)) => { for name in bound_names(declaration) { + let name = name.to_js_string(self.interner()); env.create_mutable_binding(name, false); } } Declaration::Lexical(LexicalDeclaration::Const(declaration)) => { for name in bound_names(declaration) { + let name = name.to_js_string(self.interner()); env.create_immutable_binding(name, true); } } @@ -266,11 +288,11 @@ impl ByteCompiler<'_> { let name = name.expect("function declaration must have a name"); let code = FunctionCompiler::new() - .name(name.sym()) + .name(name.sym().to_js_string(self.interner())) .generator(generator) .r#async(r#async) .strict(self.strict()) - .binding_identifier(Some(name.sym())) + .binding_identifier(Some(name.sym().to_js_string(self.interner()))) .compile( parameters, body, @@ -286,7 +308,7 @@ impl ByteCompiler<'_> { let function = create_function_object_fast(code, self.context); // c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false). - let name = js_string!(self.interner().resolve_expect(name.sym()).utf16()); + let name = name.to_js_string(self.interner()); self.context .create_global_function_binding(name, function, false)?; } @@ -294,6 +316,7 @@ impl ByteCompiler<'_> { // 17. For each String vn of declaredVarNames, do for var in declared_var_names { // a. Perform ? env.CreateGlobalVarBinding(vn, false). + let var = var.to_js_string(self.interner()); self.context.create_global_var_binding(var, false)?; } @@ -328,6 +351,7 @@ impl ByteCompiler<'_> { // a. For each element dn of the BoundNames of d, do for dn in bound_names::<'_, VariableList>(d) { // 1. Perform ! env.CreateImmutableBinding(dn, true). + let dn = dn.to_js_string(self.interner()); env.create_immutable_binding(dn, true); } } @@ -335,13 +359,15 @@ impl ByteCompiler<'_> { else { // a. For each element dn of the BoundNames of d, do for dn in d.bound_names() { + let dn = dn.to_js_string(self.interner()); + #[cfg(not(feature = "annex-b"))] // 1. Perform ! env.CreateMutableBinding(dn, false). NOTE: This step is replaced in section B.3.2.6. env.create_mutable_binding(dn, false); #[cfg(feature = "annex-b")] // 1. If ! env.HasBinding(dn) is false, then - if !env.has_binding(dn) { + if !env.has_binding(&dn) { // a. Perform ! env.CreateMutableBinding(dn, false). env.create_mutable_binding(dn, false); } @@ -403,9 +429,11 @@ impl ByteCompiler<'_> { if var_env.is_global() { // i. For each element name of varNames, do for name in &var_names { + let name = name.to_js_string(self.interner()); + // 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) { + if var_env.has_lex_binding(&name) { return Err(JsNativeError::syntax() .with_message("duplicate lexical declaration") .into()); @@ -424,12 +452,18 @@ impl ByteCompiler<'_> { // 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 { + let name = self + .context + .interner() + .resolve_expect(name.sym()) + .utf16() + .into(); + // a. If ! thisEnv.HasBinding(name) is true, then - if this_env.has_binding(*name) { + 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"); + let msg = format!("variable declaration {} in eval function already exists as a lexical variable", name.to_std_string_escaped()); return Err(JsNativeError::syntax().with_message(msg).into()); } // b. NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration. @@ -497,8 +531,10 @@ impl ByteCompiler<'_> { if !declared_function_names.contains(&name) { // 1. If varEnv is a Global Environment Record, then if var_env.is_global() { + let name = name.to_js_string(self.interner()); + // a. Let fnDefinable be ? varEnv.CanDeclareGlobalFunction(fn). - let fn_definable = self.context.can_declare_global_function(name)?; + let fn_definable = self.context.can_declare_global_function(&name)?; // b. If fnDefinable is false, throw a TypeError exception. if !fn_definable { @@ -541,9 +577,11 @@ impl ByteCompiler<'_> { // 3. Assert: The following loop will terminate. // 4. Repeat, while thisEnv is not varEnv, while this_env.environment_index() != lex_env.environment_index() { + let f = f.to_js_string(self.interner()); + // a. If thisEnv is not an Object Environment Record, then // i. If ! thisEnv.HasBinding(F) is true, then - if this_env.has_binding(f) { + if this_env.has_binding(&f) { // i. Let bindingExists be true. binding_exists = true; break; @@ -559,14 +597,16 @@ impl ByteCompiler<'_> { // 5. If bindingExists is false and varEnv is a Global Environment Record, then let fn_definable = if !binding_exists && var_env.is_global() { + let f = f.to_js_string(self.interner()); + // a. If varEnv.HasLexicalDeclaration(F) is false, then // b. Else, - if self.variable_environment.has_lex_binding(f) { + if self.variable_environment.has_lex_binding(&f) { // i. Let fnDefinable be false. false } else { // i. Let fnDefinable be ? varEnv.CanDeclareGlobalVar(F). - self.context.can_declare_global_var(f)? + self.context.can_declare_global_var(&f)? } } // 6. Else, @@ -586,14 +626,16 @@ impl ByteCompiler<'_> { { // i. If varEnv is a Global Environment Record, then if var_env.is_global() { + let f = f.to_js_string(self.interner()); // i. Perform ? varEnv.CreateGlobalVarBinding(F, true). self.context.create_global_var_binding(f, true)?; } // ii. Else, else { + let f = f.to_js_string(self.interner()); // i. Let bindingExists be ! varEnv.HasBinding(F). // ii. If bindingExists is false, then - if !var_env.has_binding(f) { + if !var_env.has_binding(&f) { // i. Perform ! varEnv.CreateMutableBinding(F, true). // ii. Perform ! varEnv.InitializeBinding(F, undefined). let binding = var_env.create_mutable_binding(f, true); @@ -636,8 +678,10 @@ impl ByteCompiler<'_> { if !declared_function_names.contains(&name) { // a. If varEnv is a Global Environment Record, then if var_env.is_global() { + let name = name.to_js_string(self.interner()); + // i. Let vnDefinable be ? varEnv.CanDeclareGlobalVar(vn). - let vn_definable = self.context.can_declare_global_var(name)?; + let vn_definable = self.context.can_declare_global_var(&name)?; // ii. If vnDefinable is false, throw a TypeError exception. if !vn_definable { @@ -672,16 +716,19 @@ impl ByteCompiler<'_> { match declaration { Declaration::Class(class) => { for name in bound_names(class) { + let name = name.to_js_string(self.interner()); lex_env.create_mutable_binding(name, false); } } Declaration::Lexical(LexicalDeclaration::Let(declaration)) => { for name in bound_names(declaration) { + let name = name.to_js_string(self.interner()); lex_env.create_mutable_binding(name, false); } } Declaration::Lexical(LexicalDeclaration::Const(declaration)) => { for name in bound_names(declaration) { + let name = name.to_js_string(self.interner()); lex_env.create_immutable_binding(name, true); } } @@ -712,11 +759,11 @@ impl ByteCompiler<'_> { }; let name = name.expect("function declaration must have a name"); let code = FunctionCompiler::new() - .name(name.sym()) + .name(name.sym().to_js_string(self.interner())) .generator(generator) .r#async(r#async) .strict(self.strict()) - .binding_identifier(Some(name.sym())) + .binding_identifier(Some(name.sym().to_js_string(self.interner()))) .compile( parameters, body, @@ -746,8 +793,10 @@ impl ByteCompiler<'_> { let index = self.push_function_to_constants(code); self.emit_with_varying_operand(Opcode::GetFunction, index); + let name = name.to_js_string(self.interner()); + // i. Let bindingExists be ! varEnv.HasBinding(fn). - let binding_exists = var_env.has_binding(name); + let binding_exists = var_env.has_binding(&name); // ii. If bindingExists is false, then // iii. Else, @@ -769,6 +818,8 @@ impl ByteCompiler<'_> { // 18. For each String vn of declaredVarNames, do for name in declared_var_names { + let name = name.to_js_string(self.interner()); + // a. If varEnv is a Global Environment Record, then if var_env.is_global() { // i. Perform ? varEnv.CreateGlobalVarBinding(vn, true). @@ -777,7 +828,7 @@ impl ByteCompiler<'_> { // b. Else, else { // i. Let bindingExists be ! varEnv.HasBinding(vn). - let binding_exists = var_env.has_binding(name); + let binding_exists = var_env.has_binding(&name); // ii. If bindingExists is false, then if !binding_exists { @@ -920,6 +971,8 @@ impl ByteCompiler<'_> { // NOTE(HalidOdat): Has been moved up, so "arguments" gets registed as // the first binding in the environment with index 0. if arguments_object_needed { + let arguments = arguments.to_js_string(self.interner()); + // a. If strict is true or simpleParameterList is false, then if strict || !formals.is_simple() { // i. Let ao be CreateUnmappedArgumentsObject(argumentsList). @@ -939,12 +992,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. - env.create_immutable_binding(arguments, false); + env.create_immutable_binding(arguments.clone(), false); } // d. Else, else { // i. Perform ! env.CreateMutableBinding("arguments", false). - env.create_mutable_binding(arguments, false); + env.create_mutable_binding(arguments.clone(), false); } // e. Perform ! env.InitializeBinding("arguments", ao). @@ -953,8 +1006,10 @@ impl ByteCompiler<'_> { // 21. For each String paramName of parameterNames, do for param_name in ¶meter_names { + let param_name = param_name.to_js_string(self.interner()); + // a. Let alreadyDeclared be ! env.HasBinding(paramName). - let already_declared = env.has_binding(*param_name); + let already_declared = env.has_binding(¶m_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. @@ -962,7 +1017,7 @@ impl ByteCompiler<'_> { // c. If alreadyDeclared is false, then if !already_declared { // i. Perform ! env.CreateMutableBinding(paramName, false). - env.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 @@ -997,12 +1052,13 @@ impl ByteCompiler<'_> { } match parameter.variable().binding() { Binding::Identifier(ident) => { + let ident = ident.to_js_string(self.interner()); 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::InitLexical, *ident); + self.emit_binding(BindingOpcode::InitLexical, ident); } Binding::Pattern(pattern) => { if let Some(init) = parameter.variable().init() { @@ -1044,8 +1100,10 @@ impl ByteCompiler<'_> { // 1. Append n to instantiatedVarNames. instantiated_var_names.push(n); + let n_string = n.to_js_string(self.interner()); + // 2. Perform ! varEnv.CreateMutableBinding(n, false). - let binding = var_env.create_mutable_binding(n, false); + let binding = var_env.create_mutable_binding(n_string.clone(), false); // 3. If parameterBindings does not contain n, or if functionNames contains n, then if !parameter_bindings.contains(&n) || function_names.contains(&n) { @@ -1055,7 +1113,7 @@ impl ByteCompiler<'_> { // 4. Else, else { // a. Let initialValue be ! env.GetBindingValue(n, false). - let binding = env.get_binding(n).expect("must have binding"); + let binding = env.get_binding(&n_string).expect("must have binding"); let index = self.get_or_insert_binding(binding); self.emit_with_varying_operand(Opcode::GetName, index); } @@ -1083,6 +1141,8 @@ impl ByteCompiler<'_> { // 1. Append n to instantiatedVarNames. instantiated_var_names.push(n); + let n = n.to_js_string(self.interner()); + // 2. Perform ! env.CreateMutableBinding(n, false). // 3. Perform ! env.InitializeBinding(n, undefined). let binding = env.create_mutable_binding(n, true); @@ -1113,9 +1173,11 @@ impl ByteCompiler<'_> { // 2. If initializedBindings does not contain F and F is not "arguments", then if !instantiated_var_names.contains(&f) && f != arguments { + let f_string = f.to_js_string(self.interner()); + // a. Perform ! varEnv.CreateMutableBinding(F, false). // b. Perform ! varEnv.InitializeBinding(F, undefined). - let binding = var_env.create_mutable_binding(f, false); + let binding = var_env.create_mutable_binding(f_string, false); let index = self.get_or_insert_binding(binding); self.emit_opcode(Opcode::PushUndefined); self.emit_with_varying_operand(Opcode::DefInitVar, index); @@ -1169,16 +1231,19 @@ impl ByteCompiler<'_> { match declaration { Declaration::Class(class) => { for name in bound_names(class) { + let name = name.to_js_string(self.interner()); lex_env.create_mutable_binding(name, false); } } Declaration::Lexical(LexicalDeclaration::Let(declaration)) => { for name in bound_names(declaration) { + let name = name.to_js_string(self.interner()); lex_env.create_mutable_binding(name, false); } } Declaration::Lexical(LexicalDeclaration::Const(declaration)) => { for name in bound_names(declaration) { + let name = name.to_js_string(self.interner()); lex_env.create_immutable_binding(name, true); } } diff --git a/boa_engine/src/bytecompiler/expression/assign.rs b/boa_engine/src/bytecompiler/expression/assign.rs index 6b45d824bfe..511b6223c45 100644 --- a/boa_engine/src/bytecompiler/expression/assign.rs +++ b/boa_engine/src/bytecompiler/expression/assign.rs @@ -1,5 +1,5 @@ use crate::{ - bytecompiler::{Access, ByteCompiler, Operand}, + bytecompiler::{Access, ByteCompiler, Operand, ToJsString}, environments::BindingLocatorError, vm::{BindingOpcode, Opcode}, }; @@ -55,7 +55,11 @@ impl ByteCompiler<'_> { match access { Access::Variable { name } => { - let binding = self.lexical_environment.get_identifier_reference(name); + let name = name.to_js_string(self.interner()); + + let binding = self + .lexical_environment + .get_identifier_reference(name.clone()); let index = self.get_or_insert_binding(binding.locator()); if binding.is_lexical() { @@ -75,13 +79,13 @@ impl ByteCompiler<'_> { self.emit_opcode(Opcode::Dup); } if binding.is_lexical() { - match self.lexical_environment.set_mutable_binding(name) { + match self.lexical_environment.set_mutable_binding(name.clone()) { 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); + let index = self.get_or_insert_string(name); self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); } Err(BindingLocatorError::Silent) => { diff --git a/boa_engine/src/bytecompiler/expression/unary.rs b/boa_engine/src/bytecompiler/expression/unary.rs index f853496f94b..d5e380afce3 100644 --- a/boa_engine/src/bytecompiler/expression/unary.rs +++ b/boa_engine/src/bytecompiler/expression/unary.rs @@ -4,7 +4,7 @@ use boa_ast::{ }; use crate::{ - bytecompiler::{Access, ByteCompiler}, + bytecompiler::{Access, ByteCompiler, ToJsString}, vm::Opcode, }; @@ -27,9 +27,10 @@ impl ByteCompiler<'_> { UnaryOp::TypeOf => { match unary.target().flatten() { Expression::Identifier(identifier) => { + let identifier = identifier.to_js_string(self.interner()); let binding = self .lexical_environment - .get_identifier_reference(*identifier); + .get_identifier_reference(identifier); let index = self.get_or_insert_binding(binding.locator()); self.emit_with_varying_operand(Opcode::GetNameOrUndefined, index); } diff --git a/boa_engine/src/bytecompiler/expression/update.rs b/boa_engine/src/bytecompiler/expression/update.rs index d71f2ddaaa3..0eb988881b3 100644 --- a/boa_engine/src/bytecompiler/expression/update.rs +++ b/boa_engine/src/bytecompiler/expression/update.rs @@ -1,5 +1,5 @@ use crate::{ - bytecompiler::{Access, ByteCompiler, Operand}, + bytecompiler::{Access, ByteCompiler, Operand, ToJsString}, environments::BindingLocatorError, vm::Opcode, }; @@ -23,7 +23,10 @@ impl ByteCompiler<'_> { match Access::from_update_target(update.target()) { Access::Variable { name } => { - let binding = self.lexical_environment.get_identifier_reference(name); + let name = name.to_js_string(self.interner()); + let binding = self + .lexical_environment + .get_identifier_reference(name.clone()); let index = self.get_or_insert_binding(binding.locator()); if binding.is_lexical() { @@ -40,13 +43,13 @@ impl ByteCompiler<'_> { } if binding.is_lexical() { - match self.lexical_environment.set_mutable_binding(name) { + match self.lexical_environment.set_mutable_binding(name.clone()) { 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); + let index = self.get_or_insert_string(name); self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); } Err(BindingLocatorError::Silent) => { diff --git a/boa_engine/src/bytecompiler/function.rs b/boa_engine/src/bytecompiler/function.rs index f3bbcf8bbe2..7db6f2ff2c8 100644 --- a/boa_engine/src/bytecompiler/function.rs +++ b/boa_engine/src/bytecompiler/function.rs @@ -4,31 +4,31 @@ use crate::{ builtins::function::ThisMode, bytecompiler::ByteCompiler, environments::CompileTimeEnvironment, + js_string, vm::{CodeBlock, CodeBlockFlags, Opcode}, - Context, + Context, JsString, }; use boa_ast::function::{FormalParameterList, FunctionBody}; use boa_gc::Gc; -use boa_interner::Sym; /// `FunctionCompiler` is used to compile AST functions to bytecode. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] #[allow(clippy::struct_excessive_bools)] pub(crate) struct FunctionCompiler { - name: Sym, + name: JsString, generator: bool, r#async: bool, strict: bool, arrow: bool, method: bool, - binding_identifier: Option, + binding_identifier: Option, } impl FunctionCompiler { /// Create a new `FunctionCompiler`. - pub(crate) const fn new() -> Self { + pub(crate) fn new() -> Self { Self { - name: Sym::EMPTY_STRING, + name: js_string!(), generator: false, r#async: false, strict: false, @@ -41,7 +41,7 @@ impl FunctionCompiler { /// Set the name of the function. pub(crate) fn name(mut self, name: N) -> Self where - N: Into>, + N: Into>, { let name = name.into(); if let Some(name) = name { @@ -79,7 +79,7 @@ impl FunctionCompiler { } /// Indicate if the function has a binding identifier. - pub(crate) const fn binding_identifier(mut self, binding_identifier: Option) -> Self { + pub(crate) fn binding_identifier(mut self, binding_identifier: Option) -> Self { self.binding_identifier = binding_identifier; self } @@ -126,7 +126,7 @@ impl FunctionCompiler { let _ = compiler.push_compile_environment(false); compiler .lexical_environment - .create_immutable_binding(binding_identifier.into(), self.strict); + .create_immutable_binding(binding_identifier, self.strict); } // Function environment diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 9851852f4e8..57a1b6546ed 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -46,6 +46,22 @@ pub(crate) use function::FunctionCompiler; pub(crate) use jump_control::JumpControlInfo; use thin_vec::ThinVec; +pub(crate) trait ToJsString { + fn to_js_string(&self, interner: &Interner) -> JsString; +} + +impl ToJsString for Sym { + fn to_js_string(&self, interner: &Interner) -> JsString { + js_string!(interner.resolve_expect(*self).utf16()) + } +} + +impl ToJsString for Identifier { + fn to_js_string(&self, interner: &Interner) -> JsString { + self.sym().to_js_string(interner) + } +} + /// Describes how a node has been defined in the source code. #[derive(Debug, Clone, Copy, PartialEq)] pub(crate) enum NodeKind { @@ -234,7 +250,7 @@ pub(crate) enum Operand { #[allow(clippy::struct_excessive_bools)] pub struct ByteCompiler<'ctx> { /// Name of this function. - pub(crate) function_name: Sym, + pub(crate) function_name: JsString, /// The number of arguments expected. pub(crate) length: u32, @@ -289,7 +305,7 @@ impl<'ctx> ByteCompiler<'ctx> { /// Creates a new [`ByteCompiler`]. #[inline] pub(crate) fn new( - name: Sym, + name: JsString, strict: bool, json_parse: bool, variable_environment: Rc, @@ -378,6 +394,10 @@ impl<'ctx> ByteCompiler<'ctx> { index } + fn get_or_insert_string(&mut self, value: JsString) -> u32 { + self.get_or_insert_literal(Literal::String(value)) + } + #[inline] fn get_or_insert_private_name(&mut self, name: PrivateName) -> u32 { self.get_or_insert_name(Identifier::new(name.description())) @@ -390,7 +410,7 @@ impl<'ctx> ByteCompiler<'ctx> { } let index = self.bindings.len() as u32; - self.bindings.push(binding); + self.bindings.push(binding.clone()); self.bindings_map.insert(binding, index); index } @@ -403,7 +423,7 @@ impl<'ctx> ByteCompiler<'ctx> { index } - fn emit_binding(&mut self, opcode: BindingOpcode, name: Identifier) { + fn emit_binding(&mut self, opcode: BindingOpcode, name: JsString) { match opcode { BindingOpcode::Var => { let binding = self.variable_environment.get_identifier_reference(name); @@ -412,37 +432,41 @@ impl<'ctx> ByteCompiler<'ctx> { self.emit_with_varying_operand(Opcode::DefVar, index); } } - 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); - } - 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::InitVar => { + match self.lexical_environment.set_mutable_binding(name.clone()) { + 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_string(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.lexical_environment.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); + BindingOpcode::SetName => { + match self.lexical_environment.set_mutable_binding(name.clone()) { + 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_string(name); + self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); + } + Err(BindingLocatorError::Silent) => { + self.emit_opcode(Opcode::Pop); + } } - }, + } } } @@ -682,9 +706,14 @@ impl<'ctx> ByteCompiler<'ctx> { self.patch_jump_with_target(label, target); } + fn resolve_identifier_expect(&self, identifier: Identifier) -> JsString { + js_string!(self.interner().resolve_expect(identifier.sym()).utf16()) + } + fn access_get(&mut self, access: Access<'_>, use_expr: bool) { match access { Access::Variable { name } => { + let name = self.resolve_identifier_expect(name); 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); @@ -750,7 +779,10 @@ impl<'ctx> ByteCompiler<'ctx> { { match access { Access::Variable { name } => { - let binding = self.lexical_environment.get_identifier_reference(name); + let name = self.resolve_identifier_expect(name); + let binding = self + .lexical_environment + .get_identifier_reference(name.clone()); let index = self.get_or_insert_binding(binding.locator()); if !binding.is_lexical() { @@ -763,13 +795,13 @@ impl<'ctx> ByteCompiler<'ctx> { } if binding.is_lexical() { - match self.lexical_environment.set_mutable_binding(name) { + match self.lexical_environment.set_mutable_binding(name.clone()) { 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); + let index = self.get_or_insert_string(name); self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); } Err(BindingLocatorError::Silent) => { @@ -862,6 +894,7 @@ impl<'ctx> ByteCompiler<'ctx> { } }, Access::Variable { name } => { + let name = name.to_js_string(self.interner()); 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); @@ -1084,14 +1117,17 @@ impl<'ctx> ByteCompiler<'ctx> { for variable in decl.0.as_ref() { match variable.binding() { Binding::Identifier(ident) => { + let ident = ident.to_js_string(self.interner()); if let Some(expr) = variable.init() { - let binding = self.lexical_environment.get_identifier_reference(*ident); + let binding = self + .lexical_environment + .get_identifier_reference(ident.clone()); let index = self.get_or_insert_binding(binding.locator()); self.emit_with_varying_operand(Opcode::GetLocator, index); self.compile_expr(expr, true); self.emit_opcode(Opcode::SetNameByLocator); } else { - self.emit_binding(BindingOpcode::Var, *ident); + self.emit_binding(BindingOpcode::Var, ident); } } Binding::Pattern(pattern) => { @@ -1114,12 +1150,13 @@ impl<'ctx> ByteCompiler<'ctx> { for variable in decls.as_ref() { match variable.binding() { Binding::Identifier(ident) => { + let ident = ident.to_js_string(self.interner()); if let Some(expr) = variable.init() { self.compile_expr(expr, true); } else { self.emit_opcode(Opcode::PushUndefined); } - self.emit_binding(BindingOpcode::InitLexical, *ident); + self.emit_binding(BindingOpcode::InitLexical, ident); } Binding::Pattern(pattern) => { if let Some(init) = variable.init() { @@ -1137,11 +1174,12 @@ impl<'ctx> ByteCompiler<'ctx> { for variable in decls.as_ref() { match variable.binding() { Binding::Identifier(ident) => { + let ident = ident.to_js_string(self.interner()); let init = variable .init() .expect("const declaration must have initializer"); self.compile_expr(init, true); - self.emit_binding(BindingOpcode::InitLexical, *ident); + self.emit_binding(BindingOpcode::InitLexical, ident); } Binding::Pattern(pattern) => { if let Some(init) = variable.init() { @@ -1178,17 +1216,23 @@ impl<'ctx> ByteCompiler<'ctx> { .name() .expect("function declaration must have name"); if self.annex_b_function_names.contains(&name) { - let binding = self.lexical_environment.get_identifier_reference(name); + let name = name.to_js_string(self.interner()); + let binding = self + .lexical_environment + .get_identifier_reference(name.clone()); let index = self.get_or_insert_binding(binding.locator()); self.emit_with_varying_operand(Opcode::GetName, index); - match self.variable_environment.set_mutable_binding_var(name) { + match self + .variable_environment + .set_mutable_binding_var(name.clone()) + { 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); + let index = self.get_or_insert_string(name); self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); } Err(BindingLocatorError::Silent) => { @@ -1219,18 +1263,20 @@ impl<'ctx> ByteCompiler<'ctx> { .. } = function; + let name = if let Some(name) = name { + Some(name.sym().to_js_string(self.interner())) + } else { + Some(js_string!()) + }; + let binding_identifier = if has_binding_identifier { - if let Some(name) = name { - Some(name.sym()) - } else { - Some(Sym::EMPTY_STRING) - } + name.clone() } else { None }; let code = FunctionCompiler::new() - .name(name.map(Identifier::sym)) + .name(name) .generator(generator) .r#async(r#async) .strict(self.strict()) @@ -1264,7 +1310,8 @@ impl<'ctx> ByteCompiler<'ctx> { NodeKind::Declaration => { self.emit_binding( BindingOpcode::InitVar, - name.expect("function declaration must have a name"), + name.expect("function declaration must have a name") + .to_js_string(self.interner()), ); } NodeKind::Expression => { @@ -1290,18 +1337,20 @@ impl<'ctx> ByteCompiler<'ctx> { .. } = function; + let name = if let Some(name) = name { + Some(name.sym().to_js_string(self.interner())) + } else { + Some(js_string!()) + }; + let binding_identifier = if has_binding_identifier { - if let Some(name) = name { - Some(name.sym()) - } else { - Some(Sym::EMPTY_STRING) - } + name.clone() } else { None }; let code = FunctionCompiler::new() - .name(name.map(Identifier::sym)) + .name(name) .generator(generator) .r#async(r#async) .strict(self.strict()) @@ -1335,18 +1384,20 @@ impl<'ctx> ByteCompiler<'ctx> { .. } = function; + let name = if let Some(name) = name { + Some(name.sym().to_js_string(self.interner())) + } else { + Some(js_string!()) + }; + let binding_identifier = if has_binding_identifier { - if let Some(name) = name { - Some(name.sym()) - } else { - Some(Sym::EMPTY_STRING) - } + name.clone() } else { None }; let code = FunctionCompiler::new() - .name(name.map(Identifier::sym)) + .name(name) .generator(generator) .r#async(r#async) .strict(true) @@ -1451,15 +1502,8 @@ impl<'ctx> ByteCompiler<'ctx> { } self.r#return(false); - let name = self - .context - .interner() - .resolve_expect(self.function_name) - .utf16() - .into(); - CodeBlock { - name, + name: self.function_name, length: self.length, this_mode: self.this_mode, params: self.params, diff --git a/boa_engine/src/bytecompiler/module.rs b/boa_engine/src/bytecompiler/module.rs index 434bd2d7ce0..843311c97b9 100644 --- a/boa_engine/src/bytecompiler/module.rs +++ b/boa_engine/src/bytecompiler/module.rs @@ -1,7 +1,7 @@ use crate::vm::{BindingOpcode, Opcode}; -use super::{ByteCompiler, Literal, Operand}; -use boa_ast::{declaration::ExportDeclaration, expression::Identifier, ModuleItem, ModuleItemList}; +use super::{ByteCompiler, Literal, Operand, ToJsString}; +use boa_ast::{declaration::ExportDeclaration, ModuleItem, ModuleItemList}; use boa_interner::Sym; impl ByteCompiler<'_> { @@ -47,13 +47,14 @@ impl ByteCompiler<'_> { if cl.name().is_none() { self.emit_binding( BindingOpcode::InitLexical, - Identifier::from(Sym::DEFAULT_EXPORT), + Sym::DEFAULT_EXPORT.to_js_string(self.interner()), ); } } ExportDeclaration::DefaultAssignmentExpression(expr) => { - let name = Identifier::from(Sym::DEFAULT_EXPORT); - self.lexical_environment.create_mutable_binding(name, false); + let name = Sym::DEFAULT_EXPORT.to_js_string(self.interner()); + self.lexical_environment + .create_mutable_binding(name.clone(), false); self.compile_expr(expr, true); if expr.is_anonymous_function_definition() { diff --git a/boa_engine/src/bytecompiler/statement/loop.rs b/boa_engine/src/bytecompiler/statement/loop.rs index fa47416a5ec..a5011421f1b 100644 --- a/boa_engine/src/bytecompiler/statement/loop.rs +++ b/boa_engine/src/bytecompiler/statement/loop.rs @@ -9,7 +9,7 @@ use boa_ast::{ use boa_interner::Sym; use crate::{ - bytecompiler::{Access, ByteCompiler, Operand}, + bytecompiler::{Access, ByteCompiler, Operand, ToJsString}, environments::BindingLocatorError, vm::{BindingOpcode, Opcode}, }; @@ -38,15 +38,16 @@ impl ByteCompiler<'_> { let names = bound_names(decl); if decl.is_const() { for name in &names { + let name = name.to_js_string(self.interner()); self.lexical_environment - .create_immutable_binding(*name, true); + .create_immutable_binding(name, true); } } else { let mut indices = Vec::new(); for name in &names { - let binding = self - .lexical_environment - .create_mutable_binding(*name, false); + let name = name.to_js_string(self.interner()); + let binding = + self.lexical_environment.create_mutable_binding(name, false); let index = self.get_or_insert_binding(binding); indices.push(index); } @@ -119,9 +120,10 @@ impl ByteCompiler<'_> { // Handle https://tc39.es/ecma262/#prod-annexB-ForInOfStatement if let IterableLoopInitializer::Var(var) = for_in_loop.initializer() { if let Binding::Identifier(ident) = var.binding() { + let ident = ident.to_js_string(self.interner()); if let Some(init) = var.init() { self.compile_expr(init, true); - self.emit_binding(BindingOpcode::InitVar, *ident); + self.emit_binding(BindingOpcode::InitVar, ident); } } } @@ -138,8 +140,8 @@ impl ByteCompiler<'_> { self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); for name in &initializer_bound_names { - self.lexical_environment - .create_mutable_binding(*name, false); + let name = name.to_js_string(self.interner()); + self.lexical_environment.create_mutable_binding(name, false); } self.compile_expr(for_in_loop.target(), true); @@ -171,7 +173,8 @@ impl ByteCompiler<'_> { match for_in_loop.initializer() { IterableLoopInitializer::Identifier(ident) => { - self.emit_binding(BindingOpcode::InitVar, *ident); + let ident = ident.to_js_string(self.interner()); + self.emit_binding(BindingOpcode::InitVar, ident); } IterableLoopInitializer::Access(access) => { self.access_set( @@ -182,7 +185,8 @@ impl ByteCompiler<'_> { } IterableLoopInitializer::Var(declaration) => match declaration.binding() { Binding::Identifier(ident) => { - self.emit_binding(BindingOpcode::InitVar, *ident); + let ident = ident.to_js_string(self.interner()); + self.emit_binding(BindingOpcode::InitVar, ident); } Binding::Pattern(pattern) => { self.compile_declaration_pattern(pattern, BindingOpcode::InitVar); @@ -190,12 +194,14 @@ impl ByteCompiler<'_> { }, IterableLoopInitializer::Let(declaration) => match declaration { Binding::Identifier(ident) => { + let ident = ident.to_js_string(self.interner()); self.lexical_environment - .create_mutable_binding(*ident, false); - self.emit_binding(BindingOpcode::InitLexical, *ident); + .create_mutable_binding(ident.clone(), false); + self.emit_binding(BindingOpcode::InitLexical, ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { + let ident = ident.to_js_string(self.interner()); self.lexical_environment .create_mutable_binding(ident, false); } @@ -204,12 +210,14 @@ impl ByteCompiler<'_> { }, IterableLoopInitializer::Const(declaration) => match declaration { Binding::Identifier(ident) => { + let ident = ident.to_js_string(self.interner()); self.lexical_environment - .create_immutable_binding(*ident, true); - self.emit_binding(BindingOpcode::InitLexical, *ident); + .create_immutable_binding(ident.clone(), true); + self.emit_binding(BindingOpcode::InitLexical, ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { + let ident = ident.to_js_string(self.interner()); self.lexical_environment .create_immutable_binding(ident, true); } @@ -261,8 +269,8 @@ impl ByteCompiler<'_> { self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); for name in &initializer_bound_names { - self.lexical_environment - .create_mutable_binding(*name, false); + let name = name.to_js_string(self.interner()); + self.lexical_environment.create_mutable_binding(name, false); } self.compile_expr(for_of_loop.iterable(), true); @@ -307,13 +315,14 @@ impl ByteCompiler<'_> { let mut handler_index = None; match for_of_loop.initializer() { IterableLoopInitializer::Identifier(ref ident) => { - match self.lexical_environment.set_mutable_binding(*ident) { + let ident = ident.to_js_string(self.interner()); + match self.lexical_environment.set_mutable_binding(ident.clone()) { 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(*ident); + let index = self.get_or_insert_string(ident); self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); } Err(BindingLocatorError::Silent) => { @@ -334,7 +343,8 @@ impl ByteCompiler<'_> { assert!(declaration.init().is_none()); match declaration.binding() { Binding::Identifier(ident) => { - self.emit_binding(BindingOpcode::InitVar, *ident); + let ident = ident.to_js_string(self.interner()); + self.emit_binding(BindingOpcode::InitVar, ident); } Binding::Pattern(pattern) => { self.compile_declaration_pattern(pattern, BindingOpcode::InitVar); @@ -343,12 +353,14 @@ impl ByteCompiler<'_> { } IterableLoopInitializer::Let(declaration) => match declaration { Binding::Identifier(ident) => { + let ident = ident.to_js_string(self.interner()); self.lexical_environment - .create_mutable_binding(*ident, false); - self.emit_binding(BindingOpcode::InitLexical, *ident); + .create_mutable_binding(ident.clone(), false); + self.emit_binding(BindingOpcode::InitLexical, ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { + let ident = ident.to_js_string(self.interner()); self.lexical_environment .create_mutable_binding(ident, false); } @@ -357,12 +369,14 @@ impl ByteCompiler<'_> { }, IterableLoopInitializer::Const(declaration) => match declaration { Binding::Identifier(ident) => { + let ident = ident.to_js_string(self.interner()); self.lexical_environment - .create_immutable_binding(*ident, true); - self.emit_binding(BindingOpcode::InitLexical, *ident); + .create_immutable_binding(ident.clone(), true); + self.emit_binding(BindingOpcode::InitLexical, ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { + let ident = ident.to_js_string(self.interner()); self.lexical_environment .create_immutable_binding(ident, true); } diff --git a/boa_engine/src/bytecompiler/statement/try.rs b/boa_engine/src/bytecompiler/statement/try.rs index bf86c55b39c..c2030207fb8 100644 --- a/boa_engine/src/bytecompiler/statement/try.rs +++ b/boa_engine/src/bytecompiler/statement/try.rs @@ -1,5 +1,5 @@ use crate::{ - bytecompiler::{jump_control::JumpControlInfoFlags, ByteCompiler}, + bytecompiler::{jump_control::JumpControlInfoFlags, ByteCompiler, ToJsString}, vm::{BindingOpcode, Opcode}, }; use boa_ast::{ @@ -119,11 +119,13 @@ impl ByteCompiler<'_> { if let Some(binding) = catch.parameter() { match binding { Binding::Identifier(ident) => { - env.create_mutable_binding(*ident, false); - self.emit_binding(BindingOpcode::InitLexical, *ident); + let ident = ident.to_js_string(self.interner()); + env.create_mutable_binding(ident.clone(), false); + self.emit_binding(BindingOpcode::InitLexical, ident); } Binding::Pattern(pattern) => { for ident in bound_names(pattern) { + let ident = ident.to_js_string(self.interner()); env.create_mutable_binding(ident, false); } self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical); diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index c27418c4cf4..1a15caf3bfa 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -29,7 +29,7 @@ use crate::{ vm::{ActiveRunnable, CallFrame, Vm}, JsNativeError, JsResult, JsString, JsValue, Source, }; -use boa_ast::{expression::Identifier, StatementList}; +use boa_ast::StatementList; use boa_interner::Interner; use boa_profiler::Profiler; @@ -609,13 +609,13 @@ impl Context { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalfunction - pub(crate) fn can_declare_global_function(&mut self, name: Identifier) -> JsResult { + pub(crate) fn can_declare_global_function(&mut self, name: &JsString) -> JsResult { // 1. Let ObjRec be envRec.[[ObjectRecord]]. // 2. Let globalObject be ObjRec.[[BindingObject]]. let global_object = self.realm().global_object().clone(); // 3. Let existingProp be ? globalObject.[[GetOwnProperty]](N). - let name = self.interner().resolve_expect(name.sym()).utf16().into(); + let name = name.clone().into(); let existing_prop = global_object.__get_own_property__(&name, self)?; // 4. If existingProp is undefined, return ? IsExtensible(globalObject). @@ -646,14 +646,13 @@ impl Context { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalvar - pub(crate) fn can_declare_global_var(&mut self, name: Identifier) -> JsResult { + pub(crate) fn can_declare_global_var(&mut self, name: &JsString) -> JsResult { // 1. Let ObjRec be envRec.[[ObjectRecord]]. // 2. Let globalObject be ObjRec.[[BindingObject]]. let global_object = self.realm().global_object().clone(); // 3. Let hasProperty be ? HasOwnProperty(globalObject, N). - let name = PropertyKey::from(self.interner().resolve_expect(name.sym()).utf16()); - let has_property = global_object.has_own_property(name, self)?; + let has_property = global_object.has_own_property(name.clone(), self)?; // 4. If hasProperty is true, return true. if has_property { @@ -672,7 +671,7 @@ impl Context { /// [spec]: https://tc39.es/ecma262/#sec-createglobalvarbinding pub(crate) fn create_global_var_binding( &mut self, - name: Identifier, + name: JsString, configurable: bool, ) -> JsResult<()> { // 1. Let ObjRec be envRec.[[ObjectRecord]]. @@ -680,7 +679,6 @@ impl Context { let global_object = self.realm().global_object().clone(); // 3. Let hasProperty be ? HasOwnProperty(globalObject, N). - let name = PropertyKey::from(self.interner().resolve_expect(name.sym()).utf16()); let has_property = global_object.has_own_property(name.clone(), self)?; // 4. Let extensible be ? IsExtensible(globalObject). @@ -765,13 +763,13 @@ impl Context { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-hasrestrictedglobalproperty - pub(crate) fn has_restricted_global_property(&mut self, name: Identifier) -> JsResult { + pub(crate) fn has_restricted_global_property(&mut self, name: &JsString) -> JsResult { // 1. Let ObjRec be envRec.[[ObjectRecord]]. // 2. Let globalObject be ObjRec.[[BindingObject]]. let global_object = self.realm().global_object().clone(); // 3. Let existingProp be ? globalObject.[[GetOwnProperty]](N). - let name = PropertyKey::from(self.interner().resolve_expect(name.sym()).utf16()); + let name = name.clone().into(); let existing_prop = global_object.__get_own_property__(&name, self)?; // 4. If existingProp is undefined, return false. diff --git a/boa_engine/src/environments/compile.rs b/boa_engine/src/environments/compile.rs index deb7c97a10c..1169f91916d 100644 --- a/boa_engine/src/environments/compile.rs +++ b/boa_engine/src/environments/compile.rs @@ -1,7 +1,6 @@ use std::{cell::RefCell, rc::Rc}; -use crate::environments::runtime::BindingLocator; -use boa_ast::expression::Identifier; +use crate::{environments::runtime::BindingLocator, JsString}; use boa_gc::{empty_trace, Finalize, Trace}; use rustc_hash::FxHashMap; @@ -26,7 +25,7 @@ struct CompileTimeBinding { pub(crate) struct CompileTimeEnvironment { outer: Option>, environment_index: u32, - bindings: RefCell>, + bindings: RefCell>, function_scope: bool, } @@ -58,21 +57,21 @@ impl CompileTimeEnvironment { } /// Check if environment has a lexical binding with the given name. - pub(crate) fn has_lex_binding(&self, name: Identifier) -> bool { + pub(crate) fn has_lex_binding(&self, name: &JsString) -> bool { self.bindings .borrow() - .get(&name) + .get(name) .map_or(false, |binding| binding.lex) } /// 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) + pub(crate) fn has_binding(&self, name: &JsString) -> bool { + self.bindings.borrow().contains_key(name) } /// 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 { + pub(crate) fn get_identifier_reference(&self, name: JsString) -> IdentifierReference { if let Some(binding) = self.bindings.borrow().get(&name) { IdentifierReference::new( BindingLocator::declarative(name, self.environment_index, binding.index), @@ -106,22 +105,21 @@ impl CompileTimeEnvironment { } /// Get the locator for a binding name. - pub(crate) fn get_binding(&self, name: Identifier) -> Option { - self.bindings - .borrow() - .get(&name) - .map(|binding| BindingLocator::declarative(name, self.environment_index, binding.index)) + pub(crate) fn get_binding(&self, name: &JsString) -> Option { + self.bindings.borrow().get(name).map(|binding| { + BindingLocator::declarative(name.clone(), self.environment_index, binding.index) + }) } /// Create a mutable binding. pub(crate) fn create_mutable_binding( &self, - name: Identifier, + name: JsString, function_scope: bool, ) -> BindingLocator { let binding_index = self.bindings.borrow().len() as u32; self.bindings.borrow_mut().insert( - name, + name.clone(), CompileTimeBinding { index: binding_index, mutable: true, @@ -133,14 +131,10 @@ impl CompileTimeEnvironment { } /// Crate an immutable binding. - pub(crate) fn create_immutable_binding( - &self, - name: Identifier, - strict: bool, - ) -> BindingLocator { + pub(crate) fn create_immutable_binding(&self, name: JsString, strict: bool) -> BindingLocator { let binding_index = self.bindings.borrow().len() as u32; self.bindings.borrow_mut().insert( - name, + name.clone(), CompileTimeBinding { index: binding_index, mutable: false, @@ -154,7 +148,7 @@ impl CompileTimeEnvironment { /// Return the binding locator for a mutable binding. pub(crate) fn set_mutable_binding( &self, - name: Identifier, + name: JsString, ) -> Result { Ok(match self.bindings.borrow().get(&name) { Some(binding) if binding.mutable => { @@ -163,8 +157,8 @@ impl CompileTimeEnvironment { Some(binding) if binding.strict => return Err(BindingLocatorError::MutateImmutable), Some(_) => return Err(BindingLocatorError::Silent), None => self.outer.as_ref().map_or_else( - || Ok(BindingLocator::global(name)), - |outer| outer.set_mutable_binding(name), + || Ok(BindingLocator::global(name.clone())), + |outer| outer.set_mutable_binding(name.clone()), )?, }) } @@ -173,12 +167,12 @@ impl CompileTimeEnvironment { /// Return the binding locator for a set operation on an existing var binding. pub(crate) fn set_mutable_binding_var( &self, - name: Identifier, + name: JsString, ) -> Result { if !self.is_function() { return self.outer.as_ref().map_or_else( - || Ok(BindingLocator::global(name)), - |outer| outer.set_mutable_binding_var(name), + || Ok(BindingLocator::global(name.clone())), + |outer| outer.set_mutable_binding_var(name.clone()), ); } @@ -189,8 +183,8 @@ impl CompileTimeEnvironment { Some(binding) if binding.strict => return Err(BindingLocatorError::MutateImmutable), Some(_) => return Err(BindingLocatorError::Silent), None => self.outer.as_ref().map_or_else( - || Ok(BindingLocator::global(name)), - |outer| outer.set_mutable_binding_var(name), + || Ok(BindingLocator::global(name.clone())), + |outer| outer.set_mutable_binding_var(name.clone()), )?, }) } @@ -215,7 +209,7 @@ impl IdentifierReference { /// Get the binding locator for this identifier reference. pub(crate) fn locator(&self) -> BindingLocator { - self.locator + self.locator.clone() } /// Check if this identifier reference is lexical. diff --git a/boa_engine/src/environments/runtime/declarative/module.rs b/boa_engine/src/environments/runtime/declarative/module.rs index 1e74f27d03a..0de1e45989c 100644 --- a/boa_engine/src/environments/runtime/declarative/module.rs +++ b/boa_engine/src/environments/runtime/declarative/module.rs @@ -1,14 +1,13 @@ -use std::cell::Cell; +use std::cell::RefCell; -use boa_ast::expression::Identifier; use boa_gc::{Finalize, GcRefCell, Trace}; -use crate::{module::Module, JsValue}; +use crate::{module::Module, JsString, JsValue}; /// Type of accessor used to access an indirect binding. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] enum BindingAccessor { - Identifier(Identifier), + Identifier(JsString), Index(u32), } @@ -17,7 +16,7 @@ enum BindingAccessor { struct IndirectBinding { module: Module, #[unsafe_ignore_trace] - accessor: Cell, + accessor: RefCell, } /// The type of binding a [`ModuleEnvironment`] can contain. @@ -61,7 +60,7 @@ impl ModuleEnvironment { BindingType::Indirect(IndirectBinding { module, accessor }) => { let env = module.environment()?; - match accessor.get() { + match &*accessor.clone().borrow() { BindingAccessor::Identifier(name) => { let index = env .compile_env() @@ -70,11 +69,11 @@ impl ModuleEnvironment { let value = env.get(index.binding_index)?; - accessor.set(BindingAccessor::Index(index.binding_index)); + *accessor.borrow_mut() = BindingAccessor::Index(index.binding_index); Some(value) } - BindingAccessor::Index(index) => env.get(index), + BindingAccessor::Index(index) => env.get(*index), } } } @@ -103,17 +102,12 @@ impl ModuleEnvironment { /// /// Panics if the binding value is out of range. #[track_caller] - pub(crate) fn set_indirect( - &self, - index: u32, - target_module: Module, - target_binding: Identifier, - ) { + pub(crate) fn set_indirect(&self, index: u32, target_module: Module, target_binding: JsString) { let mut bindings = self.bindings.borrow_mut(); bindings[index as usize] = BindingType::Indirect(IndirectBinding { module: target_module, - accessor: Cell::new(BindingAccessor::Identifier(target_binding)), + accessor: RefCell::new(BindingAccessor::Identifier(target_binding)), }); } } diff --git a/boa_engine/src/environments/runtime/mod.rs b/boa_engine/src/environments/runtime/mod.rs index d7b5c53412f..e9636750435 100644 --- a/boa_engine/src/environments/runtime/mod.rs +++ b/boa_engine/src/environments/runtime/mod.rs @@ -5,7 +5,6 @@ use crate::{ object::{JsObject, PrivateName}, Context, JsResult, JsString, JsSymbol, JsValue, }; -use boa_ast::expression::Identifier; use boa_gc::{empty_trace, Finalize, Gc, Trace}; mod declarative; @@ -417,9 +416,9 @@ impl EnvironmentStack { /// A binding locator contains all information about a binding that is needed to resolve it at runtime. /// /// Binding locators get created at bytecode compile time and are accessible at runtime via the [`crate::vm::CodeBlock`]. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Finalize)] +#[derive(Clone, Debug, Eq, Hash, PartialEq, Finalize)] pub(crate) struct BindingLocator { - name: Identifier, + name: JsString, environment_index: u32, binding_index: u32, global: bool, @@ -432,7 +431,7 @@ unsafe impl Trace for BindingLocator { impl BindingLocator { /// Creates a new declarative binding locator that has knows indices. pub(crate) const fn declarative( - name: Identifier, + name: JsString, environment_index: u32, binding_index: u32, ) -> Self { @@ -445,7 +444,7 @@ impl BindingLocator { } /// Creates a binding locator that indicates that the binding is on the global object. - pub(super) const fn global(name: Identifier) -> Self { + pub(super) const fn global(name: JsString) -> Self { Self { name, environment_index: 0, @@ -455,8 +454,8 @@ impl BindingLocator { } /// Returns the name of the binding. - pub(crate) const fn name(&self) -> Identifier { - self.name + pub(crate) const fn name(&self) -> &JsString { + &self.name } /// Returns if the binding is located on the global object. @@ -510,7 +509,7 @@ impl Context { if env.poisoned() { let compile = env.compile_env(); if compile.is_function() { - if let Some(b) = compile.get_binding(locator.name) { + if let Some(b) = compile.get_binding(locator.name()) { locator.environment_index = b.environment_index; locator.binding_index = b.binding_index; locator.global = false; @@ -523,10 +522,7 @@ impl Context { } Environment::Object(o) => { let o = o.clone(); - let key: JsString = self - .interner() - .resolve_expect(locator.name.sym()) - .into_common(false); + let key = locator.name().clone(); if o.has_property(key.clone(), self)? { if let Some(unscopables) = o.get(JsSymbol::unscopables(), self)?.as_object() { @@ -552,19 +548,13 @@ impl Context { /// Panics if the environment or binding index are out of range. pub(crate) fn is_initialized_binding(&mut self, locator: &BindingLocator) -> JsResult { if locator.global { - let key: JsString = self - .interner() - .resolve_expect(locator.name.sym()) - .into_common(false); + let key = locator.name().clone(); self.global_object().has_property(key, self) } else { match self.environment_expect(locator.environment_index) { Environment::Declarative(env) => Ok(env.get(locator.binding_index).is_some()), Environment::Object(obj) => { - let key: JsString = self - .interner() - .resolve_expect(locator.name.sym()) - .into_common(false); + let key = locator.name().clone(); obj.clone().has_property(key, self) } } @@ -576,13 +566,10 @@ impl Context { /// # Panics /// /// Panics if the environment or binding index are out of range. - pub(crate) fn get_binding(&mut self, locator: BindingLocator) -> JsResult> { + pub(crate) fn get_binding(&mut self, locator: &BindingLocator) -> JsResult> { if locator.global { let global = self.global_object(); - let key: JsString = self - .interner() - .resolve_expect(locator.name.sym()) - .into_common(false); + let key = locator.name().clone(); if global.has_property(key.clone(), self)? { global.get(key, self).map(Some) } else { @@ -593,10 +580,7 @@ impl Context { Environment::Declarative(env) => Ok(env.get(locator.binding_index)), Environment::Object(obj) => { let obj = obj.clone(); - let key: JsString = self - .interner() - .resolve_expect(locator.name.sym()) - .into_common(false); + let key = locator.name().clone(); obj.get(key, self).map(Some) } } @@ -611,15 +595,12 @@ impl Context { #[track_caller] pub(crate) fn set_binding( &mut self, - locator: BindingLocator, + locator: &BindingLocator, value: JsValue, strict: bool, ) -> JsResult<()> { if locator.global { - let key = self - .interner() - .resolve_expect(locator.name().sym()) - .into_common::(false); + let key = locator.name().clone(); self.global_object().set(key, value, strict, self)?; } else { @@ -629,10 +610,7 @@ impl Context { } Environment::Object(obj) => { let obj = obj.clone(); - let key: JsString = self - .interner() - .resolve_expect(locator.name.sym()) - .into_common(false); + let key = locator.name().clone(); obj.set(key, value, strict, self)?; } @@ -649,22 +627,16 @@ impl Context { /// # Panics /// /// Panics if the environment or binding index are out of range. - pub(crate) fn delete_binding(&mut self, locator: BindingLocator) -> JsResult { + pub(crate) fn delete_binding(&mut self, locator: &BindingLocator) -> JsResult { if locator.is_global() { - let key: JsString = self - .interner() - .resolve_expect(locator.name().sym()) - .into_common::(false); + let key = locator.name().clone(); self.global_object().__delete__(&key.into(), self) } else { match self.environment_expect(locator.environment_index) { Environment::Declarative(_) => Ok(false), Environment::Object(obj) => { let obj = obj.clone(); - let key: JsString = self - .interner() - .resolve_expect(locator.name.sym()) - .into_common(false); + let key = locator.name().clone(); obj.__delete__(&key.into(), self) } diff --git a/boa_engine/src/module/mod.rs b/boa_engine/src/module/mod.rs index 3c564d73cb0..d61c807d236 100644 --- a/boa_engine/src/module/mod.rs +++ b/boa_engine/src/module/mod.rs @@ -34,12 +34,11 @@ use std::io::Read; use std::rc::Rc; use std::{collections::HashSet, hash::BuildHasherDefault}; -use indexmap::IndexMap; +use indexmap::IndexSet; use rustc_hash::{FxHashSet, FxHasher}; -use boa_ast::expression::Identifier; use boa_gc::{Finalize, Gc, GcRefCell, Trace}; -use boa_interner::Sym; +use boa_interner::Interner; use boa_parser::{Parser, Source}; use boa_profiler::Profiler; @@ -102,10 +101,10 @@ pub(crate) struct ResolvedBinding { /// /// Note that a resolved binding can resolve to a single binding inside a module (`export var a = 1"`) /// or to a whole module namespace (`export * as ns from "mod.js"`). -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub(crate) enum BindingName { /// A local binding. - Name(Identifier), + Name(JsString), /// The whole namespace of the containing module. Namespace, } @@ -117,8 +116,8 @@ impl ResolvedBinding { } /// Gets the binding associated with the resolved export. - pub(crate) const fn binding_name(&self) -> BindingName { - self.binding_name + pub(crate) fn binding_name(&self) -> BindingName { + self.binding_name.clone() } } @@ -153,7 +152,7 @@ impl Module { let module = parser.parse_module(context.interner_mut())?; let inner = Gc::new_cyclic(|weak| { - let src = SourceTextModule::new(module, weak.clone()); + let src = SourceTextModule::new(module, weak.clone(), context.interner()); ModuleRepr { realm: realm.unwrap_or_else(|| context.realm().clone()), @@ -180,10 +179,7 @@ impl Module { realm: Option, context: &mut Context, ) -> Self { - let names: FxHashSet = export_names - .iter() - .map(|string| context.interner_mut().get_or_intern(&**string)) - .collect(); + let names = export_names.iter().cloned().collect(); let realm = realm.unwrap_or_else(|| context.realm().clone()); let inner = Gc::new_cyclic(|weak| { let synth = SyntheticModule::new(names, evaluation_steps, weak.clone()); @@ -322,9 +318,13 @@ impl Module { /// This must only be called if the [`JsPromise`] returned by [`Module::load`] has fulfilled. /// /// [spec]: https://tc39.es/ecma262/#table-abstract-methods-of-module-records - fn get_exported_names(&self, export_star_set: &mut Vec) -> FxHashSet { + fn get_exported_names( + &self, + export_star_set: &mut Vec, + interner: &Interner, + ) -> FxHashSet { match self.kind() { - ModuleKind::SourceText(src) => src.get_exported_names(export_star_set), + ModuleKind::SourceText(src) => src.get_exported_names(export_star_set, interner), ModuleKind::Synthetic(synth) => synth.get_exported_names(), } } @@ -343,11 +343,12 @@ impl Module { #[allow(clippy::mutable_key_type)] pub(crate) fn resolve_export( &self, - export_name: Sym, - resolve_set: &mut FxHashSet<(Self, Sym)>, + export_name: JsString, + resolve_set: &mut FxHashSet<(Self, JsString)>, + interner: &Interner, ) -> Result { match self.kind() { - ModuleKind::SourceText(src) => src.resolve_export(export_name, resolve_set), + ModuleKind::SourceText(src) => src.resolve_export(&export_name, resolve_set, interner), ModuleKind::Synthetic(synth) => synth.resolve_export(export_name), } } @@ -531,7 +532,8 @@ impl Module { .borrow_mut() .get_or_insert_with(|| { // a. Let exportedNames be module.GetExportedNames(). - let exported_names = self.get_exported_names(&mut Vec::default()); + let exported_names = + self.get_exported_names(&mut Vec::default(), context.interner()); // b. Let unambiguousNames be a new empty List. let unambiguous_names = exported_names @@ -540,9 +542,13 @@ impl Module { .filter_map(|name| { // i. Let resolution be module.ResolveExport(name). // ii. If resolution is a ResolvedBinding Record, append name to unambiguousNames. - self.resolve_export(name, &mut HashSet::default()) - .ok() - .map(|_| name) + self.resolve_export( + name.clone(), + &mut HashSet::default(), + context.interner(), + ) + .ok() + .map(|_| name) }) .collect(); @@ -576,31 +582,20 @@ impl Hash for Module { pub struct ModuleNamespace { module: Module, #[unsafe_ignore_trace] - exports: IndexMap>, + exports: IndexSet>, } impl ModuleNamespace { /// Abstract operation [`ModuleNamespaceCreate ( module, exports )`][spec]. /// /// [spec]: https://tc39.es/ecma262/#sec-modulenamespacecreate - pub(crate) fn create(module: Module, names: Vec, context: &mut Context) -> JsObject { + pub(crate) fn create(module: Module, names: Vec, context: &mut Context) -> JsObject { // 1. Assert: module.[[Namespace]] is empty. // ignored since this is ensured by `Module::namespace`. // 6. Let sortedExports be a List whose elements are the elements of exports ordered as if an Array of the same values had been sorted using %Array.prototype.sort% using undefined as comparefn. - let mut exports = names - .into_iter() - .map(|sym| { - ( - context - .interner() - .resolve_expect(sym) - .into_common::(false), - sym, - ) - }) - .collect::>(); - exports.sort_keys(); + let mut exports = names.into_iter().collect::>(); + exports.sort(); // 2. Let internalSlotsList be the internal slots listed in Table 32. // 3. Let M be MakeBasicObject(internalSlotsList). @@ -621,7 +616,7 @@ impl ModuleNamespace { } /// Gets the export names of the Module Namespace object. - pub(crate) const fn exports(&self) -> &IndexMap> { + pub(crate) const fn exports(&self) -> &IndexSet> { &self.exports } diff --git a/boa_engine/src/module/source.rs b/boa_engine/src/module/source.rs index 982ee917757..8a25feb172a 100644 --- a/boa_engine/src/module/source.rs +++ b/boa_engine/src/module/source.rs @@ -16,13 +16,14 @@ use boa_ast::{ }, }; use boa_gc::{custom_trace, empty_trace, Finalize, Gc, GcRefCell, Trace, WeakGc}; -use boa_interner::Sym; +use boa_interner::{Interner, Sym}; +use boa_macros::utf16; use indexmap::IndexSet; use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; use crate::{ builtins::{promise::PromiseCapability, Promise}, - bytecompiler::{ByteCompiler, FunctionSpec}, + bytecompiler::{ByteCompiler, FunctionSpec, ToJsString}, environments::{BindingLocator, CompileTimeEnvironment, EnvironmentStack}, module::ModuleKind, object::{FunctionObjectBuilder, JsPromise, RecursionLimiter}, @@ -273,7 +274,7 @@ impl std::fmt::Debug for SourceTextModule { struct Inner { parent: WeakGc, status: GcRefCell, - loaded_modules: GcRefCell>, + loaded_modules: GcRefCell>, async_parent_modules: GcRefCell>, import_meta: GcRefCell>, #[unsafe_ignore_trace] @@ -283,12 +284,12 @@ struct Inner { #[derive(Debug)] struct ModuleCode { has_tla: bool, - requested_modules: IndexSet>, + requested_modules: IndexSet>, source: boa_ast::Module, import_entries: Vec, local_export_entries: Vec, indirect_export_entries: Vec, - star_export_entries: Vec, + star_export_entries: Vec, } impl SourceTextModule { @@ -308,9 +309,18 @@ impl SourceTextModule { /// Contains part of the abstract operation [`ParseModule`][parse]. /// /// [parse]: https://tc39.es/ecma262/#sec-parsemodule - pub(super) fn new(code: boa_ast::Module, parent: WeakGc) -> Self { + pub(super) fn new( + code: boa_ast::Module, + parent: WeakGc, + interner: &Interner, + ) -> Self { // 3. Let requestedModules be the ModuleRequests of body. - let requested_modules = code.items().requests(); + let requested_modules = code + .items() + .requests() + .iter() + .map(|name| name.to_js_string(interner)) + .collect(); // 4. Let importEntries be ImportEntries of body. let import_entries = code.items().import_entries(); @@ -363,7 +373,7 @@ impl SourceTextModule { ExportEntry::StarReExport { module_request } => { // i. Assert: ee.[[ExportName]] is null. // ii. Append ee to starExportEntries. - star_export_entries.push(module_request); + star_export_entries.push(module_request.to_js_string(interner)); } // c. Else, // i. Append ee to indirectExportEntries. @@ -424,7 +434,7 @@ impl SourceTextModule { .pending_modules .set(state.pending_modules.get() + requested.len()); // d. For each String required of module.[[RequestedModules]], do - for &required in requested { + for required in requested.iter().cloned() { // i. If module.[[LoadedModules]] contains a Record whose [[Specifier]] is required, then let loaded = self.inner.loaded_modules.borrow().get(&required).cloned(); if let Some(loaded) = loaded { @@ -436,10 +446,7 @@ impl SourceTextModule { // 1. Perform HostLoadImportedModule(module, required, state.[[HostDefined]], state). // 2. NOTE: HostLoadImportedModule will call FinishLoadingImportedModule, which re-enters // the graph loading process through ContinueModuleLoading. - let name_specifier: JsString = context - .interner() - .resolve_expect(required) - .into_common(false); + let name_specifier = required.clone(); let src = self.clone(); let state = state.clone(); context.module_loader().load_imported_module( @@ -515,7 +522,11 @@ impl SourceTextModule { /// Concrete method [`GetExportedNames ( [ exportStarSet ] )`][spec]. /// /// [spec]: https://tc39.es/ecma262/#sec-getexportednames - pub(super) fn get_exported_names(&self, export_star_set: &mut Vec) -> FxHashSet { + pub(super) fn get_exported_names( + &self, + export_star_set: &mut Vec, + interner: &Interner, + ) -> FxHashSet { // 1. Assert: module.[[Status]] is not new. // 2. If exportStarSet is not present, set exportStarSet to a new empty List. @@ -536,14 +547,16 @@ impl SourceTextModule { for e in &self.inner.code.local_export_entries { // a. Assert: module provides the direct binding for this export. // b. Append e.[[ExportName]] to exportedNames. - exported_names.insert(e.export_name()); + let name = e.export_name().to_js_string(interner); + exported_names.insert(name); } // 7. For each ExportEntry Record e of module.[[IndirectExportEntries]], do for e in &self.inner.code.indirect_export_entries { // a. Assert: module imports a specific binding for this export. // b. Append e.[[ExportName]] to exportedNames. - exported_names.insert(e.export_name()); + let name = e.export_name().to_js_string(interner); + exported_names.insert(name); } // 8. For each ExportEntry Record e of module.[[StarExportEntries]], do @@ -553,9 +566,9 @@ impl SourceTextModule { // b. Let starNames be requestedModule.GetExportedNames(exportStarSet). // c. For each element n of starNames, do - for n in requested_module.get_exported_names(export_star_set) { + for n in requested_module.get_exported_names(export_star_set, interner) { // i. If SameValue(n, "default") is false, then - if n != Sym::DEFAULT { + if &n != utf16!("default") { // 1. If exportedNames does not contain n, then // a. Append n to exportedNames. exported_names.insert(n); @@ -573,32 +586,34 @@ impl SourceTextModule { #[allow(clippy::mutable_key_type)] pub(super) fn resolve_export( &self, - export_name: Sym, - resolve_set: &mut FxHashSet<(Module, Sym)>, + export_name: &JsString, + resolve_set: &mut FxHashSet<(Module, JsString)>, + interner: &Interner, ) -> Result { let parent = self.parent(); // 1. Assert: module.[[Status]] is not new. // 2. If resolveSet is not present, set resolveSet to a new empty List. // 3. For each Record { [[Module]], [[ExportName]] } r of resolveSet, do // a. If module and r.[[Module]] are the same Module Record and SameValue(exportName, r.[[ExportName]]) is true, then - if resolve_set.contains(&(parent.clone(), export_name)) { + let value = (parent.clone(), export_name.clone()); + if resolve_set.contains(&value) { // i. Assert: This is a circular import request. // ii. Return null. return Err(ResolveExportError::NotFound); } // 4. Append the Record { [[Module]]: module, [[ExportName]]: exportName } to resolveSet. - resolve_set.insert((parent.clone(), export_name)); + resolve_set.insert(value); // 5. For each ExportEntry Record e of module.[[LocalExportEntries]], do for e in &self.inner.code.local_export_entries { // a. If SameValue(exportName, e.[[ExportName]]) is true, then - if export_name == e.export_name() { + if export_name == &e.export_name().to_js_string(interner) { // i. Assert: module provides the direct binding for this export. // ii. Return ResolvedBinding Record { [[Module]]: module, [[BindingName]]: e.[[LocalName]] }. return Ok(ResolvedBinding { module: parent, - binding_name: BindingName::Name(e.local_name()), + binding_name: BindingName::Name(e.local_name().to_js_string(interner)), }); } } @@ -606,10 +621,10 @@ impl SourceTextModule { // 6. For each ExportEntry Record e of module.[[IndirectExportEntries]], do for e in &self.inner.code.indirect_export_entries { // a. If SameValue(exportName, e.[[ExportName]]) is true, then - if export_name == e.export_name() { + if export_name == &e.export_name().to_js_string(interner) { // i. Let importedModule be GetImportedModule(module, e.[[ModuleRequest]]). - let imported_module = - self.inner.loaded_modules.borrow()[&e.module_request()].clone(); + let module_request = e.module_request().to_js_string(interner); + let imported_module = self.inner.loaded_modules.borrow()[&module_request].clone(); return match e.import_name() { // ii. If e.[[ImportName]] is all, then // 1. Assert: module does not provide the direct binding for this export. @@ -622,14 +637,15 @@ impl SourceTextModule { // 1. Assert: module imports a specific binding for this export. // 2. Return importedModule.ResolveExport(e.[[ImportName]], resolveSet). ReExportImportName::Name(name) => { - imported_module.resolve_export(name, resolve_set) + let name = name.to_js_string(interner); + imported_module.resolve_export(name, resolve_set, interner) } }; } } // 7. If SameValue(exportName, "default") is true, then - if export_name == Sym::DEFAULT { + if &export_name.clone() == utf16!("default") { // a. Assert: A default export was not explicitly defined by this module. // b. Return null. // c. NOTE: A default export cannot be provided by an export * from "mod" declaration. @@ -644,13 +660,14 @@ impl SourceTextModule { // a. Let importedModule be GetImportedModule(module, e.[[ModuleRequest]]). let imported_module = self.inner.loaded_modules.borrow()[e].clone(); // b. Let resolution be importedModule.ResolveExport(exportName, resolveSet). - let resolution = match imported_module.resolve_export(export_name, resolve_set) { - // d. If resolution is not null, then - Ok(resolution) => resolution, - // c. If resolution is ambiguous, return ambiguous. - Err(e @ ResolveExportError::Ambiguous) => return Err(e), - Err(ResolveExportError::NotFound) => continue, - }; + let resolution = + match imported_module.resolve_export(export_name.clone(), resolve_set, interner) { + // d. If resolution is not null, then + Ok(resolution) => resolution, + // c. If resolution is ambiguous, return ambiguous. + Err(e @ ResolveExportError::Ambiguous) => return Err(e), + Err(ResolveExportError::NotFound) => continue, + }; // i. Assert: resolution is a ResolvedBinding Record. if let Some(star_resolution) = &star_resolution { @@ -661,7 +678,10 @@ impl SourceTextModule { if resolution.module != star_resolution.module { return Err(ResolveExportError::Ambiguous); } - match (resolution.binding_name, star_resolution.binding_name) { + match ( + resolution.binding_name, + star_resolution.binding_name.clone(), + ) { // 3. If resolution.[[BindingName]] is not starResolution.[[BindingName]] and either // resolution.[[BindingName]] or starResolution.[[BindingName]] is namespace, // return ambiguous. @@ -1080,9 +1100,9 @@ impl SourceTextModule { stack.push(self.clone()); // 11. For each String required of module.[[RequestedModules]], do - for &required in &self.inner.code.requested_modules { + for required in &self.inner.code.requested_modules { // a. Let requiredModule be GetImportedModule(module, required). - let required_module = self.inner.loaded_modules.borrow()[&required].clone(); + let required_module = self.inner.loaded_modules.borrow()[required].clone(); // b. Set index to ? InnerModuleEvaluation(requiredModule, stack, index). index = required_module.inner_evaluate(stack, index, context)?; @@ -1379,7 +1399,11 @@ impl SourceTextModule { for e in &self.inner.code.indirect_export_entries { // a. Let resolution be module.ResolveExport(e.[[ExportName]]). parent - .resolve_export(e.export_name(), &mut HashSet::default()) + .resolve_export( + e.export_name().to_js_string(context.interner()), + &mut HashSet::default(), + context.interner(), + ) // b. If resolution is either null or ambiguous, throw a SyntaxError exception. .map_err(|err| match err { ResolveExportError::NotFound => { @@ -1410,8 +1434,14 @@ impl SourceTextModule { let global_compile_env = global_env.compile_env(); let env = Rc::new(CompileTimeEnvironment::new(global_compile_env, true)); - let mut compiler = - ByteCompiler::new(Sym::MAIN, true, false, env.clone(), env.clone(), context); + let mut compiler = ByteCompiler::new( + Sym::MAIN.to_js_string(context.interner()), + true, + false, + env.clone(), + env.clone(), + context, + ); compiler.code_block_flags |= CodeBlockFlags::IS_ASYNC; compiler.async_handler = Some(compiler.push_handler()); @@ -1422,32 +1452,32 @@ impl SourceTextModule { // 7. For each ImportEntry Record in of module.[[ImportEntries]], do for entry in &self.inner.code.import_entries { // a. Let importedModule be GetImportedModule(module, in.[[ModuleRequest]]). - let imported_module = - self.inner.loaded_modules.borrow()[&entry.module_request()].clone(); + let module_request = entry.module_request().to_js_string(compiler.interner()); + let imported_module = self.inner.loaded_modules.borrow()[&module_request].clone(); if let ImportName::Name(name) = entry.import_name() { + let name = name.to_js_string(compiler.interner()); // c. Else, // i. Let resolution be importedModule.ResolveExport(in.[[ImportName]]). - let resolution = - imported_module - .resolve_export(name, &mut HashSet::default()) - // ii. If resolution is either null or ambiguous, throw a SyntaxError exception. - .map_err(|err| match err { - ResolveExportError::NotFound => JsNativeError::syntax() - .with_message(format!( - "could not find export `{}`", - compiler.interner().resolve_expect(name) - )), - ResolveExportError::Ambiguous => JsNativeError::syntax() - .with_message(format!( - "could not resolve ambiguous export `{}`", - compiler.interner().resolve_expect(name) - )), - })?; + let resolution = imported_module + .resolve_export(name.clone(), &mut HashSet::default(), compiler.interner()) + // ii. If resolution is either null or ambiguous, throw a SyntaxError exception. + .map_err(|err| match err { + ResolveExportError::NotFound => JsNativeError::syntax().with_message( + format!("could not find export `{}`", name.to_std_string_escaped()), + ), + ResolveExportError::Ambiguous => { + JsNativeError::syntax().with_message(format!( + "could not resolve ambiguous export `{}`", + name.to_std_string_escaped() + )) + } + })?; // 2. Perform ! env.CreateImmutableBinding(in.[[LocalName]], true). // 3. Perform ! env.InitializeBinding(in.[[LocalName]], namespace). - let locator = env.create_immutable_binding(entry.local_name(), true); + let local_name = entry.local_name().to_js_string(compiler.interner()); + let locator = env.create_immutable_binding(local_name, true); if let BindingName::Name(_) = resolution.binding_name { // 1. Perform env.CreateImportBinding(in.[[LocalName]], resolution.[[Module]], @@ -1469,7 +1499,10 @@ impl SourceTextModule { // b. If in.[[ImportName]] is namespace-object, then // ii. Perform ! env.CreateImmutableBinding(in.[[LocalName]], true). // iii. Perform ! env.InitializeBinding(in.[[LocalName]], namespace). - let locator = env.create_immutable_binding(entry.local_name(), true); + let locator = env.create_immutable_binding( + entry.local_name().to_js_string(compiler.interner()), + true, + ); // i. Let namespace be GetModuleNamespace(importedModule). // deferred to initialization below @@ -1489,11 +1522,13 @@ impl SourceTextModule { for var in var_declarations { // a. For each element dn of the BoundNames of d, do for name in var.bound_names() { + let name = name.to_js_string(compiler.interner()); + // i. If declaredVarNames does not contain dn, then if !declared_var_names.contains(&name) { // 1. Perform ! env.CreateMutableBinding(dn, false). // 2. Perform ! env.InitializeBinding(dn, undefined). - let binding = env.create_mutable_binding(name, false); + let binding = env.create_mutable_binding(name.clone(), false); let index = compiler.get_or_insert_binding(binding); compiler.emit_opcode(Opcode::PushUndefined); compiler.emit_with_varying_operand(Opcode::DefInitVar, index); @@ -1522,31 +1557,32 @@ impl SourceTextModule { // deferred to below. let (spec, locator): (FunctionSpec<'_>, _) = match declaration { LexicallyScopedDeclaration::Function(f) => { - let name = bound_names(f)[0]; + let name = bound_names(f)[0].to_js_string(compiler.interner()); let locator = env.create_mutable_binding(name, false); (f.into(), locator) } LexicallyScopedDeclaration::Generator(g) => { - let name = bound_names(g)[0]; + let name = bound_names(g)[0].to_js_string(compiler.interner()); let locator = env.create_mutable_binding(name, false); (g.into(), locator) } LexicallyScopedDeclaration::AsyncFunction(af) => { - let name = bound_names(af)[0]; + let name = bound_names(af)[0].to_js_string(compiler.interner()); let locator = env.create_mutable_binding(name, false); (af.into(), locator) } LexicallyScopedDeclaration::AsyncGenerator(ag) => { - let name = bound_names(ag)[0]; + let name = bound_names(ag)[0].to_js_string(compiler.interner()); let locator = env.create_mutable_binding(name, false); (ag.into(), locator) } LexicallyScopedDeclaration::Class(class) => { for name in bound_names(class) { + let name = name.to_js_string(compiler.interner()); env.create_mutable_binding(name, false); } continue; @@ -1557,6 +1593,7 @@ impl SourceTextModule { )) => { // a. For each element dn of the BoundNames of d, do for name in bound_names(c) { + let name = name.to_js_string(compiler.interner()); // 1. Perform ! env.CreateImmutableBinding(dn, true). env.create_immutable_binding(name, true); } @@ -1564,12 +1601,14 @@ impl SourceTextModule { } LexicallyScopedDeclaration::LexicalDeclaration(LexicalDeclaration::Let(l)) => { for name in bound_names(l) { + let name = name.to_js_string(compiler.interner()); env.create_mutable_binding(name, false); } continue; } LexicallyScopedDeclaration::AssignmentExpression(expr) => { for name in bound_names(expr) { + let name = name.to_js_string(compiler.interner()); env.create_mutable_binding(name, false); } continue; @@ -1762,7 +1801,7 @@ impl SourceTextModule { } /// Gets the loaded modules of this module. - pub(crate) fn loaded_modules(&self) -> &GcRefCell> { + pub(crate) fn loaded_modules(&self) -> &GcRefCell> { &self.inner.loaded_modules } diff --git a/boa_engine/src/module/synthetic.rs b/boa_engine/src/module/synthetic.rs index 521de2a9a6c..e9965262007 100644 --- a/boa_engine/src/module/synthetic.rs +++ b/boa_engine/src/module/synthetic.rs @@ -1,13 +1,12 @@ use std::rc::Rc; -use boa_ast::expression::Identifier; use boa_gc::{Finalize, Gc, GcRefCell, Trace, WeakGc}; use boa_interner::Sym; use rustc_hash::FxHashSet; use crate::{ builtins::promise::ResolvingFunctions, - bytecompiler::ByteCompiler, + bytecompiler::{ByteCompiler, ToJsString}, environments::{CompileTimeEnvironment, EnvironmentStack}, object::JsPromise, vm::{ActiveRunnable, CallFrame, CodeBlock}, @@ -159,7 +158,7 @@ impl std::fmt::Debug for SyntheticModule { struct Inner { parent: WeakGc, #[unsafe_ignore_trace] - export_names: FxHashSet, + export_names: FxHashSet, eval_context: GcRefCell)>>, eval_steps: SyntheticModuleInitializer, } @@ -178,7 +177,7 @@ impl SyntheticModule { /// Creates a new synthetic module. pub(super) fn new( - names: FxHashSet, + names: FxHashSet, eval_steps: SyntheticModuleInitializer, parent: WeakGc, ) -> Self { @@ -203,7 +202,7 @@ impl SyntheticModule { /// Concrete method [`GetExportedNames ( [ exportStarSet ] )`][spec]. /// /// [spec]: https://tc39.es/proposal-json-modules/#sec-smr-getexportednames - pub(super) fn get_exported_names(&self) -> FxHashSet { + pub(super) fn get_exported_names(&self) -> FxHashSet { // 1. Return module.[[ExportNames]]. self.inner.export_names.clone() } @@ -214,13 +213,13 @@ impl SyntheticModule { #[allow(clippy::mutable_key_type)] pub(super) fn resolve_export( &self, - export_name: Sym, + export_name: JsString, ) -> Result { if self.inner.export_names.contains(&export_name) { // 2. Return ResolvedBinding Record { [[Module]]: module, [[BindingName]]: exportName }. Ok(ResolvedBinding { module: self.parent(), - binding_name: BindingName::Name(Identifier::new(export_name)), + binding_name: BindingName::Name(export_name), }) } else { // 1. If module.[[ExportNames]] does not contain exportName, return null. @@ -243,7 +242,7 @@ impl SyntheticModule { // TODO: A bit of a hack to be able to pass the currently active runnable without an // available codeblock to execute. let compiler = ByteCompiler::new( - Sym::MAIN, + Sym::MAIN.to_js_string(context.interner()), true, false, module_compile_env.clone(), @@ -257,9 +256,8 @@ impl SyntheticModule { .export_names .iter() .map(|name| { - let ident = Identifier::new(*name); // a. Perform ! env.CreateMutableBinding(exportName, false). - module_compile_env.create_mutable_binding(ident, false) + module_compile_env.create_mutable_binding(name.clone(), false) }) .collect::>(); @@ -353,22 +351,14 @@ impl SyntheticModule { /// be passed to the list of exported names in [`Module::synthetic`] beforehand. /// /// [spec]: https://tc39.es/proposal-json-modules/#sec-createsyntheticmodule - pub fn set_export( - &self, - export_name: &JsString, - export_value: JsValue, - context: &mut Context, - ) -> JsResult<()> { - let identifier = context.interner_mut().get_or_intern(&**export_name); - let identifier = Identifier::new(identifier); - + pub fn set_export(&self, export_name: &JsString, export_value: JsValue) -> JsResult<()> { let environment = self .parent() .environment() .expect("this must be initialized before evaluating"); let locator = environment .compile_env() - .get_binding(identifier) + .get_binding(export_name) .ok_or_else(|| { JsNativeError::reference().with_message(format!( "cannot set name `{}` which was not included in the list of exports", diff --git a/boa_engine/src/object/internal_methods/module_namespace.rs b/boa_engine/src/object/internal_methods/module_namespace.rs index a267418d16e..da416768b55 100644 --- a/boa_engine/src/object/internal_methods/module_namespace.rs +++ b/boa_engine/src/object/internal_methods/module_namespace.rs @@ -102,7 +102,7 @@ fn module_namespace_exotic_get_own_property( let exports = obj.exports(); // 3. If exports does not contain P, return undefined. - if !exports.contains_key(&key) { + if !exports.contains(&key) { return Ok(None); } } @@ -183,7 +183,7 @@ fn module_namespace_exotic_has_property( // 3. If exports contains P, return true. // 4. Return false. - Ok(exports.contains_key(&key)) + Ok(exports.contains(&key)) } /// [`[[Get]] ( P, Receiver )`][spec] @@ -211,7 +211,7 @@ fn module_namespace_exotic_get( // 2. Let exports be O.[[Exports]]. let exports = obj.exports(); // 3. If exports does not contain P, return undefined. - let Some(export_name) = exports.get(&key).copied() else { + let Some(export_name) = exports.get(&key).cloned() else { return Ok(JsValue::undefined()); }; @@ -220,7 +220,11 @@ fn module_namespace_exotic_get( // 5. Let binding be m.ResolveExport(P). let binding = m - .resolve_export(export_name, &mut HashSet::default()) + .resolve_export( + export_name.clone(), + &mut HashSet::default(), + context.interner(), + ) .expect("6. Assert: binding is a ResolvedBinding Record."); // 7. Let targetModule be binding.[[Module]]. @@ -232,7 +236,7 @@ fn module_namespace_exotic_get( // 10. Let targetEnv be targetModule.[[Environment]]. let Some(env) = target_module.environment() else { // 11. If targetEnv is empty, throw a ReferenceError exception. - let import = context.interner().resolve_expect(export_name); + let import = export_name.to_std_string_escaped(); return Err(JsNativeError::reference() .with_message(format!( "cannot get import `{import}` from an uninitialized module" @@ -242,12 +246,12 @@ fn module_namespace_exotic_get( let locator = env .compile_env() - .get_binding(name) + .get_binding(&name) .expect("checked before that the name was reachable"); // 12. Return ? targetEnv.GetBindingValue(binding.[[BindingName]], true). env.get(locator.binding_index()).ok_or_else(|| { - let import = context.interner().resolve_expect(export_name); + let import = export_name.to_std_string_escaped(); JsNativeError::reference() .with_message(format!("cannot get uninitialized import `{import}`")) @@ -301,7 +305,7 @@ fn module_namespace_exotic_delete( // 3. If exports contains P, return false. // 4. Return true. - Ok(!exports.contains_key(&key)) + Ok(!exports.contains(&key)) } /// [`[[OwnPropertyKeys]] ( )`][spec]. @@ -324,7 +328,7 @@ fn module_namespace_exotic_own_property_keys( // 3. Return the list-concatenation of exports and symbolKeys. Ok(exports - .keys() + .iter() .map(|k| PropertyKey::String(k.clone())) .chain(symbol_keys) .collect()) diff --git a/boa_engine/src/script.rs b/boa_engine/src/script.rs index 724b05d1135..b484d037870 100644 --- a/boa_engine/src/script.rs +++ b/boa_engine/src/script.rs @@ -17,7 +17,7 @@ use boa_profiler::Profiler; use rustc_hash::FxHashMap; use crate::{ - bytecompiler::ByteCompiler, + bytecompiler::{ByteCompiler, ToJsString}, realm::Realm, vm::{ActiveRunnable, CallFrame, CallFrameFlags, CodeBlock}, Context, HostDefined, JsResult, JsString, JsValue, Module, @@ -116,7 +116,7 @@ impl Script { let _timer = Profiler::global().start_event("Script compilation", "Main"); let mut compiler = ByteCompiler::new( - Sym::MAIN, + Sym::MAIN.to_js_string(context.interner()), self.inner.source.strict(), false, self.inner.realm.environment().compile_env(), diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index 4ba94b1a007..b8681e26d7d 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -12,15 +12,11 @@ use bitflags::bitflags; use boa_ast::function::FormalParameterList; use boa_gc::{empty_trace, Finalize, Gc, Trace}; use boa_profiler::Profiler; -use std::{cell::Cell, mem::size_of, rc::Rc}; +use std::{cell::Cell, fmt::Display, mem::size_of, rc::Rc}; use thin_vec::ThinVec; -#[cfg(any(feature = "trace", feature = "flowgraph"))] use super::{Instruction, InstructionIterator}; -#[cfg(any(feature = "trace", feature = "flowgraph"))] -use boa_interner::{Interner, ToInternedString}; - /// This represents whether a value can be read from [`CodeBlock`] code. /// /// # Safety @@ -352,12 +348,7 @@ impl CodeBlock { /// Modifies the `pc` to point to the next instruction. /// /// Returns an empty `String` if no operands are present. - #[cfg(any(feature = "trace", feature = "flowgraph"))] - pub(crate) fn instruction_operands( - &self, - instruction: &Instruction, - interner: &Interner, - ) -> String { + pub(crate) fn instruction_operands(&self, instruction: &Instruction) -> String { match instruction { Instruction::SetFunctionName { prefix } => match prefix { 0 => "prefix: none", @@ -454,7 +445,9 @@ impl CodeBlock { format!( "{:04}: '{}'", index.value(), - interner.resolve_expect(self.bindings[index.value() as usize].name().sym()), + self.bindings[index.value() as usize] + .name() + .to_std_string_escaped() ) } Instruction::GetPropertyByName { index } @@ -701,20 +694,15 @@ impl CodeBlock { } } -#[cfg(any(feature = "trace", feature = "flowgraph"))] -impl ToInternedString for CodeBlock { - fn to_interned_string(&self, interner: &Interner) -> String { +impl Display for CodeBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let name = self.name(); - let mut f = if name == "
" { - String::new() - } else { - "\n".to_owned() - }; - f.push_str(&format!( - "{:-^70}\nLocation Count Handler Opcode Operands\n\n", + writeln!( + f, + "{:-^70}\nLocation Count Handler Opcode Operands\n", format!("Compiled Output: '{}'", name.to_std_string_escaped()), - )); + )?; let mut iterator = InstructionIterator::new(&self.bytecode); @@ -722,7 +710,7 @@ impl ToInternedString for CodeBlock { while let Some((instruction_start_pc, varying_operand_kind, instruction)) = iterator.next() { let opcode = instruction.opcode().as_str(); - let operands = self.instruction_operands(&instruction, interner); + let operands = self.instruction_operands(&instruction); let pc = iterator.pc(); let handler = if let Some((i, handler)) = self.find_handler(instruction_start_pc as u32) @@ -745,72 +733,77 @@ impl ToInternedString for CodeBlock { super::VaryingOperandKind::U32 => ".U32", }; - f.push_str(&format!( - "{instruction_start_pc:06} {count:04} {handler} {opcode}{varying_operand_kind:<27}{operands}\n", - )); + writeln!( + f, + "{instruction_start_pc:06} {count:04} {handler} {opcode}{varying_operand_kind:<27}{operands}", + )?; count += 1; } - f.push_str("\nConstants:"); + f.write_str("\nConstants:")?; if self.constants.is_empty() { - f.push_str(" \n"); + f.write_str(" \n")?; } else { - f.push('\n'); + f.write_str("\n")?; for (i, value) in self.constants.iter().enumerate() { - f.push_str(&format!(" {i:04}: ")); - let value = match value { + write!(f, " {i:04}: ")?; + match value { Constant::String(v) => { - format!("[STRING] \"{}\"", v.to_std_string_escaped().escape_debug()) + writeln!( + f, + "[STRING] \"{}\"", + v.to_std_string_escaped().escape_debug() + )?; } - Constant::BigInt(v) => format!("[BIGINT] {v}n"), - Constant::Function(code) => format!( + Constant::BigInt(v) => writeln!(f, "[BIGINT] {v}n")?, + Constant::Function(code) => writeln!( + f, "[FUNCTION] name: '{}' (length: {})\n", code.name().to_std_string_escaped(), code.length - ), + )?, Constant::CompileTimeEnvironment(v) => { - format!( + writeln!( + f, "[ENVIRONMENT] index: {}, bindings: {}", v.environment_index(), v.num_bindings() - ) + )?; } - }; - f.push_str(&value); - f.push('\n'); + } } } - f.push_str("\nBindings:\n"); + f.write_str("\nBindings:\n")?; if self.bindings.is_empty() { - f.push_str(" \n"); + f.write_str(" \n")?; } else { for (i, binding_locator) in self.bindings.iter().enumerate() { - f.push_str(&format!( - " {i:04}: {}\n", - interner.resolve_expect(binding_locator.name().sym()) - )); + writeln!( + f, + " {i:04}: {}", + binding_locator.name().to_std_string_escaped() + )?; } } - f.push_str("\nHandlers:\n"); + f.write_str("\nHandlers:\n")?; if self.handlers.is_empty() { - f.push_str(" \n"); + f.write_str(" \n")?; } else { for (i, handler) in self.handlers.iter().enumerate() { - f.push_str(&format!( - " {i:04}: Range: [{:04}, {:04}): Handler: {:04}, Stack: {:02}, Environment: {:02}\n", + writeln!(f, + " {i:04}: Range: [{:04}, {:04}): Handler: {:04}, Stack: {:02}, Environment: {:02}", handler.start, handler.end, handler.handler(), handler.stack_count, handler.environment_count, - )); + )?; } } - - f + Ok(()) } } diff --git a/boa_engine/src/vm/flowgraph/mod.rs b/boa_engine/src/vm/flowgraph/mod.rs index fc8c65527b4..b612b6115df 100644 --- a/boa_engine/src/vm/flowgraph/mod.rs +++ b/boa_engine/src/vm/flowgraph/mod.rs @@ -1,7 +1,6 @@ //! This module is responsible for generating the vm instruction flowgraph. use crate::vm::CodeBlock; -use boa_interner::Interner; use boa_macros::utf16; mod color; @@ -19,7 +18,7 @@ use super::{Constant, Instruction, InstructionIterator}; impl CodeBlock { /// Output the [`CodeBlock`] VM instructions into a [`Graph`]. #[allow(clippy::match_same_arms)] - pub fn to_graph(&self, interner: &Interner, graph: &mut SubGraph) { + pub fn to_graph(&self, graph: &mut SubGraph) { // Have to remove any invalid graph chars like `<` or `>`. let name = if self.name() == utf16!("
") { "__main__".to_string() @@ -34,10 +33,7 @@ impl CodeBlock { let opcode = instruction.opcode(); let opcode_str = opcode.as_str(); - let label = format!( - "{opcode_str} {}", - self.instruction_operands(&instruction, interner) - ); + let label = format!("{opcode_str} {}", self.instruction_operands(&instruction)); let pc = iterator.pc(); @@ -527,7 +523,7 @@ impl CodeBlock { for constant in &self.constants { if let Constant::Function(function) = constant { let subgraph = graph.subgraph(String::new()); - function.to_graph(interner, subgraph); + function.to_graph(subgraph); } } } diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index a924ee1c2ef..d33a8af3621 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -15,8 +15,6 @@ use std::{future::Future, mem::size_of, ops::ControlFlow, pin::Pin, task}; #[cfg(feature = "trace")] use crate::sys::time::Instant; -#[cfg(feature = "trace")] -use boa_interner::ToInternedString; mod call_frame; mod code_block; @@ -269,13 +267,7 @@ impl Context { " VM Start ".to_string() }; - println!( - "{}", - self.vm - .frame() - .code_block - .to_interned_string(self.interner()) - ); + println!("{}", self.vm.frame().code_block); println!( "{msg:-^width$}", width = Self::COLUMN_WIDTH * Self::NUMBER_OF_COLUMNS - 10 @@ -304,7 +296,7 @@ impl Context { .vm .frame() .code_block - .instruction_operands(&instruction, self.interner()); + .instruction_operands(&instruction); let opcode = instruction.opcode(); match opcode { diff --git a/boa_engine/src/vm/opcode/call/mod.rs b/boa_engine/src/vm/opcode/call/mod.rs index 4affc43f6f6..500bb467210 100644 --- a/boa_engine/src/vm/opcode/call/mod.rs +++ b/boa_engine/src/vm/opcode/call/mod.rs @@ -299,15 +299,14 @@ impl Operation for ImportCall { panic!("referrer cannot be a synthetic module"); }; - let sym = context.interner_mut().get_or_intern(&*specifier); - let mut loaded_modules = src.loaded_modules().borrow_mut(); // a. If referrer.[[LoadedModules]] contains a Record whose [[Specifier]] is specifier, then // b. Else, // i. Append the Record { [[Specifier]]: specifier, [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]]. - let entry = - loaded_modules.entry(sym).or_insert_with(|| m.clone()); + let entry = loaded_modules + .entry(specifier) + .or_insert_with(|| m.clone()); // i. Assert: That Record's [[Module]] is result.[[Value]]. debug_assert_eq!(&m, entry); diff --git a/boa_engine/src/vm/opcode/define/mod.rs b/boa_engine/src/vm/opcode/define/mod.rs index 4641229481b..0b6465f8565 100644 --- a/boa_engine/src/vm/opcode/define/mod.rs +++ b/boa_engine/src/vm/opcode/define/mod.rs @@ -20,7 +20,7 @@ impl DefVar { #[allow(clippy::unnecessary_wraps)] fn operation(context: &mut Context, index: usize) -> JsResult { // TODO: spec specifies to return `empty` on empty vars, but we're trying to initialize. - let binding_locator = context.vm.frame().code_block.bindings[index]; + let binding_locator = context.vm.frame().code_block.bindings[index].clone(); context.vm.environments.put_value_if_uninitialized( binding_locator.environment_index(), @@ -62,10 +62,10 @@ pub(crate) struct DefInitVar; impl DefInitVar { fn operation(context: &mut Context, index: usize) -> JsResult { let value = context.vm.pop(); - let mut binding_locator = context.vm.frame().code_block.bindings[index]; + let mut binding_locator = context.vm.frame().code_block.bindings[index].clone(); context.find_runtime_binding(&mut binding_locator)?; context.set_binding( - binding_locator, + &binding_locator, value, context.vm.frame().code_block.strict(), )?; @@ -106,7 +106,7 @@ impl PutLexicalValue { #[allow(clippy::unnecessary_wraps)] fn operation(context: &mut Context, index: usize) -> JsResult { let value = context.vm.pop(); - let binding_locator = context.vm.frame().code_block.bindings[index]; + let binding_locator = context.vm.frame().code_block.bindings[index].clone(); context.vm.environments.put_lexical_value( binding_locator.environment_index(), binding_locator.binding_index(), diff --git a/boa_engine/src/vm/opcode/delete/mod.rs b/boa_engine/src/vm/opcode/delete/mod.rs index 375dd270681..9967a65abb2 100644 --- a/boa_engine/src/vm/opcode/delete/mod.rs +++ b/boa_engine/src/vm/opcode/delete/mod.rs @@ -90,11 +90,11 @@ pub(crate) struct DeleteName; impl DeleteName { fn operation(context: &mut Context, index: usize) -> JsResult { - let mut binding_locator = context.vm.frame().code_block.bindings[index]; + let mut binding_locator = context.vm.frame().code_block.bindings[index].clone(); context.find_runtime_binding(&mut binding_locator)?; - let deleted = context.delete_binding(binding_locator)?; + let deleted = context.delete_binding(&binding_locator)?; context.vm.push(deleted); Ok(CompletionType::Normal) diff --git a/boa_engine/src/vm/opcode/get/name.rs b/boa_engine/src/vm/opcode/get/name.rs index b5560ca1412..47edce6c438 100644 --- a/boa_engine/src/vm/opcode/get/name.rs +++ b/boa_engine/src/vm/opcode/get/name.rs @@ -13,13 +13,10 @@ pub(crate) struct GetName; impl GetName { fn operation(context: &mut Context, index: usize) -> JsResult { - let mut binding_locator = context.vm.frame().code_block.bindings[index]; + let mut binding_locator = context.vm.frame().code_block.bindings[index].clone(); context.find_runtime_binding(&mut binding_locator)?; - let value = context.get_binding(binding_locator)?.ok_or_else(|| { - let name = context - .interner() - .resolve_expect(binding_locator.name().sym()) - .to_string(); + let value = context.get_binding(&binding_locator)?.ok_or_else(|| { + let name = binding_locator.name().to_std_string_escaped(); JsNativeError::reference().with_message(format!("{name} is not defined")) })?; @@ -58,7 +55,7 @@ pub(crate) struct GetLocator; impl GetLocator { fn operation(context: &mut Context, index: usize) -> JsResult { - let mut binding_locator = context.vm.frame().code_block.bindings[index]; + let mut binding_locator = context.vm.frame().code_block.bindings[index].clone(); context.find_runtime_binding(&mut binding_locator)?; context.vm.frame_mut().binding_stack.push(binding_locator); @@ -98,13 +95,10 @@ pub(crate) struct GetNameAndLocator; impl GetNameAndLocator { fn operation(context: &mut Context, index: usize) -> JsResult { - let mut binding_locator = context.vm.frame().code_block.bindings[index]; + let mut binding_locator = context.vm.frame().code_block.bindings[index].clone(); context.find_runtime_binding(&mut binding_locator)?; - let value = context.get_binding(binding_locator)?.ok_or_else(|| { - let name = context - .interner() - .resolve_expect(binding_locator.name().sym()) - .to_string(); + let value = context.get_binding(&binding_locator)?.ok_or_else(|| { + let name = binding_locator.name().to_std_string_escaped(); JsNativeError::reference().with_message(format!("{name} is not defined")) })?; @@ -144,21 +138,18 @@ pub(crate) struct GetNameOrUndefined; impl GetNameOrUndefined { fn operation(context: &mut Context, index: usize) -> JsResult { - let mut binding_locator = context.vm.frame().code_block.bindings[index]; + let mut binding_locator = context.vm.frame().code_block.bindings[index].clone(); let is_global = binding_locator.is_global(); context.find_runtime_binding(&mut binding_locator)?; - let value = if let Some(value) = context.get_binding(binding_locator)? { + let value = if let Some(value) = context.get_binding(&binding_locator)? { value } else if is_global { JsValue::undefined() } else { - let name = context - .interner() - .resolve_expect(binding_locator.name().sym()) - .to_string(); + let name = binding_locator.name().to_std_string_escaped(); return Err(JsNativeError::reference() .with_message(format!("{name} is not defined")) .into()); diff --git a/boa_engine/src/vm/opcode/set/name.rs b/boa_engine/src/vm/opcode/set/name.rs index 79e111f696d..a9646b53c0d 100644 --- a/boa_engine/src/vm/opcode/set/name.rs +++ b/boa_engine/src/vm/opcode/set/name.rs @@ -54,15 +54,15 @@ pub(crate) struct SetName; impl SetName { fn operation(context: &mut Context, index: usize) -> JsResult { - let mut binding_locator = context.vm.frame().code_block.bindings[index]; + let mut binding_locator = context.vm.frame().code_block.bindings[index].clone(); let value = context.vm.pop(); context.find_runtime_binding(&mut binding_locator)?; - verify_initialized(binding_locator, context)?; + verify_initialized(&binding_locator, context)?; context.set_binding( - binding_locator, + &binding_locator, value, context.vm.frame().code_block.strict(), )?; @@ -113,10 +113,10 @@ impl Operation for SetNameByLocator { .expect("locator should have been popped before"); let value = context.vm.pop(); - verify_initialized(binding_locator, context)?; + verify_initialized(&binding_locator, context)?; context.set_binding( - binding_locator, + &binding_locator, value, context.vm.frame().code_block.strict(), )?; @@ -126,21 +126,28 @@ impl Operation for SetNameByLocator { } /// Checks that the binding pointed by `locator` exists and is initialized. -fn verify_initialized(locator: BindingLocator, context: &mut Context) -> JsResult<()> { - if !context.is_initialized_binding(&locator)? { - let key = context.interner().resolve_expect(locator.name().sym()); +fn verify_initialized(locator: &BindingLocator, context: &mut Context) -> JsResult<()> { + if !context.is_initialized_binding(locator)? { + let key = locator.name(); let strict = context.vm.frame().code_block.strict(); let message = if locator.is_global() { - strict.then(|| format!("cannot assign to uninitialized global property `{key}`")) + strict.then(|| { + format!( + "cannot assign to uninitialized global property `{}`", + key.to_std_string_escaped() + ) + }) } else { match context.environment_expect(locator.environment_index()) { - Environment::Declarative(_) => { - Some(format!("cannot assign to uninitialized binding `{key}`")) - } - Environment::Object(_) if strict => { - Some(format!("cannot assign to uninitialized property `{key}`")) - } + Environment::Declarative(_) => Some(format!( + "cannot assign to uninitialized binding `{}`", + key.to_std_string_escaped() + )), + Environment::Object(_) if strict => Some(format!( + "cannot assign to uninitialized property `{}`", + key.to_std_string_escaped() + )), Environment::Object(_) => None, } }; diff --git a/boa_engine/src/vm/tests.rs b/boa_engine/src/vm/tests.rs index 4e473d23bd6..9b863b51efe 100644 --- a/boa_engine/src/vm/tests.rs +++ b/boa_engine/src/vm/tests.rs @@ -1,4 +1,8 @@ -use crate::{js_string, run_test_actions, JsNativeErrorKind, JsValue, TestAction}; +use crate::{ + js_string, property::Attribute, run_test_actions, Context, JsNativeErrorKind, JsValue, + TestAction, +}; +use boa_parser::Source; use indoc::indoc; #[test] @@ -387,3 +391,29 @@ fn super_construction_with_paramater_expression() { TestAction::assert_eq("new Student('Jack').name", js_string!("Jack")), ]); } + +#[test] +fn cross_context_funtion_call() { + let context1 = &mut Context::default(); + let result = context1.eval(Source::from_bytes(indoc! {r" + var global = 100; + + (function x() { + return global; + }) + "})); + + assert!(result.is_ok()); + let result = result.unwrap(); + assert!(result.is_callable()); + + let context2 = &mut Context::default(); + + context2 + .register_global_property(js_string!("func"), result, Attribute::all()) + .unwrap(); + + let result = context2.eval(Source::from_bytes("func()")); + + assert_eq!(result, Ok(JsValue::new(100))); +} diff --git a/boa_examples/src/bin/synthetic.rs b/boa_examples/src/bin/synthetic.rs index 48f17844896..3d7c84f62da 100644 --- a/boa_examples/src/bin/synthetic.rs +++ b/boa_examples/src/bin/synthetic.rs @@ -163,13 +163,13 @@ fn create_operations_module(context: &mut Context) -> Module { // The initializer is evaluated every time a module imports this synthetic module, // so we avoid creating duplicate objects by capturing and cloning them instead. SyntheticModuleInitializer::from_copy_closure_with_captures( - |module, fns, context| { + |module, fns, _| { println!("Running initializer!"); - module.set_export(&js_string!("sum"), fns.0.clone().into(), context)?; - module.set_export(&js_string!("sub"), fns.1.clone().into(), context)?; - module.set_export(&js_string!("mult"), fns.2.clone().into(), context)?; - module.set_export(&js_string!("div"), fns.3.clone().into(), context)?; - module.set_export(&js_string!("sqrt"), fns.4.clone().into(), context)?; + module.set_export(&js_string!("sum"), fns.0.clone().into())?; + module.set_export(&js_string!("sub"), fns.1.clone().into())?; + module.set_export(&js_string!("mult"), fns.2.clone().into())?; + module.set_export(&js_string!("div"), fns.3.clone().into())?; + module.set_export(&js_string!("sqrt"), fns.4.clone().into())?; Ok(()) }, (sum, sub, mult, div, sqrt), diff --git a/docs/boa_object.md b/docs/boa_object.md index 64bc38a6872..0662e0bbadd 100644 --- a/docs/boa_object.md +++ b/docs/boa_object.md @@ -32,30 +32,33 @@ This function returns the compiled bytecode of a function as a string, ------------------------Compiled Output: 'add'------------------------ Location Count Handler Opcode Operands -000000 0000 none PutLexicalValue 0000: 'x' -000005 0001 none PutLexicalValue 0001: 'y' -000010 0002 none RestParameterPop -000011 0003 none GetName 0000: 'x' -000016 0004 none GetName 0001: 'y' -000021 0005 none Add -000022 0006 none SetReturnValue -000023 0007 none Return -000024 0008 none Return - -Literals: - +000000 0000 none CreateMappedArgumentsObject +000001 0001 none PutLexicalValue 2: 0 +000004 0002 none GetArgument 0 +000006 0003 none PutLexicalValue 2: 1 +000009 0004 none GetArgument 1 +000011 0005 none PutLexicalValue 2: 2 +000014 0006 none PushDeclarativeEnvironment 2 +000016 0007 none GetName 0000: 'x' +000018 0008 none GetName 0001: 'y' +000020 0009 none Add +000021 0010 none SetReturnValue +000022 0011 none CheckReturn +000023 0012 none Return +000024 0013 none CheckReturn +000025 0014 none Return + +Constants: + 0000: [ENVIRONMENT] index: 1, bindings: 1 + 0001: [ENVIRONMENT] index: 2, bindings: 3 + 0002: [ENVIRONMENT] index: 3, bindings: 0 Bindings: 0000: x 0001: y -Functions: - - -Handlers: - +Handlers: " ->> ``` ### Function `$boa.function.trace(func, this, ...args)`