1 // Copyright (c) Facebook, Inc. and its affiliates.
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
5 use crate::try_finally_rewriter as tfr;
7 use emit_expression::{self as emit_expr, emit_await, emit_expr, LValOp, Setrange};
9 use emit_pos::{emit_pos, emit_pos_then};
10 use env::{emitter::Emitter, Env};
11 use hhbc_assertion_utils::*;
14 use instruction_sequence::{instr, Error::Unrecoverable, InstrSeq, Result};
19 use statement_state::StatementState;
21 use ffi::{Maybe, Slice, Str};
22 use lazy_static::lazy_static;
23 use naming_special_names_rust::{special_functions, special_idents, superglobals};
26 ast_defs::{self, ParamKind},
32 // Expose a mutable ref to state for emit_body so that it can set it appropriately
33 pub(crate) fn set_state<'arena, 'decl>(
34 alloc: &'arena bumpalo::Bump,
35 e: &mut Emitter<'arena, 'decl>,
36 state: StatementState<'arena>,
38 *e.emit_statement_state_mut(alloc) = state;
41 pub(crate) type Level = usize;
45 fn emit_return<'a, 'arena, 'decl>(
46 e: &mut Emitter<'arena, 'decl>,
47 env: &mut Env<'a, 'arena>,
48 ) -> Result<InstrSeq<'arena>> {
49 tfr::emit_return(e, false, env)
52 fn is_readonly_expr(e: &ast::Expr) -> bool {
54 ast::Expr_::ReadonlyExpr(_) => true,
59 fn set_bytes_kind(name: &str) -> Option<Setrange> {
61 static ref RE: Regex =
62 Regex::new(r#"(?i)^hh\\set_bytes(_rev)?_([a-z0-9]+)(_vec)?$"#).unwrap();
64 RE.captures(name).map_or(None, |groups| {
65 let op = if groups.get(1).is_some() {
71 let kind = groups.get(2).unwrap().as_str();
72 let vec = groups.get(3).is_some(); // == _vec
73 if kind == "string" && !vec {
80 let size = match kind {
83 "int32" | "float32" => 4,
84 "int64" | "float64" => 8,
87 Some(Setrange { size, vec, op })
92 pub fn emit_stmt<'a, 'arena, 'decl>(
93 e: &mut Emitter<'arena, 'decl>,
94 env: &mut Env<'a, 'arena>,
96 ) -> Result<InstrSeq<'arena>> {
97 let alloc = env.arena;
100 a::Stmt_::YieldBreak => Ok(InstrSeq::gather(
102 vec![instr::null(alloc), emit_return(e, env)?],
104 a::Stmt_::Expr(e_) => match &e_.2 {
105 a::Expr_::Await(a) => Ok(InstrSeq::gather(
107 vec![emit_await(e, env, &e_.1, a)?, instr::popc(alloc)],
109 a::Expr_::Call(c) => {
110 if let (a::Expr(_, _, a::Expr_::Id(sid)), _, exprs, None) = c.as_ref() {
111 let ft = hhbc_id::function::FunctionType::from_ast_name(alloc, &sid.1);
112 let fname = ft.to_raw_string();
113 if fname.eq_ignore_ascii_case("unset") {
119 emit_expr::emit_unset_expr(e, env, expect_normal_paramkind(ex)?)
121 .collect::<std::result::Result<Vec<_>, _>>()?,
124 if let Some(kind) = set_bytes_kind(fname) {
125 emit_expr::emit_set_range_expr(e, env, &e_.1, fname, kind, exprs)
127 emit_expr::emit_ignored_expr(e, env, &e_.1, e_)
131 emit_expr::emit_ignored_expr(e, env, pos, e_)
134 a::Expr_::Binop(bop) => {
135 if let (ast_defs::Bop::Eq(None), e_lhs, e_rhs) = bop.as_ref() {
136 if let Some(e_await) = e_rhs.2.as_await() {
137 let await_pos = &e_rhs.1;
138 if let Some(l) = e_lhs.2.as_list() {
139 let awaited_instrs = emit_await(e, env, await_pos, e_await)?;
140 let has_elements = l.iter().any(|e| !e.2.is_omitted());
142 scope::with_unnamed_local(e, |e, temp| {
147 vec![awaited_instrs, instr::popl(alloc, temp)],
151 emit_expr::emit_lval_op_list(
159 is_readonly_expr(e_rhs),
163 instr::unsetl(alloc, temp),
169 vec![awaited_instrs, instr::popc(alloc)],
173 emit_await_assignment(e, env, await_pos, e_lhs, e_await)
176 emit_expr::emit_ignored_expr(e, env, pos, e_)
179 emit_expr::emit_ignored_expr(e, env, pos, e_)
182 _ => emit_expr::emit_ignored_expr(e, env, pos, e_),
184 a::Stmt_::Return(r_opt) => match r_opt.as_ref() {
186 let ret = emit_return(e, env)?;
187 let expr_instr = if let Some(e_) = r.2.as_await() {
188 emit_await(e, env, &r.1, e_)?
190 emit_expr(e, env, &r)?
194 vec![expr_instr, emit_pos(alloc, pos), ret],
197 None => Ok(InstrSeq::gather(
201 emit_pos(alloc, pos),
202 emit_return(e, env)?,
206 a::Stmt_::Block(b) => emit_block(env, e, &b),
207 a::Stmt_::If(f) => emit_if(e, env, pos, &f.0, &f.1, &f.2),
208 a::Stmt_::While(x) => emit_while(e, env, &x.0, &x.1),
209 a::Stmt_::Using(x) => emit_using(e, env, &**x),
210 a::Stmt_::Break => Ok(emit_break(e, env, pos)),
211 a::Stmt_::Continue => Ok(emit_continue(e, env, pos)),
212 a::Stmt_::Do(x) => emit_do(e, env, &x.0, &x.1),
213 a::Stmt_::For(x) => emit_for(e, env, &x.0, &x.1, &x.2, &x.3),
214 a::Stmt_::Throw(x) => Ok(InstrSeq::gather(
217 emit_expr::emit_expr(e, env, x)?,
218 emit_pos(alloc, pos),
222 a::Stmt_::Try(x) => {
223 let (try_block, catch_list, finally_block) = &**x;
224 if catch_list.is_empty() {
225 emit_try_finally(e, env, pos, &try_block, &finally_block)
226 } else if finally_block.is_empty() {
227 emit_try_catch(e, env, pos, &try_block, &catch_list[..])
229 emit_try_catch_finally(e, env, pos, &try_block, &catch_list[..], &finally_block)
232 a::Stmt_::Switch(x) => emit_switch(e, env, pos, &x.0, &x.1),
233 a::Stmt_::Foreach(x) => emit_foreach(e, env, pos, &x.0, &x.1, &x.2),
234 a::Stmt_::Awaitall(x) => emit_awaitall(e, env, pos, &x.0, &x.1),
235 a::Stmt_::Markup(x) => emit_markup(e, env, &x, false),
236 a::Stmt_::Fallthrough | a::Stmt_::Noop => Ok(instr::empty(alloc)),
237 a::Stmt_::AssertEnv(_) => Ok(instr::empty(alloc)),
241 fn emit_case<'c, 'a, 'arena, 'decl>(
242 e: &mut Emitter<'arena, 'decl>,
243 env: &mut Env<'a, 'arena>,
247 (Option<(&'c ast::Expr, Label)>, Option<Label>),
249 let alloc = env.arena;
250 let l = e.label_gen_mut().next_regular();
252 ast::Case::Case(case_expr, b) => (
253 InstrSeq::gather(alloc, vec![instr::label(alloc, l), emit_block(env, e, b)?]),
254 (Some((case_expr, l)), None),
256 ast::Case::Default(_, b) => (
257 InstrSeq::gather(alloc, vec![instr::label(alloc, l), emit_block(env, e, b)?]),
263 fn emit_check_case<'a, 'arena, 'decl>(
264 e: &mut Emitter<'arena, 'decl>,
265 env: &mut Env<'a, 'arena>,
266 scrutinee_expr: &ast::Expr,
267 (case_expr, case_handler_label): (&ast::Expr, Label),
268 ) -> Result<InstrSeq<'arena>> {
269 let alloc = env.arena;
270 Ok(if scrutinee_expr.2.is_lvar() {
274 emit_expr::emit_two_exprs(e, env, &case_expr.1, scrutinee_expr, &case_expr)?,
276 instr::jmpnz(alloc, case_handler_label),
280 let next_case_label = e.label_gen_mut().next_regular();
285 emit_expr::emit_expr(e, env, &case_expr)?,
286 emit_pos(alloc, &case_expr.1),
288 instr::jmpz(alloc, next_case_label),
290 instr::jmp(alloc, case_handler_label),
291 instr::label(alloc, next_case_label),
297 fn emit_awaitall<'a, 'arena, 'decl>(
298 e: &mut Emitter<'arena, 'decl>,
299 env: &mut Env<'a, 'arena>,
301 el: &[(Option<ast::Lid>, ast::Expr)],
303 ) -> Result<InstrSeq<'arena>> {
304 let alloc = env.arena;
306 [] => Ok(instr::empty(alloc)),
307 [(lvar, expr)] => emit_awaitall_single(e, env, pos, lvar, expr, block),
308 _ => emit_awaitall_multi(e, env, pos, el, block),
312 fn emit_awaitall_single<'a, 'arena, 'decl>(
313 e: &mut Emitter<'arena, 'decl>,
314 env: &mut Env<'a, 'arena>,
316 lval: &Option<ast::Lid>,
319 ) -> Result<InstrSeq<'arena>> {
320 scope::with_unnamed_locals(e, |e| {
322 let load_arg = emit_expr::emit_await(e, env, pos, expr)?;
323 let (load, unset) = match lval {
324 None => (instr::popc(alloc), instr::empty(alloc)),
325 Some(ast::Lid(_, id)) => {
328 .init_unnamed_for_tempname(local_id::get_name(&id));
329 (instr::popl(alloc, *l), instr::unsetl(alloc, *l))
333 InstrSeq::gather(alloc, vec![load_arg, load]),
334 emit_stmts(e, env, block)?,
340 fn emit_awaitall_multi<'a, 'arena, 'decl>(
341 e: &mut Emitter<'arena, 'decl>,
342 env: &mut Env<'a, 'arena>,
344 el: &[(Option<ast::Lid>, ast::Expr)],
346 ) -> Result<InstrSeq<'arena>> {
347 scope::with_unnamed_locals(e, |e| {
349 let mut instrs = vec![];
350 for (_, expr) in el.iter() {
351 instrs.push(emit_expr::emit_expr(e, env, expr)?)
353 let load_args = InstrSeq::gather(alloc, instrs);
355 let mut locals: Vec<Local<'_>> = vec![];
356 for (lvar, _) in el.iter() {
357 locals.push(match lvar {
358 None => e.local_gen_mut().get_unnamed(),
359 Some(ast::Lid(_, id)) => e
361 .init_unnamed_for_tempname(local_id::get_name(&id))
366 let init_locals = InstrSeq::gather(
371 .map(|l| instr::popl(alloc, *l))
374 let unset_locals = InstrSeq::gather(
376 locals.iter().map(|l| instr::unsetl(alloc, *l)).collect(),
378 let mut instrs = vec![];
379 for l in locals.iter() {
381 let label_done = e.label_gen_mut().next_regular();
385 instr::pushl(alloc, *l),
387 instr::istypec(alloc, IstypeOp::OpNull),
388 instr::jmpnz(alloc, label_done),
389 instr::whresult(alloc),
390 instr::label(alloc, label_done),
391 instr::popl(alloc, *l),
397 let unpack = InstrSeq::gather(alloc, instrs);
398 let await_all = InstrSeq::gather(
400 vec![instr::awaitall_list(alloc, locals), instr::popc(alloc)],
402 let block_instrs = emit_stmts(e, env, block)?;
405 InstrSeq::gather(alloc, vec![load_args, init_locals]),
409 vec![emit_pos(alloc, pos), await_all, unpack, block_instrs],
417 fn emit_using<'a, 'arena, 'decl>(
418 e: &mut Emitter<'arena, 'decl>,
419 env: &mut Env<'a, 'arena>,
420 using: &ast::UsingStmt,
421 ) -> Result<InstrSeq<'arena>> {
422 let alloc = env.arena;
423 let block_pos = block_pos(&using.block)?;
424 if using.exprs.1.len() > 1 {
433 .fold(using.block.clone(), |block, expr| {
436 ast::Stmt_::mk_using(ast::UsingStmt {
437 has_await: using.has_await,
438 is_block_scoped: using.is_block_scoped,
439 exprs: (expr.1.clone(), vec![expr.clone()]),
448 let (local, preamble) = match &(using.exprs.1[0].2) {
449 ast::Expr_::Binop(x) => match (&x.0, (x.1).2.as_lvar()) {
450 (ast_defs::Bop::Eq(None), Some(ast::Lid(_, id))) => (
451 Local::Named(Str::new_str(alloc, local_id::get_name(&id).as_str())),
455 emit_expr::emit_expr(e, env, &(using.exprs.1[0]))?,
456 emit_pos(alloc, &block_pos),
462 let l = e.local_gen_mut().get_unnamed();
468 emit_expr::emit_expr(e, env, &(using.exprs.1[0]))?,
469 instr::setl(alloc, l),
476 ast::Expr_::Lvar(lid) => (
477 Local::Named(Str::new_str(alloc, local_id::get_name(&lid.1).as_str())),
481 emit_expr::emit_expr(e, env, &(using.exprs.1[0]))?,
482 emit_pos(alloc, &block_pos),
488 let l = e.local_gen_mut().get_unnamed();
494 emit_expr::emit_expr(e, env, &(using.exprs.1[0]))?,
495 instr::setl(alloc, l),
502 let finally_start = e.label_gen_mut().next_regular();
503 let finally_end = e.label_gen_mut().next_regular();
504 let body = env.do_in_using_body(e, finally_start, &using.block, emit_block)?;
505 let jump_instrs = tfr::JumpInstructions::collect(&body, &mut env.jump_targets_gen);
506 let jump_instrs_is_empty = jump_instrs.is_empty();
507 let finally_epilogue =
508 tfr::emit_finally_epilogue(e, env, &using.exprs.1[0].1, jump_instrs, finally_end)?;
509 let try_instrs = if jump_instrs_is_empty {
512 tfr::cleanup_try_body(alloc, &body)
516 e: &mut Emitter<'arena, 'decl>,
517 local: Local<'arena>,
519 is_block_scoped: bool,
520 | -> InstrSeq<'arena> {
521 let (epilogue, async_eager_label) = if has_await {
522 let after_await = e.label_gen_mut().next_regular();
527 instr::await_(alloc),
528 instr::label(alloc, after_await),
535 (instr::popc(alloc), None)
537 let fn_name = hhbc_id::method::from_raw_string(
548 instr::cgetl(alloc, local),
549 instr::nulluninit(alloc),
550 instr::fcallobjmethodd(
561 .map(|s| -> &str { alloc.alloc_str(s.as_ref()) }),
564 ObjNullFlavor::NullThrows,
568 instr::unsetl(alloc, local)
575 let exn_local = e.local_gen_mut().get_unnamed();
576 let middle = if is_empty_block(&using.block) {
579 let finally_instrs = emit_finally(e, local, using.has_await, using.is_block_scoped);
580 let catch_instrs = InstrSeq::gather(
583 emit_pos(alloc, &block_pos),
584 make_finally_catch(alloc, e, exn_local, finally_instrs),
585 emit_pos(alloc, &using.exprs.1[0].1),
588 InstrSeq::create_try_catch(
602 instr::label(alloc, finally_start),
603 emit_finally(e, local, using.has_await, using.is_block_scoped),
605 instr::label(alloc, finally_end),
612 fn block_pos(block: &ast::Block) -> Result<Pos> {
613 if block.iter().all(|b| b.0.is_none()) {
614 return Ok(Pos::make_none());
617 let mut last = block.len() - 1;
619 if !block[first].0.is_none() && !block[last].0.is_none() {
620 return Pos::btw(&block[first].0, &block[last].0).map_err(|s| Unrecoverable(s));
622 if block[first].0.is_none() {
625 if block[last].0.is_none() {
631 fn emit_cases<'a, 'arena, 'decl>(
632 env: &mut Env<'a, 'arena>,
633 e: &mut Emitter<'arena, 'decl>,
636 scrutinee_expr: &ast::Expr,
638 ) -> Result<(InstrSeq<'arena>, InstrSeq<'arena>, Label)> {
639 let alloc = env.arena;
640 let has_default = cases.iter().any(|c| c.is_default());
641 match cases.split_last() {
643 return Err(Unrecoverable(
644 "impossible - switch statements must have at least one case".into(),
647 Some((last, rest)) => {
648 // Emit all the cases except the last one
651 .map(|case| emit_case(e, env, case))
652 .collect::<Result<Vec<_>>>()?;
655 // If there is a default, emit the last case as usual
656 res.push(emit_case(e, env, last)?)
658 // Otherwise, emit the last case with an added break
660 ast::Case::Case(expr, block) => {
661 let l = e.label_gen_mut().next_regular();
666 instr::label(alloc, l),
667 emit_block(env, e, block)?,
668 emit_break(e, env, &Pos::make_none()),
671 (Some((expr, l)), None),
674 ast::Case::Default(_, _) => {
675 return Err(Unrecoverable(
676 "impossible - there shouldn't be a default".into(),
680 // ...and emit warning/exception for missing default
681 let l = e.label_gen_mut().next_regular();
686 instr::label(alloc, l),
687 emit_pos_then(alloc, pos, instr::throw_non_exhaustive_switch(alloc)),
693 let (case_body_instrs, case_exprs_and_default_labels): (Vec<InstrSeq<'arena>>, Vec<_>) =
694 res.into_iter().unzip();
695 let (case_exprs, default_labels): (Vec<Option<(&ast::Expr, Label)>>, Vec<_>) =
696 case_exprs_and_default_labels.into_iter().unzip();
698 let default_label = match default_labels
700 .filter_map(|lopt| lopt.as_ref())
707 return Err(emit_fatal::raise_fatal_runtime(
709 "Switch statements may only contain one 'default' clause.",
713 let case_expr_instrs = case_exprs
715 .filter_map(|x| x.map(|x| emit_check_case(e, env, scrutinee_expr, x)))
716 .collect::<Result<Vec<_>>>()?;
719 InstrSeq::gather(alloc, case_expr_instrs),
720 InstrSeq::gather(alloc, case_body_instrs),
727 fn emit_switch<'a, 'arena, 'decl>(
728 e: &mut Emitter<'arena, 'decl>,
729 env: &mut Env<'a, 'arena>,
731 scrutinee_expr: &ast::Expr,
733 ) -> Result<InstrSeq<'arena>> {
734 let alloc = env.arena;
735 let (instr_init, instr_free) = if scrutinee_expr.2.is_lvar() {
736 (instr::empty(alloc), instr::empty(alloc))
739 emit_expr::emit_expr(e, env, scrutinee_expr)?,
743 let break_label = e.label_gen_mut().next_regular();
745 let (case_expr_instrs, case_body_instrs, default_label) =
746 env.do_in_switch_body(e, break_label, cl, |env, e, cases| {
747 emit_cases(env, e, pos, break_label, scrutinee_expr, cases)
755 instr::jmp(alloc, default_label),
757 instr::label(alloc, break_label),
762 fn is_empty_block(b: &[ast::Stmt]) -> bool {
763 b.iter().all(|s| s.1.is_noop())
766 fn emit_try_catch_finally<'a, 'arena, 'decl>(
767 e: &mut Emitter<'arena, 'decl>,
768 env: &mut Env<'a, 'arena>,
771 catch: &[ast::Catch],
772 finally: &ast::Block,
773 ) -> Result<InstrSeq<'arena>> {
774 let is_try_block_empty = false;
776 |env: &mut Env<'a, 'arena>, e: &mut Emitter<'arena, 'decl>, finally_start: Label| {
777 env.do_in_try_catch_body(e, finally_start, r#try, catch, |env, e, t, c| {
778 emit_try_catch(e, env, pos, t, c)
781 e.local_scope(|e| emit_try_finally_(e, env, pos, emit_try_block, finally, is_try_block_empty))
784 fn emit_try_finally<'a, 'arena, 'decl>(
785 e: &mut Emitter<'arena, 'decl>,
786 env: &mut Env<'a, 'arena>,
788 try_block: &[ast::Stmt],
789 finally_block: &[ast::Stmt],
790 ) -> Result<InstrSeq<'arena>> {
791 let is_try_block_empty = is_empty_block(try_block);
793 |env: &mut Env<'a, 'arena>, e: &mut Emitter<'arena, 'decl>, finally_start: Label| {
794 env.do_in_try_body(e, finally_start, try_block, emit_block)
808 fn emit_try_finally_<
812 E: Fn(&mut Env<'a, 'arena>, &mut Emitter<'arena, 'decl>, Label) -> Result<InstrSeq<'arena>>,
814 e: &mut Emitter<'arena, 'decl>,
815 env: &mut Env<'a, 'arena>,
818 finally_block: &[ast::Stmt],
819 is_try_block_empty: bool,
820 ) -> Result<InstrSeq<'arena>> {
821 let alloc = env.arena;
822 if is_try_block_empty {
823 return env.do_in_finally_body(e, finally_block, emit_block);
825 // We need to generate four things:
826 // (1) the try-body, which will be followed by
827 // (2) the normal-continuation finally body, and
828 // (3) an epilogue to the finally body that deals with finally-blocked
829 // break and continue
830 // (4) the exceptional-continuation catch body.
835 // The try body might have un-rewritten continues and breaks which
836 // branch to a label outside of the try. This means that we must
837 // first run the normal-continuation finally, and then branch to the
838 // appropriate label.
840 // We do this by running a rewriter which turns continues and breaks
841 // inside the try body into setting temp_local to an integer which indicates
842 // what action the finally must perform when it is finished, followed by a
843 // jump directly to the finally.
844 let finally_start = e.label_gen_mut().next_regular();
845 let finally_end = e.label_gen_mut().next_regular();
847 let in_try = env.flags.contains(env::Flags::IN_TRY);
848 env.flags.set(env::Flags::IN_TRY, true);
849 let try_body_result = emit_try_block(env, e, finally_start);
850 env.flags.set(env::Flags::IN_TRY, in_try);
852 let try_body = try_body_result?;
853 let jump_instrs = tfr::JumpInstructions::collect(&try_body, &mut env.jump_targets_gen);
854 let jump_instrs_is_empty = jump_instrs.is_empty();
858 // Note that this is used both in the normal-continuation and
859 // exceptional-continuation cases; we generate the same code twice.
861 // TODO: We might consider changing the codegen so that the finally block
862 // is only generated once. We could do this by making the catch block set a
863 // temp local to -1, and then branch to the finally block. In the finally block
864 // epilogue it can check to see if the local is -1, and if so, issue an unwind
867 // It is illegal to have a continue or break which branches out of a finally.
868 // Unfortunately we at present do not detect this at parse time; rather, we
869 // generate an exception at run-time by rewriting continue and break
870 // instructions found inside finally blocks.
872 // TODO: If we make this illegal at parse time then we can remove this pass.
873 let exn_local = e.local_gen_mut().get_unnamed();
874 let finally_body = env.do_in_finally_body(e, finally_block, emit_block)?;
875 let mut finally_body_for_catch = InstrSeq::clone(alloc, &finally_body);
876 label_rewriter::clone_with_fresh_regular_labels(e, &mut finally_body_for_catch);
878 // (3) Finally epilogue
879 let finally_epilogue = tfr::emit_finally_epilogue(e, env, pos, jump_instrs, finally_end)?;
883 // We now emit the catch body; it is just cleanup code for the temp_local,
884 // a copy of the finally body (without the branching epilogue, since we are
885 // going to unwind rather than branch), and an unwind instruction.
887 // TODO: The HHVM emitter sometimes emits seemingly spurious
888 // unset-unnamed-local instructions into the catch block. These look
889 // like bugs in the emitter. Investigate; if they are bugs in the HHVM
890 // emitter, get them fixed there. If not, get a clear explanation of
891 // what they are for and why they are required.
893 let enclosing_span = env.scope.get_span();
894 let try_instrs = if jump_instrs_is_empty {
897 tfr::cleanup_try_body(alloc, &try_body)
899 let catch_instrs = InstrSeq::gather(
902 emit_pos(alloc, &enclosing_span),
903 make_finally_catch(alloc, e, exn_local, finally_body_for_catch),
906 let middle = InstrSeq::create_try_catch(
915 // Putting it all together
920 instr::label(alloc, finally_start),
921 emit_pos(alloc, pos),
924 instr::label(alloc, finally_end),
929 fn make_finally_catch<'arena, 'decl>(
930 alloc: &'arena bumpalo::Bump,
931 e: &mut Emitter<'arena, 'decl>,
932 exn_local: Local<'arena>,
933 finally_body: InstrSeq<'arena>,
934 ) -> InstrSeq<'arena> {
935 let l2 = instr::unsetl(alloc, *e.local_gen_mut().get_retval());
936 let l1 = instr::unsetl(alloc, *e.local_gen_mut().get_label());
940 instr::popl(alloc, exn_local),
943 InstrSeq::create_try_catch(
951 vec![instr::pushl(alloc, exn_local), instr::chain_faults(alloc)],
954 instr::pushl(alloc, exn_local),
960 fn emit_try_catch<'a, 'arena, 'decl>(
961 e: &mut Emitter<'arena, 'decl>,
962 env: &mut Env<'a, 'arena>,
964 try_block: &[ast::Stmt],
965 catch_list: &[ast::Catch],
966 ) -> Result<InstrSeq<'arena>> {
967 e.local_scope(|e| emit_try_catch_(e, env, pos, try_block, catch_list))
970 fn emit_try_catch_<'a, 'arena, 'decl>(
971 e: &mut Emitter<'arena, 'decl>,
972 env: &mut Env<'a, 'arena>,
974 try_block: &[ast::Stmt],
975 catch_list: &[ast::Catch],
976 ) -> Result<InstrSeq<'arena>> {
977 let alloc = env.arena;
978 if is_empty_block(&try_block) {
979 return Ok(instr::empty(alloc));
981 let end_label = e.label_gen_mut().next_regular();
983 let catch_instrs = InstrSeq::gather(
987 .map(|catch| emit_catch(e, env, pos, end_label, catch))
988 .collect::<Result<Vec<_>>>()?,
990 let in_try = env.flags.contains(env::Flags::IN_TRY);
991 env.flags.set(env::Flags::IN_TRY, true);
992 let try_body = emit_stmts(e, env, try_block);
993 env.flags.set(env::Flags::IN_TRY, in_try);
995 let try_instrs = InstrSeq::gather(alloc, vec![try_body?, emit_pos(alloc, pos)]);
996 Ok(InstrSeq::create_try_catch(
1006 fn emit_catch<'a, 'arena, 'decl>(
1007 e: &mut Emitter<'arena, 'decl>,
1008 env: &mut Env<'a, 'arena>,
1012 ) -> Result<InstrSeq<'arena>> {
1013 let alloc = env.arena;
1014 // Note that this is a "regular" label; we're not going to branch to
1015 // it directly in the event of an exception.
1016 let next_catch = e.label_gen_mut().next_regular();
1017 let id = hhbc_id::class::ClassType::from_ast_name_and_mangle(alloc, &(catch.0).1);
1018 Ok(InstrSeq::gather(
1022 instr::instanceofd(alloc, id),
1023 instr::jmpz(alloc, next_catch),
1024 instr::setl(alloc, Local::Named(Str::new_str(alloc, &((catch.1).1).1))),
1026 emit_stmts(e, env, &catch.2)?,
1027 emit_pos(alloc, pos),
1028 instr::jmp(alloc, end_label),
1029 instr::label(alloc, next_catch),
1034 fn emit_foreach<'a, 'arena, 'decl>(
1035 e: &mut Emitter<'arena, 'decl>,
1036 env: &mut Env<'a, 'arena>,
1038 collection: &ast::Expr,
1039 iterator: &ast::AsExpr,
1040 block: &[ast::Stmt],
1041 ) -> Result<InstrSeq<'arena>> {
1042 use ast::AsExpr as A;
1043 e.local_scope(|e| match iterator {
1044 A::AsV(_) | A::AsKv(_, _) => emit_foreach_(e, env, pos, collection, iterator, block),
1045 A::AwaitAsV(pos, _) | A::AwaitAsKv(pos, _, _) => {
1046 emit_foreach_await(e, env, pos, collection, iterator, block)
1051 fn emit_foreach_<'a, 'arena, 'decl>(
1052 e: &mut Emitter<'arena, 'decl>,
1053 env: &mut Env<'a, 'arena>,
1055 collection: &ast::Expr,
1056 iterator: &ast::AsExpr,
1057 block: &[ast::Stmt],
1058 ) -> Result<InstrSeq<'arena>> {
1059 let collection_instrs = emit_expr::emit_expr(e, env, collection)?;
1060 scope::with_unnamed_locals_and_iterators(e, |e| {
1061 let alloc = e.alloc;
1062 let iter_id = e.iterator_mut().get();
1063 let loop_break_label = e.label_gen_mut().next_regular();
1064 let loop_continue_label = e.label_gen_mut().next_regular();
1065 let loop_head_label = e.label_gen_mut().next_regular();
1066 let (key_id, val_id, preamble) = emit_iterator_key_value_storage(e, env, iterator)?;
1067 let iter_args = IterArgs {
1069 key_id: Maybe::from(key_id),
1072 let body = env.do_in_loop_body(
1075 loop_continue_label,
1080 let iter_init = InstrSeq::gather(
1084 emit_pos(alloc, &collection.1),
1085 instr::iterinit(alloc, iter_args.clone(), loop_break_label),
1088 let iterate = InstrSeq::gather(
1091 instr::label(alloc, loop_head_label),
1094 instr::label(alloc, loop_continue_label),
1095 emit_pos(alloc, pos),
1096 instr::iternext(alloc, iter_args, loop_head_label),
1099 let iter_done = instr::label(alloc, loop_break_label);
1100 Ok((iter_init, iterate, iter_done))
1104 fn emit_foreach_await<'a, 'arena, 'decl>(
1105 e: &mut Emitter<'arena, 'decl>,
1106 env: &mut Env<'a, 'arena>,
1108 collection: &ast::Expr,
1109 iterator: &ast::AsExpr,
1110 block: &[ast::Stmt],
1111 ) -> Result<InstrSeq<'arena>> {
1112 let instr_collection = emit_expr::emit_expr(e, env, collection)?;
1113 scope::with_unnamed_local(e, |e, iter_temp_local| {
1114 let alloc = e.alloc;
1115 let input_is_async_iterator_label = e.label_gen_mut().next_regular();
1116 let next_label = e.label_gen_mut().next_regular();
1117 let exit_label = e.label_gen_mut().next_regular();
1118 let pop_and_exit_label = e.label_gen_mut().next_regular();
1119 let async_eager_label = e.label_gen_mut().next_regular();
1120 let next_meth = hhbc_id::method::from_raw_string(alloc, "next");
1121 let iter_init = InstrSeq::gather(
1128 hhbc_id::class::from_raw_string(alloc, "HH\\AsyncIterator"),
1130 instr::jmpnz(alloc, input_is_async_iterator_label),
1131 emit_fatal::emit_fatal_runtime(
1134 "Unable to iterate non-AsyncIterator asynchronously",
1136 instr::label(alloc, input_is_async_iterator_label),
1137 instr::popl(alloc, iter_temp_local),
1140 let loop_body_instr =
1141 env.do_in_loop_body(e, exit_label, next_label, None, block, emit_block)?;
1142 let iterate = InstrSeq::gather(
1145 instr::label(alloc, next_label),
1146 instr::cgetl(alloc, iter_temp_local),
1147 instr::nulluninit(alloc),
1148 instr::fcallobjmethodd(
1151 FcallFlags::empty(),
1155 Some(async_eager_label),
1160 ObjNullFlavor::NullThrows,
1162 instr::await_(alloc),
1163 instr::label(alloc, async_eager_label),
1165 instr::istypec(alloc, IstypeOp::OpNull),
1166 instr::jmpnz(alloc, pop_and_exit_label),
1167 emit_foreach_await_key_value_storage(e, env, iterator)?,
1169 emit_pos(alloc, pos),
1170 instr::jmp(alloc, next_label),
1171 instr::label(alloc, pop_and_exit_label),
1173 instr::label(alloc, exit_label),
1176 let iter_done = instr::unsetl(alloc, iter_temp_local);
1177 Ok((iter_init, iterate, iter_done))
1181 // Assigns a location to store values for foreach-key and foreach-value and
1182 // creates a code to populate them.
1183 // NOT suitable for foreach (... await ...) which uses different code-gen
1184 // Returns: key_local_opt * value_local * key_preamble * value_preamble
1186 // - key_local_opt - local variable to store a foreach-key value if it is
1188 // - value_local - local variable to store a foreach-value
1189 // - key_preamble - list of instructions to populate foreach-key
1190 // - value_preamble - list of instructions to populate foreach-value
1191 fn emit_iterator_key_value_storage<'a, 'arena, 'decl>(
1192 e: &mut Emitter<'arena, 'decl>,
1193 env: &mut Env<'a, 'arena>,
1194 iterator: &ast::AsExpr,
1195 ) -> Result<(Option<Local<'arena>>, Local<'arena>, InstrSeq<'arena>)> {
1196 use ast::AsExpr as A;
1197 let alloc = env.arena;
1198 fn get_id_of_simple_lvar_opt(lvar: &ast::Expr_) -> Result<Option<&str>> {
1199 if let Some(ast::Lid(pos, id)) = lvar.as_lvar() {
1200 let name = local_id::get_name(&id);
1201 if name == special_idents::THIS {
1202 return Err(emit_fatal::raise_fatal_parse(
1204 "Cannot re-assign $this",
1206 } else if !(superglobals::is_superglobal(&name)) {
1207 return Ok(Some(name));
1213 A::AsKv(k, v) => Ok(
1215 get_id_of_simple_lvar_opt(&k.2)?,
1216 get_id_of_simple_lvar_opt(&v.2)?,
1218 (Some(key_id), Some(val_id)) => (
1219 Some(Local::Named(Slice::new(alloc.alloc_str(key_id).as_bytes()))),
1220 Local::Named(Slice::new(alloc.alloc_str(val_id).as_bytes())),
1221 instr::empty(alloc),
1224 let key_local = e.local_gen_mut().get_unnamed();
1225 let val_local = e.local_gen_mut().get_unnamed();
1226 let (mut key_preamble, key_load) =
1227 emit_iterator_lvalue_storage(e, env, k, key_local)?;
1228 let (mut val_preamble, val_load) =
1229 emit_iterator_lvalue_storage(e, env, v, val_local)?;
1230 // HHVM prepends code to initialize non-plain, non-list foreach-key
1231 // to the value preamble - do the same to minimize diffs
1232 if !(k.2).is_list() {
1233 key_preamble.extend(val_preamble);
1234 val_preamble = key_preamble;
1235 key_preamble = vec![];
1243 InstrSeq::gather(alloc, val_preamble),
1244 InstrSeq::gather(alloc, val_load),
1245 InstrSeq::gather(alloc, key_preamble),
1246 InstrSeq::gather(alloc, key_load),
1253 A::AsV(v) => Ok(match get_id_of_simple_lvar_opt(&v.2)? {
1256 Local::Named(Str::new_str(alloc, val_id)),
1257 instr::empty(alloc),
1260 let val_local = e.local_gen_mut().get_unnamed();
1261 let (val_preamble, val_load) = emit_iterator_lvalue_storage(e, env, v, val_local)?;
1268 InstrSeq::gather(alloc, val_preamble),
1269 InstrSeq::gather(alloc, val_load),
1275 _ => Err(Unrecoverable(
1276 "emit_iterator_key_value_storage with iterator using await".into(),
1281 fn emit_iterator_lvalue_storage<'a, 'arena, 'decl>(
1282 e: &mut Emitter<'arena, 'decl>,
1283 env: &mut Env<'a, 'arena>,
1285 local: Local<'arena>,
1286 ) -> Result<(Vec<InstrSeq<'arena>>, Vec<InstrSeq<'arena>>)> {
1287 let alloc = env.arena;
1289 ast::Expr_::Call(_) => Err(emit_fatal::raise_fatal_parse(
1291 "Can't use return value in write context",
1293 ast::Expr_::List(es) => {
1294 let (preamble, load_values) = emit_load_list_elements(
1305 let load_values = vec![
1306 InstrSeq::gather(alloc, load_values.into_iter().rev().collect()),
1307 instr::unsetl(alloc, local),
1309 Ok((preamble, load_values))
1312 let (lhs, rhs, set_op) = emit_expr::emit_lval_op_nonlist_steps(
1318 instr::cgetl(alloc, local),
1321 false, // TODO: Readonly iterator assignment
1325 vec![rhs, set_op, instr::popc(alloc), instr::unsetl(alloc, local)],
1331 fn emit_load_list_elements<'a, 'arena, 'decl>(
1332 e: &mut Emitter<'arena, 'decl>,
1333 env: &mut Env<'a, 'arena>,
1334 path: Vec<InstrSeq<'arena>>,
1336 ) -> Result<(Vec<InstrSeq<'arena>>, Vec<InstrSeq<'arena>>)> {
1337 let alloc = env.arena;
1338 let (preamble, load_value): (Vec<Vec<InstrSeq<'arena>>>, Vec<Vec<InstrSeq<'arena>>>) = es
1342 emit_load_list_element(
1346 .map(|x| InstrSeq::clone(alloc, x))
1347 .collect::<Vec<_>>(),
1352 .collect::<Result<Vec<_>>>()?
1356 preamble.into_iter().flatten().collect(),
1357 load_value.into_iter().flatten().collect(),
1361 fn emit_load_list_element<'a, 'arena, 'decl>(
1362 e: &mut Emitter<'arena, 'decl>,
1363 env: &mut Env<'a, 'arena>,
1364 mut path: Vec<InstrSeq<'arena>>,
1367 ) -> Result<(Vec<InstrSeq<'arena>>, Vec<InstrSeq<'arena>>)> {
1368 let alloc = env.arena;
1369 let query_value = |path| {
1373 InstrSeq::gather(alloc, path),
1378 MemberKey::EI(i as i64, ReadonlyOp::Any),
1384 ast::Expr_::Lvar(lid) => {
1385 let load_value = InstrSeq::gather(
1391 Local::Named(Str::new_str(alloc, local_id::get_name(&lid.1))),
1396 (vec![], vec![load_value])
1398 ast::Expr_::List(es) => {
1399 let instr_dim = instr::dim(
1402 MemberKey::EI(i as i64, ReadonlyOp::Any),
1404 path.push(instr_dim);
1405 emit_load_list_elements(e, env, path, es)?
1408 let set_instrs = emit_expr::emit_lval_op_nonlist(
1417 false, // TODO readonly load list elements
1419 let load_value = InstrSeq::gather(alloc, vec![set_instrs, instr::popc(alloc)]);
1420 (vec![], vec![load_value])
1425 //Emit code for the value and possibly key l-value operation in a foreach
1426 // await statement. The result of invocation of the `next` method has been
1427 // stored on top of the stack. For example:
1428 // foreach (foo() await as $a->f => list($b[0], $c->g)) { ... }
1429 // Here, we need to construct l-value operations that access the [0] (for $a->f)
1430 // and [1;0] (for $b[0]) and [1;1] (for $c->g) indices of the array returned
1431 // from the `next` method.
1432 fn emit_foreach_await_key_value_storage<'a, 'arena, 'decl>(
1433 e: &mut Emitter<'arena, 'decl>,
1434 env: &mut Env<'a, 'arena>,
1435 iterator: &ast::AsExpr,
1436 ) -> Result<InstrSeq<'arena>> {
1437 use ast::AsExpr as A;
1438 let alloc = env.arena;
1440 A::AwaitAsKv(_, k, v) | A::AsKv(k, v) => Ok(InstrSeq::gather(
1443 emit_foreach_await_lvalue_storage(e, env, k, &[0], true)?,
1444 emit_foreach_await_lvalue_storage(e, env, v, &[1], false)?,
1447 A::AwaitAsV(_, v) | A::AsV(v) => emit_foreach_await_lvalue_storage(e, env, v, &[1], false),
1451 // Emit code for either the key or value l-value operation in foreach await.
1452 // `indices` is the initial prefix of the array indices ([0] for key or [1] for
1453 // value) that is prepended onto the indices needed for list destructuring
1455 // TODO: we don't need unnamed local if the target is a local
1456 fn emit_foreach_await_lvalue_storage<'a, 'arena, 'decl>(
1457 e: &mut Emitter<'arena, 'decl>,
1458 env: &mut Env<'a, 'arena>,
1461 keep_on_stack: bool,
1462 ) -> Result<InstrSeq<'arena>> {
1463 scope::with_unnamed_local(e, |e, local| {
1465 instr::popl(e.alloc, local),
1468 emit_expr::emit_lval_op_list(
1481 instr::pushl(e.alloc, local)
1483 instr::unsetl(e.alloc, local)
1489 fn emit_stmts<'a, 'arena, 'decl>(
1490 e: &mut Emitter<'arena, 'decl>,
1491 env: &mut Env<'a, 'arena>,
1493 ) -> Result<InstrSeq<'arena>> {
1494 let alloc = env.arena;
1495 Ok(InstrSeq::gather(
1498 .map(|s| emit_stmt(e, env, s))
1499 .collect::<Result<Vec<_>>>()?,
1503 fn emit_block<'a, 'arena, 'decl>(
1504 env: &mut Env<'a, 'arena>,
1505 emitter: &mut Emitter<'arena, 'decl>,
1506 block: &[ast::Stmt],
1507 ) -> Result<InstrSeq<'arena>> {
1508 emit_stmts(emitter, env, block)
1511 fn emit_do<'a, 'arena, 'decl>(
1512 e: &mut Emitter<'arena, 'decl>,
1513 env: &mut Env<'a, 'arena>,
1516 ) -> Result<InstrSeq<'arena>> {
1517 let alloc = env.arena;
1518 let break_label = e.label_gen_mut().next_regular();
1519 let cont_label = e.label_gen_mut().next_regular();
1520 let start_label = e.label_gen_mut().next_regular();
1521 let jmpnz_instr = emit_expr::emit_jmpnz(e, env, cond, start_label)?.instrs;
1522 Ok(InstrSeq::gather(
1525 instr::label(alloc, start_label),
1526 env.do_in_loop_body(e, break_label, cont_label, None, body, emit_block)?,
1527 instr::label(alloc, cont_label),
1529 instr::label(alloc, break_label),
1534 fn emit_while<'a, 'arena, 'decl>(
1535 e: &mut Emitter<'arena, 'decl>,
1536 env: &mut Env<'a, 'arena>,
1539 ) -> Result<InstrSeq<'arena>> {
1540 let alloc = env.arena;
1541 let break_label = e.label_gen_mut().next_regular();
1542 let cont_label = e.label_gen_mut().next_regular();
1543 let start_label = e.label_gen_mut().next_regular();
1544 /* TODO: This is *bizarre* codegen for a while loop.
1545 It would be better to generate this as
1546 instr_label continue_label;
1548 instr_jmpz break_label;
1550 instr_jmp continue_label;
1551 instr_label break_label;
1553 let i3 = emit_expr::emit_jmpnz(e, env, cond, start_label)?.instrs;
1554 let i2 = env.do_in_loop_body(e, break_label, cont_label, None, body, emit_block)?;
1555 let i1 = emit_expr::emit_jmpz(e, env, cond, break_label)?.instrs;
1556 Ok(InstrSeq::gather(
1560 instr::label(alloc, start_label),
1562 instr::label(alloc, cont_label),
1564 instr::label(alloc, break_label),
1569 fn emit_for<'a, 'arena, 'decl>(
1570 e: &mut Emitter<'arena, 'decl>,
1571 env: &mut Env<'a, 'arena>,
1572 e1: &Vec<ast::Expr>,
1573 e2: &Option<ast::Expr>,
1574 e3: &Vec<ast::Expr>,
1576 ) -> Result<InstrSeq<'arena>> {
1577 let alloc = env.arena;
1578 let break_label = e.label_gen_mut().next_regular();
1579 let cont_label = e.label_gen_mut().next_regular();
1580 let start_label = e.label_gen_mut().next_regular();
1581 fn emit_cond<'a, 'arena, 'decl>(
1582 emitter: &mut Emitter<'arena, 'decl>,
1583 env: &mut Env<'a, 'arena>,
1586 cond: &Option<ast::Expr>,
1587 ) -> Result<InstrSeq<'arena>> {
1588 let alloc = env.arena;
1594 instr::jmp(alloc, label)
1599 emit_expr::emit_jmpz(emitter, env, cond, label)
1601 emit_expr::emit_jmpnz(emitter, env, cond, label)
1607 // TODO: this is bizarre codegen for a "for" loop.
1608 // This should be codegen'd as
1609 // emit_ignored_expr initializer;
1610 // instr_label start_label;
1611 // from_expr condition;
1612 // instr_jmpz break_label;
1614 // instr_label continue_label;
1615 // emit_ignored_expr increment;
1616 // instr_jmp start_label;
1617 // instr_label break_label;
1618 let i5 = emit_cond(e, env, false, start_label, e2)?;
1619 let i4 = emit_expr::emit_ignored_exprs(e, env, &Pos::make_none(), e3)?;
1620 let i3 = env.do_in_loop_body(e, break_label, cont_label, None, body, emit_block)?;
1621 let i2 = emit_cond(e, env, true, break_label, e2)?;
1622 let i1 = emit_expr::emit_ignored_exprs(e, env, &Pos::make_none(), e1)?;
1623 Ok(InstrSeq::gather(
1628 instr::label(alloc, start_label),
1630 instr::label(alloc, cont_label),
1633 instr::label(alloc, break_label),
1638 pub fn emit_dropthrough_return<'a, 'arena, 'decl>(
1639 e: &mut Emitter<'arena, 'decl>,
1640 env: &mut Env<'a, 'arena>,
1641 ) -> Result<InstrSeq<'arena>> {
1642 let alloc = env.arena;
1643 match e.emit_statement_state().default_dropthrough.as_ref() {
1644 Some(instrs) => Ok(InstrSeq::clone(alloc, instrs)),
1646 let ret = emit_return(e, env)?;
1647 let state = e.emit_statement_state();
1650 &(state.function_pos.last_char()),
1653 vec![InstrSeq::clone(alloc, &state.default_return_value), ret],
1660 pub fn emit_final_stmt<'a, 'arena, 'decl>(
1661 e: &mut Emitter<'arena, 'decl>,
1662 env: &mut Env<'a, 'arena>,
1664 ) -> Result<InstrSeq<'arena>> {
1665 let alloc = env.arena;
1667 a::Stmt_::Throw(_) | a::Stmt_::Return(_) | a::Stmt_::YieldBreak => emit_stmt(e, env, stmt),
1668 a::Stmt_::Block(stmts) => emit_final_stmts(e, env, stmts),
1670 let ret = emit_dropthrough_return(e, env)?;
1671 Ok(InstrSeq::gather(alloc, vec![emit_stmt(e, env, stmt)?, ret]))
1676 pub fn emit_final_stmts<'a, 'arena, 'decl>(
1677 e: &mut Emitter<'arena, 'decl>,
1678 env: &mut Env<'a, 'arena>,
1679 block: &[ast::Stmt],
1680 ) -> Result<InstrSeq<'arena>> {
1681 let alloc = env.arena;
1683 [] => emit_dropthrough_return(e, env),
1685 let mut ret = Vec::with_capacity(block.len());
1686 for (i, s) in block.iter().enumerate() {
1687 let instrs = if i == block.len() - 1 {
1688 emit_final_stmt(e, env, s)?
1690 emit_stmt(e, env, s)?
1694 Ok(InstrSeq::gather(alloc, ret))
1699 pub fn emit_markup<'a, 'arena, 'decl>(
1700 e: &mut Emitter<'arena, 'decl>,
1701 env: &mut Env<'a, 'arena>,
1702 (_, s): &ast::Pstring,
1703 check_for_hashbang: bool,
1704 ) -> Result<InstrSeq<'arena>> {
1705 let alloc = env.arena;
1706 let mut emit_ignored_call_expr = |fname: String, expr: ast::Expr| {
1707 let call_expr = ast::Expr(
1710 ast::Expr_::mk_call(
1714 ast::Expr_::mk_id(ast_defs::Id(Pos::make_none(), fname)),
1717 vec![(ParamKind::Pnormal, expr)],
1721 emit_expr::emit_ignored_expr(e, env, &Pos::make_none(), &call_expr)
1723 let mut emit_ignored_call_expr_for_nonempty_str = |fname: String, expr_str: String| {
1724 if expr_str.is_empty() {
1725 Ok(instr::empty(alloc))
1727 emit_ignored_call_expr(
1729 ast::Expr((), Pos::make_none(), ast::Expr_::mk_string(expr_str.into())),
1733 let markup = if s.is_empty() {
1737 static ref HASHBANG_PAT: regex::Regex = regex::Regex::new(r"^#!.*\n").unwrap();
1739 let tail = String::from(match HASHBANG_PAT.shortest_match(&s) {
1740 Some(i) if check_for_hashbang => {
1741 // if markup text starts with #!
1742 // - extract a line with hashbang
1743 // - it will be emitted as a call to print_hashbang function
1744 // - emit remaining part of text as regular markup
1749 emit_ignored_call_expr_for_nonempty_str(special_functions::ECHO.into(), tail)?
1754 fn emit_break<'a, 'arena, 'decl>(
1755 e: &mut Emitter<'arena, 'decl>,
1756 env: &mut Env<'a, 'arena>,
1758 ) -> InstrSeq<'arena> {
1759 use tfr::EmitBreakOrContinueFlags as Flags;
1760 tfr::emit_break_or_continue(e, Flags::IS_BREAK, env, pos, 1)
1763 fn emit_continue<'a, 'arena, 'decl>(
1764 e: &mut Emitter<'arena, 'decl>,
1765 env: &mut Env<'a, 'arena>,
1767 ) -> InstrSeq<'arena> {
1768 use tfr::EmitBreakOrContinueFlags as Flags;
1769 tfr::emit_break_or_continue(e, Flags::empty(), env, pos, 1)
1772 fn emit_await_assignment<'a, 'arena, 'decl>(
1773 e: &mut Emitter<'arena, 'decl>,
1774 env: &mut Env<'a, 'arena>,
1778 ) -> Result<InstrSeq<'arena>> {
1779 let alloc = env.arena;
1780 match lval.2.as_lvar() {
1781 Some(ast::Lid(_, id)) if !emit_expr::is_local_this(env, &id) => Ok(InstrSeq::gather(
1784 emit_expr::emit_await(e, env, pos, r)?,
1785 emit_pos(alloc, pos),
1788 emit_expr::get_local(e, env, pos, local_id::get_name(&id))?,
1793 let awaited_instrs = emit_await(e, env, pos, r)?;
1794 scope::with_unnamed_local(e, |e, temp| {
1795 let rhs_instrs = instr::pushl(e.alloc, temp);
1796 let (lhs, rhs, setop) = emit_expr::emit_lval_op_nonlist_steps(
1805 false, // unnamed local assignment does not need readonly check
1808 InstrSeq::gather(e.alloc, vec![awaited_instrs, instr::popl(e.alloc, temp)]),
1810 InstrSeq::gather(e.alloc, vec![rhs, setop, instr::popc(e.alloc)]),
1817 fn emit_if<'a, 'arena, 'decl>(
1818 e: &mut Emitter<'arena, 'decl>,
1819 env: &mut Env<'a, 'arena>,
1821 condition: &ast::Expr,
1822 consequence: &[ast::Stmt],
1823 alternative: &[ast::Stmt],
1824 ) -> Result<InstrSeq<'arena>> {
1825 let alloc = env.arena;
1826 if alternative.is_empty() || (alternative.len() == 1 && alternative[0].1.is_noop()) {
1827 let done_label = e.label_gen_mut().next_regular();
1828 let consequence_instr = emit_stmts(e, env, consequence)?;
1829 Ok(InstrSeq::gather(
1832 emit_expr::emit_jmpz(e, env, condition, done_label)?.instrs,
1834 instr::label(alloc, done_label),
1838 let alternative_label = e.label_gen_mut().next_regular();
1839 let done_label = e.label_gen_mut().next_regular();
1840 let consequence_instr = emit_stmts(e, env, consequence)?;
1841 let alternative_instr = emit_stmts(e, env, alternative)?;
1842 Ok(InstrSeq::gather(
1845 emit_expr::emit_jmpz(e, env, condition, alternative_label)?.instrs,
1847 emit_pos(alloc, pos),
1848 instr::jmp(alloc, done_label),
1849 instr::label(alloc, alternative_label),
1851 instr::label(alloc, done_label),