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.
11 open Instruction_sequence
15 let hack_arr_dv_arrs () =
16 Hhbc_options.hack_arr_dv_arrs !Hhbc_options.compiler_options
18 let from_variadic_param_hint_opt ho
=
21 | None
-> Some
(p, A.Happly
((p, "array"), []))
22 | Some h
-> Some
(p, A.Happly
((p, "array"), [h
]))
24 let resolve_class_id ~scope cid
=
25 let cexpr = expr_to_class_expr ~resolve_self
:false scope cid
in
27 | Class_id
((p, _
) as cid
) -> p, A.Id cid
28 | Class_parent
| Class_self
| Class_static
| Class_expr _
29 | Class_unnamed_local _
-> cid
31 let resolver_visitor =
35 method! on_Class_get scope _ cid id
=
36 let cid = resolve_class_id ~scope
cid in
39 method! on_Class_const scope _
cid id
=
40 let cid = resolve_class_id ~scope
cid in
41 A.Class_const
(cid, id
)
45 let get_hint_display_name hint
=
46 Option.map hint
(fun h
->
48 | "HH\\bool" -> "bool"
50 | "HH\\varray" -> "HH\\varray"
51 | "HH\\darray" -> "HH\\darray"
52 | "HH\\varray_or_darray" -> "HH\\varray_or_darray"
53 | "HH\\vec_or_dict" -> "HH\\vec_or_dict"
54 | "HH\\arraylike" -> "HH\\arraylike"
57 | "HH\\arraykey" -> "arraykey"
58 | "HH\\float" -> "float"
59 | "HH\\string" -> "string"
63 (* By now only check default type for bool, array, int, float and string.
64 Return None when hint_type and default_value matches (in hh mode,
65 "class" type matches anything). If not, return default_value type string
66 for printing fatal parse error *)
67 let match_default_and_hint is_hh_file hint_type param_expr
=
68 let accept_any = match hint_type
with
69 | "class" -> is_hh_file
74 else match param_expr
with
75 | Some
(_
, (A.True
| A.False
)) ->
76 begin match hint_type
with
80 | Some
(_
, A.Array _
) ->
81 begin match hint_type
with
85 | "HH\\varray_or_darray" -> None
88 | Some
(_
, A.Int _
) ->
89 begin match hint_type
with
96 | Some
(_
, A.Float _
) ->
97 begin match hint_type
with
102 | Some
(_
, A.String _
) ->
103 begin match hint_type
with
111 (* Return None if it passes type check, otherwise return error msg *)
112 let default_type_check param_name param_type_info param_expr
=
114 Option.bind param_type_info
Hhas_type_info.user_type
116 let hint_type = get_hint_display_name hint in
117 (* If matches, return None, otherwise return default_type *)
118 let default_type = Option.bind
hint_type (function hint_type ->
119 match_default_and_hint
120 (Emit_env.is_hh_syntax_enabled
())
121 hint_type param_expr
)
123 let param_true_name = Hhbc_string_utils.Locals.strip_dollar param_name
125 Option.bind
default_type (function t
->
127 | Some
"class" -> Some
("Default value for parameter "
128 ^
param_true_name ^
" with a class type hint can only be NULL")
129 | Some h
-> Some
("Default value for parameter "
130 ^
param_true_name ^
" with type " ^ t ^
131 " needs to have the same type as the type hint " ^ h
)
134 let from_ast ~tparams ~namespace ~generate_defaults ~scope
p =
135 let param_name = snd
p.A.param_id
in
136 let param_is_variadic = p.Ast.param_is_variadic in
137 let param_is_inout = p.Ast.param_callconv
= Some
A.Pinout
in
138 let param_user_attributes =
139 Emit_attribute.from_asts namespace
p.Ast.param_user_attributes in
142 then from_variadic_param_hint_opt p.Ast.param_hint
143 else p.Ast.param_hint
145 let tparams = if param_is_variadic then "array"::tparams else tparams in
147 match p.A.param_expr
with
148 | Some
(_
, A.Null
) -> true
150 let param_type_info = Option.map
param_hint
151 Emit_type_hint.(hint_to_type_info
152 ~kind
:Param ~skipawaitable
:false ~
nullable ~namespace ~
tparams) in
153 (* Do the type check for default value type and hint type *)
156 match (default_type_check param_name param_type_info p.A.param_expr
) with
158 | Some s
-> Emit_fatal.raise_fatal_parse
(fst
p.A.param_id
) s
161 Option.map
p.Ast.param_expr ~f
:(resolver_visitor#on_expr scope
)
163 let param_default_value =
165 then Option.map
param_expr ~f
:(fun e
-> Label.next_default_arg
(), e
)
168 if param_is_variadic && param_name = "..." then None
else
169 Some
(Hhas_param.make
param_name p.A.param_is_reference
param_is_variadic
170 param_is_inout param_user_attributes param_type_info param_default_value)
172 let rename_params params
=
173 let names = Hh_core.List.fold_left params
174 ~init
:SSet.empty ~f
:(fun n
p -> SSet.add
(Hhas_param.name
p) n
) in
175 let rec rename param_counts param
=
176 let name = Hhas_param.name param
in
177 match SMap.get
name param_counts
with
179 (SMap.add
name 0 param_counts
, param
)
181 let param_counts = SMap.add
name (count
+ 1) param_counts in
182 let newname = name ^ string_of_int count
in
183 if SSet.mem
newname names
184 then rename param_counts param
185 else param_counts, (Hhas_param.with_name
newname param
)
187 List.rev
(snd
(Hh_core.List.map_env
SMap.empty
(List.rev params
) rename))
189 let from_asts ~namespace ~
tparams ~generate_defaults ~scope ast_params
=
190 let hhas_params = List.filter_map ast_params
191 (from_ast ~
tparams ~namespace ~generate_defaults ~scope
) in
192 rename_params hhas_params
194 let emit_param_default_value_setter ?
(is_native
= false) env pos params
=
195 let setters = List.filter_map params
(fun p ->
196 let param_name = Hhas_param.name p in
197 let dvo = Hhas_param.default_value
p in
198 let nop_requirements e
= is_native
&& snd e
= A.Null
&& begin
199 let is_optional, is_mixed
, is_callable
=
200 match Hhas_param.type_info
p with
201 | Some
{Hhas_type_info.type_info_user_type
= Some s
; _} ->
202 let is_optional = String_utils.string_starts_with s
"?" in
203 let s = String_utils.lstrip
s "?" in
204 is_optional, s = "HH\\mixed", s = "callable"
205 | _ -> false, false, false in
207 Hhas_param.is_reference
p ||
208 (is_optional && is_callable
)
211 Option.map
dvo (fun (l
, e
) ->
214 if nop_requirements e
then
217 Emit_expression.emit_expr ~need_ref
:false env e
;
218 Emit_pos.emit_pos pos
;
219 instr_setl
(Local.Named
param_name);
224 if List.is_empty
setters
227 let l = Label.next_regular
() in
228 instr_label
l, gather
[gather
setters; instr_jmpns
l]