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 decl_provider::DeclProvider;
6 use hhbc_by_ref_ast_scope::{self as ast_scope, Lambda, Scope, ScopeItem};
7 use hhbc_by_ref_emit_attribute as emit_attribute;
8 use hhbc_by_ref_emit_body as emit_body;
9 use hhbc_by_ref_emit_fatal::{emit_fatal_runtimeomitframe, raise_fatal_parse};
10 use hhbc_by_ref_emit_memoize_helpers as emit_memoize_helpers;
11 use hhbc_by_ref_emit_native_opcode as emit_native_opcode;
12 use hhbc_by_ref_env::emitter::Emitter;
13 use hhbc_by_ref_hhas_attribute as hhas_attribute;
14 use hhbc_by_ref_hhas_coeffects::HhasCoeffects;
15 use hhbc_by_ref_hhas_method::{HhasMethod, HhasMethodFlags};
16 use hhbc_by_ref_hhas_pos::Span;
17 use hhbc_by_ref_hhbc_id::{method, Id};
18 use hhbc_by_ref_hhbc_string_utils as string_utils;
19 use hhbc_by_ref_instruction_sequence::{instr, Result};
20 use hhbc_by_ref_options::{HhvmFlags, Options};
21 use naming_special_names_rust::{classes, special_idents, user_attributes};
22 use ocamlrep::rc::RcOc;
23 use oxidized::{ast as T, ast_defs};
25 use itertools::Either;
28 pub fn from_asts<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
29 alloc: &'arena bumpalo::Bump,
30 emitter: &mut Emitter<'arena, 'decl, D>,
32 methods: &'a [T::Method_],
33 ) -> Result<Vec<HhasMethod<'arena>>> {
36 .map(|m| from_ast(alloc, emitter, class, m))
37 .collect::<Result<Vec<_>>>()
40 pub fn from_ast<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
41 alloc: &'arena bumpalo::Bump,
42 emitter: &mut Emitter<'arena, 'decl, D>,
44 method_: impl Into<Cow<'a, T::Method_>>,
45 ) -> Result<HhasMethod<'arena>> {
46 let method_: Cow<'a, T::Method_> = method_.into();
47 let method = method_.as_ref();
48 let is_memoize = method
51 .any(|ua| user_attributes::is_memoized(&ua.name.1));
52 let class_name = string_utils::mangle(string_utils::strip_global_ns(&class.name.1).into());
53 let is_closure_body = &method.name.1 == "__invoke" && (class.name.1).starts_with("Closure$");
54 let mut attributes = emit_attribute::from_asts(alloc, emitter, &method.user_attributes)?;
56 attributes.extend(emit_attribute::add_reified_attribute(
61 let call_context = if is_closure_body {
62 match &method.user_attributes[..] {
64 name: ast_defs::Id(_, ref s),
66 }] if s.eq_ignore_ascii_case("__DynamicMethCallerForce") => {
67 Some("__SystemLib\\DynamicContextOverrideUnsafe".to_string())
75 let is_native = attributes
77 .any(|attr| attr.is(user_attributes::is_native));
78 let is_native_opcode_impl = hhas_attribute::is_native_opcode_impl(&attributes);
79 let is_abstract = class.kind.is_cinterface() || method.abstract_;
80 let is_async = method.fun_kind.is_fasync() || method.fun_kind.is_fasync_generator();
81 let is_no_injection = hhas_attribute::is_no_injection(&attributes);
83 if !(method.static_ || is_closure_body) {
84 for p in method.params.iter() {
85 if p.name == special_idents::THIS {
86 return Err(raise_fatal_parse(&p.pos, "Cannot re-assign $this"));
90 if class.kind.is_cinterface() && !method.body.ast.is_empty() {
91 return Err(raise_fatal_parse(
94 "Interface method {}::{} cannot contain body",
95 class_name, &method.name.1
99 let is_cabstract = match class.kind {
100 ast_defs::ClassishKind::Cclass(k) => k.is_abstract(),
103 if !method.static_ && class.final_ && is_cabstract {
104 return Err(raise_fatal_parse(
107 "Class {} contains non-static method {} and therefore cannot be declared 'abstract final'",
108 class_name, &method.name.1
113 let visibility = if is_native_opcode_impl {
114 T::Visibility::Public
115 } else if is_memoize {
116 T::Visibility::Private
120 let deprecation_info = if is_memoize {
123 hhas_attribute::deprecation_info(attributes.iter())
125 let default_dropthrough = if method.abstract_ {
126 Some(emit_fatal_runtimeomitframe(
130 "Cannot call abstract method {}::{}()",
131 class_name, &method.name.1
137 let mut scope = Scope {
139 ScopeItem::Class(ast_scope::Class::new_ref(class)),
140 ScopeItem::Method(match &method_ {
141 Cow::Borrowed(m) => ast_scope::Method::new_ref(&m),
142 Cow::Owned(m) => ast_scope::Method::new_rc(&m),
147 scope.items.push(ScopeItem::Lambda(Lambda {
149 coeffects: HhasCoeffects::default(),
152 let namespace = RcOc::clone(
157 .unwrap_or(&class.namespace),
159 let mut coeffects = if method.ctxs == None && is_closure_body {
160 let parent_coeffects = emitter
162 .get_lambda_coeffects_of_scope(&class.name.1, &method.name.1);
163 parent_coeffects.inherit_to_child_closure()
165 HhasCoeffects::from_ast(
173 if is_native_opcode_impl
174 && (class.name.1.as_str() == classes::GENERATOR
175 || class.name.1.as_str() == classes::ASYNC_GENERATOR)
177 match method.name.1.as_str() {
178 "send" | "raise" | "throw" | "next" | "rewind" => {
179 coeffects = coeffects.with_gen_coeffect()
184 if emitter.systemlib() {
185 match (class.name.1.as_str(), method.name.1.as_str()) {
186 ("\\__SystemLib\\MethCallerHelper", "__invoke")
187 | ("\\__SystemLib\\DynMethCallerHelper", "__invoke") => {
188 coeffects = coeffects.with_caller()
193 let ast_body_block = &method.body.ast;
194 let (body, is_generator, is_pair_generator) = if is_native_opcode_impl {
196 emit_native_opcode::emit_body(
200 &class.user_attributes,
203 method.ret.1.as_ref(),
209 let class_tparam_names = class
212 .map(|tp| (tp.name).1.as_str())
213 .collect::<Vec<_>>();
214 let mut flags = emit_body::Flags::empty();
216 emit_body::Flags::SKIP_AWAITABLE,
217 method.fun_kind.is_fasync(),
219 flags.set(emit_body::Flags::MEMOIZE, is_memoize);
220 flags.set(emit_body::Flags::CLOSURE_BODY, is_closure_body);
221 flags.set(emit_body::Flags::NATIVE, is_native);
222 flags.set(emit_body::Flags::ASYNC, is_async);
224 emit_body::Flags::HAS_COEFFECTS_LOCAL,
225 coeffects.has_coeffects_local(),
227 emit_body::emit_body(
231 Either::Right(ast_body_block),
235 immediate_tparams: &method.tparams,
236 class_tparam_names: class_tparam_names.as_slice(),
237 ast_params: &method.params,
238 ret: method.ret.1.as_ref(),
240 deprecation_info: &deprecation_info,
241 doc_comment: method.doc_comment.clone(),
250 method::MethodType::from_ast_name_and_suffix(
253 emit_memoize_helpers::MEMOIZE_SUFFIX,
256 method::MethodType::from_ast_name(alloc, &method.name.1)
259 let is_interceptable = is_method_interceptable(emitter.options());
260 let span = if is_native_opcode_impl {
263 Span::from_pos(&method.span)
265 let mut flags = HhasMethodFlags::empty();
266 flags.set(HhasMethodFlags::IS_STATIC, method.static_);
267 flags.set(HhasMethodFlags::IS_FINAL, method.final_);
268 flags.set(HhasMethodFlags::IS_ABSTRACT, is_abstract);
269 flags.set(HhasMethodFlags::IS_ASYNC, is_async);
270 flags.set(HhasMethodFlags::IS_GENERATOR, is_generator);
271 flags.set(HhasMethodFlags::IS_PAIR_GENERATOR, is_pair_generator);
272 flags.set(HhasMethodFlags::IS_CLOSURE_BODY, is_closure_body);
273 flags.set(HhasMethodFlags::IS_INTERCEPTABLE, is_interceptable);
274 flags.set(HhasMethodFlags::IS_MEMOIZE_IMPL, is_memoize);
275 flags.set(HhasMethodFlags::NO_INJECTION, is_no_injection);
287 fn is_method_interceptable(opts: &Options) -> bool {
290 .contains(HhvmFlags::JIT_ENABLE_RENAME_FUNCTION)