Browse Source

Decouple `Context` from `ByteCompiler` (#3829)

* Decouple GlobalDeclarationInstantiation from Context

* Decouple EvalDeclarationInstantiation from Context

* Remove `Context` from `ByteCompiler`

* Fix typo

* Apply review
pull/3832/head
Haled Odat 7 months ago committed by GitHub
parent
commit
6ee208fc5d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 40
      core/engine/src/builtins/eval/mod.rs
  2. 2
      core/engine/src/builtins/function/mod.rs
  3. 2
      core/engine/src/builtins/json/mod.rs
  4. 10
      core/engine/src/bytecompiler/class.rs
  5. 707
      core/engine/src/bytecompiler/declarations.rs
  6. 7
      core/engine/src/bytecompiler/function.rs
  7. 36
      core/engine/src/bytecompiler/mod.rs
  8. 2
      core/engine/src/module/source.rs
  9. 2
      core/engine/src/module/synthetic.rs
  10. 21
      core/engine/src/script.rs
  11. 21
      core/engine/src/vm/code_block.rs
  12. 16
      core/engine/src/vm/flowgraph/mod.rs
  13. 38
      core/engine/src/vm/opcode/control_flow/throw.rs
  14. 53
      core/engine/src/vm/opcode/define/mod.rs
  15. 220
      core/engine/src/vm/opcode/global.rs
  16. 62
      core/engine/src/vm/opcode/mod.rs

40
core/engine/src/builtins/eval/mod.rs

@ -9,11 +9,13 @@
//! [spec]: https://tc39.es/ecma262/#sec-eval-x //! [spec]: https://tc39.es/ecma262/#sec-eval-x
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
use std::rc::Rc;
use crate::{ use crate::{
builtins::{function::OrdinaryFunction, BuiltInObject}, builtins::{function::OrdinaryFunction, BuiltInObject},
bytecompiler::ByteCompiler, bytecompiler::{eval_declaration_instantiation_context, ByteCompiler},
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
environments::Environment, environments::{CompileTimeEnvironment, Environment},
error::JsNativeError, error::JsNativeError,
js_string, js_string,
object::JsObject, object::JsObject,
@ -229,24 +231,48 @@ impl Eval {
let var_environment = context.vm.environments.outer_function_environment(); let var_environment = context.vm.environments.outer_function_environment();
let mut var_env = var_environment.compile_env(); let mut var_env = var_environment.compile_env();
let lex_env = context.vm.environments.current_compile_environment();
let lex_env = Rc::new(CompileTimeEnvironment::new(lex_env, strict));
let mut annex_b_function_names = Vec::new();
eval_declaration_instantiation_context(
&mut annex_b_function_names,
&body,
strict,
if strict { &lex_env } else { &var_env },
&lex_env,
context,
)?;
let mut compiler = ByteCompiler::new( let mut compiler = ByteCompiler::new(
js_string!("<main>"), js_string!("<main>"),
body.strict(), body.strict(),
false, false,
var_env.clone(), var_env.clone(),
context.vm.environments.current_compile_environment(), lex_env.clone(),
context, context.interner_mut(),
); );
let env_index = compiler.push_compile_environment(strict); compiler.current_open_environments_count += 1;
let env_index = compiler.constants.len() as u32;
compiler
.constants
.push(crate::vm::Constant::CompileTimeEnvironment(lex_env.clone()));
compiler.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); compiler.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index);
let lex_env = compiler.lexical_environment.clone();
if strict { if strict {
var_env = lex_env.clone(); var_env = lex_env.clone();
compiler.variable_environment = lex_env.clone(); compiler.variable_environment = lex_env.clone();
} }
compiler.eval_declaration_instantiation(&body, strict, &var_env, &lex_env)?; #[cfg(feature = "annex-b")]
{
compiler.annex_b_function_names = annex_b_function_names;
}
compiler.eval_declaration_instantiation(&body, strict, &var_env, &lex_env);
compiler.compile_statement_list(body.statements(), true, false); compiler.compile_statement_list(body.statements(), true, false);
let code_block = Gc::new(compiler.finish()); let code_block = Gc::new(compiler.finish());

2
core/engine/src/builtins/function/mod.rs

@ -624,7 +624,7 @@ impl BuiltInFunctionObject {
&body, &body,
context.realm().environment().compile_env(), context.realm().environment().compile_env(),
context.realm().environment().compile_env(), context.realm().environment().compile_env(),
context, context.interner_mut(),
); );
let environments = context.vm.environments.pop_to_global(); let environments = context.vm.environments.pop_to_global();

2
core/engine/src/builtins/json/mod.rs

@ -118,7 +118,7 @@ impl Json {
true, true,
context.realm().environment().compile_env(), context.realm().environment().compile_env(),
context.realm().environment().compile_env(), context.realm().environment().compile_env(),
context, context.interner_mut(),
); );
compiler.compile_statement_list(script.statements(), true, false); compiler.compile_statement_list(script.statements(), true, false);
Gc::new(compiler.finish()) Gc::new(compiler.finish())

10
core/engine/src/bytecompiler/class.rs

@ -54,7 +54,7 @@ impl ByteCompiler<'_> {
self.json_parse, self.json_parse,
self.variable_environment.clone(), self.variable_environment.clone(),
self.lexical_environment.clone(), self.lexical_environment.clone(),
self.context, self.interner,
); );
compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR; compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR;
@ -287,7 +287,7 @@ impl ByteCompiler<'_> {
self.json_parse, self.json_parse,
self.variable_environment.clone(), self.variable_environment.clone(),
self.lexical_environment.clone(), self.lexical_environment.clone(),
self.context, self.interner,
); );
// Function environment // Function environment
@ -315,7 +315,7 @@ impl ByteCompiler<'_> {
self.json_parse, self.json_parse,
self.variable_environment.clone(), self.variable_environment.clone(),
self.lexical_environment.clone(), self.lexical_environment.clone(),
self.context, self.interner,
); );
let _ = field_compiler.push_compile_environment(true); let _ = field_compiler.push_compile_environment(true);
if let Some(node) = field { if let Some(node) = field {
@ -353,7 +353,7 @@ impl ByteCompiler<'_> {
self.json_parse, self.json_parse,
self.variable_environment.clone(), self.variable_environment.clone(),
self.lexical_environment.clone(), self.lexical_environment.clone(),
self.context, self.interner,
); );
let _ = field_compiler.push_compile_environment(true); let _ = field_compiler.push_compile_environment(true);
if let Some(node) = field { if let Some(node) = field {
@ -387,7 +387,7 @@ impl ByteCompiler<'_> {
false, false,
self.variable_environment.clone(), self.variable_environment.clone(),
self.lexical_environment.clone(), self.lexical_environment.clone(),
self.context, self.interner,
); );
let _ = compiler.push_compile_environment(true); let _ = compiler.push_compile_environment(true);

707
core/engine/src/bytecompiler/declarations.rs

