Tweak HhasBody C++: Replace (,) with Pair
[hiphop-php.git] / hphp / hack / src / hhbc / hhbc_by_ref / emit_body.rs
blobf073fa1642367637bf0f5612ce0cfab0b95a5222
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 mod emit_statement;
6 mod reified_generics_helpers;
7 mod try_finally_rewriter;
9 use emit_statement::emit_final_stmts;
10 use reified_generics_helpers as RGH;
12 use aast::TypeHint;
13 use aast_defs::{Hint, Hint_::*};
14 use decl_provider::DeclProvider;
15 use hash::HashSet;
16 use hhbc_by_ref_ast_body::AstBody;
17 use hhbc_by_ref_ast_class_expr::ClassExpr;
18 use hhbc_by_ref_ast_scope::{Scope, ScopeItem};
19 use hhbc_by_ref_decl_vars as decl_vars;
20 use hhbc_by_ref_emit_adata as emit_adata;
21 use hhbc_by_ref_emit_expression as emit_expression;
22 use hhbc_by_ref_emit_fatal::{emit_fatal_runtime, raise_fatal_parse};
23 use hhbc_by_ref_emit_param as emit_param;
24 use hhbc_by_ref_emit_pos::emit_pos;
25 use hhbc_by_ref_emit_type_hint as emit_type_hint;
26 use hhbc_by_ref_env::{emitter::Emitter, Env};
27 use hhbc_by_ref_generator as generator;
28 use hhbc_by_ref_hhas_body::{HhasBody, HhasBodyEnv};
29 use hhbc_by_ref_hhas_param::HhasParam;
30 use hhbc_by_ref_hhas_type::Info as HhasTypeInfo;
31 use hhbc_by_ref_hhbc_ast::{
32     ClassishKind, FcallArgs, FcallFlags, Instruct, IstypeOp, MemberKey, MemberOpMode, ParamId,
33     QueryOp, ReadOnlyOp,
35 use hhbc_by_ref_hhbc_id::function;
36 use hhbc_by_ref_hhbc_string_utils as string_utils;
37 use hhbc_by_ref_instruction_sequence::{instr, unrecoverable, Error, InstrSeq, Result};
38 use hhbc_by_ref_label::Label;
39 use hhbc_by_ref_label_rewriter as label_rewriter;
40 use hhbc_by_ref_local::Local;
41 use hhbc_by_ref_options::{CompilerFlags, LangFlags};
42 use hhbc_by_ref_runtime::TypedValue;
43 use hhbc_by_ref_statement_state::StatementState;
44 use hhbc_by_ref_unique_id_builder::*;
46 use naming_special_names_rust::user_attributes as ua;
48 use ocamlrep::rc::RcOc;
49 use oxidized::{
50     aast, aast_defs, ast as tast, ast_defs, doc_comment::DocComment, namespace_env, pos::Pos,
53 use ffi::{Maybe::*, Slice, Str};
55 use bitflags::bitflags;
56 use indexmap::IndexSet;
57 use itertools::Either;
59 static THIS: &'static str = "$this";
61 /// Optional arguments for emit_body; use Args::default() for defaults
62 pub struct Args<'a, 'arena> {
63     pub immediate_tparams: &'a Vec<tast::Tparam>,
64     pub class_tparam_names: &'a [&'a str],
65     pub ast_params: &'a Vec<tast::FunParam>,
66     pub ret: Option<&'a tast::Hint>,
67     pub pos: &'a Pos,
68     pub deprecation_info: &'a Option<&'a [TypedValue<'arena>]>,
69     pub doc_comment: Option<DocComment>,
70     pub default_dropthrough: Option<InstrSeq<'arena>>,
71     pub call_context: Option<String>,
72     pub flags: Flags,
75 bitflags! {
76     pub struct Flags: u8 {
77         const HAS_COEFFECTS_LOCAL = 1 << 0;
78         const SKIP_AWAITABLE = 1 << 1;
79         const MEMOIZE = 1 << 2;
80         const CLOSURE_BODY = 1 << 3;
81         const NATIVE = 1 << 4;
82         const ASYNC = 1 << 6;
83         const DEBUGGER_MODIFY_PROGRAM = 1 << 7;
84     }
87 pub fn emit_body_with_default_args<'b, 'arena, 'decl, D: DeclProvider<'decl>>(
88     alloc: &'arena bumpalo::Bump,
89     emitter: &mut Emitter<'arena, 'decl, D>,
90     namespace: RcOc<namespace_env::Env>,
91     body: &'b tast::Program,
92     return_value: InstrSeq<'arena>,
93 ) -> Result<HhasBody<'arena>> {
94     let args = Args {
95         immediate_tparams: &vec![],
96         class_tparam_names: &vec![],
97         ast_params: &vec![],
98         ret: None,
99         pos: &Pos::make_none(),
100         deprecation_info: &None,
101         doc_comment: None,
102         default_dropthrough: None,
103         call_context: None,
104         flags: Flags::empty(),
105     };
106     emit_body(
107         alloc,
108         emitter,
109         namespace,
110         Either::Left(body),
111         return_value,
112         Scope::toplevel(),
113         args,
114     )
115     .map(|r| r.0)
118 pub fn emit_body<'b, 'arena, 'decl, D: DeclProvider<'decl>>(
119     alloc: &'arena bumpalo::Bump,
120     emitter: &mut Emitter<'arena, 'decl, D>,
121     namespace: RcOc<namespace_env::Env>,
122     body: AstBody<'b>,
123     return_value: InstrSeq<'arena>,
124     scope: Scope<'_>,
125     args: Args<'_, 'arena>,
126 ) -> Result<(HhasBody<'arena>, bool, bool)> {
127     let tparams = scope
128         .get_tparams()
129         .into_iter()
130         .map(|tp| tp.clone())
131         .collect::<Vec<tast::Tparam>>();
132     let mut tp_names = get_tp_names(&tparams);
133     let (is_generator, is_pair_generator) = generator::is_function_generator(&body);
135     emitter.label_gen_mut().reset();
136     emitter.iterator_mut().reset();
138     let return_type_info = make_return_type_info(
139         alloc,
140         args.flags.contains(Flags::SKIP_AWAITABLE),
141         args.flags.contains(Flags::NATIVE),
142         args.ret,
143         &tp_names,
144     )?;
146     let params = make_params(
147         alloc,
148         emitter,
149         &mut tp_names,
150         args.ast_params,
151         &scope,
152         args.flags,
153     )?;
155     let upper_bounds = emit_generics_upper_bounds(
156         alloc,
157         args.immediate_tparams,
158         args.class_tparam_names,
159         args.flags.contains(Flags::SKIP_AWAITABLE),
160     );
161     let shadowed_tparams = emit_shadowed_tparams(args.immediate_tparams, args.class_tparam_names);
162     let decl_vars = make_decl_vars(
163         emitter,
164         &scope,
165         args.immediate_tparams,
166         &params,
167         &body,
168         args.flags,
169     )?;
170     let mut env = make_env(alloc, namespace, scope, args.call_context);
172     set_emit_statement_state(
173         alloc,
174         emitter,
175         return_value,
176         &params,
177         &return_type_info,
178         args.ret,
179         args.pos,
180         args.default_dropthrough,
181         args.flags,
182         is_generator,
183     );
184     env.jump_targets_gen.reset();
186     let should_reserve_locals = set_function_jmp_targets(emitter, &mut env);
187     let num_closures = match emitter
188         .emit_global_state()
189         .num_closures
190         .get(&get_unique_id_for_scope(&env.scope))
191     {
192         Some(num) => *num,
193         None => 0,
194     };
195     let local_gen = emitter.local_gen_mut();
196     local_gen.reset(params.len() + decl_vars.len());
197     if should_reserve_locals {
198         local_gen.reserve_retval_and_label_id_locals();
199     };
200     let body_instrs = make_body_instrs(
201         emitter,
202         &mut env,
203         &params,
204         &tparams,
205         &decl_vars,
206         body,
207         is_generator,
208         args.deprecation_info.clone(),
209         &args.pos,
210         &args.ast_params,
211         args.flags,
212     )?;
213     Ok((
214         make_body(
215             alloc,
216             emitter,
217             body_instrs,
218             decl_vars,
219             false, // is_memoize_wrapper
220             false, // is_memoize_wrapper_lsb
221             num_closures,
222             upper_bounds,
223             shadowed_tparams,
224             params,
225             Some(return_type_info),
226             args.doc_comment.to_owned(),
227             Some(&env),
228         )?,
229         is_generator,
230         is_pair_generator,
231     ))
234 fn make_body_instrs<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
235     emitter: &mut Emitter<'arena, 'decl, D>,
236     env: &mut Env<'a, 'arena>,
237     params: &[HhasParam<'arena>],
238     tparams: &[tast::Tparam],
239     decl_vars: &[String],
240     body: AstBody,
241     is_generator: bool,
242     deprecation_info: Option<&[TypedValue<'arena>]>,
243     pos: &Pos,
244     ast_params: &[tast::FunParam],
245     flags: Flags,
246 ) -> Result<InstrSeq<'arena>> {
247     let alloc = env.arena;
248     let stmt_instrs = if flags.contains(Flags::NATIVE) {
249         instr::nativeimpl(alloc)
250     } else {
251         env.do_function(emitter, &body, emit_ast_body)?
252     };
254     let (begin_label, default_value_setters) =
255         emit_param::emit_param_default_value_setter(emitter, env, pos, params)?;
257     let header_content = make_header_content(
258         emitter,
259         env,
260         params,
261         tparams,
262         decl_vars,
263         is_generator,
264         deprecation_info,
265         pos,
266         ast_params,
267         flags,
268     )?;
269     let first_instr_is_label = match InstrSeq::first(&stmt_instrs) {
270         Some(Instruct::ILabel(_)) => true,
271         _ => false,
272     };
273     let header = if first_instr_is_label && InstrSeq::is_empty(&header_content) {
274         InstrSeq::gather(alloc, vec![begin_label, instr::entrynop(alloc)])
275     } else {
276         InstrSeq::gather(alloc, vec![begin_label, header_content])
277     };
279     let mut body_instrs = InstrSeq::gather(alloc, vec![header, stmt_instrs, default_value_setters]);
280     if flags.contains(Flags::DEBUGGER_MODIFY_PROGRAM) {
281         modify_prog_for_debugger_eval(&mut body_instrs);
282     };
283     Ok(body_instrs)
286 fn make_header_content<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
287     emitter: &mut Emitter<'arena, 'decl, D>,
288     env: &mut Env<'a, 'arena>,
289     params: &[HhasParam<'arena>],
290     tparams: &[tast::Tparam],
291     _decl_vars: &[String],
292     is_generator: bool,
293     deprecation_info: Option<&[TypedValue<'arena>]>,
294     pos: &Pos,
295     ast_params: &[tast::FunParam],
296     flags: Flags,
297 ) -> Result<InstrSeq<'arena>> {
298     let alloc = env.arena;
299     let method_prolog = if flags.contains(Flags::NATIVE) {
300         instr::empty(alloc)
301     } else {
302         emit_method_prolog(emitter, env, pos, params, ast_params, tparams)?
303     };
305     let deprecation_warning =
306         emit_deprecation_info(alloc, &env.scope, deprecation_info, emitter.systemlib())?;
308     let generator_info = if is_generator {
309         InstrSeq::gather(alloc, vec![instr::createcont(alloc), instr::popc(alloc)])
310     } else {
311         instr::empty(alloc)
312     };
314     Ok(InstrSeq::gather(
315         alloc,
316         vec![method_prolog, deprecation_warning, generator_info],
317     ))
320 fn make_decl_vars<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
321     emitter: &mut Emitter<'arena, 'decl, D>,
322     scope: &Scope<'a>,
323     immediate_tparams: &[tast::Tparam],
324     params: &[HhasParam<'arena>],
325     body: &AstBody,
326     arg_flags: Flags,
327 ) -> Result<Vec<String>> {
328     let explicit_use_set = &emitter.emit_global_state().explicit_use_set;
330     let mut decl_vars =
331         decl_vars::from_ast(params, body, explicit_use_set).map_err(unrecoverable)?;
333     let mut decl_vars = if arg_flags.contains(Flags::CLOSURE_BODY) {
334         let mut captured_vars = scope.get_captured_vars();
335         move_this(&mut decl_vars);
336         decl_vars.retain(|v| !captured_vars.contains(v));
337         captured_vars.extend_from_slice(&decl_vars.as_slice());
338         captured_vars
339     } else {
340         match &scope.items[..] {
341             [] | [.., ScopeItem::Class(_), _] => move_this(&mut decl_vars),
342             _ => {}
343         };
344         decl_vars
345     };
347     if arg_flags.contains(Flags::HAS_COEFFECTS_LOCAL) {
348         decl_vars.insert(0, string_utils::coeffects::LOCAL_NAME.into());
349     }
351     if !arg_flags.contains(Flags::CLOSURE_BODY)
352         && immediate_tparams
353             .iter()
354             .any(|t| t.reified != tast::ReifyKind::Erased)
355     {
356         decl_vars.insert(0, string_utils::reified::GENERICS_LOCAL_NAME.into());
357     }
358     Ok(decl_vars)
361 pub fn emit_return_type_info<'arena>(
362     alloc: &'arena bumpalo::Bump,
363     tp_names: &[&str],
364     skip_awaitable: bool,
365     ret: Option<&aast::Hint>,
366 ) -> Result<HhasTypeInfo<'arena>> {
367     match ret {
368         None => Ok(HhasTypeInfo::make(
369             Just(Str::new_str(alloc, "")),
370             hhbc_by_ref_hhas_type::constraint::Constraint::default(),
371         )),
372         Some(hint) => emit_type_hint::hint_to_type_info(
373             alloc,
374             &emit_type_hint::Kind::Return,
375             skip_awaitable,
376             false, // nullable
377             tp_names,
378             &hint,
379         ),
380     }
383 fn make_return_type_info<'arena>(
384     alloc: &'arena bumpalo::Bump,
385     skip_awaitable: bool,
386     is_native: bool,
387     ret: Option<&aast::Hint>,
388     tp_names: &[&str],
389 ) -> Result<HhasTypeInfo<'arena>> {
390     let return_type_info = emit_return_type_info(alloc, tp_names, skip_awaitable, ret);
391     if is_native {
392         return return_type_info.map(|rti| {
393             emit_type_hint::emit_type_constraint_for_native_function(alloc, tp_names, ret, rti)
394         });
395     };
396     return_type_info
399 pub fn make_env<'a, 'arena>(
400     alloc: &'arena bumpalo::Bump,
401     namespace: RcOc<namespace_env::Env>,
402     scope: Scope<'a>,
403     call_context: Option<String>,
404 ) -> Env<'a, 'arena> {
405     let mut env = Env::default(alloc, namespace);
406     env.call_context = call_context;
407     env.scope = scope;
408     env
411 fn make_params<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
412     alloc: &'arena bumpalo::Bump,
413     emitter: &mut Emitter<'arena, 'decl, D>,
414     tp_names: &mut Vec<&str>,
415     ast_params: &[tast::FunParam],
416     scope: &Scope<'a>,
417     flags: Flags,
418 ) -> Result<Vec<HhasParam<'arena>>> {
419     let generate_defaults = !flags.contains(Flags::MEMOIZE);
420     emit_param::from_asts(
421         alloc,
422         emitter,
423         tp_names,
424         generate_defaults,
425         scope,
426         ast_params,
427     )
430 pub fn make_body<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
431     alloc: &'arena bumpalo::Bump,
432     emitter: &mut Emitter<'arena, 'decl, D>,
433     mut body_instrs: InstrSeq<'arena>,
434     decl_vars: Vec<String>,
435     is_memoize_wrapper: bool,
436     is_memoize_wrapper_lsb: bool,
437     num_closures: u32,
438     upper_bounds: Vec<(String, Vec<HhasTypeInfo<'arena>>)>,
439     shadowed_tparams: Vec<String>,
440     mut params: Vec<HhasParam<'arena>>,
441     return_type_info: Option<HhasTypeInfo<'arena>>,
442     doc_comment: Option<DocComment>,
443     opt_env: Option<&Env<'a, 'arena>>,
444 ) -> Result<HhasBody<'arena>> {
445     emit_adata::rewrite_typed_values(alloc, emitter, &mut body_instrs)?;
446     if emitter
447         .options()
448         .hack_compiler_flags
449         .contains(CompilerFlags::RELABEL)
450     {
451         label_rewriter::relabel_function(alloc, &mut params, &mut body_instrs);
452     }
453     let num_iters = if is_memoize_wrapper {
454         0
455     } else {
456         emitter.iterator().count()
457     };
458     let body_env = if let Some(env) = opt_env {
459         let is_namespaced = env.namespace.name.is_none();
460         if let Some(cd) = env.scope.get_class() {
461             Some(HhasBodyEnv {
462                 is_namespaced,
463                 class_info: Just((cd.get_kind().into(), Str::new_str(alloc, cd.get_name_str()))),
464                 parent_name: ClassExpr::get_parent_class_name(cd)
465                     .map(|s| Str::new_str(alloc, s))
466                     .into(),
467             })
468         } else {
469             Some(HhasBodyEnv {
470                 is_namespaced,
471                 class_info: Nothing,
472                 parent_name: Nothing,
473             })
474         }
475     } else {
476         None
477     };
478     Ok(HhasBody {
479         body_instrs,
480         decl_vars: Slice::fill_iter(alloc, decl_vars.into_iter().map(|s| Str::new_str(alloc, s))),
481         num_iters,
482         is_memoize_wrapper,
483         is_memoize_wrapper_lsb,
484         num_closures,
485         upper_bounds: Slice::fill_iter(
486             alloc,
487             upper_bounds.into_iter().map(|(s, is)| {
488                 (
489                     Str::new_str(alloc, s),
490                     Slice::fill_iter(alloc, is.into_iter()),
491                 )
492                     .into()
493             }),
494         ),
495         shadowed_tparams: Slice::fill_iter(
496             alloc,
497             shadowed_tparams.into_iter().map(|s| Str::new_str(alloc, s)),
498         ),
499         params: Slice::fill_iter(alloc, params.into_iter()),
500         return_type_info,
501         doc_comment: doc_comment.map(|c| Str::new_str(alloc, &(c.0).1)),
502         env: body_env,
503     })
506 fn emit_ast_body<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
507     env: &mut Env<'a, 'arena>,
508     e: &mut Emitter<'arena, 'decl, D>,
509     body: &AstBody,
510 ) -> Result<InstrSeq<'arena>> {
511     match body {
512         Either::Left(p) => emit_defs(env, e, p),
513         Either::Right(b) => emit_final_stmts(e, env, b),
514     }
517 fn emit_defs<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
518     env: &mut Env<'a, 'arena>,
519     emitter: &mut Emitter<'arena, 'decl, D>,
520     prog: &[tast::Def],
521 ) -> Result<InstrSeq<'arena>> {
522     use tast::Def;
523     fn emit_def<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
524         env: &mut Env<'a, 'arena>,
525         emitter: &mut Emitter<'arena, 'decl, D>,
526         def: &tast::Def,
527     ) -> Result<InstrSeq<'arena>> {
528         let alloc = env.arena;
529         match def {
530             Def::Stmt(s) => emit_statement::emit_stmt(emitter, env, s),
531             Def::Namespace(ns) => emit_defs(env, emitter, &ns.1),
532             _ => Ok(instr::empty(alloc)),
533         }
534     }
535     fn aux<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
536         env: &mut Env<'a, 'arena>,
537         emitter: &mut Emitter<'arena, 'decl, D>,
538         defs: &[tast::Def],
539     ) -> Result<InstrSeq<'arena>> {
540         let alloc = env.arena;
541         match defs {
542             [Def::SetNamespaceEnv(ns), ..] => {
543                 env.namespace = RcOc::clone(&ns);
544                 aux(env, emitter, &defs[1..])
545             }
546             [] => emit_statement::emit_dropthrough_return(emitter, env),
547             [Def::Stmt(s)] => {
548                 // emit last statement in the list as final statement
549                 emit_statement::emit_final_stmt(emitter, env, &s)
550             }
551             [Def::Stmt(s1), Def::Stmt(s2)] => match s2.1.as_markup() {
552                 Some(id) if id.1.is_empty() => emit_statement::emit_final_stmt(emitter, env, &s1),
553                 _ => Ok(InstrSeq::gather(
554                     alloc,
555                     vec![
556                         emit_statement::emit_stmt(emitter, env, s1)?,
557                         emit_statement::emit_final_stmt(emitter, env, &s2)?,
558                     ],
559                 )),
560             },
561             [def, ..] => Ok(InstrSeq::gather(
562                 alloc,
563                 vec![emit_def(env, emitter, def)?, aux(env, emitter, &defs[1..])?],
564             )),
565         }
566     }
567     let alloc = env.arena;
568     match prog {
569         [Def::Stmt(s), ..] if s.1.is_markup() => Ok(InstrSeq::gather(
570             alloc,
571             vec![
572                 emit_statement::emit_markup(emitter, env, s.1.as_markup().unwrap(), true)?,
573                 aux(env, emitter, &prog[1..])?,
574             ],
575         )),
576         [] | [_] => aux(env, emitter, &prog[..]),
577         [def, ..] => {
578             let i1 = emit_def(env, emitter, def)?;
579             if i1.is_empty() {
580                 emit_defs(env, emitter, &prog[1..])
581             } else {
582                 Ok(InstrSeq::gather(
583                     alloc,
584                     vec![i1, aux(env, emitter, &prog[1..])?],
585                 ))
586             }
587         }
588     }
591 pub fn has_type_constraint<'a, 'arena>(
592     env: &Env<'a, 'arena>,
593     ti: Option<&HhasTypeInfo>,
594     ast_param: &tast::FunParam,
595 ) -> (RGH::ReificationLevel, Option<tast::Hint>) {
596     use RGH::ReificationLevel as L;
597     match (ti, &ast_param.type_hint.1) {
598         (Some(ti), Some(h)) if ti.has_type_constraint() => {
599             // TODO(hrust): how to avoid clone on h
600             let h = RGH::remove_erased_generics(env, h.clone());
601             (RGH::has_reified_type_constraint(env, &h), Some(h))
602         }
603         _ => (L::Unconstrained, None),
604     }
607 ////////////////////////////////////////////////////////////////////////////////
608 // atom_helpers
610 mod atom_helpers {
611     use crate::*;
612     use aast_defs::ReifyKind::Erased;
613     use ast_defs::Id;
614     use hhbc_by_ref_instruction_sequence::{instr, unrecoverable, InstrSeq, Result};
615     use hhbc_by_ref_local::Local::Named;
616     use tast::Tparam;
618     fn strip_ns(name: &str) -> &str {
619         match name.chars().next() {
620             Some('\\') => &name[1..],
621             _ => name,
622         }
623     }
625     pub fn is_generic(name: &str, tparams: &[Tparam]) -> bool {
626         tparams.iter().any(|t| match t.name {
627             Id(_, ref s) => s == self::strip_ns(name),
628         })
629     }
631     pub fn is_erased_generic(name: &str, tparams: &[Tparam]) -> bool {
632         tparams.iter().any(|t| match t.name {
633             Id(_, ref s) if s == self::strip_ns(name) => t.reified == Erased,
634             _ => false,
635         })
636     }
638     pub fn index_of_generic(tparams: &[Tparam], name: &str) -> Result<usize> {
639         match tparams.iter().enumerate().find_map(|(i, t)| match t.name {
640             Id(_, ref s) if s == self::strip_ns(name) => Some(i),
641             _ => None,
642         }) {
643             Some(u) => Ok(u),
644             None => Err(unrecoverable("Expected generic")),
645         }
646     }
648     pub fn emit_clscnsl<'arena>(
649         alloc: &'arena bumpalo::Bump,
650         param: &HhasParam<'arena>,
651         pos: &Pos,
652         cls_instrs: InstrSeq<'arena>,
653         msg: &str,
654         label_not_a_class: Label,
655         label_done: Label,
656     ) -> Result<InstrSeq<'arena>> {
657         let param_name = &param.name;
658         let loc = Named(Str::new_str(alloc, param_name));
659         Ok(InstrSeq::gather(
660             alloc,
661             vec![
662                 cls_instrs,
663                 instr::classgetc(alloc),
664                 instr::clscnsl(alloc, loc.clone()),
665                 instr::popl(alloc, loc),
666                 instr::jmp(alloc, label_done.clone()),
667                 instr::label(alloc, label_not_a_class),
668                 emit_fatal_runtime(alloc, &pos, msg),
669                 instr::label(alloc, label_done),
670             ],
671         ))
672     }
673 } //mod atom_helpers
675 ////////////////////////////////////////////////////////////////////////////////
676 // atom_instrs
678 fn atom_instrs<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
679     emitter: &mut Emitter<'arena, 'decl, D>,
680     env: &mut Env<'a, 'arena>,
681     param: &HhasParam<'arena>,
682     ast_param: &tast::FunParam,
683     tparams: &[tast::Tparam],
684 ) -> Result<Option<InstrSeq<'arena>>> {
685     let alloc = env.arena;
686     if !param
687         .user_attributes
688         .as_ref()
689         .iter()
690         .any(|a| a.is(|x| x == ua::VIA_LABEL))
691     {
692         return Ok(None); // Not an atom. Nothing to do.
693     }
694     match &ast_param.type_hint {
695         TypeHint(_, None) => Err(raise_fatal_parse(
696             &ast_param.pos,
697             ua::VIA_LABEL.to_owned() + " param type hint unavailable",
698         )),
699         TypeHint(_, Some(Hint(_, h))) => {
700             let label_done = emitter.label_gen_mut().next_regular();
701             let label_not_a_class = emitter.label_gen_mut().next_regular();
702             match &**h {
703                 Happly(ast_defs::Id(_, ref ctor), vec) if ctor == "\\HH\\MemberOf" => {
704                     match &vec[..] {
705                         [hint, _] => {
706                             let Hint(_, e) = hint;
707                             match &**e {
708                                 // Immediate type.
709                                 Happly(ast_defs::Id(pos, ref tag), _) => {
710                                     if atom_helpers::is_erased_generic(tag, tparams) {
711                                         Err(raise_fatal_parse(
712                                             &pos,
713                                             "Erased generic as HH\\MemberOf enum type",
714                                         ))
715                                     } else {
716                                         if !atom_helpers::is_generic(tag, tparams) {
717                                             //'tag' is just a name.
718                                             Ok(Some(atom_helpers::emit_clscnsl(
719                                                 alloc,
720                                                 param,
721                                                 &pos,
722                                                 InstrSeq::gather(
723                                                     alloc,
724                                                     vec![
725                                                         emit_expression::emit_expr(
726                                                             emitter,
727                                                             env,
728                                                             &(tast::Expr(
729                                                                 (),
730                                                                 Pos::make_none(),
731                                                                 aast::Expr_::String(
732                                                                     bstr::BString::from(
733                                                                         tag.to_owned(),
734                                                                     ),
735                                                                 ),
736                                                             )),
737                                                         )?,
738                                                         emit_expression::emit_expr(
739                                                             emitter,
740                                                             env,
741                                                             &(tast::Expr(
742                                                                 (),
743                                                                 Pos::make_none(),
744                                                                 aast::Expr_::True,
745                                                             )),
746                                                         )?,
747                                                         instr::oodeclexists(
748                                                             alloc,
749                                                             ClassishKind::Class,
750                                                         ),
751                                                         instr::jmpz(
752                                                             alloc,
753                                                             label_not_a_class.clone(),
754                                                         ),
755                                                         emit_expression::emit_expr(
756                                                             emitter,
757                                                             env,
758                                                             &(tast::Expr(
759                                                                 (),
760                                                                 Pos::make_none(),
761                                                                 aast::Expr_::String(
762                                                                     bstr::BString::from(
763                                                                         tag.to_owned(),
764                                                                     ),
765                                                                 ),
766                                                             )),
767                                                         )?,
768                                                     ],
769                                                 ),
770                                                 "Type is not a class",
771                                                 label_not_a_class,
772                                                 label_done,
773                                             )?))
774                                         } else {
775                                             //'tag' is a reified generic.
776                                             Ok(Some(atom_helpers::emit_clscnsl(
777                                                 alloc,
778                                                 param,
779                                                 &pos,
780                                                 InstrSeq::gather(
781                                                     alloc,
782                                                     vec![
783                                                     emit_expression::emit_reified_generic_instrs(
784                                                         alloc,
785                                                         &Pos::make_none(),
786                                                         true,
787                                                         atom_helpers::index_of_generic(
788                                                             tparams, tag,
789                                                         )?,
790                                                     )?,
791                                                     instr::basec(alloc, 0, MemberOpMode::ModeNone),
792                                                     instr::querym(
793                                                         alloc,
794                                                         1,
795                                                         QueryOp::CGetQuiet,
796                                                         MemberKey::ET(
797                                                             "classname".into(),
798                                                             ReadOnlyOp::Any,
799                                                         ),
800                                                     ),
801                                                     instr::dup(alloc),
802                                                     instr::istypec(alloc, IstypeOp::OpNull),
803                                                     instr::jmpnz(alloc, label_not_a_class.clone()),
804                                                 ],
805                                                 ),
806                                                 "Generic type parameter does not resolve to a class",
807                                                 label_not_a_class,
808                                                 label_done,
809                                             )?))
810                                         }
811                                     }
812                                 }
813                                 // Type constant.
814                                 Haccess(Hint(_, h), _) => {
815                                     match &**h {
816                                         Happly(ast_defs::Id(pos, ref tag), _) => {
817                                             if atom_helpers::is_erased_generic(tag, tparams) {
818                                                 Err(raise_fatal_parse(
819                                                     &pos,
820                                                     "Erased generic as HH\\MemberOf enum type",
821                                                 ))
822                                             } else {
823                                                 //'tag' is a type constant.
824                                                 Ok(Some(atom_helpers::emit_clscnsl(
825                                                     alloc,
826                                                     param,
827                                                     &pos,
828                                                     InstrSeq::gather(alloc, vec![
829                                                         emit_expression::get_type_structure_for_hint(
830                                                             alloc,
831                                                             emitter,
832                                                             tparams
833                                                                 .iter()
834                                                                 .map(|fp| fp.name.1.as_str())
835                                                                 .collect::<Vec<_>>()
836                                                                 .as_slice(),
837                                                             &IndexSet::new(),
838                                                             hint,
839                                                         )?,
840                                                         instr::combine_and_resolve_type_struct(alloc, 1),
841                                                         instr::basec(alloc, 0, MemberOpMode::ModeNone),
842                                                         instr::querym(alloc, 1, QueryOp::CGetQuiet, MemberKey::ET(Str::from("classname"), ReadOnlyOp::Any)),
843                                                         instr::dup(alloc),
844                                                         instr::istypec(alloc, IstypeOp::OpNull),
845                                                         instr::jmpnz(alloc, label_not_a_class.clone()),
846                                                     ]),
847                                                     "Type constant does not resolve to a class",
848                                                     label_not_a_class,
849                                                     label_done,
850                                             )?))
851                                             }
852                                         }
853                                         _ => Err(unrecoverable(
854                                             "Unexpected case for HH\\MemberOf enum type",
855                                         )),
856                                     }
857                                 }
858                                 _ => {
859                                     Err(unrecoverable("Unexpected case for HH\\MemberOf enum type"))
860                                 }
861                             }
862                         }
863                         _ => Err(unrecoverable(
864                             "Wrong number of type arguments to HH\\MemberOf",
865                         )),
866                     }
867                 }
868                 _ => Err(raise_fatal_parse(
869                     &ast_param.pos,
870                     "'".to_owned() + ua::VIA_LABEL + "' applied to a non-HH\\MemberOf parameter",
871                 )),
872             }
873         }
874     }
877 pub fn emit_method_prolog<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
878     emitter: &mut Emitter<'arena, 'decl, D>,
879     env: &mut Env<'a, 'arena>,
880     pos: &Pos,
881     params: &[HhasParam<'arena>],
882     ast_params: &[tast::FunParam],
883     tparams: &[tast::Tparam],
884 ) -> Result<InstrSeq<'arena>> {
885     let alloc = env.arena;
886     let mut make_param_instr =
887         |(param, ast_param): (&HhasParam<'arena>, &tast::FunParam)| -> Result<Option<InstrSeq<'arena>>> {
888             let param_name = &param.name;
889             let param_name = || ParamId::ParamNamed(Str::new_str(alloc, param_name));
890             if param.is_variadic {
891                 Ok(None)
892             } else {
893                 use RGH::ReificationLevel as L;
894                 let param_checks =
895                     match has_type_constraint(env, Option::from(param.type_info.as_ref()), ast_param) {
896                         (L::Unconstrained, _) => Ok(None),
897                         (L::Not, _) => Ok(Some(instr::verify_param_type(alloc, param_name()))),
898                         (L::Maybe, Some(h)) => Ok(Some(InstrSeq::gather(alloc, vec![
899                             emit_expression::get_type_structure_for_hint(
900                                 alloc,
901                                 emitter,
902                                 tparams
903                                     .iter()
904                                     .map(|fp| fp.name.1.as_str())
905                                     .collect::<Vec<_>>()
906                                     .as_slice(),
907                                 &IndexSet::new(),
908                                 &h,
909                             )?,
910                             instr::verify_param_type_ts(alloc, param_name()),
911                         ]))),
912                         (L::Definitely, Some(h)) => {
913                             let check = instr::istypel(
914                                 alloc,
915                                 Local::Named(Str::new_str(alloc, param.name.as_str())),
916                                 IstypeOp::OpNull,
917                             );
918                             let verify_instr = instr::verify_param_type_ts(alloc, param_name());
919                             RGH::simplify_verify_type(emitter, env, pos, check, &h, verify_instr)
920                                 .map(Some)
921                         }
922                         _ => Err(unrecoverable("impossible")),
923                     }?;
925                 let atom_instrs = if emitter
926                     .options()
927                     .hhvm
928                     .hack_lang
929                     .flags
930                     .contains(LangFlags::ENABLE_ENUM_CLASSES)
931                 {
932                     atom_instrs(emitter, env, param, ast_param, tparams)?
933                 } else {
934                     None
935                 };
937                 match (param_checks, atom_instrs) {
938                     (None, None) => Ok(None),
939                     (Some(is), None) => Ok(Some(is)),
940                     (Some(is), Some(js)) => Ok(Some(InstrSeq::gather(alloc, vec![is, js]))),
941                     (None, Some(js)) => Ok(Some(js)),
942                 }
943             }
944         };
946     let ast_params = ast_params
947         .iter()
948         .filter(|p| !(p.is_variadic && p.name == "..."))
949         .collect::<Vec<_>>();
950     if params.len() != ast_params.len() {
951         return Err(Error::Unrecoverable("length mismatch".into()));
952     }
954     let param_instrs = params
955         .iter()
956         .zip(ast_params.into_iter())
957         .filter_map(|p| make_param_instr(p).transpose())
958         .collect::<Result<Vec<_>>>()?;
959     let mut instrs = vec![emit_pos(alloc, pos)];
960     for i in param_instrs.iter() {
961         instrs.push(InstrSeq::clone(alloc, i));
962     }
963     Ok(InstrSeq::gather(alloc, instrs))
966 pub fn emit_deprecation_info<'a, 'arena>(
967     alloc: &'arena bumpalo::Bump,
968     scope: &Scope<'a>,
969     deprecation_info: Option<&[TypedValue<'arena>]>,
970     is_systemlib: bool,
971 ) -> Result<InstrSeq<'arena>> {
972     Ok(match deprecation_info {
973         None => instr::empty(alloc),
974         Some(args) => {
975             fn strip_id<'a>(id: &'a tast::Id) -> &'a str {
976                 string_utils::strip_global_ns(id.1.as_str())
977             }
978             let (class_name, trait_instrs, concat_instruction): (String, _, _) =
979                 match scope.get_class() {
980                     None => ("".into(), instr::empty(alloc), instr::empty(alloc)),
981                     Some(c) if c.get_kind() == tast::ClassishKind::Ctrait => (
982                         "::".into(),
983                         InstrSeq::gather(alloc, vec![instr::self_(alloc), instr::classname(alloc)]),
984                         instr::concat(alloc),
985                     ),
986                     Some(c) => (
987                         strip_id(c.get_name()).to_string() + "::",
988                         instr::empty(alloc),
989                         instr::empty(alloc),
990                     ),
991                 };
993             let fn_name = match scope.items.last() {
994                 Some(ScopeItem::Function(f)) => strip_id(f.get_name()),
995                 Some(ScopeItem::Method(m)) => strip_id(m.get_name()),
996                 _ => {
997                     return Err(Error::Unrecoverable(
998                         "deprecated functions must have names".into(),
999                     ));
1000                 }
1001             };
1002             let deprecation_string = class_name
1003                 + fn_name
1004                 + ": "
1005                 + (if args.is_empty() {
1006                     "deprecated function"
1007                 } else if let TypedValue::String(s) = &args[0] {
1008                     s.as_str()
1009                 } else {
1010                     return Err(Error::Unrecoverable(
1011                         "deprecated attribute first argument is not a string".into(),
1012                     ));
1013                 });
1014             let sampling_rate = if args.len() <= 1 {
1015                 1i64
1016             } else if let Some(TypedValue::Int(i)) = args.get(1) {
1017                 *i
1018             } else {
1019                 return Err(Error::Unrecoverable(
1020                     "deprecated attribute second argument is not an integer".into(),
1021                 ));
1022             };
1023             let error_code = if is_systemlib {
1024                 /*E_DEPRECATED*/
1025                 8192
1026             } else {
1027                 /*E_USER_DEPRECATED*/
1028                 16384
1029             };
1031             if sampling_rate <= 0 {
1032                 instr::empty(alloc)
1033             } else {
1034                 InstrSeq::gather(
1035                     alloc,
1036                     vec![
1037                         instr::nulluninit(alloc),
1038                         instr::nulluninit(alloc),
1039                         trait_instrs,
1040                         instr::string(alloc, deprecation_string),
1041                         concat_instruction,
1042                         instr::int64(alloc, sampling_rate),
1043                         instr::int(alloc, error_code),
1044                         instr::fcallfuncd(
1045                             alloc,
1046                             FcallArgs::new(FcallFlags::default(), 1, Slice::empty(), None, 3, None),
1047                             function::from_raw_string(alloc, "trigger_sampled_error"),
1048                         ),
1049                         instr::popc(alloc),
1050                     ],
1051                 )
1052             }
1053         }
1054     })
1057 fn set_emit_statement_state<'arena, 'decl, D: DeclProvider<'decl>>(
1058     alloc: &'arena bumpalo::Bump,
1059     emitter: &mut Emitter<'arena, 'decl, D>,
1060     default_return_value: InstrSeq<'arena>,
1061     params: &[HhasParam<'arena>],
1062     return_type_info: &HhasTypeInfo,
1063     return_type: Option<&tast::Hint>,
1064     pos: &Pos,
1065     default_dropthrough: Option<InstrSeq<'arena>>,
1066     flags: Flags,
1067     is_generator: bool,
1068 ) {
1069     let verify_return = match &return_type_info.user_type {
1070         Just(s) if s.as_str() == "" => None,
1071         _ if return_type_info.has_type_constraint() && !is_generator => {
1072             return_type.map(|h| h.clone())
1073         }
1074         _ => None,
1075     };
1076     let default_dropthrough = if default_dropthrough.is_some() {
1077         default_dropthrough
1078     } else if flags.contains(Flags::ASYNC) && verify_return.is_some() {
1079         Some(InstrSeq::gather(
1080             alloc,
1081             vec![
1082                 instr::null(alloc),
1083                 instr::verify_ret_type_c(alloc),
1084                 instr::retc(alloc),
1085             ],
1086         ))
1087     } else {
1088         None
1089     };
1090     let (num_out, verify_out) = emit_verify_out(alloc, params);
1092     emit_statement::set_state(
1093         alloc,
1094         emitter,
1095         StatementState {
1096             verify_return,
1097             default_return_value,
1098             default_dropthrough,
1099             verify_out,
1100             function_pos: pos.clone(),
1101             num_out,
1102         },
1103     )
1106 fn emit_verify_out<'arena>(
1107     alloc: &'arena bumpalo::Bump,
1108     params: &[HhasParam<'arena>],
1109 ) -> (usize, InstrSeq<'arena>) {
1110     let param_instrs: Vec<InstrSeq<'arena>> = params
1111         .iter()
1112         .enumerate()
1113         .filter_map(|(i, p)| {
1114             if p.is_inout {
1115                 Some(InstrSeq::gather(
1116                     alloc,
1117                     vec![
1118                         instr::cgetl(alloc, Local::Named(Str::new_str(alloc, p.name.as_str()))),
1119                         match p.type_info.as_ref() {
1120                             Just(HhasTypeInfo { user_type, .. })
1121                                 if user_type.as_ref().map_or(true, |t| {
1122                                     !(t.as_str().ends_with("HH\\mixed")
1123                                         || t.as_str().ends_with("HH\\dynamic"))
1124                                 }) =>
1125                             {
1126                                 instr::verify_out_type(alloc, ParamId::ParamUnnamed(i as isize))
1127                             }
1128                             _ => instr::empty(alloc),
1129                         },
1130                     ],
1131                 ))
1132             } else {
1133                 None
1134             }
1135         })
1136         .collect();
1137     (
1138         param_instrs.len(),
1139         InstrSeq::gather(alloc, param_instrs.into_iter().rev().collect()),
1140     )
1143 pub fn emit_generics_upper_bounds<'arena>(
1144     alloc: &'arena bumpalo::Bump,
1145     immediate_tparams: &[tast::Tparam],
1146     class_tparam_names: &[&str],
1147     skip_awaitable: bool,
1148 ) -> Vec<(String, Vec<HhasTypeInfo<'arena>>)> {
1149     let constraint_filter = |(kind, hint): &(ast_defs::ConstraintKind, tast::Hint)| {
1150         if let ast_defs::ConstraintKind::ConstraintAs = &kind {
1151             let mut tparam_names = get_tp_names(immediate_tparams);
1152             tparam_names.extend_from_slice(class_tparam_names);
1153             emit_type_hint::hint_to_type_info(
1154                 alloc,
1155                 &emit_type_hint::Kind::UpperBound,
1156                 skip_awaitable,
1157                 false, // nullable
1158                 &tparam_names,
1159                 &hint,
1160             )
1161             .ok() //TODO(hrust) propagate Err result
1162         } else {
1163             None
1164         }
1165     };
1166     let tparam_filter = |tparam: &tast::Tparam| {
1167         let ubs = tparam
1168             .constraints
1169             .iter()
1170             .filter_map(constraint_filter)
1171             .collect::<Vec<_>>();
1172         match &ubs[..] {
1173             [] => None,
1174             _ => Some((get_tp_name(tparam).to_owned(), ubs)),
1175         }
1176     };
1177     immediate_tparams
1178         .iter()
1179         .filter_map(tparam_filter)
1180         .collect::<Vec<_>>()
1183 fn emit_shadowed_tparams(
1184     immediate_tparams: &[tast::Tparam],
1185     class_tparam_names: &[&str],
1186 ) -> Vec<String> {
1187     let s1 = get_tp_names_set(immediate_tparams);
1188     let s2: HashSet<&str> = class_tparam_names.iter().cloned().collect();
1189     // TODO(hrust): remove sort after Rust emitter released
1190     let mut r = s1
1191         .intersection(&s2)
1192         .map(|s| (*s).into())
1193         .collect::<Vec<_>>();
1194     r.sort();
1195     r
1198 fn move_this(vars: &mut Vec<String>) {
1199     if vars.iter().any(|v| v == &THIS) {
1200         vars.retain(|s| s != THIS);
1201         vars.push(String::from(THIS));
1202     }
1205 fn get_tp_name(tparam: &tast::Tparam) -> &str {
1206     let ast_defs::Id(_, name) = &tparam.name;
1207     name
1210 pub fn get_tp_names(tparams: &[tast::Tparam]) -> Vec<&str> {
1211     tparams.iter().map(get_tp_name).collect()
1214 pub fn get_tp_names_set(tparams: &[tast::Tparam]) -> HashSet<&str> {
1215     tparams.iter().map(get_tp_name).collect()
1218 #[allow(clippy::needless_lifetimes)]
1219 fn modify_prog_for_debugger_eval<'arena>(_body_instrs: &mut InstrSeq<'arena>) {
1220     unimplemented!() // SF(2021-03-17): I found it like this.
1223 fn set_function_jmp_targets<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
1224     emitter: &mut Emitter<'arena, 'decl, D>,
1225     env: &mut Env<'a, 'arena>,
1226 ) -> bool {
1227     let function_state_key = get_unique_id_for_scope(&env.scope);
1228     emitter
1229         .emit_global_state()
1230         .functions_with_finally
1231         .contains(&function_state_key)