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.
12 open Instruction_sequence
16 module TC
= Hhas_type_constraint
17 module SN
= Naming_special_names
18 module SU
= Hhbc_string_utils
19 module ULS
= Unique_list_string
20 module Opts
= Hhbc_options
22 let can_inline_gen_functions () =
23 not
(Opts.jit_enable_rename_function
!Opts.compiler_options
)
25 let max_array_elem_on_stack () =
26 Hhbc_options.max_array_elem_size_on_the_stack
!Hhbc_options.compiler_options
28 type genva_inline_context
=
31 | GI_list_assignment
of Tast.expr list
33 type emit_jmp_result
= {
34 (* generated instruction sequence *)
35 instrs
: Instruction_sequence.t
;
36 (* does instruction sequence fall through *)
38 (* was label associated with emit operation used *)
42 (* Locals, array elements, and properties all support the same range of l-value
44 module LValOp
= struct
52 let jit_enable_rename_function () =
53 Hhbc_options.jit_enable_rename_function !Hhbc_options.compiler_options
55 let is_local_this env
(lid
: Aast.local_id
) : bool =
56 let id = Local_id.get_name lid
in
57 let scope = Emit_env.get_scope env
in
58 id = SN.SpecialIdents.this
59 && Ast_scope.Scope.has_this
scope
60 && not
(Ast_scope.Scope.is_toplevel
scope)
62 module InoutLocals
= struct
63 (* for every local that appear as a part of inout argument and also mutated inside
64 argument list this record stores:
65 - position of the first argument when local appears as inout
66 - position of the last argument where local is mutated.
67 Within the this range at every usage of the local must be captured to make sure
68 that later when inout arguments will be written back the same value of the
77 { first_inout
= Int.max_value
; last_write
= Int.min_value
; num_uses
= 0 }
80 if i
< r
.first_inout
then
81 { r
with first_inout
= i
}
86 if i
> r
.last_write
then
87 { r
with last_write
= i
}
91 let add_use _i r
= { r
with num_uses
= r
.num_uses
+ 1 }
93 let in_range i r
= i
> r
.first_inout
|| i
<= r
.last_write
95 let has_single_ref r
= r
.num_uses
< 2
97 let update name i f m
=
98 let r = SMap.get name m
|> Option.value ~default
:not_aliased |> f i
in
101 let add_write name i m
= update (Local_id.get_name name
) i
add_write m
103 let add_inout name i m
= update (Local_id.get_name name
) i
add_inout m
105 let add_use name i m
= update (Local_id.get_name name
) i
add_use m
107 let collect_written_variables env
(args
: Tast.expr list
) : alias_info
SMap.t
109 (* check value of the argument *)
110 let rec handle_arg ~is_top i acc
(arg
: Tast.expr
) =
113 | A.Callconv
(Ast_defs.Pinout
, (_
, A.Lvar
(_
, id)))
114 when not
(is_local_this env
id) ->
115 let acc = add_use id i
acc in
122 let acc = add_use id i
acc in
125 (* dive into argument value *)
127 (* collect lvars on the left hand side of '=' operator *)
128 and collect_lvars_lhs i
acc (e
: Tast.expr
) =
130 | A.Lvar
(_
, id) when not
(is_local_this env
id) ->
131 let acc = add_use id i
acc in
133 | A.List exprs
-> List.fold_left exprs ~f
:(collect_lvars_lhs i
) ~init
:acc
135 (* descend into expression *)
136 and dive i
(acc : alias_info
SMap.t
) expr
: alias_info
SMap.t
=
137 let state = ref acc in
140 inherit [_
] A.iter
as super
143 method! on_Binop _ bop l
r =
146 | Ast_defs.Eq
_ -> state := collect_lvars_lhs i
!state l
149 super#on_Binop
() bop l
r
152 method! on_Unop
_ op e
=
157 state := collect_lvars_lhs i
!state e
160 super#on_Unop
() op e
163 method! on_Lvar
_ (_p
, id) =
164 let _ = state := add_use id 0 !state in
165 super#on_Lvar
() (_p
, id)
167 (* f(inout $v) or f(&$v) *)
168 method! on_Call
_ _ _ _ args uargs
=
169 let f = handle_arg ~is_top
:false i
in
170 List.iter args ~
f:(fun arg
-> state := f !state arg
);
171 List.iter uargs ~
f:(fun arg
-> state := f !state arg
)
174 visitor#on_expr
() expr
;
177 List.foldi args ~
f:(handle_arg ~is_top
:true) ~init
:SMap.empty
179 (* determines if value of a local 'name' that appear in parameter 'i'
180 should be saved to local because it might be overwritten later *)
181 let should_save_local_value name i aliases
=
182 Option.value_map ~default
:false ~
f:(in_range i
) (SMap.get name aliases
)
184 let should_move_local_value local aliases
=
186 | Local.Named name
->
187 Option.value_map ~default
:true ~
f:has_single_ref (SMap.get name aliases
)
188 | Local.Unnamed
_ -> false
191 (* Describes what kind of value is intended to be stored in local *)
192 type stored_value_kind
=
194 | Value_kind_expression
196 (* represents sequence of instructions interleaved with temp locals.
197 <i, None :: rest> - is emitted i :: <rest> (commonly used for final instructions in sequence)
198 <i, Some (l, local_kind) :: rest> is emitted as
202 setl/popl l; depending on local_kind
210 type instruction_sequence_with_locals
=
211 (Instruction_sequence.t
* (Local.t
* stored_value_kind
) option) list
213 (* converts instruction_sequence_with_locals of loads into instruction_sequence.t *)
214 let rebuild_load_store load store
=
215 let rec aux = function
218 let (ld
, st
) = aux xs
in
220 | (i
, Some
(l
, kind
)) :: xs
->
221 let (ld
, st
) = aux xs
in
223 if kind
= Value_kind_expression
then
228 let unset = instr_unsetl l
in
229 (i
:: set :: ld
, unset :: st
)
231 let (ld
, st
) = aux load
in
232 (gather ld
, gather
(store
:: st
))
234 (* result of emit_array_get *)
235 type array_get_instr
=
236 (* normal $a[..] that does not need to spill anything*)
237 | Array_get_regular
of Instruction_sequence.t
238 (* subscript expression used as inout argument that need to spill intermediate
240 load - instruction_sequence_with_locals to load value
241 store - instruction to set value back (can use locals defined in load part)
243 | Array_get_inout
of {
244 load
: instruction_sequence_with_locals
;
245 store
: Instruction_sequence.t
;
248 type 'a array_get_base_data
= {
250 cls_instrs
: Instruction_sequence.t
;
251 setup_instrs
: Instruction_sequence.t
;
252 base_stack_size
: int;
256 (* result of emit_base *)
257 type array_get_base
=
258 (* normal <base> part in <base>[..] that does not need to spill anything *)
259 | Array_get_base_regular
of Instruction_sequence.t array_get_base_data
260 (* base of subscript expression used as inout argument that need to spill
261 intermediate values *)
262 | Array_get_base_inout
of {
263 (* instructions to load base part *)
264 load
: instruction_sequence_with_locals array_get_base_data
;
265 (* instruction to load base part for setting inout argument back *)
266 store
: Instruction_sequence.t
;
271 | LValOp.IncDec
_ -> true
274 let is_global_namespace env
=
275 Namespace_env.is_global_namespace (Emit_env.get_namespace env
)
277 let enable_intrinsics_extension () =
278 Hhbc_options.enable_intrinsics_extension !Hhbc_options.compiler_options
280 let optimize_null_checks () =
281 Hhbc_options.optimize_null_checks !Hhbc_options.compiler_options
283 let hack_arr_compat_notices () =
284 Hhbc_options.hack_arr_compat_notices !Hhbc_options.compiler_options
286 let hack_arr_dv_arrs () =
287 Hhbc_options.hack_arr_dv_arrs !Hhbc_options.compiler_options
289 let php7_ltr_assign () =
290 Hhbc_options.php7_ltr_assign !Hhbc_options.compiler_options
292 (* Strict binary operations; assumes that operands are already on stack *)
294 let check_int_overflow =
295 Hhbc_options.check_int_overflow !Hhbc_options.compiler_options
301 ( if check_int_overflow then
308 ( if check_int_overflow then
315 ( if check_int_overflow then
319 | Ast_defs.Slash
-> instr
(IOp Div
)
320 | Ast_defs.Eqeq
-> instr
(IOp Eq
)
321 | Ast_defs.Eqeqeq
-> instr
(IOp Same
)
322 | Ast_defs.Starstar
-> instr
(IOp Pow
)
323 | Ast_defs.Diff
-> instr
(IOp Neq
)
324 | Ast_defs.Diff2
-> instr
(IOp NSame
)
325 | Ast_defs.Lt
-> instr
(IOp Lt
)
326 | Ast_defs.Lte
-> instr
(IOp Lte
)
327 | Ast_defs.Gt
-> instr
(IOp Gt
)
328 | Ast_defs.Gte
-> instr
(IOp Gte
)
329 | Ast_defs.Dot
-> instr
(IOp Concat
)
330 | Ast_defs.Amp
-> instr
(IOp BitAnd
)
331 | Ast_defs.Bar
-> instr
(IOp BitOr
)
332 | Ast_defs.Ltlt
-> instr
(IOp Shl
)
333 | Ast_defs.Gtgt
-> instr
(IOp Shr
)
334 | Ast_defs.Cmp
-> instr
(IOp Cmp
)
335 | Ast_defs.Percent
-> instr
(IOp Mod
)
336 | Ast_defs.Xor
-> instr
(IOp BitXor
)
337 | Ast_defs.LogXor
-> instr
(IOp Xor
)
338 | Ast_defs.Eq
_ -> failwith
"assignment is emitted differently"
339 | Ast_defs.QuestionQuestion
->
340 failwith
"null coalescence is emitted differently"
343 failwith
"short-circuiting operator cannot be generated as a simple binop"
345 let binop_to_eqop op
=
346 let check_int_overflow =
347 Hhbc_options.check_int_overflow !Hhbc_options.compiler_options
352 ( if check_int_overflow then
358 ( if check_int_overflow then
364 ( if check_int_overflow then
368 | Ast_defs.Slash
-> Some DivEqual
369 | Ast_defs.Starstar
-> Some PowEqual
370 | Ast_defs.Amp
-> Some AndEqual
371 | Ast_defs.Bar
-> Some OrEqual
372 | Ast_defs.Xor
-> Some XorEqual
373 | Ast_defs.Ltlt
-> Some SlEqual
374 | Ast_defs.Gtgt
-> Some SrEqual
375 | Ast_defs.Percent
-> Some ModEqual
376 | Ast_defs.Dot
-> Some ConcatEqual
379 let unop_to_incdec_op op
=
380 let check_int_overflow =
381 Hhbc_options.check_int_overflow !Hhbc_options.compiler_options
385 if check_int_overflow then
390 if check_int_overflow then
395 if check_int_overflow then
400 if check_int_overflow then
404 | _ -> failwith
"invalid incdec op"
412 | "is_bool" -> Some OpBool
417 | "is_string" -> Some OpStr
418 | "is_array" -> Some OpArr
419 | "is_object" -> Some OpObj
420 | "is_null" -> Some OpNull
421 (* We don't use IsType with the resource type because `is_resource()` does
422 validation in addition to a simple type check. We will use it for
423 is-expressions because they only do type checks.
424 | "is_resource" -> Some OpRes *)
425 | "is_scalar" -> Some OpScalar
426 | "HH\\is_keyset" -> Some OpKeyset
427 | "HH\\is_dict" -> Some OpDict
428 | "HH\\is_vec" -> Some OpVec
431 ( if hack_arr_dv_arrs () then
437 ( if hack_arr_dv_arrs () then
441 | "HH\\is_any_array" -> Some OpArrLike
442 | "HH\\is_class_meth" -> Some OpClsMeth
443 | "HH\\is_fun" -> Some OpFunc
446 let is_isexp_op lower_fq_id
: Aast.hint
option =
447 let h n
= (Pos.none
, Aast.Happly
((Pos.none
, n
), [])) in
448 match lower_fq_id
with
453 | "is_bool" -> Some
(h "\\HH\\bool")
457 Some
(h "\\HH\\float")
458 | "is_string" -> Some
(h "\\HH\\string")
459 | "is_null" -> Some
(h "\\HH\\void")
460 | "HH\\is_keyset" -> Some
(h "\\HH\\keyset")
461 | "HH\\is_dict" -> Some
(h "\\HH\\dict")
462 | "HH\\is_vec" -> Some
(h "\\HH\\vec")
465 let get_queryMOpMode op
=
467 | QueryOp.InOut
-> MemberOpMode.InOut
468 | QueryOp.CGet
-> MemberOpMode.Warn
469 | _ -> MemberOpMode.ModeNone
471 (* Returns either Some (index, is_soft) or None *)
472 let is_reified_tparam ~
(is_fun
: bool) (env
: Emit_env.t
) (name
: string) =
473 let scope = Emit_env.get_scope env
in
476 Ast_scope.Scope.get_fun_tparams
scope
478 (Ast_scope.Scope.get_class_tparams
scope).A.c_tparam_list
481 List.exists ~
f:(function { A.ua_name
= n
; _ } ->
482 snd n
= SN.UserAttributes.uaSoft
)
489 A.tp_reified
= reified
;
490 A.tp_user_attributes
= ual
;
494 if (reified
= A.Reified
|| reified
= A.SoftReified
) && id = name
then
495 Some
(i
, is_soft ual
)
499 let extract_shape_field_name_pstring env annot
= function
500 | Ast_defs.SFlit_int s
-> A.Int
(snd s
)
501 | Ast_defs.SFlit_str s
-> A.String
(snd s
)
502 | Ast_defs.SFclass_const
(((pn
, name
) as id), p
) ->
504 Option.is_some
(is_reified_tparam ~is_fun
:true env name
)
505 || Option.is_some
(is_reified_tparam ~is_fun
:false env name
)
507 Emit_fatal.raise_fatal_parse
509 "Reified generics cannot be used in shape keys";
510 A.Class_const
((annot
, A.CI
id), p
)
512 let rec text_of_expr (e
: Tast.expr
) =
514 (* Note we force string literals to become single-quoted, regardless of
515 whether they were single- or double-quoted in the source. Gross. *)
516 | (_, A.String s
) -> "'" ^ s ^
"'"
517 | (_, A.Id
(_, id)) -> id
518 | (_, A.Lvar
(_, id)) -> Local_id.get_name
id
519 | (_, A.Array_get
((_, A.Lvar
(_, id)), Some e
)) ->
520 Local_id.get_name
id ^
"[" ^
text_of_expr e ^
"]"
522 (* TODO: get text of expression *)
525 let text_of_class_id (cid
: Tast.class_id
) =
527 | A.CIparent
-> "parent"
529 | A.CIstatic
-> "static"
530 | A.CIexpr e
-> text_of_expr e
533 let text_of_prop (prop
: Tast.class_get_expr
) =
535 | A.CGstring
(_, s
) -> s
536 | A.CGexpr e
-> text_of_expr e
538 let from_ast_null_flavor = function
539 | A.OG_nullsafe
-> Hhbc_ast.Obj_null_safe
540 | A.OG_nullthrows
-> Hhbc_ast.Obj_null_throws
542 let parse_include (e
: Tast.expr
) =
543 let strip_backslash p
=
544 let len = String.length p
in
545 if len > 0 && p
.[0] = '
/'
then
546 String.sub p
1 (len - 1)
550 let rec split_var_lit = function
551 | (_, A.Binop
(Ast_defs.Dot
, e1
, e2
)) ->
552 let (v
, l
) = split_var_lit e2
in
554 let (var
, lit
) = split_var_lit e1
in
558 | (_, A.String lit
) -> ("", lit
)
559 | e
-> (text_of_expr e
, "")
561 let (var
, lit
) = split_var_lit e
in
563 if var
= "__DIR__" then
564 ("", strip_backslash lit
)
569 if Filename.is_relative lit
then
570 Hhas_symbol_refs.SearchPathRelative lit
572 Hhas_symbol_refs.Absolute lit
574 Hhas_symbol_refs.IncludeRootRelative
(var
, strip_backslash lit
)
576 let rec expr_and_new env pos instr_to_add_new instr_to_add
= function
577 | A.AFvalue e
-> gather
[emit_expr env e
; emit_pos pos
; instr_to_add_new
]
578 | A.AFkvalue
(k
, v
) ->
579 gather
[emit_two_exprs env
(fst
@@ fst k
) k v
; instr_to_add
]
581 and get_local env
(pos
, (str
: string)) : Hhbc_ast.local_id
=
582 if str
= SN.SpecialIdents.dollardollar
then
583 match Emit_env.get_pipe_var env
with
585 Emit_fatal.raise_fatal_runtime
587 "Pipe variables must occur only in the RHS of pipe expressions"
589 else if SN.SpecialIdents.is_tmp_var str
then
590 Local.get_unnamed_local_for_tempname str
594 and emit_local ~notice env
(lid
: Aast.lid
) =
595 let (pos
, id) = lid
in
596 let str = Local_id.get_name
id in
597 if SN.Superglobals.globals
= str || SN.Superglobals.is_superglobal
str then
600 instr_string
(SU.Locals.strip_dollar
str);
605 let local = get_local env
(pos
, str) in
606 if is_local_this env
id && not
(Emit_env.get_needs_local_this env
) then
607 emit_pos_then pos
@@ instr
(IMisc
(BareThis notice
))
611 (* Emit CGetL2 for local variables, and return true to indicate that
612 * the result will be just below the top of the stack *)
613 and emit_first_expr env
(expr
: Tast.expr
) =
617 ( (is_local_this env
id && not
(Emit_env.get_needs_local_this env
))
618 || Local_id.get_name
id = SN.Superglobals.globals
619 || SN.Superglobals.is_superglobal
(Local_id.get_name
id) ) ->
620 (instr_cgetl2
(get_local env
(pos
, Local_id.get_name
id)), true)
621 | _ -> (emit_expr env expr
, false)
623 (* Special case for binary operations to make use of CGetL2 *)
624 and emit_two_exprs env
(outer_pos
: Pos.t
) (e1
: Tast.expr
) (e2
: Tast.expr
) =
625 let (instrs1
, is_under_top
) = emit_first_expr env e1
in
626 let instrs2 = emit_expr env e2
in
629 | (_, A.Lvar
_) -> true
635 if instrs2_is_var then
636 [emit_pos outer_pos
; instrs2; instrs1
]
638 [instrs2; emit_pos outer_pos
; instrs1
]
639 else if instrs2_is_var then
640 [instrs1
; emit_pos outer_pos
; instrs2]
642 [instrs1
; instrs2; emit_pos outer_pos
]
644 and emit_is_null env
(e
: Tast.expr
) =
646 | (_, A.Lvar
(pos
, id)) when not
(is_local_this env
id) ->
647 instr_istypel
(get_local env
(pos
, Local_id.get_name
id)) OpNull
648 | _ -> gather
[emit_expr env e
; instr_istypec OpNull
]
650 and emit_binop env annot op
(e1
: Tast.expr
) (e2
: Tast.expr
) =
651 let (pos
, _) = annot
in
652 let default () = gather
[emit_two_exprs env pos e1 e2
; from_binop op
] in
656 emit_short_circuit_op env annot
(A.Binop
(op
, e1
, e2
))
657 | Ast_defs.Eq None
-> emit_lval_op env pos
LValOp.Set e1
(Some e2
)
658 | Ast_defs.Eq
(Some
Ast_defs.QuestionQuestion
) ->
659 emit_null_coalesce_assignment env pos e1 e2
660 | Ast_defs.Eq
(Some obop
) ->
662 match binop_to_eqop obop
with
663 | None
-> failwith
"illegal eq op"
664 | Some op
-> emit_lval_op env pos
(LValOp.SetOp op
) e1
(Some e2
)
666 | Ast_defs.QuestionQuestion
->
667 let end_label = Label.next_regular
() in
670 fst
(emit_quiet_expr env pos e1
);
672 instr_istypec OpNull
;
674 instr_jmpnz
end_label;
677 instr_label
end_label;
680 if not
(optimize_null_checks ()) then
684 | Ast_defs.Eqeqeq
when snd e2
= A.Null
-> emit_is_null env e1
685 | Ast_defs.Eqeqeq
when snd e1
= A.Null
-> emit_is_null env e2
686 | Ast_defs.Diff2
when snd e2
= A.Null
->
687 gather
[emit_is_null env e1
; instr_not
]
688 | Ast_defs.Diff2
when snd e1
= A.Null
->
689 gather
[emit_is_null env e2
; instr_not
]
693 and get_type_structure_for_hint ~targ_map ~
tparams (h : Aast.hint
) =
694 let tv = Emit_type_constant.hint_to_type_constant ~
tparams ~targ_map
h in
695 let i = Emit_adata.get_array_identifier
tv in
696 if hack_arr_dv_arrs () then
697 instr
(ILitConst
(Dict
i))
699 instr
(ILitConst
(Array
i))
701 (* NOTE: Make sure the type structure retrieval code is synced with emit_is. *)
702 and emit_as env pos e
h is_nullable
=
705 let arg_local = Local.get_unnamed_local
() in
706 let type_struct_local = Local.get_unnamed_local
() in
707 let (ts_instrs
, is_static
) = emit_reified_arg env ~isas
:true pos
h in
708 let then_label = Label.next_regular
() in
709 let done_label = Label.next_regular
() in
710 let main_block ts_instrs resolve
=
714 instr_setl
type_struct_local;
715 instr_istypestructc resolve
;
716 instr_jmpnz
then_label;
717 ( if is_nullable
then
718 gather
[instr_null
; instr_jmp
done_label]
722 instr_pushl
arg_local;
723 instr_pushl
type_struct_local;
724 instr_throwastypestructexception
;
728 (* Set aside the argument value. *)
732 instr_setl
arg_local;
733 (* Store type struct in a variable and reuse it. *)
736 (get_type_structure_for_hint ~targ_map
:SMap.empty ~
tparams:[] h)
739 main_block ts_instrs DontResolve
);
740 instr_label
then_label;
741 instr_pushl
arg_local;
742 instr_unsetl
type_struct_local;
743 instr_label
done_label;
746 and emit_is env pos
(h : Aast.hint
) =
747 let (ts_instrs
, is_static
) = emit_reified_arg env ~isas
:true pos
h in
750 | Aast.Happly
((_, id), []) when SU.strip_hh_ns
id = SN.Typehints.this
->
755 get_type_structure_for_hint ~targ_map
:SMap.empty ~
tparams:[] h;
756 instr_istypestructc Resolve
;
759 gather
[ts_instrs
; instr_istypestructc DontResolve
]
761 and emit_cast env pos hint expr
=
764 | Aast.Happly
((_, id), []) ->
765 let id = SU.strip_ns
id in
766 let id = SU.strip_hh_ns
id in
769 | _ when id = SN.Typehints.int -> instr
(IOp CastInt
)
770 | _ when id = SN.Typehints.bool -> instr
(IOp CastBool
)
771 | _ when id = SN.Typehints.string -> instr
(IOp CastString
)
772 | _ when id = SN.Typehints.array
-> instr
(IOp CastArray
)
773 | _ when id = SN.Typehints.float -> instr
(IOp CastDouble
)
774 (* TODO: Is unset a real typehint? *)
775 | _ when id = "unset" -> gather
[instr_popc
; instr_null
]
777 Emit_fatal.raise_fatal_parse
779 ("Invalid cast type: " ^
SU.strip_global_ns
id)
781 | _ -> Emit_fatal.raise_fatal_parse pos
"Invalid cast type"
783 gather
[emit_expr env expr
; emit_pos pos
; op]
785 and emit_conditional_expression
786 env pos
(etest
: Tast.expr
) (etrue
: Tast.expr
option) (efalse
: Tast.expr
)
790 let false_label = Label.next_regular
() in
791 let end_label = Label.next_regular
() in
792 let r = emit_jmpz env etest
false_label in
796 (* only emit true branch if there is fallthrough from condition *)
798 if r.is_fallthrough
then
799 gather
[emit_expr env etrue
; emit_pos pos
; instr_jmp
end_label]
802 (* only emit false branch if false_label is used *)
804 if r.is_label_used
then
805 gather
[instr_label
false_label; emit_expr env efalse
]
808 (* end_label is used to jump out of true branch so they should be emitted
811 if r.is_fallthrough
then
812 instr_label
end_label
817 let end_label = Label.next_regular
() in
822 instr_jmpnz
end_label;
824 emit_expr env efalse
;
825 instr_label
end_label;
828 and get_erased_tparams env
=
829 Ast_scope.Scope.get_tparams
(Emit_env.get_scope env
)
830 |> List.filter_map ~
f:(function
831 | { A.tp_name
= (_, name
); A.tp_reified
; _ } ->
832 Option.some_if
(tp_reified
= A.Erased
) name
)
834 and has_non_tparam_generics env
(targs
: Aast.hint list
) =
835 let erased_tparams = get_erased_tparams env
in
836 List.exists targs ~
f:(function
837 | (_, Aast.Happly
((_, id), _))
838 when List.mem ~equal
:String.equal
erased_tparams id ->
842 and has_non_tparam_generics_targs env
(targs
: Tast.targ list
) =
843 let erased_tparams = get_erased_tparams env
in
844 List.exists targs ~
f:(function
845 | (_, (_, Aast.Happly
((_, id), _)))
846 when List.mem ~equal
:String.equal
erased_tparams id ->
850 and emit_reified_targs env pos targs
=
851 let len = List.length targs
in
852 let scope = Emit_env.get_scope env
in
853 let current_fun_tparams = Ast_scope.Scope.get_fun_tparams
scope in
854 let current_cls_tparam = Ast_scope.Scope.get_class_tparams
scope in
855 let is_in_lambda = Ast_scope.Scope.is_in_lambda (Emit_env.get_scope env
) in
856 let is_soft { A.tp_user_attributes
= ua
; _ } =
857 List.exists ua ~
f:(function { A.ua_name
= n
; _ } ->
858 snd n
= SN.UserAttributes.uaSoft
)
861 List.length tparam
= len
862 && List.for_all2_exn tparam targs ~
f:(fun tp ta
->
864 | ({ A.tp_name
= (_, name1
); _ }, (_, A.Happly
((_, name2
), []))) ->
865 name1
= name2
&& not
(is_soft tp
)
868 if (not
is_in_lambda) && is_same current_fun_tparams then
869 instr_cgetl
(Local.Named
SU.Reified.reified_generics_local_name
)
870 else if (not
is_in_lambda) && is_same current_cls_tparam.A.c_tparam_list
then
879 (Hhbc_id.Prop.from_raw_string
SU.Reified.reified_prop_name
));
881 (* TODO(T31677864): If the full generic array is static and does not require
882 * resolution, emit it as static array *)
887 @@ List.map targs ~
f:(fun h ->
888 fst
@@ emit_reified_arg env ~isas
:false pos
h);
890 ( if hack_arr_dv_arrs () then
899 (cid
: Tast.class_id
)
900 (targs
: Tast.targ list
)
901 (args
: Tast.expr list
)
902 (uargs
: Tast.expr list
) =
903 if has_inout_args args
then
904 Emit_fatal.raise_fatal_parse pos
"Unexpected inout arg in new expr";
905 let scope = Emit_env.get_scope env
in
906 (* If `new self` or `new parent `when self or parent respectively has
907 * reified generics, do not resolve *)
910 | (_, A.CIexpr
(_, A.Id
(_, n
))) when SU.is_self n
->
911 (Ast_scope.Scope.get_class_tparams
scope).A.c_tparam_list
912 |> List.for_all ~
f:(fun t
-> t
.A.tp_reified
= A.Erased
)
913 | (_, A.CIexpr
(_, A.Id
(_, n
))) when SU.is_parent n
->
914 let cls = Ast_scope.Scope.get_class
scope in
915 Option.value_map
cls ~
default:true ~
f:(fun cls ->
916 match cls.A.c_extends
with
917 | (_, Aast.Happly
(_, l
)) :: _ ->
918 not
@@ has_non_tparam_generics env l
922 let cexpr = class_id_to_class_expr ~
resolve_self scope cid
in
923 let (cexpr, has_generics
) =
925 | Class_id
(_, name
) ->
927 match emit_reified_type_opt env pos name
with
929 if not
@@ List.is_empty targs
then
930 Emit_fatal.raise_fatal_parse
932 "Cannot have higher kinded reified generics";
933 (Class_reified instrs
, H.MaybeGenerics
)
934 | None
when not
(has_non_tparam_generics_targs env targs
) ->
935 (cexpr, H.NoGenerics
)
936 | None
-> (cexpr, H.HasGenerics
)
938 | _ -> (cexpr, H.NoGenerics
)
942 (* Special case for statically-known class *)
944 let fq_id = Hhbc_id.Class.elaborate_id
id in
945 Emit_symbol_refs.add_class
(Hhbc_id.Class.to_raw_string
fq_id);
947 match has_generics
with
948 | H.NoGenerics
-> gather
[emit_pos pos
; instr_newobjd
fq_id]
953 emit_reified_targs env pos
(List.map ~
f:snd targs
);
954 instr_newobjrd
fq_id;
957 failwith
"Internal error: This case should have been transformed"
959 | Class_special cls_ref
-> gather
[emit_pos pos
; instr_newobjs cls_ref
]
960 | Class_reified instrs
when has_generics
= H.MaybeGenerics
->
961 gather
[instrs
; instr_classgetts
; instr_newobjr
]
962 | _ -> gather
[emit_load_class_ref env pos
cexpr; instr_newobj
]
964 Scope.with_unnamed_locals
966 let (instr_args
, _) = emit_args_and_inout_setters env args
in
970 | uargs
:: _ -> emit_expr env uargs
983 (get_fcall_args ~lock_while_unwinding
:true args uargs None
);
989 (* TODO(T36697624) more efficient bytecode for static records *)
990 and emit_record env pos
id is_array es
=
991 let fq_id = Hhbc_id.Class.elaborate_id
id in
994 instr_new_recordarray
998 Emit_symbol_refs.add_class
(Hhbc_id.Class.to_raw_string
fq_id);
999 emit_struct_array env pos es
(instr fq_id)
1001 and emit_clone env expr
= gather
[emit_expr env expr
; instr_clone
]
1004 env
(expr
: Tast.expr
) (fl
: (Ast_defs.shape_field_name
* Tast.expr
) list
)
1008 List.map
fl ~
f:(fun (fn
, e
) ->
1009 ((p, extract_shape_field_name_pstring env
p fn
), e
))
1011 emit_expr env
(p, A.Darray
(None
, fl))
1013 and emit_call_expr env pos e targs args uargs async_eager_label
=
1014 match (snd e
, targs
, args
, uargs
) with
1015 | (A.Id
(_, id), _, [(_, A.String data
)], [])
1016 when id = SN.SpecialFunctions.hhas_adata
->
1017 let v = Typed_value.HhasAdata data
in
1018 emit_pos_then pos
@@ instr (ILitConst
(TypedValue
v))
1019 | (A.Id
(_, id), _, _, []) when id = SN.PseudoFunctions.isset
->
1020 emit_call_isset_exprs env pos args
1021 | (A.Id
(_, id), _, [arg1
], []) when id = SN.PseudoFunctions.empty
->
1022 emit_call_empty_expr env pos arg1
1023 | (A.Id
(_, id), _, ([_; _] | [_; _; _]), [])
1024 when id = SN.FB.idx
&& not
(jit_enable_rename_function ()) ->
1025 emit_idx env pos args
1026 | (A.Id
(_, id), _, [arg1
], []) when id = SN.EmitterSpecialFunctions.eval
->
1027 emit_eval env pos arg1
1028 | (A.Id
(_, s
), _, [(_, A.String c1
); (_, A.String c2
)], [])
1029 when is_global_namespace env
&& s
= SN.EmitterSpecialFunctions.class_alias
1031 gather
[emit_pos pos
; instr_true
; instr_alias_cls c1 c2
]
1032 | (A.Id
(_, s
), _, [(_, A.String c1
); (_, A.String c2
); arg3
], [])
1033 when is_global_namespace env
&& s
= SN.EmitterSpecialFunctions.class_alias
1035 gather
[emit_expr env arg3
; emit_pos pos
; instr_alias_cls c1 c2
]
1036 | (A.Id
(_, id), _, [arg1
], [])
1037 when id = SN.EmitterSpecialFunctions.set_frame_metadata
->
1042 instr_popl
(Local.Named
"$86metadata");
1045 | (A.Id
(_, s
), _, [], [])
1046 when s
= SN.PseudoFunctions.exit
|| s
= SN.PseudoFunctions.die
->
1047 emit_pos_then pos
@@ emit_exit env None
1048 | (A.Id
(_, s
), _, [arg1
], [])
1049 when s
= SN.PseudoFunctions.exit
|| s
= SN.PseudoFunctions.die
->
1050 emit_pos_then pos
@@ emit_exit env
(Some arg1
)
1052 let instrs = emit_call env pos e targs args uargs async_eager_label
in
1053 emit_pos_then pos
instrs
1055 and emit_known_class_id
id =
1056 let fq_id = Hhbc_id.Class.elaborate_id
id in
1057 Emit_symbol_refs.add_class
(Hhbc_id.Class.to_raw_string
fq_id);
1058 gather
[instr_string
(Hhbc_id.Class.to_raw_string
fq_id); instr_classgetc
]
1060 and emit_load_class_ref env pos
cexpr =
1064 | Class_special
SpecialClsRef.Self
-> instr_self
1065 | Class_special
SpecialClsRef.Static
-> instr_lateboundcls
1066 | Class_special
SpecialClsRef.Parent
-> instr_parent
1067 | Class_id
id -> emit_known_class_id
id
1068 | Class_expr expr
->
1069 gather
[emit_pos pos
; emit_expr env expr
; instr_classgetc
]
1070 | Class_reified
instrs -> gather
[emit_pos pos
; instrs; instr_classgetc
]
1072 and emit_load_class_const env pos
(cexpr : Ast_class_expr.class_expr
) id =
1074 if SU.is_class
id then
1075 instr (IMisc ClassName
)
1077 instr (ILitConst
(ClsCns
(Hhbc_id.Const.from_ast_name
id)))
1079 gather
[emit_load_class_ref env pos
cexpr; load_const]
1082 env
(cexpr : Ast_class_expr.class_expr
) (prop
: Tast.class_get_expr
) =
1085 | A.CGstring
(pos
, id) ->
1086 emit_pos_then pos
@@ instr_string
(SU.Locals.strip_dollar
id)
1087 | A.CGexpr e
-> emit_expr env e
1090 let cexpr_local = emit_expr env e
in
1095 Scope.stash_top_in_unnamed_local
load_prop;
1101 ((_, (A.BracedExpr
_ | A.Call
_ | A.Binop
_ | A.Class_get
_)) as e
) ->
1103 | Class_expr
((_, A.Lvar
(_, id)) as e
) when Local_id.get_name
id = "$this"
1109 | A.CGstring
(pos, _) -> pos
1110 | A.CGexpr
((pos, _), _) -> pos
1112 (load_prop (), emit_load_class_ref env
pos cexpr)
1114 and emit_class_get env qop
(cid
: Tast.class_id
) (prop
: Tast.class_get_expr
) =
1116 class_id_to_class_expr ~
resolve_self:false (Emit_env.get_scope env
) cid
1120 of_pair
@@ emit_class_expr env
cexpr prop
;
1122 | QueryOp.CGet
-> instr_cgets
1123 | QueryOp.CGetQuiet
-> failwith
"emit_class_get: CGetQuiet"
1124 | QueryOp.Isset
-> instr_issets
1125 | QueryOp.Empty
-> instr_emptys
1126 | QueryOp.InOut
-> failwith
"emit_class_get: InOut");
1129 (* Class constant <cid>::<id>.
1130 * We follow the logic for the Construct::KindOfClassConstantExpression
1131 * case in emitter.cpp
1133 and emit_class_const env
pos (cid
: Tast.class_id
) (_, id) :
1134 Instruction_sequence.t
=
1136 class_id_to_class_expr ~
resolve_self:true (Emit_env.get_scope env
) cid
1140 | Class_id
(_, name
) ->
1141 Option.value ~
default:cexpr (get_reified_var_cexpr env
pos name
)
1145 | Class_id cid
-> emit_class_const_impl cid
id
1146 | _ -> emit_load_class_const env
pos cexpr id
1148 and emit_class_const_impl cid
id =
1149 let fq_id = Hhbc_id.Class.elaborate_id cid
in
1150 let fq_id_str = Hhbc_id.Class.to_raw_string
fq_id in
1151 emit_pos_then
(fst cid
)
1153 if SU.is_class
id then
1154 instr_string
fq_id_str
1156 Emit_symbol_refs.add_class
fq_id_str;
1157 instr (ILitConst
(ClsCnsD
(Hhbc_id.Const.from_ast_name
id, fq_id)))
1160 and emit_yield env
pos = function
1161 | A.AFvalue e
-> gather
[emit_expr env e
; emit_pos
pos; instr_yield
]
1162 | A.AFkvalue
(e1
, e2
) ->
1163 gather
[emit_expr env e1
; emit_expr env e2
; emit_pos
pos; instr_yieldk
]
1165 and emit_string2 env
pos (exprs
: Tast.expr list
) =
1167 | [e
] -> gather
[emit_expr env e
; emit_pos
pos; instr (IOp CastString
)]
1171 emit_two_exprs env
(fst
(fst e1
)) e1 e2
;
1175 (List.map es
(fun e
->
1176 gather
[emit_expr env e
; emit_pos
pos; instr (IOp Concat
)]));
1178 | [] -> failwith
"String2 with zero arguments is impossible"
1180 and emit_lambda
(env
: Emit_env.t
) (fundef
: Tast.fun_
) (ids
: Aast.lid list
) =
1181 (* Closure conversion puts the class number used for CreateCl in the "name"
1182 * of the function definition *)
1183 let fundef_name = snd fundef
.A.f_name
in
1184 let class_num = int_of_string
fundef_name in
1185 let explicit_use = SSet.mem
fundef_name (Emit_env.get_explicit_use_set
()) in
1186 let is_in_lambda = Ast_scope.Scope.is_in_lambda (Emit_env.get_scope env
) in
1190 @@ List.map ids
(fun (pos, id) ->
1191 match SU.Reified.is_captured_generic
@@ Local_id.get_name
id with
1192 | Some
(is_fun
, i) ->
1193 if is_in_lambda then
1196 (SU.Reified.reified_generic_captured_name is_fun
i))
1198 emit_reified_generic_instrs
Pos.none ~is_fun
i
1200 let lid = get_local env
(pos, Local_id.get_name
id) in
1201 if explicit_use then
1205 instr (IMisc
(CreateCl
(List.length ids
, class_num)));
1208 and emit_id
(env
: Emit_env.t
) ((p, s
) : Aast.sid
) =
1209 match String.uppercase s
with
1210 | "__FILE__" -> instr (ILitConst File
)
1211 | "__DIR__" -> instr (ILitConst Dir
)
1212 | "__CLASS__" -> gather
[instr_self
; instr_classname
]
1213 | "__METHOD__" -> instr (ILitConst Method
)
1214 | "__FUNCTION_CREDENTIAL__" -> instr (ILitConst FuncCred
)
1216 (* If the expression goes on multi lines, we return the last line *)
1217 let (_, line
, _, _) = Pos.info_pos_extended
p in
1219 | "__NAMESPACE__" ->
1220 let ns = Emit_env.get_namespace env
in
1221 instr_string
(Option.value ~
default:"" ns.Namespace_env.ns_name
)
1222 | "__COMPILER_FRONTEND__" -> instr_string
"hackc"
1227 let fq_id = Hhbc_id.Const.from_ast_name s
in
1228 Emit_symbol_refs.add_constant s
;
1229 emit_pos_then
p @@ instr (ILitConst
(CnsE
fq_id))
1231 and rename_xhp
(p, s
) = (p, SU.Xhp.mangle s
)
1233 and emit_xhp
(env
: Emit_env.t
) annot
id attributes
(children
: Tast.expr list
)
1235 (* Translate into a constructor call. The arguments are:
1236 * 1) struct-like array of attributes
1237 * 2) vec-like array of children
1238 * 3) filename, for debugging
1239 * 4) line number, for debugging
1241 * Spread operators are injected into the attributes array with placeholder
1242 * keys that the runtime will interpret as a spread. These keys are not
1243 * parseable as user-specified attributes, so they will never collide.
1245 let (pos, _) = annot
in
1246 let create_spread p id = (p, "...$" ^ string_of_int
id) in
1247 let convert_attr (spread_id
, attrs
) = function
1248 | A.Xhp_simple
(name
, v) ->
1249 let attr = (Ast_defs.SFlit_str name
, v) in
1250 (spread_id
, attr :: attrs
)
1252 let ((p, _), _) = e
in
1253 let attr = (Ast_defs.SFlit_str
(create_spread p spread_id
), e
) in
1254 (spread_id
+ 1, attr :: attrs
)
1256 let (_, attributes
) =
1257 List.fold_left ~
f:convert_attr ~init
:(0, []) attributes
1259 let attribute_map = (annot
, A.Shape
(List.rev attributes
)) in
1260 let children_vec = (annot
, A.Varray
(None
, children
)) in
1261 let filename = (annot
, A.Id
(pos, "__FILE__")) in
1262 let line = (annot
, A.Id
(pos, "__LINE__")) in
1263 let renamed_id = rename_xhp
id in
1264 Emit_symbol_refs.add_class
(snd
renamed_id);
1268 ( (annot
, A.CI
renamed_id),
1270 [attribute_map; children_vec; filename; line],
1274 and emit_import env annot
(flavor
: Aast.import_flavor
) (e
: Tast.expr
) =
1275 let (pos, _) = annot
in
1276 let inc = parse_include e
in
1277 Emit_symbol_refs.add_include
inc;
1278 let (e
, import_op
) =
1280 | Aast.Include
-> (e
, IIncludeEvalDefine Incl
)
1281 | Aast.Require
-> (e
, IIncludeEvalDefine Req
)
1282 | Aast.IncludeOnce
-> (e
, IIncludeEvalDefine InclOnce
)
1283 | Aast.RequireOnce
->
1285 Hhbc_options.include_roots !Hhbc_options.compiler_options
1288 Hhas_symbol_refs.resolve_to_doc_root_relative
inc ~
include_roots
1290 | Hhas_symbol_refs.DocRootRelative path
->
1291 ((annot
, A.String path
), IIncludeEvalDefine ReqDoc
)
1292 | _ -> (e
, IIncludeEvalDefine ReqOnce
))
1294 gather
[emit_expr env e
; emit_pos
pos; instr import_op
]
1296 and emit_call_isset_expr env outer_pos
(expr
: Tast.expr
) =
1297 let ((pos, _), expr_
) = expr
in
1299 | A.Array_get
((_, A.Lvar
(_, x
)), Some e
)
1300 when Local_id.get_name x
= SN.Superglobals.globals
->
1301 gather
[emit_expr env e
; emit_pos outer_pos
; instr_issetg
]
1302 | A.Array_get
(base_expr
, opt_elem_expr
) ->
1303 fst
(emit_array_get env
pos QueryOp.Isset base_expr opt_elem_expr
)
1304 | A.Class_get
(cid
, id) -> emit_class_get env
QueryOp.Isset cid
id
1305 | A.Obj_get
(expr
, prop
, nullflavor
) ->
1306 fst
(emit_obj_get env
pos QueryOp.Isset expr prop nullflavor
)
1308 when SN.Superglobals.is_superglobal
(Local_id.get_name n
)
1309 || Local_id.get_name n
= SN.Superglobals.globals
->
1313 instr_string
@@ SU.Locals.strip_dollar
(Local_id.get_name n
);
1317 | A.Lvar
((_, name
) as id)
1318 when is_local_this env name
&& not
(Emit_env.get_needs_local_this env
) ->
1322 emit_local ~notice
:NoNotice env
id;
1324 instr_istypec OpNull
;
1327 | A.Lvar
(pos, id) ->
1328 emit_pos_then outer_pos
1329 @@ instr (IIsset
(IssetL
(get_local env
(pos, Local_id.get_name
id))))
1330 | _ -> gather
[emit_expr env expr
; instr_istypec OpNull
; instr_not
]
1332 and emit_call_empty_expr env outer_pos
((annot
, expr_
) as expr
) =
1333 let (pos, _) = annot
in
1335 | A.Array_get
((_, A.Lvar
(_, x
)), Some e
)
1336 when Local_id.get_name x
= SN.Superglobals.globals
->
1337 gather
[emit_expr env e
; emit_pos outer_pos
; instr_emptyg
]
1338 | A.Array_get
(base_expr
, opt_elem_expr
) ->
1339 fst
(emit_array_get env
pos QueryOp.Empty base_expr opt_elem_expr
)
1340 | A.Class_get
(cid
, id) -> emit_class_get env
QueryOp.Empty cid
id
1341 | A.Obj_get
(expr
, prop
, nullflavor
) ->
1342 fst
(emit_obj_get env
pos QueryOp.Empty expr prop nullflavor
)
1344 when SN.Superglobals.is_superglobal
(Local_id.get_name
id)
1345 || Local_id.get_name
id = SN.Superglobals.globals
->
1348 instr_string
@@ SU.Locals.strip_dollar
(Local_id.get_name
id);
1352 | A.Lvar
(pos, id) ->
1353 if (not
(is_local_this env
id)) || Emit_env.get_needs_local_this env
then
1354 emit_pos_then outer_pos
1355 @@ instr_emptyl
(get_local env
(pos, Local_id.get_name
id))
1360 instr (IMisc
(BareThis NoNotice
));
1364 | _ -> gather
[emit_expr env expr
; instr_not
]
1366 and emit_unset_expr env expr
=
1367 emit_lval_op_nonlist env
(fst
(fst expr
)) LValOp.Unset expr empty
0
1369 and emit_set_range_expr env
pos name kind args
=
1370 let raise_fatal msg
=
1371 Emit_fatal.raise_fatal_parse
pos (Printf.sprintf
"%s %s" name msg
)
1373 let (range_op
, size
, allow_count
) = kind
in
1374 let (base
, offset
, src
, args
) =
1376 | b
:: o
:: s
:: rest
-> (b
, o
, s
, rest
)
1377 | _ -> raise_fatal "expects at least 3 arguments"
1380 match (args
, allow_count
) with
1381 | ([c
], true) -> emit_expr env c
1382 | ([], _) -> instr_int
(-1)
1383 | (_, false) -> raise_fatal "expects no more than 3 arguments"
1384 | (_, true) -> raise_fatal "expects no more than 4 arguments"
1386 let (base_expr
, cls_expr
, base_setup
, base_stack
, cls_stack
) =
1387 emit_base ~notice
:Notice ~is_object
:false env
MemberOpMode.Define
3 3 base
1393 emit_expr env offset
;
1397 instr (IFinal
(SetRangeM
(base_stack
+ cls_stack
, range_op
, size
)));
1400 and emit_call_isset_exprs env
pos (exprs
: Tast.expr list
) =
1403 Emit_fatal.raise_fatal_parse
pos "Cannot use isset() without any arguments"
1404 | [expr
] -> emit_call_isset_expr env
pos expr
1406 let n = List.length exprs
in
1407 let its_done = Label.next_regular
() in
1411 @@ List.mapi exprs
(fun i expr
->
1414 emit_call_isset_expr env
pos expr
;
1416 gather
[instr_dup
; instr_jmpz
its_done; instr_popc
]
1420 instr_label
its_done;
1423 and emit_exit env
(expr_opt
: Tast.expr
option) =
1426 (match expr_opt
with
1427 | None
-> instr_int
0
1428 | Some e
-> emit_expr env e
);
1432 and emit_idx env
pos (es
: Tast.expr list
) =
1434 if List.length es
= 2 then
1439 gather
[emit_exprs env es
; emit_pos
pos; default; instr_idx
]
1441 and emit_eval env
pos e
= gather
[emit_expr env e
; emit_pos
pos; instr_eval
]
1443 and emit_xhp_obj_get env
pos (_, ty
) (e
: Tast.expr
) s nullflavor
=
1444 let annot = (pos, ty
) in
1446 (annot, A.Obj_get
(e
, (annot, A.Id
(pos, "getAttribute")), nullflavor
))
1448 let args = [(annot, A.String
(SU.Xhp.clean s
))] in
1449 emit_call env
pos fn_name [] args [] None
1451 and try_inline_gen_call env
(e
: Tast.expr
) =
1452 if not
(can_inline_gen_functions ()) then
1456 | A.Call
(_, (_, A.Id
(_, s
)), _, [arg
], [])
1457 when SU.strip_global_ns s
= "gena" ->
1458 Some
(inline_gena_call env arg
)
1461 (* emits iteration over the ~collection where loop body is
1463 and emit_iter ~collection
f =
1464 Scope.with_unnamed_locals_and_iterators
1466 let iter = Iterator.get_iterator
() in
1467 let value_local = Local.get_unnamed_local
() in
1468 let key_local = Local.get_unnamed_local
() in
1469 let loop_end = Label.next_regular
() in
1470 let loop_next = Label.next_regular
() in
1472 gather
[collection
; instr_iterinitk
iter loop_end value_local key_local]
1477 instr_label
loop_next;
1478 f value_local key_local;
1479 instr_iternextk
iter loop_next value_local key_local;
1484 [instr_unsetl
value_local; instr_unsetl
key_local; instr_label
loop_end]
1486 (iter_init, iterate, iter_done)
1488 and inline_gena_call env
(arg
: Tast.expr
) =
1491 (* convert input to array *)
1492 let load_array = emit_expr env arg
in
1493 Scope.with_unnamed_local
1495 let async_eager_label = Label.next_regular
() in
1500 ( if hack_arr_dv_arrs () then
1503 instr_cast_darray
);
1504 instr_popl arr_local
;
1512 instr_cgetl arr_local
;
1513 instr_fcallclsmethodd
1514 (make_fcall_args ~
async_eager_label 1)
1515 (Hhbc_id.Method.from_raw_string
1516 ( if hack_arr_dv_arrs () then
1520 (Hhbc_id.Class.from_raw_string
"HH\\AwaitAllWaitHandle");
1522 instr_label
async_eager_label;
1524 ( emit_iter ~collection
:(instr_cgetl arr_local
)
1525 @@ fun value_local key_local ->
1528 (* generate code for
1529 arr_local[key_local] = WHResult (value_local) *)
1530 instr_cgetl
value_local;
1532 instr_basel arr_local
MemberOpMode.Define
;
1533 instr_setm
0 (MemberKey.EL
key_local);
1538 instr_pushl arr_local
)
1540 and emit_await env
pos (expr
: Tast.expr
) =
1541 match try_inline_gen_call env expr
with
1544 let after_await = Label.next_regular
() in
1547 | A.Call
(_, e
, targs
, args, uargs
) ->
1548 emit_call_expr env
pos e targs
args uargs
(Some
after_await)
1549 | _ -> emit_expr env expr
1556 instr_istypec OpNull
;
1557 instr_jmpnz
after_await;
1559 instr_label
after_await;
1562 and emit_callconv _env kind _e
=
1564 | Ast_defs.Pinout
->
1565 failwith
"emit_callconv: This should have been caught at emit_arg"
1567 and get_reified_var_cexpr env
pos name
: Ast_class_expr.class_expr
option =
1568 match emit_reified_type_opt env
pos name
with
1576 instr_basec
0 MemberOpMode.Warn
;
1577 instr_querym
1 QueryOp.CGet
(MemberKey.ET
"classname");
1580 and emit_reified_generic_instrs
pos ~is_fun index
=
1584 (Local.Named
SU.Reified.reified_generics_local_name
)
1592 @@ Hhbc_id.Prop.from_raw_string
SU.Reified.reified_prop_name
;
1597 [base; instr_querym
0 QueryOp.CGet
(MemberKey.EI
(Int64.of_int index
))]
1599 and emit_reified_type_opt
(env
: Emit_env.t
) pos name
=
1600 let is_in_lambda = Ast_scope.Scope.is_in_lambda (Emit_env.get_scope env
) in
1601 let cget_instr is_fun
i =
1603 (Local.Named
(SU.Reified.reified_generic_captured_name is_fun
i))
1609 Emit_fatal.raise_fatal_parse
1612 ^
" is annotated to be a soft reified generic,"
1613 ^
" it cannot be used until the __Soft annotation is removed" )
1615 let rec aux ~is_fun
=
1616 match is_reified_tparam ~is_fun env name
with
1617 | Some
(i, is_soft) ->
1620 ( if is_in_lambda then
1623 emit_reified_generic_instrs
pos ~is_fun
i )
1632 and emit_reified_type env
pos name
=
1633 match emit_reified_type_opt env
pos name
with
1634 | Some
instrs -> instrs
1635 | None
-> Emit_fatal.raise_fatal_runtime
Pos.none
"Invalid reified param"
1637 and emit_expr
(env
: Emit_env.t
) (expr
: Tast.expr
) =
1638 let (((pos, _) as annot), expr_
) = expr
in
1647 Ast_constant_folder.expr_to_typed_value
(Emit_env.get_namespace env
) expr
1649 emit_pos_then
pos @@ instr (ILitConst
(TypedValue
v))
1650 | A.PrefixedString
(_, e
)
1651 | A.ParenthesizedExpr e
->
1653 | A.Lvar
((pos, _) as lid) ->
1654 gather
[emit_pos
pos; emit_local ~notice
:Notice env
lid]
1655 | A.Class_const
(cid
, id) -> emit_class_const env
pos cid
id
1656 | A.Unop
(op, e
) -> emit_unop env
pos op e
1657 | A.Binop
(op, e1
, e2
) -> emit_binop env
annot op e1 e2
1658 | A.Pipe
(_, e1
, e2
) -> emit_pipe env e1 e2
1659 | A.Is
(e
, h) -> gather
[emit_expr env e
; emit_is env
pos h]
1660 | A.As
(e
, h, is_nullable
) -> emit_as env
pos e
h is_nullable
1661 | A.Cast
((_, hint
), e
) -> emit_cast env
pos hint e
1662 | A.Eif
(etest
, etrue
, efalse
) ->
1663 emit_conditional_expression env
pos etest etrue efalse
1664 | A.Expr_list es
-> gather
@@ List.map es ~
f:(emit_expr env
)
1665 | A.Array_get
((_, A.Lvar
(_, x
)), Some e
)
1666 when Local_id.get_name x
= SN.Superglobals.globals
->
1667 gather
[emit_expr env e
; emit_pos
pos; instr (IGet CGetG
)]
1668 | A.Array_get
(base_expr
, opt_elem_expr
) ->
1669 fst
(emit_array_get env
pos QueryOp.CGet base_expr opt_elem_expr
)
1670 | A.Obj_get
(expr
, prop
, nullflavor
) ->
1671 fst
(emit_obj_get env
pos QueryOp.CGet expr prop nullflavor
)
1672 | A.Call
(_, e
, targs
, args, uargs
) ->
1673 emit_call_expr env
pos e targs
args uargs None
1674 | A.New
(cid
, targs
, args, uargs
, _constructor_annot
) ->
1675 emit_new env
pos cid targs
args uargs
1676 | A.Record
(cid
, is_array
, es
) ->
1677 let es2 = List.map ~
f:(fun (e1
, e2
) -> A.AFkvalue
(e1
, e2
)) es
in
1678 emit_record env
pos cid is_array
es2
1679 | A.Array es
-> emit_pos_then
pos @@ emit_collection env expr es
1680 | A.Darray
(ta
, es
) ->
1683 let es2 = List.map ~
f:(fun (e1
, e2
) -> A.AFkvalue
(e1
, e2
)) es
in
1684 let darray_e = (fst expr
, A.Darray
(ta
, es
)) in
1685 emit_collection env
darray_e es2
1686 | A.Varray
(ta
, es
) ->
1689 let es2 = List.map ~
f:(fun e
-> A.AFvalue e
) es
in
1690 let varray_e = (fst expr
, A.Varray
(ta
, es
)) in
1691 emit_collection env
varray_e es2
1692 | A.Collection
((pos, name
), _, fields
) ->
1693 emit_named_collection_str env expr
pos name fields
1694 | A.ValCollection
(name
, _, el
) ->
1695 let fields = List.map el ~
f:(fun e
-> A.AFvalue e
) in
1696 let emit n = emit_named_collection env expr
pos n fields in
1698 | A.Vector
-> emit CollectionType.Vector
1699 | A.ImmVector
-> emit CollectionType.ImmVector
1700 | A.Set
-> emit CollectionType.Set
1701 | A.ImmSet
-> emit CollectionType.ImmSet
1702 | _ -> emit_collection env expr
fields)
1703 | A.Pair
(e1
, e2
) ->
1704 let fields = [A.AFvalue e1
; A.AFvalue e2
] in
1705 emit_named_collection env expr
pos CollectionType.Pair
fields
1706 | A.KeyValCollection
(name
, _, fields) ->
1707 let fields = List.map
fields ~
f:(fun (e1
, e2
) -> A.AFkvalue
(e1
, e2
)) in
1708 let emit n = emit_named_collection env expr
pos n fields in
1710 | A.Map
-> emit CollectionType.Map
1711 | A.ImmMap
-> emit CollectionType.ImmMap
1712 | _ -> emit_collection env expr
fields)
1713 | A.Clone e
-> emit_pos_then
pos @@ emit_clone env e
1714 | A.Shape
fl -> emit_pos_then
pos @@ emit_shape env expr
fl
1715 | A.Await e
-> emit_await env
pos e
1716 | A.Yield e
-> emit_yield env
pos e
1717 | A.Yield_break
-> failwith
"yield break should be in statement position"
1718 | A.Yield_from
_ -> failwith
"complex yield_from expression"
1721 "expected Lfun to be converted to Efun during closure conversion emit_expr"
1722 | A.Efun
(fundef
, ids
) -> emit_pos_then
pos @@ emit_lambda env fundef ids
1723 | A.Class_get
(cid
, id) -> emit_class_get env
QueryOp.CGet cid
id
1724 | A.String2 es
-> emit_string2 env
pos es
1725 | A.BracedExpr e
-> emit_expr env e
1726 | A.Id
id -> emit_pos_then
pos @@ emit_id env
id
1727 | A.Xml
(id, attributes
, children
) ->
1728 emit_xhp env
(fst expr
) id attributes children
1729 | A.Callconv
(kind
, e
) -> emit_callconv env kind e
1730 | A.Import
(flavor
, e
) -> emit_import env
annot flavor e
1731 | A.Omitted
-> empty
1732 | A.Suspend
_ -> failwith
"Codegen for 'suspend' operator is not supported"
1734 Emit_fatal.raise_fatal_parse
1736 "list() can only be used as an lvar. Did you mean to use tuple()?"
1737 | A.Any
-> failwith
"Cannot codegen from an Any node"
1740 | A.Dollardollar
_ ->
1741 failwith
"TODO Codegen after naming pass on AAST"
1742 | A.Typename
_ -> failwith
"Typename should not occur in expressions"
1743 | A.ImmutableVar
_ -> failwith
"Codegen for 'let' is not supported"
1745 | A.PU_identifier
_ ->
1746 failwith
"TODO(T35357243): Pocket Universes syntax must be erased by now"
1747 | A.Fun_id
_ -> failwith
"TODO Unimplemented expression Fun"
1748 | A.Method_id
(_, _) -> failwith
"TODO Unimplemented expression Method"
1749 | A.Method_caller
(_, _) -> failwith
"TODO Unimplemented expression Method"
1750 | A.Smethod_id
(_, _) -> failwith
"TODO Unimplemented expression Smethod"
1751 | A.Special_func
_ -> failwith
"TODO Unimplemented expression Special"
1752 | A.Assert
_ -> failwith
"TODO Unimplemented expression Assert"
1754 and emit_static_collection env ~transform_to_collection
pos tv =
1755 let arrprov_enabled =
1756 Hhbc_options.array_provenance
!Hhbc_options.compiler_options
1758 let transform_instr =
1759 match transform_to_collection
with
1760 | Some collection_type
-> instr_colfromarray collection_type
1764 gather
[emit_pos
pos; instr (ILitConst
(TypedValue
tv)); transform_instr]
1766 (* This is a nasty hack to make sure that static arrays in a PSF function are tagged
1767 * using dynamic provenance information *)
1770 && Ast_scope.Scope.has_function_attribute
1771 (Emit_env.get_scope env
)
1772 "__ProvenanceSkipFrame"
1782 (Hhbc_id.Function.from_raw_string
"HH\\tag_provenance_here");
1787 and emit_value_only_collection env
pos es constructor
=
1788 let limit = max_array_elem_on_stack () in
1793 @@ List.map exprs ~
f:(function
1797 -> emit_expr env e
);
1799 instr @@ ILitConst
(constructor
@@ List.length exprs
);
1802 let outofline exprs
=
1804 @@ List.map exprs ~
f:(function
1808 -> gather
[emit_expr env e
; instr_add_new_elemc
])
1810 match List.groupi ~break
:(fun i _ _ -> i = limit) es
with
1813 | x1
:: x2
:: _ -> gather
[inline x1
; outofline x2
]
1815 and emit_keyvalue_collection ctype env
pos es constructor
=
1816 let (transform_instr, add_elem_instr
) =
1818 | CollectionType.Dict
1819 | CollectionType.Array
->
1820 (empty
, instr_add_new_elemc
)
1821 | _ -> (instr_colfromarray ctype
, gather
[instr_dup
; instr_add_elemc
])
1826 instr (ILitConst constructor
);
1828 (List.map es ~
f:(expr_and_new env
pos add_elem_instr instr_add_elemc
));
1833 and emit_struct_array env
pos es ctor
=
1835 List.map
es ~
f:(function
1836 | A.AFkvalue
(k
, v) ->
1837 let ns = Emit_env.get_namespace env
in
1838 (* TODO: Consider reusing folded keys from is_struct_init *)
1840 match snd
@@ Ast_constant_folder.fold_expr
ns k
with
1841 | A.String s
-> (s
, emit_expr env
v)
1842 | _ -> failwith
"Key must be a string"
1844 | _ -> failwith
"impossible")
1847 [gather
@@ List.map
es ~
f:snd
; emit_pos
pos; ctor
@@ List.map
es ~
f:fst
]
1849 (* isPackedInit() returns true if this expression list looks like an
1850 * array with no keys and no ref values *)
1851 and is_packed_init ?
(hack_arr_compat
= true) es =
1852 let is_only_values =
1853 List.for_all
es ~
f:(function
1854 | A.AFkvalue
_ -> false
1857 let keys_are_zero_indexed_properly_formed =
1858 List.foldi
es ~init
:true ~
f:(fun i b
f ->
1862 | A.AFkvalue
((_, A.Int k
), _) -> int_of_string k
= i
1863 (* arrays with int-like string keys are still considered packed
1864 and should be emitted via NewArray *)
1865 | A.AFkvalue
((_, A.String k
), _) when not hack_arr_compat
->
1866 (try int_of_string k
= i with Failure
_ -> false)
1867 (* True and False are considered 1 and 0, respectively *)
1868 | A.AFkvalue
((_, A.True
), _) -> i = 1
1869 | A.AFkvalue
((_, A.False
), _) -> i = 0
1870 | A.AFvalue
_ -> true
1874 List.exists
es ~
f:(function
1875 | A.AFkvalue
((_, (A.True
| A.False
)), _) -> true
1878 (is_only_values || keys_are_zero_indexed_properly_formed)
1879 && (not
(has_bool_keys && hack_arr_compat
&& hack_arr_compat_notices ()))
1880 && List.length
es > 0
1882 and is_struct_init env
es allow_numerics
=
1883 let keys = ULS.empty
in
1884 let (are_all_keys_non_numeric_strings
, keys) =
1885 List.fold_right
es ~init
:(true, keys) ~
f:(fun field
(b
, keys) ->
1887 | A.AFkvalue
(key
, _) ->
1888 let ns = Emit_env.get_namespace env
in
1890 match snd
@@ Ast_constant_folder.fold_expr
ns key
with
1894 @@ Typed_value.string_to_int_opt
1895 ~allow_following
:false
1899 | _ -> (false, keys)
1901 | _ -> (false, keys))
1903 let num_keys = List.length
es in
1904 let has_duplicate_keys = ULS.cardinal
keys <> num_keys in
1905 let limit = max_array_elem_on_stack () in
1906 (allow_numerics
|| are_all_keys_non_numeric_strings
)
1907 && (not
has_duplicate_keys)
1908 && num_keys <= limit
1911 (* transform_to_collection argument keeps track of
1912 * what collection to transform to *)
1913 and emit_dynamic_collection env
(expr
: Tast.expr
) es =
1914 let ((pos, _), expr_
) = expr
in
1915 let count = List.length
es in
1916 let emit_collection_helper ctype
=
1917 if is_struct_init env
es true then
1920 emit_struct_array env
pos es instr_newstructdict
;
1922 instr_colfromarray ctype
;
1925 emit_keyvalue_collection ctype env
pos es (NewDictArray
count)
1928 | A.ValCollection
(A.Vec
, _, _)
1929 | A.Collection
((_, "vec"), _, _) ->
1930 emit_value_only_collection env
pos es (fun n -> NewVecArray
n)
1931 | A.ValCollection
(A.Keyset
, _, _)
1932 | A.Collection
((_, "keyset"), _, _) ->
1933 emit_value_only_collection env
pos es (fun n -> NewKeysetArray
n)
1934 | A.KeyValCollection
(A.Dict
, _, _)
1935 | A.Collection
((_, "dict"), _, _) ->
1936 if is_struct_init env
es true then
1937 emit_struct_array env
pos es instr_newstructdict
1939 emit_keyvalue_collection
1944 (NewDictArray
count)
1945 | A.Collection
((_, name
), _, _) when SU.strip_ns name
= "Set" ->
1946 emit_collection_helper CollectionType.Set
1947 | A.ValCollection
(A.Set
, _, _) -> emit_collection_helper CollectionType.Set
1948 | A.Collection
((_, name
), _, _) when SU.strip_ns name
= "ImmSet" ->
1949 emit_collection_helper CollectionType.ImmSet
1950 | A.ValCollection
(A.ImmSet
, _, _) ->
1951 emit_collection_helper CollectionType.ImmSet
1952 | A.Collection
((_, name
), _, _) when SU.strip_ns name
= "Map" ->
1953 emit_collection_helper CollectionType.Map
1954 | A.KeyValCollection
(A.Map
, _, _) ->
1955 emit_collection_helper CollectionType.Map
1956 | A.Collection
((_, name
), _, _) when SU.strip_ns name
= "ImmMap" ->
1957 emit_collection_helper CollectionType.ImmMap
1958 | A.KeyValCollection
(A.ImmMap
, _, _) ->
1959 emit_collection_helper CollectionType.ImmMap
1961 emit_value_only_collection env
pos es (fun n ->
1962 if hack_arr_dv_arrs () then
1967 if is_struct_init env
es false then
1968 emit_struct_array env
pos es (fun arg
->
1971 if hack_arr_dv_arrs () then
1972 instr_newstructdict arg
1974 instr_newstructdarray arg
)
1976 emit_keyvalue_collection
1977 CollectionType.Array
1981 ( if hack_arr_dv_arrs () then
1986 (* From here on, we're only dealing with PHP arrays *)
1987 if is_packed_init
es then
1988 emit_value_only_collection env
pos es (fun n -> NewPackedArray
n)
1989 else if is_struct_init env
es false then
1990 emit_struct_array env
pos es instr_newstructarray
1991 else if is_packed_init ~hack_arr_compat
:false es then
1992 emit_keyvalue_collection
CollectionType.Array env
pos es (NewArray
count)
1994 emit_keyvalue_collection
1995 CollectionType.Array
1999 (NewMixedArray
count)
2001 and emit_named_collection_str env
(expr
: Tast.expr
) pos name
fields =
2002 let name = SU.Types.fix_casing
@@ SU.strip_ns
name in
2005 | "dict" -> CollectionType.Dict
2006 | "vec" -> CollectionType.Vec
2007 | "keyset" -> CollectionType.Keyset
2008 | "Vector" -> CollectionType.Vector
2009 | "ImmVector" -> CollectionType.ImmVector
2010 | "Map" -> CollectionType.Map
2011 | "ImmMap" -> CollectionType.ImmMap
2012 | "Set" -> CollectionType.Set
2013 | "ImmSet" -> CollectionType.ImmSet
2014 | "Pair" -> CollectionType.Pair
2015 | _ -> failwith
@@ "collection: " ^
name ^
" does not exist"
2017 emit_named_collection env expr
pos ctype fields
2019 and emit_named_collection env
(expr
: Tast.expr
) pos ctype fields =
2020 let emit_vector_like collection_type
=
2022 emit_pos_then
pos @@ instr_newcol collection_type
2024 let ((_, ty
), _) = expr
in
2025 let annot = (pos, ty
) in
2030 (annot, A.Collection
((pos, "vec"), None
, fields))
2032 instr_colfromarray collection_type
;
2035 let emit_map_or_set collection_type
=
2037 emit_pos_then
pos @@ instr_newcol collection_type
2039 emit_collection ~transform_to_collection
:collection_type env expr
fields
2042 | CollectionType.Dict
2043 | CollectionType.Vec
2044 | CollectionType.Keyset
->
2045 emit_pos_then
pos @@ emit_collection env expr
fields
2046 | CollectionType.Vector
2047 | CollectionType.ImmVector
->
2048 emit_vector_like ctype
2049 | CollectionType.Map
2050 | CollectionType.ImmMap
2051 | CollectionType.Set
2052 | CollectionType.ImmSet
->
2053 emit_map_or_set ctype
2054 | CollectionType.Pair
->
2058 (List.map
fields (function
2059 | A.AFvalue e
-> emit_expr env e
2060 | _ -> failwith
"impossible Pair argument"));
2061 instr (ILitConst NewPair
);
2063 | _ -> failwith
"Unexpected named collection type"
2065 and is_php_array
= function
2066 | (_, A.Array
_) -> true
2067 | (_, A.Varray
_) -> not
(hack_arr_dv_arrs ())
2068 | (_, A.Darray
_) -> not
(hack_arr_dv_arrs ())
2071 and emit_collection ?transform_to_collection env
(expr
: Tast.expr
) es =
2072 let pos = Tast_annotate.get_pos expr
in
2074 Ast_constant_folder.expr_to_opt_typed_value
2076 ~restrict_keys
:(not
@@ is_php_array expr
)
2077 (Emit_env.get_namespace env
)
2080 | Some
tv -> emit_static_collection env ~transform_to_collection
pos tv
2081 | None
-> emit_dynamic_collection env expr
es
2083 and emit_pipe env
(e1
: Tast.expr
) (e2
: Tast.expr
) =
2084 let lhs_instrs = emit_expr env e1
in
2085 Scope.with_unnamed_local
2087 let env = Emit_env.with_pipe_var temp
env in
2088 let rhs_instrs = emit_expr
env e2
in
2089 (gather
[lhs_instrs; instr_popl temp
], rhs_instrs, instr_unsetl temp
)
2091 (* Emit code that is equivalent to
2094 * Generate specialized code in case expr is statically known, and for
2095 * !, && and || expressions
2097 and emit_jmpz
env (expr
: Tast.expr
) label
: emit_jmp_result
=
2098 let ((pos, _), expr_
) = expr
in
2099 let with_pos i = emit_pos_then
pos i in
2100 let opt = optimize_null_checks () in
2101 let ns = Emit_env.get_namespace
env in
2102 match Ast_constant_folder.expr_to_opt_typed_value
ns expr
with
2104 let b = Typed_value.to_bool
v in
2106 { instrs = with_pos empty
; is_fallthrough
= true; is_label_used
= false }
2109 instrs = with_pos @@ instr_jmp label
;
2110 is_fallthrough
= false;
2111 is_label_used
= true;
2116 | A.Unop
(Ast_defs.Unot
, e
) ->
2117 let (annot, expr_
) = e
in
2118 emit_jmpnz
env annot expr_ label
2119 | A.Binop
(Ast_defs.Barbar
, e1
, e2
) ->
2120 let skip_label = Label.next_regular
() in
2121 let (e1_annot
, e1_expr_
) = e1
in
2122 let r1 = emit_jmpnz
env e1_annot e1_expr_
skip_label in
2123 if not
r1.is_fallthrough
then
2125 if r1.is_label_used
then
2126 gather
[r1.instrs; instr_label
skip_label]
2131 instrs = with_pos instrs;
2132 is_fallthrough
= r1.is_label_used
;
2133 is_label_used
= false;
2136 let r2 = emit_jmpz
env e2 label
in
2142 optional
r1.is_label_used
[instr_label
skip_label];
2146 instrs = with_pos instrs;
2147 is_fallthrough
= r2.is_fallthrough
|| r1.is_label_used
;
2148 is_label_used
= r2.is_label_used
;
2150 | A.Binop
(Ast_defs.Ampamp
, e1
, e2
) ->
2151 let r1 = emit_jmpz
env e1 label
in
2152 if not
r1.is_fallthrough
then
2154 instrs = with_pos r1.instrs;
2155 is_fallthrough
= false;
2156 is_label_used
= r1.is_label_used
;
2159 let r2 = emit_jmpz
env e2 label
in
2161 instrs = with_pos @@ gather
[r1.instrs; r2.instrs];
2162 is_fallthrough
= r2.is_fallthrough
;
2163 is_label_used
= r1.is_label_used
|| r2.is_label_used
;
2165 | A.Binop
(Ast_defs.Eqeqeq
, e
, (_, A.Null
))
2166 | A.Binop
(Ast_defs.Eqeqeq
, (_, A.Null
), e
)
2169 instrs = with_pos @@ gather
[emit_is_null
env e
; instr_jmpz label
];
2170 is_fallthrough
= true;
2171 is_label_used
= true;
2173 | A.Binop
(Ast_defs.Diff2
, e
, (_, A.Null
))
2174 | A.Binop
(Ast_defs.Diff2
, (_, A.Null
), e
)
2177 instrs = with_pos @@ gather
[emit_is_null
env e
; instr_jmpnz label
];
2178 is_fallthrough
= true;
2179 is_label_used
= true;
2183 instrs = with_pos @@ gather
[emit_expr
env expr
; instr_jmpz label
];
2184 is_fallthrough
= true;
2185 is_label_used
= true;
2189 (* Emit code that is equivalent to
2192 * Generate specialized code in case expr is statically known, and for
2193 * !, && and || expressions
2195 and emit_jmpnz
env annot (expr_
: Tast.expr_
) label
: emit_jmp_result
=
2196 let (pos, _) = annot in
2197 let with_pos i = emit_pos_then
pos i in
2198 let opt = optimize_null_checks () in
2200 Ast_constant_folder.expr_to_opt_typed_value
2201 (Emit_env.get_namespace
env)
2205 if Typed_value.to_bool
v then
2207 instrs = with_pos @@ instr_jmp label
;
2208 is_fallthrough
= false;
2209 is_label_used
= true;
2212 { instrs = with_pos empty
; is_fallthrough
= true; is_label_used
= false }
2216 | A.Unop
(Ast_defs.Unot
, e
) -> emit_jmpz
env e label
2217 | A.Binop
(Ast_defs.Barbar
, (annot1
, e1
), (annot2
, e2
)) ->
2218 let r1 = emit_jmpnz
env annot1 e1 label
in
2219 if not
r1.is_fallthrough
then
2222 let r2 = emit_jmpnz
env annot2 e2 label
in
2224 instrs = with_pos @@ gather
[r1.instrs; r2.instrs];
2225 is_fallthrough
= r2.is_fallthrough
;
2226 is_label_used
= r1.is_label_used
|| r2.is_label_used
;
2228 | A.Binop
(Ast_defs.Ampamp
, e1
, (annot2
, e2
)) ->
2229 let skip_label = Label.next_regular
() in
2230 let r1 = emit_jmpz
env e1
skip_label in
2231 if not
r1.is_fallthrough
then
2238 optional
r1.is_label_used
[instr_label
skip_label];
2240 is_fallthrough
= r1.is_label_used
;
2241 is_label_used
= false;
2244 let r2 = emit_jmpnz
env annot2 e2 label
in
2252 optional
r1.is_label_used
[instr_label
skip_label];
2254 is_fallthrough
= r2.is_fallthrough
|| r1.is_label_used
;
2255 is_label_used
= r2.is_label_used
;
2257 | A.Binop
(Ast_defs.Eqeqeq
, e
, (_, A.Null
))
2258 | A.Binop
(Ast_defs.Eqeqeq
, (_, A.Null
), e
)
2261 instrs = with_pos @@ gather
[emit_is_null
env e
; instr_jmpnz label
];
2262 is_fallthrough
= true;
2263 is_label_used
= true;
2265 | A.Binop
(Ast_defs.Diff2
, e
, (_, A.Null
))
2266 | A.Binop
(Ast_defs.Diff2
, (_, A.Null
), e
)
2269 instrs = with_pos @@ gather
[emit_is_null
env e
; instr_jmpz label
];
2270 is_fallthrough
= true;
2271 is_label_used
= true;
2277 @@ gather
[emit_expr
env (annot, expr_
); instr_jmpnz label
];
2278 is_fallthrough
= true;
2279 is_label_used
= true;
2283 and emit_short_circuit_op
env annot (expr
: Tast.expr_
) =
2284 let (pos, _) = annot in
2285 let its_true = Label.next_regular
() in
2286 let its_done = Label.next_regular
() in
2287 let r1 = emit_jmpnz
env annot expr
its_true in
2289 if r1.is_label_used
then
2290 gather
[instr_label
its_true; emit_pos
pos; instr_true
]
2294 if r1.is_fallthrough
then
2302 instr_label
its_done;
2305 gather
[r1.instrs; if_true]
2307 and emit_null_coalesce_assignment
env pos (e1
: Tast.expr
) (e2
: Tast.expr
) =
2308 let end_label = Label.next_regular
() in
2309 let do_set_label = Label.next_regular
() in
2310 let l_nonnull = Local.get_unnamed_local
() in
2311 let (quiet_instr
, querym_n_unpopped
) =
2312 emit_quiet_expr ~null_coalesce_assignment
:true env pos e1
2314 let emit_popc_n n_unpopped
=
2315 match n_unpopped
with
2316 | Some
n -> gather
(List.init
n (fun _ -> instr_popc
))
2323 instr_istypec OpNull
;
2324 instr_jmpnz
do_set_label;
2325 instr_popl
l_nonnull;
2326 emit_popc_n querym_n_unpopped
;
2327 instr_pushl
l_nonnull;
2328 instr_jmp
end_label;
2329 instr_label
do_set_label;
2332 ~null_coalesce_assignment
:true
2338 instr_label
end_label;
2342 ?
(null_coalesce_assignment
= false)
2345 (expr
: Tast.expr
) : Instruction_sequence.t
* Hhbc_ast.num_params
option =
2346 let (_, expr_
) = expr
in
2348 | A.Lvar
(name_pos
, name)
2349 when Local_id.get_name
name = SN.Superglobals.globals
->
2353 instr_string
(SU.Locals.strip_dollar
(Local_id.get_name
name));
2358 | A.Lvar
(_, name) when not
(is_local_this env name) ->
2359 (instr_cgetquietl
(get_local
env (pos, Local_id.get_name
name)), None
)
2360 | A.Array_get
((_, A.Lvar
(_, x
)), Some e
)
2361 when Local_id.get_name x
= SN.Superglobals.globals
->
2362 (gather
[emit_expr
env e
; emit_pos
pos; instr (IGet CGetG
)], None
)
2363 | A.Array_get
(base_expr
, opt_elem_expr
) ->
2365 ~null_coalesce_assignment
2371 | A.Obj_get
(expr
, prop
, nullflavor
) ->
2373 ~null_coalesce_assignment
2380 | _ -> (emit_expr
env expr
, None
)
2382 (* returns instruction that will represent setter for $base[local] where
2383 is_base is true when result cell is base for another subscript operator and
2384 false when it is final left hand side of the assignment *)
2385 and emit_store_for_simple_base
2386 ~is_base
env pos elem_stack_size
(base_expr
: Tast.expr
) local =
2387 let (base_expr_instrs_begin
, base_expr_instrs_end
, base_setup_instrs
, _, _) =
2398 let mk = MemberKey.EL
local in
2400 instr_dim
MemberOpMode.Define
mk
2406 base_expr_instrs_begin
;
2407 base_expr_instrs_end
;
2413 (* get LocalTempKind option for a given expression
2414 - None - expression can be emitted as is
2415 - Some Value_kind_local - expression represents local that will be
2417 - Some Value_kind_expression - spilled non-trivial expression *)
2418 and get_local_temp_kind
2419 ~is_base inout_param_info
env (e_opt
: Tast.expr option) =
2420 match (e_opt
, inout_param_info
) with
2421 (* not inout case - no need to save *)
2423 (* local that will later be overwritten *)
2424 | (Some
(_, A.Lvar
(_, id)), Some
(i, aliases
))
2425 when InoutLocals.should_save_local_value (Local_id.get_name
id) i aliases
2427 Some Value_kind_local
2428 (* non-trivial expression *)
2430 if is_trivial ~is_base
env e
then
2433 Some Value_kind_expression
2436 and is_trivial ~is_base
env (_, e
) =
2442 (not
(is_local_this env s
)) || Emit_env.get_needs_local_this
env
2443 | A.Array_get
_ when not is_base
-> false
2444 | A.Array_get
(b, None
) -> is_trivial ~is_base
env b
2445 | A.Array_get
(b, Some e
) ->
2446 is_trivial ~is_base
env b && is_trivial ~is_base
env e
2449 (* Emit code for e1[e2] or isset(e1[e2]).
2452 ?
(null_coalesce_assignment
= false)
2458 (base_expr
: Tast.expr)
2459 (opt_elem_expr
: Tast.expr option) =
2461 emit_array_get_worker
2462 ~null_coalesce_assignment
2465 ~inout_param_info
:None
2473 | (Array_get_regular
i, querym_n_unpopped
) -> (i, querym_n_unpopped
)
2474 | (Array_get_inout
_, _) -> failwith
"unexpected inout"
2476 and emit_array_get_worker
2477 ?
(null_coalesce_assignment
= false)
2484 (base_expr
: Tast.expr)
2485 (opt_elem_expr
: Tast.expr option) =
2486 (* Disallow use of array(..)[] *)
2487 match (base_expr
, opt_elem_expr
) with
2488 | (((pos, _), A.Array
_), None
) ->
2489 Emit_fatal.raise_fatal_parse
2491 "Can't use array() as base in write context"
2492 | (((pos, _), _), None
) when not
(Emit_env.does_env_allow_array_append
env)
2494 Emit_fatal.raise_fatal_runtime
pos "Can't use [] for reading"
2496 let local_temp_kind =
2497 get_local_temp_kind ~is_base
:false inout_param_info
env opt_elem_expr
2500 if null_coalesce_assignment
then
2503 Option.value mode ~
default:(get_queryMOpMode qop
)
2505 let querym_n_unpopped = ref None
in
2506 let (elem_expr_instrs
, elem_stack_size
) =
2509 ~null_coalesce_assignment
2519 | QueryOp.Isset
-> NoNotice
2521 ~null_coalesce_assignment
2528 let cls_stack_size =
2529 match base_result with
2530 | Array_get_base_regular
base -> base.cls_stack_size
2531 | Array_get_base_inout
base -> base.load
.cls_stack_size
2535 ~null_coalesce_assignment
2540 let make_final total_stack_size
=
2546 ( if null_coalesce_assignment
then (
2547 querym_n_unpopped := Some total_stack_size
;
2550 QueryM
(total_stack_size
, qop
, mk) ))
2553 match (base_result, local_temp_kind) with
2554 | (Array_get_base_regular
base, None
) ->
2555 (* both base and expression don't need to store anything *)
2565 (base.base_stack_size
+ base.cls_stack_size + elem_stack_size
);
2567 | (Array_get_base_regular
base, Some local_kind
) ->
2568 (* base does not need temp locals but index expression does *)
2569 let local = Local.get_unnamed_local
() in
2572 (* load base and indexer, value of indexer will be saved in local *)
2573 ( gather
[base.base_instrs
; elem_expr_instrs
],
2574 Some
(local, local_kind
) );
2575 (* finish loading the value *)
2582 ( base.base_stack_size
2583 + base.cls_stack_size
2584 + elem_stack_size
);
2592 emit_store_for_simple_base
2602 Array_get_inout
{ load; store }
2603 | (Array_get_base_inout
base, None
) ->
2604 (* base needs temp locals, indexer - does not,
2605 simply concat two instruction sequences *)
2607 base.load.base_instrs
2612 base.load.cls_instrs
;
2614 base.load.setup_instrs
;
2616 ( base.load.base_stack_size
2617 + base.load.cls_stack_size
2618 + elem_stack_size
);
2623 let store = gather
[base.store; instr_setm
0 mk; instr_popc
] in
2624 Array_get_inout
{ load; store }
2625 | (Array_get_base_inout
base, Some local_kind
) ->
2626 (* both base and index need temp locals,
2627 create local for index value *)
2628 let local = Local.get_unnamed_local
() in
2631 base.load.base_instrs
2633 (* load index, value will be saved in local *)
2634 (elem_expr_instrs
, Some
(local, local_kind
));
2637 base.load.cls_instrs
;
2639 base.load.setup_instrs
;
2641 ( base.load.base_stack_size
2642 + base.load.cls_stack_size
2643 + elem_stack_size
);
2649 gather
[base.store; instr_setm
0 (MemberKey.EL
local); instr_popc
]
2651 Array_get_inout
{ load; store }
2653 (instr, !querym_n_unpopped)
2655 (* Emit code for e1->e2 or e1?->e2 or isset(e1->e2).
2658 ?
(null_coalesce_assignment
= false)
2665 let (annot, expr_
) = expr in
2668 when Local_id.get_name
id = SN.SpecialIdents.this
2669 && null_flavor
= Ast_defs.OG_nullsafe
->
2670 Emit_fatal.raise_fatal_parse
pos "?-> is not allowed with $this"
2674 | A.Id
(_, s
) when SU.Xhp.is_xhp s
->
2675 (emit_xhp_obj_get
env pos annot expr s null_flavor
, None
)
2678 if null_coalesce_assignment
then
2681 get_queryMOpMode qop
2683 let (_, _, prop_stack_size
) =
2684 emit_prop_expr ~null_coalesce_assignment
env null_flavor
0 prop
2686 let ( base_expr_instrs_begin
,
2687 base_expr_instrs_end
,
2694 ~null_coalesce_assignment
2701 let (mk, prop_expr_instrs
, _) =
2703 ~null_coalesce_assignment
2709 let total_stack_size =
2710 prop_stack_size
+ base_stack_size
+ cls_stack_size
2713 if null_coalesce_assignment
then
2718 let final_instr = instr (IFinal
(QueryM
(num_params, qop
, mk))) in
2719 let querym_n_unpopped =
2720 if null_coalesce_assignment
then
2721 Some
total_stack_size
2728 base_expr_instrs_begin
;
2730 base_expr_instrs_end
;
2736 (instr, querym_n_unpopped)
2739 and is_special_class_constant_accessed_with_class_id cName
(id : string) =
2740 let is_self_parent_or_static =
2742 | A.CIexpr
(_, A.Id
(_, cName
))
2743 when SU.is_self cName
|| SU.is_parent cName
|| SU.is_static cName
->
2747 SU.is_class
id && not
is_self_parent_or_static
2749 and emit_elem_instrs
2752 ?
(null_coalesce_assignment
= false)
2753 (opt_elem_expr
: Tast.expr option) =
2754 match opt_elem_expr
with
2755 (* These all have special inline versions of member keys *)
2756 | Some
(_, (A.Int
_ | A.String
_)) -> (empty
, 0)
2757 | Some
(_, A.Lvar
(pos, id)) when not
(is_local_this env id) ->
2758 if Option.is_some
local_temp_kind then
2759 (instr_cgetquietl
(get_local
env (pos, Local_id.get_name
id)), 0)
2760 else if null_coalesce_assignment
then
2761 (instr_cgetl
(get_local
env (pos, Local_id.get_name
id)), 1)
2764 (* Handle Foo::class but not self::class. *)
2765 | Some
(_, A.Class_const
((_, cid
), (_, id)))
2766 when is_special_class_constant_accessed_with_class_id cid
id ->
2768 | Some
expr -> (emit_expr
env expr, 1)
2769 | None
-> (empty
, 0)
2771 (* Get the member key for an array element expression: the `elem` in
2772 * expressions of the form `base[elem]`.
2773 * If the array element is missing, use the special key `W`.
2775 and get_elem_member_key
2776 ?
(null_coalesce_assignment
= false)
2779 (opt_expr
: Tast.expr option) =
2781 (* Special case for local *)
2782 | Some
(_, A.Lvar
(p, id)) when not
(is_local_this env id) ->
2783 if null_coalesce_assignment
then
2784 MemberKey.EC stack_index
2786 MemberKey.EL
(get_local
env (p, Local_id.get_name
id))
2787 (* Special case for literal integer *)
2788 | Some
((_, A.Int
str) as int_expr
) ->
2789 Ast_constant_folder.(
2790 let namespace = Emit_env.get_namespace
env in
2792 match expr_to_typed_value
namespace int_expr
with
2793 | TV.Int
i -> MemberKey.EI
i
2794 | _ -> failwith
(str ^
" is not a valid integer index")
2796 (* Special case for literal string *)
2797 | Some
(_, A.String
str) -> MemberKey.ET
str
2798 (* Special case for class name *)
2799 | Some
(_, A.Class_const
((_, cid
), (p, id)))
2800 when is_special_class_constant_accessed_with_class_id cid
id ->
2802 match (cid
, Ast_scope.Scope.get_class
(Emit_env.get_scope
env)) with
2803 | (A.CIself
, Some cd
) -> SU.strip_global_ns
@@ snd cd
.A.c_name
2804 | (A.CIexpr
(_, A.Id
(_, id)), _)
2805 | (A.CI
(_, id), _) ->
2806 SU.strip_global_ns
id
2809 "Unreachable due to is_special_class_constant_accessed_with_class_id"
2811 let fq_id = Hhbc_id.Class.elaborate_id
(p, cName) in
2812 MemberKey.ET
(Hhbc_id.Class.to_raw_string
fq_id)
2814 | Some
_ -> MemberKey.EC stack_index
2815 (* ELement missing (so it's array append) *)
2816 | None
-> MemberKey.W
2818 (* Get the member key for a property, and return any instructions and
2819 * the size of the stack in the case that the property cannot be
2820 * placed inline in the instruction. *)
2822 ?
(null_coalesce_assignment
= false)
2826 (prop_expr
: Tast.expr) =
2828 match snd prop_expr
with
2829 | A.Id
(pos, name) when String_utils.string_starts_with
name "$" ->
2830 MemberKey.PL
(get_local
env (pos, name))
2831 (* Special case for known property name *)
2834 let pid = Hhbc_id.Prop.from_ast_name
id in
2836 match null_flavor
with
2837 | Ast_defs.OG_nullthrows
-> MemberKey.PT
pid
2838 | Ast_defs.OG_nullsafe
-> MemberKey.QT
pid
2840 | A.Lvar
(pos, name) when not
(is_local_this env name) ->
2841 MemberKey.PL
(get_local
env (pos, Local_id.get_name
name))
2843 | _ -> MemberKey.PC stack_index
2845 (* For nullsafe access, insist that property is known *)
2850 let ((pos, _), _) = prop_expr
in
2851 if null_flavor
= Ast_defs.OG_nullsafe
then
2852 Emit_fatal.raise_fatal_parse
2854 "?-> can only be used with scalar property names"
2858 | MemberKey.PC
_ -> (mk, emit_expr
env prop_expr
, 1)
2859 | MemberKey.PL
local ->
2860 if null_coalesce_assignment
then
2861 (MemberKey.PC stack_index
, instr_cgetl
local, 1)
2864 | _ -> (mk, empty
, 0)
2866 (* Emit code for a base expression `expr` that forms part of
2867 * an element access `expr[elem]` or field access `expr->fld`.
2868 * The instructions are divided into three sections:
2869 * 1. base and element/property expression instructions:
2870 * push non-trivial base and key values on the stack
2871 * 2. base selector instructions: a sequence of Base/Dim instructions that
2872 * actually constructs the base address from "member keys" that are inlined
2873 * in the instructions, or pulled from the key values that
2874 * were pushed on the stack in section 1.
2875 * 3. (constructed by the caller) a final accessor e.g. QueryM or setter
2876 * e.g. SetOpM instruction that has the final key inlined in the
2877 * instruction, or pulled from the key values that were pushed on the
2878 * stack in section 1.
2879 * The function returns a triple (base_instrs, base_setup_instrs, stack_size)
2880 * where base_instrs is section 1 above, base_setup_instrs is section 2, and
2881 * stack_size is the number of values pushed onto the stack by section 1.
2883 * For example, the r-value expression $arr[3][$ix+2]
2885 * # Section 1, pushing the value of $ix+2 on the stack
2889 * # Section 2, constructing the base address of $arr[3]
2892 * # Section 3, indexing the array using the value at stack position 0 (EC:0)
2893 * QueryM 1 CGet EC:0
2898 ?
(null_coalesce_assignment
= false)
2908 ~inout_param_info
:None
2909 ~null_coalesce_assignment
2917 | Array_get_base_regular
i ->
2923 | Array_get_base_inout
_ -> failwith
"unexpected inout"
2925 and emit_base_worker
2929 ?
(null_coalesce_assignment
= false)
2934 (expr : Tast.expr) =
2935 let (((pos, _) as annot), expr_
) = expr in
2937 if mode = MemberOpMode.InOut
then
2942 let local_temp_kind =
2943 get_local_temp_kind ~is_base
:true inout_param_info
env (Some
expr)
2945 (* generic handler that will try to save local into temp if this is necessary *)
2947 base_instrs cls_instrs setup_instrs base_stack_size
cls_stack_size =
2948 match local_temp_kind with
2949 | Some local_temp
->
2950 let local = Local.get_unnamed_local
() in
2951 Array_get_base_inout
2955 (* run begin part, result will be stored into temp *)
2956 base_instrs
= [(base_instrs
, Some
(local, local_temp
))];
2962 store = instr_basel
local MemberOpMode.Define
;
2965 Array_get_base_regular
2975 | A.Lvar
(name_pos
, x
)
2976 when SN.Superglobals.is_superglobal
(Local_id.get_name x
)
2977 || Local_id.get_name x
= SN.Superglobals.globals
->
2979 ( emit_pos_then name_pos
2980 @@ instr_string
(SU.Locals.strip_dollar
(Local_id.get_name x
)) )
2982 (instr (IBase
(BaseGC
(base_offset
, base_mode))))
2985 | A.Lvar
(thispos
, x
)
2986 when is_object
&& Local_id.get_name x
= SN.SpecialIdents.this
->
2988 (emit_pos_then thispos
@@ instr (IMisc CheckThis
))
2990 (instr (IBase BaseH
))
2994 when (not
(is_local_this env str)) || Emit_env.get_needs_local_this
env ->
2995 let v = get_local
env (pos, Local_id.get_name
str) in
2996 if Option.is_some
local_temp_kind then
2997 emit_default (instr_cgetquietl
v) empty
(instr_basel
v base_mode) 0 0
2999 emit_default empty empty
(instr (IBase
(BaseL
(v, base_mode)))) 0 0
3002 (emit_local ~notice
env lid)
3004 (instr (IBase
(BaseC
(base_offset
, base_mode))))
3007 | A.Array_get
((_, A.Lvar
(_, x
)), Some
(_, A.Lvar
(y_pos
, y_id
)))
3008 when Local_id.get_name x
= SN.Superglobals.globals
->
3009 let v = get_local
env (y_pos
, Local_id.get_name y_id
) in
3010 emit_default empty empty
(instr (IBase
(BaseGL
(v, base_mode)))) 0 0
3011 | A.Array_get
((_, A.Lvar
(_, x
)), Some e
)
3012 when Local_id.get_name x
= SN.Superglobals.globals
->
3013 let elem_expr_instrs = emit_expr
env e
in
3017 (instr (IBase
(BaseGC
(base_offset
, base_mode))))
3020 (* $a[] can not be used as the base of an array get unless as an lval *)
3021 | A.Array_get
(_, None
) when not
(Emit_env.does_env_allow_array_append
env)
3023 Emit_fatal.raise_fatal_runtime
pos "Can't use [] for reading"
3024 (* base is in turn array_get - do a specific handling for inout params
3026 | A.Array_get
(base_expr
, opt_elem_expr
) ->
3027 let local_temp_kind =
3028 get_local_temp_kind ~is_base
:false inout_param_info
env opt_elem_expr
3030 let (elem_expr_instrs, elem_stack_size
) =
3033 ~null_coalesce_assignment
3042 ~null_coalesce_assignment
3045 (base_offset
+ elem_stack_size
)
3049 let cls_stack_size =
3050 match base_result with
3051 | Array_get_base_regular
base -> base.cls_stack_size
3052 | Array_get_base_inout
base -> base.load.cls_stack_size
3056 ~null_coalesce_assignment
3058 (base_offset
+ cls_stack_size)
3061 let make_setup_instrs base_setup_instrs
=
3062 gather
[base_setup_instrs
; instr (IBase
(Dim
(mode, mk)))]
3065 match (base_result, local_temp_kind) with
3066 (* both base and index don't use temps - fallback to default handler *)
3067 | (Array_get_base_regular
base, None
) ->
3069 (gather
[base.base_instrs
; elem_expr_instrs])
3071 (make_setup_instrs base.setup_instrs
)
3072 (base.base_stack_size
+ elem_stack_size
)
3074 | (Array_get_base_regular
base, Some local_temp
) ->
3075 (* base does not need temps but index does *)
3076 let local = Local.get_unnamed_local
() in
3077 let base_instrs = gather
[base.base_instrs; elem_expr_instrs] in
3078 Array_get_base_inout
3082 (* store result of instr_begin to temp *)
3083 base_instrs = [(base_instrs, Some
(local, local_temp
))];
3084 cls_instrs
= base.cls_instrs
;
3085 setup_instrs
= make_setup_instrs base.setup_instrs
;
3086 base_stack_size
= base.base_stack_size
+ elem_stack_size
;
3087 cls_stack_size = base.cls_stack_size;
3090 emit_store_for_simple_base
3098 | (Array_get_base_inout
base, None
) ->
3099 (* base needs temps, index - does not *)
3100 Array_get_base_inout
3104 (* concat index evaluation to base *)
3106 base.load.base_instrs @ [(elem_expr_instrs, None
)];
3107 cls_instrs
= base.load.cls_instrs
;
3108 setup_instrs
= make_setup_instrs base.load.setup_instrs
;
3109 base_stack_size
= base.load.base_stack_size
+ elem_stack_size
;
3110 cls_stack_size = base.load.cls_stack_size;
3112 store = gather
[base.store; instr_dim
MemberOpMode.Define
mk];
3114 | (Array_get_base_inout
base, Some local_kind
) ->
3115 (* both base and index needs locals *)
3116 let local = Local.get_unnamed_local
() in
3117 Array_get_base_inout
3122 base.load.base_instrs
3124 (* evaluate index, result will be stored in local *)
3125 (elem_expr_instrs, Some
(local, local_kind
));
3127 cls_instrs
= base.load.cls_instrs
;
3128 setup_instrs
= make_setup_instrs base.load.setup_instrs
;
3129 base_stack_size
= base.load.base_stack_size
+ elem_stack_size
;
3130 cls_stack_size = base.load.cls_stack_size;
3136 instr_dim
MemberOpMode.Define
(MemberKey.EL
local);
3140 | A.Obj_get
(base_expr
, prop_expr
, null_flavor
) ->
3142 match snd prop_expr
with
3143 | A.Id
(_, s
) when SU.Xhp.is_xhp s
->
3145 (emit_xhp_obj_get
env pos annot base_expr s null_flavor
)
3147 (gather
[instr_basec base_offset
base_mode])
3151 let (_, _, prop_stack_size
) =
3152 emit_prop_expr ~null_coalesce_assignment
env null_flavor
0 prop_expr
3154 let ( base_expr_instrs_begin
,
3155 base_expr_instrs_end
,
3162 ~null_coalesce_assignment
3165 (base_offset
+ prop_stack_size
)
3169 let (mk, prop_expr_instrs
, _) =
3171 ~null_coalesce_assignment
3174 (base_offset
+ cls_stack_size)
3177 let total_stack_size = prop_stack_size
+ base_stack_size
in
3178 let final_instr = instr (IBase
(Dim
(mode, mk))) in
3180 (gather
[base_expr_instrs_begin
; prop_expr_instrs
])
3181 base_expr_instrs_end
3182 (gather
[base_setup_instrs
; final_instr])
3186 | A.Class_get
(cid
, prop
) ->
3188 class_id_to_class_expr ~
resolve_self:false (Emit_env.get_scope
env) cid
3190 let (cexpr_begin
, cexpr_end
) = emit_class_expr
env cexpr prop
in
3194 (instr_basesc
(base_offset
+ 1) rhs_stack_size
base_mode)
3198 let base_expr_instrs = emit_expr
env expr in
3202 (emit_pos_then
pos @@ instr (IBase
(BaseC
(base_offset
, base_mode))))
3206 and emit_ignored_expr
env ?
(pop_pos
: Pos.t
= Pos.none
) (e
: Tast.expr) =
3208 | A.Expr_list
es -> gather
@@ List.map ~
f:(emit_ignored_expr
env ~pop_pos
) es
3209 | _ -> gather
[emit_expr
env e
; emit_pos_then pop_pos
@@ instr_popc
]
3212 * Replaces erased generics with underscores or
3213 * raises a parse error if used with is/as expressions
3215 and fixup_type_arg
(env : Emit_env.t
) ~isas
(hint
: Aast.hint
) =
3216 let erased_tparams = get_erased_tparams
env in
3217 let rec aux ((p, hint
) as _x
: Aast.hint
) =
3219 | Aast.Hoption
h -> (p, Aast.Hoption
(aux h))
3220 | Aast.Hlike
h -> (p, Aast.Hlike
(aux h))
3228 hf_param_mutability
;
3231 hf_is_mutable_return
;
3239 hf_param_tys
= List.map ~
f:aux hf_param_tys
;
3241 hf_param_mutability
;
3242 (* TODO: shouldn't we also replace the hint in here? *)
3244 hf_return_ty
= aux hf_return_ty
;
3245 hf_is_mutable_return
;
3247 | Aast.Htuple hl
-> (p, Aast.Htuple
(List.map ~
f:aux hl
))
3248 | Aast.Happly
((_, id), _)
3249 when List.mem ~equal
:String.equal
erased_tparams id ->
3251 Emit_fatal.raise_fatal_parse
3253 "Erased generics are not allowed in is/as expressions"
3255 (p, Aast.Happly
((p, "_"), []))
3256 | Aast.Happly
(id, hl
) -> (p, Aast.Happly
(id, List.map ~
f:aux hl
))
3258 { Aast.nsi_allows_unknown_fields
= uf
; Aast.nsi_field_map
= fm
} ->
3262 Aast.nsi_allows_unknown_fields
= uf
;
3263 Aast.nsi_field_map
= List.map ~
f:aux_sf fm
;
3265 | Aast.Haccess
_ -> (p, hint
)
3266 | Aast.Hsoft
h -> (p, Aast.Hsoft
(aux h))
3267 | _ -> failwith
"todo"
3268 and aux_sf sfi
= { sfi
with Aast.sfi_hint
= aux sfi
.Aast.sfi_hint
} in
3271 and emit_reified_arg
env ~isas
pos (hint
: Aast.hint
) =
3272 let hint = fixup_type_arg
env ~isas
hint in
3273 let scope = Emit_env.get_scope
env in
3274 let f is_fun tparam
acc =
3276 | { A.tp_name
= (_, id); A.tp_reified
; _ } when not
(tp_reified
= A.Erased
)
3278 SMap.add
id is_fun
acc
3283 (Ast_scope.Scope.get_fun_tparams
scope)
3289 (Ast_scope.Scope.get_class_tparams
scope).A.c_tparam_list
3293 let acc = ref (0, SMap.empty
) in
3296 inherit [_] A.iter as super
3298 method! on_hint_
_ h =
3300 let (i, map
) = !acc in
3301 match SMap.get
name current_targs with
3302 | Some
_ when not
(SMap.mem
name map
) ->
3303 acc := (i + 1, SMap.add
name i map
)
3307 | A.Haccess
(_, sids
) -> List.iter sids
(fun sid
-> add_name (snd sid
))
3308 | A.Happly
((_, name), h) ->
3310 let _ = List.map ~
f:(super#on_hint
()) h in
3312 | A.Habstr
name -> add_name name
3318 visitor#on_hint
() hint;
3319 let (count, targ_map
) = !acc in
3321 | Aast.Happly
((_, name), []) when SMap.mem
name current_targs ->
3322 (emit_reified_type
env pos name, false)
3324 let ts = get_type_structure_for_hint ~targ_map ~
tparams:[] hint in
3329 (* Sort map from key 0 to count and convert each identified into cgetl *)
3331 SMap.bindings targ_map
3332 |> List.sort ~compare
:(fun (_, x
) (_, y
) -> Pervasives.compare x y
)
3333 |> List.map ~
f:(fun (v, _) -> emit_reified_type
env pos v)
3335 gather
[gather
values; ts]
3337 ( gather
[ts_list; instr_combine_and_resolve_type_struct
(count + 1)],
3340 (* Emit arguments of a function call and inout setter for inout args *)
3341 and emit_args_and_inout_setters
env (args : Tast.expr list
) =
3343 if has_inout_args
args then
3344 InoutLocals.collect_written_variables env args
3348 let emit_arg_and_inout_setter i (arg
: Tast.expr) =
3351 | A.Callconv
(Ast_defs.Pinout
, (_, A.Lvar
(pos, id))) ->
3352 let local = get_local
env (pos, Local_id.get_name
id) in
3353 let not_in_try = not
(Emit_env.is_in_try
env) in
3355 if not_in_try && InoutLocals.should_move_local_value local aliases then
3356 gather
[instr_null
; instr_popl
local]
3360 (gather
[instr_cgetl
local; move_instrs], instr_popl
local)
3361 (* inout $arr[...][...] *)
3363 (Ast_defs.Pinout
, ((pos, _), A.Array_get
(base_expr
, opt_elem_expr
)))
3365 let array_get_result =
3367 (emit_array_get_worker
3368 ~inout_param_info
:(Some
(i, aliases))
3376 match array_get_result with
3377 | Array_get_regular
instrs ->
3382 ~
mode:MemberOpMode.Define
3393 instr_setm
0 (get_elem_member_key
env 0 opt_elem_expr
);
3398 | Array_get_inout
{ load; store } -> rebuild_load_store load store
3400 (* unsupported inout *)
3401 | A.Callconv
(Ast_defs.Pinout
, _) ->
3402 failwith
"emit_arg_and_inout_setter: Unexpected inout expression type"
3403 (* regular argument *)
3404 | _ -> (emit_expr
env arg
, empty
)
3406 let rec aux i (args : Tast.expr list
) =
3408 | [] -> (empty
, empty
)
3409 | arg
:: rem_args
->
3410 let (this_arg
, this_setter
) = emit_arg_and_inout_setter i arg
in
3411 let (rem_args
, rem_setters
) = aux (i + 1) rem_args
in
3412 (gather
[this_arg
; rem_args
], gather
[this_setter
; rem_setters
])
3414 let (instr_args
, instr_setters
) = aux 0 args in
3415 if has_inout_args
args then
3416 let retval = Local.get_unnamed_local
() in
3417 (instr_args
, gather
[instr_popl
retval; instr_setters
; instr_pushl
retval])
3421 (* Create fcall_args for a given call *)
3422 and get_fcall_args ?
(lock_while_unwinding
= false) args uargs
async_eager_label
3424 let num_args = List.length
args in
3426 List.fold_left
args ~init
:1 ~
f:(fun acc arg
->
3427 if is_inout_arg arg
then
3433 { default_fcall_flags
with has_unpack
= uargs
<> []; lock_while_unwinding
}
3435 let inouts = List.map
args is_inout_arg
in
3436 make_fcall_args ~
flags ~
num_rets ~
inouts ?
async_eager_label num_args
3438 (* Expression that appears in an object context, such as expr->meth(...) *)
3439 and emit_object_expr
env (expr : Tast.expr) =
3440 let (_, expr_
) = expr in
3442 | A.Lvar
(_, x
) when is_local_this env x
-> instr_this
3443 | _ -> emit_expr
env expr
3445 and is_inout_arg
= function
3446 | (_, A.Callconv
(Ast_defs.Pinout
, _)) -> true
3449 and has_inout_args
es = List.exists
es ~
f:is_inout_arg
3451 and emit_call_lhs_and_fcall
3452 env (expr : Tast.expr) fcall_args
(targs
: Tast.targ list
) =
3453 let ((pos, _), expr_
) = expr in
3454 let does_not_have_non_tparam_generics =
3455 not
(has_non_tparam_generics_targs
env targs
)
3457 let emit_generics (flags, a
, b, c
, d
) =
3458 if does_not_have_non_tparam_generics then
3459 (empty
, (flags, a
, b, c
, d
))
3461 ( emit_reified_targs
env pos (List.map ~
f:snd targs
),
3462 ({ flags with has_generics
= true }, a
, b, c
, d
) )
3465 | A.Obj_get
(obj
, (_, A.String
id), null_flavor
)
3466 | A.Obj_get
(obj
, (_, A.Id
(_, id)), null_flavor
) ->
3467 let name = Hhbc_id.Method.from_ast_name
id in
3468 let obj = emit_object_expr
env obj in
3469 let (generics
, fcall_args
) = emit_generics fcall_args
in
3470 let null_flavor = from_ast_null_flavor null_flavor in
3471 ( gather
[obj; instr_nulluninit
; instr_nulluninit
],
3472 gather
[generics
; instr_fcallobjmethodd fcall_args
name null_flavor] )
3473 | A.Obj_get
(obj, method_expr
, null_flavor) ->
3474 let obj = emit_object_expr
env obj in
3475 let tmp = Local.get_unnamed_local
() in
3476 let null_flavor = from_ast_null_flavor null_flavor in
3482 emit_expr
env method_expr
;
3485 gather
[instr_pushl
tmp; instr_fcallobjmethod fcall_args
null_flavor] )
3486 | A.Class_const
(cid
, (_, id)) ->
3488 class_id_to_class_expr ~
resolve_self:false (Emit_env.get_scope
env) cid
3490 let method_id = Hhbc_id.Method.from_ast_name
id in
3491 let method_id_string = Hhbc_id.Method.to_raw_string
method_id in
3494 | Class_id
(_, name) ->
3495 Option.value ~
default:cexpr (get_reified_var_cexpr
env pos name)
3500 (* Statically known *)
3502 let fq_cid = Hhbc_id.Class.elaborate_id cid
in
3503 let fq_cid_string = Hhbc_id.Class.to_raw_string
fq_cid in
3504 Emit_symbol_refs.add_class
fq_cid_string;
3505 let (generics
, fcall_args
) = emit_generics fcall_args
in
3506 ( gather
[instr_nulluninit
; instr_nulluninit
; instr_nulluninit
],
3507 gather
[generics
; instr_fcallclsmethodd fcall_args
method_id fq_cid]
3509 | Class_special clsref
->
3510 let (generics
, fcall_args
) = emit_generics fcall_args
in
3511 ( gather
[instr_nulluninit
; instr_nulluninit
; instr_nulluninit
],
3512 gather
[generics
; instr_fcallclsmethodsd fcall_args clsref
method_id]
3514 | Class_expr
expr ->
3515 let (generics
, fcall_args
) = emit_generics fcall_args
in
3516 let emit_fcall instr_meth
=
3523 instr_fcallclsmethod
3524 ~is_log_as_dynamic_call
:DontLogAsDynamicCall
3528 ( gather
[instr_nulluninit
; instr_nulluninit
; instr_nulluninit
],
3529 emit_fcall (instr_string
method_id_string) )
3530 | Class_reified
instrs ->
3531 (* TODO(T31677864): Implement reification here *)
3532 let tmp = Local.get_unnamed_local
() in
3543 instr_string
method_id_string;
3546 instr_fcallclsmethod fcall_args
;
3549 | A.Class_get
(cid
, e
) ->
3551 class_id_to_class_expr ~
resolve_self:false (Emit_env.get_scope
env) cid
3553 let emit_meth_name () =
3555 | A.CGstring
(pos, id) ->
3556 emit_pos_then
pos @@ instr_cgetl
(Local.Named
id)
3557 | A.CGexpr e
-> emit_expr
env e
3561 | Class_id
(_, name) ->
3562 Option.value ~
default:cexpr (get_reified_var_cexpr
env pos name)
3568 let tmp = Local.get_unnamed_local
() in
3580 emit_known_class_id cid
;
3581 instr_fcallclsmethod fcall_args
;
3583 | Class_special clsref
->
3584 let tmp = Local.get_unnamed_local
() in
3593 gather
[instr_pushl
tmp; instr_fcallclsmethods fcall_args clsref
] )
3594 | Class_expr
expr ->
3595 let cls = Local.get_unnamed_local
() in
3596 let meth = Local.get_unnamed_local
() in
3612 instr_fcallclsmethod fcall_args
;
3614 | Class_reified
instrs ->
3615 let cls = Local.get_unnamed_local
() in
3616 let meth = Local.get_unnamed_local
() in
3632 instr_fcallclsmethod fcall_args
;
3636 let fq_id = Hhbc_id.Function.from_ast_name s
in
3638 let (flags, num_args, _, _, _) = fcall_args
in
3639 match SU.strip_global_ns s
with
3640 | "min" when num_args = 2 && not
flags.has_unpack
->
3641 Hhbc_id.Function.from_raw_string
"__SystemLib\\min2"
3642 | "max" when num_args = 2 && not
flags.has_unpack
->
3643 Hhbc_id.Function.from_raw_string
"__SystemLib\\max2"
3646 let (generics
, fcall_args
) = emit_generics fcall_args
in
3647 ( gather
[instr_nulluninit
; instr_nulluninit
; instr_nulluninit
],
3648 gather
[generics
; instr_fcallfuncd fcall_args
fq_id] )
3650 let fq_id = Hhbc_id.Function.from_raw_string s
in
3651 let (generics
, fcall_args
) = emit_generics fcall_args
in
3652 ( gather
[instr_nulluninit
; instr_nulluninit
; instr_nulluninit
],
3653 gather
[generics
; instr_fcallfuncd fcall_args
fq_id] )
3655 let tmp = Local.get_unnamed_local
() in
3664 gather
[instr_pushl
tmp; instr_fcallfunc fcall_args
] )
3666 and get_call_builtin_func_info
id =
3668 | "array_key_exists" -> Some
(2, IMisc AKExists
)
3669 | "hphp_array_idx" -> Some
(3, IMisc ArrayIdx
)
3670 | "intval" -> Some
(1, IOp CastInt
)
3671 | "boolval" -> Some
(1, IOp CastBool
)
3672 | "strval" -> Some
(1, IOp CastString
)
3675 Some
(1, IOp CastDouble
)
3676 | "HH\\vec" -> Some
(1, IOp CastVec
)
3677 | "HH\\keyset" -> Some
(1, IOp CastKeyset
)
3678 | "HH\\dict" -> Some
(1, IOp CastDict
)
3683 ( if hack_arr_dv_arrs () then
3691 ( if hack_arr_dv_arrs () then
3695 | "HH\\global_get" -> Some
(1, IGet CGetG
)
3696 | "HH\\global_isset" -> Some
(1, IIsset IssetG
)
3699 (* TODO: work out what HHVM does special here *)
3700 and emit_name_string
env e
= emit_expr
env e
3702 and emit_special_function
3703 env pos annot id (args : Tast.expr list
) (uargs
: Tast.expr list
) default =
3704 let nargs = List.length
args + List.length uargs
in
3706 Hhbc_id.Function.from_ast_name
id
3707 |> Hhbc_id.Function.to_raw_string
3709 (* Make sure that we do not treat a special function that is aliased as not
3711 match (lower_fq_name, args) with
3712 | (id, _) when id = SN.SpecialFunctions.echo
->
3715 @@ List.mapi
args (fun i arg
->
3721 ( if i = nargs - 1 then
3728 | ("HH\\invariant", e
:: rest
) ->
3729 let l = Label.next_regular
() in
3730 let annot = (pos, snd
annot) in
3731 (* TODO: Can we capitalize hh to HH? *)
3732 let expr_id = (annot, A.Id
(pos, "\\hh\\invariant_violation")) in
3736 (* Could use emit_jmpnz for better code *)
3741 (annot, A.Call
(Aast.Cnormal
, expr_id, [], rest
, uargs
));
3742 Emit_fatal.emit_fatal_runtime
pos "invariant_violation";
3747 let l0 = Label.next_regular
() in
3748 let l1 = Label.next_regular
() in
3752 instr_string
"zend.assertions";
3753 instr_fcallbuiltin
1 1 0 "ini_get";
3763 | ("HH\\sequence", []) -> Some instr_null
3764 | ("HH\\sequence", args) ->
3766 (gather
(List.intersperse
(List.map
args ~
f:(emit_expr
env)) instr_popc
))
3767 | ((("class_exists" | "interface_exists" | "trait_exists") as id), arg1
:: _)
3768 when nargs = 1 || nargs = 2 ->
3771 | "class_exists" -> KClass
3772 | "interface_exists" -> KInterface
3773 | "trait_exists" -> KTrait
3774 | _ -> failwith
"class_kind"
3779 emit_name_string
env arg1
;
3780 instr (IOp CastString
);
3784 gather
[emit_expr
env (List.nth_exn
args 1); instr (IOp CastBool
)]
3786 instr (IMisc
(OODeclExists
class_kind));
3788 | (("exit" | "die"), _) when nargs = 0 || nargs = 1 ->
3789 Some
(emit_exit
env (List.hd
args))
3792 Emit_fatal.raise_fatal_runtime
3794 ("fun() expects exactly 1 parameter, " ^ string_of_int
nargs ^
" given")
3797 | [(_, A.String func_name
)] ->
3798 let func_name = SU.strip_global_ns
func_name in
3799 if Hhbc_options.emit_func_pointers
!Hhbc_options.compiler_options
then
3801 (instr_resolve_func
@@ Hhbc_id.Function.from_raw_string
func_name)
3803 Some
(instr_string
func_name)
3805 Emit_fatal.raise_fatal_runtime
pos "Constant string expected in fun()"
3807 | ("__systemlib\\fun", _) ->
3808 (* used by meth_caller() to directly emit func ptr *)
3810 Emit_fatal.raise_fatal_runtime
3812 ("fun() expects exactly 1 parameter, " ^ string_of_int
nargs ^
" given")
3815 | [(_, A.String
func_name)] ->
3816 let func_name = SU.strip_global_ns
func_name in
3817 Some
(instr_resolve_func
@@ Hhbc_id.Function.from_raw_string
func_name)
3819 Emit_fatal.raise_fatal_runtime
pos "Constant string expected in fun()"
3821 | ("HH\\inst_meth", _) ->
3824 | [obj_expr
; method_name
] ->
3828 emit_expr
env obj_expr
;
3829 emit_expr
env method_name
;
3831 Hhbc_options.emit_inst_meth_pointers
3832 !Hhbc_options.compiler_options
3834 instr_resolve_obj_method
3836 instr (ILitConst
(NewVArray
2)) );
3839 Emit_fatal.raise_fatal_runtime
3841 ( "inst_meth() expects exactly 2 parameters, "
3842 ^ string_of_int
nargs
3845 | ("HH\\class_meth", _) ->
3849 ((_, A.Class_const
_) as class_name
);
3850 ((_, A.String
_) as method_name
);
3852 | [((_, A.String
_) as class_name
); ((_, A.String
_) as method_name
)] ->
3856 emit_expr
env class_name
;
3857 emit_expr
env method_name
;
3859 Hhbc_options.emit_cls_meth_pointers
3860 !Hhbc_options.compiler_options
3862 instr_resolve_cls_method NoWarn
3864 instr (ILitConst
(NewVArray
2)) );
3866 | [class_name
; method_name
] ->
3870 emit_expr
env class_name
;
3871 emit_expr
env method_name
;
3873 Hhbc_options.emit_cls_meth_pointers
3874 !Hhbc_options.compiler_options
3876 instr_resolve_cls_method Warn
3878 instr (ILitConst
(NewVArray
2)) );
3881 Emit_fatal.raise_fatal_runtime
3883 ( "class_meth() expects exactly 2 parameters, "
3884 ^ string_of_int
nargs
3887 | ("HH\\global_set", _) ->
3895 emit_expr
env gvalue
;
3897 instr (IMutator SetG
);
3902 Emit_fatal.raise_fatal_runtime
3904 ( "global_set() expects exactly 2 parameters, "
3905 ^ string_of_int
nargs
3908 | ("HH\\global_unset", _) ->
3917 instr (IMutator UnsetG
);
3921 Emit_fatal.raise_fatal_runtime
3923 ( "global_unset() expects exactly 1 parameter, "
3924 ^ string_of_int
nargs
3927 | ("__hhvm_internal_whresult", [(_, A.Lvar
(_, param
))])
3928 when Emit_env.is_systemlib
() ->
3931 [instr_cgetl
(Local.Named
(Local_id.get_name param
)); instr_whresult
])
3932 | ("__hhvm_internal_newlikearrayl", [(_, A.Lvar
(_, param
)); (_, A.Int
n)])
3933 when Emit_env.is_systemlib
() ->
3938 (Local.Named
(Local_id.get_name param
), int_of_string
n))))
3941 match (args, istype_op lower_fq_name, is_isexp_op lower_fq_name) with
3942 | ([arg_expr
], _, Some
h) ->
3943 Some
(gather
[emit_expr
env arg_expr
; emit_is
env pos h])
3944 | ([(_, A.Lvar
((_, arg_str
) as arg_id
))], Some
i, _)
3945 when SN.Superglobals.is_superglobal
(Local_id.get_name arg_str
)
3946 || Local_id.get_name arg_str
= SN.Superglobals.globals
->
3950 emit_local ~notice
:NoNotice
env arg_id
;
3952 instr (IIsset
(IsTypeC
i));
3954 | ([(_, A.Lvar
(arg_pos
, arg_str
))], Some
i, _)
3955 when not
(is_local_this env arg_str
) ->
3959 (IsTypeL
(get_local
env (arg_pos
, Local_id.get_name arg_str
), i))))
3960 | ([arg_expr
], Some
i, _) ->
3963 [emit_expr
env arg_expr
; emit_pos
pos; instr (IIsset
(IsTypeC
i))])
3966 match get_call_builtin_func_info
lower_fq_name with
3967 | Some
(nargs, i) when nargs = List.length
args ->
3968 Some
(gather
[emit_exprs
env args; emit_pos
pos; instr i])
3977 (targs
: Tast.targ list
)
3978 (args : Tast.expr list
)
3979 (uargs
: Tast.expr list
)
3981 let (annot, expr_
) = expr in
3983 | A.Id
(_, s
) -> Emit_symbol_refs.add_function s
3985 let fcall_args = get_fcall_args
args uargs
async_eager_label in
3986 let (_flags
, _args
, num_ret
, _inouts
, _eager
) = fcall_args in
3987 let num_uninit = num_ret
- 1 in
3989 Scope.with_unnamed_locals
3991 let (instr_lhs
, instr_fcall
) =
3992 emit_call_lhs_and_fcall
env expr fcall_args targs
3994 let (instr_args
, instr_inout_setters
) =
3995 emit_args_and_inout_setters
env args
4000 | uargs
:: _ -> emit_expr
env uargs
4005 gather
@@ List.init
num_uninit ~
f:(fun _ -> instr_nulluninit
);
4011 instr_inout_setters
;
4015 match (expr_
, args) with
4016 | (A.Id
(_, id), _) ->
4017 let special_fn_opt =
4018 emit_special_function
env pos annot id args uargs
default
4021 match special_fn_opt with
4022 | Some
instrs -> instrs
4023 | None
-> default ()
4027 and emit_final_member_op stack_index
op mk =
4029 | LValOp.Set
-> instr (IFinal
(SetM
(stack_index
, mk)))
4030 | LValOp.SetOp
op -> instr (IFinal
(SetOpM
(stack_index
, op, mk)))
4031 | LValOp.IncDec
op -> instr (IFinal
(IncDecM
(stack_index
, op, mk)))
4032 | LValOp.Unset
-> instr (IFinal
(UnsetM
(stack_index
, mk)))
4034 and emit_final_local_op
pos op lid =
4038 | LValOp.Set
-> instr (IMutator
(SetL
lid))
4039 | LValOp.SetOp
op -> instr (IMutator
(SetOpL
(lid, op)))
4040 | LValOp.IncDec
op -> instr (IMutator
(IncDecL
(lid, op)))
4041 | LValOp.Unset
-> instr (IMutator
(UnsetL
lid))
4043 and emit_final_global_op
pos op =
4045 | LValOp.Set
-> emit_pos_then
pos @@ instr (IMutator SetG
)
4046 | LValOp.SetOp
op -> instr (IMutator
(SetOpG
op))
4047 | LValOp.IncDec
op -> instr (IMutator
(IncDecG
op))
4048 | LValOp.Unset
-> emit_pos_then
pos @@ instr (IMutator UnsetG
)
4050 and emit_final_static_op cid
(prop
: Tast.class_get_expr
) op =
4052 | LValOp.Set
-> instr (IMutator SetS
)
4053 | LValOp.SetOp
op -> instr (IMutator
(SetOpS
op))
4054 | LValOp.IncDec
op -> instr (IMutator
(IncDecS
op))
4058 | A.CGexpr
((pos, _), _) -> pos
4059 | A.CGstring
(pos, _) -> pos
4061 let cid = text_of_class_id cid in
4062 let id = text_of_prop prop
in
4063 Emit_fatal.emit_fatal_runtime
4065 ("Attempt to unset static property " ^
SU.strip_ns
cid ^
"::" ^
id)
4067 (* Given a local $local and a list of integer array indices i_1, ..., i_n,
4068 * generate code to extract the value of $local[i_n]...[i_1]:
4070 * Dim Warn EI:i_n ...
4072 * QueryM 0 CGet EI:i_1
4074 and emit_array_get_fixed last_usage
local indices
=
4075 let (base, stack_count
) =
4077 (gather
[instr_pushl
local; instr_basec
0 MemberOpMode.Warn
], 1)
4079 (instr_basel
local MemberOpMode.Warn
, 0)
4083 @@ List.rev_mapi
indices (fun i ix
->
4084 let mk = MemberKey.EI
(Int64.of_int ix
) in
4086 instr (IFinal
(QueryM
(stack_count
, QueryOp.CGet
, mk)))
4088 instr (IBase
(Dim
(MemberOpMode.Warn
, mk))))
4090 gather
[base; indices]
4092 and can_use_as_rhs_in_list_assignment
(expr : Tast.expr_
) =
4095 | Call
(_, (_, Id
(_, s
)), _, _, _) when s
= SN.SpecialFunctions.echo
->
4118 | Pipe
(_, _, (_, r))
4119 | Binop
(Ast_defs.Eq None
, (_, List
_), (_, r)) ->
4120 can_use_as_rhs_in_list_assignment
r
4121 | Binop
(Ast_defs.Plus
, _, _)
4122 | Binop
(Ast_defs.QuestionQuestion
, _, _)
4123 | Binop
(Ast_defs.Eq
_, _, _)
4126 (* Everything below is false *)
4130 | KeyValCollection
_
4136 | Method_caller
(_, _)
4159 | ParenthesizedExpr
_
4167 | PU_identifier
_ ->
4168 failwith
"TODO(T35357243): Pocket Universes syntax must be erased by now")
4170 (* Generate code for each lvalue assignment in a list destructuring expression.
4171 * Lvalues are assigned right-to-left, regardless of the nesting structure. So
4172 * list($a, list($b, $c)) = $d
4173 * and list(list($a, $b), $c) = $d
4174 * will both assign to $c, $b and $a in that order.
4175 * Returns a pair of instructions:
4176 * 1. initialization part of the left hand side
4178 * this is necessary to handle cases like:
4179 * list($a[$f()]) = b();
4180 * here f() should be invoked before b()
4182 and emit_lval_op_list
4183 ?
(last_usage
= false)
4187 (indices : int list
)
4188 (expr : Tast.expr) =
4189 let is_ltr = php7_ltr_assign () in
4192 let last_non_omitted =
4193 (* last usage of the local will happen when processing last non-omitted
4194 element in the list - find it *)
4198 |> List.foldi ~init
:None ~
f:(fun i acc (_, v) ->
4199 if v = A.Omitted
then
4203 (* in right-to-left case result list will be reversed
4204 so we need to find first non-omitted expression *)
4207 |> List.findi ~
f:(fun _ (_, v) -> v <> A.Omitted
)
4208 |> Option.map ~
f:fst
4212 let (lhs_instrs, set_instrs
) =
4213 List.mapi exprs
(fun i expr ->
4215 ~last_usage
:(Some
i = last_non_omitted)
4223 ( gather
lhs_instrs,
4225 ( if not
is_ltr then
4229 | A.Omitted
-> (empty
, empty
)
4231 (* Generate code to access the element from the array *)
4233 match (local, indices) with
4234 | (Some
local, _ :: _) -> emit_array_get_fixed last_usage
local indices
4235 | (Some
local, []) ->
4240 | (None
, _) -> instr_null
4242 (* Generate code to assign to the lvalue *)
4243 (* Return pair: side effects to initialize lhs + assignment *)
4244 let (lhs_instrs, rhs_instrs, set_op
) =
4245 emit_lval_op_nonlist_steps
env outer_pos
LValOp.Set
expr access_instrs 1
4267 (* Emit code for an l-value operation *)
4269 ?
(null_coalesce_assignment
= false)
4275 match (op, expr1
, opt_expr2
) with
4276 (* Special case for list destructuring, only on assignment *)
4277 | (LValOp.Set
, (_, A.List
l), Some expr2
) ->
4278 let instr_rhs = emit_expr
env expr2
in
4280 List.exists
l ~
f:(function
4281 | (_, A.Omitted
) -> false
4284 if not
has_elements then
4287 Scope.with_unnamed_local
4290 if can_use_as_rhs_in_list_assignment
(snd expr2
) then
4295 let (instr_lhs
, instr_assign
) = emit_lval_op_list
env pos loc [] expr1
in
4297 ( gather
[instr_lhs
; instr_rhs; instr_popl
local],
4305 let (rhs_instrs, rhs_stack_size
) =
4306 match opt_expr2
with
4307 | None
-> (empty
, 0)
4308 | Some
(_, A.Yield af
) ->
4309 let temp = Local.get_unnamed_local
() in
4312 emit_yield
env pos af
;
4318 | Some e
-> (emit_expr
env e
, 1)
4320 emit_lval_op_nonlist
4321 ~null_coalesce_assignment
4329 and emit_lval_op_nonlist
4330 ?
(null_coalesce_assignment
= false) env pos op e
rhs_instrs rhs_stack_size
4332 let (lhs, rhs
, setop
) =
4333 emit_lval_op_nonlist_steps
4334 ~null_coalesce_assignment
4342 gather
[lhs; rhs
; setop
]
4344 and emit_lval_op_nonlist_steps
4345 ?
(null_coalesce_assignment
= false)
4352 let ((pos, _), expr_
) = expr in
4355 (* Unbelieveably, $test[] += 5; is legal in PHP, but $test[] = $test[] + 5 is not *)
4358 | LValOp.IncDec
_ ->
4359 { env with Emit_env.env_allows_array_append
= true }
4363 | A.Lvar
(name_pos
, id)
4364 when SN.Superglobals.is_superglobal
(Local_id.get_name
id)
4365 || Local_id.get_name
id = SN.Superglobals.globals
->
4366 ( emit_pos_then name_pos
4368 @@ SU.Locals.strip_dollar
(Local_id.get_name
id),
4370 emit_final_global_op outer_pos
op )
4371 | A.Lvar
((_, str) as id) when is_local_this env str && is_incdec op ->
4372 (emit_local ~notice
:Notice
env id, rhs_instrs, empty
)
4373 | A.Lvar
(pos, name) when (not
(is_local_this env name)) || op = LValOp.Unset
4380 (get_local
env (pos, Local_id.get_name
name)) )
4381 | A.Array_get
((_, A.Lvar
(_, x
)), Some e
)
4382 when Local_id.get_name x
= SN.Superglobals.globals
->
4383 let final_global_op_instrs = emit_final_global_op
pos op in
4384 if rhs_stack_size
= 0 then
4385 (emit_expr
env e
, empty
, final_global_op_instrs)
4387 let (index_instrs
, under_top
) = emit_first_expr
env e
in
4389 (empty
, gather
[rhs_instrs; index_instrs
], final_global_op_instrs)
4391 (index_instrs
, rhs_instrs, final_global_op_instrs)
4392 | A.Array_get
(_, None
) when not
(Emit_env.does_env_allow_array_append
env)
4394 Emit_fatal.raise_fatal_runtime
pos "Can't use [] for reading"
4395 | A.Array_get
(base_expr
, opt_elem_expr
) ->
4398 | LValOp.Unset
-> MemberOpMode.Unset
4399 | _ -> MemberOpMode.Define
4401 let (elem_expr_instrs, elem_stack_size
) =
4403 ~
local_temp_kind:None
4404 ~null_coalesce_assignment
4408 let elem_expr_instrs =
4409 if null_coalesce_assignment
then
4414 let base_offset = elem_stack_size
+ rhs_stack_size
in
4415 let ( base_expr_instrs_begin
,
4416 base_expr_instrs_end
,
4423 ~null_coalesce_assignment
4432 ~null_coalesce_assignment
4434 (rhs_stack_size
+ cls_stack_size)
4437 let total_stack_size =
4438 elem_stack_size
+ base_stack_size
+ cls_stack_size
4441 emit_pos_then
pos @@ emit_final_member_op
total_stack_size op mk
4445 ( if null_coalesce_assignment
then
4448 base_expr_instrs_begin
);
4450 ( if null_coalesce_assignment
then
4453 base_expr_instrs_end
);
4456 gather
[emit_pos
pos; base_setup_instrs
; final_instr] )
4457 | A.Obj_get
(e1
, e2
, null_flavor) ->
4458 if null_flavor = Ast_defs.OG_nullsafe
then
4459 Emit_fatal.raise_fatal_parse
pos "?-> is not allowed in write context";
4462 | LValOp.Unset
-> MemberOpMode.Unset
4463 | _ -> MemberOpMode.Define
4465 let (_, _, prop_stack_size
) =
4466 emit_prop_expr ~null_coalesce_assignment
env null_flavor 0 e2
4468 let base_offset = prop_stack_size
+ rhs_stack_size
in
4469 let ( base_expr_instrs_begin
,
4470 base_expr_instrs_end
,
4477 ~null_coalesce_assignment
4484 let (mk, prop_expr_instrs
, _) =
4486 ~null_coalesce_assignment
4489 (rhs_stack_size
+ cls_stack_size)
4492 let prop_expr_instrs =
4493 if null_coalesce_assignment
then
4498 let total_stack_size =
4499 prop_stack_size
+ base_stack_size
+ cls_stack_size
4502 emit_pos_then
pos @@ emit_final_member_op
total_stack_size op mk
4506 ( if null_coalesce_assignment
then
4509 base_expr_instrs_begin
);
4511 ( if null_coalesce_assignment
then
4514 base_expr_instrs_end
);
4517 gather
[base_setup_instrs
; final_instr] )
4518 | A.Class_get
(cid, prop
) ->
4520 class_id_to_class_expr ~
resolve_self:false (Emit_env.get_scope
env) cid
4522 let final_instr = emit_pos_then
pos @@ emit_final_static_op
cid prop
op in
4523 (of_pair
@@ emit_class_expr
env cexpr prop
, rhs_instrs, final_instr)
4524 | A.Unop
(uop
, e
) ->
4528 [emit_lval_op_nonlist
env pos op e empty rhs_stack_size
; from_unop uop
]
4531 Emit_fatal.raise_fatal_parse
pos "Can't use return value in write context"
4534 let check_int_overflow =
4535 Hhbc_options.check_int_overflow !Hhbc_options.compiler_options
4538 | Ast_defs.Utild
-> instr (IOp BitNot
)
4539 | Ast_defs.Unot
-> instr (IOp Not
)
4543 ( if check_int_overflow then
4547 | Ast_defs.Uminus
->
4550 ( if check_int_overflow then
4558 | Ast_defs.Usilence
->
4559 failwith
"this unary operation cannot be translated"
4561 and emit_unop
env pos op e
=
4564 gather
[emit_expr
env e
; emit_pos_then
pos @@ from_unop
op]
4566 gather
[emit_expr
env e
; emit_pos_then
pos @@ from_unop
op]
4571 instr (ILitConst
(Int
Int64.zero
));
4573 emit_pos_then
pos @@ from_unop
op;
4575 | Ast_defs.Uminus
->
4579 instr (ILitConst
(Int
Int64.zero
));
4581 emit_pos_then
pos @@ from_unop
op;
4586 | Ast_defs.Updecr
->
4587 emit_lval_op
env pos (LValOp.IncDec
(unop_to_incdec_op op)) e None
4588 | Ast_defs.Usilence
->
4591 let temp_local = Local.get_unnamed_local
() in
4595 instr_silence_start
temp_local;
4598 (gather
[emit_pos
pos; instr_silence_end
temp_local]);
4600 instr_silence_end
temp_local;
4603 and emit_exprs
env (exprs
: Tast.expr list
) =
4607 gather
(emit_expr
env expr :: List.map exprs
(emit_expr
env))