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.
5 use ast_scope_rust::{self as ast_scope, Scope, ScopeItem};
6 use emit_attribute_rust as emit_attribute;
7 use emit_body_rust as emit_body;
8 use emit_memoize_helpers_rust as emit_memoize_helpers;
9 use emit_param_rust as emit_param;
10 use emit_pos_rust::emit_pos_then;
11 use env::{emitter::Emitter, local, Env};
12 use hhas_body_rust::HhasBody;
13 use hhas_coeffects::HhasCoeffects;
14 use hhas_function_rust::{Flags as HhasFunctionFlags, HhasFunction};
15 use hhas_param_rust::HhasParam;
16 use hhas_pos_rust::Span;
17 use hhas_type::Info as HhasTypeInfo;
18 use hhbc_ast_rust::{FcallArgs, FcallFlags};
19 use hhbc_id_rust::{function::Type as FunId, Id};
20 use hhbc_string_utils_rust::reified;
21 use instruction_sequence_rust::{instr, InstrSeq, Result};
22 use ocamlrep::rc::RcOc;
23 use options::{HhvmFlags, Options, RepoFlags};
24 use oxidized::{ast as T, pos::Pos};
25 use runtime::TypedValue;
27 pub(crate) fn is_interceptable(opts: &Options) -> bool {
30 .contains(HhvmFlags::JIT_ENABLE_RENAME_FUNCTION)
31 && !opts.repo_flags.contains(RepoFlags::AUTHORITATIVE)
34 pub(crate) fn emit_wrapper_function<'a>(
35 emitter: &mut Emitter,
36 original_id: FunId<'a>,
38 deprecation_info: &Option<&[TypedValue]>,
40 ) -> Result<HhasFunction<'a>> {
41 emit_memoize_helpers::check_memoize_possible(&(f.name).0, &f.params, false)?;
43 items: vec![ScopeItem::Function(ast_scope::Fun::new_ref(f))],
45 let mut tparams = scope
48 .map(|tp| tp.name.1.as_str())
51 emit_param::from_asts(emitter, &mut tparams, &f.namespace, true, &scope, &f.params)?;
52 let mut attributes = emit_attribute::from_asts(emitter, &f.namespace, &f.user_attributes)?;
53 attributes.extend(emit_attribute::add_reified_attribute(&f.tparams));
54 let return_type_info = emit_body::emit_return_type_info(
56 f.fun_kind.is_fasync(), /* skip_awaitable */
62 .any(|tp| tp.reified.is_reified() || tp.reified.is_soft_reified());
63 let mut env = Env::default(RcOc::clone(&f.namespace)).with_scope(scope);
64 // TODO(hrust): avoid cloning
65 let renamed_id_owned: FunId<'static> = renamed_id.to_raw_string().to_string().into();
66 let body_instrs = make_memoize_function_code(
74 f.fun_kind.is_fasync(),
77 let coeffects = HhasCoeffects::from_ast(&f.ctxs, &f.params);
78 env.with_rx_body(coeffects.is_any_rx_or_pure());
79 let body = make_wrapper_body(
88 let mut flags = HhasFunctionFlags::empty();
89 flags.set(HhasFunctionFlags::ASYNC, f.fun_kind.is_fasync());
90 let is_interceptable = is_interceptable(emitter.options());
91 flags.set(HhasFunctionFlags::INTERCEPTABLE, is_interceptable);
97 span: Span::from_pos(&f.span),
103 fn make_memoize_function_code(
107 deprecation_info: Option<&[TypedValue]>,
108 hhas_params: &[HhasParam],
109 ast_params: &[T::FunParam],
110 renamed_id: FunId<'static>,
114 let fun = if hhas_params.is_empty() && !is_reified {
115 make_memoize_function_no_params_code(e, env, deprecation_info, renamed_id, is_async)
117 make_memoize_function_with_params_code(
129 Ok(emit_pos_then(pos, fun))
132 fn make_memoize_function_with_params_code(
136 deprecation_info: Option<&[TypedValue]>,
137 hhas_params: &[HhasParam],
138 ast_params: &[T::FunParam],
139 renamed_id: FunId<'static>,
143 let param_count = hhas_params.len();
144 let notfound = e.label_gen_mut().next_regular();
145 let suspended_get = e.label_gen_mut().next_regular();
146 let eager_set = e.label_gen_mut().next_regular();
147 // The local that contains the reified generics is the first non parameter local,
148 // so the first local is parameter count + 1 when there are reified = generics
149 let add_refied = usize::from(is_reified);
150 let first_local = local::Type::Unnamed(param_count + add_refied);
151 let deprecation_body =
152 emit_body::emit_deprecation_info(&env.scope, deprecation_info, e.systemlib())?;
153 let (begin_label, default_value_setters) =
154 // Default value setters belong in the wrapper method not in the original method
155 emit_param::emit_param_default_value_setter(e, env, pos, hhas_params)?;
157 let mut fcall_flags = FcallFlags::default();
158 fcall_flags.set(FcallFlags::HAS_GENERICS, is_reified);
164 Some(eager_set.clone())
172 let (reified_get, reified_memokeym) = if !is_reified {
173 (instr::empty(), instr::empty())
176 instr::cgetl(local::Type::Named(reified::GENERICS_LOCAL_NAME.into())),
177 InstrSeq::gather(emit_memoize_helpers::get_memo_key_list(
179 param_count + add_refied,
180 reified::GENERICS_LOCAL_NAME.into(),
184 let param_count = (param_count + add_refied) as isize;
185 Ok(InstrSeq::gather(vec![
187 emit_body::emit_method_prolog(e, env, pos, hhas_params, ast_params, &[])?,
189 emit_memoize_helpers::param_code_sets(hhas_params, param_count as usize),
192 InstrSeq::gather(vec![
193 instr::memoget_eager(
195 suspended_get.clone(),
196 Some((first_local.clone(), param_count)),
199 instr::label(suspended_get),
200 instr::retc_suspended(),
203 InstrSeq::gather(vec![
204 instr::memoget(notfound.clone(), Some((first_local.clone(), param_count))),
208 instr::label(notfound),
211 emit_memoize_helpers::param_code_gets(hhas_params),
213 instr::fcallfuncd(fcall_args, renamed_id),
214 instr::memoset(Some((first_local.clone(), param_count))),
216 InstrSeq::gather(vec![
217 instr::retc_suspended(),
218 instr::label(eager_set),
219 instr::memoset_eager(Some((first_local, param_count))),
223 InstrSeq::gather(vec![instr::retc()])
225 default_value_setters,
229 fn make_memoize_function_no_params_code(
232 deprecation_info: Option<&[TypedValue]>,
233 renamed_id: FunId<'static>,
236 let notfound = e.label_gen_mut().next_regular();
237 let suspended_get = e.label_gen_mut().next_regular();
238 let eager_set = e.label_gen_mut().next_regular();
239 let deprecation_body =
240 emit_body::emit_deprecation_info(&env.scope, deprecation_info, e.systemlib())?;
241 let fcall_args = FcallArgs::new(
242 FcallFlags::default(),
246 Some(eager_set.clone())
253 Ok(InstrSeq::gather(vec![
256 InstrSeq::gather(vec![
257 instr::memoget_eager(notfound.clone(), suspended_get.clone(), None),
259 instr::label(suspended_get),
260 instr::retc_suspended(),
263 InstrSeq::gather(vec![instr::memoget(notfound.clone(), None), instr::retc()])
265 instr::label(notfound),
268 instr::fcallfuncd(fcall_args, renamed_id),
269 instr::memoset(None),
271 InstrSeq::gather(vec![
272 instr::retc_suspended(),
273 instr::label(eager_set),
274 instr::memoset_eager(None),
283 fn make_wrapper_body<'a>(
284 emitter: &mut Emitter,
286 return_type_info: HhasTypeInfo,
287 params: Vec<HhasParam>,
288 body_instrs: InstrSeq,
290 ) -> Result<HhasBody<'a>> {
291 emit_body::make_body(
295 vec![reified::GENERICS_LOCAL_NAME.into()]
299 true, /* is_memoize_wrapper */
300 false, /* is_memoize_wrapper_lsb */
301 vec![], /* upper_bounds */
302 vec![], /* shadowed_tparams */
304 Some(return_type_info),
305 None, /* doc comment */