@ -3,11 +3,12 @@ use std::rc::Rc;
use crate::{ use crate::{
bytecompiler::{ByteCompiler, FunctionCompiler, FunctionSpec, NodeKind}, bytecompiler::{ByteCompiler, FunctionCompiler, FunctionSpec, NodeKind},
environments::CompileTimeEnvironment, environments::CompileTimeEnvironment,
vm::{create_function_object_fast, BindingOpcode, Opcode}, vm::{BindingOpcode, Opcode},
JsNativeError, JsResult, Context, JsNativeError, JsResult,
}; };
use boa_ast::{ use boa_ast::{
declaration::{Binding, LexicalDeclaration, VariableList}, declaration::{Binding, LexicalDeclaration, VariableList},
expression::Identifier,
function::{FormalParameterList, FunctionBody}, function::{FormalParameterList, FunctionBody},
operations::{ operations::{
all_private_identifiers_valid, bound_names, lexically_declared_names, all_private_identifiers_valid, bound_names, lexically_declared_names,
@ -24,6 +25,361 @@ use boa_ast::operations::annex_b_function_declarations_names;
use super::{Operand, ToJsString}; use super::{Operand, ToJsString};
/// `GlobalDeclarationInstantiation ( script, env )`
///
/// This diverges from the specification by separating the context from the compilation process.
/// Many steps are skipped that are done during bytecode compilation.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
#[cfg(not(feature = "annex-b"))]
#[allow(clippy::unnecessary_wraps)]
#[allow(clippy::ptr_arg)]
pub(crate) fn global_declaration_instantiation_context(
_annex_b_function_names: &mut Vec<Identifier>,
_script: &Script,
_env: &Rc<CompileTimeEnvironment>,
_context: &mut Context,
) -> JsResult<()> {
Ok(())
}
/// `GlobalDeclarationInstantiation ( script, env )`
///
/// This diverges from the specification by separating the context from the compilation process.
/// Many steps are skipped that are done during bytecode compilation.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
#[cfg(feature = "annex-b")]
pub(crate) fn global_declaration_instantiation_context(
annex_b_function_names: &mut Vec<Identifier>,
script: &Script,
env: &Rc<CompileTimeEnvironment>,
context: &mut Context,
) -> JsResult<()> {
// SKIP: 1. Let lexNames be the LexicallyDeclaredNames of script.
// SKIP: 2. Let varNames be the VarDeclaredNames of script.
// SKIP: 3. For each element name of lexNames, do
// SKIP: 4. For each element name of varNames, do
// 5. Let varDeclarations be the VarScopedDeclarations of script.
// Note: VarScopedDeclarations for a Script node is TopLevelVarScopedDeclarations.
let var_declarations = var_scoped_declarations(script);
// SKIP: 6. Let functionsToInitialize be a new empty List.
// 7. Let declaredFunctionNames be a new empty List.
let mut declared_function_names = Vec::new();
// 8. For each element d of varDeclarations, in reverse List order, do
for declaration in var_declarations.iter().rev() {
// a. If d is not either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then
// a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.
// a.ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.
let name = match declaration {
VarScopedDeclaration::Function(f) => f.name(),
VarScopedDeclaration::Generator(f) => f.name(),
VarScopedDeclaration::AsyncFunction(f) => f.name(),
VarScopedDeclaration::AsyncGenerator(f) => f.name(),
VarScopedDeclaration::VariableDeclaration(_) => {
continue;
}
};
// a.iii. Let fn be the sole element of the BoundNames of d.
let name = name.expect("function declaration must have a name");
// a.iv. If declaredFunctionNames does not contain fn, then
if !declared_function_names.contains(&name) {
// SKIP: 1. Let fnDefinable be ? env.CanDeclareGlobalFunction(fn).
// SKIP: 2. If fnDefinable is false, throw a TypeError exception.
// 3. Append fn to declaredFunctionNames.
declared_function_names.push(name);
// SKIP: 4. Insert d as the first element of functionsToInitialize.
}
}
// // 9. Let declaredVarNames be a new empty List.
let mut declared_var_names = Vec::new();
// 10. For each element d of varDeclarations, do
// a. If d is either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then
for declaration in var_declarations {
let VarScopedDeclaration::VariableDeclaration(declaration) = declaration else {
continue;
};
// i. For each String vn of the BoundNames of d, do
for name in bound_names(&declaration) {
// 1. If declaredFunctionNames does not contain vn, then
if !declared_function_names.contains(&name) {
// SKIP: a. Let vnDefinable be ? env.CanDeclareGlobalVar(vn).
// SKIP: b. If vnDefinable is false, throw a TypeError exception.
// c. If declaredVarNames does not contain vn, then
if !declared_var_names.contains(&name) {
// i. Append vn to declaredVarNames.
declared_var_names.push(name);
}
}
}
}
// 11. NOTE: No abnormal terminations occur after this algorithm step if the global object is an ordinary object.
// However, if the global object is a Proxy exotic object it may exhibit behaviours
// that cause abnormal terminations in some of the following steps.
// 12. NOTE: Annex B.3.2.2 adds additional steps at this point.
// 12. Perform the following steps:
// a. Let strict be IsStrict of script.
// b. If strict is false, then
if !script.strict() {
let lex_names = lexically_declared_names(script);
// i. Let declaredFunctionOrVarNames be the list-concatenation of declaredFunctionNames and declaredVarNames.
// ii. For each FunctionDeclaration f that is directly contained in the StatementList of a Block, CaseClause,
// or DefaultClause Contained within script, do
for f in annex_b_function_declarations_names(script) {
// 1. Let F be StringValue of the BindingIdentifier of f.
// 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 = f.to_js_string(context.interner());
// a. If env.HasLexicalDeclaration(F) is false, then
if !env.has_lex_binding(&f_string) {
// i. Let fnDefinable be ? env.CanDeclareGlobalVar(F).
let fn_definable = context.can_declare_global_function(&f_string)?;
// ii. If fnDefinable is true, then
if fn_definable {
// i. NOTE: A var binding for F is only instantiated here if it is neither
// a VarDeclaredName nor the name of another FunctionDeclaration.
// ii. If declaredFunctionOrVarNames does not contain F, then
if !declared_function_names.contains(&f) && !declared_var_names.contains(&f)
{
// i. Perform ? env.CreateGlobalVarBinding(F, false).
context.create_global_var_binding(f_string, false)?;
// ii. Append F to declaredFunctionOrVarNames.
declared_function_names.push(f);
}
// iii. When the FunctionDeclaration f is evaluated, perform the following
// steps in place of the FunctionDeclaration Evaluation algorithm provided in 15.2.6:
// i. Let genv be the running execution context's VariableEnvironment.
// ii. Let benv be the running execution context's LexicalEnvironment.
// iii. Let fobj be ! benv.GetBindingValue(F, false).
// iv. Perform ? genv.SetMutableBinding(F, fobj, false).
// v. Return unused.
annex_b_function_names.push(f);
}
}
}
}
}
// SKIP: 13. Let lexDeclarations be the LexicallyScopedDeclarations of script.
// SKIP: 14. Let privateEnv be null.
// SKIP: 15. For each element d of lexDeclarations, do
// SKIP: 16. For each Parse Node f of functionsToInitialize, do
// SKIP: 17. For each String vn of declaredVarNames, do
// 18. Return unused.
Ok(())
}
/// `EvalDeclarationInstantiation ( body, varEnv, lexEnv, privateEnv, strict )`
///
/// This diverges from the specification by separating the context from the compilation process.
/// Many steps are skipped that are done during bytecode compilation.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-evaldeclarationinstantiation
pub(crate) fn eval_declaration_instantiation_context(
#[allow(unused, clippy::ptr_arg)] annex_b_function_names: &mut Vec<Identifier>,
body: &Script,
#[allow(unused)] strict: bool,
#[allow(unused)] var_env: &Rc<CompileTimeEnvironment>,
#[allow(unused)] lex_env: &Rc<CompileTimeEnvironment>,
context: &mut Context,
) -> JsResult<()> {
// SKIP: 3. If strict is false, then
// 4. Let privateIdentifiers be a new empty List.
// 5. Let pointer be privateEnv.
// 6. Repeat, while pointer is not null,
// a. For each Private Name binding of pointer.[[Names]], do
// i. If privateIdentifiers does not contain binding.[[Description]],
// append binding.[[Description]] to privateIdentifiers.
// b. Set pointer to pointer.[[OuterPrivateEnvironment]].
let private_identifiers = context.vm.environments.private_name_descriptions();
let private_identifiers = private_identifiers
.into_iter()
.map(|ident| {
context
.interner()
.get(ident.as_slice())
.expect("string should be in interner")
})
.collect();
// 7. If AllPrivateIdentifiersValid of body with argument privateIdentifiers is false, throw a SyntaxError exception.
if !all_private_identifiers_valid(body, private_identifiers) {
return Err(JsNativeError::syntax()
.with_message("invalid private identifier")
.into());
}
// 2. Let varDeclarations be the VarScopedDeclarations of body.
#[cfg(feature = "annex-b")]
let var_declarations = var_scoped_declarations(body);
// SKIP: 8. Let functionsToInitialize be a new empty List.
// 9. Let declaredFunctionNames be a new empty List.
#[cfg(feature = "annex-b")]
let mut declared_function_names = Vec::new();
// 10. For each element d of varDeclarations, in reverse List order, do
#[cfg(feature = "annex-b")]
for declaration in var_declarations.iter().rev() {
// a. If d is not either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then
// a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.
// a.ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.
let name = match &declaration {
VarScopedDeclaration::Function(f) => f.name(),
VarScopedDeclaration::Generator(f) => f.name(),
VarScopedDeclaration::AsyncFunction(f) => f.name(),
VarScopedDeclaration::AsyncGenerator(f) => f.name(),
VarScopedDeclaration::VariableDeclaration(_) => {
continue;
}
};
// a.iii. Let fn be the sole element of the BoundNames of d.
let name = name.expect("function declaration must have a name");
// a.iv. If declaredFunctionNames does not contain fn, then
if !declared_function_names.contains(&name) {
// SKIP: 1. If varEnv is a Global Environment Record, then
// 2. Append fn to declaredFunctionNames.
declared_function_names.push(name);
// SKIP: 3. Insert d as the first element of functionsToInitialize.
}
}
// 11. NOTE: Annex B.3.2.3 adds additional steps at this point.
// 11. If strict is false, then
#[cfg(feature = "annex-b")]
if !strict {
let lexically_declared_names = lexically_declared_names(body);
// a. Let declaredFunctionOrVarNames be the list-concatenation of declaredFunctionNames and declaredVarNames.
// b. For each FunctionDeclaration f that is directly contained in the StatementList
// of a Block, CaseClause, or DefaultClause Contained within body, do
for f in annex_b_function_declarations_names(body) {
// i. Let F be StringValue of the BindingIdentifier of f.
// ii. If replacing the FunctionDeclaration f with a VariableStatement that has F
// as a BindingIdentifier would not produce any Early Errors for body, then
if !lexically_declared_names.contains(&f) {
// 1. Let bindingExists be false.
let mut binding_exists = false;
// 2. Let thisEnv be lexEnv.
let mut this_env = lex_env.clone();
// 3. Assert: The following loop will terminate.
// 4. Repeat, while thisEnv is not varEnv,
while this_env.environment_index() != lex_env.environment_index() {
let f = f.to_js_string(context.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) {
// i. Let bindingExists be true.
binding_exists = true;
break;
}
// b. Set thisEnv to thisEnv.[[OuterEnv]].
if let Some(outer) = this_env.outer() {
this_env = outer;
} else {
break;
}
}
// 5. If bindingExists is false and varEnv is a Global Environment Record, then
let fn_definable = if !binding_exists && var_env.is_global() {
let f = f.to_js_string(context.interner());
// a. If varEnv.HasLexicalDeclaration(F) is false, then
// b. Else,
if var_env.has_lex_binding(&f) {
// i. Let fnDefinable be false.
false
} else {
// i. Let fnDefinable be ? varEnv.CanDeclareGlobalVar(F).
context.can_declare_global_var(&f)?
}
}
// 6. Else,
else {
// a. Let fnDefinable be true.
true
};
// 7. If bindingExists is false and fnDefinable is true, then
if !binding_exists && fn_definable {
// a. If declaredFunctionOrVarNames does not contain F, then
if !declared_function_names.contains(&f) {
// i. If varEnv is a Global Environment Record, then
if var_env.is_global() {
let f = f.to_js_string(context.interner());
// i. Perform ? varEnv.CreateGlobalVarBinding(F, true).
context.create_global_var_binding(f, true)?;
}
// SKIP: ii. Else,
// SKIP: iii. Append F to declaredFunctionOrVarNames.
}
// b. When the FunctionDeclaration f is evaluated, perform the following steps
// in place of the FunctionDeclaration Evaluation algorithm provided in 15.2.6:
// i. Let genv be the running execution context's VariableEnvironment.
// ii. Let benv be the running execution context's LexicalEnvironment.
// iii. Let fobj be ! benv.GetBindingValue(F, false).
// iv. Perform ? genv.SetMutableBinding(F, fobj, false).
// v. Return unused.
annex_b_function_names.push(f);
}
}
}
}
// SKIP: 12. Let declaredVarNames be a new empty List.
// SKIP: 13. For each element d of varDeclarations, do
// SKIP: 14. NOTE: No abnormal terminations occur after this algorithm step unless varEnv is a
// Global Environment Record and the global object is a Proxy exotic object.
// SKIP: 15. Let lexDeclarations be the LexicallyScopedDeclarations of body.
// SKIP: 16. For each element d of lexDeclarations, do
// SKIP: 17. For each Parse Node f of functionsToInitialize, do
// SKIP: 18. For each String vn of declaredVarNames, do
// 19. Return unused.
Ok(())
}
impl ByteCompiler<'_> { impl ByteCompiler<'_> {
/// `GlobalDeclarationInstantiation ( script, env )` /// `GlobalDeclarationInstantiation ( script, env )`
/// ///
@ -35,7 +391,7 @@ impl ByteCompiler<'_> {
&mut self, &mut self,
script: &Script, script: &Script,
env: &Rc<CompileTimeEnvironment>, env: &Rc<CompileTimeEnvironment>,
) -> JsResult<()> { ) {
// 1. Let lexNames be the LexicallyDeclaredNames of script. // 1. Let lexNames be the LexicallyDeclaredNames of script.
let lex_names = lexically_declared_names(script); let lex_names = lexically_declared_names(script);
@ -44,47 +400,34 @@ impl ByteCompiler<'_> {
// 3. For each element name of lexNames, do // 3. For each element name of lexNames, do
for name in lex_names { for name in lex_names {
let name = self let name = name.to_js_string(self.interner());
.context
.interner()
.resolve_expect(name.sym())
.utf16()
.into();
// Note: Our implementation differs from the spec here. // Note: Our implementation differs from the spec here.
// a. If env.HasVarDeclaration(name) is true, throw a SyntaxError exception. // a. If env.HasVarDeclaration(name) is true, throw a SyntaxError exception.
// b. If env.HasLexicalDeclaration(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() self.emit_syntax_error("duplicate lexical declaration");
.with_message("duplicate lexical declaration") return;
.into());
} }
// c. Let hasRestrictedGlobal be ? env.HasRestrictedGlobalProperty(name). // c. Let hasRestrictedGlobal be ? env.HasRestrictedGlobalProperty(name).
let has_restricted_global = self.context.has_restricted_global_property(&name)?; let index = self.get_or_insert_string(name);
self.emit_with_varying_operand(Opcode::HasRestrictedGlobalProperty, index);
// d. If hasRestrictedGlobal is true, throw a SyntaxError exception. // d. If hasRestrictedGlobal is true, throw a SyntaxError exception.
if has_restricted_global { let exit = self.jump_if_false();
return Err(JsNativeError::syntax() self.emit_syntax_error("cannot redefine non-configurable global property");
.with_message("cannot redefine non-configurable global property") self.patch_jump(exit);
.into());
}
} }
// 4. For each element name of varNames, do // 4. For each element name of varNames, do
for name in var_names { for name in var_names {
let name = self let name = name.to_js_string(self.interner());
.context
.interner()
.resolve_expect(name.sym())
.utf16()
.into();
// a. If env.HasLexicalDeclaration(name) is true, throw a SyntaxError exception. // 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() self.emit_syntax_error("duplicate lexical declaration");
.with_message("duplicate lexical declaration") return;
.into());
} }
} }
@ -119,16 +462,13 @@ impl ByteCompiler<'_> {
// a.iv. If declaredFunctionNames does not contain fn, then // a.iv. If declaredFunctionNames does not contain fn, then
if !declared_function_names.contains(&name) { if !declared_function_names.contains(&name) {
// 1. Let fnDefinable be ? env.CanDeclareGlobalFunction(fn). // 1. Let fnDefinable be ? env.CanDeclareGlobalFunction(fn).
let fn_definable = self let index = self.get_or_insert_name(name);
.context self.emit_with_varying_operand(Opcode::CanDeclareGlobalFunction, index);
.can_declare_global_function(&name.to_js_string(self.interner()))?;
// 2. If fnDefinable is false, throw a TypeError exception. // 2. If fnDefinable is false, throw a TypeError exception.
if !fn_definable { let exit = self.jump_if_true();
return Err(JsNativeError::typ() self.emit_type_error("cannot declare global function");
.with_message("cannot declare global function") self.patch_jump(exit);
.into());
}
// 3. Append fn to declaredFunctionNames. // 3. Append fn to declaredFunctionNames.
declared_function_names.push(name); declared_function_names.push(name);
@ -155,16 +495,13 @@ impl ByteCompiler<'_> {
// 1. If declaredFunctionNames does not contain vn, then // 1. If declaredFunctionNames does not contain vn, then
if !declared_function_names.contains(&name) { if !declared_function_names.contains(&name) {
// a. Let vnDefinable be ? env.CanDeclareGlobalVar(vn). // a. Let vnDefinable be ? env.CanDeclareGlobalVar(vn).
let definable = self let index = self.get_or_insert_name(name);
.context self.emit_with_varying_operand(Opcode::CanDeclareGlobalVar, index);
.can_declare_global_var(&name.to_js_string(self.interner()))?;
// b. If vnDefinable is false, throw a TypeError exception. // b. If vnDefinable is false, throw a TypeError exception.
if !definable { let exit = self.jump_if_true();
return Err(JsNativeError::typ() self.emit_type_error("cannot declare global variable");
.with_message("cannot declare global variable") self.patch_jump(exit);
.into());
}
// c. If declaredVarNames does not contain vn, then // c. If declaredVarNames does not contain vn, then
if !declared_var_names.contains(&name) { if !declared_var_names.contains(&name) {
@ -175,60 +512,15 @@ impl ByteCompiler<'_> {
} }
} }
// 11. NOTE: No abnormal terminations occur after this algorithm step if the global object is an ordinary object. // NOTE: These steps depend on the global object are done before bytecode compilation.
//
// SKIP: 11. NOTE: No abnormal terminations occur after this algorithm step if the global object is an ordinary object.
// However, if the global object is a Proxy exotic object it may exhibit behaviours // However, if the global object is a Proxy exotic object it may exhibit behaviours
// that cause abnormal terminations in some of the following steps. // that cause abnormal terminations in some of the following steps.
// SKIP: 12. NOTE: Annex B.3.2.2 adds additional steps at this point.
// 12. NOTE: Annex B.3.2.2 adds additional steps at this point. // SKIP: 12. Perform the following steps:
// 12. Perform the following steps: // SKIP: a. Let strict be IsStrict of script.
// a. Let strict be IsStrict of script. // SKIP: b. If strict is false, then
// b. If strict is false, then
#[cfg(feature = "annex-b")]
if !script.strict() {
let lex_names = lexically_declared_names(script);
// i. Let declaredFunctionOrVarNames be the list-concatenation of declaredFunctionNames and declaredVarNames.
// ii. For each FunctionDeclaration f that is directly contained in the StatementList of a Block, CaseClause,
// or DefaultClause Contained within script, do
for f in annex_b_function_declarations_names(script) {
// 1. Let F be StringValue of the BindingIdentifier of f.
// 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_string) {
// i. Let fnDefinable be ? env.CanDeclareGlobalVar(F).
let fn_definable = self.context.can_declare_global_function(&f_string)?;
// ii. If fnDefinable is true, then
if fn_definable {
// i. NOTE: A var binding for F is only instantiated here if it is neither
// a VarDeclaredName nor the name of another FunctionDeclaration.
// ii. If declaredFunctionOrVarNames does not contain F, then
if !declared_function_names.contains(&f)
&& !declared_var_names.contains(&f)
{
// i. Perform ? env.CreateGlobalVarBinding(F, false).
self.context.create_global_var_binding(f_string, false)?;
// ii. Append F to declaredFunctionOrVarNames.
declared_function_names.push(f);
}
// iii. When the FunctionDeclaration f is evaluated, perform the following
// steps in place of the FunctionDeclaration Evaluation algorithm provided in 15.2.6:
// i. Let genv be the running execution context's VariableEnvironment.
// ii. Let benv be the running execution context's LexicalEnvironment.
// iii. Let fobj be ! benv.GetBindingValue(F, false).
// iv. Perform ? genv.SetMutableBinding(F, fobj, false).
// v. Return unused.
self.annex_b_function_names.push(f);
}
}
}
}
}
// 13. Let lexDeclarations be the LexicallyScopedDeclarations of script. // 13. Let lexDeclarations be the LexicallyScopedDeclarations of script.
// 14. Let privateEnv be null. // 14. Let privateEnv be null.
@ -298,30 +590,34 @@ impl ByteCompiler<'_> {
body, body,
self.variable_environment.clone(), self.variable_environment.clone(),
self.lexical_environment.clone(), self.lexical_environment.clone(),
self.context, self.interner,
); );
// Ensures global functions are printed when generating the global flowgraph. // Ensures global functions are printed when generating the global flowgraph.
let _ = self.push_function_to_constants(code.clone()); let function_index = self.push_function_to_constants(code.clone());
// b. Let fo be InstantiateFunctionObject of f with arguments env and privateEnv. // b. Let fo be InstantiateFunctionObject of f with arguments env and privateEnv.
let function = create_function_object_fast(code, self.context); self.emit_with_varying_operand(Opcode::GetFunction, function_index);
// c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false). // c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false).
let name = name.to_js_string(self.interner()); let name_index = self.get_or_insert_name(name);
self.context self.emit(
.create_global_function_binding(name, function, false)?; Opcode::CreateGlobalFunctionBinding,
&[Operand::Bool(false), Operand::Varying(name_index)],
);
} }
// 17. For each String vn of declaredVarNames, do // 17. For each String vn of declaredVarNames, do
for var in declared_var_names { for var in declared_var_names {
// a. Perform ? env.CreateGlobalVarBinding(vn, false). // a. Perform ? env.CreateGlobalVarBinding(vn, false).
let var = var.to_js_string(self.interner()); let index = self.get_or_insert_name(var);
self.context.create_global_var_binding(var, false)?; self.emit(
Opcode::CreateGlobalVarBinding,
&[Operand::Bool(false), Operand::Varying(index)],
);
} }
// 18. Return unused. // 18. Return unused.
Ok(())
} }
/// `BlockDeclarationInstantiation ( code, env )` /// `BlockDeclarationInstantiation ( code, env )`
@ -416,7 +712,7 @@ impl ByteCompiler<'_> {
strict: bool, strict: bool,
var_env: &Rc<CompileTimeEnvironment>, var_env: &Rc<CompileTimeEnvironment>,
lex_env: &Rc<CompileTimeEnvironment>, lex_env: &Rc<CompileTimeEnvironment>,
) -> JsResult<()> { ) {
// 2. Let varDeclarations be the VarScopedDeclarations of body. // 2. Let varDeclarations be the VarScopedDeclarations of body.
let var_declarations = var_scoped_declarations(body); let var_declarations = var_scoped_declarations(body);
@ -434,9 +730,8 @@ impl ByteCompiler<'_> {
// 1. If varEnv.HasLexicalDeclaration(name) is true, throw a SyntaxError exception. // 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. // 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() self.emit_syntax_error("duplicate lexical declaration");
.with_message("duplicate lexical declaration") return;
.into());
} }
} }
} }
@ -452,19 +747,15 @@ impl ByteCompiler<'_> {
// declaration so it doesn't need to be checked for var/let hoisting conflicts. // declaration so it doesn't need to be checked for var/let hoisting conflicts.
// 2. For each element name of varNames, do // 2. For each element name of varNames, do
for name in &var_names { for name in &var_names {
let name = self let name = self.interner().resolve_expect(name.sym()).utf16().into();
.context
.interner()
.resolve_expect(name.sym())
.utf16()
.into();
// a. If ! thisEnv.HasBinding(name) is true, then // 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. // i. Throw a SyntaxError exception.
// ii. NOTE: Annex B.3.4 defines alternate semantics for the above step. // ii. NOTE: Annex B.3.4 defines alternate semantics for the above step.
let msg = format!("variable declaration {} in eval function already exists as a lexical variable", name.to_std_string_escaped()); 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()); self.emit_syntax_error(&msg);
return;
} }
// b. NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration. // b. NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration.
} }
@ -478,30 +769,17 @@ impl ByteCompiler<'_> {
} }
} }
// 4. Let privateIdentifiers be a new empty List. // NOTE: These steps depend on the current environment state are done before bytecode compilation,
// 5. Let pointer be privateEnv. // in `eval_declaration_instantiation_context`.
// 6. Repeat, while pointer is not null, //
// a. For each Private Name binding of pointer.[[Names]], do // SKIP: 4. Let privateIdentifiers be a new empty List.
// i. If privateIdentifiers does not contain binding.[[Description]], // SKIP: 5. Let pointer be privateEnv.
// append binding.[[Description]] to privateIdentifiers. // SKIP: 6. Repeat, while pointer is not null,
// b. Set pointer to pointer.[[OuterPrivateEnvironment]]. // a. For each Private Name binding of pointer.[[Names]], do
let private_identifiers = self.context.vm.environments.private_name_descriptions(); // i. If privateIdentifiers does not contain binding.[[Description]],
let private_identifiers = private_identifiers // append binding.[[Description]] to privateIdentifiers.
.into_iter() // b. Set pointer to pointer.[[OuterPrivateEnvironment]].
.map(|ident| { // SKIP: 7. If AllPrivateIdentifiersValid of body with argument privateIdentifiers is false, throw a SyntaxError exception.
self.context
.interner()
.get(ident.as_slice())
.expect("string should be in interner")
})
.collect();
// 7. If AllPrivateIdentifiersValid of body with argument privateIdentifiers is false, throw a SyntaxError exception.
if !all_private_identifiers_valid(body, private_identifiers) {
return Err(JsNativeError::syntax()
.with_message("invalid private identifier")
.into());
}
// 8. Let functionsToInitialize be a new empty List. // 8. Let functionsToInitialize be a new empty List.
let mut functions_to_initialize = Vec::new(); let mut functions_to_initialize = Vec::new();
@ -531,17 +809,15 @@ impl ByteCompiler<'_> {
if !declared_function_names.contains(&name) { if !declared_function_names.contains(&name) {
// 1. If varEnv is a Global Environment Record, then // 1. If varEnv is a Global Environment Record, then
if var_env.is_global() { if var_env.is_global() {
let name = name.to_js_string(self.interner()); let index = self.get_or_insert_name(name);
// a. Let fnDefinable be ? varEnv.CanDeclareGlobalFunction(fn). // a. Let fnDefinable be ? varEnv.CanDeclareGlobalFunction(fn).
let fn_definable = self.context.can_declare_global_function(&name)?; self.emit_with_varying_operand(Opcode::CanDeclareGlobalFunction, index);
// b. If fnDefinable is false, throw a TypeError exception. // b. If fnDefinable is false, throw a TypeError exception.
if !fn_definable { let exit = self.jump_if_true();
return Err(JsNativeError::typ() self.emit_type_error("cannot declare global function");
.with_message("cannot declare global function") self.patch_jump(exit);
.into());
}
} }
// 2. Append fn to declaredFunctionNames. // 2. Append fn to declaredFunctionNames.
@ -558,105 +834,20 @@ impl ByteCompiler<'_> {
// 11. If strict is false, then // 11. If strict is false, then
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
if !strict { if !strict {
let lexically_declared_names = lexically_declared_names(body); // NOTE: This diviates from the specification, we split the first part of defining the annex-b names
// in `eval_declaration_instantiation_context`, because it depends on the context.
// a. Let declaredFunctionOrVarNames be the list-concatenation of declaredFunctionNames and declaredVarNames. if !var_env.is_global() {
// b. For each FunctionDeclaration f that is directly contained in the StatementList for name in self.annex_b_function_names.clone() {
// of a Block, CaseClause, or DefaultClause Contained within body, do let f = name.to_js_string(self.interner());
for f in annex_b_function_declarations_names(body) { // i. Let bindingExists be ! varEnv.HasBinding(F).
// i. Let F be StringValue of the BindingIdentifier of f. // ii. If bindingExists is false, then
// ii. If replacing the FunctionDeclaration f with a VariableStatement that has F if !var_env.has_binding(&f) {
// as a BindingIdentifier would not produce any Early Errors for body, then // i. Perform ! varEnv.CreateMutableBinding(F, true).
if !lexically_declared_names.contains(&f) { // ii. Perform ! varEnv.InitializeBinding(F, undefined).
// 1. Let bindingExists be false. let binding = var_env.create_mutable_binding(f, true);
let mut binding_exists = false; let index = self.get_or_insert_binding(binding);
self.emit_opcode(Opcode::PushUndefined);
// 2. Let thisEnv be lexEnv. self.emit_with_varying_operand(Opcode::DefInitVar, index);
let mut this_env = lex_env.clone();
// 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) {
// i. Let bindingExists be true.
binding_exists = true;
break;
}
// b. Set thisEnv to thisEnv.[[OuterEnv]].
if let Some(outer) = this_env.outer() {
this_env = outer;
} else {
break;
}
}
// 5. If bindingExists is false and varEnv is a Global Environment Record, then
let fn_definable = if !binding_exists && var_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) {
// i. Let fnDefinable be false.
false
} else {
// i. Let fnDefinable be ? varEnv.CanDeclareGlobalVar(F).
self.context.can_declare_global_var(&f)?
}
}
// 6. Else,
else {
// a. Let fnDefinable be true.
true
};
// 7. If bindingExists is false and fnDefinable is true, then
if !binding_exists && fn_definable {
let mut function_names = Vec::new();
// a. If declaredFunctionOrVarNames does not contain F, then
if !declared_function_names.contains(&f)
//&& !var_names.contains(&f)
&& !function_names.contains(&f)
{
// 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) {
// i. Perform ! varEnv.CreateMutableBinding(F, true).
// ii. Perform ! varEnv.InitializeBinding(F, undefined).
let binding = var_env.create_mutable_binding(f, true);
let index = self.get_or_insert_binding(binding);
self.emit_opcode(Opcode::PushUndefined);
self.emit_with_varying_operand(Opcode::DefInitVar, index);
}
}
// iii. Append F to declaredFunctionOrVarNames.
function_names.push(f);
}
// b. When the FunctionDeclaration f is evaluated, perform the following steps
// in place of the FunctionDeclaration Evaluation algorithm provided in 15.2.6:
// i. Let genv be the running execution context's VariableEnvironment.
// ii. Let benv be the running execution context's LexicalEnvironment.
// iii. Let fobj be ! benv.GetBindingValue(F, false).
// iv. Perform ? genv.SetMutableBinding(F, fobj, false).
// v. Return unused.
self.annex_b_function_names.push(f);
} }
} }
} }
@ -678,17 +869,15 @@ impl ByteCompiler<'_> {
if !declared_function_names.contains(&name) { if !declared_function_names.contains(&name) {
// a. If varEnv is a Global Environment Record, then // a. If varEnv is a Global Environment Record, then
if var_env.is_global() { if var_env.is_global() {
let name = name.to_js_string(self.interner()); let index = self.get_or_insert_name(name);
// i. Let vnDefinable be ? varEnv.CanDeclareGlobalVar(vn). // i. Let vnDefinable be ? varEnv.CanDeclareGlobalVar(vn).
let vn_definable = self.context.can_declare_global_var(&name)?; self.emit_with_varying_operand(Opcode::CanDeclareGlobalVar, index);
// ii. If vnDefinable is false, throw a TypeError exception. // ii. If vnDefinable is false, throw a TypeError exception.
if !vn_definable { let exit = self.jump_if_true();
return Err(JsNativeError::typ() self.emit_type_error("cannot declare global function");
.with_message("cannot declare global variable") self.patch_jump(exit);
.into());
}
} }
// b. If declaredVarNames does not contain vn, then // b. If declaredVarNames does not contain vn, then
@ -769,7 +958,7 @@ impl ByteCompiler<'_> {
body, body,
self.variable_environment.clone(), self.variable_environment.clone(),
self.lexical_environment.clone(), self.lexical_environment.clone(),
self.context, self.interner,
); );
// c. If varEnv is a Global Environment Record, then // c. If varEnv is a Global Environment Record, then
@ -818,15 +1007,20 @@ impl ByteCompiler<'_> {
// 18. For each String vn of declaredVarNames, do // 18. For each String vn of declaredVarNames, do
for name in declared_var_names { for name in declared_var_names {
let name = name.to_js_string(self.interner());
// a. If varEnv is a Global Environment Record, then // a. If varEnv is a Global Environment Record, then
if var_env.is_global() { if var_env.is_global() {
let index = self.get_or_insert_name(name);
// i. Perform ? varEnv.CreateGlobalVarBinding(vn, true). // i. Perform ? varEnv.CreateGlobalVarBinding(vn, true).
self.context.create_global_var_binding(name, true)?; self.emit(
Opcode::CreateGlobalVarBinding,
&[Operand::Bool(true), Operand::Varying(index)],
);
} }
// b. Else, // b. Else,
else { else {
let name = name.to_js_string(self.interner());
// i. Let bindingExists be ! varEnv.HasBinding(vn). // i. Let bindingExists be ! varEnv.HasBinding(vn).
let binding_exists = var_env.has_binding(&name); let binding_exists = var_env.has_binding(&name);
@ -844,7 +1038,6 @@ impl ByteCompiler<'_> {
} }
// 19. Return unused. // 19. Return unused.
Ok(())
} }
/// `FunctionDeclarationInstantiation ( func, argumentsList )` /// `FunctionDeclarationInstantiation ( func, argumentsList )`

