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 Apr 22, 2023
1 parent 3db79f6 commit c9a4df9
Show file tree
Hide file tree
Showing 17 changed files with 328 additions and 112 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
30 changes: 28 additions & 2 deletions boa_engine/src/bytecompiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -737,8 +737,17 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
configurable_globals: 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 @@ -750,6 +759,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, configurable_globals);
}
Expand All @@ -773,8 +786,17 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
self.create_script_decls(list, true);

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 @@ -786,6 +808,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, true);
}
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, configurable_globals: bool) {
self.compile_expr(node.cond(), true);
let jelse = self.jump_if_false();

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

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, configurable_globals);
self.patch_jump(exit);
if !else_body.returns_value() {
self.emit_opcode(Opcode::PushUndefined);
}
self.compile_stmt(else_body, true, configurable_globals);
}
}
self.patch_jump(exit);

if !use_expr {
self.emit_opcode(Opcode::Pop);
}
}
}
11 changes: 10 additions & 1 deletion boa_engine/src/bytecompiler/statement/labelled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,42 @@ impl ByteCompiler<'_, '_> {
match labelled.item() {
LabelledItem::Statement(stmt) => match stmt {
Statement::ForLoop(for_loop) => {
self.compile_for_loop(for_loop, Some(labelled.label()), configurable_globals);
self.compile_for_loop(
for_loop,
Some(labelled.label()),
use_expr,
configurable_globals,
);
}
Statement::ForInLoop(for_in_loop) => {
self.compile_for_in_loop(
for_in_loop,
Some(labelled.label()),
use_expr,
configurable_globals,
);
}
Statement::ForOfLoop(for_of_loop) => {
self.compile_for_of_loop(
for_of_loop,
Some(labelled.label()),
use_expr,
configurable_globals,
);
}
Statement::WhileLoop(while_loop) => {
self.compile_while_loop(
while_loop,
Some(labelled.label()),
use_expr,
configurable_globals,
);
}
Statement::DoWhileLoop(do_while_loop) => {
self.compile_do_while_loop(
do_while_loop,
Some(labelled.label()),
use_expr,
configurable_globals,
);
}
Expand Down
87 changes: 62 additions & 25 deletions boa_engine/src/bytecompiler/statement/loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ impl ByteCompiler<'_, '_> {
&mut self,
for_loop: &ForLoop,
label: Option<Sym>,
use_expr: bool,
configurable_globals: bool,
) {
self.push_compile_environment(false);
Expand Down Expand Up @@ -49,10 +50,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 @@ -67,7 +66,11 @@ impl ByteCompiler<'_, '_> {
}
let exit = self.jump_if_false();

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

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

Expand All @@ -77,16 +80,20 @@ 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>,
use_expr: bool,
configurable_globals: bool,
) {
let initializer_bound_names = match for_in_loop.initializer() {
Expand Down Expand Up @@ -118,9 +125,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 @@ -189,7 +194,11 @@ impl ByteCompiler<'_, '_> {
}
}

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

if let Some(iteration_environment) = iteration_environment {
let env_info = self.pop_compile_environment();
Expand All @@ -202,20 +211,29 @@ 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>,
use_expr: bool,
configurable_globals: bool,
) {
let initializer_bound_names = match for_of_loop.initializer() {
Expand Down Expand Up @@ -250,10 +268,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 @@ -325,7 +341,11 @@ impl ByteCompiler<'_, '_> {
}
}

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

if let Some(iteration_environment) = iteration_environment {
let env_info = self.pop_compile_environment();
Expand All @@ -338,51 +358,61 @@ 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>,
use_expr: bool,
configurable_globals: 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, configurable_globals);

if !while_loop.body().returns_value() {
self.emit_opcode(Opcode::PushUndefined);
}
self.compile_stmt(while_loop.body(), true, configurable_globals);
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,
configurable_globals: 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();
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.patch_jump_with_target(loop_start, start_address);
self.push_loop_control_info(label, start_address);

Expand All @@ -392,13 +422,20 @@ impl ByteCompiler<'_, '_> {

self.patch_jump(initial_label);

self.compile_stmt(do_while_loop.body(), false, configurable_globals);
if !do_while_loop.body().returns_value() {
self.emit_opcode(Opcode::PushUndefined);
}
self.compile_stmt(do_while_loop.body(), true, configurable_globals);
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);
}
}
}

0 comments on commit c9a4df9

Please sign in to comment.