Add internal identifier to functions, methods, properties and classes
[hiphop-php.git] / hphp / hack / src / hackc / emitter / emit_memoize_function.rs
blob625229634819f97834ff5e16b5c6c2aeb2781b55
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.
6 use ast_scope::{self, Scope, ScopeItem};
7 use emit_pos::emit_pos_then;
8 use env::{emitter::Emitter, Env};
9 use error::Result;
10 use ffi::{Slice, Str};
11 use hhbc::{
12     hhas_attribute::{self, HhasAttribute},
13     hhas_body::HhasBody,
14     hhas_coeffects::HhasCoeffects,
15     hhas_function::{HhasFunction, HhasFunctionFlags},
16     hhas_param::HhasParam,
17     hhas_pos::HhasSpan,
18     hhas_type::HhasTypeInfo,
19     FCallArgs, FCallArgsFlags, Label, Local, LocalRange, TypedValue,
21 use hhbc_string_utils::reified;
22 use hhvm_types_ffi::ffi::Attr;
23 use instruction_sequence::{instr, InstrSeq};
24 use ocamlrep::rc::RcOc;
25 use options::{HhvmFlags, Options, RepoFlags};
26 use oxidized::{ast as T, pos::Pos};
28 pub fn is_interceptable(opts: &Options) -> bool {
29     opts.hhvm
30         .flags
31         .contains(HhvmFlags::JIT_ENABLE_RENAME_FUNCTION)
32         && !opts.repo_flags.contains(RepoFlags::AUTHORITATIVE)
35 pub(crate) fn get_attrs_for_fun<'a, 'arena, 'decl>(
36     emitter: &mut Emitter<'arena, 'decl>,
37     fd: &'a T::FunDef,
38     user_attrs: &'a [HhasAttribute<'arena>],
39     is_memoize_impl: bool,
40 ) -> Attr {
41     let f = &fd.fun;
42     let is_systemlib = emitter.systemlib();
43     let is_dyn_call =
44         is_systemlib || (hhas_attribute::has_dynamically_callable(user_attrs) && !is_memoize_impl);
45     let is_prov_skip_frame = hhas_attribute::has_provenance_skip_frame(user_attrs);
46     let is_meth_caller = hhas_attribute::has_meth_caller(user_attrs);
48     let mut attrs = Attr::AttrNone;
49     attrs.set(Attr::AttrBuiltin, is_meth_caller | is_systemlib);
50     attrs.set(Attr::AttrDynamicallyCallable, is_dyn_call);
51     attrs.set(Attr::AttrInterceptable, is_interceptable(emitter.options()));
52     attrs.set(
53         Attr::AttrIsFoldable,
54         hhas_attribute::has_foldable(user_attrs),
55     );
56     attrs.set(Attr::AttrIsMethCaller, is_meth_caller);
57     attrs.set(
58         Attr::AttrNoInjection,
59         hhas_attribute::is_no_injection(user_attrs),
60     );
61     attrs.set(Attr::AttrPersistent, is_systemlib);
62     attrs.set(Attr::AttrProvenanceSkipFrame, is_prov_skip_frame);
63     attrs.set(Attr::AttrReadonlyReturn, f.readonly_ret.is_some());
64     attrs.set(Attr::AttrUnique, is_systemlib);
65     attrs.set(Attr::AttrInternal, fd.internal);
66     attrs
69 pub(crate) fn emit_wrapper_function<'a, 'arena, 'decl>(
70     emitter: &mut Emitter<'arena, 'decl>,
71     original_id: hhbc::FunctionName<'arena>,
72     renamed_id: &hhbc::FunctionName<'arena>,
73     deprecation_info: Option<&[TypedValue<'arena>]>,
74     fd: &'a T::FunDef,
75 ) -> Result<HhasFunction<'arena>> {
76     let alloc = emitter.alloc;
77     let f = &fd.fun;
78     emit_memoize_helpers::check_memoize_possible(&(f.name).0, &f.params, false)?;
79     let scope = Scope {
80         items: vec![ScopeItem::Function(ast_scope::Fun::new_ref(fd))],
81     };
82     let mut tparams = scope
83         .get_tparams()
84         .iter()
85         .map(|tp| tp.name.1.as_str())
86         .collect::<Vec<_>>();
87     let params = emit_param::from_asts(emitter, &mut tparams, true, &scope, &f.params)?;
88     let mut attributes = emit_attribute::from_asts(emitter, &f.user_attributes)?;
89     attributes.extend(emit_attribute::add_reified_attribute(alloc, &f.tparams));
90     let return_type_info = emit_body::emit_return_type_info(
91         alloc,
92         &tparams,
93         f.fun_kind.is_fasync(), /* skip_awaitable */
94         f.ret.1.as_ref(),
95     )?;
96     let is_reified = f
97         .tparams
98         .iter()
99         .any(|tp| tp.reified.is_reified() || tp.reified.is_soft_reified());
100     let should_emit_implicit_context = emitter
101         .options()
102         .hhvm
103         .flags
104         .contains(HhvmFlags::ENABLE_IMPLICIT_CONTEXT)
105         && attributes.iter().any(|a| {
106             naming_special_names_rust::user_attributes::is_memoized_policy_sharded(
107                 a.name.unsafe_as_str(),
108             )
109         });
110     let mut env = Env::default(alloc, RcOc::clone(&fd.namespace)).with_scope(scope);
111     let (body_instrs, decl_vars) = make_memoize_function_code(
112         emitter,
113         &mut env,
114         &f.span,
115         deprecation_info,
116         &params,
117         &f.params,
118         *renamed_id,
119         f.fun_kind.is_fasync(),
120         is_reified,
121         should_emit_implicit_context,
122     )?;
123     let coeffects = HhasCoeffects::from_ast(alloc, f.ctxs.as_ref(), &f.params, &f.tparams, vec![]);
124     let body = make_wrapper_body(
125         emitter,
126         env,
127         return_type_info,
128         params,
129         decl_vars,
130         body_instrs,
131     )?;
133     let mut flags = HhasFunctionFlags::empty();
134     flags.set(HhasFunctionFlags::ASYNC, f.fun_kind.is_fasync());
135     let attrs = get_attrs_for_fun(emitter, fd, &attributes, false);
137     Ok(HhasFunction {
138         attributes: Slice::fill_iter(alloc, attributes.into_iter()),
139         name: original_id,
140         body,
141         span: HhasSpan::from_pos(&f.span),
142         coeffects,
143         flags,
144         attrs,
145     })
148 fn make_memoize_function_code<'a, 'arena, 'decl>(
149     e: &mut Emitter<'arena, 'decl>,
150     env: &mut Env<'a, 'arena>,
151     pos: &Pos,
152     deprecation_info: Option<&[TypedValue<'arena>]>,
153     hhas_params: &[(HhasParam<'arena>, Option<(Label, T::Expr)>)],
154     ast_params: &[T::FunParam],
155     renamed_id: hhbc::FunctionName<'arena>,
156     is_async: bool,
157     is_reified: bool,
158     should_emit_implicit_context: bool,
159 ) -> Result<(InstrSeq<'arena>, Vec<Str<'arena>>)> {
160     let (fun, decl_vars) = if hhas_params.is_empty() && !is_reified && !should_emit_implicit_context
161     {
162         make_memoize_function_no_params_code(e, env, deprecation_info, renamed_id, is_async)
163     } else {
164         make_memoize_function_with_params_code(
165             e,
166             env,
167             pos,
168             deprecation_info,
169             hhas_params,
170             ast_params,
171             renamed_id,
172             is_async,
173             is_reified,
174             should_emit_implicit_context,
175         )
176     }?;
177     Ok((emit_pos_then(pos, fun), decl_vars))
180 fn make_memoize_function_with_params_code<'a, 'arena, 'decl>(
181     e: &mut Emitter<'arena, 'decl>,
182     env: &mut Env<'a, 'arena>,
183     pos: &Pos,
184     deprecation_info: Option<&[TypedValue<'arena>]>,
185     hhas_params: &[(HhasParam<'arena>, Option<(Label, T::Expr)>)],
186     ast_params: &[T::FunParam],
187     renamed_id: hhbc::FunctionName<'arena>,
188     is_async: bool,
189     is_reified: bool,
190     should_emit_implicit_context: bool,
191 ) -> Result<(InstrSeq<'arena>, Vec<Str<'arena>>)> {
192     let alloc = e.alloc;
193     let param_count = hhas_params.len();
194     let notfound = e.label_gen_mut().next_regular();
195     let suspended_get = e.label_gen_mut().next_regular();
196     let eager_set = e.label_gen_mut().next_regular();
197     // The local that contains the reified generics is the first non parameter local,
198     // so the first unnamed local is parameter count + 1 when there are reified generics.
199     let add_reified = usize::from(is_reified);
200     let add_implicit_context = usize::from(should_emit_implicit_context);
201     let generics_local = Local::new(param_count); // only used if is_reified == true.
202     let decl_vars = match is_reified {
203         true => vec![reified::GENERICS_LOCAL_NAME.into()],
204         false => Vec::new(),
205     };
206     e.init_named_locals(
207         hhas_params
208             .iter()
209             .map(|(param, _)| param.name)
210             .chain(decl_vars.iter().copied()),
211     );
212     let first_unnamed_idx = param_count + add_reified;
213     let deprecation_body =
214         emit_body::emit_deprecation_info(alloc, &env.scope, deprecation_info, e.systemlib())?;
215     let (begin_label, default_value_setters) =
216         // Default value setters belong in the wrapper method not in the original method
217         emit_param::emit_param_default_value_setter(e, env, pos, hhas_params)?;
218     let fcall_args = {
219         let mut fcall_flags = FCallArgsFlags::default();
220         fcall_flags.set(FCallArgsFlags::HasGenerics, is_reified);
221         FCallArgs::new(
222             fcall_flags,
223             1,
224             param_count as u32,
225             Slice::empty(),
226             Slice::empty(),
227             if is_async { Some(eager_set) } else { None },
228             None,
229         )
230     };
231     let (reified_get, reified_memokeym) = if !is_reified {
232         (instr::empty(), instr::empty())
233     } else {
234         (
235             instr::c_get_l(generics_local),
236             InstrSeq::gather(emit_memoize_helpers::get_memo_key_list(
237                 Local::new(param_count + first_unnamed_idx),
238                 generics_local,
239             )),
240         )
241     };
242     let ic_memokey = if !should_emit_implicit_context {
243         instr::empty()
244     } else {
245         // Last unnamed local slot
246         let local = Local::new(first_unnamed_idx + param_count + add_reified);
247         emit_memoize_helpers::get_implicit_context_memo_key(alloc, local)
248     };
249     let first_unnamed_local = Local::new(first_unnamed_idx);
250     let key_count = (param_count + add_reified + add_implicit_context) as isize;
251     let local_range = LocalRange {
252         start: first_unnamed_local,
253         len: key_count.try_into().unwrap(),
254     };
255     let instrs = InstrSeq::gather(vec![
256         begin_label,
257         emit_body::emit_method_prolog(e, env, pos, hhas_params, ast_params, &[])?,
258         deprecation_body,
259         emit_memoize_helpers::param_code_sets(hhas_params.len(), Local::new(first_unnamed_idx)),
260         reified_memokeym,
261         ic_memokey,
262         if is_async {
263             InstrSeq::gather(vec![
264                 instr::memo_get_eager(notfound, suspended_get, local_range),
265                 instr::ret_c(),
266                 instr::label(suspended_get),
267                 instr::ret_c_suspended(),
268             ])
269         } else {
270             InstrSeq::gather(vec![instr::memo_get(notfound, local_range), instr::ret_c()])
271         },
272         instr::label(notfound),
273         instr::null_uninit(),
274         instr::null_uninit(),
275         emit_memoize_helpers::param_code_gets(hhas_params.len()),
276         reified_get,
277         instr::f_call_func_d(fcall_args, renamed_id),
278         instr::memo_set(local_range),
279         if is_async {
280             InstrSeq::gather(vec![
281                 instr::ret_c_suspended(),
282                 instr::label(eager_set),
283                 instr::memo_set_eager(local_range),
284                 instr::ret_c(),
285             ])
286         } else {
287             instr::ret_c()
288         },
289         default_value_setters,
290     ]);
291     Ok((instrs, decl_vars))
294 fn make_memoize_function_no_params_code<'a, 'arena, 'decl>(
295     e: &mut Emitter<'arena, 'decl>,
296     env: &mut Env<'a, 'arena>,
297     deprecation_info: Option<&[TypedValue<'arena>]>,
298     renamed_id: hhbc::FunctionName<'arena>,
299     is_async: bool,
300 ) -> Result<(InstrSeq<'arena>, Vec<Str<'arena>>)> {
301     let alloc = e.alloc;
302     let notfound = e.label_gen_mut().next_regular();
303     let suspended_get = e.label_gen_mut().next_regular();
304     let eager_set = e.label_gen_mut().next_regular();
305     let deprecation_body =
306         emit_body::emit_deprecation_info(alloc, &env.scope, deprecation_info, e.systemlib())?;
307     let fcall_args = FCallArgs::new(
308         FCallArgsFlags::default(),
309         1,
310         0,
311         Slice::empty(),
312         Slice::empty(),
313         if is_async { Some(eager_set) } else { None },
314         None,
315     );
316     let instrs = InstrSeq::gather(vec![
317         deprecation_body,
318         if is_async {
319             InstrSeq::gather(vec![
320                 instr::memo_get_eager(notfound, suspended_get, LocalRange::default()),
321                 instr::ret_c(),
322                 instr::label(suspended_get),
323                 instr::ret_c_suspended(),
324             ])
325         } else {
326             InstrSeq::gather(vec![
327                 instr::memo_get(notfound, LocalRange::default()),
328                 instr::ret_c(),
329             ])
330         },
331         instr::label(notfound),
332         instr::null_uninit(),
333         instr::null_uninit(),
334         instr::f_call_func_d(fcall_args, renamed_id),
335         instr::memo_set(LocalRange::default()),
336         if is_async {
337             InstrSeq::gather(vec![
338                 instr::ret_c_suspended(),
339                 instr::label(eager_set),
340                 instr::memo_set_eager(LocalRange::default()),
341                 instr::ret_c(),
342             ])
343         } else {
344             instr::ret_c()
345         },
346     ]);
347     Ok((instrs, Vec::new()))
350 fn make_wrapper_body<'a, 'arena, 'decl>(
351     emitter: &mut Emitter<'arena, 'decl>,
352     env: Env<'a, 'arena>,
353     return_type_info: HhasTypeInfo<'arena>,
354     params: Vec<(HhasParam<'arena>, Option<(Label, T::Expr)>)>,
355     decl_vars: Vec<Str<'arena>>,
356     body_instrs: InstrSeq<'arena>,
357 ) -> Result<HhasBody<'arena>> {
358     emit_body::make_body(
359         emitter.alloc,
360         emitter,
361         body_instrs,
362         decl_vars,
363         true,   /* is_memoize_wrapper */
364         false,  /* is_memoize_wrapper_lsb */
365         vec![], /* upper_bounds */
366         vec![], /* shadowed_tparams */
367         params,
368         Some(return_type_info),
369         None, /* doc comment */
370         Some(&env),
371     )