7
core/engine/src/bytecompiler/function.rs

@ -6,10 +6,11 @@ use crate::{
environments::CompileTimeEnvironment, environments::CompileTimeEnvironment,
js_string, js_string,
vm::{CodeBlock, CodeBlockFlags, Opcode}, vm::{CodeBlock, CodeBlockFlags, Opcode},
Context, JsString, JsString,
}; };
use boa_ast::function::{FormalParameterList, FunctionBody}; use boa_ast::function::{FormalParameterList, FunctionBody};
use boa_gc::Gc; use boa_gc::Gc;
use boa_interner::Interner;
/// `FunctionCompiler` is used to compile AST functions to bytecode. /// `FunctionCompiler` is used to compile AST functions to bytecode.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -91,7 +92,7 @@ impl FunctionCompiler {
body: &FunctionBody, body: &FunctionBody,
variable_environment: Rc<CompileTimeEnvironment>, variable_environment: Rc<CompileTimeEnvironment>,
lexical_environment: Rc<CompileTimeEnvironment>, lexical_environment: Rc<CompileTimeEnvironment>,
context: &mut Context, interner: &mut Interner,
) -> Gc<CodeBlock> { ) -> Gc<CodeBlock> {
self.strict = self.strict || body.strict(); self.strict = self.strict || body.strict();
@ -103,7 +104,7 @@ impl FunctionCompiler {
false, false,
variable_environment, variable_environment,
lexical_environment, lexical_environment,
context, interner,
); );
compiler.length = length; compiler.length = length;
compiler compiler

