Refactor emit_adata
[hiphop-php.git] / hphp / hack / src / hhbc / hhbc_by_ref / emit_body.rs
blobfb620dda99300b88230281a89d008aeb3aed558a
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 bytecode_printer::{print_expr, Context, ExprEnv};
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::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::{aast, aast_defs, ast, ast_defs, doc_comment::DocComment, namespace_env, pos::Pos};
51 use ffi::{Maybe, Maybe::*, Pair, Slice, Str};
53 use bitflags::bitflags;
54 use indexmap::IndexSet;
55 use itertools::Either;
57 static THIS: &'static str = "$this";
59 /// Optional arguments for emit_body; use Args::default() for defaults
60 pub struct Args<'a, 'arena> {
61     pub immediate_tparams: &'a Vec<ast::Tparam>,
62     pub class_tparam_names: &'a [&'a str],
63     pub ast_params: &'a Vec<ast::FunParam>,
64     pub ret: Option<&'a ast::Hint>,
65     pub pos: &'a Pos,
66     pub deprecation_info: &'a Option<&'a [TypedValue<'arena>]>,
67     pub doc_comment: Option<DocComment>,
68     pub default_dropthrough: Option<InstrSeq<'arena>>,
69     pub call_context: Option<String>,
70     pub flags: Flags,
73 bitflags! {
74     pub struct Flags: u8 {
75         const HAS_COEFFECTS_LOCAL = 1 << 0;
76         const SKIP_AWAITABLE = 1 << 1;
77         const MEMOIZE = 1 << 2;
78         const CLOSURE_BODY = 1 << 3;
79         const NATIVE = 1 << 4;
80         const ASYNC = 1 << 6;
81         const DEBUGGER_MODIFY_PROGRAM = 1 << 7;
82     }
85 pub fn emit_body_with_default_args<'b, 'arena, 'decl>(
86     alloc: &'arena bumpalo::Bump,
87     emitter: &mut Emitter<'arena, 'decl>,
88     namespace: RcOc<namespace_env::Env>,
89     body: &'b ast::Program,
90     return_value: InstrSeq<'arena>,
91 ) -> Result<HhasBody<'arena>> {
92     let args = Args {
93         immediate_tparams: &vec![],
94         class_tparam_names: &vec![],
95         ast_params: &vec![],
96         ret: None,
97         pos: &Pos::make_none(),
98         deprecation_info: &None,
99         doc_comment: None,
100         default_dropthrough: None,
101         call_context: None,
102         flags: Flags::empty(),
103     };
104     emit_body(
105         alloc,
106         emitter,
107         namespace,
108         Either::Left(body),
109         return_value,
110         Scope::toplevel(),
111         args,
112     )
113     .map(|r| r.0)
116 pub fn emit_body<'b, 'arena, 'decl>(
117     alloc: &'arena bumpalo::Bump,
118     emitter: &mut Emitter<'arena, 'decl>,
119     namespace: RcOc<namespace_env::Env>,
120     body: AstBody<'b>,
121     return_value: InstrSeq<'arena>,
122     scope: Scope<'_, 'arena>,
123     args: Args<'_, 'arena>,
124 ) -> Result<(HhasBody<'arena>, bool, bool)> {
125     let tparams = scope
126         .get_tparams()
127         .into_iter()
128         .map(|tp| tp.clone())
129         .collect::<Vec<ast::Tparam>>();
130     let mut tp_names = get_tp_names(&tparams);
131     let (is_generator, is_pair_generator) = generator::is_function_generator(&body);
133     emitter.label_gen_mut().reset();
134     emitter.iterator_mut().reset();
136     let return_type_info = make_return_type_info(
137         alloc,
138         args.flags.contains(Flags::SKIP_AWAITABLE),
139         args.flags.contains(Flags::NATIVE),
140         args.ret,
141         &tp_names,
142     )?;
144     let params = make_params(emitter, &mut tp_names, args.ast_params, &scope, args.flags)?;
146     let upper_bounds = emit_generics_upper_bounds(
147         alloc,
148         args.immediate_tparams,
149         args.class_tparam_names,
150         args.flags.contains(Flags::SKIP_AWAITABLE),
151     );
152     let shadowed_tparams = emit_shadowed_tparams(args.immediate_tparams, args.class_tparam_names);
153     let decl_vars = make_decl_vars(
154         emitter,
155         &scope,
156         args.immediate_tparams,
157         &params,
158         &body,
159         args.flags,
160     )?;
161     let mut env = make_env(alloc, namespace, scope, args.call_context);
163     set_emit_statement_state(
164         alloc,
165         emitter,
166         return_value,
167         &params,
168         &return_type_info,
169         args.ret,
170         args.pos,
171         args.default_dropthrough,
172         args.flags,
173         is_generator,
174     );
175     env.jump_targets_gen.reset();
177     let should_reserve_locals = set_function_jmp_targets(emitter, &mut env);
178     let num_closures = match emitter
179         .emit_global_state()
180         .num_closures
181         .get(&get_unique_id_for_scope(&env.scope))
182     {
183         Some(num) => *num,
184         None => 0,
185     };
186     let local_gen = emitter.local_gen_mut();
187     local_gen.reset(params.len() + decl_vars.len());
188     if should_reserve_locals {
189         local_gen.reserve_retval_and_label_id_locals();
190     };
191     let body_instrs = make_body_instrs(
192         emitter,
193         &mut env,
194         &params,
195         &tparams,
196         &decl_vars,
197         body,
198         is_generator,
199         args.deprecation_info.clone(),
200         &args.pos,
201         &args.ast_params,
202         args.flags,
203     )?;
204     Ok((
205         make_body(
206             alloc,
207             emitter,
208             body_instrs,
209             decl_vars,
210             false, // is_memoize_wrapper
211             false, // is_memoize_wrapper_lsb
212             num_closures,
213             upper_bounds,
214             shadowed_tparams,
215             params,
216             Some(return_type_info),
217             args.doc_comment.to_owned(),
218             Some(&env),
219         )?,
220         is_generator,
221         is_pair_generator,
222     ))
225 fn make_body_instrs<'a, 'arena, 'decl>(
226     emitter: &mut Emitter<'arena, 'decl>,
227     env: &mut Env<'a, 'arena>,
228     params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
229     tparams: &[ast::Tparam],
230     decl_vars: &[String],
231     body: AstBody<'_>,
232     is_generator: bool,
233     deprecation_info: Option<&[TypedValue<'arena>]>,
234     pos: &Pos,
235     ast_params: &[ast::FunParam],
236     flags: Flags,
237 ) -> Result<InstrSeq<'arena>> {
238     let alloc = env.arena;
239     let stmt_instrs = if flags.contains(Flags::NATIVE) {
240         instr::nativeimpl(alloc)
241     } else {
242         env.do_function(emitter, &body, emit_ast_body)?
243     };
245     let (begin_label, default_value_setters) =
246         emit_param::emit_param_default_value_setter(emitter, env, pos, params)?;
248     let header_content = make_header_content(
249         emitter,
250         env,
251         params,
252         tparams,
253         decl_vars,
254         is_generator,
255         deprecation_info,
256         pos,
257         ast_params,
258         flags,
259     )?;
260     let first_instr_is_label = match InstrSeq::first(&stmt_instrs) {
261         Some(Instruct::ILabel(_)) => true,
262         _ => false,
263     };
264     let header = if first_instr_is_label && InstrSeq::is_empty(&header_content) {
265         InstrSeq::gather(alloc, vec![begin_label, instr::entrynop(alloc)])
266     } else {
267         InstrSeq::gather(alloc, vec![begin_label, header_content])
268     };
270     let mut body_instrs = InstrSeq::gather(alloc, vec![header, stmt_instrs, default_value_setters]);
271     if flags.contains(Flags::DEBUGGER_MODIFY_PROGRAM) {
272         modify_prog_for_debugger_eval(&mut body_instrs);
273     };
274     Ok(body_instrs)
277 fn make_header_content<'a, 'arena, 'decl>(
278     emitter: &mut Emitter<'arena, 'decl>,
279     env: &mut Env<'a, 'arena>,
280     params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
281     tparams: &[ast::Tparam],
282     _decl_vars: &[String],
283     is_generator: bool,
284     deprecation_info: Option<&[TypedValue<'arena>]>,
285     pos: &Pos,
286     ast_params: &[ast::FunParam],
287     flags: Flags,
288 ) -> Result<InstrSeq<'arena>> {
289     let alloc = env.arena;
290     let method_prolog = if flags.contains(Flags::NATIVE) {
291         instr::empty(alloc)
292     } else {
293         emit_method_prolog(emitter, env, pos, params, ast_params, tparams)?
294     };
296     let deprecation_warning =
297         emit_deprecation_info(alloc, &env.scope, deprecation_info, emitter.systemlib())?;
299     let generator_info = if is_generator {
300         InstrSeq::gather(alloc, vec![instr::createcont(alloc), instr::popc(alloc)])
301     } else {
302         instr::empty(alloc)
303     };
305     Ok(InstrSeq::gather(
306         alloc,
307         vec![method_prolog, deprecation_warning, generator_info],
308     ))
311 fn make_decl_vars<'a, 'arena, 'decl>(
312     emitter: &mut Emitter<'arena, 'decl>,
313     scope: &Scope<'a, 'arena>,
314     immediate_tparams: &[ast::Tparam],
315     params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
316     body: &AstBody<'_>,
317     arg_flags: Flags,
318 ) -> Result<Vec<String>> {
319     let explicit_use_set = &emitter.emit_global_state().explicit_use_set;
321     let mut decl_vars =
322         decl_vars::from_ast(params, body, explicit_use_set).map_err(unrecoverable)?;
324     let mut decl_vars = if arg_flags.contains(Flags::CLOSURE_BODY) {
325         let mut captured_vars = scope.get_captured_vars();
326         move_this(&mut decl_vars);
327         decl_vars.retain(|v| !captured_vars.contains(v));
328         captured_vars.extend_from_slice(&decl_vars.as_slice());
329         captured_vars
330     } else {
331         match &scope.items[..] {
332             [] | [.., ScopeItem::Class(_), _] => move_this(&mut decl_vars),
333             _ => {}
334         };
335         decl_vars
336     };
338     if arg_flags.contains(Flags::HAS_COEFFECTS_LOCAL) {
339         decl_vars.insert(0, string_utils::coeffects::LOCAL_NAME.into());
340     }
342     if !arg_flags.contains(Flags::CLOSURE_BODY)
343         && immediate_tparams
344             .iter()
345             .any(|t| t.reified != ast::ReifyKind::Erased)
346     {
347         decl_vars.insert(0, string_utils::reified::GENERICS_LOCAL_NAME.into());
348     }
349     Ok(decl_vars)
352 pub fn emit_return_type_info<'arena>(
353     alloc: &'arena bumpalo::Bump,
354     tp_names: &[&str],
355     skip_awaitable: bool,
356     ret: Option<&aast::Hint>,
357 ) -> Result<HhasTypeInfo<'arena>> {
358     match ret {
359         None => Ok(HhasTypeInfo::make(
360             Just(Str::new_str(alloc, "")),
361             hhbc_by_ref_hhas_type::constraint::Constraint::default(),
362         )),
363         Some(hint) => emit_type_hint::hint_to_type_info(
364             alloc,
365             &emit_type_hint::Kind::Return,
366             skip_awaitable,
367             false, // nullable
368             tp_names,
369             &hint,
370         ),
371     }
374 fn make_return_type_info<'arena>(
375     alloc: &'arena bumpalo::Bump,
376     skip_awaitable: bool,
377     is_native: bool,
378     ret: Option<&aast::Hint>,
379     tp_names: &[&str],
380 ) -> Result<HhasTypeInfo<'arena>> {
381     let return_type_info = emit_return_type_info(alloc, tp_names, skip_awaitable, ret);
382     if is_native {
383         return return_type_info.map(|rti| {
384             emit_type_hint::emit_type_constraint_for_native_function(alloc, tp_names, ret, rti)
385         });
386     };
387     return_type_info
390 pub fn make_env<'a, 'arena>(
391     alloc: &'arena bumpalo::Bump,
392     namespace: RcOc<namespace_env::Env>,
393     scope: Scope<'a, 'arena>,
394     call_context: Option<String>,
395 ) -> Env<'a, 'arena> {
396     let mut env = Env::default(alloc, namespace);
397     env.call_context = call_context;
398     env.scope = scope;
399     env
402 fn make_params<'a, 'arena, 'decl>(
403     emitter: &mut Emitter<'arena, 'decl>,
404     tp_names: &mut Vec<&str>,
405     ast_params: &[ast::FunParam],
406     scope: &Scope<'a, 'arena>,
407     flags: Flags,
408 ) -> Result<Vec<(HhasParam<'arena>, Option<(Label, ast::Expr)>)>> {
409     let generate_defaults = !flags.contains(Flags::MEMOIZE);
410     emit_param::from_asts(emitter, tp_names, generate_defaults, scope, ast_params)
413 pub fn make_body<'a, 'arena, 'decl>(
414     alloc: &'arena bumpalo::Bump,
415     emitter: &mut Emitter<'arena, 'decl>,
416     mut body_instrs: InstrSeq<'arena>,
417     decl_vars: Vec<String>,
418     is_memoize_wrapper: bool,
419     is_memoize_wrapper_lsb: bool,
420     num_closures: u32,
421     upper_bounds: Vec<Pair<Str<'arena>, Slice<'arena, HhasTypeInfo<'arena>>>>,
422     shadowed_tparams: Vec<String>,
423     mut params: Vec<(HhasParam<'arena>, Option<(Label, ast::Expr)>)>,
424     return_type_info: Option<HhasTypeInfo<'arena>>,
425     doc_comment: Option<DocComment>,
426     opt_env: Option<&Env<'a, 'arena>>,
427 ) -> Result<HhasBody<'arena>> {
428     emit_adata::rewrite_typed_values(emitter, &mut body_instrs)?;
429     if emitter
430         .options()
431         .hack_compiler_flags
432         .contains(CompilerFlags::RELABEL)
433     {
434         label_rewriter::relabel_function(alloc, &mut params, &mut body_instrs);
435     }
436     let num_iters = if is_memoize_wrapper {
437         0
438     } else {
439         emitter.iterator().count()
440     };
441     let body_env = if let Some(env) = opt_env {
442         let is_namespaced = env.namespace.name.is_none();
443         if let Some(cd) = env.scope.get_class() {
444             Some(HhasBodyEnv {
445                 is_namespaced,
446                 class_info: Just(
447                     (cd.get_kind().into(), Str::new_str(alloc, cd.get_name_str())).into(),
448                 ),
449                 parent_name: ClassExpr::get_parent_class_name(cd)
450                     .as_ref()
451                     .map(|s| Str::new_str(alloc, s))
452                     .into(),
453             })
454         } else {
455             Some(HhasBodyEnv {
456                 is_namespaced,
457                 class_info: Nothing,
458                 parent_name: Nothing,
459             })
460         }
461     } else {
462         None
463     };
464     params.iter_mut().for_each(|(p, default_value)| {
465         p.default_value = Maybe::from(
466             default_value
467                 .as_ref()
468                 .map(|(l, expr)| {
469                     let mut ctx = Context::new(emitter, None, false, emitter.systemlib());
470                     let expr_env = ExprEnv {
471                         codegen_env: body_env.as_ref(),
472                     };
473                     let mut buf = Vec::new();
474                     print_expr(&mut ctx, &mut buf, &expr_env, &expr)
475                         .map(|_| Pair(l.clone(), Str::from_vec(alloc, buf)))
476                         .ok()
477                 })
478                 .flatten(),
479         );
480     });
482     Ok(HhasBody {
483         body_instrs,
484         decl_vars: Slice::fill_iter(
485             alloc,
486             decl_vars.into_iter().map(|s| Str::new_str(alloc, &s)),
487         ),
488         num_iters,
489         is_memoize_wrapper,
490         is_memoize_wrapper_lsb,
491         num_closures,
492         upper_bounds: Slice::fill_iter(alloc, upper_bounds.into_iter()),
493         shadowed_tparams: Slice::fill_iter(
494             alloc,
495             shadowed_tparams
496                 .into_iter()
497                 .map(|s| Str::new_str(alloc, &s)),
498         ),
499         params: Slice::fill_iter(alloc, params.into_iter().map(|x| x.0.to_owned())),
500         return_type_info: Maybe::from(return_type_info),
501         doc_comment: Maybe::from(doc_comment.map(|c| Str::new_str(alloc, &(c.0).1))),
502         env: Maybe::from(body_env),
503     })
506 fn emit_ast_body<'a, 'arena, 'decl>(
507     env: &mut Env<'a, 'arena>,
508     e: &mut Emitter<'arena, 'decl>,
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>(
518     env: &mut Env<'a, 'arena>,
519     emitter: &mut Emitter<'arena, 'decl>,
520     prog: &[ast::Def],
521 ) -> Result<InstrSeq<'arena>> {
522     use ast::Def;
523     fn emit_def<'a, 'arena, 'decl>(
524         env: &mut Env<'a, 'arena>,
525         emitter: &mut Emitter<'arena, 'decl>,
526         def: &ast::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>(
536         env: &mut Env<'a, 'arena>,
537         emitter: &mut Emitter<'arena, 'decl>,
538         defs: &[ast::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: &ast::FunParam,
595 ) -> (RGH::ReificationLevel, Option<ast::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::Tparam;
614     use ast_defs::Id;
615     use hhbc_by_ref_instruction_sequence::{instr, unrecoverable, InstrSeq, Result};
616     use hhbc_by_ref_local::Local::Named;
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 loc = Named(Str::new_str(alloc, param.name.unsafe_as_str()));
658         Ok(InstrSeq::gather(
659             alloc,
660             vec![
661                 cls_instrs,
662                 instr::classgetc(alloc),
663                 instr::clscnsl(alloc, loc.clone()),
664                 instr::popl(alloc, loc),
665                 instr::jmp(alloc, label_done.clone()),
666                 instr::label(alloc, label_not_a_class),
667                 emit_fatal_runtime(alloc, &pos, msg),
668                 instr::label(alloc, label_done),
669             ],
670         ))
671     }
672 } //mod atom_helpers
674 ////////////////////////////////////////////////////////////////////////////////
675 // atom_instrs
677 fn atom_instrs<'a, 'arena, 'decl>(
678     emitter: &mut Emitter<'arena, 'decl>,
679     env: &mut Env<'a, 'arena>,
680     param: &HhasParam<'arena>,
681     ast_param: &ast::FunParam,
682     tparams: &[ast::Tparam],
683 ) -> Result<Option<InstrSeq<'arena>>> {
684     let alloc = env.arena;
685     if !param
686         .user_attributes
687         .as_ref()
688         .iter()
689         .any(|a| a.is(|x| x == ua::VIA_LABEL))
690     {
691         return Ok(None); // Not an atom. Nothing to do.
692     }
693     match &ast_param.type_hint {
694         TypeHint(_, None) => Err(raise_fatal_parse(
695             &ast_param.pos,
696             ua::VIA_LABEL.to_owned() + " param type hint unavailable",
697         )),
698         TypeHint(_, Some(Hint(_, h))) => {
699             let label_done = emitter.label_gen_mut().next_regular();
700             let label_not_a_class = emitter.label_gen_mut().next_regular();
701             match &**h {
702                 Happly(ast_defs::Id(_, ref ctor), vec) if ctor == "\\HH\\MemberOf" => {
703                     match &vec[..] {
704                         [hint, _] => {
705                             let Hint(_, e) = hint;
706                             match &**e {
707                                 // Immediate type.
708                                 Happly(ast_defs::Id(pos, ref tag), _) => {
709                                     if atom_helpers::is_erased_generic(tag, tparams) {
710                                         Err(raise_fatal_parse(
711                                             &pos,
712                                             "Erased generic as HH\\MemberOf enum type",
713                                         ))
714                                     } else {
715                                         if !atom_helpers::is_generic(tag, tparams) {
716                                             //'tag' is just a name.
717                                             Ok(Some(atom_helpers::emit_clscnsl(
718                                                 alloc,
719                                                 param,
720                                                 &pos,
721                                                 InstrSeq::gather(
722                                                     alloc,
723                                                     vec![
724                                                         emit_expression::emit_expr(
725                                                             emitter,
726                                                             env,
727                                                             &(ast::Expr(
728                                                                 (),
729                                                                 Pos::make_none(),
730                                                                 aast::Expr_::String(
731                                                                     bstr::BString::from(
732                                                                         tag.to_owned(),
733                                                                     ),
734                                                                 ),
735                                                             )),
736                                                         )?,
737                                                         emit_expression::emit_expr(
738                                                             emitter,
739                                                             env,
740                                                             &(ast::Expr(
741                                                                 (),
742                                                                 Pos::make_none(),
743                                                                 aast::Expr_::True,
744                                                             )),
745                                                         )?,
746                                                         instr::oodeclexists(
747                                                             alloc,
748                                                             ClassishKind::Class,
749                                                         ),
750                                                         instr::jmpz(
751                                                             alloc,
752                                                             label_not_a_class.clone(),
753                                                         ),
754                                                         emit_expression::emit_expr(
755                                                             emitter,
756                                                             env,
757                                                             &(ast::Expr(
758                                                                 (),
759                                                                 Pos::make_none(),
760                                                                 aast::Expr_::String(
761                                                                     bstr::BString::from(
762                                                                         tag.to_owned(),
763                                                                     ),
764                                                                 ),
765                                                             )),
766                                                         )?,
767                                                     ],
768                                                 ),
769                                                 "Type is not a class",
770                                                 label_not_a_class,
771                                                 label_done,
772                                             )?))
773                                         } else {
774                                             //'tag' is a reified generic.
775                                             Ok(Some(atom_helpers::emit_clscnsl(
776                                                 alloc,
777                                                 param,
778                                                 &pos,
779                                                 InstrSeq::gather(
780                                                     alloc,
781                                                     vec![
782                                                     emit_expression::emit_reified_generic_instrs(
783                                                         alloc,
784                                                         &Pos::make_none(),
785                                                         true,
786                                                         atom_helpers::index_of_generic(
787                                                             tparams, tag,
788                                                         )?,
789                                                     )?,
790                                                     instr::basec(alloc, 0, MemberOpMode::ModeNone),
791                                                     instr::querym(
792                                                         alloc,
793                                                         1,
794                                                         QueryOp::CGetQuiet,
795                                                         MemberKey::ET(
796                                                             "classname".into(),
797                                                             ReadonlyOp::Any,
798                                                         ),
799                                                     ),
800                                                     instr::dup(alloc),
801                                                     instr::istypec(alloc, IstypeOp::OpNull),
802                                                     instr::jmpnz(alloc, label_not_a_class.clone()),
803                                                 ],
804                                                 ),
805                                                 "Generic type parameter does not resolve to a class",
806                                                 label_not_a_class,
807                                                 label_done,
808                                             )?))
809                                         }
810                                     }
811                                 }
812                                 // Type constant.
813                                 Haccess(Hint(_, h), _) => {
814                                     match &**h {
815                                         Happly(ast_defs::Id(pos, ref tag), _) => {
816                                             if atom_helpers::is_erased_generic(tag, tparams) {
817                                                 Err(raise_fatal_parse(
818                                                     &pos,
819                                                     "Erased generic as HH\\MemberOf enum type",
820                                                 ))
821                                             } else {
822                                                 //'tag' is a type constant.
823                                                 Ok(Some(atom_helpers::emit_clscnsl(
824                                                     alloc,
825                                                     param,
826                                                     &pos,
827                                                     InstrSeq::gather(alloc, vec![
828                                                         emit_expression::get_type_structure_for_hint(
829                                                             emitter,
830                                                             tparams
831                                                                 .iter()
832                                                                 .map(|fp| fp.name.1.as_str())
833                                                                 .collect::<Vec<_>>()
834                                                                 .as_slice(),
835                                                             &IndexSet::new(),
836                                                             hint,
837                                                         )?,
838                                                         instr::combine_and_resolve_type_struct(alloc, 1),
839                                                         instr::basec(alloc, 0, MemberOpMode::ModeNone),
840                                                         instr::querym(alloc, 1, QueryOp::CGetQuiet, MemberKey::ET(Str::from("classname"), ReadonlyOp::Any)),
841                                                         instr::dup(alloc),
842                                                         instr::istypec(alloc, IstypeOp::OpNull),
843                                                         instr::jmpnz(alloc, label_not_a_class.clone()),
844                                                     ]),
845                                                     "Type constant does not resolve to a class",
846                                                     label_not_a_class,
847                                                     label_done,
848                                             )?))
849                                             }
850                                         }
851                                         _ => Err(unrecoverable(
852                                             "Unexpected case for HH\\MemberOf enum type",
853                                         )),
854                                     }
855                                 }
856                                 _ => {
857                                     Err(unrecoverable("Unexpected case for HH\\MemberOf enum type"))
858                                 }
859                             }
860                         }
861                         _ => Err(unrecoverable(
862                             "Wrong number of type arguments to HH\\MemberOf",
863                         )),
864                     }
865                 }
866                 _ => Err(raise_fatal_parse(
867                     &ast_param.pos,
868                     "'".to_owned() + ua::VIA_LABEL + "' applied to a non-HH\\MemberOf parameter",
869                 )),
870             }
871         }
872     }
875 pub fn emit_method_prolog<'a, 'arena, 'decl>(
876     emitter: &mut Emitter<'arena, 'decl>,
877     env: &mut Env<'a, 'arena>,
878     pos: &Pos,
879     params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
880     ast_params: &[ast::FunParam],
881     tparams: &[ast::Tparam],
882 ) -> Result<InstrSeq<'arena>> {
883     let alloc = env.arena;
884     let mut make_param_instr =
885         |(param, ast_param): (&HhasParam<'arena>, &ast::FunParam)| -> Result<Option<InstrSeq<'arena>>> {
886             let param_name = || ParamId::ParamNamed(Str::new_str(alloc, param.name.unsafe_as_str()));
887             if param.is_variadic {
888                 Ok(None)
889             } else {
890                 use RGH::ReificationLevel as L;
891                 let param_checks =
892                     match has_type_constraint(env, Option::from(param.type_info.as_ref()), ast_param) {
893                         (L::Unconstrained, _) => Ok(None),
894                         (L::Not, _) => Ok(Some(instr::verify_param_type(alloc, param_name()))),
895                         (L::Maybe, Some(h)) => {
896                             if RGH::happly_decl_has_no_reified_generics(emitter, &h) {
897                                 Ok(Some(instr::verify_param_type(alloc, param_name())))
898                             } else {
899                                 Ok(Some(InstrSeq::gather(alloc, vec![
900                                     emit_expression::get_type_structure_for_hint(
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                             }
913                         }
914                         (L::Definitely, Some(h)) => {
915                             if RGH::happly_decl_has_no_reified_generics(emitter, &h) {
916                                 Ok(Some(instr::verify_param_type(alloc, param_name())))
917                             } else {
918                                 let check = instr::istypel(
919                                     alloc,
920                                     Local::Named(Str::new_str(alloc, param.name.unsafe_as_str())),
921                                     IstypeOp::OpNull,
922                                 );
923                                 let verify_instr = instr::verify_param_type_ts(alloc, param_name());
924                                 RGH::simplify_verify_type(emitter, env, pos, check, &h, verify_instr)
925                                     .map(Some)
926                             }
927                         }
928                         _ => Err(unrecoverable("impossible")),
929                     }?;
931                 let atom_instrs = if emitter
932                     .options()
933                     .hhvm
934                     .hack_lang
935                     .flags
936                     .contains(LangFlags::ENABLE_ENUM_CLASSES)
937                 {
938                     atom_instrs(emitter, env, param, ast_param, tparams)?
939                 } else {
940                     None
941                 };
943                 match (param_checks, atom_instrs) {
944                     (None, None) => Ok(None),
945                     (Some(is), None) => Ok(Some(is)),
946                     (Some(is), Some(js)) => Ok(Some(InstrSeq::gather(alloc, vec![is, js]))),
947                     (None, Some(js)) => Ok(Some(js)),
948                 }
949             }
950         };
952     let ast_params = ast_params
953         .iter()
954         .filter(|p| !(p.is_variadic && p.name == "..."))
955         .collect::<Vec<_>>();
956     if params.len() != ast_params.len() {
957         return Err(Error::Unrecoverable("length mismatch".into()));
958     }
960     let param_instrs = params
961         .iter()
962         .zip(ast_params.into_iter())
963         .filter_map(|((param, _), ast_param)| make_param_instr((param, ast_param)).transpose())
964         .collect::<Result<Vec<_>>>()?;
965     let mut instrs = vec![emit_pos(alloc, pos)];
966     for i in param_instrs.iter() {
967         instrs.push(InstrSeq::clone(alloc, i));
968     }
969     Ok(InstrSeq::gather(alloc, instrs))
972 pub fn emit_deprecation_info<'a, 'arena>(
973     alloc: &'arena bumpalo::Bump,
974     scope: &Scope<'a, 'arena>,
975     deprecation_info: Option<&[TypedValue<'arena>]>,
976     is_systemlib: bool,
977 ) -> Result<InstrSeq<'arena>> {
978     Ok(match deprecation_info {
979         None => instr::empty(alloc),
980         Some(args) => {
981             fn strip_id<'a>(id: &'a ast::Id) -> &'a str {
982                 string_utils::strip_global_ns(id.1.as_str())
983             }
984             let (class_name, trait_instrs, concat_instruction): (String, _, _) =
985                 match scope.get_class() {
986                     None => ("".into(), instr::empty(alloc), instr::empty(alloc)),
987                     Some(c) if c.get_kind() == ast::ClassishKind::Ctrait => (
988                         "::".into(),
989                         InstrSeq::gather(alloc, vec![instr::self_(alloc), instr::classname(alloc)]),
990                         instr::concat(alloc),
991                     ),
992                     Some(c) => (
993                         strip_id(c.get_name()).to_string() + "::",
994                         instr::empty(alloc),
995                         instr::empty(alloc),
996                     ),
997                 };
999             let fn_name = match scope.items.last() {
1000                 Some(ScopeItem::Function(f)) => strip_id(f.get_name()),
1001                 Some(ScopeItem::Method(m)) => strip_id(m.get_name()),
1002                 _ => {
1003                     return Err(Error::Unrecoverable(
1004                         "deprecated functions must have names".into(),
1005                     ));
1006                 }
1007             };
1008             let deprecation_string = class_name
1009                 + fn_name
1010                 + ": "
1011                 + (if args.is_empty() {
1012                     "deprecated function"
1013                 } else if let TypedValue::String(s) = &args[0] {
1014                     s.unsafe_as_str()
1015                 } else {
1016                     return Err(Error::Unrecoverable(
1017                         "deprecated attribute first argument is not a string".into(),
1018                     ));
1019                 });
1020             let sampling_rate = if args.len() <= 1 {
1021                 1i64
1022             } else if let Some(TypedValue::Int(i)) = args.get(1) {
1023                 *i
1024             } else {
1025                 return Err(Error::Unrecoverable(
1026                     "deprecated attribute second argument is not an integer".into(),
1027                 ));
1028             };
1029             let error_code = if is_systemlib {
1030                 /*E_DEPRECATED*/
1031                 8192
1032             } else {
1033                 /*E_USER_DEPRECATED*/
1034                 16384
1035             };
1037             if sampling_rate <= 0 {
1038                 instr::empty(alloc)
1039             } else {
1040                 InstrSeq::gather(
1041                     alloc,
1042                     vec![
1043                         instr::nulluninit(alloc),
1044                         instr::nulluninit(alloc),
1045                         trait_instrs,
1046                         instr::string(alloc, deprecation_string),
1047                         concat_instruction,
1048                         instr::int64(alloc, sampling_rate),
1049                         instr::int(alloc, error_code),
1050                         instr::fcallfuncd(
1051                             alloc,
1052                             FcallArgs::new(
1053                                 FcallFlags::default(),
1054                                 1,
1055                                 Slice::empty(),
1056                                 Slice::empty(),
1057                                 None,
1058                                 3,
1059                                 None,
1060                             ),
1061                             function::from_raw_string(alloc, "trigger_sampled_error"),
1062                         ),
1063                         instr::popc(alloc),
1064                     ],
1065                 )
1066             }
1067         }
1068     })
1071 fn set_emit_statement_state<'arena, 'decl>(
1072     alloc: &'arena bumpalo::Bump,
1073     emitter: &mut Emitter<'arena, 'decl>,
1074     default_return_value: InstrSeq<'arena>,
1075     params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
1076     return_type_info: &HhasTypeInfo<'_>,
1077     return_type: Option<&ast::Hint>,
1078     pos: &Pos,
1079     default_dropthrough: Option<InstrSeq<'arena>>,
1080     flags: Flags,
1081     is_generator: bool,
1082 ) {
1083     let verify_return = match &return_type_info.user_type {
1084         Just(s) if s.unsafe_as_str() == "" => None,
1085         _ if return_type_info.has_type_constraint() && !is_generator => {
1086             return_type.map(|h| h.clone())
1087         }
1088         _ => None,
1089     };
1090     let default_dropthrough = if default_dropthrough.is_some() {
1091         default_dropthrough
1092     } else if flags.contains(Flags::ASYNC) && verify_return.is_some() {
1093         Some(InstrSeq::gather(
1094             alloc,
1095             vec![
1096                 instr::null(alloc),
1097                 instr::verify_ret_type_c(alloc),
1098                 instr::retc(alloc),
1099             ],
1100         ))
1101     } else {
1102         None
1103     };
1104     let (num_out, verify_out) = emit_verify_out(alloc, params);
1106     emit_statement::set_state(
1107         alloc,
1108         emitter,
1109         StatementState {
1110             verify_return,
1111             default_return_value,
1112             default_dropthrough,
1113             verify_out,
1114             function_pos: pos.clone(),
1115             num_out,
1116         },
1117     )
1120 fn emit_verify_out<'arena>(
1121     alloc: &'arena bumpalo::Bump,
1122     params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
1123 ) -> (usize, InstrSeq<'arena>) {
1124     let param_instrs: Vec<InstrSeq<'arena>> = params
1125         .iter()
1126         .enumerate()
1127         .filter_map(|(i, (p, _))| {
1128             if p.is_inout {
1129                 Some(InstrSeq::gather(
1130                     alloc,
1131                     vec![
1132                         instr::cgetl(
1133                             alloc,
1134                             Local::Named(Str::new_str(alloc, p.name.unsafe_as_str())),
1135                         ),
1136                         match p.type_info.as_ref() {
1137                             Just(HhasTypeInfo { user_type, .. })
1138                                 if user_type.as_ref().map_or(true, |t| {
1139                                     !(t.unsafe_as_str().ends_with("HH\\mixed")
1140                                         || t.unsafe_as_str().ends_with("HH\\dynamic"))
1141                                 }) =>
1142                             {
1143                                 instr::verify_out_type(alloc, ParamId::ParamUnnamed(i as isize))
1144                             }
1145                             _ => instr::empty(alloc),
1146                         },
1147                     ],
1148                 ))
1149             } else {
1150                 None
1151             }
1152         })
1153         .collect();
1154     (
1155         param_instrs.len(),
1156         InstrSeq::gather(alloc, param_instrs.into_iter().rev().collect()),
1157     )
1160 pub fn emit_generics_upper_bounds<'arena>(
1161     alloc: &'arena bumpalo::Bump,
1162     immediate_tparams: &[ast::Tparam],
1163     class_tparam_names: &[&str],
1164     skip_awaitable: bool,
1165 ) -> Vec<Pair<Str<'arena>, Slice<'arena, HhasTypeInfo<'arena>>>> {
1166     let constraint_filter = |(kind, hint): &(ast_defs::ConstraintKind, ast::Hint)| {
1167         if let ast_defs::ConstraintKind::ConstraintAs = &kind {
1168             let mut tparam_names = get_tp_names(immediate_tparams);
1169             tparam_names.extend_from_slice(class_tparam_names);
1170             emit_type_hint::hint_to_type_info(
1171                 alloc,
1172                 &emit_type_hint::Kind::UpperBound,
1173                 skip_awaitable,
1174                 false, // nullable
1175                 &tparam_names,
1176                 &hint,
1177             )
1178             .ok() //TODO(hrust) propagate Err result
1179         } else {
1180             None
1181         }
1182     };
1183     let tparam_filter = |tparam: &ast::Tparam| {
1184         let ubs = tparam
1185             .constraints
1186             .iter()
1187             .filter_map(constraint_filter)
1188             .collect::<Vec<_>>();
1189         match &ubs[..] {
1190             [] => None,
1191             _ => Some(
1192                 (
1193                     Str::new_str(alloc, get_tp_name(tparam)),
1194                     Slice::fill_iter(alloc, ubs.into_iter()),
1195                 )
1196                     .into(),
1197             ),
1198         }
1199     };
1200     immediate_tparams
1201         .iter()
1202         .filter_map(tparam_filter)
1203         .collect::<Vec<_>>()
1206 fn emit_shadowed_tparams(
1207     immediate_tparams: &[ast::Tparam],
1208     class_tparam_names: &[&str],
1209 ) -> Vec<String> {
1210     let s1 = get_tp_names_set(immediate_tparams);
1211     let s2: HashSet<&str> = class_tparam_names.iter().cloned().collect();
1212     // TODO(hrust): remove sort after Rust emitter released
1213     let mut r = s1
1214         .intersection(&s2)
1215         .map(|s| (*s).into())
1216         .collect::<Vec<_>>();
1217     r.sort();
1218     r
1221 fn move_this(vars: &mut Vec<String>) {
1222     if vars.iter().any(|v| v == &THIS) {
1223         vars.retain(|s| s != THIS);
1224         vars.push(String::from(THIS));
1225     }
1228 fn get_tp_name(tparam: &ast::Tparam) -> &str {
1229     let ast_defs::Id(_, name) = &tparam.name;
1230     name
1233 pub fn get_tp_names(tparams: &[ast::Tparam]) -> Vec<&str> {
1234     tparams.iter().map(get_tp_name).collect()
1237 pub fn get_tp_names_set(tparams: &[ast::Tparam]) -> HashSet<&str> {
1238     tparams.iter().map(get_tp_name).collect()
1241 #[allow(clippy::needless_lifetimes)]
1242 fn modify_prog_for_debugger_eval<'arena>(_body_instrs: &mut InstrSeq<'arena>) {
1243     unimplemented!() // SF(2021-03-17): I found it like this.
1246 fn set_function_jmp_targets<'a, 'arena, 'decl>(
1247     emitter: &mut Emitter<'arena, 'decl>,
1248     env: &mut Env<'a, 'arena>,
1249 ) -> bool {
1250     let function_state_key = get_unique_id_for_scope(&env.scope);
1251     emitter
1252         .emit_global_state()
1253         .functions_with_finally
1254         .contains(&function_state_key)