2 * Copyright (c) 2017, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
10 open Instruction_sequence
15 module SU
= Hhbc_string_utils
17 let is_last_param_variadic param_count params
=
18 let last_p = List.nth_exn params
(param_count
- 1) in
19 Hhas_param.is_variadic
last_p
21 let emit_body_instrs_inout params call_instrs
=
22 let param_count = List.length params
in
23 let param_instrs = gather
@@
24 List.map params ~f
:(fun p
->
25 if Hhas_param.is_inout p
27 instr_cgetquietl
(Local.Named
(Hhas_param.name p
));
29 instr_popl
(Local.Named
(Hhas_param.name p
))
31 else instr_pushl
(Local.Named
(Hhas_param.name p
))
33 let inout_params = List.filter_map params ~f
:(fun p
->
34 if not
@@ Hhas_param.is_inout p
then None
else
35 Some
(instr_setl
@@ Local.Named
(Hhas_param.name p
))) in
36 let msrv = Hhbc_options.use_msrv_for_inout
!Hhbc_options.compiler_options
in
37 let local = Local.get_unnamed_local
() in
38 let has_variadic = is_last_param_variadic param_count params
in
39 let num_inout = List.length
inout_params in
40 let num_uninit = if msrv then num_inout else 0 in
42 gather
@@ List.init
num_uninit ~f
:(fun _
-> instr_nulluninit
);
45 begin match (msrv, has_variadic) with
46 | (false, false) -> instr
(ICall
(FCall
param_count))
47 | (false, true) -> instr
(ICall
(FCallUnpack
param_count))
48 | (true, false) -> instr
(ICall
(FCallM
(param_count, num_inout + 1)))
49 | (true, true) -> instr
(ICall
(FCallUnpackM
(param_count, num_inout + 1)))
51 begin if msrv then empty
else instr_unboxr_nop
end;
52 Emit_inout_helpers.emit_list_set_for_inout_call
local inout_params;
56 let emit_body_instrs_ref params call_instrs
=
57 let param_count = List.length params
in
58 let msrv = Hhbc_options.use_msrv_for_inout
!Hhbc_options.compiler_options
in
59 let param_instrs = gather
@@
60 List.map params ~f
:(fun p
->
61 let local = Local.Named
(Hhas_param.name p
) in
62 if Hhas_param.is_reference p
then
67 let param_get_instrs =
68 List.filter_map params ~f
:(fun p
->
69 if Hhas_param.is_reference p
70 then Some
(instr_cgetl
(Local.Named
(Hhas_param.name p
))) else None
) in
72 if is_last_param_variadic param_count params
73 then instr_fcallunpack
param_count
74 else instr_fcall
param_count
81 gather
param_get_instrs;
83 instr_retm
(List.length
param_get_instrs + 1)
86 instr_new_vec_array
(List.length
param_get_instrs + 1);
91 let emit_body_instrs ~wrapper_type env pos params call_instrs
=
93 match wrapper_type
with
94 | Emit_inout_helpers.InoutWrapper
->
95 emit_body_instrs_inout params call_instrs
96 | Emit_inout_helpers.RefWrapper
->
97 emit_body_instrs_ref params call_instrs
99 let begin_label, default_value_setters
=
100 Emit_param.emit_param_default_value_setter env pos params
in
103 Emit_pos.emit_pos pos
;
105 default_value_setters
;
108 (* Construct the wrapper function body *)
109 let make_wrapper_body env doc decl_vars return_type params instrs
=
110 let params = List.map
params ~f
:Hhas_param.without_type
in
111 let return_user_type = Hhas_type_info.user_type return_type
in
112 let return_tc = Hhas_type_constraint.make None
[] in
113 let return_type = Hhas_type_info.make
return_user_type return_tc in
117 false (* is_memoize_wrapper *)
120 [] (* static_inits: this is intentionally empty *)
124 let emit_wrapper_function
125 ~decl_vars ~is_top ~wrapper_type ~original_id ~renamed_id ast_fun
=
126 (* Wrapper methods may not have iterators *)
127 Iterator.reset_iterator
();
128 let scope = [Ast_scope.ScopeItem.Function ast_fun
] in
129 let namespace = ast_fun
.Ast.f_namespace
in
131 empty
|> with_namespace
namespace |> with_scope
scope
134 List.map
(Ast_scope.Scope.get_tparams
scope) (fun (_
, (_
, s
), _
) -> s
) in
135 let params = Emit_param.from_asts ~
namespace ~
tparams ~generate_defaults
:true
136 ~
scope ast_fun
.Ast.f_params
in
137 let function_attributes =
138 Emit_attribute.from_asts
namespace ast_fun
.Ast.f_user_attributes
139 |> Core_list.filter ~f
:(fun attr
-> not
(Hhas_attribute.is_native attr
)) in
140 let scope = [Ast_scope.ScopeItem.Function ast_fun
] in
141 let return_type_info =
142 Emit_body.emit_return_type_info
143 ~
scope ~skipawaitable
:false ~
namespace ast_fun
.Ast.f_ret
in
144 let param_count = List.length
params in
145 let modified_params, name
, call_instrs
=
146 if wrapper_type
= Emit_inout_helpers.InoutWrapper
then
147 List.map ~f
:Hhas_param.switch_inout_to_reference
params,
149 instr_fpushfuncd
param_count renamed_id
151 List.map ~f
:Hhas_param.switch_reference_to_inout
params,
153 instr_fpushfuncd
param_count original_id
155 Local.reset_local
@@ List.length decl_vars
+ List.length
params;
157 emit_body_instrs ~wrapper_type
env ast_fun
.Ast.f_span
params call_instrs
in
158 let fault_instrs = extract_fault_funclets
body_instrs in
159 let body_instrs = gather
[body_instrs; fault_instrs] in
160 let doc = ast_fun
.A.f_doc_comment
in
162 make_wrapper_body env doc decl_vars
return_type_info modified_params body_instrs
164 let return_by_ref = ast_fun
.Ast.f_ret_by_ref
in
165 let is_interceptable =
166 Interceptable.is_function_interceptable
namespace ast_fun
function_attributes in
171 (Hhas_pos.pos_to_span ast_fun
.Ast.f_span
)
173 false (* is_generator *)
174 false (* is_pair_generator *)
176 true (* no_injection *)
177 true (* inout_wrapper *)
180 false (* is_memoize_impl *)
182 let emit_wrapper_method
183 ~is_closure ~decl_vars ~original_id ~renamed_id ast_class ast_method
=
184 (* Wrapper methods may not have iterators *)
185 Iterator.reset_iterator
();
187 if is_closure
then List.filter
decl_vars ((<>) "$0Closure") else decl_vars in
189 [Ast_scope.ScopeItem.Method ast_method
;
190 Ast_scope.ScopeItem.Class ast_class
] in
191 let namespace = ast_class
.Ast.c_namespace
in
193 empty
|> with_namespace
namespace |> with_scope
scope
196 List.map
(Ast_scope.Scope.get_tparams
scope) (fun (_
, (_
, s
), _
) -> s
) in
197 let params = Emit_param.from_asts ~
namespace ~
tparams ~generate_defaults
:true
198 ~
scope ast_method
.Ast.m_params
in
199 let has_ref_params = List.exists
params ~f
:Hhas_param.is_reference
in
200 let method_is_abstract =
201 List.mem ast_method
.Ast.m_kind
Ast.Abstract
||
202 ast_class
.Ast.c_kind
= Ast.Cinterface
in
203 let method_is_final = List.mem ast_method
.Ast.m_kind
Ast.Final
in
204 let method_is_static = not is_closure
&&
205 List.mem ast_method
.Ast.m_kind
Ast.Static
in
206 let method_attributes = Emit_attribute.from_asts
207 (Emit_env.get_namespace
env) ast_method
.Ast.m_user_attributes
in
208 let method_is_private =
209 List.mem ast_method
.Ast.m_kind
Ast.Private
in
210 let method_is_protected =
211 List.mem ast_method
.Ast.m_kind
Ast.Protected
in
212 let method_is_public =
213 List.mem ast_method
.Ast.m_kind
Ast.Public
||
214 (not
method_is_private && not
method_is_protected) in
215 let is_in_trait = ast_class
.Ast.c_kind
= Ast.Ctrait
in
216 let return_type_info =
217 Emit_body.emit_return_type_info
218 ~
scope ~skipawaitable
:false ~
namespace ast_method
.Ast.m_ret
in
219 let method_is_return_by_ref = ast_method
.Ast.m_ret_by_ref
in
220 let param_count = List.length
params in
222 Hhbc_id.Class.from_ast_name
@@ SU.Xhp.mangle
@@ snd ast_class
.A.c_name
in
223 let wrapper_type, original_id
, renamed_id
, params =
224 if is_closure
|| has_ref_params then
225 Emit_inout_helpers.RefWrapper
, renamed_id
, original_id
,
226 List.map ~f
:Hhas_param.switch_inout_to_reference
params
228 Emit_inout_helpers.InoutWrapper
, original_id
, renamed_id
, params
234 instr_fpushfunc
param_count []
236 else if method_is_static then
238 instr_fpushclsmethodsd
param_count H.SpecialClsRef.Self renamed_id
240 instr_fpushclsmethodd
param_count renamed_id
class_name
244 instr_fpushobjmethodd
param_count renamed_id
Ast.OG_nullsafe
247 Local.reset_local
@@ List.length
decl_vars + List.length
params;
249 emit_body_instrs ~
wrapper_type env ast_method
.Ast.m_span
params call_instrs in
250 let fault_instrs = extract_fault_funclets
body_instrs in
251 let body_instrs = gather
[body_instrs; fault_instrs] in
253 if wrapper_type = Emit_inout_helpers.InoutWrapper
then
254 List.map ~f
:Hhas_param.switch_inout_to_reference
params
256 List.map ~f
:Hhas_param.switch_reference_to_inout
params
258 let doc = ast_method
.A.m_doc_comment
in
260 make_wrapper_body env doc decl_vars return_type_info params body_instrs
262 let method_is_interceptable =
263 Interceptable.is_method_interceptable
namespace ast_class original_id
method_attributes in
272 true (*method_no_injection*)
273 true (*method_inout_wrapper*)
276 (Hhas_pos.pos_to_span ast_method
.Ast.m_span
)
277 false (*method_is_async*)
278 false (*method_is_generator*)
279 false (*method_is_pair_generator*)
280 false (*method_is_closure_body*)
281 method_is_return_by_ref
282 method_is_interceptable
283 false (*method_is_memoize_impl*)