36
core/engine/src/bytecompiler/mod.rs

@ -21,7 +21,7 @@ use crate::{
BindingOpcode, CodeBlock, CodeBlockFlags, Constant, GeneratorResumeKind, Handler, BindingOpcode, CodeBlock, CodeBlockFlags, Constant, GeneratorResumeKind, Handler,
InlineCache, Opcode, VaryingOperandKind, InlineCache, Opcode, VaryingOperandKind,
}, },
Context, JsBigInt, JsString, JsBigInt, JsString,
}; };
use boa_ast::{ use boa_ast::{
declaration::{Binding, LexicalDeclaration, VarDeclaration}, declaration::{Binding, LexicalDeclaration, VarDeclaration},
@ -41,10 +41,13 @@ use boa_ast::{
use boa_gc::Gc; use boa_gc::Gc;
use boa_interner::{Interner, Sym}; use boa_interner::{Interner, Sym};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use thin_vec::ThinVec;
pub(crate) use declarations::{
eval_declaration_instantiation_context, global_declaration_instantiation_context,
};
pub(crate) use function::FunctionCompiler; pub(crate) use function::FunctionCompiler;
pub(crate) use jump_control::JumpControlInfo; pub(crate) use jump_control::JumpControlInfo;
use thin_vec::ThinVec;
pub(crate) trait ToJsString { pub(crate) trait ToJsString {
fn to_js_string(&self, interner: &Interner) -> JsString; fn to_js_string(&self, interner: &Interner) -> JsString;
@ -277,7 +280,7 @@ pub struct ByteCompiler<'ctx> {
/// The current lexical environment. /// The current lexical environment.
pub(crate) lexical_environment: Rc<CompileTimeEnvironment>, pub(crate) lexical_environment: Rc<CompileTimeEnvironment>,
current_open_environments_count: u32, pub(crate) current_open_environments_count: u32,
current_stack_value_count: u32, current_stack_value_count: u32,
pub(crate) code_block_flags: CodeBlockFlags, pub(crate) code_block_flags: CodeBlockFlags,
handlers: ThinVec<Handler>, handlers: ThinVec<Handler>,
@ -293,11 +296,10 @@ pub struct ByteCompiler<'ctx> {
pub(crate) async_handler: Option<u32>, pub(crate) async_handler: Option<u32>,
json_parse: bool, json_parse: bool,
// TODO: remove when we separate scripts from the context pub(crate) interner: &'ctx mut Interner,
pub(crate) context: &'ctx mut Context,
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
annex_b_function_names: Vec<Identifier>, pub(crate) annex_b_function_names: Vec<Identifier>,
} }
impl<'ctx> ByteCompiler<'ctx> { impl<'ctx> ByteCompiler<'ctx> {
@ -313,8 +315,7 @@ impl<'ctx> ByteCompiler<'ctx> {
json_parse: bool, json_parse: bool,
variable_environment: Rc<CompileTimeEnvironment>, variable_environment: Rc<CompileTimeEnvironment>,
lexical_environment: Rc<CompileTimeEnvironment>, lexical_environment: Rc<CompileTimeEnvironment>,
// TODO: remove when we separate scripts from the context interner: &'ctx mut Interner,
context: &'ctx mut Context,
) -> ByteCompiler<'ctx> { ) -> ByteCompiler<'ctx> {
let mut code_block_flags = CodeBlockFlags::empty(); let mut code_block_flags = CodeBlockFlags::empty();
code_block_flags.set(CodeBlockFlags::STRICT, strict); code_block_flags.set(CodeBlockFlags::STRICT, strict);
@ -343,7 +344,7 @@ impl<'ctx> ByteCompiler<'ctx> {
json_parse, json_parse,
variable_environment, variable_environment,
lexical_environment, lexical_environment,
context, interner,
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
annex_b_function_names: Vec::new(), annex_b_function_names: Vec::new(),
@ -367,7 +368,7 @@ impl<'ctx> ByteCompiler<'ctx> {
} }
pub(crate) fn interner(&self) -> &Interner { pub(crate) fn interner(&self) -> &Interner {
self.context.interner() self.interner
} }
fn get_or_insert_literal(&mut self, literal: Literal) -> u32 { fn get_or_insert_literal(&mut self, literal: Literal) -> u32 {
@ -568,6 +569,15 @@ impl<'ctx> ByteCompiler<'ctx> {
self.emit_with_varying_operand(Opcode::SetPropertyByName, ic_index); self.emit_with_varying_operand(Opcode::SetPropertyByName, ic_index);
} }
fn emit_type_error(&mut self, message: &str) {
let error_msg = self.get_or_insert_literal(Literal::String(js_string!(message)));
self.emit_with_varying_operand(Opcode::ThrowNewTypeError, error_msg);
}
fn emit_syntax_error(&mut self, message: &str) {
let error_msg = self.get_or_insert_literal(Literal::String(js_string!(message)));
self.emit_with_varying_operand(Opcode::ThrowNewSyntaxError, error_msg);
}
fn emit_u64(&mut self, value: u64) { fn emit_u64(&mut self, value: u64) {
self.bytecode.extend(value.to_ne_bytes()); self.bytecode.extend(value.to_ne_bytes());
} }
@ -1308,7 +1318,7 @@ impl<'ctx> ByteCompiler<'ctx> {
body, body,
self.variable_environment.clone(), self.variable_environment.clone(),
self.lexical_environment.clone(), self.lexical_environment.clone(),
self.context, self.interner,
); );
self.push_function_to_constants(code) self.push_function_to_constants(code)
@ -1383,7 +1393,7 @@ impl<'ctx> ByteCompiler<'ctx> {
body, body,
self.variable_environment.clone(), self.variable_environment.clone(),
self.lexical_environment.clone(), self.lexical_environment.clone(),
self.context, self.interner,
); );
let index = self.push_function_to_constants(code); let index = self.push_function_to_constants(code);
@ -1430,7 +1440,7 @@ impl<'ctx> ByteCompiler<'ctx> {
body, body,
self.variable_environment.clone(), self.variable_environment.clone(),
self.lexical_environment.clone(), self.lexical_environment.clone(),
self.context, self.interner,
); );
let index = self.push_function_to_constants(code); let index = self.push_function_to_constants(code);

2
core/engine/src/module/source.rs

@ -1432,7 +1432,7 @@ impl SourceTextModule {
false, false,
env.clone(), env.clone(),
env.clone(), env.clone(),
context, context.interner_mut(),
); );
compiler.code_block_flags |= CodeBlockFlags::IS_ASYNC; compiler.code_block_flags |= CodeBlockFlags::IS_ASYNC;

2
core/engine/src/module/synthetic.rs

@ -286,7 +286,7 @@ impl SyntheticModule {
false, false,
module_compile_env.clone(), module_compile_env.clone(),
module_compile_env.clone(), module_compile_env.clone(),
context, context.interner_mut(),
); );
// 4. For each String exportName in module.[[ExportNames]], do // 4. For each String exportName in module.[[ExportNames]], do

21
core/engine/src/script.rs

@ -17,7 +17,7 @@ use boa_parser::{source::ReadChar, Parser, Source};
use boa_profiler::Profiler; use boa_profiler::Profiler;
use crate::{ use crate::{
bytecompiler::ByteCompiler, bytecompiler::{global_declaration_instantiation_context, ByteCompiler},
js_string, js_string,
realm::Realm, realm::Realm,
vm::{ActiveRunnable, CallFrame, CallFrameFlags, CodeBlock}, vm::{ActiveRunnable, CallFrame, CallFrameFlags, CodeBlock},
@ -119,19 +119,34 @@ impl Script {
let _timer = Profiler::global().start_event("Script compilation", "Main"); let _timer = Profiler::global().start_event("Script compilation", "Main");
let mut annex_b_function_names = Vec::new();
global_declaration_instantiation_context(
&mut annex_b_function_names,
&self.inner.source,
&self.inner.realm.environment().compile_env(),
context,
)?;
let mut compiler = ByteCompiler::new( let mut compiler = ByteCompiler::new(
js_string!("<main>"), js_string!("<main>"),
self.inner.source.strict(), self.inner.source.strict(),
false, false,
self.inner.realm.environment().compile_env(), self.inner.realm.environment().compile_env(),
self.inner.realm.environment().compile_env(), self.inner.realm.environment().compile_env(),
context, context.interner_mut(),
); );
#[cfg(feature = "annex-b")]
{
compiler.annex_b_function_names = annex_b_function_names;
}
// TODO: move to `Script::evaluate` to make this operation infallible. // TODO: move to `Script::evaluate` to make this operation infallible.
compiler.global_declaration_instantiation( compiler.global_declaration_instantiation(
&self.inner.source, &self.inner.source,
&self.inner.realm.environment().compile_env(), &self.inner.realm.environment().compile_env(),
)?; );
compiler.compile_statement_list(self.inner.source.statements(), true, false); compiler.compile_statement_list(self.inner.source.statements(), true, false);
let cb = Gc::new(compiler.finish()); let cb = Gc::new(compiler.finish());

21
core/engine/src/vm/code_block.rs

@ -385,7 +385,11 @@ impl CodeBlock {
Instruction::PushFloat { value } => ryu_js::Buffer::new().format(*value).to_string(), Instruction::PushFloat { value } => ryu_js::Buffer::new().format(*value).to_string(),
Instruction::PushDouble { value } => ryu_js::Buffer::new().format(*value).to_string(), Instruction::PushDouble { value } => ryu_js::Buffer::new().format(*value).to_string(),
Instruction::PushLiteral { index } Instruction::PushLiteral { index }
| Instruction::ThrowNewTypeError { message: index } => index.value().to_string(), | Instruction::ThrowNewTypeError { message: index }
| Instruction::ThrowNewSyntaxError { message: index }
| Instruction::HasRestrictedGlobalProperty { index }
| Instruction::CanDeclareGlobalFunction { index }
| Instruction::CanDeclareGlobalVar { index } => index.value().to_string(),
Instruction::PushRegExp { Instruction::PushRegExp {
pattern_index: source_index, pattern_index: source_index,
flags_index: flag_index, flags_index: flag_index,
@ -526,11 +530,15 @@ impl CodeBlock {
format!("done: {done}") format!("done: {done}")
} }
Instruction::CreateGlobalFunctionBinding { Instruction::CreateGlobalFunctionBinding {
name_index, index,
configurable,
}
| Instruction::CreateGlobalVarBinding {
index,
configurable, configurable,
} => { } => {
let name = self let name = self
.constant_string(name_index.value() as usize) .constant_string(index.value() as usize)
.to_std_string_escaped(); .to_std_string_escaped();
format!("name: {name}, configurable: {configurable}") format!("name: {name}, configurable: {configurable}")
} }
@ -712,12 +720,7 @@ impl CodeBlock {
| Instruction::Reserved51 | Instruction::Reserved51
| Instruction::Reserved52 | Instruction::Reserved52
| Instruction::Reserved53 | Instruction::Reserved53
| Instruction::Reserved54 | Instruction::Reserved54 => unreachable!("Reserved opcodes are unrechable"),
| Instruction::Reserved55
| Instruction::Reserved56
| Instruction::Reserved57
| Instruction::Reserved58
| Instruction::Reserved59 => unreachable!("Reserved opcodes are unrechable"),
} }
} }
} }

16
core/engine/src/vm/flowgraph/mod.rs

@ -72,7 +72,11 @@ impl CodeBlock {
graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
} }
Instruction::PushLiteral { .. } | Instruction::PushRegExp { .. } => { Instruction::PushLiteral { .. }
| Instruction::PushRegExp { .. }
| Instruction::HasRestrictedGlobalProperty { .. }
| Instruction::CanDeclareGlobalFunction { .. }
| Instruction::CanDeclareGlobalVar { .. } => {
graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
} }
@ -281,7 +285,7 @@ impl CodeBlock {
graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
} }
Instruction::ThrowNewTypeError { .. } => { Instruction::ThrowNewTypeError { .. } | Instruction::ThrowNewSyntaxError { .. } => {
graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
if let Some((i, handler)) = self.find_handler(previous_pc as u32) { if let Some((i, handler)) = self.find_handler(previous_pc as u32) {
graph.add_edge( graph.add_edge(
@ -449,6 +453,7 @@ impl CodeBlock {
| Instruction::CreateMappedArgumentsObject | Instruction::CreateMappedArgumentsObject
| Instruction::CreateUnmappedArgumentsObject | Instruction::CreateUnmappedArgumentsObject
| Instruction::CreateGlobalFunctionBinding { .. } | Instruction::CreateGlobalFunctionBinding { .. }
| Instruction::CreateGlobalVarBinding { .. }
| Instruction::Nop => { | Instruction::Nop => {
graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
@ -511,12 +516,7 @@ impl CodeBlock {
| Instruction::Reserved51 | Instruction::Reserved51
| Instruction::Reserved52 | Instruction::Reserved52
| Instruction::Reserved53 | Instruction::Reserved53
| Instruction::Reserved54 | Instruction::Reserved54 => unreachable!("Reserved opcodes are unrechable"),
| Instruction::Reserved55
| Instruction::Reserved56
| Instruction::Reserved57
| Instruction::Reserved58
| Instruction::Reserved59 => unreachable!("Reserved opcodes are unrechable"),
} }
} }

38
core/engine/src/vm/opcode/control_flow/throw.rs

@ -151,3 +151,41 @@ impl Operation for ThrowNewTypeError {
Self::operation(context, index) Self::operation(context, index)
} }
} }
/// `ThrowNewSyntaxError` implements the Opcode Operation for `Opcode::ThrowNewSyntaxError`
///
/// Operation:
/// - Throws a `SyntaxError` exception.
#[derive(Debug, Clone, Copy)]
pub(crate) struct ThrowNewSyntaxError;
impl ThrowNewSyntaxError {
fn operation(context: &mut Context, index: usize) -> JsResult<CompletionType> {
let msg = context.vm.frame().code_block().constant_string(index);
let msg = msg
.to_std_string()
.expect("throw message must be an ASCII string");
Err(JsNativeError::syntax().with_message(msg).into())
}
}
impl Operation for ThrowNewSyntaxError {
const NAME: &'static str = "ThrowNewSyntaxError";
const INSTRUCTION: &'static str = "INST - ThrowNewSyntaxError";
const COST: u8 = 2;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u8>() as usize;
Self::operation(context, index)
}
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u16>() as usize;
Self::operation(context, index)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u32>() as usize;
Self::operation(context, index)
}
}

53
core/engine/src/vm/opcode/define/mod.rs

@ -137,56 +137,3 @@ impl Operation for PutLexicalValue {
Self::operation(context, index as usize) Self::operation(context, index as usize)
} }
} }
/// `CreateGlobalFunctionBinding` implements the Opcode Operation for `Opcode::CreateGlobalFunctionBinding`
///
/// Operation:
/// - Performs [`CreateGlobalFunctionBinding ( N, V, D )`][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-createglobalfunctionbinding
#[derive(Debug, Clone, Copy)]
pub(crate) struct CreateGlobalFunctionBinding;
impl CreateGlobalFunctionBinding {
#[allow(clippy::unnecessary_wraps)]
fn operation(
context: &mut Context,
index: usize,
configurable: bool,
) -> JsResult<CompletionType> {
let name = context.vm.frame().code_block().constant_string(index);
let value = context.vm.pop();
let function = value
.as_object()
.expect("valeu should be an function")
.clone();
context.create_global_function_binding(name, function, configurable)?;
Ok(CompletionType::Normal)
}
}
impl Operation for CreateGlobalFunctionBinding {
const NAME: &'static str = "CreateGlobalFunctionBinding";
const INSTRUCTION: &'static str = "INST - CreateGlobalFunctionBinding";
const COST: u8 = 2;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
let configurable = context.vm.read::<u8>() != 0;
let index = context.vm.read::<u8>() as usize;
Self::operation(context, index, configurable)
}
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
let configurable = context.vm.read::<u8>() != 0;
let index = context.vm.read::<u16>() as usize;
Self::operation(context, index, configurable)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
let configurable = context.vm.read::<u8>() != 0;
let index = context.vm.read::<u32>() as usize;
Self::operation(context, index, configurable)
}
}

