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.
6 #![allow(unused_variables, dead_code)]
8 use ast_constant_folder_rust as ast_constant_folder;
9 use ast_scope_rust::Scope;
10 use emit_fatal_rust as emit_fatal;
11 use emit_symbol_refs_rust as emit_symbol_refs;
12 use emit_type_constant_rust as emit_type_constant;
13 use env::{emitter::Emitter, local, Env};
15 use hhbc_id_rust::{class, function, method, Id};
16 use hhbc_string_utils_rust as string_utils;
17 use instruction_sequence_rust::{unrecoverable, Error::Unrecoverable, InstrSeq, Result};
18 use label_rust::Label;
19 use naming_special_names_rust::{
20 emitter_special_functions, fb, pseudo_consts, pseudo_functions, special_functions,
21 special_idents, superglobals, user_attributes,
23 use options::{CompilerFlags, HhvmFlags, LangFlags, Options};
24 use oxidized::{aast, aast_defs, ast as tast, ast_defs, local_id, pos::Pos};
25 use runtime::TypedValue;
26 use scope_rust::scope;
28 use std::{collections::BTreeMap, convert::TryInto, iter};
31 pub struct EmitJmpResult {
32 // generated instruction sequence
34 // does instruction sequence fall through
36 // was label associated with emit operation used
40 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
49 fn is_incdec(&self) -> bool {
50 if let Self::IncDec(_) = self {
57 pub fn is_local_this(env: &Env, lid: &local_id::LocalId) -> bool {
58 local_id::get_name(lid) == special_idents::THIS
59 && Scope::has_this(&env.scope)
60 && !Scope::is_toplevel(&env.scope)
65 use oxidized::{aast_defs::Lid, aast_visitor, aast_visitor::Node, ast as tast, ast_defs};
66 use std::{collections::HashMap, marker::PhantomData};
74 impl Default for AliasInfo {
75 fn default() -> Self {
77 first_inout: std::usize::MAX,
78 last_write: std::usize::MIN,
85 pub fn add_inout(&mut self, i: usize) {
86 if i < self.first_inout {
91 pub fn add_write(&mut self, i: usize) {
92 if i > self.last_write {
97 pub fn add_use(&mut self) {
101 pub fn in_range(&self, i: usize) -> bool {
102 i > self.first_inout || i <= self.last_write
105 pub fn has_single_ref(&self) -> bool {
110 type AliasInfoMap = HashMap<String, AliasInfo>;
112 fn add_write(name: String, i: usize, map: &mut AliasInfoMap) {
113 map.entry(name).or_default().add_write(i);
116 fn add_inout(name: String, i: usize, map: &mut AliasInfoMap) {
117 map.entry(name).or_default().add_inout(i);
120 fn add_use(name: String, map: &mut AliasInfoMap) {
121 map.entry(name).or_default().add_use();
124 // determines if value of a local 'name' that appear in parameter 'i'
125 // should be saved to local because it might be overwritten later
126 fn should_save_local_value(name: String, i: usize, aliases: &AliasInfoMap) -> bool {
127 aliases.get(&name).map_or(false, |alias| alias.in_range(i))
130 fn should_move_local_value(local: &local::Type, aliases: &AliasInfoMap) -> bool {
132 local::Type::Named(name) => aliases
134 .map_or(true, |alias| alias.has_single_ref()),
135 local::Type::Unnamed(_) => false,
139 fn collect_written_variables(env: &Env, args: &Vec<tast::Expr>) -> AliasInfoMap {
140 let mut acc = HashMap::new();
143 .for_each(|(i, arg)| handle_arg(env, true, i, arg, &mut acc));
147 fn handle_arg(env: &Env, is_top: bool, i: usize, arg: &tast::Expr, acc: &mut AliasInfoMap) {
148 use tast::{Expr, Expr_};
150 let Expr(_, e) = arg;
152 Expr_::Callconv(x) => {
153 if let (ast_defs::ParamKind::Pinout, Expr(_, Expr_::Lvar(lid))) = &**x {
154 let Lid(_, lid) = &**lid;
155 if !is_local_this(env, &lid) {
156 add_use(lid.1.to_string(), acc);
158 add_inout(lid.1.to_string(), i, acc);
160 add_write(lid.1.to_string(), i, acc);
165 Expr_::Lvar(lid) => {
166 let Lid(_, (_, id)) = &**lid;
167 add_use(id.to_string(), acc);
170 // dive into argument value
173 phantom: PhantomData,
175 &mut Ctx { state: acc, env, i },
183 phantom: PhantomData<&'a str>,
187 state: &'a mut AliasInfoMap,
192 impl<'a> aast_visitor::Visitor for Visitor<'a> {
193 type Context = Ctx<'a>;
194 type Ex = ast_defs::Pos;
201 ) -> &mut dyn aast_visitor::Visitor<
202 Context = Self::Context,
211 fn visit_expr_(&mut self, c: &mut Self::Context, p: &tast::Expr_) {
212 p.recurse(c, self.object());
214 tast::Expr_::Binop(expr) => {
215 let (bop, left, _) = &**expr;
216 if let ast_defs::Bop::Eq(_) = bop {
217 collect_lvars_hs(c, left)
220 tast::Expr_::Unop(expr) => {
221 let (uop, e) = &**expr;
223 ast_defs::Uop::Uincr | ast_defs::Uop::Udecr => collect_lvars_hs(c, e),
227 tast::Expr_::Lvar(expr) => {
228 let Lid(_, (_, id)) = &**expr;
229 add_use(id.to_string(), &mut c.state);
231 tast::Expr_::Call(expr) => {
232 let (_, _, _, args, uarg) = &**expr;
234 .for_each(|arg| handle_arg(&c.env, false, c.i, arg, &mut c.state));
236 .map(|arg| handle_arg(&c.env, false, c.i, arg, &mut c.state));
243 // collect lvars on the left hand side of '=' operator
244 fn collect_lvars_hs(ctx: &mut Ctx, expr: &tast::Expr) {
245 let tast::Expr(_, e) = expr;
247 tast::Expr_::Lvar(lid) => {
248 let Lid(_, lid) = &**lid;
249 if !is_local_this(&ctx.env, &lid) {
250 add_use(lid.1.to_string(), &mut ctx.state);
251 add_write(lid.1.to_string(), ctx.i, &mut ctx.state);
254 tast::Expr_::List(exprs) => exprs.iter().for_each(|expr| collect_lvars_hs(ctx, expr)),
260 pub fn get_type_structure_for_hint(
263 targ_map: &BTreeMap<&str, i64>,
267 emit_type_constant::hint_to_type_constant(opts, tparams, targ_map, &hint, false, false);
268 unimplemented!("TODO(hrust) after porting most of emit_adata")
271 pub struct Setrange {
277 /// kind of value stored in local
278 pub enum StoredValueKind {
283 /// represents sequence of instructions interleaved with temp locals.
284 /// <(i, None) :: rest> - is emitted i :: <rest> (commonly used for final instructions in sequence)
285 /// <(i, Some(l, local_kind)) :: rest> is emitted as
289 /// setl/popl l; depending on local_kind
296 type InstrSeqWithLocals = Vec<(InstrSeq, Option<(local::Type, StoredValueKind)>)>;
298 /// result of emit_array_get
300 /// regular $a[..] that does not need to spill anything
302 /// subscript expression used as inout argument that need to spill intermediate values:
304 /// instruction sequence with locals to load value
305 load: InstrSeqWithLocals,
306 /// instruction to set value back (can use locals defined in load part)
311 struct ArrayGetBaseData<T> {
313 cls_instrs: InstrSeq,
314 setup_instrs: InstrSeq,
315 base_stack_size: usize,
316 cls_stack_size: usize,
319 /// result of emit_base
321 /// regular <base> part in <base>[..] that does not need to spill anything
322 Regular(ArrayGetBaseData<InstrSeq>),
323 /// base of subscript expression used as inout argument that need to spill
324 /// intermediate values
326 /// instructions to load base part
327 load: ArrayGetBaseData<InstrSeqWithLocals>,
328 /// instruction to load base part for setting inout argument back
333 pub fn emit_expr(emitter: &mut Emitter, env: &Env, expression: &tast::Expr) -> Result {
336 let tast::Expr(pos, expr) = expression;
344 let v = ast_constant_folder::expr_to_typed_value(emitter, &env.namespace, expression)
345 .map_err(|_| unrecoverable("expr_to_typed_value failed"))?;
346 emit_pos_then(emitter, pos, InstrSeq::make_typedvalue(v))
348 Expr_::PrefixedString(e) => emit_expr(emitter, env, &(&**e).1),
349 Expr_::ParenthesizedExpr(e) => emit_expr(emitter, env, &*e),
351 let Lid(pos, _) = &**e;
352 Ok(InstrSeq::gather(vec![
353 emit_pos(emitter, pos)?,
354 emit_local(env, BareThisOp::Notice, &**e)?,
357 Expr_::ClassConst(e) => emit_class_const(env, pos, &**e),
358 Expr_::Unop(e) => emit_unop(emitter, env, pos, &**e),
359 Expr_::Binop(e) => emit_binop(emitter, env, pos, e.as_ref()),
360 Expr_::Pipe(e) => emit_pipe(env, &**e),
361 Expr_::Is(is_expr) => {
362 let (e, h) = &**is_expr;
363 Ok(InstrSeq::gather(vec![
364 emit_expr(emitter, env, e)?,
365 emit_is_hint(env, pos, h)?,
368 Expr_::As(e) => emit_as(env, pos, &**e),
369 Expr_::Cast(e) => emit_cast(env, pos, &**e),
370 Expr_::Eif(e) => emit_conditional_expr(env, pos, &**e),
371 Expr_::ExprList(es) => Ok(InstrSeq::gather(
373 .map(|e| emit_expr(emitter, env, e).unwrap_or_default())
376 Expr_::ArrayGet(e) => {
377 let (base_expr, opt_elem_expr) = &**e;
378 match (base_expr.lvar_name(), opt_elem_expr) {
379 (Some(name), Some(e)) if name == superglobals::GLOBALS => {
380 Ok(InstrSeq::gather(vec![
381 emit_expr(emitter, env, e)?,
382 emit_pos(emitter, pos)?,
383 InstrSeq::make_cgetg(),
386 _ => emit_array_get(env, pos, QueryOp::CGet, &**e),
389 Expr_::ObjGet(e) => emit_obj_get(env, pos, QueryOp::CGet, &**e),
390 Expr_::Call(c) => emit_call_expr(emitter, env, pos, None, &*c),
391 Expr_::New(e) => emit_new(env, pos, &**e),
392 Expr_::Record(e) => emit_record(env, pos, &**e),
393 Expr_::Array(es) => emit_pos_then(emitter, pos, emit_collection(env, expression, es)?),
394 Expr_::Darray(e) => {
395 let instrs = emit_collection(env, expression, &mk_afkvalues(&(&**e).1))?;
396 emit_pos_then(emitter, pos, instrs)
398 Expr_::Varray(e) => {
399 let instrs = emit_collection(env, expression, &mk_afvalues(&(&**e).1))?;
400 emit_pos_then(emitter, pos, instrs)
402 Expr_::Collection(e) => emit_named_collection_str(env, expression, &**e),
403 Expr_::ValCollection(e) => {
404 let (kind, _, es) = &**e;
405 let fields = mk_afvalues(es);
406 let collection_typ = match kind {
407 aast_defs::VcKind::Vector => CollectionType::Vector,
408 aast_defs::VcKind::ImmVector => CollectionType::ImmVector,
409 aast_defs::VcKind::Set => CollectionType::Set,
410 aast_defs::VcKind::ImmSet => CollectionType::ImmSet,
411 _ => return emit_collection(env, expression, &fields),
413 emit_named_collection(env, pos, expression, &fields, collection_typ)
416 let (e1, e2) = (**e).to_owned();
417 let fields = mk_afvalues(&vec![e1, e2]);
418 emit_named_collection(env, pos, expression, &fields, CollectionType::Pair)
420 Expr_::KeyValCollection(e) => {
421 let (kind, _, fields) = &**e;
422 let fields = mk_afkvalues(
426 .map(|tast::Field(e1, e2)| (e1, e2))
429 let collection_typ = match kind {
430 aast_defs::KvcKind::Map => CollectionType::Map,
431 aast_defs::KvcKind::ImmMap => CollectionType::ImmMap,
432 _ => return emit_collection(env, expression, &fields),
434 emit_named_collection(env, pos, expression, &fields, collection_typ)
436 Expr_::Clone(e) => emit_pos_then(emitter, pos, emit_clone(env, &**e)?),
437 Expr_::Shape(e) => emit_pos_then(emitter, pos, emit_shape(env, expression, e)?),
438 Expr_::Await(e) => emit_await(emitter, env, pos, &**e),
439 Expr_::Yield(e) => emit_yield(emitter, env, pos, &**e),
440 Expr_::Efun(e) => emit_pos_then(emitter, pos, emit_lambda(env, &**e)?),
441 Expr_::ClassGet(e) => emit_class_get(env, QueryOp::CGet, &**e),
442 Expr_::String2(es) => emit_string2(env, pos, es),
443 Expr_::BracedExpr(e) => emit_expr(emitter, env, &**e),
445 let instrs = emit_id(emitter, env, &**e)?;
446 emit_pos_then(emitter, pos, instrs)
448 Expr_::Xml(e) => emit_xhp(env, pos, &**e),
449 Expr_::Callconv(e) => Err(Unrecoverable(
450 "emit_callconv: This should have been caught at emit_arg".into(),
452 Expr_::Import(e) => emit_import(env, pos, &**e),
453 Expr_::Omitted => Ok(InstrSeq::Empty),
454 Expr_::YieldBreak => Err(Unrecoverable(
455 "yield break should be in statement position".into(),
457 Expr_::YieldFrom(_) => Err(Unrecoverable("complex yield_from expression".into())),
458 Expr_::Lfun(_) => Err(Unrecoverable(
459 "expected Lfun to be converted to Efun during closure conversion emit_expr".into(),
461 _ => unimplemented!("TODO(hrust)"),
465 fn emit_exprs(e: &mut Emitter, env: &Env, exprs: &[tast::Expr]) -> Result {
466 if exprs.is_empty() {
472 .map(|expr| emit_expr(e, env, expr))
473 .collect::<Result<Vec<_>>>()?,
478 fn emit_pos_then(emitter: &Emitter, pos: &Pos, instrs: InstrSeq) -> Result {
479 Ok(emit_pos_rust::emit_pos_then(emitter, pos, instrs))
482 fn emit_pos(emitter: &Emitter, pos: &Pos) -> Result {
483 Ok(emit_pos_rust::emit_pos(emitter, pos))
486 fn emit_id(emitter: &mut Emitter, env: &Env, id: &tast::Sid) -> Result {
487 use pseudo_consts::*;
488 use InstructLitConst::*;
490 let ast_defs::Id(p, s) = id;
491 let res = match s.as_str() {
492 G__FILE__ => InstrSeq::make_lit_const(File),
493 G__DIR__ => InstrSeq::make_lit_const(Dir),
494 G__METHOD__ => InstrSeq::make_lit_const(Method),
495 G__FUNCTION_CREDENTIAL__ => InstrSeq::make_lit_const(FuncCred),
496 G__CLASS__ => InstrSeq::gather(vec![InstrSeq::make_self(), InstrSeq::make_classname()]),
497 G__COMPILER_FRONTEND__ => InstrSeq::make_string("hackc"),
498 G__LINE__ => InstrSeq::make_int(p.info_pos_extended().1.try_into().map_err(|_| {
499 emit_fatal::raise_fatal_parse(p, "error converting end of line from usize to isize")
501 G__NAMESPACE__ => InstrSeq::make_string(env.namespace.name.as_ref().map_or("", |s| &s[..])),
502 EXIT | DIE => return emit_exit(emitter, env, None),
504 //panic!("TODO: uncomment after D19350786 lands")
505 //let cid: ConstId = r#const::Type::from_ast_name(&s);
506 let cid: ConstId = string_utils::strip_global_ns(&s).to_string().into();
507 emit_symbol_refs::State::add_constant(emitter, cid.clone());
508 return emit_pos_then(emitter, p, InstrSeq::make_lit_const(CnsE(cid)));
514 fn emit_exit(emitter: &mut Emitter, env: &Env, expr_opt: Option<&tast::Expr>) -> Result {
515 Ok(InstrSeq::gather(vec![
516 expr_opt.map_or_else(|| Ok(InstrSeq::make_int(0)), |e| emit_expr(emitter, env, e))?,
517 InstrSeq::make_exit(),
524 (id, attributes, children): &(tast::Sid, Vec<tast::XhpAttribute>, Vec<tast::Expr>),
526 unimplemented!("TODO(hrust)")
529 fn emit_yield(e: &mut Emitter, env: &Env, pos: &Pos, af: &tast::Afield) -> Result {
530 unimplemented!("TODO(hrust)")
533 fn emit_import(env: &Env, pos: &Pos, (flavor, expr): &(tast::ImportFlavor, tast::Expr)) -> Result {
534 unimplemented!("TODO(hrust)")
537 fn emit_string2(env: &Env, pos: &Pos, es: &Vec<tast::Expr>) -> Result {
538 unimplemented!("TODO(hrust)")
541 fn emit_clone(env: &Env, expr: &tast::Expr) -> Result {
542 unimplemented!("TODO(hrust)")
545 fn emit_lambda(env: &Env, (fndef, ids): &(tast::Fun_, Vec<aast_defs::Lid>)) -> Result {
546 unimplemented!("TODO(hrust)")
549 pub fn emit_await(emitter: &mut Emitter, env: &Env, pos: &Pos, expr: &tast::Expr) -> Result {
550 let tast::Expr(_, e) = expr;
551 let cant_inline_gen_functions = emitter
555 .contains(HhvmFlags::JIT_ENABLE_RENAME_FUNCTION);
557 Some((_, tast::Expr(_, tast::Expr_::Id(id)), _, args, None))
558 if (cant_inline_gen_functions
560 && string_utils::strip_global_ns(&(*id.1)) == "gena") =>
562 return inline_gena_call(emitter, env, &args[0])
565 let after_await = emitter.label_gen_mut().next_regular();
566 let instrs = match e {
567 tast::Expr_::Call(c) => {
568 emit_call_expr(emitter, env, pos, Some(after_await.clone()), &*c)?
570 _ => emit_expr(emitter, env, expr)?,
572 Ok(InstrSeq::gather(vec![
574 emit_pos(emitter, pos)?,
575 InstrSeq::make_dup(),
576 InstrSeq::make_istypec(IstypeOp::OpNull),
577 InstrSeq::make_jmpnz(after_await.clone()),
578 InstrSeq::make_await(),
579 InstrSeq::make_label(after_await),
585 fn hack_arr_dv_arrs(opts: &Options) -> bool {
586 opts.hhvm.flags.contains(HhvmFlags::HACK_ARR_DV_ARRS)
589 fn inline_gena_call(emitter: &mut Emitter, env: &Env, arg: &tast::Expr) -> Result {
590 let load_arr = emit_expr(emitter, env, arg)?;
591 let async_eager_label = emitter.label_gen_mut().next_regular();
592 let hack_arr_dv_arrs = hack_arr_dv_arrs(emitter.options());
594 scope::with_unnamed_local(emitter, |e, arr_local| {
595 let before = InstrSeq::gather(vec![
597 if hack_arr_dv_arrs {
598 InstrSeq::make_cast_dict()
600 InstrSeq::make_cast_darray()
602 InstrSeq::make_popl(arr_local.clone()),
605 let inner = InstrSeq::gather(vec![
606 InstrSeq::make_nulluninit(),
607 InstrSeq::make_nulluninit(),
608 InstrSeq::make_nulluninit(),
609 InstrSeq::make_cgetl(arr_local.clone()),
610 InstrSeq::make_fcallclsmethodd(
612 FcallFlags::default(),
615 Some(async_eager_label.clone()),
618 method::from_raw_string(if hack_arr_dv_arrs {
623 class::from_raw_string("HH\\AwaitAllWaitHandle"),
625 InstrSeq::make_await(),
626 InstrSeq::make_label(async_eager_label.clone()),
627 InstrSeq::make_popc(),
630 InstrSeq::make_cgetl(arr_local.clone()),
631 |key_local, val_local| {
632 InstrSeq::gather(vec![
633 InstrSeq::make_cgetl(val_local),
634 InstrSeq::make_whresult(),
635 InstrSeq::make_basel(arr_local.clone(), MemberOpMode::Define),
636 InstrSeq::make_setm(0, MemberKey::EL(key_local)),
637 InstrSeq::make_popc(),
643 let after = InstrSeq::make_pushl(arr_local);
645 Ok((before, inner, after))
649 fn emit_iter<F: FnOnce(local::Type, local::Type) -> InstrSeq>(
651 collection: InstrSeq,
654 scope::with_unnamed_locals_and_iterators(e, |e| {
655 let iter_id = e.iterator_mut().get();
656 let val_id = e.local_gen_mut().get_unnamed();
657 let key_id = e.local_gen_mut().get_unnamed();
658 let loop_end = e.label_gen_mut().next_regular();
659 let loop_next = e.label_gen_mut().next_regular();
660 let iter_args = IterArgs {
662 key_id: Some(key_id.clone()),
663 val_id: val_id.clone(),
665 let iter_init = InstrSeq::gather(vec![
667 InstrSeq::make_iterinit(iter_args.clone(), loop_end.clone()),
669 let iterate = InstrSeq::gather(vec![
670 InstrSeq::make_label(loop_next.clone()),
671 f(val_id.clone(), key_id.clone()),
672 InstrSeq::make_iternext(iter_args, loop_next),
674 let iter_done = InstrSeq::gather(vec![
675 InstrSeq::make_unsetl(val_id),
676 InstrSeq::make_unsetl(key_id),
677 InstrSeq::make_label(loop_end),
679 (iter_init, iterate, iter_done)
686 fl: &Vec<(ast_defs::ShapeFieldName, tast::Expr)>,
688 unimplemented!("TODO(hrust)")
691 fn emit_named_collection(
695 fields: &Vec<tast::Afield>,
696 collection_typ: CollectionType,
698 unimplemented!("TODO(hrust)")
701 fn emit_named_collection_str(
704 (ast_defs::Id(pos, name), _, fields): &(
706 Option<tast::CollectionTarg>,
710 unimplemented!("TODO(hrust)")
713 fn mk_afkvalues(es: &Vec<(tast::Expr, tast::Expr)>) -> Vec<tast::Afield> {
716 .map(|(e1, e2)| tast::Afield::mk_afkvalue(e1, e2))
720 fn mk_afvalues(es: &Vec<tast::Expr>) -> Vec<tast::Afield> {
723 .map(|e| tast::Afield::mk_afvalue(e))
727 fn emit_collection(env: &Env, expr: &tast::Expr, fields: &Vec<tast::Afield>) -> Result {
728 unimplemented!("TODO(hrust)")
734 (cid, is_array, es): &(tast::Sid, bool, Vec<(tast::Expr, tast::Expr)>),
736 let es = mk_afkvalues(es);
737 unimplemented!("TODO(hrust)")
740 fn emit_call_isset_exprs(e: &mut Emitter, env: &Env, pos: &Pos, exprs: &[tast::Expr]) -> Result {
744 fn emit_idx(e: &mut Emitter, env: &Env, pos: &Pos, es: &[tast::Expr]) -> Result {
745 let default = if es.len() == 2 {
746 InstrSeq::make_null()
750 Ok(InstrSeq::gather(vec![
751 emit_exprs(e, env, es)?,
754 InstrSeq::make_idx(),
763 targs: &[tast::Targ],
765 uarg: Option<&tast::Expr>,
766 async_eager_label: Option<Label>,
768 if let Some(ast_defs::Id(_, s)) = expr.as_id() {
769 let fid = function::Type::from_ast_name(s);
770 emit_symbol_refs::add_function(e, fid);
772 let fcall_args = get_fcall_args(args, uarg, async_eager_label, false);
773 let FcallArgs(_, _, num_ret, _, _) = &fcall_args;
774 let num_uninit = num_ret - 1;
775 let default = scope::with_unnamed_locals(e, |e| {
776 let (lhs, fcall) = emit_call_lhs_and_fcall(e, env, expr, fcall_args, targs)?;
777 let (args, inout_setters) = emit_args_inout_setters(e, env, args)?;
778 let uargs = uarg.map_or(Ok(InstrSeq::Empty), |uarg| emit_expr(e, env, uarg))?;
781 InstrSeq::gather(vec![
783 iter::repeat(InstrSeq::make_nulluninit())
785 .collect::<Vec<_>>(),
798 .and_then(|ast_defs::Id(_, id)| {
799 emit_special_function(e, env, pos, &expr.0, &id, args, uarg, &default).transpose()
801 .unwrap_or(Ok(default))
804 fn emit_reified_targs(e: &mut Emitter, env: &Env, pos: &Pos, targs: &[&tast::Hint]) -> Result {
808 fn get_erased_tparams<'a>(env: &'a Env<'a>) -> Vec<&'a str> {
812 .filter_map(|tparam| match tparam.reified {
813 tast::ReifyKind::Erased => Some(tparam.name.1.as_str()),
819 fn has_non_tparam_generics_targs(env: &Env, targs: &[tast::Targ]) -> bool {
820 let erased_tparams = get_erased_tparams(env);
821 targs.iter().any(|targ| {
825 .map_or(true, |(id, _)| !erased_tparams.contains(&id.1.as_str()))
829 fn emit_call_lhs_and_fcall(
833 mut fcall_args: FcallArgs,
834 targs: &[tast::Targ],
835 ) -> Result<(InstrSeq, InstrSeq)> {
836 let tast::Expr(pos, expr_) = expr;
837 use tast::Expr_ as E_;
839 let emit_generics = |e, env, fcall_args: &mut FcallArgs| {
840 let does_not_have_non_tparam_generics = !has_non_tparam_generics_targs(env, targs);
841 if does_not_have_non_tparam_generics {
844 *(&mut fcall_args.0) = fcall_args.0 | FcallFlags::HAS_GENERICS;
859 E_::ObjGet(_) => unimplemented!(),
860 E_::ClassConst(_) => unimplemented!(),
861 E_::ClassGet(_) => unimplemented!(),
863 let FcallArgs(flags, num_args, _, _, _) = fcall_args;
864 let fq_id = match string_utils::strip_global_ns(&id.1) {
865 "min" if num_args == 2 && flags.contains(FcallFlags::HAS_UNPACK) => {
866 function::Type::from_ast_name("__SystemLib\\min2")
868 "max" if num_args == 2 && flags.contains(FcallFlags::HAS_UNPACK) => {
869 function::Type::from_ast_name("__SystemLib\\max2")
872 //TODO(hrust): enable `function::Type::from_ast_name(&id.1)`
873 string_utils::strip_global_ns(&id.1).to_string().into()
876 let generics = emit_generics(e, env, &mut fcall_args)?;
878 InstrSeq::gather(vec![
879 InstrSeq::make_nulluninit(),
880 InstrSeq::make_nulluninit(),
881 InstrSeq::make_nulluninit(),
883 InstrSeq::gather(vec![generics, InstrSeq::make_fcallfuncd(fcall_args, fq_id)]),
886 E_::String(s) => unimplemented!(),
888 let tmp = e.local_gen_mut().get_unnamed();
890 InstrSeq::gather(vec![
891 InstrSeq::make_nulluninit(),
892 InstrSeq::make_nulluninit(),
893 InstrSeq::make_nulluninit(),
894 emit_expr(e, env, expr)?,
895 InstrSeq::make_popl(tmp.clone()),
897 InstrSeq::gather(vec![
898 InstrSeq::make_pushl(tmp),
899 InstrSeq::make_fcallfunc(fcall_args),
906 fn emit_args_inout_setters(
910 ) -> Result<(InstrSeq, InstrSeq)> {
911 fn emit_arg_and_inout_setter(
916 ) -> Result<(InstrSeq, InstrSeq)> {
917 use tast::Expr_ as E_;
919 E_::Callconv(cc) => {
922 E_::Lvar(l) => unimplemented!(),
923 // inout $arr[...][...]
924 E_::ArrayGet(ag) => unimplemented!(),
925 _ => Err(unrecoverable(
926 "emit_arg_and_inout_setter: Unexpected inout expression type",
930 _ => Ok((emit_expr(e, env, arg)?, InstrSeq::Empty)),
933 let (instr_args, instr_setters): (Vec<InstrSeq>, Vec<InstrSeq>) = args
936 .map(|(i, arg)| emit_arg_and_inout_setter(e, env, i, arg))
937 .collect::<Result<Vec<_>>>()?
940 let instr_args = InstrSeq::gather(instr_args);
941 let instr_setters = InstrSeq::gather(instr_setters);
942 if has_inout_arg(args) {
943 let retval = e.local_gen_mut().get_unnamed();
946 InstrSeq::gather(vec![
947 InstrSeq::make_popl(retval.clone()),
949 InstrSeq::make_pushl(retval),
953 Ok((instr_args, InstrSeq::Empty))
959 uarg: Option<&tast::Expr>,
960 async_eager_label: Option<Label>,
961 lock_while_unwinding: bool,
963 let num_args = args.len();
964 let num_rets = 1 + args.iter().filter(|x| is_inout_arg(*x)).count();
965 let mut flags = FcallFlags::default();
966 flags.set(FcallFlags::HAS_UNPACK, uarg.is_some());
967 flags.set(FcallFlags::LOCK_WHILE_UNWINDING, lock_while_unwinding);
968 let inouts: Vec<bool> = args.iter().map(is_inout_arg).collect();
969 FcallArgs::new(flags, num_rets, inouts, async_eager_label, num_args)
972 fn is_inout_arg(e: &tast::Expr) -> bool {
973 e.1.as_callconv().map_or(false, |cc| cc.0.is_pinout())
976 fn has_inout_arg(es: &[tast::Expr]) -> bool {
977 es.iter().any(is_inout_arg)
980 fn emit_special_function(
987 uarg: Option<&tast::Expr>,
989 ) -> Result<Option<InstrSeq>> {
990 let nargs = args.len() + uarg.map_or(0, |_| 1);
991 let fq = function::Type::from_ast_name(id);
992 let lower_fq_name = fq.to_raw_string();
993 match (lower_fq_name, args) {
994 (id, _) if id == special_functions::ECHO => Ok(Some(InstrSeq::gather(
998 Ok(InstrSeq::gather(vec![
999 emit_expr(e, env, arg)?,
1001 InstrSeq::make_print(),
1005 InstrSeq::make_popc()
1009 .collect::<Result<_>>()?,
1011 _ => unimplemented!(),
1015 fn emit_eval(e: &mut Emitter, env: &Env, pos: &Pos, expr: &tast::Expr) -> Result {
1016 Ok(InstrSeq::gather(vec![
1017 emit_expr(e, env, expr)?,
1019 InstrSeq::make_eval(),
1027 async_eager_label: Option<Label>,
1028 (_, expr, targs, args, uarg): &(
1036 let jit_enable_rename_function = e
1040 .contains(HhvmFlags::JIT_ENABLE_RENAME_FUNCTION);
1041 use {tast::Expr as E, tast::Expr_ as E_};
1042 match (&expr.1, &args[..], uarg) {
1043 (E_::Id(id), [E(_, E_::String(data))], None) if id.1 == special_functions::HHAS_ADATA => {
1044 let v = TypedValue::HhasAdata(data.into());
1045 emit_pos_then(e, pos, InstrSeq::make_typedvalue(v))
1047 (E_::Id(id), _, None) if id.1 == pseudo_functions::ISSET => {
1048 emit_call_isset_exprs(e, env, pos, args)
1050 (E_::Id(id), args, None)
1052 && !jit_enable_rename_function
1053 && (args.len() == 2 || args.len() == 3) =>
1055 emit_idx(e, env, pos, args)
1057 (E_::Id(id), [arg1], None) if id.1 == emitter_special_functions::EVAL => {
1058 emit_eval(e, env, pos, arg1)
1060 (E_::Id(id), [arg1], None) if id.1 == emitter_special_functions::SET_FRAME_METADATA => {
1061 Ok(InstrSeq::gather(vec![
1062 emit_expr(e, env, arg1)?,
1064 InstrSeq::make_popl(local::Type::Named("$86metadata".into())),
1065 InstrSeq::make_null(),
1068 (E_::Id(id), [], None)
1069 if id.1 == pseudo_functions::EXIT || id.1 == pseudo_functions::DIE =>
1071 let exit = emit_exit(e, env, None)?;
1072 emit_pos_then(e, pos, exit)
1074 (E_::Id(id), [arg1], None)
1075 if id.1 == pseudo_functions::EXIT || id.1 == pseudo_functions::DIE =>
1077 let exit = emit_exit(e, env, Some(arg1))?;
1078 emit_pos_then(e, pos, exit)
1081 let instrs = emit_call(
1091 emit_pos_then(e, pos, instrs)
1099 (cid, targs, args, uarg, _): &(
1107 unimplemented!("TODO(hrust)")
1114 (expr, prop, nullflavor): &(tast::Expr, tast::Expr, ast_defs::OgNullFlavor),
1116 unimplemented!("TODO(hrust)")
1123 (base_expr, opt_elem_expr): &(tast::Expr, Option<tast::Expr>),
1125 unimplemented!("TODO(hrust)")
1131 (cid, cls_get_expr): &(tast::ClassId, tast::ClassGetExpr),
1133 unimplemented!("TODO(hrust)")
1136 fn emit_conditional_expr(
1139 (etest, etrue, efalse): &(tast::Expr, Option<tast::Expr>, tast::Expr),
1141 unimplemented!("TODO(hrust)")
1144 fn emit_local(env: &Env, notice: BareThisOp, lid: &aast_defs::Lid) -> Result {
1145 unimplemented!("TODO(hrust)")
1148 fn emit_class_const(env: &Env, pos: &Pos, (ci, id): &(tast::ClassId, ast_defs::Pstring)) -> Result {
1149 unimplemented!("TODO(hrust)")
1156 (uop, expr): &(ast_defs::Uop, tast::Expr),
1158 use ast_defs::Uop as U;
1160 U::Utild | U::Unot => Ok(InstrSeq::gather(vec![
1161 emit_expr(e, env, expr)?,
1162 emit_pos_then(e, pos, from_unop(e.options(), uop)?)?,
1164 U::Uplus | U::Uminus => Ok(InstrSeq::gather(vec![
1166 InstrSeq::make_int(0),
1167 emit_expr(e, env, expr)?,
1168 emit_pos_then(e, pos, from_unop(e.options(), uop)?)?,
1170 U::Uincr | U::Udecr | U::Upincr | U::Updecr => emit_lval_op(
1174 LValOp::IncDec(unop_to_incdec_op(e.options(), uop)?),
1179 U::Usilence => e.local_scope(|e| {
1180 let temp_local = e.local_gen_mut().get_unnamed();
1181 Ok(InstrSeq::gather(vec![
1183 InstrSeq::make_silence_start(temp_local.clone()),
1185 let try_instrs = emit_expr(e, env, expr)?;
1186 let catch_instrs = InstrSeq::gather(vec![
1188 InstrSeq::make_silence_end(temp_local.clone()),
1190 InstrSeq::create_try_catch(
1193 false, /* skip_throw */
1199 InstrSeq::make_silence_end(temp_local),
1205 fn unop_to_incdec_op(opts: &Options, op: &ast_defs::Uop) -> Result<IncdecOp> {
1206 let check_int_overflow = opts
1209 .contains(LangFlags::CHECK_INT_OVERFLOW);
1210 let if_check_or = |op1, op2| Ok(if check_int_overflow { op1 } else { op2 });
1211 use {ast_defs::Uop as U, IncdecOp as I};
1213 U::Uincr => if_check_or(I::PreIncO, I::PreInc),
1214 U::Udecr => if_check_or(I::PreDecO, I::PreDec),
1215 U::Upincr => if_check_or(I::PostIncO, I::PostInc),
1216 U::Updecr => if_check_or(I::PostDecO, I::PostDec),
1217 _ => Err(Unrecoverable("invalid incdec op".into())),
1221 fn from_unop(opts: &Options, op: &ast_defs::Uop) -> Result {
1222 let check_int_overflow = opts
1225 .contains(LangFlags::CHECK_INT_OVERFLOW);
1226 use ast_defs::Uop as U;
1228 U::Utild => InstrSeq::make_bitnot(),
1229 U::Unot => InstrSeq::make_not(),
1231 if check_int_overflow {
1232 InstrSeq::make_addo()
1234 InstrSeq::make_add()
1238 if check_int_overflow {
1239 InstrSeq::make_addo()
1241 InstrSeq::make_add()
1245 return Err(Unrecoverable(
1246 "this unary operation cannot be translated".into(),
1252 fn binop_to_eqop(opts: &Options, op: &ast_defs::Bop) -> Option<EqOp> {
1253 use {ast_defs::Bop as B, EqOp::*};
1254 let check_int_overflow = opts
1257 .contains(LangFlags::CHECK_INT_OVERFLOW);
1259 B::Plus => Some(if check_int_overflow {
1264 B::Minus => Some(if check_int_overflow {
1269 B::Star => Some(if check_int_overflow {
1274 B::Slash => Some(DivEqual),
1275 B::Starstar => Some(PowEqual),
1276 B::Amp => Some(AndEqual),
1277 B::Bar => Some(OrEqual),
1278 B::Xor => Some(XorEqual),
1279 B::Ltlt => Some(SlEqual),
1280 B::Gtgt => Some(SrEqual),
1281 B::Percent => Some(ModEqual),
1282 B::Dot => Some(ConcatEqual),
1287 fn optimize_null_checks(e: &Emitter) -> bool {
1289 .hack_compiler_flags
1290 .contains(CompilerFlags::OPTIMIZE_NULL_CHECKS)
1293 fn from_binop(opts: &Options, op: &ast_defs::Bop) -> Result {
1294 let check_int_overflow = opts
1297 .contains(LangFlags::CHECK_INT_OVERFLOW);
1298 use ast_defs::Bop as B;
1301 if check_int_overflow {
1302 InstrSeq::make_addo()
1304 InstrSeq::make_add()
1308 if check_int_overflow {
1309 InstrSeq::make_subo()
1311 InstrSeq::make_sub()
1315 if check_int_overflow {
1316 InstrSeq::make_mulo()
1318 InstrSeq::make_mul()
1321 B::Slash => InstrSeq::make_div(),
1322 B::Eqeq => InstrSeq::make_eq(),
1323 B::Eqeqeq => InstrSeq::make_same(),
1324 B::Starstar => InstrSeq::make_pow(),
1325 B::Diff => InstrSeq::make_neq(),
1326 B::Diff2 => InstrSeq::make_nsame(),
1327 B::Lt => InstrSeq::make_lt(),
1328 B::Lte => InstrSeq::make_lte(),
1329 B::Gt => InstrSeq::make_gt(),
1330 B::Gte => InstrSeq::make_gte(),
1331 B::Dot => InstrSeq::make_concat(),
1332 B::Amp => InstrSeq::make_bitand(),
1333 B::Bar => InstrSeq::make_bitor(),
1334 B::Ltlt => InstrSeq::make_shl(),
1335 B::Gtgt => InstrSeq::make_shr(),
1336 B::Cmp => InstrSeq::make_cmp(),
1337 B::Percent => InstrSeq::make_mod(),
1338 B::Xor => InstrSeq::make_bitxor(),
1339 B::LogXor => InstrSeq::make_xor(),
1340 B::Eq(_) => return Err(Unrecoverable("assignment is emitted differently".into())),
1341 B::QuestionQuestion => {
1342 return Err(Unrecoverable(
1343 "null coalescence is emitted differently".into(),
1346 B::Barbar | B::Ampamp => {
1347 return Err(Unrecoverable(
1348 "short-circuiting operator cannot be generated as a simple binop".into(),
1369 null_coalesce_assignment: bool,
1370 ) -> Result<(InstrSeq, Option<NumParams>)> {
1374 fn emit_null_coalesce_assignment(
1388 (op, e1, e2): &(ast_defs::Bop, tast::Expr, tast::Expr),
1390 use ast_defs::Bop as B;
1392 B::Ampamp | B::Barbar => unimplemented!("TODO(hrust)"),
1393 B::Eq(None) => emit_lval_op(e, env, pos, LValOp::Set, e1, Some(e2), false),
1394 B::Eq(Some(eop)) if eop.is_question_question() => {
1395 emit_null_coalesce_assignment(e, env, pos, e1, e2)
1397 B::Eq(Some(eop)) => match binop_to_eqop(e.options(), eop) {
1398 None => Err(Unrecoverable("illegal eq op".into())),
1399 Some(op) => emit_lval_op(e, env, pos, LValOp::SetOp(op), e1, Some(e2), false),
1401 B::QuestionQuestion => {
1402 let end_label = e.label_gen_mut().next_regular();
1403 Ok(InstrSeq::gather(vec![
1404 emit_quiet_expr(e, env, pos, e1, false)?.0,
1405 InstrSeq::make_dup(),
1406 InstrSeq::make_istypec(IstypeOp::OpNull),
1407 InstrSeq::make_not(),
1408 InstrSeq::make_jmpnz(end_label.clone()),
1409 InstrSeq::make_popc(),
1410 emit_expr(e, env, e2)?,
1411 InstrSeq::make_label(end_label),
1415 let default = |e: &mut Emitter| {
1416 Ok(InstrSeq::gather(vec![
1417 emit_two_exprs(e, env, pos, e1, e2)?,
1418 from_binop(e.options(), op)?,
1421 if optimize_null_checks(e) {
1423 B::Eqeqeq if e2.1.is_null() => emit_is_null(e, env, e1),
1424 B::Eqeqeq if e1.1.is_null() => emit_is_null(e, env, e2),
1425 B::Diff2 if e2.1.is_null() => Ok(InstrSeq::gather(vec![
1426 emit_is_null(e, env, e1)?,
1427 InstrSeq::make_not(),
1429 B::Diff2 if e1.1.is_null() => Ok(InstrSeq::gather(vec![
1430 emit_is_null(e, env, e2)?,
1431 InstrSeq::make_not(),
1442 fn emit_pipe(env: &Env, (_, e1, e2): &(aast_defs::Lid, tast::Expr, tast::Expr)) -> Result {
1443 unimplemented!("TODO(hrust)")
1446 fn emit_is_hint(env: &Env, pos: &Pos, h: &aast_defs::Hint) -> Result {
1447 unimplemented!("TODO(hrust)")
1453 (e, h, is_nullable): &(tast::Expr, aast_defs::Hint, bool),
1455 unimplemented!("TODO(hrust)")
1458 fn emit_cast(env: &Env, pos: &Pos, (h, e): &(aast_defs::Hint, tast::Expr)) -> Result {
1459 unimplemented!("TODO(hrust)")
1462 pub fn emit_unset_expr<Ex, Fb, En, Hi>(env: &Env, e: &aast::Expr<Ex, Fb, En, Hi>) -> Result {
1463 unimplemented!("TODO(hrust)")
1466 pub fn emit_set_range_expr(
1472 args: &[tast::Expr],
1473 last_arg: Option<&tast::Expr>,
1475 let raise_fatal = |msg: &str| {
1476 Err(emit_fatal::raise_fatal_parse(
1478 format!("{} {}", name, msg),
1482 // NOTE(hrust) last_arg is separated because the caller
1483 // would otherwise need to clone both Vec<&Expr> and Expr,
1484 // or it would need to pass chained FixedSizeIterators
1486 let (base, offset, src, n) = match last_arg {
1487 Some(a) if n >= 2 => (a, &args[n - 1], &args[n - 2], n - 2),
1488 None if n >= 3 => (&args[n - 1], &args[n - 2], &args[n - 3], n - 3),
1489 _ => return raise_fatal("expects at least 3 arguments"),
1491 let count_instrs = match args.get(n - 1) {
1492 Some(c) if kind.vec => emit_expr(e, env, c)?,
1493 None => InstrSeq::make_int(-1),
1495 return if !kind.vec {
1496 raise_fatal("expects no more than 3 arguments")
1498 raise_fatal("expects no more than 4 arguments")
1502 let (base_expr, cls_expr, base_setup, base_stack, cls_stack) = emit_base(
1507 null_coalesce_assignment: None,
1511 MemberOpMode::Define,
1514 Ok(InstrSeq::gather(vec![
1517 emit_expr(e, env, offset)?,
1518 emit_expr(e, env, src)?,
1521 InstrSeq::make_instr(Instruct::IFinal(InstructFinal::SetRangeM(
1522 (base_stack + cls_stack)
1524 .expect("StackIndex overflow"),
1526 kind.size.try_into().expect("Setrange size overflow"),
1531 pub fn is_reified_tparam(env: &Env, is_fun: bool, name: &str) -> Option<(usize, bool)> {
1532 let is = |tparams: &Vec<tast::Tparam>| {
1533 let is_soft = |ual: &Vec<tast::UserAttribute>| {
1534 ual.iter().any(|ua| &ua.name.1 == user_attributes::SOFT)
1536 use tast::ReifyKind::*;
1537 tparams.iter().enumerate().find_map(|(i, tp)| {
1538 if (tp.reified == Reified || tp.reified == SoftReified) && tp.name.1 == name {
1539 Some((i, is_soft(&tp.user_attributes)))
1546 env.scope.get_fun_tparams().and_then(is)
1548 is(&env.scope.get_class_params().list)
1552 /// Emit code for a base expression `expr` that forms part of
1553 /// an element access `expr[elem]` or field access `expr->fld`.
1554 /// The instructions are divided into three sections:
1555 /// 1. base and element/property expression instructions:
1556 /// push non-trivial base and key values on the stack
1557 /// 2. base selector instructions: a sequence of Base/Dim instructions that
1558 /// actually constructs the base address from "member keys" that are inlined
1559 /// in the instructions, or pulled from the key values that
1560 /// were pushed on the stack in section 1.
1561 /// 3. (constructed by the caller) a final accessor e.g. QueryM or setter
1562 /// e.g. SetOpM instruction that has the final key inlined in the
1563 /// instruction, or pulled from the key values that were pushed on the
1564 /// stack in section 1.
1565 /// The function returns a triple (base_instrs, base_setup_instrs, stack_size)
1566 /// where base_instrs is section 1 above, base_setup_instrs is section 2, and
1567 /// stack_size is the number of values pushed onto the stack by section 1.
1569 /// For example, the r-value expression $arr[3][$ix+2]
1571 /// # Section 1, pushing the value of $ix+2 on the stack
1575 /// # Section 2, constructing the base address of $arr[3]
1578 /// # Section 3, indexing the array using the value at stack position 0 (EC:0)
1579 /// QueryM 1 CGet EC:0
1587 ) -> Result<(InstrSeq, InstrSeq, InstrSeq, StackIndex, StackIndex)> {
1588 let _notice = BareThisOp::Notice;
1589 unimplemented!("TODO(hrust)")
1592 #[derive(Debug, Default)]
1593 struct EmitBaseArgs {
1595 null_coalesce_assignment: Option<bool>,
1596 base_offset: StackIndex,
1597 rhs_stack_size: StackIndex,
1600 pub fn emit_ignored_expr(emitter: &mut Emitter, env: &Env, pos: &Pos, expr: &tast::Expr) -> Result {
1601 if let Some(es) = expr.1.as_expr_list() {
1602 Ok(InstrSeq::gather(
1604 .map(|e| emit_ignored_expr(emitter, env, pos, e))
1605 .collect::<Result<Vec<_>>>()?,
1608 Ok(InstrSeq::gather(vec![
1609 emit_expr(emitter, env, expr)?,
1610 emit_pos_then(emitter, pos, InstrSeq::make_popc())?,
1615 pub fn emit_lval_op(
1621 expr2: Option<&tast::Expr>,
1622 null_coalesce_assignment: bool,
1624 match (op, &expr1.1, expr2) {
1625 (LValOp::Set, tast::Expr_::List(l), Some(expr2)) => {
1626 let instr_rhs = emit_expr(e, env, expr2)?;
1627 let has_elements = l.iter().any(|e| !e.1.is_omitted());
1631 scope::with_unnamed_local(e, |e, local| {
1632 let loc = if can_use_as_rhs_in_list_assignment(&expr2.1)? {
1637 let (instr_lhs, instr_assign) =
1638 emit_lval_op_list(e, env, pos, loc, &[], expr1, false)?;
1640 InstrSeq::gather(vec![
1643 InstrSeq::make_popl(local.clone()),
1646 InstrSeq::make_pushl(local),
1651 _ => e.local_scope(|e| {
1652 let (rhs_instrs, rhs_stack_size) = match expr2 {
1653 None => (InstrSeq::Empty, 0),
1654 Some(tast::Expr(_, tast::Expr_::Yield(af))) => {
1655 let temp = e.local_gen_mut().get_unnamed();
1657 InstrSeq::gather(vec![
1658 emit_yield(e, env, pos, af)?,
1659 InstrSeq::make_setl(temp.clone()),
1660 InstrSeq::make_popc(),
1661 InstrSeq::make_pushl(temp),
1666 Some(expr) => (emit_expr(e, env, expr)?, 1),
1668 emit_lval_op_nonlist(
1676 null_coalesce_assignment,
1682 fn can_use_as_rhs_in_list_assignment(expr: &tast::Expr_) -> Result<bool> {
1683 use aast::Expr_ as E_;
1688 .map_or(false, |id| id.1 == special_functions::ECHO) =>
1698 | E_::FunctionPointer(_)
1712 | E_::Await(_) => true,
1713 E_::Pipe(p) => can_use_as_rhs_in_list_assignment(&(p.2).1)?,
1714 E_::Binop(b) if b.0.is_eq() => can_use_as_rhs_in_list_assignment(&(b.2).1)?,
1715 E_::Binop(b) => b.0.is_plus() || b.0.is_question_question() || b.0.is_any_eq(),
1716 E_::PUIdentifier(_) => {
1717 return Err(Unrecoverable(
1718 "TODO(T35357243): Pocket Universes syntax must be erased by now".into(),
1725 pub fn emit_lval_op_list(
1729 local: Option<local::Type>,
1733 ) -> Result<(InstrSeq, InstrSeq)> {
1737 pub fn emit_lval_op_nonlist(
1743 rhs_instrs: InstrSeq,
1744 rhs_stack_size: usize,
1745 null_coalesce_assignment: bool,
1747 emit_lval_op_nonlist_steps(
1755 null_coalesce_assignment,
1757 .map(|(lhs, rhs, setop)| InstrSeq::gather(vec![lhs, rhs, setop]))
1760 pub fn emit_final_global_op(e: &mut Emitter, pos: &Pos, op: LValOp) -> Result {
1763 L::Set => emit_pos_then(e, pos, InstrSeq::make_setg()),
1764 L::SetOp(op) => Ok(InstrSeq::make_setopg(op)),
1765 L::IncDec(op) => Ok(InstrSeq::make_incdecg(op)),
1766 L::Unset => emit_pos_then(e, pos, InstrSeq::make_unsetg()),
1770 pub fn emit_final_local_op(e: &mut Emitter, pos: &Pos, op: LValOp, lid: local::Type) -> Result {
1776 L::Set => InstrSeq::make_setl(lid),
1777 L::SetOp(op) => InstrSeq::make_setopl(lid, op),
1778 L::IncDec(op) => InstrSeq::make_incdecl(lid, op),
1779 L::Unset => InstrSeq::make_unsetl(lid),
1784 pub fn emit_lval_op_nonlist_steps(
1790 rhs_instrs: InstrSeq,
1791 rhs_stack_size: usize,
1792 null_coalesce_assignment: bool,
1793 ) -> Result<(InstrSeq, InstrSeq, InstrSeq)> {
1794 let f = |env: &mut Env| {
1795 use tast::Expr_ as E_;
1798 E_::Lvar(v) if superglobals::is_any_global(local_id::get_name(&v.1)) => (
1802 InstrSeq::make_string(string_utils::lstrip(local_id::get_name(&v.1), "$")),
1805 emit_final_global_op(e, outer_pos, op)?,
1807 E_::Lvar(v) if is_local_this(env, &v.1) && op.is_incdec() => (
1808 emit_local(env, BareThisOp::Notice, v)?,
1812 E_::Lvar(v) if !is_local_this(env, &v.1) || op == LValOp::Unset => {
1813 (InstrSeq::Empty, rhs_instrs, {
1814 let lid = get_local(e, env, &v.0, &(v.1).1)?;
1815 emit_final_local_op(e, outer_pos, op, lid)?
1818 E_::ArrayGet(_) => unimplemented!(),
1819 E_::ObjGet(_) => unimplemented!(),
1820 E_::ClassGet(_) => unimplemented!(),
1824 InstrSeq::gather(vec![
1825 emit_lval_op_nonlist(
1835 from_unop(e.options(), &uop.0)?,
1839 return Err(emit_fatal::raise_fatal_parse(
1841 "Can't use return value in write context",
1846 // TODO(shiqicao): remove clone!
1847 let mut env = env.clone();
1849 LValOp::Set | LValOp::SetOp(_) | LValOp::IncDec(_) => env.with_allows_array_append(f),
1854 pub fn emit_reified_arg(
1855 _emitter: &mut Emitter,
1860 ) -> Result<(InstrSeq, bool)> {
1861 unimplemented!("TODO(hrust)")
1864 pub fn get_local(e: &mut Emitter, env: &Env, pos: &Pos, s: &str) -> Result<local::Type> {
1865 if s == special_idents::DOLLAR_DOLLAR {
1867 } else if special_idents::is_tmp_var(s) {
1868 Ok(e.local_gen().get_unnamed_for_tempname(s).clone())
1870 Ok(local::Type::Named(s.into()))
1874 pub fn emit_is_null(e: &mut Emitter, env: &Env, expr: &tast::Expr) -> Result {
1875 if let Some(tast::Lid(pos, id)) = expr.1.as_lvar() {
1876 if !is_local_this(env, id) {
1877 return Ok(InstrSeq::make_istypel(
1878 get_local(e, env, pos, local_id::get_name(id))?,
1884 Ok(InstrSeq::gather(vec![
1885 emit_expr(e, env, expr)?,
1886 InstrSeq::make_istypec(IstypeOp::OpNull),
1894 expr_: &tast::Expr_,
1896 ) -> Result<EmitJmpResult> {
1905 ) -> Result<EmitJmpResult> {
1906 let tast::Expr(pos, expr_) = expr;
1907 let opt = optimize_null_checks(e);
1909 match ast_constant_folder::expr_to_typed_value(e, &env.namespace, expr) {
1911 let b: bool = v.into();
1914 instrs: emit_pos_then(e, pos, InstrSeq::Empty)?,
1915 is_fallthrough: true,
1916 is_label_used: false,
1920 instrs: emit_pos_then(e, pos, InstrSeq::make_jmp(label.clone()))?,
1921 is_fallthrough: false,
1922 is_label_used: true,
1927 use {ast_defs::Uop as U, tast::Expr_ as E};
1929 E::Unop(uo) if uo.0 == U::Unot => {
1930 emit_jmpnz(e, env, &(uo.1).0, &(uo.1).1, label)?
1932 E::Binop(bo) if bo.0.is_barbar() => unimplemented!(),
1933 E::Binop(bo) if bo.0.is_ampamp() => unimplemented!(),
1936 && ((bo.1).1.is_null() || (bo.2).1.is_null())
1940 emit_is_null(e, env, if (bo.1).1.is_null() { &bo.2 } else { &bo.1 })?;
1942 instrs: emit_pos_then(
1945 InstrSeq::gather(vec![is_null, InstrSeq::make_jmpz(label.clone())]),
1947 is_fallthrough: true,
1948 is_label_used: true,
1952 if bo.0.is_diff2() && ((bo.1).1.is_null() || (bo.2).1.is_null()) && opt =>
1955 emit_is_null(e, env, if (bo.1).1.is_null() { &bo.2 } else { &bo.1 })?;
1957 instrs: emit_pos_then(
1960 InstrSeq::gather(vec![
1962 InstrSeq::make_jmpnz(label.clone()),
1965 is_fallthrough: true,
1966 is_label_used: true,
1970 let instr = emit_expr(e, env, expr)?;
1972 instrs: emit_pos_then(
1975 InstrSeq::gather(vec![instr, InstrSeq::make_jmpz(label.clone())]),
1977 is_fallthrough: true,
1978 is_label_used: true,