mirror of https://github.com/boa-dev/boa.git
Browse Source
This Pull Request is currently unfinished but will fix/close #1808 after some review and more work It changes the following: - Divides byte compiler logic into separate files I would like some review on the current code I have to know if the patterns I'm using are acceptable for the codebase, if everything looks good I will try to separate more code into different small modules to finish the work here.pull/2489/head
e-codes-stuff
2 years ago
13 changed files with 2417 additions and 2200 deletions
@ -0,0 +1,537 @@
|
||||
use boa_ast::{ |
||||
declaration::Binding, |
||||
expression::Identifier, |
||||
function::{Class, ClassElement}, |
||||
operations::bound_names, |
||||
property::{MethodDefinition, PropertyName}, |
||||
}; |
||||
use boa_gc::Gc; |
||||
use boa_interner::Sym; |
||||
use rustc_hash::FxHashMap; |
||||
|
||||
use crate::{ |
||||
vm::{BindingOpcode, CodeBlock, Opcode}, |
||||
JsResult, |
||||
}; |
||||
|
||||
use super::{ByteCompiler, Literal, NodeKind}; |
||||
|
||||
impl ByteCompiler<'_> { |
||||
/// This function compiles a class declaration or expression.
|
||||
///
|
||||
/// The compilation of a class declaration and expression is mostly equal.
|
||||
/// A class declaration binds the resulting class object to it's identifier.
|
||||
/// A class expression leaves the resulting class object on the stack for following operations.
|
||||
pub(crate) fn compile_class(&mut self, class: &Class, expression: bool) -> JsResult<()> { |
||||
let code = CodeBlock::new( |
||||
class.name().map_or(Sym::EMPTY_STRING, Identifier::sym), |
||||
0, |
||||
true, |
||||
); |
||||
let mut compiler = ByteCompiler { |
||||
code_block: code, |
||||
literals_map: FxHashMap::default(), |
||||
names_map: FxHashMap::default(), |
||||
bindings_map: FxHashMap::default(), |
||||
jump_info: Vec::new(), |
||||
in_async_generator: false, |
||||
json_parse: self.json_parse, |
||||
context: self.context, |
||||
}; |
||||
compiler.context.push_compile_time_environment(true); |
||||
|
||||
if let Some(expr) = class.constructor() { |
||||
compiler.code_block.length = expr.parameters().length(); |
||||
compiler.code_block.params = expr.parameters().clone(); |
||||
compiler |
||||
.context |
||||
.create_mutable_binding(Sym::ARGUMENTS.into(), false, false); |
||||
compiler.code_block.arguments_binding = Some( |
||||
compiler |
||||
.context |
||||
.initialize_mutable_binding(Sym::ARGUMENTS.into(), false), |
||||
); |
||||
for parameter in expr.parameters().as_ref() { |
||||
if parameter.is_rest_param() { |
||||
compiler.emit_opcode(Opcode::RestParameterInit); |
||||
} |
||||
|
||||
match parameter.variable().binding() { |
||||
Binding::Identifier(ident) => { |
||||
compiler |
||||
.context |
||||
.create_mutable_binding(*ident, false, false); |
||||
if let Some(init) = parameter.variable().init() { |
||||
let skip = |
||||
compiler.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); |
||||
compiler.compile_expr(init, true)?; |
||||
compiler.patch_jump(skip); |
||||
} |
||||
compiler.emit_binding(BindingOpcode::InitArg, *ident); |
||||
} |
||||
Binding::Pattern(pattern) => { |
||||
for ident in bound_names(pattern) { |
||||
compiler.context.create_mutable_binding(ident, false, false); |
||||
} |
||||
compiler.compile_declaration_pattern(pattern, BindingOpcode::InitArg)?; |
||||
} |
||||
} |
||||
} |
||||
if !expr.parameters().has_rest_parameter() { |
||||
compiler.emit_opcode(Opcode::RestParameterPop); |
||||
} |
||||
let env_label = if expr.parameters().has_expressions() { |
||||
compiler.code_block.num_bindings = compiler.context.get_binding_number(); |
||||
compiler.context.push_compile_time_environment(true); |
||||
compiler.code_block.function_environment_push_location = |
||||
compiler.next_opcode_location(); |
||||
Some(compiler.emit_opcode_with_two_operands(Opcode::PushFunctionEnvironment)) |
||||
} else { |
||||
None |
||||
}; |
||||
compiler.create_decls(expr.body(), false); |
||||
compiler.compile_statement_list(expr.body(), false, false)?; |
||||
if let Some(env_label) = env_label { |
||||
let (num_bindings, compile_environment) = |
||||
compiler.context.pop_compile_time_environment(); |
||||
let index_compile_environment = |
||||
compiler.push_compile_environment(compile_environment); |
||||
compiler.patch_jump_with_target(env_label.0, num_bindings as u32); |
||||
compiler.patch_jump_with_target(env_label.1, index_compile_environment as u32); |
||||
let (_, compile_environment) = compiler.context.pop_compile_time_environment(); |
||||
compiler.push_compile_environment(compile_environment); |
||||
} else { |
||||
let (num_bindings, compile_environment) = |
||||
compiler.context.pop_compile_time_environment(); |
||||
compiler.push_compile_environment(compile_environment); |
||||
compiler.code_block.num_bindings = num_bindings; |
||||
compiler.code_block.is_class_constructor = true; |
||||
} |
||||
} else { |
||||
if class.super_ref().is_some() { |
||||
compiler.emit_opcode(Opcode::SuperCallDerived); |
||||
} |
||||
let (num_bindings, compile_environment) = |
||||
compiler.context.pop_compile_time_environment(); |
||||
compiler.push_compile_environment(compile_environment); |
||||
compiler.code_block.num_bindings = num_bindings; |
||||
compiler.code_block.is_class_constructor = true; |
||||
} |
||||
|
||||
compiler.emit_opcode(Opcode::PushUndefined); |
||||
compiler.emit_opcode(Opcode::Return); |
||||
|
||||
let code = Gc::new(compiler.finish()); |
||||
let index = self.code_block.functions.len() as u32; |
||||
self.code_block.functions.push(code); |
||||
self.emit(Opcode::GetFunction, &[index]); |
||||
|
||||
self.emit_opcode(Opcode::Dup); |
||||
if let Some(node) = class.super_ref() { |
||||
self.compile_expr(node, true)?; |
||||
self.emit_opcode(Opcode::PushClassPrototype); |
||||
} else { |
||||
self.emit_opcode(Opcode::PushUndefined); |
||||
} |
||||
self.emit_opcode(Opcode::SetClassPrototype); |
||||
self.emit_opcode(Opcode::Swap); |
||||
|
||||
// TODO: set function name for getter and setters
|
||||
for element in class.elements() { |
||||
match element { |
||||
ClassElement::StaticMethodDefinition(name, method_definition) => { |
||||
self.emit_opcode(Opcode::Dup); |
||||
match &method_definition { |
||||
MethodDefinition::Get(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineClassGetterByName, &[index]); |
||||
} |
||||
PropertyName::Computed(ref name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::DefineClassGetterByValue); |
||||
} |
||||
}, |
||||
MethodDefinition::Set(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineClassSetterByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::DefineClassSetterByValue); |
||||
} |
||||
}, |
||||
MethodDefinition::Ordinary(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineClassMethodByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::DefineClassMethodByValue); |
||||
} |
||||
}, |
||||
MethodDefinition::Async(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineClassMethodByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::DefineClassMethodByValue); |
||||
} |
||||
}, |
||||
MethodDefinition::Generator(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineClassMethodByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::DefineClassMethodByValue); |
||||
} |
||||
}, |
||||
MethodDefinition::AsyncGenerator(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineClassMethodByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::DefineClassMethodByValue); |
||||
} |
||||
}, |
||||
} |
||||
} |
||||
// TODO: set names for private methods
|
||||
ClassElement::PrivateStaticMethodDefinition(name, method_definition) => { |
||||
self.emit_opcode(Opcode::Dup); |
||||
match &method_definition { |
||||
MethodDefinition::Get(expr) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::SetPrivateGetter, &[index]); |
||||
} |
||||
MethodDefinition::Set(expr) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::SetPrivateSetter, &[index]); |
||||
} |
||||
MethodDefinition::Ordinary(expr) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::SetPrivateMethod, &[index]); |
||||
} |
||||
MethodDefinition::Async(expr) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::SetPrivateMethod, &[index]); |
||||
} |
||||
MethodDefinition::Generator(expr) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::SetPrivateMethod, &[index]); |
||||
} |
||||
MethodDefinition::AsyncGenerator(expr) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::SetPrivateMethod, &[index]); |
||||
} |
||||
} |
||||
} |
||||
ClassElement::FieldDefinition(name, field) => { |
||||
self.emit_opcode(Opcode::Dup); |
||||
match name { |
||||
PropertyName::Literal(name) => { |
||||
self.emit_push_literal(Literal::String( |
||||
self.interner().resolve_expect(*name).into_common(false), |
||||
)); |
||||
} |
||||
PropertyName::Computed(name) => { |
||||
self.compile_expr(name, true)?; |
||||
} |
||||
} |
||||
let field_code = CodeBlock::new(Sym::EMPTY_STRING, 0, true); |
||||
let mut field_compiler = ByteCompiler { |
||||
code_block: field_code, |
||||
literals_map: FxHashMap::default(), |
||||
names_map: FxHashMap::default(), |
||||
bindings_map: FxHashMap::default(), |
||||
jump_info: Vec::new(), |
||||
in_async_generator: false, |
||||
json_parse: self.json_parse, |
||||
context: self.context, |
||||
}; |
||||
field_compiler.context.push_compile_time_environment(true); |
||||
if let Some(node) = field { |
||||
field_compiler.compile_expr(node, true)?; |
||||
} else { |
||||
field_compiler.emit_opcode(Opcode::PushUndefined); |
||||
} |
||||
let (num_bindings, compile_environment) = |
||||
field_compiler.context.pop_compile_time_environment(); |
||||
field_compiler.push_compile_environment(compile_environment); |
||||
field_compiler.code_block.num_bindings = num_bindings; |
||||
field_compiler.emit_opcode(Opcode::Return); |
||||
|
||||
let code = Gc::new(field_compiler.finish()); |
||||
let index = self.code_block.functions.len() as u32; |
||||
self.code_block.functions.push(code); |
||||
self.emit(Opcode::GetFunction, &[index]); |
||||
self.emit_opcode(Opcode::PushClassField); |
||||
} |
||||
ClassElement::PrivateFieldDefinition(name, field) => { |
||||
self.emit_opcode(Opcode::Dup); |
||||
let name_index = self.get_or_insert_name((*name).into()); |
||||
let field_code = CodeBlock::new(Sym::EMPTY_STRING, 0, true); |
||||
let mut field_compiler = ByteCompiler { |
||||
code_block: field_code, |
||||
literals_map: FxHashMap::default(), |
||||
names_map: FxHashMap::default(), |
||||
bindings_map: FxHashMap::default(), |
||||
jump_info: Vec::new(), |
||||
in_async_generator: false, |
||||
json_parse: self.json_parse, |
||||
context: self.context, |
||||
}; |
||||
field_compiler.context.push_compile_time_environment(true); |
||||
if let Some(node) = field { |
||||
field_compiler.compile_expr(node, true)?; |
||||
} else { |
||||
field_compiler.emit_opcode(Opcode::PushUndefined); |
||||
} |
||||
let (num_bindings, compile_environment) = |
||||
field_compiler.context.pop_compile_time_environment(); |
||||
field_compiler.push_compile_environment(compile_environment); |
||||
field_compiler.code_block.num_bindings = num_bindings; |
||||
field_compiler.emit_opcode(Opcode::Return); |
||||
|
||||
let code = Gc::new(field_compiler.finish()); |
||||
let index = self.code_block.functions.len() as u32; |
||||
self.code_block.functions.push(code); |
||||
self.emit(Opcode::GetFunction, &[index]); |
||||
self.emit(Opcode::PushClassFieldPrivate, &[name_index]); |
||||
} |
||||
ClassElement::StaticFieldDefinition(name, field) => { |
||||
self.emit_opcode(Opcode::Dup); |
||||
match name { |
||||
PropertyName::Literal(name) => { |
||||
if let Some(node) = field { |
||||
self.compile_expr(node, true)?; |
||||
} else { |
||||
self.emit_opcode(Opcode::PushUndefined); |
||||
} |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineOwnPropertyByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
if let Some(node) = field { |
||||
self.compile_expr(node, true)?; |
||||
} else { |
||||
self.emit_opcode(Opcode::PushUndefined); |
||||
} |
||||
self.emit_opcode(Opcode::DefineOwnPropertyByValue); |
||||
} |
||||
} |
||||
} |
||||
ClassElement::PrivateStaticFieldDefinition(name, field) => { |
||||
self.emit_opcode(Opcode::Dup); |
||||
if let Some(node) = field { |
||||
self.compile_expr(node, true)?; |
||||
} else { |
||||
self.emit_opcode(Opcode::PushUndefined); |
||||
} |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::SetPrivateField, &[index]); |
||||
} |
||||
ClassElement::StaticBlock(statement_list) => { |
||||
self.emit_opcode(Opcode::Dup); |
||||
let mut compiler = |
||||
ByteCompiler::new(Sym::EMPTY_STRING, true, false, self.context); |
||||
compiler.context.push_compile_time_environment(true); |
||||
compiler.create_decls(statement_list, false); |
||||
compiler.compile_statement_list(statement_list, false, false)?; |
||||
let (num_bindings, compile_environment) = |
||||
compiler.context.pop_compile_time_environment(); |
||||
compiler.push_compile_environment(compile_environment); |
||||
compiler.code_block.num_bindings = num_bindings; |
||||
|
||||
let code = Gc::new(compiler.finish()); |
||||
let index = self.code_block.functions.len() as u32; |
||||
self.code_block.functions.push(code); |
||||
self.emit(Opcode::GetFunction, &[index]); |
||||
self.emit_opcode(Opcode::SetHomeObject); |
||||
self.emit(Opcode::Call, &[0]); |
||||
self.emit_opcode(Opcode::Pop); |
||||
} |
||||
// TODO: set names for private methods
|
||||
ClassElement::PrivateMethodDefinition(name, method_definition) => { |
||||
self.emit_opcode(Opcode::Dup); |
||||
match method_definition { |
||||
MethodDefinition::Get(expr) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::PushClassPrivateGetter, &[index]); |
||||
} |
||||
MethodDefinition::Set(expr) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::PushClassPrivateSetter, &[index]); |
||||
} |
||||
MethodDefinition::Ordinary(expr) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::PushClassPrivateMethod, &[index]); |
||||
} |
||||
MethodDefinition::Async(expr) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::PushClassPrivateMethod, &[index]); |
||||
} |
||||
MethodDefinition::Generator(expr) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::PushClassPrivateMethod, &[index]); |
||||
} |
||||
MethodDefinition::AsyncGenerator(expr) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::PushClassPrivateMethod, &[index]); |
||||
} |
||||
} |
||||
} |
||||
ClassElement::MethodDefinition(..) => {} |
||||
} |
||||
} |
||||
|
||||
self.emit_opcode(Opcode::Swap); |
||||
|
||||
for element in class.elements() { |
||||
match element { |
||||
ClassElement::MethodDefinition(name, method_definition) => { |
||||
self.emit_opcode(Opcode::Dup); |
||||
// TODO: set names for getters and setters
|
||||
match method_definition { |
||||
MethodDefinition::Get(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineClassGetterByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::DefineClassGetterByValue); |
||||
} |
||||
}, |
||||
MethodDefinition::Set(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineClassSetterByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::DefineClassSetterByValue); |
||||
} |
||||
}, |
||||
MethodDefinition::Ordinary(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineClassMethodByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::DefineClassMethodByValue); |
||||
} |
||||
}, |
||||
MethodDefinition::Async(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineClassMethodByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::DefineClassMethodByValue); |
||||
} |
||||
}, |
||||
MethodDefinition::Generator(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineClassMethodByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::DefineClassMethodByValue); |
||||
} |
||||
}, |
||||
MethodDefinition::AsyncGenerator(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineClassMethodByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::DefineClassMethodByValue); |
||||
} |
||||
}, |
||||
} |
||||
} |
||||
ClassElement::PrivateMethodDefinition(..) |
||||
| ClassElement::PrivateFieldDefinition(..) |
||||
| ClassElement::StaticFieldDefinition(..) |
||||
| ClassElement::PrivateStaticFieldDefinition(..) |
||||
| ClassElement::StaticMethodDefinition(..) |
||||
| ClassElement::PrivateStaticMethodDefinition(..) |
||||
| ClassElement::StaticBlock(..) |
||||
| ClassElement::FieldDefinition(..) => {} |
||||
} |
||||
} |
||||
|
||||
self.emit_opcode(Opcode::Pop); |
||||
|
||||
if !expression { |
||||
self.emit_binding( |
||||
BindingOpcode::InitVar, |
||||
class.name().expect("class statements must have a name"), |
||||
); |
||||
} |
||||
Ok(()) |
||||
} |
||||
} |
@ -0,0 +1,258 @@
|
||||
use boa_ast::{ |
||||
pattern::{ArrayPatternElement, ObjectPatternElement, Pattern}, |
||||
property::PropertyName, |
||||
}; |
||||
|
||||
use crate::{ |
||||
bytecompiler::{Access, ByteCompiler, Literal}, |
||||
vm::{BindingOpcode, Opcode}, |
||||
JsResult, |
||||
}; |
||||
|
||||
impl ByteCompiler<'_> { |
||||
pub(crate) fn compile_declaration_pattern_impl( |
||||
&mut self, |
||||
pattern: &Pattern, |
||||
def: BindingOpcode, |
||||
) -> JsResult<()> { |
||||
match pattern { |
||||
Pattern::Object(pattern) => { |
||||
self.emit_opcode(Opcode::ValueNotNullOrUndefined); |
||||
|
||||
self.emit_opcode(Opcode::RequireObjectCoercible); |
||||
|
||||
let mut additional_excluded_keys_count = 0; |
||||
let rest_exits = pattern.has_rest(); |
||||
|
||||
for binding in pattern.bindings() { |
||||
use ObjectPatternElement::{ |
||||
AssignmentPropertyAccess, AssignmentRestPropertyAccess, Pattern, |
||||
RestProperty, SingleName, |
||||
}; |
||||
|
||||
match binding { |
||||
// SingleNameBinding : BindingIdentifier Initializer[opt]
|
||||
SingleName { |
||||
ident, |
||||
name, |
||||
default_init, |
||||
} => { |
||||
self.emit_opcode(Opcode::Dup); |
||||
match name { |
||||
PropertyName::Literal(name) => { |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::GetPropertyByName, &[index]); |
||||
} |
||||
PropertyName::Computed(node) => { |
||||
self.compile_expr(node, true)?; |
||||
if rest_exits { |
||||
self.emit_opcode(Opcode::GetPropertyByValuePush); |
||||
} else { |
||||
self.emit_opcode(Opcode::GetPropertyByValue); |
||||
} |
||||
} |
||||
} |
||||
|
||||
if let Some(init) = default_init { |
||||
let skip = |
||||
self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); |
||||
self.compile_expr(init, true)?; |
||||
self.patch_jump(skip); |
||||
} |
||||
self.emit_binding(def, *ident); |
||||
|
||||
if rest_exits && name.computed().is_some() { |
||||
self.emit_opcode(Opcode::Swap); |
||||
additional_excluded_keys_count += 1; |
||||
} |
||||
} |
||||
// BindingRestProperty : ... BindingIdentifier
|
||||
RestProperty { |
||||
ident, |
||||
excluded_keys, |
||||
} => { |
||||
self.emit_opcode(Opcode::PushEmptyObject); |
||||
|
||||
for key in excluded_keys { |
||||
self.emit_push_literal(Literal::String( |
||||
self.interner().resolve_expect(key.sym()).into_common(false), |
||||
)); |
||||
} |
||||
|
||||
self.emit( |
||||
Opcode::CopyDataProperties, |
||||
&[excluded_keys.len() as u32, additional_excluded_keys_count], |
||||
); |
||||
self.emit_binding(def, *ident); |
||||
} |
||||
AssignmentRestPropertyAccess { |
||||
access, |
||||
excluded_keys, |
||||
} => { |
||||
self.emit_opcode(Opcode::Dup); |
||||
self.emit_opcode(Opcode::PushEmptyObject); |
||||
for key in excluded_keys { |
||||
self.emit_push_literal(Literal::String( |
||||
self.interner().resolve_expect(key.sym()).into_common(false), |
||||
)); |
||||
} |
||||
self.emit(Opcode::CopyDataProperties, &[excluded_keys.len() as u32, 0]); |
||||
self.access_set( |
||||
Access::Property { access }, |
||||
false, |
||||
ByteCompiler::access_set_top_of_stack_expr_fn, |
||||
)?; |
||||
} |
||||
AssignmentPropertyAccess { |
||||
name, |
||||
access, |
||||
default_init, |
||||
} => { |
||||
self.emit_opcode(Opcode::Dup); |
||||
match name { |
||||
PropertyName::Literal(name) => { |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::GetPropertyByName, &[index]); |
||||
} |
||||
PropertyName::Computed(node) => { |
||||
self.compile_expr(node, true)?; |
||||
if rest_exits { |
||||
self.emit_opcode(Opcode::GetPropertyByValuePush); |
||||
} else { |
||||
self.emit_opcode(Opcode::GetPropertyByValue); |
||||
} |
||||
} |
||||
} |
||||
|
||||
if let Some(init) = default_init { |
||||
let skip = |
||||
self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); |
||||
self.compile_expr(init, true)?; |
||||
self.patch_jump(skip); |
||||
} |
||||
|
||||
self.access_set( |
||||
Access::Property { access }, |
||||
false, |
||||
ByteCompiler::access_set_top_of_stack_expr_fn, |
||||
)?; |
||||
|
||||
if rest_exits && name.computed().is_some() { |
||||
self.emit_opcode(Opcode::Swap); |
||||
additional_excluded_keys_count += 1; |
||||
} |
||||
} |
||||
Pattern { |
||||
name, |
||||
pattern, |
||||
default_init, |
||||
} => { |
||||
self.emit_opcode(Opcode::Dup); |
||||
match name { |
||||
PropertyName::Literal(name) => { |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::GetPropertyByName, &[index]); |
||||
} |
||||
PropertyName::Computed(node) => { |
||||
self.compile_expr(node, true)?; |
||||
self.emit_opcode(Opcode::GetPropertyByValue); |
||||
} |
||||
} |
||||
|
||||
if let Some(init) = default_init { |
||||
let skip = |
||||
self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); |
||||
self.compile_expr(init, true)?; |
||||
self.patch_jump(skip); |
||||
} |
||||
|
||||
self.compile_declaration_pattern(pattern, def)?; |
||||
} |
||||
} |
||||
} |
||||
|
||||
if !rest_exits { |
||||
self.emit_opcode(Opcode::Pop); |
||||
} |
||||
} |
||||
Pattern::Array(pattern) => { |
||||
self.emit_opcode(Opcode::ValueNotNullOrUndefined); |
||||
self.emit_opcode(Opcode::InitIterator); |
||||
|
||||
for binding in pattern.bindings().iter() { |
||||
use ArrayPatternElement::{ |
||||
Elision, Pattern, PatternRest, PropertyAccess, PropertyAccessRest, |
||||
SingleName, SingleNameRest, |
||||
}; |
||||
|
||||
match binding { |
||||
// ArrayBindingPattern : [ Elision ]
|
||||
Elision => { |
||||
self.emit_opcode(Opcode::IteratorNext); |
||||
self.emit_opcode(Opcode::Pop); |
||||
} |
||||
// SingleNameBinding : BindingIdentifier Initializer[opt]
|
||||
SingleName { |
||||
ident, |
||||
default_init, |
||||
} => { |
||||
self.emit_opcode(Opcode::IteratorNext); |
||||
if let Some(init) = default_init { |
||||
let skip = |
||||
self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); |
||||
self.compile_expr(init, true)?; |
||||
self.patch_jump(skip); |
||||
} |
||||
self.emit_binding(def, *ident); |
||||
} |
||||
PropertyAccess { access } => { |
||||
self.emit_opcode(Opcode::IteratorNext); |
||||
self.access_set( |
||||
Access::Property { access }, |
||||
false, |
||||
ByteCompiler::access_set_top_of_stack_expr_fn, |
||||
)?; |
||||
} |
||||
// BindingElement : BindingPattern Initializer[opt]
|
||||
Pattern { |
||||
pattern, |
||||
default_init, |
||||
} => { |
||||
self.emit_opcode(Opcode::IteratorNext); |
||||
|
||||
if let Some(init) = default_init { |
||||
let skip = |
||||
self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); |
||||
self.compile_expr(init, true)?; |
||||
self.patch_jump(skip); |
||||
} |
||||
|
||||
self.compile_declaration_pattern(pattern, def)?; |
||||
} |
||||
// BindingRestElement : ... BindingIdentifier
|
||||
SingleNameRest { ident } => { |
||||
self.emit_opcode(Opcode::IteratorToArray); |
||||
self.emit_binding(def, *ident); |
||||
} |
||||
PropertyAccessRest { access } => { |
||||
self.emit_opcode(Opcode::IteratorToArray); |
||||
self.access_set( |
||||
Access::Property { access }, |
||||
false, |
||||
ByteCompiler::access_set_top_of_stack_expr_fn, |
||||
)?; |
||||
} |
||||
// BindingRestElement : ... BindingPattern
|
||||
PatternRest { pattern } => { |
||||
self.emit_opcode(Opcode::IteratorToArray); |
||||
self.compile_declaration_pattern(pattern, def)?; |
||||
} |
||||
} |
||||
} |
||||
|
||||
self.emit_opcode(Opcode::IteratorClose); |
||||
} |
||||
} |
||||
Ok(()) |
||||
} |
||||
} |
@ -0,0 +1 @@
|
||||
mod declaration_pattern; |
@ -0,0 +1,90 @@
|
||||
use boa_ast::expression::operator::{assign::AssignOp, Assign}; |
||||
|
||||
use crate::{ |
||||
bytecompiler::{Access, ByteCompiler}, |
||||
vm::{BindingOpcode, Opcode}, |
||||
JsResult, |
||||
}; |
||||
|
||||
impl ByteCompiler<'_> { |
||||
pub(crate) fn compile_assign(&mut self, assign: &Assign, use_expr: bool) -> JsResult<()> { |
||||
if assign.op() == AssignOp::Assign { |
||||
match Access::from_assign_target(assign.lhs()) { |
||||
Ok(access) => self.access_set(access, use_expr, |compiler, _| { |
||||
compiler.compile_expr(assign.rhs(), true)?; |
||||
Ok(()) |
||||
})?, |
||||
Err(pattern) => { |
||||
self.compile_expr(assign.rhs(), true)?; |
||||
if use_expr { |
||||
self.emit_opcode(Opcode::Dup); |
||||
} |
||||
self.compile_declaration_pattern(pattern, BindingOpcode::SetName)?; |
||||
} |
||||
} |
||||
} else { |
||||
let access = Access::from_assign_target(assign.lhs()) |
||||
.expect("patterns should throw early errors on complex assignment operators"); |
||||
|
||||
let shortcircuit_operator_compilation = |
||||
|compiler: &mut ByteCompiler<'_>, opcode: Opcode| -> JsResult<()> { |
||||
let (early_exit, pop_count) = |
||||
compiler.access_set(access, use_expr, |compiler, level| { |
||||
compiler.access_get(access, true)?; |
||||
let early_exit = compiler.emit_opcode_with_operand(opcode); |
||||
compiler.compile_expr(assign.rhs(), true)?; |
||||
Ok((early_exit, level)) |
||||
})?; |
||||
if pop_count == 0 { |
||||
compiler.patch_jump(early_exit); |
||||
} else { |
||||
let exit = compiler.emit_opcode_with_operand(Opcode::Jump); |
||||
compiler.patch_jump(early_exit); |
||||
for _ in 0..pop_count { |
||||
compiler.emit_opcode(Opcode::Swap); |
||||
compiler.emit_opcode(Opcode::Pop); |
||||
} |
||||
compiler.patch_jump(exit); |
||||
} |
||||
Ok(()) |
||||
}; |
||||
|
||||
let opcode = match assign.op() { |
||||
AssignOp::Assign => unreachable!(), |
||||
AssignOp::Add => Opcode::Add, |
||||
AssignOp::Sub => Opcode::Sub, |
||||
AssignOp::Mul => Opcode::Mul, |
||||
AssignOp::Div => Opcode::Div, |
||||
AssignOp::Mod => Opcode::Mod, |
||||
AssignOp::Exp => Opcode::Pow, |
||||
AssignOp::And => Opcode::BitAnd, |
||||
AssignOp::Or => Opcode::BitOr, |
||||
AssignOp::Xor => Opcode::BitXor, |
||||
AssignOp::Shl => Opcode::ShiftLeft, |
||||
AssignOp::Shr => Opcode::ShiftRight, |
||||
AssignOp::Ushr => Opcode::UnsignedShiftRight, |
||||
AssignOp::BoolAnd => { |
||||
shortcircuit_operator_compilation(self, Opcode::LogicalAnd)?; |
||||
return Ok(()); |
||||
} |
||||
AssignOp::BoolOr => { |
||||
shortcircuit_operator_compilation(self, Opcode::LogicalOr)?; |
||||
return Ok(()); |
||||
} |
||||
AssignOp::Coalesce => { |
||||
shortcircuit_operator_compilation(self, Opcode::Coalesce)?; |
||||
return Ok(()); |
||||
} |
||||
}; |
||||
|
||||
self.access_set(access, use_expr, |compiler, _| { |
||||
compiler.access_get(access, true)?; |
||||
compiler.compile_expr(assign.rhs(), true)?; |
||||
compiler.emit(opcode, &[]); |
||||
Ok(()) |
||||
})?; |
||||
} |
||||
|
||||
Ok(()) |
||||
} |
||||
} |
@ -0,0 +1,98 @@
|
||||
use boa_ast::expression::operator::{ |
||||
binary::{ArithmeticOp, BinaryOp, BitwiseOp, LogicalOp, RelationalOp}, |
||||
Binary, |
||||
}; |
||||
|
||||
use crate::{bytecompiler::ByteCompiler, vm::Opcode, JsResult}; |
||||
|
||||
impl ByteCompiler<'_> { |
||||
pub(crate) fn compile_binary(&mut self, binary: &Binary, use_expr: bool) -> JsResult<()> { |
||||
self.compile_expr(binary.lhs(), true)?; |
||||
match binary.op() { |
||||
BinaryOp::Arithmetic(op) => { |
||||
self.compile_expr(binary.rhs(), true)?; |
||||
match op { |
||||
ArithmeticOp::Add => self.emit_opcode(Opcode::Add), |
||||
ArithmeticOp::Sub => self.emit_opcode(Opcode::Sub), |
||||
ArithmeticOp::Div => self.emit_opcode(Opcode::Div), |
||||
ArithmeticOp::Mul => self.emit_opcode(Opcode::Mul), |
||||
ArithmeticOp::Exp => self.emit_opcode(Opcode::Pow), |
||||
ArithmeticOp::Mod => self.emit_opcode(Opcode::Mod), |
||||
} |
||||
|
||||
if !use_expr { |
||||
self.emit(Opcode::Pop, &[]); |
||||
} |
||||
} |
||||
BinaryOp::Bitwise(op) => { |
||||
self.compile_expr(binary.rhs(), true)?; |
||||
match op { |
||||
BitwiseOp::And => self.emit_opcode(Opcode::BitAnd), |
||||
BitwiseOp::Or => self.emit_opcode(Opcode::BitOr), |
||||
BitwiseOp::Xor => self.emit_opcode(Opcode::BitXor), |
||||
BitwiseOp::Shl => self.emit_opcode(Opcode::ShiftLeft), |
||||
BitwiseOp::Shr => self.emit_opcode(Opcode::ShiftRight), |
||||
BitwiseOp::UShr => self.emit_opcode(Opcode::UnsignedShiftRight), |
||||
} |
||||
|
||||
if !use_expr { |
||||
self.emit(Opcode::Pop, &[]); |
||||
} |
||||
} |
||||
BinaryOp::Relational(op) => { |
||||
self.compile_expr(binary.rhs(), true)?; |
||||
match op { |
||||
RelationalOp::Equal => self.emit_opcode(Opcode::Eq), |
||||
RelationalOp::NotEqual => self.emit_opcode(Opcode::NotEq), |
||||
RelationalOp::StrictEqual => self.emit_opcode(Opcode::StrictEq), |
||||
RelationalOp::StrictNotEqual => self.emit_opcode(Opcode::StrictNotEq), |
||||
RelationalOp::GreaterThan => self.emit_opcode(Opcode::GreaterThan), |
||||
RelationalOp::GreaterThanOrEqual => { |
||||
self.emit_opcode(Opcode::GreaterThanOrEq); |
||||
} |
||||
RelationalOp::LessThan => self.emit_opcode(Opcode::LessThan), |
||||
RelationalOp::LessThanOrEqual => self.emit_opcode(Opcode::LessThanOrEq), |
||||
RelationalOp::In => self.emit_opcode(Opcode::In), |
||||
RelationalOp::InstanceOf => self.emit_opcode(Opcode::InstanceOf), |
||||
} |
||||
|
||||
if !use_expr { |
||||
self.emit(Opcode::Pop, &[]); |
||||
} |
||||
} |
||||
BinaryOp::Logical(op) => { |
||||
match op { |
||||
LogicalOp::And => { |
||||
let exit = self.emit_opcode_with_operand(Opcode::LogicalAnd); |
||||
self.compile_expr(binary.rhs(), true)?; |
||||
self.patch_jump(exit); |
||||
} |
||||
LogicalOp::Or => { |
||||
let exit = self.emit_opcode_with_operand(Opcode::LogicalOr); |
||||
self.compile_expr(binary.rhs(), true)?; |
||||
self.patch_jump(exit); |
||||
} |
||||
LogicalOp::Coalesce => { |
||||
let exit = self.emit_opcode_with_operand(Opcode::Coalesce); |
||||
self.compile_expr(binary.rhs(), true)?; |
||||
self.patch_jump(exit); |
||||
} |
||||
}; |
||||
|
||||
if !use_expr { |
||||
self.emit(Opcode::Pop, &[]); |
||||
} |
||||
} |
||||
BinaryOp::Comma => { |
||||
self.emit(Opcode::Pop, &[]); |
||||
self.compile_expr(binary.rhs(), true)?; |
||||
|
||||
if !use_expr { |
||||
self.emit(Opcode::Pop, &[]); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
Ok(()) |
||||
} |
||||
} |
@ -0,0 +1,317 @@
|
||||
use crate::{ |
||||
bytecompiler::{ByteCompiler, Literal}, |
||||
vm::Opcode, |
||||
JsResult, |
||||
}; |
||||
|
||||
use boa_ast::{ |
||||
expression::{ |
||||
access::{PropertyAccess, PropertyAccessField}, |
||||
literal::{Literal as AstLiteral, TemplateElement, TemplateLiteral}, |
||||
operator::Conditional, |
||||
}, |
||||
Expression, |
||||
}; |
||||
|
||||
mod assign; |
||||
mod binary; |
||||
mod object_literal; |
||||
mod unary; |
||||
|
||||
use boa_interner::Sym; |
||||
|
||||
use super::{Access, Callable, NodeKind}; |
||||
impl ByteCompiler<'_> { |
||||
fn compile_literal(&mut self, lit: &AstLiteral, use_expr: bool) { |
||||
match lit { |
||||
AstLiteral::String(v) => self.emit_push_literal(Literal::String( |
||||
self.interner().resolve_expect(*v).into_common(false), |
||||
)), |
||||
AstLiteral::Int(v) => self.emit_push_integer(*v), |
||||
AstLiteral::Num(v) => self.emit_push_rational(*v), |
||||
AstLiteral::BigInt(v) => { |
||||
self.emit_push_literal(Literal::BigInt(v.clone().into())); |
||||
} |
||||
AstLiteral::Bool(true) => self.emit(Opcode::PushTrue, &[]), |
||||
AstLiteral::Bool(false) => self.emit(Opcode::PushFalse, &[]), |
||||
AstLiteral::Null => self.emit(Opcode::PushNull, &[]), |
||||
AstLiteral::Undefined => self.emit(Opcode::PushUndefined, &[]), |
||||
} |
||||
|
||||
if !use_expr { |
||||
self.emit(Opcode::Pop, &[]); |
||||
} |
||||
} |
||||
|
||||
fn compile_conditional(&mut self, op: &Conditional, use_expr: bool) -> JsResult<()> { |
||||
self.compile_expr(op.condition(), true)?; |
||||
let jelse = self.jump_if_false(); |
||||
self.compile_expr(op.if_true(), true)?; |
||||
let exit = self.jump(); |
||||
self.patch_jump(jelse); |
||||
self.compile_expr(op.if_false(), true)?; |
||||
self.patch_jump(exit); |
||||
|
||||
if !use_expr { |
||||
self.emit(Opcode::Pop, &[]); |
||||
}; |
||||
|
||||
Ok(()) |
||||
} |
||||
|
||||
fn compile_template_literal( |
||||
&mut self, |
||||
template_literal: &TemplateLiteral, |
||||
use_expr: bool, |
||||
) -> JsResult<()> { |
||||
for element in template_literal.elements() { |
||||
match element { |
||||
TemplateElement::String(s) => self.emit_push_literal(Literal::String( |
||||
self.interner().resolve_expect(*s).into_common(false), |
||||
)), |
||||
TemplateElement::Expr(expr) => { |
||||
self.compile_expr(expr, true)?; |
||||
} |
||||
} |
||||
} |
||||
|
||||
self.emit( |
||||
Opcode::ConcatToString, |
||||
&[template_literal.elements().len() as u32], |
||||
); |
||||
|
||||
if !use_expr { |
||||
self.emit(Opcode::Pop, &[]); |
||||
} |
||||
|
||||
Ok(()) |
||||
} |
||||
|
||||
pub(crate) fn compile_expr_impl(&mut self, expr: &Expression, use_expr: bool) -> JsResult<()> { |
||||
match expr { |
||||
Expression::Literal(lit) => self.compile_literal(lit, use_expr), |
||||
Expression::Unary(unary) => self.compile_unary(unary, use_expr)?, |
||||
Expression::Binary(binary) => self.compile_binary(binary, use_expr)?, |
||||
Expression::Assign(assign) => self.compile_assign(assign, use_expr)?, |
||||
Expression::ObjectLiteral(object) => { |
||||
self.compile_object_literal(object, use_expr)?; |
||||
} |
||||
Expression::Identifier(name) => { |
||||
self.access_get(Access::Variable { name: *name }, use_expr)?; |
||||
} |
||||
Expression::PropertyAccess(access) => { |
||||
self.access_get(Access::Property { access }, use_expr)?; |
||||
} |
||||
Expression::Conditional(op) => self.compile_conditional(op, use_expr)?, |
||||
Expression::ArrayLiteral(array) => { |
||||
self.emit_opcode(Opcode::PushNewArray); |
||||
self.emit_opcode(Opcode::PopOnReturnAdd); |
||||
|
||||
for element in array.as_ref() { |
||||
if let Some(element) = element { |
||||
self.compile_expr(element, true)?; |
||||
if let Expression::Spread(_) = element { |
||||
self.emit_opcode(Opcode::InitIterator); |
||||
self.emit_opcode(Opcode::PushIteratorToArray); |
||||
} else { |
||||
self.emit_opcode(Opcode::PushValueToArray); |
||||
} |
||||
} else { |
||||
self.emit_opcode(Opcode::PushElisionToArray); |
||||
} |
||||
} |
||||
|
||||
self.emit_opcode(Opcode::PopOnReturnSub); |
||||
if !use_expr { |
||||
self.emit(Opcode::Pop, &[]); |
||||
} |
||||
} |
||||
Expression::This => { |
||||
self.access_get(Access::This, use_expr)?; |
||||
} |
||||
Expression::Spread(spread) => self.compile_expr(spread.target(), true)?, |
||||
Expression::Function(function) => { |
||||
self.function(function.into(), NodeKind::Expression, use_expr)?; |
||||
} |
||||
Expression::ArrowFunction(function) => { |
||||
self.function(function.into(), NodeKind::Expression, use_expr)?; |
||||
} |
||||
Expression::AsyncArrowFunction(function) => { |
||||
self.function(function.into(), NodeKind::Expression, use_expr)?; |
||||
} |
||||
Expression::Generator(function) => { |
||||
self.function(function.into(), NodeKind::Expression, use_expr)?; |
||||
} |
||||
Expression::AsyncFunction(function) => { |
||||
self.function(function.into(), NodeKind::Expression, use_expr)?; |
||||
} |
||||
Expression::AsyncGenerator(function) => { |
||||
self.function(function.into(), NodeKind::Expression, use_expr)?; |
||||
} |
||||
Expression::Call(call) => self.call(Callable::Call(call), use_expr)?, |
||||
Expression::New(new) => self.call(Callable::New(new), use_expr)?, |
||||
Expression::TemplateLiteral(template_literal) => { |
||||
self.compile_template_literal(template_literal, use_expr)?; |
||||
} |
||||
Expression::Await(expr) => { |
||||
self.compile_expr(expr.target(), true)?; |
||||
self.emit_opcode(Opcode::Await); |
||||
self.emit_opcode(Opcode::GeneratorNext); |
||||
if !use_expr { |
||||
self.emit_opcode(Opcode::Pop); |
||||
} |
||||
} |
||||
Expression::Yield(r#yield) => { |
||||
if let Some(expr) = r#yield.target() { |
||||
self.compile_expr(expr, true)?; |
||||
} else { |
||||
self.emit_opcode(Opcode::PushUndefined); |
||||
} |
||||
|
||||
if r#yield.delegate() { |
||||
if self.in_async_generator { |
||||
self.emit_opcode(Opcode::InitIteratorAsync); |
||||
} else { |
||||
self.emit_opcode(Opcode::InitIterator); |
||||
} |
||||
self.emit_opcode(Opcode::PushUndefined); |
||||
let start_address = self.next_opcode_location(); |
||||
let start = self.emit_opcode_with_operand(Opcode::GeneratorNextDelegate); |
||||
self.emit(Opcode::Jump, &[start_address]); |
||||
self.patch_jump(start); |
||||
} else if self.in_async_generator { |
||||
self.emit_opcode(Opcode::Await); |
||||
self.emit_opcode(Opcode::AsyncGeneratorNext); |
||||
let jump_return = self.emit_opcode_with_operand(Opcode::JumpIfFalse); |
||||
let jump = self.emit_opcode_with_operand(Opcode::JumpIfFalse); |
||||
self.emit_opcode(Opcode::Yield); |
||||
self.emit_opcode(Opcode::GeneratorNext); |
||||
self.patch_jump(jump); |
||||
self.emit_opcode(Opcode::Await); |
||||
self.emit_opcode(Opcode::GeneratorNext); |
||||
self.patch_jump(jump_return); |
||||
} else { |
||||
self.emit_opcode(Opcode::Yield); |
||||
self.emit_opcode(Opcode::GeneratorNext); |
||||
} |
||||
|
||||
if !use_expr { |
||||
self.emit_opcode(Opcode::Pop); |
||||
} |
||||
} |
||||
Expression::TaggedTemplate(template) => { |
||||
match template.tag() { |
||||
Expression::PropertyAccess(PropertyAccess::Simple(access)) => { |
||||
self.compile_expr(access.target(), true)?; |
||||
self.emit(Opcode::Dup, &[]); |
||||
match access.field() { |
||||
PropertyAccessField::Const(field) => { |
||||
let index = self.get_or_insert_name((*field).into()); |
||||
self.emit(Opcode::GetPropertyByName, &[index]); |
||||
} |
||||
PropertyAccessField::Expr(field) => { |
||||
self.compile_expr(field, true)?; |
||||
self.emit(Opcode::GetPropertyByValue, &[]); |
||||
} |
||||
} |
||||
} |
||||
Expression::PropertyAccess(PropertyAccess::Private(access)) => { |
||||
self.compile_expr(access.target(), true)?; |
||||
self.emit(Opcode::Dup, &[]); |
||||
let index = self.get_or_insert_name(access.field().into()); |
||||
self.emit(Opcode::GetPrivateField, &[index]); |
||||
} |
||||
expr => { |
||||
self.compile_expr(expr, true)?; |
||||
self.emit_opcode(Opcode::This); |
||||
self.emit_opcode(Opcode::Swap); |
||||
} |
||||
} |
||||
|
||||
self.emit_opcode(Opcode::PushNewArray); |
||||
for cooked in template.cookeds() { |
||||
if let Some(cooked) = cooked { |
||||
self.emit_push_literal(Literal::String( |
||||
self.interner().resolve_expect(*cooked).into_common(false), |
||||
)); |
||||
} else { |
||||
self.emit_opcode(Opcode::PushUndefined); |
||||
} |
||||
self.emit_opcode(Opcode::PushValueToArray); |
||||
} |
||||
self.emit_opcode(Opcode::Dup); |
||||
|
||||
self.emit_opcode(Opcode::PushNewArray); |
||||
for raw in template.raws() { |
||||
self.emit_push_literal(Literal::String( |
||||
self.interner().resolve_expect(*raw).into_common(false), |
||||
)); |
||||
self.emit_opcode(Opcode::PushValueToArray); |
||||
} |
||||
|
||||
let index = self.get_or_insert_name(Sym::RAW.into()); |
||||
self.emit(Opcode::SetPropertyByName, &[index]); |
||||
self.emit(Opcode::Pop, &[]); |
||||
|
||||
for expr in template.exprs() { |
||||
self.compile_expr(expr, true)?; |
||||
} |
||||
|
||||
self.emit(Opcode::Call, &[(template.exprs().len() + 1) as u32]); |
||||
} |
||||
Expression::Class(class) => self.class(class, true)?, |
||||
Expression::SuperCall(super_call) => { |
||||
let contains_spread = super_call |
||||
.arguments() |
||||
.iter() |
||||
.any(|arg| matches!(arg, Expression::Spread(_))); |
||||
|
||||
if contains_spread { |
||||
self.emit_opcode(Opcode::PushNewArray); |
||||
for arg in super_call.arguments() { |
||||
self.compile_expr(arg, true)?; |
||||
if let Expression::Spread(_) = arg { |
||||
self.emit_opcode(Opcode::InitIterator); |
||||
self.emit_opcode(Opcode::PushIteratorToArray); |
||||
} else { |
||||
self.emit_opcode(Opcode::PushValueToArray); |
||||
} |
||||
} |
||||
} else { |
||||
for arg in super_call.arguments() { |
||||
self.compile_expr(arg, true)?; |
||||
} |
||||
} |
||||
|
||||
if contains_spread { |
||||
self.emit_opcode(Opcode::SuperCallSpread); |
||||
} else { |
||||
self.emit(Opcode::SuperCall, &[super_call.arguments().len() as u32]); |
||||
} |
||||
|
||||
if !use_expr { |
||||
self.emit_opcode(Opcode::Pop); |
||||
} |
||||
} |
||||
Expression::NewTarget => { |
||||
if use_expr { |
||||
self.emit_opcode(Opcode::PushNewTarget); |
||||
} |
||||
} |
||||
Expression::Optional(opt) => { |
||||
self.compile_optional_preserve_this(opt)?; |
||||
|
||||
self.emit_opcode(Opcode::Swap); |
||||
self.emit_opcode(Opcode::Pop); |
||||
|
||||
if !use_expr { |
||||
self.emit_opcode(Opcode::Pop); |
||||
} |
||||
} |
||||
// TODO: try to remove this variant somehow
|
||||
Expression::FormalParameterList(_) => unreachable!(), |
||||
} |
||||
|
||||
Ok(()) |
||||
} |
||||
} |
@ -0,0 +1,170 @@
|
||||
use boa_ast::{ |
||||
expression::literal::ObjectLiteral, |
||||
property::{MethodDefinition, PropertyDefinition, PropertyName}, |
||||
}; |
||||
use boa_interner::Sym; |
||||
|
||||
use crate::{ |
||||
bytecompiler::{Access, ByteCompiler, NodeKind}, |
||||
vm::Opcode, |
||||
JsNativeError, JsResult, |
||||
}; |
||||
impl ByteCompiler<'_> { |
||||
pub(crate) fn compile_object_literal( |
||||
&mut self, |
||||
object: &ObjectLiteral, |
||||
use_expr: bool, |
||||
) -> JsResult<()> { |
||||
self.emit_opcode(Opcode::PushEmptyObject); |
||||
for property in object.properties() { |
||||
self.emit_opcode(Opcode::Dup); |
||||
match property { |
||||
PropertyDefinition::IdentifierReference(ident) => { |
||||
let index = self.get_or_insert_name(*ident); |
||||
self.access_get(Access::Variable { name: *ident }, true)?; |
||||
self.emit(Opcode::DefineOwnPropertyByName, &[index]); |
||||
} |
||||
PropertyDefinition::Property(name, expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.compile_expr(expr, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
if *name == Sym::__PROTO__ && !self.json_parse { |
||||
self.emit_opcode(Opcode::SetPrototype); |
||||
} else { |
||||
self.emit(Opcode::DefineOwnPropertyByName, &[index]); |
||||
} |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
if expr.is_function_definition() { |
||||
self.emit_opcode(Opcode::Dup); |
||||
self.compile_expr(expr, true)?; |
||||
self.emit_opcode(Opcode::SetFunctionName); |
||||
self.emit_u8(0); |
||||
} else { |
||||
self.compile_expr(expr, true)?; |
||||
} |
||||
self.emit_opcode(Opcode::DefineOwnPropertyByValue); |
||||
} |
||||
}, |
||||
PropertyDefinition::MethodDefinition(name, kind) => match kind { |
||||
MethodDefinition::Get(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::SetPropertyGetterByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.emit_opcode(Opcode::Dup); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::SetFunctionName); |
||||
self.emit_u8(1); |
||||
self.emit_opcode(Opcode::SetPropertyGetterByValue); |
||||
} |
||||
}, |
||||
MethodDefinition::Set(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::SetPropertySetterByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.emit_opcode(Opcode::Dup); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::SetFunctionName); |
||||
self.emit_u8(2); |
||||
self.emit_opcode(Opcode::SetPropertySetterByValue); |
||||
} |
||||
}, |
||||
MethodDefinition::Ordinary(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineOwnPropertyByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.emit_opcode(Opcode::Dup); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::SetFunctionName); |
||||
self.emit_u8(0); |
||||
self.emit_opcode(Opcode::DefineOwnPropertyByValue); |
||||
} |
||||
}, |
||||
MethodDefinition::Async(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineOwnPropertyByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.emit_opcode(Opcode::Dup); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::SetFunctionName); |
||||
self.emit_u8(0); |
||||
self.emit_opcode(Opcode::DefineOwnPropertyByValue); |
||||
} |
||||
}, |
||||
MethodDefinition::Generator(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineOwnPropertyByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.emit_opcode(Opcode::Dup); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::SetFunctionName); |
||||
self.emit_u8(0); |
||||
self.emit_opcode(Opcode::DefineOwnPropertyByValue); |
||||
} |
||||
}, |
||||
MethodDefinition::AsyncGenerator(expr) => match name { |
||||
PropertyName::Literal(name) => { |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
let index = self.get_or_insert_name((*name).into()); |
||||
self.emit(Opcode::DefineOwnPropertyByName, &[index]); |
||||
} |
||||
PropertyName::Computed(name_node) => { |
||||
self.compile_expr(name_node, true)?; |
||||
self.emit_opcode(Opcode::ToPropertyKey); |
||||
self.emit_opcode(Opcode::Dup); |
||||
self.function(expr.into(), NodeKind::Expression, true)?; |
||||
self.emit_opcode(Opcode::SetFunctionName); |
||||
self.emit_u8(0); |
||||
self.emit_opcode(Opcode::DefineOwnPropertyByValue); |
||||
} |
||||
}, |
||||
}, |
||||
PropertyDefinition::SpreadObject(expr) => { |
||||
self.compile_expr(expr, true)?; |
||||
self.emit_opcode(Opcode::Swap); |
||||
self.emit(Opcode::CopyDataProperties, &[0, 0]); |
||||
self.emit_opcode(Opcode::Pop); |
||||
} |
||||
// TODO: Promote to early errors
|
||||
PropertyDefinition::CoverInitializedName(_, _) => { |
||||
return Err(JsNativeError::syntax() |
||||
.with_message("invalid assignment pattern in object literal") |
||||
.into()) |
||||
} |
||||
} |
||||
} |
||||
|
||||
if !use_expr { |
||||
self.emit(Opcode::Pop, &[]); |
||||
} |
||||
|
||||
Ok(()) |
||||
} |
||||
} |
@ -0,0 +1,113 @@
|
||||
use boa_ast::{ |
||||
expression::operator::{unary::UnaryOp, Unary}, |
||||
Expression, |
||||
}; |
||||
|
||||
use crate::{ |
||||
bytecompiler::{Access, ByteCompiler}, |
||||
vm::Opcode, |
||||
JsNativeError, JsResult, |
||||
}; |
||||
|
||||
impl ByteCompiler<'_> { |
||||
pub(crate) fn compile_unary(&mut self, unary: &Unary, use_expr: bool) -> JsResult<()> { |
||||
let opcode = match unary.op() { |
||||
UnaryOp::IncrementPre => { |
||||
// TODO: promote to an early error.
|
||||
let access = Access::from_expression(unary.target()).ok_or_else(|| { |
||||
JsNativeError::syntax().with_message("Invalid increment operand") |
||||
})?; |
||||
|
||||
self.access_set(access, true, |compiler, _| { |
||||
compiler.compile_expr(unary.target(), true)?; |
||||
compiler.emit(Opcode::Inc, &[]); |
||||
Ok(()) |
||||
})?; |
||||
|
||||
None |
||||
} |
||||
UnaryOp::DecrementPre => { |
||||
// TODO: promote to an early error.
|
||||
let access = Access::from_expression(unary.target()).ok_or_else(|| { |
||||
JsNativeError::syntax().with_message("Invalid decrement operand") |
||||
})?; |
||||
|
||||
self.access_set(access, true, |compiler, _| { |
||||
compiler.compile_expr(unary.target(), true)?; |
||||
compiler.emit(Opcode::Dec, &[]); |
||||
Ok(()) |
||||
})?; |
||||
None |
||||
} |
||||
UnaryOp::IncrementPost => { |
||||
// TODO: promote to an early error.
|
||||
let access = Access::from_expression(unary.target()).ok_or_else(|| { |
||||
JsNativeError::syntax().with_message("Invalid increment operand") |
||||
})?; |
||||
|
||||
self.access_set(access, false, |compiler, level| { |
||||
compiler.compile_expr(unary.target(), true)?; |
||||
compiler.emit(Opcode::IncPost, &[]); |
||||
compiler.emit_opcode(Opcode::RotateRight); |
||||
compiler.emit_u8(level + 2); |
||||
Ok(()) |
||||
})?; |
||||
|
||||
None |
||||
} |
||||
UnaryOp::DecrementPost => { |
||||
// TODO: promote to an early error.
|
||||
let access = Access::from_expression(unary.target()).ok_or_else(|| { |
||||
JsNativeError::syntax().with_message("Invalid decrement operand") |
||||
})?; |
||||
|
||||
self.access_set(access, false, |compiler, level| { |
||||
compiler.compile_expr(unary.target(), true)?; |
||||
compiler.emit(Opcode::DecPost, &[]); |
||||
compiler.emit_opcode(Opcode::RotateRight); |
||||
compiler.emit_u8(level + 2); |
||||
Ok(()) |
||||
})?; |
||||
|
||||
None |
||||
} |
||||
UnaryOp::Delete => { |
||||
if let Some(access) = Access::from_expression(unary.target()) { |
||||
self.access_delete(access)?; |
||||
} else { |
||||
self.compile_expr(unary.target(), false)?; |
||||
self.emit(Opcode::PushTrue, &[]); |
||||
} |
||||
None |
||||
} |
||||
UnaryOp::Minus => Some(Opcode::Neg), |
||||
UnaryOp::Plus => Some(Opcode::Pos), |
||||
UnaryOp::Not => Some(Opcode::LogicalNot), |
||||
UnaryOp::Tilde => Some(Opcode::BitNot), |
||||
UnaryOp::TypeOf => { |
||||
match &unary.target() { |
||||
Expression::Identifier(identifier) => { |
||||
let binding = self.context.get_binding_value(*identifier); |
||||
let index = self.get_or_insert_binding(binding); |
||||
self.emit(Opcode::GetNameOrUndefined, &[index]); |
||||
} |
||||
expr => self.compile_expr(expr, true)?, |
||||
} |
||||
self.emit_opcode(Opcode::TypeOf); |
||||
None |
||||
} |
||||
UnaryOp::Void => Some(Opcode::Void), |
||||
}; |
||||
|
||||
if let Some(opcode) = opcode { |
||||
self.compile_expr(unary.target(), true)?; |
||||
self.emit(opcode, &[]); |
||||
} |
||||
|
||||
if !use_expr { |
||||
self.emit(Opcode::Pop, &[]); |
||||
} |
||||
|
||||
Ok(()) |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,96 @@
|
||||
use boa_ast::statement::Continue; |
||||
|
||||
use crate::{ |
||||
bytecompiler::{ByteCompiler, JumpControlInfoKind}, |
||||
vm::Opcode, |
||||
JsNativeError, JsResult, |
||||
}; |
||||
|
||||
impl ByteCompiler<'_> { |
||||
pub(crate) fn compile_continue(&mut self, node: Continue) -> JsResult<()> { |
||||
let next = self.next_opcode_location(); |
||||
if let Some(info) = self |
||||
.jump_info |
||||
.last() |
||||
.filter(|info| info.kind == JumpControlInfoKind::Try) |
||||
{ |
||||
let start_address = info.start_address; |
||||
let in_finally = if let Some(finally_start) = info.finally_start { |
||||
next > finally_start.index |
||||
} else { |
||||
false |
||||
}; |
||||
let in_catch_no_finally = !info.has_finally && info.in_catch; |
||||
|
||||
if in_finally { |
||||
self.emit_opcode(Opcode::PopIfThrown); |
||||
} |
||||
if in_finally || in_catch_no_finally { |
||||
self.emit_opcode(Opcode::CatchEnd2); |
||||
} else { |
||||
self.emit_opcode(Opcode::TryEnd); |
||||
} |
||||
|
||||
self.emit(Opcode::FinallySetJump, &[start_address]); |
||||
|
||||
let label = self.jump(); |
||||
self.jump_info |
||||
.last_mut() |
||||
.expect("no jump information found") |
||||
.try_continues |
||||
.push(label); |
||||
} else { |
||||
let mut items = self |
||||
.jump_info |
||||
.iter() |
||||
.rev() |
||||
.filter(|info| info.kind == JumpControlInfoKind::Loop); |
||||
let address = if let Some(label_name) = node.label() { |
||||
let mut num_loops = 0; |
||||
let mut emit_for_of_in_exit = 0; |
||||
let mut address_info = None; |
||||
for info in items { |
||||
if info.label == node.label() { |
||||
address_info = Some(info); |
||||
break; |
||||
} |
||||
num_loops += 1; |
||||
if info.for_of_in_loop { |
||||
emit_for_of_in_exit += 1; |
||||
} |
||||
} |
||||
// TODO: promote to an early error.
|
||||
let address = address_info |
||||
.ok_or_else(|| { |
||||
JsNativeError::syntax().with_message(format!( |
||||
"Cannot use the undeclared label '{}'", |
||||
self.context.interner().resolve_expect(label_name) |
||||
)) |
||||
})? |
||||
.start_address; |
||||
for _ in 0..emit_for_of_in_exit { |
||||
self.emit_opcode(Opcode::Pop); |
||||
self.emit_opcode(Opcode::Pop); |
||||
self.emit_opcode(Opcode::Pop); |
||||
} |
||||
for _ in 0..num_loops { |
||||
self.emit_opcode(Opcode::LoopEnd); |
||||
} |
||||
address |
||||
} else { |
||||
items |
||||
.next() |
||||
// TODO: promote to an early error.
|
||||
.ok_or_else(|| { |
||||
JsNativeError::syntax().with_message("continue must be inside loop") |
||||
})? |
||||
.start_address |
||||
}; |
||||
self.emit_opcode(Opcode::LoopEnd); |
||||
self.emit_opcode(Opcode::LoopStart); |
||||
self.emit(Opcode::Jump, &[address]); |
||||
} |
||||
|
||||
Ok(()) |
||||
} |
||||
} |
@ -0,0 +1,363 @@
|
||||
use boa_ast::{ |
||||
declaration::Binding, |
||||
operations::bound_names, |
||||
statement::{ |
||||
iteration::{ForLoopInitializer, IterableLoopInitializer}, |
||||
DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop, |
||||
}, |
||||
}; |
||||
use boa_interner::Sym; |
||||
|
||||
use crate::{ |
||||
bytecompiler::{Access, ByteCompiler}, |
||||
vm::{BindingOpcode, Opcode}, |
||||
JsResult, |
||||
}; |
||||
|
||||
impl ByteCompiler<'_> { |
||||
pub(crate) fn compile_for_loop( |
||||
&mut self, |
||||
for_loop: &ForLoop, |
||||
label: Option<Sym>, |
||||
configurable_globals: bool, |
||||
) -> JsResult<()> { |
||||
self.context.push_compile_time_environment(false); |
||||
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); |
||||
|
||||
if let Some(init) = for_loop.init() { |
||||
match init { |
||||
ForLoopInitializer::Expression(expr) => self.compile_expr(expr, false)?, |
||||
ForLoopInitializer::Var(decl) => { |
||||
self.create_decls_from_var_decl(decl, configurable_globals); |
||||
self.compile_var_decl(decl)?; |
||||
} |
||||
ForLoopInitializer::Lexical(decl) => { |
||||
self.create_decls_from_lexical_decl(decl); |
||||
self.compile_lexical_decl(decl)?; |
||||
} |
||||
} |
||||
} |
||||
|
||||
self.emit_opcode(Opcode::LoopStart); |
||||
let initial_jump = self.jump(); |
||||
|
||||
let start_address = self.next_opcode_location(); |
||||
self.push_loop_control_info(label, start_address); |
||||
|
||||
self.emit_opcode(Opcode::LoopContinue); |
||||
if let Some(final_expr) = for_loop.final_expr() { |
||||
self.compile_expr(final_expr, false)?; |
||||
} |
||||
|
||||
self.patch_jump(initial_jump); |
||||
|
||||
if let Some(condition) = for_loop.condition() { |
||||
self.compile_expr(condition, true)?; |
||||
} else { |
||||
self.emit_opcode(Opcode::PushTrue); |
||||
} |
||||
let exit = self.jump_if_false(); |
||||
|
||||
self.compile_stmt(for_loop.body(), false, configurable_globals)?; |
||||
|
||||
self.emit(Opcode::Jump, &[start_address]); |
||||
|
||||
self.patch_jump(exit); |
||||
self.pop_loop_control_info(); |
||||
self.emit_opcode(Opcode::LoopEnd); |
||||
|
||||
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); |
||||
let index_compile_environment = self.push_compile_environment(compile_environment); |
||||
self.patch_jump_with_target(push_env.0, num_bindings as u32); |
||||
self.patch_jump_with_target(push_env.1, index_compile_environment as u32); |
||||
self.emit_opcode(Opcode::PopEnvironment); |
||||
Ok(()) |
||||
} |
||||
|
||||
pub(crate) fn compile_for_in_loop( |
||||
&mut self, |
||||
for_in_loop: &ForInLoop, |
||||
label: Option<Sym>, |
||||
configurable_globals: bool, |
||||
) -> JsResult<()> { |
||||
let init_bound_names = bound_names(for_in_loop.initializer()); |
||||
if init_bound_names.is_empty() { |
||||
self.compile_expr(for_in_loop.target(), true)?; |
||||
} else { |
||||
self.context.push_compile_time_environment(false); |
||||
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); |
||||
|
||||
for name in init_bound_names { |
||||
self.context.create_mutable_binding(name, false, false); |
||||
} |
||||
self.compile_expr(for_in_loop.target(), true)?; |
||||
|
||||
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); |
||||
let index_compile_environment = self.push_compile_environment(compile_environment); |
||||
self.patch_jump_with_target(push_env.0, num_bindings as u32); |
||||
self.patch_jump_with_target(push_env.1, index_compile_environment as u32); |
||||
self.emit_opcode(Opcode::PopEnvironment); |
||||
} |
||||
|
||||
let early_exit = self.emit_opcode_with_operand(Opcode::ForInLoopInitIterator); |
||||
|
||||
self.emit_opcode(Opcode::LoopStart); |
||||
let start_address = self.next_opcode_location(); |
||||
self.push_loop_control_info_for_of_in_loop(label, start_address); |
||||
self.emit_opcode(Opcode::LoopContinue); |
||||
|
||||
self.context.push_compile_time_environment(false); |
||||
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); |
||||
let exit = self.emit_opcode_with_operand(Opcode::ForInLoopNext); |
||||
|
||||
match for_in_loop.initializer() { |
||||
IterableLoopInitializer::Identifier(ident) => { |
||||
self.context.create_mutable_binding(*ident, true, true); |
||||
let binding = self.context.set_mutable_binding(*ident); |
||||
let index = self.get_or_insert_binding(binding); |
||||
self.emit(Opcode::DefInitVar, &[index]); |
||||
} |
||||
IterableLoopInitializer::Access(access) => { |
||||
self.access_set( |
||||
Access::Property { access }, |
||||
false, |
||||
ByteCompiler::access_set_top_of_stack_expr_fn, |
||||
)?; |
||||
} |
||||
IterableLoopInitializer::Var(declaration) => match declaration { |
||||
Binding::Identifier(ident) => { |
||||
self.context |
||||
.create_mutable_binding(*ident, true, configurable_globals); |
||||
self.emit_binding(BindingOpcode::InitVar, *ident); |
||||
} |
||||
Binding::Pattern(pattern) => { |
||||
for ident in bound_names(pattern) { |
||||
self.context.create_mutable_binding(ident, true, false); |
||||
} |
||||
self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?; |
||||
} |
||||
}, |
||||
IterableLoopInitializer::Let(declaration) => match declaration { |
||||
Binding::Identifier(ident) => { |
||||
self.context.create_mutable_binding(*ident, false, false); |
||||
self.emit_binding(BindingOpcode::InitLet, *ident); |
||||
} |
||||
Binding::Pattern(pattern) => { |
||||
for ident in bound_names(pattern) { |
||||
self.context.create_mutable_binding(ident, false, false); |
||||
} |
||||
self.compile_declaration_pattern(pattern, BindingOpcode::InitLet)?; |
||||
} |
||||
}, |
||||
IterableLoopInitializer::Const(declaration) => match declaration { |
||||
Binding::Identifier(ident) => { |
||||
self.context.create_immutable_binding(*ident, true); |
||||
self.emit_binding(BindingOpcode::InitConst, *ident); |
||||
} |
||||
Binding::Pattern(pattern) => { |
||||
for ident in bound_names(pattern) { |
||||
self.context.create_immutable_binding(ident, true); |
||||
} |
||||
self.compile_declaration_pattern(pattern, BindingOpcode::InitConst)?; |
||||
} |
||||
}, |
||||
IterableLoopInitializer::Pattern(pattern) => { |
||||
for ident in bound_names(pattern) { |
||||
self.context.create_mutable_binding(ident, true, true); |
||||
} |
||||
self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?; |
||||
} |
||||
} |
||||
|
||||
self.compile_stmt(for_in_loop.body(), false, configurable_globals)?; |
||||
|
||||
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); |
||||
let index_compile_environment = self.push_compile_environment(compile_environment); |
||||
self.patch_jump_with_target(push_env.0, num_bindings as u32); |
||||
self.patch_jump_with_target(push_env.1, index_compile_environment as u32); |
||||
self.emit_opcode(Opcode::PopEnvironment); |
||||
|
||||
self.emit(Opcode::Jump, &[start_address]); |
||||
|
||||
self.patch_jump(exit); |
||||
self.pop_loop_control_info(); |
||||
self.emit_opcode(Opcode::LoopEnd); |
||||
self.emit_opcode(Opcode::IteratorClose); |
||||
|
||||
self.patch_jump(early_exit); |
||||
Ok(()) |
||||
} |
||||
|
||||
pub(crate) fn compile_for_of_loop( |
||||
&mut self, |
||||
for_of_loop: &ForOfLoop, |
||||
label: Option<Sym>, |
||||
configurable_globals: bool, |
||||
) -> JsResult<()> { |
||||
let init_bound_names = bound_names(for_of_loop.initializer()); |
||||
if init_bound_names.is_empty() { |
||||
self.compile_expr(for_of_loop.iterable(), true)?; |
||||
} else { |
||||
self.context.push_compile_time_environment(false); |
||||
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); |
||||
|
||||
for name in init_bound_names { |
||||
self.context.create_mutable_binding(name, false, false); |
||||
} |
||||
self.compile_expr(for_of_loop.iterable(), true)?; |
||||
|
||||
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); |
||||
let index_compile_environment = self.push_compile_environment(compile_environment); |
||||
self.patch_jump_with_target(push_env.0, num_bindings as u32); |
||||
self.patch_jump_with_target(push_env.1, index_compile_environment as u32); |
||||
self.emit_opcode(Opcode::PopEnvironment); |
||||
} |
||||
|
||||
if for_of_loop.r#await() { |
||||
self.emit_opcode(Opcode::InitIteratorAsync); |
||||
} else { |
||||
self.emit_opcode(Opcode::InitIterator); |
||||
} |
||||
|
||||
self.emit_opcode(Opcode::LoopStart); |
||||
let start_address = self.next_opcode_location(); |
||||
self.push_loop_control_info_for_of_in_loop(label, start_address); |
||||
self.emit_opcode(Opcode::LoopContinue); |
||||
|
||||
self.context.push_compile_time_environment(false); |
||||
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); |
||||
|
||||
let exit = if for_of_loop.r#await() { |
||||
self.emit_opcode(Opcode::ForAwaitOfLoopIterate); |
||||
self.emit_opcode(Opcode::Await); |
||||
self.emit_opcode(Opcode::GeneratorNext); |
||||
self.emit_opcode_with_operand(Opcode::ForAwaitOfLoopNext) |
||||
} else { |
||||
self.emit_opcode_with_operand(Opcode::ForInLoopNext) |
||||
}; |
||||
|
||||
match for_of_loop.initializer() { |
||||
IterableLoopInitializer::Identifier(ref ident) => { |
||||
self.context.create_mutable_binding(*ident, true, true); |
||||
let binding = self.context.set_mutable_binding(*ident); |
||||
let index = self.get_or_insert_binding(binding); |
||||
self.emit(Opcode::DefInitVar, &[index]); |
||||
} |
||||
IterableLoopInitializer::Access(access) => { |
||||
self.access_set( |
||||
Access::Property { access }, |
||||
false, |
||||
ByteCompiler::access_set_top_of_stack_expr_fn, |
||||
)?; |
||||
} |
||||
IterableLoopInitializer::Var(declaration) => match declaration { |
||||
Binding::Identifier(ident) => { |
||||
self.context.create_mutable_binding(*ident, true, false); |
||||
self.emit_binding(BindingOpcode::InitVar, *ident); |
||||
} |
||||
Binding::Pattern(pattern) => { |
||||
for ident in bound_names(pattern) { |
||||
self.context.create_mutable_binding(ident, true, false); |
||||
} |
||||
self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?; |
||||
} |
||||
}, |
||||
IterableLoopInitializer::Let(declaration) => match declaration { |
||||
Binding::Identifier(ident) => { |
||||
self.context.create_mutable_binding(*ident, false, false); |
||||
self.emit_binding(BindingOpcode::InitLet, *ident); |
||||
} |
||||
Binding::Pattern(pattern) => { |
||||
for ident in bound_names(pattern) { |
||||
self.context.create_mutable_binding(ident, false, false); |
||||
} |
||||
self.compile_declaration_pattern(pattern, BindingOpcode::InitLet)?; |
||||
} |
||||
}, |
||||
IterableLoopInitializer::Const(declaration) => match declaration { |
||||
Binding::Identifier(ident) => { |
||||
self.context.create_immutable_binding(*ident, true); |
||||
self.emit_binding(BindingOpcode::InitConst, *ident); |
||||
} |
||||
Binding::Pattern(pattern) => { |
||||
for ident in bound_names(pattern) { |
||||
self.context.create_immutable_binding(ident, true); |
||||
} |
||||
self.compile_declaration_pattern(pattern, BindingOpcode::InitConst)?; |
||||
} |
||||
}, |
||||
IterableLoopInitializer::Pattern(pattern) => { |
||||
for ident in bound_names(pattern) { |
||||
self.context.create_mutable_binding(ident, true, true); |
||||
} |
||||
self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?; |
||||
} |
||||
} |
||||
|
||||
self.compile_stmt(for_of_loop.body(), false, configurable_globals)?; |
||||
|
||||
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); |
||||
let index_compile_environment = self.push_compile_environment(compile_environment); |
||||
self.patch_jump_with_target(push_env.0, num_bindings as u32); |
||||
self.patch_jump_with_target(push_env.1, index_compile_environment as u32); |
||||
self.emit_opcode(Opcode::PopEnvironment); |
||||
|
||||
self.emit(Opcode::Jump, &[start_address]); |
||||
|
||||
self.patch_jump(exit); |
||||
self.pop_loop_control_info(); |
||||
self.emit_opcode(Opcode::LoopEnd); |
||||
self.emit_opcode(Opcode::IteratorClose); |
||||
Ok(()) |
||||
} |
||||
|
||||
pub(crate) fn compile_while_loop( |
||||
&mut self, |
||||
while_loop: &WhileLoop, |
||||
label: Option<Sym>, |
||||
configurable_globals: bool, |
||||
) -> JsResult<()> { |
||||
self.emit_opcode(Opcode::LoopStart); |
||||
let start_address = self.next_opcode_location(); |
||||
self.push_loop_control_info(label, start_address); |
||||
self.emit_opcode(Opcode::LoopContinue); |
||||
|
||||
self.compile_expr(while_loop.condition(), true)?; |
||||
let exit = self.jump_if_false(); |
||||
self.compile_stmt(while_loop.body(), false, configurable_globals)?; |
||||
self.emit(Opcode::Jump, &[start_address]); |
||||
self.patch_jump(exit); |
||||
|
||||
self.pop_loop_control_info(); |
||||
self.emit_opcode(Opcode::LoopEnd); |
||||
Ok(()) |
||||
} |
||||
|
||||
pub(crate) fn compile_do_while_loop( |
||||
&mut self, |
||||
do_while_loop: &DoWhileLoop, |
||||
label: Option<Sym>, |
||||
configurable_globals: bool, |
||||
) -> JsResult<()> { |
||||
self.emit_opcode(Opcode::LoopStart); |
||||
let initial_label = self.jump(); |
||||
|
||||
let start_address = self.next_opcode_location(); |
||||
self.push_loop_control_info(label, start_address); |
||||
self.emit_opcode(Opcode::LoopContinue); |
||||
|
||||
let condition_label_address = self.next_opcode_location(); |
||||
self.compile_expr(do_while_loop.cond(), true)?; |
||||
let exit = self.jump_if_false(); |
||||
|
||||
self.patch_jump(initial_label); |
||||
|
||||
self.compile_stmt(do_while_loop.body(), false, configurable_globals)?; |
||||
self.emit(Opcode::Jump, &[condition_label_address]); |
||||
self.patch_jump(exit); |
||||
|
||||
self.pop_loop_control_info(); |
||||
self.emit_opcode(Opcode::LoopEnd); |
||||
Ok(()) |
||||
} |
||||
} |
@ -0,0 +1,228 @@
|
||||
use boa_ast::{ |
||||
statement::{Block, Break, If, Labelled, LabelledItem, Switch}, |
||||
Statement, |
||||
}; |
||||
use boa_interner::Sym; |
||||
|
||||
use crate::{vm::Opcode, JsNativeError, JsResult}; |
||||
|
||||
use super::{ByteCompiler, JumpControlInfoKind, NodeKind}; |
||||
|
||||
mod r#continue; |
||||
mod r#loop; |
||||
mod r#try; |
||||
|
||||
impl ByteCompiler<'_> { |
||||
pub(crate) fn compile_if(&mut self, node: &If, configurable_globals: bool) -> JsResult<()> { |
||||
self.compile_expr(node.cond(), true)?; |
||||
let jelse = self.jump_if_false(); |
||||
|
||||
self.compile_stmt(node.body(), false, configurable_globals)?; |
||||
|
||||
match node.else_node() { |
||||
None => { |
||||
self.patch_jump(jelse); |
||||
} |
||||
Some(else_body) => { |
||||
let exit = self.jump(); |
||||
self.patch_jump(jelse); |
||||
self.compile_stmt(else_body, false, configurable_globals)?; |
||||
self.patch_jump(exit); |
||||
} |
||||
} |
||||
|
||||
Ok(()) |
||||
} |
||||
|
||||
pub(crate) fn compile_labelled( |
||||
&mut self, |
||||
labelled: &Labelled, |
||||
use_expr: bool, |
||||
configurable_globals: bool, |
||||
) -> JsResult<()> { |
||||
match labelled.item() { |
||||
LabelledItem::Statement(stmt) => match stmt { |
||||
Statement::ForLoop(for_loop) => { |
||||
self.compile_for_loop(for_loop, Some(labelled.label()), configurable_globals)?; |
||||
} |
||||
Statement::ForInLoop(for_in_loop) => { |
||||
self.compile_for_in_loop( |
||||
for_in_loop, |
||||
Some(labelled.label()), |
||||
configurable_globals, |
||||
)?; |
||||
} |
||||
Statement::ForOfLoop(for_of_loop) => { |
||||
self.compile_for_of_loop( |
||||
for_of_loop, |
||||
Some(labelled.label()), |
||||
configurable_globals, |
||||
)?; |
||||
} |
||||
Statement::WhileLoop(while_loop) => { |
||||
self.compile_while_loop( |
||||
while_loop, |
||||
Some(labelled.label()), |
||||
configurable_globals, |
||||
)?; |
||||
} |
||||
Statement::DoWhileLoop(do_while_loop) => { |
||||
self.compile_do_while_loop( |
||||
do_while_loop, |
||||
Some(labelled.label()), |
||||
configurable_globals, |
||||
)?; |
||||
} |
||||
Statement::Block(block) => { |
||||
self.compile_block( |
||||
block, |
||||
Some(labelled.label()), |
||||
use_expr, |
||||
configurable_globals, |
||||
)?; |
||||
} |
||||
stmt => self.compile_stmt(stmt, use_expr, configurable_globals)?, |
||||
}, |
||||
LabelledItem::Function(f) => { |
||||
self.function(f.into(), NodeKind::Declaration, false)?; |
||||
} |
||||
} |
||||
|
||||
Ok(()) |
||||
} |
||||
|
||||
pub(crate) fn compile_break(&mut self, node: Break) -> JsResult<()> { |
||||
let next = self.next_opcode_location(); |
||||
if let Some(info) = self |
||||
.jump_info |
||||
.last() |
||||
.filter(|info| info.kind == JumpControlInfoKind::Try) |
||||
{ |
||||
let in_finally = if let Some(finally_start) = info.finally_start { |
||||
next >= finally_start.index |
||||
} else { |
||||
false |
||||
}; |
||||
let in_catch_no_finally = !info.has_finally && info.in_catch; |
||||
|
||||
if in_finally { |
||||
self.emit_opcode(Opcode::PopIfThrown); |
||||
} |
||||
if in_finally || in_catch_no_finally { |
||||
self.emit_opcode(Opcode::CatchEnd2); |
||||
} else { |
||||
self.emit_opcode(Opcode::TryEnd); |
||||
} |
||||
self.emit(Opcode::FinallySetJump, &[u32::MAX]); |
||||
} |
||||
let label = self.jump(); |
||||
if let Some(label_name) = node.label() { |
||||
let mut found = false; |
||||
for info in self.jump_info.iter_mut().rev() { |
||||
if info.label == Some(label_name) { |
||||
info.breaks.push(label); |
||||
found = true; |
||||
break; |
||||
} |
||||
} |
||||
// TODO: promote to an early error.
|
||||
if !found { |
||||
return Err(JsNativeError::syntax() |
||||
.with_message(format!( |
||||
"Cannot use the undeclared label '{}'", |
||||
self.interner().resolve_expect(label_name) |
||||
)) |
||||
.into()); |
||||
} |
||||
} else { |
||||
self.jump_info |
||||
.last_mut() |
||||
// TODO: promote to an early error.
|
||||
.ok_or_else(|| { |
||||
JsNativeError::syntax() |
||||
.with_message("unlabeled break must be inside loop or switch") |
||||
})? |
||||
.breaks |
||||
.push(label); |
||||
} |
||||
|
||||
Ok(()) |
||||
} |
||||
|
||||
pub(crate) fn compile_switch( |
||||
&mut self, |
||||
switch: &Switch, |
||||
configurable_globals: bool, |
||||
) -> JsResult<()> { |
||||
self.context.push_compile_time_environment(false); |
||||
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); |
||||
for case in switch.cases() { |
||||
self.create_decls(case.body(), configurable_globals); |
||||
} |
||||
self.emit_opcode(Opcode::LoopStart); |
||||
|
||||
let start_address = self.next_opcode_location(); |
||||
self.push_switch_control_info(None, start_address); |
||||
|
||||
self.compile_expr(switch.val(), true)?; |
||||
let mut labels = Vec::with_capacity(switch.cases().len()); |
||||
for case in switch.cases() { |
||||
self.compile_expr(case.condition(), true)?; |
||||
labels.push(self.emit_opcode_with_operand(Opcode::Case)); |
||||
} |
||||
|
||||
let exit = self.emit_opcode_with_operand(Opcode::Default); |
||||
|
||||
for (label, case) in labels.into_iter().zip(switch.cases()) { |
||||
self.patch_jump(label); |
||||
self.compile_statement_list(case.body(), false, configurable_globals)?; |
||||
} |
||||
|
||||
self.patch_jump(exit); |
||||
if let Some(body) = switch.default() { |
||||
self.create_decls(body, configurable_globals); |
||||
self.compile_statement_list(body, false, configurable_globals)?; |
||||
} |
||||
|
||||
self.pop_switch_control_info(); |
||||
|
||||
self.emit_opcode(Opcode::LoopEnd); |
||||
|
||||
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); |
||||
let index_compile_environment = self.push_compile_environment(compile_environment); |
||||
self.patch_jump_with_target(push_env.0, num_bindings as u32); |
||||
self.patch_jump_with_target(push_env.1, index_compile_environment as u32); |
||||
self.emit_opcode(Opcode::PopEnvironment); |
||||
|
||||
Ok(()) |
||||
} |
||||
|
||||
pub(crate) fn compile_block( |
||||
&mut self, |
||||
block: &Block, |
||||
label: Option<Sym>, |
||||
use_expr: bool, |
||||
configurable_globals: bool, |
||||
) -> JsResult<()> { |
||||
if let Some(label) = label { |
||||
let next = self.next_opcode_location(); |
||||
self.push_labelled_block_control_info(label, next); |
||||
} |
||||
|
||||
self.context.push_compile_time_environment(false); |
||||
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); |
||||
self.create_decls(block.statement_list(), configurable_globals); |
||||
self.compile_statement_list(block.statement_list(), use_expr, configurable_globals)?; |
||||
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); |
||||
let index_compile_environment = self.push_compile_environment(compile_environment); |
||||
self.patch_jump_with_target(push_env.0, num_bindings as u32); |
||||
self.patch_jump_with_target(push_env.1, index_compile_environment as u32); |
||||
|
||||
if label.is_some() { |
||||
self.pop_labelled_block_control_info(); |
||||
} |
||||
|
||||
self.emit_opcode(Opcode::PopEnvironment); |
||||
Ok(()) |
||||
} |
||||
} |
@ -0,0 +1,120 @@
|
||||
use boa_ast::{declaration::Binding, operations::bound_names, statement::Try}; |
||||
|
||||
use crate::{ |
||||
bytecompiler::{ByteCompiler, Label}, |
||||
vm::{BindingOpcode, Opcode}, |
||||
JsResult, |
||||
}; |
||||
|
||||
impl ByteCompiler<'_> { |
||||
pub(crate) fn compile_try( |
||||
&mut self, |
||||
t: &Try, |
||||
use_expr: bool, |
||||
configurable_globals: bool, |
||||
) -> JsResult<()> { |
||||
self.push_try_control_info(t.finally().is_some()); |
||||
let try_start = self.next_opcode_location(); |
||||
self.emit(Opcode::TryStart, &[ByteCompiler::DUMMY_ADDRESS, 0]); |
||||
self.context.push_compile_time_environment(false); |
||||
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); |
||||
|
||||
self.create_decls(t.block().statement_list(), configurable_globals); |
||||
self.compile_statement_list(t.block().statement_list(), use_expr, configurable_globals)?; |
||||
|
||||
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); |
||||
let index_compile_environment = self.push_compile_environment(compile_environment); |
||||
self.patch_jump_with_target(push_env.0, num_bindings as u32); |
||||
self.patch_jump_with_target(push_env.1, index_compile_environment as u32); |
||||
self.emit_opcode(Opcode::PopEnvironment); |
||||
self.emit_opcode(Opcode::TryEnd); |
||||
|
||||
let finally = self.jump(); |
||||
self.patch_jump(Label { index: try_start }); |
||||
|
||||
if let Some(catch) = t.catch() { |
||||
self.push_try_control_info_catch_start(); |
||||
let catch_start = if t.finally().is_some() { |
||||
Some(self.emit_opcode_with_operand(Opcode::CatchStart)) |
||||
} else { |
||||
None |
||||
}; |
||||
self.context.push_compile_time_environment(false); |
||||
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); |
||||
if let Some(binding) = catch.parameter() { |
||||
match binding { |
||||
Binding::Identifier(ident) => { |
||||
self.context.create_mutable_binding(*ident, false, false); |
||||
self.emit_binding(BindingOpcode::InitLet, *ident); |
||||
} |
||||
Binding::Pattern(pattern) => { |
||||
for ident in bound_names(pattern) { |
||||
self.context.create_mutable_binding(ident, false, false); |
||||
} |
||||
self.compile_declaration_pattern(pattern, BindingOpcode::InitLet)?; |
||||
} |
||||
} |
||||
} else { |
||||
self.emit_opcode(Opcode::Pop); |
||||
} |
||||
|
||||
self.create_decls(catch.block().statement_list(), configurable_globals); |
||||
self.compile_statement_list( |
||||
catch.block().statement_list(), |
||||
use_expr, |
||||
configurable_globals, |
||||
)?; |
||||
|
||||
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); |
||||
let index_compile_environment = self.push_compile_environment(compile_environment); |
||||
self.patch_jump_with_target(push_env.0, num_bindings as u32); |
||||
self.patch_jump_with_target(push_env.1, index_compile_environment as u32); |
||||
self.emit_opcode(Opcode::PopEnvironment); |
||||
if let Some(catch_start) = catch_start { |
||||
self.emit_opcode(Opcode::CatchEnd); |
||||
self.patch_jump(catch_start); |
||||
} else { |
||||
self.emit_opcode(Opcode::CatchEnd2); |
||||
} |
||||
} |
||||
|
||||
self.patch_jump(finally); |
||||
|
||||
if let Some(finally) = t.finally() { |
||||
self.emit_opcode(Opcode::FinallyStart); |
||||
let finally_start_address = self.next_opcode_location(); |
||||
self.push_try_control_info_finally_start(Label { |
||||
index: finally_start_address, |
||||
}); |
||||
self.patch_jump_with_target( |
||||
Label { |
||||
index: try_start + 4, |
||||
}, |
||||
finally_start_address, |
||||
); |
||||
|
||||
self.context.push_compile_time_environment(false); |
||||
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); |
||||
|
||||
self.create_decls(finally.block().statement_list(), configurable_globals); |
||||
self.compile_statement_list( |
||||
finally.block().statement_list(), |
||||
false, |
||||
configurable_globals, |
||||
)?; |
||||
|
||||
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); |
||||
let index_compile_environment = self.push_compile_environment(compile_environment); |
||||
self.patch_jump_with_target(push_env.0, num_bindings as u32); |
||||
self.patch_jump_with_target(push_env.1, index_compile_environment as u32); |
||||
self.emit_opcode(Opcode::PopEnvironment); |
||||
|
||||
self.emit_opcode(Opcode::FinallyEnd); |
||||
self.pop_try_control_info(Some(finally_start_address)); |
||||
} else { |
||||
self.pop_try_control_info(None); |
||||
} |
||||
|
||||
Ok(()) |
||||
} |
||||
} |
Loading…
Reference in new issue