Rename directory hhbc to hackc
[hiphop-php.git] / hphp / hack / src / hackc / emitter / emit_statement.rs
blobbac87b66a13254e70997f3fd9b54838665e8c112
1 // Copyright (c) Facebook, Inc. and its affiliates.
2 //
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};
8 use emit_fatal;
9 use emit_pos::{emit_pos, emit_pos_then};
10 use env::{emitter::Emitter, Env};
11 use hhbc_assertion_utils::*;
12 use hhbc_ast::*;
13 use hhbc_id::Id;
14 use instruction_sequence::{instr, Error::Unrecoverable, InstrSeq, Result};
15 use label::Label;
16 use label_rewriter;
17 use local::Local;
18 use scope::scope;
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};
24 use oxidized::{
25     aast as a, ast,
26     ast_defs::{self, ParamKind},
27     local_id,
28     pos::Pos,
30 use regex::Regex;
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>,
37 ) {
38     *e.emit_statement_state_mut(alloc) = state;
41 pub(crate) type Level = usize;
43 // Wrapper functions
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 {
53     match &e.2 {
54         ast::Expr_::ReadonlyExpr(_) => true,
55         _ => false,
56     }
59 fn set_bytes_kind(name: &str) -> Option<Setrange> {
60     lazy_static! {
61         static ref RE: Regex =
62             Regex::new(r#"(?i)^hh\\set_bytes(_rev)?_([a-z0-9]+)(_vec)?$"#).unwrap();
63     }
64     RE.captures(name).map_or(None, |groups| {
65         let op = if groups.get(1).is_some() {
66             // == _rev
67             SetrangeOp::Reverse
68         } else {
69             SetrangeOp::Forward
70         };
71         let kind = groups.get(2).unwrap().as_str();
72         let vec = groups.get(3).is_some(); // == _vec
73         if kind == "string" && !vec {
74             Some(Setrange {
75                 size: 1,
76                 vec: true,
77                 op,
78             })
79         } else {
80             let size = match kind {
81                 "bool" | "int8" => 1,
82                 "int16" => 2,
83                 "int32" | "float32" => 4,
84                 "int64" | "float64" => 8,
85                 _ => return None,
86             };
87             Some(Setrange { size, vec, op })
88         }
89     })
92 pub fn emit_stmt<'a, 'arena, 'decl>(
93     e: &mut Emitter<'arena, 'decl>,
94     env: &mut Env<'a, 'arena>,
95     stmt: &ast::Stmt,
96 ) -> Result<InstrSeq<'arena>> {
97     let alloc = env.arena;
98     let pos = &stmt.0;
99     match &stmt.1 {
100         a::Stmt_::YieldBreak => Ok(InstrSeq::gather(
101             alloc,
102             vec![instr::null(alloc), emit_return(e, env)?],
103         )),
104         a::Stmt_::Expr(e_) => match &e_.2 {
105             a::Expr_::Await(a) => Ok(InstrSeq::gather(
106                 alloc,
107                 vec![emit_await(e, env, &e_.1, a)?, instr::popc(alloc)],
108             )),
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") {
114                         Ok(InstrSeq::gather(
115                             alloc,
116                             exprs
117                                 .iter()
118                                 .map(|ex| {
119                                     emit_expr::emit_unset_expr(e, env, expect_normal_paramkind(ex)?)
120                                 })
121                                 .collect::<std::result::Result<Vec<_>, _>>()?,
122                         ))
123                     } else {
124                         if let Some(kind) = set_bytes_kind(fname) {
125                             emit_expr::emit_set_range_expr(e, env, &e_.1, fname, kind, exprs)
126                         } else {
127                             emit_expr::emit_ignored_expr(e, env, &e_.1, e_)
128                         }
129                     }
130                 } else {
131                     emit_expr::emit_ignored_expr(e, env, pos, e_)
132                 }
133             }
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());
141                             if has_elements {
142                                 scope::with_unnamed_local(e, |e, temp| {
143                                     let alloc = e.alloc;
144                                     Ok((
145                                         InstrSeq::gather(
146                                             alloc,
147                                             vec![awaited_instrs, instr::popl(alloc, temp)],
148                                         ),
149                                         (
150                                             alloc,
151                                             emit_expr::emit_lval_op_list(
152                                                 e,
153                                                 env,
154                                                 pos,
155                                                 Some(&temp),
156                                                 &[],
157                                                 e_lhs,
158                                                 false,
159                                                 is_readonly_expr(e_rhs),
160                                             )?,
161                                         )
162                                             .into(),
163                                         instr::unsetl(alloc, temp),
164                                     ))
165                                 })
166                             } else {
167                                 Ok(InstrSeq::gather(
168                                     alloc,
169                                     vec![awaited_instrs, instr::popc(alloc)],
170                                 ))
171                             }
172                         } else {
173                             emit_await_assignment(e, env, await_pos, e_lhs, e_await)
174                         }
175                     } else {
176                         emit_expr::emit_ignored_expr(e, env, pos, e_)
177                     }
178                 } else {
179                     emit_expr::emit_ignored_expr(e, env, pos, e_)
180                 }
181             }
182             _ => emit_expr::emit_ignored_expr(e, env, pos, e_),
183         },
184         a::Stmt_::Return(r_opt) => match r_opt.as_ref() {
185             Some(r) => {
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_)?
189                 } else {
190                     emit_expr(e, env, &r)?
191                 };
192                 Ok(InstrSeq::gather(
193                     alloc,
194                     vec![expr_instr, emit_pos(alloc, pos), ret],
195                 ))
196             }
197             None => Ok(InstrSeq::gather(
198                 alloc,
199                 vec![
200                     instr::null(alloc),
201                     emit_pos(alloc, pos),
202                     emit_return(e, env)?,
203                 ],
204             )),
205         },
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(
215             alloc,
216             vec![
217                 emit_expr::emit_expr(e, env, x)?,
218                 emit_pos(alloc, pos),
219                 instr::throw(alloc),
220             ],
221         )),
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[..])
228             } else {
229                 emit_try_catch_finally(e, env, pos, &try_block, &catch_list[..], &finally_block)
230             }
231         }
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)),
238     }
241 fn emit_case<'c, 'a, 'arena, 'decl>(
242     e: &mut Emitter<'arena, 'decl>,
243     env: &mut Env<'a, 'arena>,
244     case: &'c ast::Case,
245 ) -> Result<(
246     InstrSeq<'arena>,
247     (Option<(&'c ast::Expr, Label)>, Option<Label>),
248 )> {
249     let alloc = env.arena;
250     let l = e.label_gen_mut().next_regular();
251     Ok(match case {
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),
255         ),
256         ast::Case::Default(_, b) => (
257             InstrSeq::gather(alloc, vec![instr::label(alloc, l), emit_block(env, e, b)?]),
258             (None, Some(l)),
259         ),
260     })
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() {
271         InstrSeq::gather(
272             alloc,
273             vec![
274                 emit_expr::emit_two_exprs(e, env, &case_expr.1, scrutinee_expr, &case_expr)?,
275                 instr::eq(alloc),
276                 instr::jmpnz(alloc, case_handler_label),
277             ],
278         )
279     } else {
280         let next_case_label = e.label_gen_mut().next_regular();
281         InstrSeq::gather(
282             alloc,
283             vec![
284                 instr::dup(alloc),
285                 emit_expr::emit_expr(e, env, &case_expr)?,
286                 emit_pos(alloc, &case_expr.1),
287                 instr::eq(alloc),
288                 instr::jmpz(alloc, next_case_label),
289                 instr::popc(alloc),
290                 instr::jmp(alloc, case_handler_label),
291                 instr::label(alloc, next_case_label),
292             ],
293         )
294     })
297 fn emit_awaitall<'a, 'arena, 'decl>(
298     e: &mut Emitter<'arena, 'decl>,
299     env: &mut Env<'a, 'arena>,
300     pos: &Pos,
301     el: &[(Option<ast::Lid>, ast::Expr)],
302     block: &ast::Block,
303 ) -> Result<InstrSeq<'arena>> {
304     let alloc = env.arena;
305     match el {
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),
309     }
312 fn emit_awaitall_single<'a, 'arena, 'decl>(
313     e: &mut Emitter<'arena, 'decl>,
314     env: &mut Env<'a, 'arena>,
315     pos: &Pos,
316     lval: &Option<ast::Lid>,
317     expr: &ast::Expr,
318     block: &ast::Block,
319 ) -> Result<InstrSeq<'arena>> {
320     scope::with_unnamed_locals(e, |e| {
321         let alloc = e.alloc;
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)) => {
326                 let l = e
327                     .local_gen_mut()
328                     .init_unnamed_for_tempname(local_id::get_name(&id));
329                 (instr::popl(alloc, *l), instr::unsetl(alloc, *l))
330             }
331         };
332         Ok((
333             InstrSeq::gather(alloc, vec![load_arg, load]),
334             emit_stmts(e, env, block)?,
335             unset,
336         ))
337     })
340 fn emit_awaitall_multi<'a, 'arena, 'decl>(
341     e: &mut Emitter<'arena, 'decl>,
342     env: &mut Env<'a, 'arena>,
343     pos: &Pos,
344     el: &[(Option<ast::Lid>, ast::Expr)],
345     block: &ast::Block,
346 ) -> Result<InstrSeq<'arena>> {
347     scope::with_unnamed_locals(e, |e| {
348         let alloc = e.alloc;
349         let mut instrs = vec![];
350         for (_, expr) in el.iter() {
351             instrs.push(emit_expr::emit_expr(e, env, expr)?)
352         }
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
360                     .local_gen_mut()
361                     .init_unnamed_for_tempname(local_id::get_name(&id))
362                     .to_owned(),
363             });
364         }
366         let init_locals = InstrSeq::gather(
367             alloc,
368             locals
369                 .iter()
370                 .rev()
371                 .map(|l| instr::popl(alloc, *l))
372                 .collect(),
373         );
374         let unset_locals = InstrSeq::gather(
375             alloc,
376             locals.iter().map(|l| instr::unsetl(alloc, *l)).collect(),
377         );
378         let mut instrs = vec![];
379         for l in locals.iter() {
380             instrs.push({
381                 let label_done = e.label_gen_mut().next_regular();
382                 InstrSeq::gather(
383                     alloc,
384                     vec![
385                         instr::pushl(alloc, *l),
386                         instr::dup(alloc),
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),
392                     ],
393                 )
394             });
395         }
397         let unpack = InstrSeq::gather(alloc, instrs);
398         let await_all = InstrSeq::gather(
399             alloc,
400             vec![instr::awaitall_list(alloc, locals), instr::popc(alloc)],
401         );
402         let block_instrs = emit_stmts(e, env, block)?;
403         Ok((
404             // before
405             InstrSeq::gather(alloc, vec![load_args, init_locals]),
406             // inner
407             InstrSeq::gather(
408                 alloc,
409                 vec![emit_pos(alloc, pos), await_all, unpack, block_instrs],
410             ),
411             // after
412             unset_locals,
413         ))
414     })
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 {
425         emit_stmts(
426             e,
427             env,
428             using
429                 .exprs
430                 .1
431                 .iter()
432                 .rev()
433                 .fold(using.block.clone(), |block, expr| {
434                     vec![ast::Stmt(
435                         expr.1.clone(),
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()]),
440                             block,
441                         }),
442                     )]
443                 })
444                 .as_slice(),
445         )
446     } else {
447         e.local_scope(|e| {
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())),
452                         InstrSeq::gather(
453                             alloc,
454                             vec![
455                                 emit_expr::emit_expr(e, env, &(using.exprs.1[0]))?,
456                                 emit_pos(alloc, &block_pos),
457                                 instr::popc(alloc),
458                             ],
459                         ),
460                     ),
461                     _ => {
462                         let l = e.local_gen_mut().get_unnamed();
463                         (
464                             l,
465                             InstrSeq::gather(
466                                 alloc,
467                                 vec![
468                                     emit_expr::emit_expr(e, env, &(using.exprs.1[0]))?,
469                                     instr::setl(alloc, l),
470                                     instr::popc(alloc),
471                                 ],
472                             ),
473                         )
474                     }
475                 },
476                 ast::Expr_::Lvar(lid) => (
477                     Local::Named(Str::new_str(alloc, local_id::get_name(&lid.1).as_str())),
478                     InstrSeq::gather(
479                         alloc,
480                         vec![
481                             emit_expr::emit_expr(e, env, &(using.exprs.1[0]))?,
482                             emit_pos(alloc, &block_pos),
483                             instr::popc(alloc),
484                         ],
485                     ),
486                 ),
487                 _ => {
488                     let l = e.local_gen_mut().get_unnamed();
489                     (
490                         l,
491                         InstrSeq::gather(
492                             alloc,
493                             vec![
494                                 emit_expr::emit_expr(e, env, &(using.exprs.1[0]))?,
495                                 instr::setl(alloc, l),
496                                 instr::popc(alloc),
497                             ],
498                         ),
499                     )
500                 }
501             };
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 {
510                 body
511             } else {
512                 tfr::cleanup_try_body(alloc, &body)
513             };
515             let emit_finally = |
516                 e: &mut Emitter<'arena, 'decl>,
517                 local: Local<'arena>,
518                 has_await: bool,
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();
523                     (
524                         InstrSeq::gather(
525                             alloc,
526                             vec![
527                                 instr::await_(alloc),
528                                 instr::label(alloc, after_await),
529                                 instr::popc(alloc),
530                             ],
531                         ),
532                         Some(after_await),
533                     )
534                 } else {
535                     (instr::popc(alloc), None)
536                 };
537                 let fn_name = hhbc_id::method::from_raw_string(
538                     alloc,
539                     if has_await {
540                         "__disposeAsync"
541                     } else {
542                         "__dispose"
543                     },
544                 );
545                 InstrSeq::gather(
546                     alloc,
547                     vec![
548                         instr::cgetl(alloc, local),
549                         instr::nulluninit(alloc),
550                         instr::fcallobjmethodd(
551                             alloc,
552                             FcallArgs::new(
553                                 FcallFlags::empty(),
554                                 1,
555                                 Slice::empty(),
556                                 Slice::empty(),
557                                 async_eager_label,
558                                 0,
559                                 env.call_context
560                                     .as_ref()
561                                     .map(|s| -> &str { alloc.alloc_str(s.as_ref()) }),
562                             ),
563                             fn_name,
564                             ObjNullFlavor::NullThrows,
565                         ),
566                         epilogue,
567                         if is_block_scoped {
568                             instr::unsetl(alloc, local)
569                         } else {
570                             instr::empty(alloc)
571                         },
572                     ],
573                 )
574             };
575             let exn_local = e.local_gen_mut().get_unnamed();
576             let middle = if is_empty_block(&using.block) {
577                 instr::empty(alloc)
578             } else {
579                 let finally_instrs = emit_finally(e, local, using.has_await, using.is_block_scoped);
580                 let catch_instrs = InstrSeq::gather(
581                     alloc,
582                     vec![
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),
586                     ],
587                 );
588                 InstrSeq::create_try_catch(
589                     alloc,
590                     e.label_gen_mut(),
591                     None,
592                     true,
593                     try_instrs,
594                     catch_instrs,
595                 )
596             };
597             Ok(InstrSeq::gather(
598                 alloc,
599                 vec![
600                     preamble,
601                     middle,
602                     instr::label(alloc, finally_start),
603                     emit_finally(e, local, using.has_await, using.is_block_scoped),
604                     finally_epilogue,
605                     instr::label(alloc, finally_end),
606                 ],
607             ))
608         })
609     }
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());
615     }
616     let mut first = 0;
617     let mut last = block.len() - 1;
618     loop {
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));
621         }
622         if block[first].0.is_none() {
623             first += 1;
624         }
625         if block[last].0.is_none() {
626             last -= 1;
627         }
628     }
631 fn emit_cases<'a, 'arena, 'decl>(
632     env: &mut Env<'a, 'arena>,
633     e: &mut Emitter<'arena, 'decl>,
634     pos: &Pos,
635     break_label: Label,
636     scrutinee_expr: &ast::Expr,
637     cases: &[ast::Case],
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() {
642         None => {
643             return Err(Unrecoverable(
644                 "impossible - switch statements must have at least one case".into(),
645             ));
646         }
647         Some((last, rest)) => {
648             // Emit all the cases except the last one
649             let mut res = rest
650                 .iter()
651                 .map(|case| emit_case(e, env, case))
652                 .collect::<Result<Vec<_>>>()?;
654             if has_default {
655                 // If there is a default, emit the last case as usual
656                 res.push(emit_case(e, env, last)?)
657             } else {
658                 // Otherwise, emit the last case with an added break
659                 match last {
660                     ast::Case::Case(expr, block) => {
661                         let l = e.label_gen_mut().next_regular();
662                         res.push((
663                             InstrSeq::gather(
664                                 alloc,
665                                 vec![
666                                     instr::label(alloc, l),
667                                     emit_block(env, e, block)?,
668                                     emit_break(e, env, &Pos::make_none()),
669                                 ],
670                             ),
671                             (Some((expr, l)), None),
672                         ))
673                     }
674                     ast::Case::Default(_, _) => {
675                         return Err(Unrecoverable(
676                             "impossible - there shouldn't be a default".into(),
677                         ));
678                     }
679                 };
680                 // ...and emit warning/exception for missing default
681                 let l = e.label_gen_mut().next_regular();
682                 res.push((
683                     InstrSeq::gather(
684                         alloc,
685                         vec![
686                             instr::label(alloc, l),
687                             emit_pos_then(alloc, pos, instr::throw_non_exhaustive_switch(alloc)),
688                         ],
689                     ),
690                     (None, Some(l)),
691                 ))
692             };
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
699                 .iter()
700                 .filter_map(|lopt| lopt.as_ref())
701                 .collect::<Vec<_>>()
702                 .as_slice()
703             {
704                 [] => break_label,
705                 [l] => **l,
706                 _ => {
707                     return Err(emit_fatal::raise_fatal_runtime(
708                         pos,
709                         "Switch statements may only contain one 'default' clause.",
710                     ));
711                 }
712             };
713             let case_expr_instrs = case_exprs
714                 .into_iter()
715                 .filter_map(|x| x.map(|x| emit_check_case(e, env, scrutinee_expr, x)))
716                 .collect::<Result<Vec<_>>>()?;
718             Ok((
719                 InstrSeq::gather(alloc, case_expr_instrs),
720                 InstrSeq::gather(alloc, case_body_instrs),
721                 default_label,
722             ))
723         }
724     }
727 fn emit_switch<'a, 'arena, 'decl>(
728     e: &mut Emitter<'arena, 'decl>,
729     env: &mut Env<'a, 'arena>,
730     pos: &Pos,
731     scrutinee_expr: &ast::Expr,
732     cl: &Vec<ast::Case>,
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))
737     } else {
738         (
739             emit_expr::emit_expr(e, env, scrutinee_expr)?,
740             instr::popc(alloc),
741         )
742     };
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)
748         })?;
749     Ok(InstrSeq::gather(
750         alloc,
751         vec![
752             instr_init,
753             case_expr_instrs,
754             instr_free,
755             instr::jmp(alloc, default_label),
756             case_body_instrs,
757             instr::label(alloc, break_label),
758         ],
759     ))
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>,
769     pos: &Pos,
770     r#try: &[ast::Stmt],
771     catch: &[ast::Catch],
772     finally: &ast::Block,
773 ) -> Result<InstrSeq<'arena>> {
774     let is_try_block_empty = false;
775     let emit_try_block =
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)
779             })
780         };
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>,
787     pos: &Pos,
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);
792     let emit_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)
795         };
796     e.local_scope(|e| {
797         emit_try_finally_(
798             e,
799             env,
800             pos,
801             emit_try_block,
802             finally_block,
803             is_try_block_empty,
804         )
805     })
808 fn emit_try_finally_<
809     'a,
810     'arena,
811     'decl,
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>,
816     pos: &Pos,
817     emit_try_block: E,
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);
824     };
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.
831     //
833     //     (1) Try 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();
856     //  (2) Finally body
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
865     // instruction.
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)?;
881     //  (4) Catch body
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 {
895         try_body
896     } else {
897         tfr::cleanup_try_body(alloc, &try_body)
898     };
899     let catch_instrs = InstrSeq::gather(
900         alloc,
901         vec![
902             emit_pos(alloc, &enclosing_span),
903             make_finally_catch(alloc, e, exn_local, finally_body_for_catch),
904         ],
905     );
906     let middle = InstrSeq::create_try_catch(
907         alloc,
908         e.label_gen_mut(),
909         None,
910         true,
911         try_instrs,
912         catch_instrs,
913     );
915     // Putting it all together
916     Ok(InstrSeq::gather(
917         alloc,
918         vec![
919             middle,
920             instr::label(alloc, finally_start),
921             emit_pos(alloc, pos),
922             finally_body,
923             finally_epilogue,
924             instr::label(alloc, finally_end),
925         ],
926     ))
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());
937     InstrSeq::gather(
938         alloc,
939         vec![
940             instr::popl(alloc, exn_local),
941             l1,
942             l2,
943             InstrSeq::create_try_catch(
944                 alloc,
945                 e.label_gen_mut(),
946                 None,
947                 false,
948                 finally_body,
949                 InstrSeq::gather(
950                     alloc,
951                     vec![instr::pushl(alloc, exn_local), instr::chain_faults(alloc)],
952                 ),
953             ),
954             instr::pushl(alloc, exn_local),
955             instr::throw(alloc),
956         ],
957     )
960 fn emit_try_catch<'a, 'arena, 'decl>(
961     e: &mut Emitter<'arena, 'decl>,
962     env: &mut Env<'a, 'arena>,
963     pos: &Pos,
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>,
973     pos: &Pos,
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));
980     };
981     let end_label = e.label_gen_mut().next_regular();
983     let catch_instrs = InstrSeq::gather(
984         alloc,
985         catch_list
986             .iter()
987             .map(|catch| emit_catch(e, env, pos, end_label, catch))
988             .collect::<Result<Vec<_>>>()?,
989     );
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(
997         alloc,
998         e.label_gen_mut(),
999         Some(end_label),
1000         false,
1001         try_instrs,
1002         catch_instrs,
1003     ))
1006 fn emit_catch<'a, 'arena, 'decl>(
1007     e: &mut Emitter<'arena, 'decl>,
1008     env: &mut Env<'a, 'arena>,
1009     pos: &Pos,
1010     end_label: Label,
1011     catch: &ast::Catch,
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(
1019         alloc,
1020         vec![
1021             instr::dup(alloc),
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))),
1025             instr::popc(alloc),
1026             emit_stmts(e, env, &catch.2)?,
1027             emit_pos(alloc, pos),
1028             instr::jmp(alloc, end_label),
1029             instr::label(alloc, next_catch),
1030         ],
1031     ))
1034 fn emit_foreach<'a, 'arena, 'decl>(
1035     e: &mut Emitter<'arena, 'decl>,
1036     env: &mut Env<'a, 'arena>,
1037     pos: &Pos,
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)
1047         }
1048     })
1051 fn emit_foreach_<'a, 'arena, 'decl>(
1052     e: &mut Emitter<'arena, 'decl>,
1053     env: &mut Env<'a, 'arena>,
1054     pos: &Pos,
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 {
1068             iter_id,
1069             key_id: Maybe::from(key_id),
1070             val_id,
1071         };
1072         let body = env.do_in_loop_body(
1073             e,
1074             loop_break_label,
1075             loop_continue_label,
1076             Some(iter_id),
1077             block,
1078             emit_block,
1079         )?;
1080         let iter_init = InstrSeq::gather(
1081             alloc,
1082             vec![
1083                 collection_instrs,
1084                 emit_pos(alloc, &collection.1),
1085                 instr::iterinit(alloc, iter_args.clone(), loop_break_label),
1086             ],
1087         );
1088         let iterate = InstrSeq::gather(
1089             alloc,
1090             vec![
1091                 instr::label(alloc, loop_head_label),
1092                 preamble,
1093                 body,
1094                 instr::label(alloc, loop_continue_label),
1095                 emit_pos(alloc, pos),
1096                 instr::iternext(alloc, iter_args, loop_head_label),
1097             ],
1098         );
1099         let iter_done = instr::label(alloc, loop_break_label);
1100         Ok((iter_init, iterate, iter_done))
1101     })
1104 fn emit_foreach_await<'a, 'arena, 'decl>(
1105     e: &mut Emitter<'arena, 'decl>,
1106     env: &mut Env<'a, 'arena>,
1107     pos: &Pos,
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(
1122             alloc,
1123             vec![
1124                 instr_collection,
1125                 instr::dup(alloc),
1126                 instr::instanceofd(
1127                     alloc,
1128                     hhbc_id::class::from_raw_string(alloc, "HH\\AsyncIterator"),
1129                 ),
1130                 instr::jmpnz(alloc, input_is_async_iterator_label),
1131                 emit_fatal::emit_fatal_runtime(
1132                     alloc,
1133                     pos,
1134                     "Unable to iterate non-AsyncIterator asynchronously",
1135                 ),
1136                 instr::label(alloc, input_is_async_iterator_label),
1137                 instr::popl(alloc, iter_temp_local),
1138             ],
1139         );
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(
1143             alloc,
1144             vec![
1145                 instr::label(alloc, next_label),
1146                 instr::cgetl(alloc, iter_temp_local),
1147                 instr::nulluninit(alloc),
1148                 instr::fcallobjmethodd(
1149                     alloc,
1150                     FcallArgs::new(
1151                         FcallFlags::empty(),
1152                         1,
1153                         Slice::empty(),
1154                         Slice::empty(),
1155                         Some(async_eager_label),
1156                         0,
1157                         None,
1158                     ),
1159                     next_meth,
1160                     ObjNullFlavor::NullThrows,
1161                 ),
1162                 instr::await_(alloc),
1163                 instr::label(alloc, async_eager_label),
1164                 instr::dup(alloc),
1165                 instr::istypec(alloc, IstypeOp::OpNull),
1166                 instr::jmpnz(alloc, pop_and_exit_label),
1167                 emit_foreach_await_key_value_storage(e, env, iterator)?,
1168                 loop_body_instr,
1169                 emit_pos(alloc, pos),
1170                 instr::jmp(alloc, next_label),
1171                 instr::label(alloc, pop_and_exit_label),
1172                 instr::popc(alloc),
1173                 instr::label(alloc, exit_label),
1174             ],
1175         );
1176         let iter_done = instr::unsetl(alloc, iter_temp_local);
1177         Ok((iter_init, iterate, iter_done))
1178     })
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
1185 // where:
1186 // - key_local_opt - local variable to store a foreach-key value if it is
1187 //     declared
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(
1203                     &pos,
1204                     "Cannot re-assign $this",
1205                 ));
1206             } else if !(superglobals::is_superglobal(&name)) {
1207                 return Ok(Some(name));
1208             }
1209         };
1210         Ok(None)
1211     }
1212     match iterator {
1213         A::AsKv(k, v) => Ok(
1214             match (
1215                 get_id_of_simple_lvar_opt(&k.2)?,
1216                 get_id_of_simple_lvar_opt(&v.2)?,
1217             ) {
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),
1222                 ),
1223                 _ => {
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![];
1236                     };
1237                     (
1238                         Some(key_local),
1239                         val_local,
1240                         InstrSeq::gather(
1241                             alloc,
1242                             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),
1247                             ],
1248                         ),
1249                     )
1250                 }
1251             },
1252         ),
1253         A::AsV(v) => Ok(match get_id_of_simple_lvar_opt(&v.2)? {
1254             Some(val_id) => (
1255                 None,
1256                 Local::Named(Str::new_str(alloc, val_id)),
1257                 instr::empty(alloc),
1258             ),
1259             None => {
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)?;
1262                 (
1263                     None,
1264                     val_local,
1265                     InstrSeq::gather(
1266                         alloc,
1267                         vec![
1268                             InstrSeq::gather(alloc, val_preamble),
1269                             InstrSeq::gather(alloc, val_load),
1270                         ],
1271                     ),
1272                 )
1273             }
1274         }),
1275         _ => Err(Unrecoverable(
1276             "emit_iterator_key_value_storage with iterator using await".into(),
1277         )),
1278     }
1281 fn emit_iterator_lvalue_storage<'a, 'arena, 'decl>(
1282     e: &mut Emitter<'arena, 'decl>,
1283     env: &mut Env<'a, 'arena>,
1284     lvalue: &ast::Expr,
1285     local: Local<'arena>,
1286 ) -> Result<(Vec<InstrSeq<'arena>>, Vec<InstrSeq<'arena>>)> {
1287     let alloc = env.arena;
1288     match &lvalue.2 {
1289         ast::Expr_::Call(_) => Err(emit_fatal::raise_fatal_parse(
1290             &lvalue.1,
1291             "Can't use return value in write context",
1292         )),
1293         ast::Expr_::List(es) => {
1294             let (preamble, load_values) = emit_load_list_elements(
1295                 e,
1296                 env,
1297                 vec![instr::basel(
1298                     alloc,
1299                     local,
1300                     MemberOpMode::Warn,
1301                     ReadonlyOp::Any,
1302                 )],
1303                 es,
1304             )?;
1305             let load_values = vec![
1306                 InstrSeq::gather(alloc, load_values.into_iter().rev().collect()),
1307                 instr::unsetl(alloc, local),
1308             ];
1309             Ok((preamble, load_values))
1310         }
1311         _ => {
1312             let (lhs, rhs, set_op) = emit_expr::emit_lval_op_nonlist_steps(
1313                 e,
1314                 env,
1315                 &lvalue.1,
1316                 LValOp::Set,
1317                 lvalue,
1318                 instr::cgetl(alloc, local),
1319                 1,
1320                 false,
1321                 false, // TODO: Readonly iterator assignment
1322             )?;
1323             Ok((
1324                 vec![lhs],
1325                 vec![rhs, set_op, instr::popc(alloc), instr::unsetl(alloc, local)],
1326             ))
1327         }
1328     }
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>>,
1335     es: &[ast::Expr],
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
1339         .iter()
1340         .enumerate()
1341         .map(|(i, x)| {
1342             emit_load_list_element(
1343                 e,
1344                 env,
1345                 path.iter()
1346                     .map(|x| InstrSeq::clone(alloc, x))
1347                     .collect::<Vec<_>>(),
1348                 i,
1349                 x,
1350             )
1351         })
1352         .collect::<Result<Vec<_>>>()?
1353         .into_iter()
1354         .unzip();
1355     Ok((
1356         preamble.into_iter().flatten().collect(),
1357         load_value.into_iter().flatten().collect(),
1358     ))
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>>,
1365     i: usize,
1366     elem: &ast::Expr,
1367 ) -> Result<(Vec<InstrSeq<'arena>>, Vec<InstrSeq<'arena>>)> {
1368     let alloc = env.arena;
1369     let query_value = |path| {
1370         InstrSeq::gather(
1371             alloc,
1372             vec![
1373                 InstrSeq::gather(alloc, path),
1374                 instr::querym(
1375                     alloc,
1376                     0,
1377                     QueryOp::CGet,
1378                     MemberKey::EI(i as i64, ReadonlyOp::Any),
1379                 ),
1380             ],
1381         )
1382     };
1383     Ok(match &elem.2 {
1384         ast::Expr_::Lvar(lid) => {
1385             let load_value = InstrSeq::gather(
1386                 alloc,
1387                 vec![
1388                     query_value(path),
1389                     instr::setl(
1390                         alloc,
1391                         Local::Named(Str::new_str(alloc, local_id::get_name(&lid.1))),
1392                     ),
1393                     instr::popc(alloc),
1394                 ],
1395             );
1396             (vec![], vec![load_value])
1397         }
1398         ast::Expr_::List(es) => {
1399             let instr_dim = instr::dim(
1400                 alloc,
1401                 MemberOpMode::Warn,
1402                 MemberKey::EI(i as i64, ReadonlyOp::Any),
1403             );
1404             path.push(instr_dim);
1405             emit_load_list_elements(e, env, path, es)?
1406         }
1407         _ => {
1408             let set_instrs = emit_expr::emit_lval_op_nonlist(
1409                 e,
1410                 env,
1411                 &elem.1,
1412                 LValOp::Set,
1413                 elem,
1414                 query_value(path),
1415                 1,
1416                 false,
1417                 false, // TODO readonly load list elements
1418             )?;
1419             let load_value = InstrSeq::gather(alloc, vec![set_instrs, instr::popc(alloc)]);
1420             (vec![], vec![load_value])
1421         }
1422     })
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;
1439     match iterator {
1440         A::AwaitAsKv(_, k, v) | A::AsKv(k, v) => Ok(InstrSeq::gather(
1441             alloc,
1442             vec![
1443                 emit_foreach_await_lvalue_storage(e, env, k, &[0], true)?,
1444                 emit_foreach_await_lvalue_storage(e, env, v, &[1], false)?,
1445             ],
1446         )),
1447         A::AwaitAsV(_, v) | A::AsV(v) => emit_foreach_await_lvalue_storage(e, env, v, &[1], false),
1448     }
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>,
1459     lvalue: &ast::Expr,
1460     indices: &[isize],
1461     keep_on_stack: bool,
1462 ) -> Result<InstrSeq<'arena>> {
1463     scope::with_unnamed_local(e, |e, local| {
1464         Ok((
1465             instr::popl(e.alloc, local),
1466             (
1467                 e.alloc,
1468                 emit_expr::emit_lval_op_list(
1469                     e,
1470                     env,
1471                     &lvalue.1,
1472                     Some(&local),
1473                     indices,
1474                     lvalue,
1475                     false,
1476                     false,
1477                 )?,
1478             )
1479                 .into(),
1480             if keep_on_stack {
1481                 instr::pushl(e.alloc, local)
1482             } else {
1483                 instr::unsetl(e.alloc, local)
1484             },
1485         ))
1486     })
1489 fn emit_stmts<'a, 'arena, 'decl>(
1490     e: &mut Emitter<'arena, 'decl>,
1491     env: &mut Env<'a, 'arena>,
1492     stl: &[ast::Stmt],
1493 ) -> Result<InstrSeq<'arena>> {
1494     let alloc = env.arena;
1495     Ok(InstrSeq::gather(
1496         alloc,
1497         stl.iter()
1498             .map(|s| emit_stmt(e, env, s))
1499             .collect::<Result<Vec<_>>>()?,
1500     ))
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>,
1514     body: &[ast::Stmt],
1515     cond: &ast::Expr,
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(
1523         alloc,
1524         vec![
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),
1528             jmpnz_instr,
1529             instr::label(alloc, break_label),
1530         ],
1531     ))
1534 fn emit_while<'a, 'arena, 'decl>(
1535     e: &mut Emitter<'arena, 'decl>,
1536     env: &mut Env<'a, 'arena>,
1537     cond: &ast::Expr,
1538     body: &[ast::Stmt],
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;
1547              emit_expr e;
1548              instr_jmpz break_label;
1549              body;
1550              instr_jmp continue_label;
1551              instr_label break_label;
1552     */
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(
1557         alloc,
1558         vec![
1559             i1,
1560             instr::label(alloc, start_label),
1561             i2,
1562             instr::label(alloc, cont_label),
1563             i3,
1564             instr::label(alloc, break_label),
1565         ],
1566     ))
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>,
1575     body: &[ast::Stmt],
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>,
1584         jmpz: bool,
1585         label: Label,
1586         cond: &Option<ast::Expr>,
1587     ) -> Result<InstrSeq<'arena>> {
1588         let alloc = env.arena;
1589         Ok(match cond {
1590             None => {
1591                 if jmpz {
1592                     instr::empty(alloc)
1593                 } else {
1594                     instr::jmp(alloc, label)
1595                 }
1596             }
1597             Some(cond) => {
1598                 if jmpz {
1599                     emit_expr::emit_jmpz(emitter, env, cond, label)
1600                 } else {
1601                     emit_expr::emit_jmpnz(emitter, env, cond, label)
1602                 }?
1603                 .instrs
1604             }
1605         })
1606     }
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;
1613     //  body;
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(
1624         alloc,
1625         vec![
1626             i1,
1627             i2,
1628             instr::label(alloc, start_label),
1629             i3,
1630             instr::label(alloc, cont_label),
1631             i4,
1632             i5,
1633             instr::label(alloc, break_label),
1634         ],
1635     ))
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)),
1645         None => {
1646             let ret = emit_return(e, env)?;
1647             let state = e.emit_statement_state();
1648             Ok(emit_pos_then(
1649                 alloc,
1650                 &(state.function_pos.last_char()),
1651                 InstrSeq::gather(
1652                     alloc,
1653                     vec![InstrSeq::clone(alloc, &state.default_return_value), ret],
1654                 ),
1655             ))
1656         }
1657     }
1660 pub fn emit_final_stmt<'a, 'arena, 'decl>(
1661     e: &mut Emitter<'arena, 'decl>,
1662     env: &mut Env<'a, 'arena>,
1663     stmt: &ast::Stmt,
1664 ) -> Result<InstrSeq<'arena>> {
1665     let alloc = env.arena;
1666     match &stmt.1 {
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),
1669         _ => {
1670             let ret = emit_dropthrough_return(e, env)?;
1671             Ok(InstrSeq::gather(alloc, vec![emit_stmt(e, env, stmt)?, ret]))
1672         }
1673     }
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;
1682     match block {
1683         [] => emit_dropthrough_return(e, env),
1684         _ => {
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)?
1689                 } else {
1690                     emit_stmt(e, env, s)?
1691                 };
1692                 ret.push(instrs);
1693             }
1694             Ok(InstrSeq::gather(alloc, ret))
1695         }
1696     }
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(
1708             (),
1709             Pos::make_none(),
1710             ast::Expr_::mk_call(
1711                 ast::Expr(
1712                     (),
1713                     Pos::make_none(),
1714                     ast::Expr_::mk_id(ast_defs::Id(Pos::make_none(), fname)),
1715                 ),
1716                 vec![],
1717                 vec![(ParamKind::Pnormal, expr)],
1718                 None,
1719             ),
1720         );
1721         emit_expr::emit_ignored_expr(e, env, &Pos::make_none(), &call_expr)
1722     };
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))
1726         } else {
1727             emit_ignored_call_expr(
1728                 fname,
1729                 ast::Expr((), Pos::make_none(), ast::Expr_::mk_string(expr_str.into())),
1730             )
1731         }
1732     };
1733     let markup = if s.is_empty() {
1734         instr::empty(alloc)
1735     } else {
1736         lazy_static! {
1737             static ref HASHBANG_PAT: regex::Regex = regex::Regex::new(r"^#!.*\n").unwrap();
1738         }
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
1745                 &s[i..]
1746             }
1747             _ => s,
1748         });
1749         emit_ignored_call_expr_for_nonempty_str(special_functions::ECHO.into(), tail)?
1750     };
1751     Ok(markup)
1754 fn emit_break<'a, 'arena, 'decl>(
1755     e: &mut Emitter<'arena, 'decl>,
1756     env: &mut Env<'a, 'arena>,
1757     pos: &Pos,
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>,
1766     pos: &Pos,
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>,
1775     pos: &Pos,
1776     lval: &ast::Expr,
1777     r: &ast::Expr,
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(
1782             alloc,
1783             vec![
1784                 emit_expr::emit_await(e, env, pos, r)?,
1785                 emit_pos(alloc, pos),
1786                 instr::popl(
1787                     alloc,
1788                     emit_expr::get_local(e, env, pos, local_id::get_name(&id))?,
1789                 ),
1790             ],
1791         )),
1792         _ => {
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(
1797                     e,
1798                     env,
1799                     pos,
1800                     LValOp::Set,
1801                     lval,
1802                     rhs_instrs,
1803                     1,
1804                     false,
1805                     false, // unnamed local assignment does not need readonly check
1806                 )?;
1807                 Ok((
1808                     InstrSeq::gather(e.alloc, vec![awaited_instrs, instr::popl(e.alloc, temp)]),
1809                     lhs,
1810                     InstrSeq::gather(e.alloc, vec![rhs, setop, instr::popc(e.alloc)]),
1811                 ))
1812             })
1813         }
1814     }
1817 fn emit_if<'a, 'arena, 'decl>(
1818     e: &mut Emitter<'arena, 'decl>,
1819     env: &mut Env<'a, 'arena>,
1820     pos: &Pos,
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(
1830             alloc,
1831             vec![
1832                 emit_expr::emit_jmpz(e, env, condition, done_label)?.instrs,
1833                 consequence_instr,
1834                 instr::label(alloc, done_label),
1835             ],
1836         ))
1837     } else {
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(
1843             alloc,
1844             vec![
1845                 emit_expr::emit_jmpz(e, env, condition, alternative_label)?.instrs,
1846                 consequence_instr,
1847                 emit_pos(alloc, pos),
1848                 instr::jmp(alloc, done_label),
1849                 instr::label(alloc, alternative_label),
1850                 alternative_instr,
1851                 instr::label(alloc, done_label),
1852             ],
1853         ))
1854     }