-
-
Notifications
You must be signed in to change notification settings - Fork 400
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
1 parent
9912c37
commit ce51449
Showing
13 changed files
with
2,417 additions
and
2,200 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
258 changes: 258 additions & 0 deletions
258
boa_engine/src/bytecompiler/declaration/declaration_pattern.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
mod declaration_pattern; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(()) | ||
} | ||
} |
Oops, something went wrong.