Skip to content

Commit

Permalink
Add loop and switch return values
Browse files Browse the repository at this point in the history
  • Loading branch information
raskad committed May 7, 2023
1 parent 70b0d49 commit 58dbd4c
Show file tree
Hide file tree
Showing 17 changed files with 366 additions and 143 deletions.
11 changes: 11 additions & 0 deletions boa_ast/src/statement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,17 @@ impl Statement {
_ => false,
}
}

/// Returns `true` if the statement returns a value.
#[inline]
#[must_use]
pub const fn returns_value(&self) -> bool {
match self {
Self::Block(block) if block.statement_list().statements().is_empty() => false,
Self::Empty | Self::Var(_) | Self::Break(_) | Self::Continue(_) => false,
_ => true,
}
}
}

impl ToIndentedString for Statement {
Expand Down
15 changes: 14 additions & 1 deletion boa_engine/src/bytecompiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,8 +745,17 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
/// Compile a [`StatementList`].
pub fn compile_statement_list(&mut self, list: &StatementList, use_expr: bool, block: bool) {
if use_expr {
let expr_index = list
let list_until_loop_exit: Vec<_> = list
.statements()
.iter()
.take_while(|item| {
!matches!(
item,
StatementListItem::Statement(Statement::Break(_) | Statement::Continue(_))
)
})
.collect();
let expr_index = list_until_loop_exit
.iter()
.rev()
.skip_while(|item| {
Expand All @@ -758,6 +767,10 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
})
.count();

if expr_index == 0 && !list.statements().is_empty() {
self.emit_opcode(Opcode::PushUndefined);
}

for (i, item) in list.statements().iter().enumerate() {
self.compile_stmt_list_item(item, i + 1 == expr_index, block);
}
Expand Down
24 changes: 17 additions & 7 deletions boa_engine/src/bytecompiler/statement/if.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
use crate::bytecompiler::ByteCompiler;
use crate::{bytecompiler::ByteCompiler, vm::Opcode};
use boa_ast::statement::If;

impl ByteCompiler<'_, '_> {
pub(crate) fn compile_if(&mut self, node: &If, use_expr: bool) {
self.compile_expr(node.cond(), true);
let jelse = self.jump_if_false();

self.compile_stmt(node.body(), use_expr);
if !node.body().returns_value() {
self.emit_opcode(Opcode::PushUndefined);
}
self.compile_stmt(node.body(), true);

let exit = self.jump();
self.patch_jump(jelse);
match node.else_node() {
None => {
self.patch_jump(jelse);
self.emit_opcode(Opcode::PushUndefined);
}
Some(else_body) => {
let exit = self.jump();
self.patch_jump(jelse);
self.compile_stmt(else_body, use_expr);
self.patch_jump(exit);
if !else_body.returns_value() {
self.emit_opcode(Opcode::PushUndefined);
}
self.compile_stmt(else_body, true);
}
}
self.patch_jump(exit);

if !use_expr {
self.emit_opcode(Opcode::Pop);
}
}
}
10 changes: 5 additions & 5 deletions boa_engine/src/bytecompiler/statement/labelled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@ impl ByteCompiler<'_, '_> {
match labelled.item() {
LabelledItem::Statement(stmt) => match stmt {
Statement::ForLoop(for_loop) => {
self.compile_for_loop(for_loop, Some(labelled.label()));
self.compile_for_loop(for_loop, Some(labelled.label()), use_expr);
}
Statement::ForInLoop(for_in_loop) => {
self.compile_for_in_loop(for_in_loop, Some(labelled.label()));
self.compile_for_in_loop(for_in_loop, Some(labelled.label()), use_expr);
}
Statement::ForOfLoop(for_of_loop) => {
self.compile_for_of_loop(for_of_loop, Some(labelled.label()));
self.compile_for_of_loop(for_of_loop, Some(labelled.label()), use_expr);
}
Statement::WhileLoop(while_loop) => {
self.compile_while_loop(while_loop, Some(labelled.label()));
self.compile_while_loop(while_loop, Some(labelled.label()), use_expr);
}
Statement::DoWhileLoop(do_while_loop) => {
self.compile_do_while_loop(do_while_loop, Some(labelled.label()));
self.compile_do_while_loop(do_while_loop, Some(labelled.label()), use_expr);
}
stmt => self.compile_stmt(stmt, use_expr),
},
Expand Down
114 changes: 83 additions & 31 deletions boa_engine/src/bytecompiler/statement/loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ use crate::{
};

impl ByteCompiler<'_, '_> {
pub(crate) fn compile_for_loop(&mut self, for_loop: &ForLoop, label: Option<Sym>) {
pub(crate) fn compile_for_loop(
&mut self,
for_loop: &ForLoop,
label: Option<Sym>,
use_expr: bool,
) {
self.push_compile_environment(false);
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
self.push_empty_loop_jump_control();
Expand Down Expand Up @@ -54,11 +59,8 @@ impl ByteCompiler<'_, '_> {
.expect("jump_control must exist as it was just pushed")
.set_start_address(start_address);

let (continue_start, continue_exit) =
self.emit_opcode_with_two_operands(Opcode::LoopContinue);

self.emit_opcode(Opcode::LoopContinue);
self.patch_jump_with_target(loop_start, start_address);
self.patch_jump_with_target(continue_start, start_address);

if let Some(final_expr) = for_loop.final_expr() {
self.compile_expr(final_expr, false);
Expand All @@ -73,7 +75,11 @@ impl ByteCompiler<'_, '_> {
}
let exit = self.jump_if_false();

self.compile_stmt(for_loop.body(), false);
if !for_loop.body().returns_value() {
self.emit_opcode(Opcode::PushUndefined);
}
self.compile_stmt(for_loop.body(), true);
self.emit_opcode(Opcode::LoopUpdateReturnValue);

self.emit(Opcode::Jump, &[start_address]);

Expand All @@ -83,13 +89,21 @@ impl ByteCompiler<'_, '_> {

self.patch_jump(exit);
self.patch_jump(loop_exit);
self.patch_jump(continue_exit);
self.pop_loop_control_info();
self.emit_opcode(Opcode::LoopEnd);

if !use_expr {
self.emit_opcode(Opcode::Pop);
}
self.emit_opcode(Opcode::PopEnvironment);
}

pub(crate) fn compile_for_in_loop(&mut self, for_in_loop: &ForInLoop, label: Option<Sym>) {
pub(crate) fn compile_for_in_loop(
&mut self,
for_in_loop: &ForInLoop,
label: Option<Sym>,
use_expr: bool,
) {
// Handle https://tc39.es/ecma262/#prod-annexB-ForInOfStatement
if let IterableLoopInitializer::Var(var) = for_in_loop.initializer() {
if let Binding::Identifier(ident) = var.binding() {
Expand Down Expand Up @@ -128,9 +142,7 @@ impl ByteCompiler<'_, '_> {
let (loop_start, exit_label) = self.emit_opcode_with_two_operands(Opcode::LoopStart);
let start_address = self.next_opcode_location();
self.push_loop_control_info_for_of_in_loop(label, start_address);
let (continue_label, cont_exit_label) =
self.emit_opcode_with_two_operands(Opcode::LoopContinue);
self.patch_jump_with_target(continue_label, start_address);
self.emit_opcode(Opcode::LoopContinue);
self.patch_jump_with_target(loop_start, start_address);

self.emit_opcode(Opcode::Pop); // pop the `done` value.
Expand Down Expand Up @@ -192,7 +204,11 @@ impl ByteCompiler<'_, '_> {
}
}

self.compile_stmt(for_in_loop.body(), false);
if !for_in_loop.body().returns_value() {
self.emit_opcode(Opcode::PushUndefined);
}
self.compile_stmt(for_in_loop.body(), true);
self.emit_opcode(Opcode::LoopUpdateReturnValue);

if let Some(iteration_environment) = iteration_environment {
let env_info = self.pop_compile_environment();
Expand All @@ -205,17 +221,30 @@ impl ByteCompiler<'_, '_> {

self.patch_jump(exit);
self.patch_jump(exit_label);
self.patch_jump(cont_exit_label);
self.pop_loop_control_info();
self.emit_opcode(Opcode::LoopEnd);
self.emit_opcode(Opcode::RotateRight);
self.emit_u8(4);
self.emit_opcode(Opcode::Pop);
self.emit_opcode(Opcode::Pop);
self.emit_opcode(Opcode::Pop);

let skip_early_exit = self.jump();
self.patch_jump(early_exit);
self.emit_opcode(Opcode::PushUndefined);
self.patch_jump(skip_early_exit);

if !use_expr {
self.emit_opcode(Opcode::Pop);
}
}

pub(crate) fn compile_for_of_loop(&mut self, for_of_loop: &ForOfLoop, label: Option<Sym>) {
pub(crate) fn compile_for_of_loop(
&mut self,
for_of_loop: &ForOfLoop,
label: Option<Sym>,
use_expr: bool,
) {
let initializer_bound_names = match for_of_loop.initializer() {
IterableLoopInitializer::Let(declaration)
| IterableLoopInitializer::Const(declaration) => bound_names(declaration),
Expand Down Expand Up @@ -248,10 +277,8 @@ impl ByteCompiler<'_, '_> {
let (loop_start, loop_exit) = self.emit_opcode_with_two_operands(Opcode::LoopStart);
let start_address = self.next_opcode_location();
self.push_loop_control_info_for_of_in_loop(label, start_address);
let (cont_label, cont_exit_label) =
self.emit_opcode_with_two_operands(Opcode::LoopContinue);
self.emit_opcode(Opcode::LoopContinue);
self.patch_jump_with_target(loop_start, start_address);
self.patch_jump_with_target(cont_label, start_address);

self.emit_opcode(Opcode::Pop); // pop the `done` value.
self.emit_opcode(Opcode::IteratorNext);
Expand Down Expand Up @@ -322,7 +349,11 @@ impl ByteCompiler<'_, '_> {
}
}

self.compile_stmt(for_of_loop.body(), false);
if !for_of_loop.body().returns_value() {
self.emit_opcode(Opcode::PushUndefined);
}
self.compile_stmt(for_of_loop.body(), true);
self.emit_opcode(Opcode::LoopUpdateReturnValue);

if let Some(iteration_environment) = iteration_environment {
let env_info = self.pop_compile_environment();
Expand All @@ -335,62 +366,83 @@ impl ByteCompiler<'_, '_> {

self.patch_jump(exit);
self.patch_jump(loop_exit);
self.patch_jump(cont_exit_label);
self.pop_loop_control_info();
self.emit_opcode(Opcode::LoopEnd);
self.emit_opcode(Opcode::RotateRight);
self.emit_u8(4);
self.iterator_close(for_of_loop.r#await());
if !use_expr {
self.emit_opcode(Opcode::Pop);
}
}

pub(crate) fn compile_while_loop(&mut self, while_loop: &WhileLoop, label: Option<Sym>) {
pub(crate) fn compile_while_loop(
&mut self,
while_loop: &WhileLoop,
label: Option<Sym>,
use_expr: bool,
) {
let (loop_start, loop_exit) = self.emit_opcode_with_two_operands(Opcode::LoopStart);
let start_address = self.next_opcode_location();
let (continue_start, continue_exit) =
self.emit_opcode_with_two_operands(Opcode::LoopContinue);

self.emit_opcode(Opcode::LoopContinue);
self.push_loop_control_info(label, start_address);
self.patch_jump_with_target(loop_start, start_address);
self.patch_jump_with_target(continue_start, start_address);

self.compile_expr(while_loop.condition(), true);
let exit = self.jump_if_false();
self.compile_stmt(while_loop.body(), false);

if !while_loop.body().returns_value() {
self.emit_opcode(Opcode::PushUndefined);
}
self.compile_stmt(while_loop.body(), true);
self.emit_opcode(Opcode::LoopUpdateReturnValue);

self.emit(Opcode::Jump, &[start_address]);

self.patch_jump(exit);
self.patch_jump(loop_exit);
self.patch_jump(continue_exit);
self.pop_loop_control_info();
self.emit_opcode(Opcode::LoopEnd);
if !use_expr {
self.emit_opcode(Opcode::Pop);
}
}

pub(crate) fn compile_do_while_loop(
&mut self,
do_while_loop: &DoWhileLoop,
label: Option<Sym>,
use_expr: bool,
) {
let (loop_start, loop_exit) = self.emit_opcode_with_two_operands(Opcode::LoopStart);
let initial_label = self.jump();

let start_address = self.next_opcode_location();

self.patch_jump_with_target(loop_start, start_address);
self.push_loop_control_info(label, start_address);

let condition_label_address = self.next_opcode_location();
let (continue_start, continue_exit) =
self.emit_opcode_with_two_operands(Opcode::LoopContinue);
self.patch_jump_with_target(continue_start, start_address);
self.emit_opcode(Opcode::LoopContinue);
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);
if !do_while_loop.body().returns_value() {
self.emit_opcode(Opcode::PushUndefined);
}
self.compile_stmt(do_while_loop.body(), true);
self.emit_opcode(Opcode::LoopUpdateReturnValue);

self.emit(Opcode::Jump, &[condition_label_address]);
self.patch_jump(exit);
self.patch_jump(loop_exit);
self.patch_jump(continue_exit);

self.pop_loop_control_info();
self.emit_opcode(Opcode::LoopEnd);
if !use_expr {
self.emit_opcode(Opcode::Pop);
}
}
}
14 changes: 7 additions & 7 deletions boa_engine/src/bytecompiler/statement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,19 @@ impl ByteCompiler<'_, '_> {
Statement::Var(var) => self.compile_var_decl(var),
Statement::If(node) => self.compile_if(node, use_expr),
Statement::ForLoop(for_loop) => {
self.compile_for_loop(for_loop, None);
self.compile_for_loop(for_loop, None, use_expr);
}
Statement::ForInLoop(for_in_loop) => {
self.compile_for_in_loop(for_in_loop, None);
self.compile_for_in_loop(for_in_loop, None, use_expr);
}
Statement::ForOfLoop(for_of_loop) => {
self.compile_for_of_loop(for_of_loop, None);
self.compile_for_of_loop(for_of_loop, None, use_expr);
}
Statement::WhileLoop(while_loop) => {
self.compile_while_loop(while_loop, None);
self.compile_while_loop(while_loop, None, use_expr);
}
Statement::DoWhileLoop(do_while_loop) => {
self.compile_do_while_loop(do_while_loop, None);
self.compile_do_while_loop(do_while_loop, None, use_expr);
}
Statement::Block(block) => {
self.compile_block(block, use_expr);
Expand All @@ -46,7 +46,7 @@ impl ByteCompiler<'_, '_> {
self.emit(Opcode::Throw, &[]);
}
Statement::Switch(switch) => {
self.compile_switch(switch);
self.compile_switch(switch, use_expr);
}
Statement::Return(ret) => {
if let Some(expr) = ret.target() {
Expand All @@ -58,7 +58,7 @@ impl ByteCompiler<'_, '_> {
}
Statement::Try(t) => self.compile_try(t, use_expr),
Statement::Expression(expr) => self.compile_expr(expr, use_expr),
Statement::With(with) => self.compile_with(with),
Statement::With(with) => self.compile_with(with, use_expr),
Statement::Empty => {}
}
}
Expand Down
Loading

0 comments on commit 58dbd4c

Please sign in to comment.