220
core/engine/src/vm/opcode/global.rs

@ -0,0 +1,220 @@
use crate::{vm::CompletionType, Context, JsResult};
use super::Operation;
/// `HasRestrictedGlobalProperty` implements the Opcode Operation for `Opcode::HasRestrictedGlobalProperty`
///
/// Operation:
/// - Performs [`HasRestrictedGlobalProperty ( N )`][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-hasrestrictedglobalproperty
#[derive(Debug, Clone, Copy)]
pub(crate) struct HasRestrictedGlobalProperty;
impl HasRestrictedGlobalProperty {
fn operation(context: &mut Context, index: usize) -> JsResult<CompletionType> {
let name = &context.vm.frame().code_block().constant_string(index);
let value = context.has_restricted_global_property(name)?;
context.vm.push(value);
Ok(CompletionType::Normal)
}
}
impl Operation for HasRestrictedGlobalProperty {
const NAME: &'static str = "HasRestrictedGlobalProperty";
const INSTRUCTION: &'static str = "INST - HasRestrictedGlobalProperty";
const COST: u8 = 4;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u8>() as usize;
Self::operation(context, index)
}
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u16>() as usize;
Self::operation(context, index)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u32>() as usize;
Self::operation(context, index)
}
}
/// `CanDeclareGlobalFunction` implements the Opcode Operation for `Opcode::CanDeclareGlobalFunction`
///
/// Operation:
/// - Performs [`CanDeclareGlobalFunction ( N )`][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalfunction
#[derive(Debug, Clone, Copy)]
pub(crate) struct CanDeclareGlobalFunction;
impl CanDeclareGlobalFunction {
fn operation(context: &mut Context, index: usize) -> JsResult<CompletionType> {
let name = &context.vm.frame().code_block().constant_string(index);
let value = context.can_declare_global_function(name)?;
context.vm.push(value);
Ok(CompletionType::Normal)
}
}
impl Operation for CanDeclareGlobalFunction {
const NAME: &'static str = "CanDeclareGlobalFunction";
const INSTRUCTION: &'static str = "INST - CanDeclareGlobalFunction";
const COST: u8 = 4;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u8>() as usize;
Self::operation(context, index)
}
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u16>() as usize;
Self::operation(context, index)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u32>() as usize;
Self::operation(context, index)
}
}
/// `CanDeclareGlobalVar` implements the Opcode Operation for `Opcode::CanDeclareGlobalVar`
///
/// Operation:
/// - Performs [`CanDeclareGlobalVar ( N )`][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalvar
#[derive(Debug, Clone, Copy)]
pub(crate) struct CanDeclareGlobalVar;
impl CanDeclareGlobalVar {
fn operation(context: &mut Context, index: usize) -> JsResult<CompletionType> {
let name = &context.vm.frame().code_block().constant_string(index);
let value = context.can_declare_global_var(name)?;
context.vm.push(value);
Ok(CompletionType::Normal)
}
}
impl Operation for CanDeclareGlobalVar {
const NAME: &'static str = "CanDeclareGlobalVar";
const INSTRUCTION: &'static str = "INST - CanDeclareGlobalVar";
const COST: u8 = 4;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u8>() as usize;
Self::operation(context, index)
}
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u16>() as usize;
Self::operation(context, index)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u32>() as usize;
Self::operation(context, index)
}
}
/// `CreateGlobalFunctionBinding` implements the Opcode Operation for `Opcode::CreateGlobalFunctionBinding`
///
/// Operation:
/// - Performs [`CreateGlobalFunctionBinding ( N, V, D )`][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-createglobalfunctionbinding
#[derive(Debug, Clone, Copy)]
pub(crate) struct CreateGlobalFunctionBinding;
impl CreateGlobalFunctionBinding {
#[allow(clippy::unnecessary_wraps)]
fn operation(
context: &mut Context,
index: usize,
configurable: bool,
) -> JsResult<CompletionType> {
let name = context.vm.frame().code_block().constant_string(index);
let value = context.vm.pop();
let function = value
.as_object()
.expect("valeu should be an function")
.clone();
context.create_global_function_binding(name, function, configurable)?;
Ok(CompletionType::Normal)
}
}
impl Operation for CreateGlobalFunctionBinding {
const NAME: &'static str = "CreateGlobalFunctionBinding";
const INSTRUCTION: &'static str = "INST - CreateGlobalFunctionBinding";
const COST: u8 = 2;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
let configurable = context.vm.read::<u8>() != 0;
let index = context.vm.read::<u8>() as usize;
Self::operation(context, index, configurable)
}
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
let configurable = context.vm.read::<u8>() != 0;
let index = context.vm.read::<u16>() as usize;
Self::operation(context, index, configurable)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
let configurable = context.vm.read::<u8>() != 0;
let index = context.vm.read::<u32>() as usize;
Self::operation(context, index, configurable)
}
}
/// `CreateGlobalVarBinding` implements the Opcode Operation for `Opcode::CreateGlobalVarBinding`
///
/// Operation:
/// - Performs [`CreateGlobalVarBinding ( N, V, D )`][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-createglobalvarbinding
#[derive(Debug, Clone, Copy)]
pub(crate) struct CreateGlobalVarBinding;
impl CreateGlobalVarBinding {
#[allow(clippy::unnecessary_wraps)]
fn operation(
context: &mut Context,
index: usize,
configurable: bool,
) -> JsResult<CompletionType> {
let name = context.vm.frame().code_block().constant_string(index);
context.create_global_var_binding(name, configurable)?;
Ok(CompletionType::Normal)
}
}
impl Operation for CreateGlobalVarBinding {
const NAME: &'static str = "CreateGlobalVarBinding";
const INSTRUCTION: &'static str = "INST - CreateGlobalVarBinding";
const COST: u8 = 2;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
let configurable = context.vm.read::<u8>() != 0;
let index = context.vm.read::<u8>() as usize;
Self::operation(context, index, configurable)
}
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
let configurable = context.vm.read::<u8>() != 0;
let index = context.vm.read::<u16>() as usize;
Self::operation(context, index, configurable)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
let configurable = context.vm.read::<u8>() != 0;
let index = context.vm.read::<u32>() as usize;
Self::operation(context, index, configurable)
}
}

