1 // Copyright (c) Facebook, Inc. and its affiliates.
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
6 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 ast_body::AstBody;
13 use ast_class_expr::ClassExpr;
14 use ast_scope::{Scope, ScopeItem};
15 use emit_pos::emit_pos;
16 use env::{emitter::Emitter, Env};
18 use hhas_body::{HhasBody, HhasBodyEnv};
19 use hhas_param::HhasParam;
20 use hhas_type::HhasTypeInfo;
21 use hhbc_ast::{FCallArgsFlags, FcallArgs, Instruct, IsTypeOp, ParamId, Pseudo};
22 use hhbc_id::function;
23 use hhbc_string_utils as string_utils;
24 use instruction_sequence::{instr, unrecoverable, Error, InstrSeq, Result};
26 use local::{Local, LocalId};
27 use options::CompilerFlags;
28 use runtime::TypedValue;
29 use statement_state::StatementState;
30 use unique_id_builder::*;
32 use ocamlrep::rc::RcOc;
33 use oxidized::{aast, ast, ast_defs, doc_comment::DocComment, namespace_env, pos::Pos};
35 use ffi::{Maybe, Maybe::*, Pair, Slice, Str};
37 use bitflags::bitflags;
38 use indexmap::IndexSet;
39 use itertools::Either;
41 static THIS: &str = "$this";
43 /// Optional arguments for emit_body; use Args::default() for defaults
44 pub struct Args<'a, 'arena> {
45 pub immediate_tparams: &'a Vec<ast::Tparam>,
46 pub class_tparam_names: &'a [&'a str],
47 pub ast_params: &'a Vec<ast::FunParam>,
48 pub ret: Option<&'a ast::Hint>,
50 pub deprecation_info: &'a Option<&'a [TypedValue<'arena>]>,
51 pub doc_comment: Option<DocComment>,
52 pub default_dropthrough: Option<InstrSeq<'arena>>,
53 pub call_context: Option<String>,
58 pub struct Flags: u8 {
59 const HAS_COEFFECTS_LOCAL = 1 << 0;
60 const SKIP_AWAITABLE = 1 << 1;
61 const MEMOIZE = 1 << 2;
62 const CLOSURE_BODY = 1 << 3;
63 const NATIVE = 1 << 4;
65 const DEBUGGER_MODIFY_PROGRAM = 1 << 7;
69 pub fn emit_body_with_default_args<'b, 'arena, 'decl>(
70 alloc: &'arena bumpalo::Bump,
71 emitter: &mut Emitter<'arena, 'decl>,
72 namespace: RcOc<namespace_env::Env>,
73 body: &'b ast::Program,
74 return_value: InstrSeq<'arena>,
75 ) -> Result<HhasBody<'arena>> {
77 immediate_tparams: &vec![],
78 class_tparam_names: &[],
81 pos: &Pos::make_none(),
82 deprecation_info: &None,
84 default_dropthrough: None,
86 flags: Flags::empty(),
92 Either::Left(body.as_slice()),
100 pub fn emit_body<'b, 'arena, 'decl>(
101 alloc: &'arena bumpalo::Bump,
102 emitter: &mut Emitter<'arena, 'decl>,
103 namespace: RcOc<namespace_env::Env>,
105 return_value: InstrSeq<'arena>,
106 scope: Scope<'_, 'arena>,
107 args: Args<'_, 'arena>,
108 ) -> Result<(HhasBody<'arena>, bool, bool)> {
109 let tparams: Vec<ast::Tparam> = scope.get_tparams().into_iter().cloned().collect();
110 let mut tp_names = get_tp_names(&tparams);
111 let (is_generator, is_pair_generator) = generator::is_function_generator(&body);
113 emitter.label_gen_mut().reset();
114 emitter.iterator_mut().reset();
116 let return_type_info = make_return_type_info(
118 args.flags.contains(Flags::SKIP_AWAITABLE),
119 args.flags.contains(Flags::NATIVE),
124 let params = make_params(emitter, &mut tp_names, args.ast_params, &scope, args.flags)?;
126 let upper_bounds = emit_generics_upper_bounds(
128 args.immediate_tparams,
129 args.class_tparam_names,
130 args.flags.contains(Flags::SKIP_AWAITABLE),
132 let shadowed_tparams = emit_shadowed_tparams(args.immediate_tparams, args.class_tparam_names);
133 let decl_vars = make_decl_vars(
136 args.immediate_tparams,
141 let mut env = make_env(alloc, namespace, scope, args.call_context);
143 set_emit_statement_state(
151 args.default_dropthrough,
155 env.jump_targets_gen.reset();
157 let should_reserve_locals = set_function_jmp_targets(emitter, &mut env);
158 let local_gen = emitter.local_gen_mut();
159 let num_locals = params.len() + decl_vars.len();
160 local_gen.reset(LocalId::from_usize(num_locals));
161 if should_reserve_locals {
162 local_gen.reserve_retval_and_label_id_locals();
164 let body_instrs = make_body_instrs(
172 args.deprecation_info.clone(),
183 false, // is_memoize_wrapper
184 false, // is_memoize_wrapper_lsb
188 Some(return_type_info),
189 args.doc_comment.to_owned(),
197 fn make_body_instrs<'a, 'arena, 'decl>(
198 emitter: &mut Emitter<'arena, 'decl>,
199 env: &mut Env<'a, 'arena>,
200 params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
201 tparams: &[ast::Tparam],
202 decl_vars: &[String],
205 deprecation_info: Option<&[TypedValue<'arena>]>,
207 ast_params: &[ast::FunParam],
209 ) -> Result<InstrSeq<'arena>> {
210 let stmt_instrs = if flags.contains(Flags::NATIVE) {
213 env.do_function(emitter, &body, emit_ast_body)?
216 let (begin_label, default_value_setters) =
217 emit_param::emit_param_default_value_setter(emitter, env, pos, params)?;
219 let header_content = make_header_content(
231 let first_instr_is_label = match InstrSeq::first(&stmt_instrs) {
232 Some(Instruct::Pseudo(Pseudo::Label(_))) => true,
235 let header = if first_instr_is_label && InstrSeq::is_empty(&header_content) {
236 InstrSeq::gather(vec![begin_label, instr::entrynop()])
238 InstrSeq::gather(vec![begin_label, header_content])
241 let mut body_instrs = InstrSeq::gather(vec![header, stmt_instrs, default_value_setters]);
242 if flags.contains(Flags::DEBUGGER_MODIFY_PROGRAM) {
243 modify_prog_for_debugger_eval(&mut body_instrs);
248 fn make_header_content<'a, 'arena, 'decl>(
249 emitter: &mut Emitter<'arena, 'decl>,
250 env: &mut Env<'a, 'arena>,
251 params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
252 tparams: &[ast::Tparam],
253 _decl_vars: &[String],
255 deprecation_info: Option<&[TypedValue<'arena>]>,
257 ast_params: &[ast::FunParam],
259 ) -> Result<InstrSeq<'arena>> {
260 let alloc = env.arena;
261 let method_prolog = if flags.contains(Flags::NATIVE) {
264 emit_method_prolog(emitter, env, pos, params, ast_params, tparams)?
267 let deprecation_warning =
268 emit_deprecation_info(alloc, &env.scope, deprecation_info, emitter.systemlib())?;
270 let generator_info = if is_generator {
271 InstrSeq::gather(vec![instr::createcont(), instr::popc()])
276 Ok(InstrSeq::gather(vec![
283 fn make_decl_vars<'a, 'arena, 'decl>(
284 emitter: &mut Emitter<'arena, 'decl>,
285 scope: &Scope<'a, 'arena>,
286 immediate_tparams: &[ast::Tparam],
287 params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
290 ) -> Result<Vec<String>> {
291 let explicit_use_set = &emitter.emit_global_state().explicit_use_set;
294 decl_vars::from_ast(params, body, explicit_use_set).map_err(unrecoverable)?;
296 let mut decl_vars = if arg_flags.contains(Flags::CLOSURE_BODY) {
297 let mut captured_vars = scope.get_captured_vars();
298 move_this(&mut decl_vars);
299 decl_vars.retain(|v| !captured_vars.contains(v));
300 captured_vars.extend_from_slice(decl_vars.as_slice());
303 match &scope.items[..] {
304 [] | [.., ScopeItem::Class(_), _] => move_this(&mut decl_vars),
310 if arg_flags.contains(Flags::HAS_COEFFECTS_LOCAL) {
311 decl_vars.insert(0, string_utils::coeffects::LOCAL_NAME.into());
314 if !arg_flags.contains(Flags::CLOSURE_BODY)
317 .any(|t| t.reified != ast::ReifyKind::Erased)
319 decl_vars.insert(0, string_utils::reified::GENERICS_LOCAL_NAME.into());
324 pub fn emit_return_type_info<'arena>(
325 alloc: &'arena bumpalo::Bump,
327 skip_awaitable: bool,
328 ret: Option<&aast::Hint>,
329 ) -> Result<HhasTypeInfo<'arena>> {
331 None => Ok(HhasTypeInfo::make(
332 Just(Str::new_str(alloc, "")),
333 hhas_type::constraint::Constraint::default(),
335 Some(hint) => emit_type_hint::hint_to_type_info(
337 &emit_type_hint::Kind::Return,
346 fn make_return_type_info<'arena>(
347 alloc: &'arena bumpalo::Bump,
348 skip_awaitable: bool,
350 ret: Option<&aast::Hint>,
352 ) -> Result<HhasTypeInfo<'arena>> {
353 let return_type_info = emit_return_type_info(alloc, tp_names, skip_awaitable, ret);
355 return return_type_info.map(|rti| {
356 emit_type_hint::emit_type_constraint_for_native_function(alloc, tp_names, ret, rti)
362 pub fn make_env<'a, 'arena>(
363 alloc: &'arena bumpalo::Bump,
364 namespace: RcOc<namespace_env::Env>,
365 scope: Scope<'a, 'arena>,
366 call_context: Option<String>,
367 ) -> Env<'a, 'arena> {
368 let mut env = Env::default(alloc, namespace);
369 env.call_context = call_context;
374 fn make_params<'a, 'arena, 'decl>(
375 emitter: &mut Emitter<'arena, 'decl>,
376 tp_names: &mut Vec<&str>,
377 ast_params: &[ast::FunParam],
378 scope: &Scope<'a, 'arena>,
380 ) -> Result<Vec<(HhasParam<'arena>, Option<(Label, ast::Expr)>)>> {
381 let generate_defaults = !flags.contains(Flags::MEMOIZE);
382 emit_param::from_asts(emitter, tp_names, generate_defaults, scope, ast_params)
385 pub fn make_body<'a, 'arena, 'decl>(
386 alloc: &'arena bumpalo::Bump,
387 emitter: &mut Emitter<'arena, 'decl>,
388 mut body_instrs: InstrSeq<'arena>,
389 decl_vars: Vec<String>,
390 is_memoize_wrapper: bool,
391 is_memoize_wrapper_lsb: bool,
392 upper_bounds: Vec<Pair<Str<'arena>, Slice<'arena, HhasTypeInfo<'arena>>>>,
393 shadowed_tparams: Vec<String>,
394 mut params: Vec<(HhasParam<'arena>, Option<(Label, ast::Expr)>)>,
395 return_type_info: Option<HhasTypeInfo<'arena>>,
396 doc_comment: Option<DocComment>,
397 opt_env: Option<&Env<'a, 'arena>>,
398 ) -> Result<HhasBody<'arena>> {
402 .contains(CompilerFlags::RELABEL)
404 label_rewriter::relabel_function(&mut params, &mut body_instrs);
406 let num_iters = if is_memoize_wrapper {
409 emitter.iterator().count()
411 let body_env = if let Some(env) = opt_env {
412 let is_namespaced = env.namespace.name.is_none();
413 if let Some(cd) = env.scope.get_class() {
417 (cd.get_kind().into(), Str::new_str(alloc, cd.get_name_str())).into(),
419 parent_name: ClassExpr::get_parent_class_name(cd)
421 .map(|s| Str::new_str(alloc, s))
428 parent_name: Nothing,
435 // Pretty-print the DV initializer expression as a Hack source code string,
436 // to make it available for reflection.
437 params.iter_mut().for_each(|(p, default_value)| {
438 p.default_value = Maybe::from(
442 use print_expr::{Context, ExprEnv};
443 let ctx = Context::new(emitter);
444 let expr_env = ExprEnv {
445 codegen_env: body_env.as_ref(),
447 let mut buf = Vec::new();
448 print_expr::print_expr(&ctx, &mut buf, &expr_env, expr)
449 .map(|_| Pair(l.clone(), Str::from_vec(alloc, buf)))
457 body_instrs: body_instrs.compact(alloc),
458 decl_vars: Slice::fill_iter(
460 decl_vars.into_iter().map(|s| Str::new_str(alloc, &s)),
464 is_memoize_wrapper_lsb,
465 upper_bounds: Slice::fill_iter(alloc, upper_bounds.into_iter()),
466 shadowed_tparams: Slice::fill_iter(
470 .map(|s| Str::new_str(alloc, &s)),
472 params: Slice::fill_iter(alloc, params.into_iter().map(|(p, _)| p)),
473 return_type_info: return_type_info.into(),
474 doc_comment: doc_comment.map(|c| Str::new_str(alloc, &(c.0).1)).into(),
475 env: body_env.into(),
479 fn emit_ast_body<'a, 'arena, 'decl>(
480 env: &mut Env<'a, 'arena>,
481 e: &mut Emitter<'arena, 'decl>,
483 ) -> Result<InstrSeq<'arena>> {
485 Either::Left(p) => emit_defs(env, e, p),
486 Either::Right(b) => emit_final_stmts(e, env, b),
490 fn emit_defs<'a, 'arena, 'decl>(
491 env: &mut Env<'a, 'arena>,
492 emitter: &mut Emitter<'arena, 'decl>,
494 ) -> Result<InstrSeq<'arena>> {
496 fn emit_def<'a, 'arena, 'decl>(
497 env: &mut Env<'a, 'arena>,
498 emitter: &mut Emitter<'arena, 'decl>,
500 ) -> Result<InstrSeq<'arena>> {
502 Def::Stmt(s) => emit_statement::emit_stmt(emitter, env, s),
503 Def::Namespace(ns) => emit_defs(env, emitter, &ns.1),
504 _ => Ok(instr::empty()),
507 fn aux<'a, 'arena, 'decl>(
508 env: &mut Env<'a, 'arena>,
509 emitter: &mut Emitter<'arena, 'decl>,
511 ) -> Result<InstrSeq<'arena>> {
513 [Def::SetNamespaceEnv(ns), ..] => {
514 env.namespace = RcOc::clone(ns);
515 aux(env, emitter, &defs[1..])
517 [] => emit_statement::emit_dropthrough_return(emitter, env),
519 // emit last statement in the list as final statement
520 emit_statement::emit_final_stmt(emitter, env, s)
522 [Def::Stmt(s1), Def::Stmt(s2)] => match s2.1.as_markup() {
523 Some(id) if id.1.is_empty() => emit_statement::emit_final_stmt(emitter, env, s1),
524 _ => Ok(InstrSeq::gather(vec![
525 emit_statement::emit_stmt(emitter, env, s1)?,
526 emit_statement::emit_final_stmt(emitter, env, s2)?,
529 [def, ..] => Ok(InstrSeq::gather(vec![
530 emit_def(env, emitter, def)?,
531 aux(env, emitter, &defs[1..])?,
537 [Def::Stmt(s), ..] if s.1.is_markup() => Ok(InstrSeq::gather(vec![
538 emit_statement::emit_markup(emitter, env, s.1.as_markup().unwrap(), true)?,
539 aux(env, emitter, &prog[1..])?,
541 [] | [_] => aux(env, emitter, prog),
543 let i1 = emit_def(env, emitter, def)?;
545 emit_defs(env, emitter, &prog[1..])
547 Ok(InstrSeq::gather(vec![i1, aux(env, emitter, &prog[1..])?]))
553 pub fn has_type_constraint<'a, 'arena>(
554 env: &Env<'a, 'arena>,
555 ti: Option<&HhasTypeInfo<'_>>,
556 ast_param: &ast::FunParam,
557 ) -> (RGH::ReificationLevel, Option<ast::Hint>) {
558 use RGH::ReificationLevel as L;
559 match (ti, &ast_param.type_hint.1) {
560 (Some(ti), Some(h)) if ti.has_type_constraint() => {
561 // TODO(hrust): how to avoid clone on h
562 let h = RGH::remove_erased_generics(env, h.clone());
563 (RGH::has_reified_type_constraint(env, &h), Some(h))
565 _ => (L::Unconstrained, None),
569 pub fn emit_method_prolog<'a, 'arena, 'decl>(
570 emitter: &mut Emitter<'arena, 'decl>,
571 env: &mut Env<'a, 'arena>,
573 params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
574 ast_params: &[ast::FunParam],
575 tparams: &[ast::Tparam],
576 ) -> Result<InstrSeq<'arena>> {
577 let alloc = env.arena;
578 let mut make_param_instr =
579 |param: &HhasParam<'arena>, ast_param: &ast::FunParam| -> Result<InstrSeq<'arena>> {
581 || ParamId::ParamNamed(Str::new_str(alloc, param.name.unsafe_as_str()));
582 if param.is_variadic {
585 use RGH::ReificationLevel as L;
586 match has_type_constraint(env, Option::from(param.type_info.as_ref()), ast_param) {
587 (L::Unconstrained, _) => Ok(instr::empty()),
588 (L::Not, _) => Ok(instr::verify_param_type(param_name())),
589 (L::Maybe, Some(h)) => {
590 if !RGH::happly_decl_has_reified_generics(emitter, &h) {
591 Ok(instr::verify_param_type(param_name()))
593 Ok(InstrSeq::gather(vec![
594 emit_expression::get_type_structure_for_hint(
598 .map(|fp| fp.name.1.as_str())
604 instr::verify_param_type_ts(param_name()),
608 (L::Definitely, Some(h)) => {
609 if !RGH::happly_decl_has_reified_generics(emitter, &h) {
610 Ok(instr::verify_param_type(param_name()))
612 let check = instr::istypel(
613 Local::Named(Str::new_str(alloc, param.name.unsafe_as_str())),
616 let verify_instr = instr::verify_param_type_ts(param_name());
617 RGH::simplify_verify_type(emitter, env, pos, check, &h, verify_instr)
620 _ => Err(unrecoverable("impossible")),
625 let ast_params = ast_params
627 .filter(|p| !(p.is_variadic && p.name == "..."))
628 .collect::<Vec<_>>();
629 if params.len() != ast_params.len() {
630 return Err(Error::Unrecoverable("length mismatch".into()));
633 let mut instrs = Vec::with_capacity(1 + params.len());
634 instrs.push(emit_pos(pos));
635 for ((param, _), ast_param) in params.iter().zip(ast_params.into_iter()) {
636 instrs.push(make_param_instr(param, ast_param)?);
638 Ok(InstrSeq::gather(instrs))
641 pub fn emit_deprecation_info<'a, 'arena>(
642 alloc: &'arena bumpalo::Bump,
643 scope: &Scope<'a, 'arena>,
644 deprecation_info: Option<&[TypedValue<'arena>]>,
646 ) -> Result<InstrSeq<'arena>> {
647 Ok(match deprecation_info {
648 None => instr::empty(),
650 fn strip_id<'a>(id: &'a ast::Id) -> &'a str {
651 string_utils::strip_global_ns(id.1.as_str())
653 let (class_name, trait_instrs, concat_instruction): (String, _, _) =
654 match scope.get_class() {
655 None => ("".into(), instr::empty(), instr::empty()),
656 Some(c) if c.get_kind() == ast::ClassishKind::Ctrait => (
658 InstrSeq::gather(vec![instr::selfcls(), instr::classname()]),
662 strip_id(c.get_name()).to_string() + "::",
668 let fn_name = match scope.items.last() {
669 Some(ScopeItem::Function(f)) => strip_id(f.get_name()),
670 Some(ScopeItem::Method(m)) => strip_id(m.get_name()),
672 return Err(Error::Unrecoverable(
673 "deprecated functions must have names".into(),
677 let deprecation_string = class_name
680 + (if args.is_empty() {
681 "deprecated function"
682 } else if let TypedValue::String(s) = &args[0] {
685 return Err(Error::Unrecoverable(
686 "deprecated attribute first argument is not a string".into(),
689 let sampling_rate = if args.len() <= 1 {
691 } else if let Some(TypedValue::Int(i)) = args.get(1) {
694 return Err(Error::Unrecoverable(
695 "deprecated attribute second argument is not an integer".into(),
698 let error_code = if is_systemlib {
702 /*E_USER_DEPRECATED*/
706 if sampling_rate <= 0 {
709 InstrSeq::gather(vec![
713 instr::string(alloc, deprecation_string),
715 instr::int(sampling_rate),
716 instr::int(error_code),
719 FCallArgsFlags::default(),
727 function::from_raw_string(alloc, "trigger_sampled_error"),
736 fn set_emit_statement_state<'arena, 'decl>(
737 alloc: &'arena bumpalo::Bump,
738 emitter: &mut Emitter<'arena, 'decl>,
739 default_return_value: InstrSeq<'arena>,
740 params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
741 return_type_info: &HhasTypeInfo<'_>,
742 return_type: Option<&ast::Hint>,
744 default_dropthrough: Option<InstrSeq<'arena>>,
748 let verify_return = match &return_type_info.user_type {
749 Just(s) if s.unsafe_as_str() == "" => None,
750 _ if return_type_info.has_type_constraint() && !is_generator => return_type.cloned(),
753 let default_dropthrough = if default_dropthrough.is_some() {
755 } else if flags.contains(Flags::ASYNC) && verify_return.is_some() {
756 Some(InstrSeq::gather(vec![
758 instr::verify_ret_type_c(),
764 let (num_out, verify_out) = emit_verify_out(alloc, params);
766 emit_statement::set_state(
770 default_return_value,
773 function_pos: pos.clone(),
779 fn emit_verify_out<'arena>(
780 alloc: &'arena bumpalo::Bump,
781 params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
782 ) -> (usize, InstrSeq<'arena>) {
783 let param_instrs: Vec<InstrSeq<'arena>> = params
786 .filter_map(|(i, (p, _))| {
788 Some(InstrSeq::gather(vec![
789 instr::cgetl(Local::Named(Str::new_str(alloc, p.name.unsafe_as_str()))),
790 match p.type_info.as_ref() {
791 Just(HhasTypeInfo { user_type, .. })
792 if user_type.as_ref().map_or(true, |t| {
793 !(t.unsafe_as_str().ends_with("HH\\mixed")
794 || t.unsafe_as_str().ends_with("HH\\dynamic"))
797 instr::verify_out_type(ParamId::ParamUnnamed(i as isize))
809 InstrSeq::gather(param_instrs.into_iter().rev().collect()),
813 pub fn emit_generics_upper_bounds<'arena>(
814 alloc: &'arena bumpalo::Bump,
815 immediate_tparams: &[ast::Tparam],
816 class_tparam_names: &[&str],
817 skip_awaitable: bool,
818 ) -> Vec<Pair<Str<'arena>, Slice<'arena, HhasTypeInfo<'arena>>>> {
819 let constraint_filter = |(kind, hint): &(ast_defs::ConstraintKind, ast::Hint)| {
820 if let ast_defs::ConstraintKind::ConstraintAs = &kind {
821 let mut tparam_names = get_tp_names(immediate_tparams);
822 tparam_names.extend_from_slice(class_tparam_names);
823 emit_type_hint::hint_to_type_info(
825 &emit_type_hint::Kind::UpperBound,
831 .ok() //TODO(hrust) propagate Err result
836 let tparam_filter = |tparam: &ast::Tparam| {
840 .filter_map(constraint_filter)
841 .collect::<Vec<_>>();
846 Str::new_str(alloc, get_tp_name(tparam)),
847 Slice::fill_iter(alloc, ubs.into_iter()),
855 .filter_map(tparam_filter)
859 fn emit_shadowed_tparams(
860 immediate_tparams: &[ast::Tparam],
861 class_tparam_names: &[&str],
863 let s1 = get_tp_names_set(immediate_tparams);
864 let s2: HashSet<&str> = class_tparam_names.iter().cloned().collect();
865 // TODO(hrust): remove sort after Rust emitter released
868 .map(|s| (*s).into())
869 .collect::<Vec<_>>();
874 fn move_this(vars: &mut Vec<String>) {
875 if vars.iter().any(|v| v == THIS) {
876 vars.retain(|s| s != THIS);
877 vars.push(String::from(THIS));
881 fn get_tp_name(tparam: &ast::Tparam) -> &str {
882 let ast_defs::Id(_, name) = &tparam.name;
886 pub fn get_tp_names(tparams: &[ast::Tparam]) -> Vec<&str> {
887 tparams.iter().map(get_tp_name).collect()
890 pub fn get_tp_names_set(tparams: &[ast::Tparam]) -> HashSet<&str> {
891 tparams.iter().map(get_tp_name).collect()
894 fn modify_prog_for_debugger_eval<'arena>(_body_instrs: &mut InstrSeq<'arena>) {
895 unimplemented!() // SF(2021-03-17): I found it like this.
898 fn set_function_jmp_targets<'a, 'arena, 'decl>(
899 emitter: &mut Emitter<'arena, 'decl>,
900 env: &mut Env<'a, 'arena>,
902 let function_state_key = get_unique_id_for_scope(&env.scope);
905 .functions_with_finally
906 .contains(&function_state_key)