62
core/engine/src/vm/opcode/mod.rs

@ -17,6 +17,7 @@ mod dup;
mod environment; mod environment;
mod generator; mod generator;
mod get; mod get;
mod global;
mod iteration; mod iteration;
mod meta; mod meta;
mod modifier; mod modifier;
@ -60,6 +61,8 @@ pub(crate) use generator::*;
#[doc(inline)] #[doc(inline)]
pub(crate) use get::*; pub(crate) use get::*;
#[doc(inline)] #[doc(inline)]
pub(crate) use global::*;
#[doc(inline)]
pub(crate) use iteration::*; pub(crate) use iteration::*;
#[doc(inline)] #[doc(inline)]
pub(crate) use meta::*; pub(crate) use meta::*;
@ -1590,6 +1593,13 @@ generate_opcodes! {
/// Stack: **=>** /// Stack: **=>**
ThrowNewTypeError { message: VaryingOperand }, ThrowNewTypeError { message: VaryingOperand },
/// Throw a new `SyntaxError` exception
///
/// Operands: message: u32
///
/// Stack: **=>**
ThrowNewSyntaxError { message: VaryingOperand },
/// Pops value converts it to boolean and pushes it back. /// Pops value converts it to boolean and pushes it back.
/// ///
/// Operands: /// Operands:
@ -2070,14 +2080,50 @@ generate_opcodes! {
/// Stack: **=>** `arguments` /// Stack: **=>** `arguments`
CreateUnmappedArgumentsObject, CreateUnmappedArgumentsObject,
/// Performs [`HasRestrictedGlobalProperty ( N )`][spec]
///
/// Operands: `index`: u32
///
/// Stack: **=>**
///
/// [spec]: https://tc39.es/ecma262/#sec-hasrestrictedglobalproperty
HasRestrictedGlobalProperty { index: VaryingOperand },
/// Performs [`CanDeclareGlobalFunction ( N )`][spec]
///
/// Operands: `index`: u32
///
/// Stack: **=>**
///
/// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalfunction
CanDeclareGlobalFunction { index: VaryingOperand },
/// Performs [`CanDeclareGlobalVar ( N )`][spec]
///
/// Operands: `index`: u32
///
/// Stack: **=>**
///
/// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalvar
CanDeclareGlobalVar { index: VaryingOperand },
/// Performs [`CreateGlobalFunctionBinding ( N, V, D )`][spec] /// Performs [`CreateGlobalFunctionBinding ( N, V, D )`][spec]
/// ///
/// Operands: configurable: `bool`, `name_index`: `VaryingOperand` /// Operands: configurable: `bool`, `index`: `VaryingOperand`
/// ///
/// Stack: `value` **=>** /// Stack: `function` **=>**
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-createglobalfunctionbinding /// [spec]: https://tc39.es/ecma262/#sec-createglobalfunctionbinding
CreateGlobalFunctionBinding { configurable: bool, name_index: VaryingOperand }, CreateGlobalFunctionBinding { configurable: bool, index: VaryingOperand },
/// Performs [`CreateGlobalVarBinding ( N, V, D )`][spec]
///
/// Operands: configurable: `bool`, `index`: `VaryingOperand`
///
/// Stack: **=>**
///
/// [spec]: https://tc39.es/ecma262/#sec-createglobalvarbinding
CreateGlobalVarBinding { configurable: bool, index: VaryingOperand },
/// No-operation instruction, does nothing. /// No-operation instruction, does nothing.
/// ///
@ -2208,16 +2254,6 @@ generate_opcodes! {
Reserved53 => Reserved, Reserved53 => Reserved,
/// Reserved [`Opcode`]. /// Reserved [`Opcode`].
Reserved54 => Reserved, Reserved54 => Reserved,
/// Reserved [`Opcode`].
Reserved55 => Reserved,
/// Reserved [`Opcode`].
Reserved56 => Reserved,
/// Reserved [`Opcode`].
Reserved57 => Reserved,
/// Reserved [`Opcode`].
Reserved58 => Reserved,
/// Reserved [`Opcode`].
Reserved59 => Reserved,
} }
/// Specific opcodes for bindings. /// Specific opcodes for bindings.

Loading…
Cancel
Save