2 * Copyright (c) 2017, Facebook, Inc.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
13 open Instruction_sequence
17 module TC
= Hhas_type_constraint
18 module SN
= Naming_special_names
19 module CBR
= Continue_break_rewriter
21 (* When using the PassX instructions we need to emit the right kind *)
22 module PassByRefKind
= struct
23 type t
= AllowCell
| WarnOnCell
| ErrorOnCell
26 (* Locals, array elements, and properties all support the same range of l-value
28 module LValOp
= struct
35 let self_name = ref (None
: string option)
36 let set_self n
= self_name := n
38 let compiler_options = ref Hhbc_options.default
39 let set_compiler_options o
= compiler_options := o
41 (* Emit a comment in lieu of instructions for not-yet-implemented features *)
42 let emit_nyi description
=
43 instr
(IComment
("NYI: " ^ description
))
46 String.sub id
1 (String.length id
- 1)
48 let make_varray p es
= p
, A.Array
(List.map es ~f
:(fun e
-> A.AFvalue e
))
49 let make_kvarray p kvs
=
50 p
, A.Array
(List.map kvs ~f
:(fun (k
, v
) -> A.AFkvalue
(k
, v
)))
52 (* Strict binary operations; assumes that operands are already on stack *)
54 let ints_overflow_to_ints =
55 Hhbc_options.ints_overflow_to_ints !compiler_options in
57 | A.Plus
-> instr
(IOp
(if ints_overflow_to_ints then Add
else AddO
))
58 | A.Minus
-> instr
(IOp
(if ints_overflow_to_ints then Sub
else SubO
))
59 | A.Star
-> instr
(IOp
(if ints_overflow_to_ints then Mul
else MulO
))
60 | A.Slash
-> instr
(IOp Div
)
61 | A.Eqeq
-> instr
(IOp Eq
)
62 | A.EQeqeq
-> instr
(IOp Same
)
63 | A.Starstar
-> instr
(IOp Pow
)
64 | A.Diff
-> instr
(IOp Neq
)
65 | A.Diff2
-> instr
(IOp NSame
)
66 | A.Lt
-> instr
(IOp Lt
)
67 | A.Lte
-> instr
(IOp Lte
)
68 | A.Gt
-> instr
(IOp Gt
)
69 | A.Gte
-> instr
(IOp Gte
)
70 | A.Dot
-> instr
(IOp Concat
)
71 | A.Amp
-> instr
(IOp BitAnd
)
72 | A.Bar
-> instr
(IOp BitOr
)
73 | A.Ltlt
-> instr
(IOp Shl
)
74 | A.Gtgt
-> instr
(IOp Shr
)
75 | A.Percent
-> instr
(IOp Mod
)
76 | A.Xor
-> instr
(IOp BitXor
)
77 | A.Eq _
-> emit_nyi "Eq"
80 failwith
"short-circuiting operator cannot be generated as a simple binop"
82 let binop_to_eqop op
=
83 let ints_overflow_to_ints =
84 Hhbc_options.ints_overflow_to_ints !compiler_options in
86 | A.Plus
-> Some
(if ints_overflow_to_ints then PlusEqual
else PlusEqualO
)
87 | A.Minus
-> Some
(if ints_overflow_to_ints then MinusEqual
else MinusEqualO
)
88 | A.Star
-> Some
(if ints_overflow_to_ints then MulEqual
else MulEqualO
)
89 | A.Slash
-> Some DivEqual
90 | A.Starstar
-> Some PowEqual
91 | A.Amp
-> Some AndEqual
92 | A.Bar
-> Some OrEqual
93 | A.Xor
-> Some XorEqual
94 | A.Ltlt
-> Some SlEqual
95 | A.Gtgt
-> Some SrEqual
96 | A.Percent
-> Some ModEqual
97 | A.Dot
-> Some ConcatEqual
100 let unop_to_incdec_op op
=
101 let ints_overflow_to_ints =
102 Hhbc_options.ints_overflow_to_ints !compiler_options in
104 | A.Uincr
-> Some
(if ints_overflow_to_ints then PreInc
else PreIncO
)
105 | A.Udecr
-> Some
(if ints_overflow_to_ints then PreDec
else PreDecO
)
106 | A.Upincr
-> Some
(if ints_overflow_to_ints then PostInc
else PostIncO
)
107 | A.Updecr
-> Some
(if ints_overflow_to_ints then PostDec
else PostDecO
)
110 let collection_type = function
118 | x
-> failwith
("unknown collection type '" ^ x ^
"'")
122 | "is_int" | "is_integer" -> Some OpInt
123 | "is_bool" -> Some OpBool
124 | "is_float" | "is_real" | "is_double" -> Some OpDbl
125 | "is_string" -> Some OpStr
126 | "is_array" -> Some OpArr
127 | "is_object" -> Some OpObj
128 | "is_null" -> Some OpNull
129 | "is_scalar" -> Some OpScalar
132 (* See EmitterVisitor::getPassByRefKind in emitter.cpp *)
133 let get_passByRefKind expr
=
134 let open PassByRefKind
in
135 let rec from_non_list_assignment permissive_kind expr
=
137 | A.New _
| A.Lvar _
| A.Clone _
-> AllowCell
138 | A.Binop
(A.Eq None
, (_
, A.List _
), e
) ->
139 from_non_list_assignment WarnOnCell e
140 | A.Array_get
(_
, Some _
) -> permissive_kind
141 | A.Binop
(A.Eq _
, _
, _
) -> WarnOnCell
142 | A.Unop
((A.Uincr
| A.Udecr
), _
) -> WarnOnCell
143 | _
-> ErrorOnCell
in
144 from_non_list_assignment AllowCell expr
146 let extract_shape_field_name_pstring = function
148 | A.SFclass_const
(_
, p
) -> p
150 let extract_shape_field_name = function
152 | A.SFclass_const
(_
, (_
, s
)) -> s
154 let rec expr_and_newc instr_to_add_new instr_to_add
= function
156 gather
[from_expr e
; instr_to_add_new
]
157 | A.AFkvalue
(k
, v
) ->
164 if x
= SN.SpecialIdents.this
then instr_this
165 else instr_cgetl
(Local.Named x
)
167 and emit_two_exprs e1 e2
=
168 (* Special case to make use of CGetL2 *)
170 | (_
, A.Lvar
(_
, local
)) ->
173 instr_cgetl2
(Local.Named local
);
181 and emit_binop op e1 e2
=
183 | A.AMpamp
-> emit_logical_and e1 e2
184 | A.BArbar
-> emit_logical_or e1 e2
185 | A.Eq None
-> emit_lval_op
LValOp.Set e1
(Some e2
)
186 | A.Eq
(Some obop
) ->
187 begin match binop_to_eqop obop
with
188 | None
-> emit_nyi "illegal eq op"
189 | Some op
-> emit_lval_op
(LValOp.SetOp op
) e1
(Some e2
)
193 emit_two_exprs e1 e2
;
197 and emit_instanceof e1 e2
=
199 | (_
, (_
, A.Id
(_
, id
))) ->
202 instr_instanceofd id
]
209 and emit_null_coalesce e1 e2
=
210 let end_label = Label.next_regular
() in
214 instr_istypec OpNull
;
216 instr_jmpnz
end_label;
219 instr_label
end_label;
222 and emit_cast hint expr
=
224 begin match hint
with
225 | A.Happly
((_
, id
), [])
226 when id
= SN.Typehints.int
227 || id
= SN.Typehints.integer
->
229 | A.Happly
((_
, id
), [])
230 when id
= SN.Typehints.bool
231 || id
= SN.Typehints.boolean
->
233 | A.Happly
((_
, id
), [])
234 when id
= SN.Typehints.string ->
235 instr
(IOp CastString
)
236 | A.Happly
((_
, id
), [])
237 when id
= SN.Typehints.object_cast
->
238 instr
(IOp CastObject
)
239 | A.Happly
((_
, id
), [])
240 when id
= SN.Typehints.array
->
241 instr
(IOp CastArray
)
242 | A.Happly
((_
, id
), [])
243 when id
= SN.Typehints.real
244 || id
= SN.Typehints.double
245 || id
= SN.Typehints.float ->
246 instr
(IOp CastDouble
)
256 and emit_conditional_expression etest etrue efalse
=
259 let false_label = Label.next_regular
() in
260 let end_label = Label.next_regular
() in
263 instr_jmpz
false_label;
266 instr_label
false_label;
268 instr_label
end_label;
271 let end_label = Label.next_regular
() in
275 instr_jmpnz
end_label;
278 instr_label
end_label;
281 and emit_aget class_expr
=
282 match class_expr
with
283 | _
, A.Lvar
(_
, id
) ->
284 instr
(IGet
(ClsRefGetL
(Local.Named id
, 0)))
288 from_expr class_expr
;
289 instr
(IGet
(ClsRefGetC
0))
292 and emit_new class_expr args uargs
=
293 let nargs = List.length args
+ List.length uargs
in
294 match class_expr
with
297 instr_fpushctord
nargs id
;
298 emit_args_and_call args uargs
;
303 emit_aget class_expr
;
304 instr_fpushctor
nargs 0;
305 emit_args_and_call args uargs
;
309 and emit_clone expr
=
315 and emit_shape expr fl
=
316 let are_values_all_literals =
317 List.for_all fl ~f
:(fun (_
, e
) -> is_literal e
)
320 if are_values_all_literals then
325 A.String
(extract_shape_field_name_pstring fn
)), e
))
327 from_expr
(fst expr
, A.Array
fl)
329 let es = List.map
fl ~f
:(fun (_
, e
) -> from_expr e
) in
330 let keys = List.map
fl ~f
:(fun (fn
, _
) -> extract_shape_field_name fn
) in
333 instr_newstructarray
keys;
336 and emit_tuple
p es =
337 (* Did you know that tuples are functions? *)
338 let af_list = List.map
es ~f
:(fun e
-> A.AFvalue e
) in
339 from_expr
(p, A.Array
af_list)
341 and emit_call_expr expr
=
342 let instrs, flavor
= emit_flavored_expr expr
in
345 (* If the instruction has produced a ref then unbox it *)
346 if flavor
= Flavor.Ref
then instr_unboxr
else empty
349 and emit_known_class_id cid
=
351 instr_string
(Utils.strip_ns cid
);
352 instr
(IGet
(ClsRefGetC
0))
355 and emit_class_id cid
=
356 if cid
= SN.Classes.cStatic
357 then instr
(IMisc
(LateBoundCls
0))
359 if cid
= SN.Classes.cSelf
360 then match !self_name with
361 | None
-> instr
(IMisc Self
)
362 | Some cid
-> emit_known_class_id cid
363 else emit_known_class_id cid
365 and emit_class_get param_num_opt cid id
=
367 (* We need to strip off the initial dollar *)
368 instr_string
(strip_dollar id
);
370 match param_num_opt
with
371 | None
-> instr
(IGet
(CGetS
0))
372 | Some i
-> instr
(ICall
(FPassS
(i
, 0)))
375 and emit_class_const cid id
=
376 if id
= SN.Members.mClass
then instr_string cid
377 else if cid
= SN.Classes.cStatic
380 IMisc
(LateBoundCls
0);
381 ILitConst
(ClsCns
(id
, 0));
383 else if cid
= SN.Classes.cSelf
385 match !self_name with
389 ILitConst
(ClsCns
(id
, 0));
391 | Some cid
-> instr
(ILitConst
(ClsCnsD
(id
, cid
)))
393 instr
(ILitConst
(ClsCnsD
(id
, cid
)))
396 let after_await = Label.next_regular
() in
400 instr_istypec OpNull
;
401 instr_jmpnz
after_await;
403 instr_label
after_await;
406 and emit_yield
= function
412 | A.AFkvalue
(e1
, e2
) ->
419 and emit_yield_break
() =
425 and emit_string2 exprs
=
430 instr
(IOp CastString
)
434 emit_two_exprs e1 e2
;
436 gather
(List.map
es (fun e
-> gather
[from_expr e
; instr
(IOp Concat
)]))
439 | [] -> failwith
"String2 with zero arguments is impossible"
441 and emit_lambda fundef ids
=
442 (* Closure conversion puts the class number used for CreateCl in the "name"
443 * of the function definition *)
444 let class_num = int_of_string
(snd fundef
.A.f_name
) in
446 (* TODO: deal with explicit use (...) capture variables *)
447 gather
@@ List.map ids
448 (fun (x
, _isref
) -> instr
(IGet
(CUGetL
(Local.Named
(snd x
)))));
449 instr
(IMisc
(CreateCl
(List.length ids
, class_num)))
454 | "__FILE__" -> instr
(ILitConst File
)
455 | "__DIR__" -> instr
(ILitConst Dir
)
457 (* If the expression goes on multi lines, we return the last line *)
458 let _, line
, _, _ = Pos.info_pos_extended
p in
460 | _ -> emit_nyi ("emit_id: " ^ s
)
462 and rename_xhp
(p, s
) =
463 (* Translates given :name to xhp_name *)
464 if String_utils.string_starts_with s
":"
465 then (p, "xhp_" ^
(String_utils.lstrip s
":"))
466 else failwith
"Incorrectly named xhp element"
468 and emit_xhp
p id attributes children
=
469 (* Translate into a constructor call. The arguments are:
470 * 1) shape-like array of attributes
471 * 2) vec-like array of children
472 * 3) filename, for debugging
473 * 4) line number, for debugging
475 let convert_xml_attr (name
, v
) = (A.SFlit name
, v
) in
476 let attributes = List.map ~f
:convert_xml_attr attributes in
477 let attribute_map = p, A.Shape
attributes in
478 let children_vec = make_varray p children
in
479 let filename = p, A.Id
(p, "__FILE__") in
480 let line = p, A.Id
(p, "__LINE__") in
483 (p, A.Id
(rename_xhp id
)),
484 [attribute_map ; children_vec ; filename ; line],
487 and emit_import flavor e
=
488 let import_instr = match flavor
with
489 | A.Include
-> instr
@@ IIncludeEvalDefine Incl
490 | A.Require
-> instr
@@ IIncludeEvalDefine Req
491 | A.IncludeOnce
-> instr
@@ IIncludeEvalDefine InclOnce
492 | A.RequireOnce
-> instr
@@ IIncludeEvalDefine ReqOnce
499 and emit_lvarvar n
(_, id
) =
501 instr_cgetl
(Local.Named id
);
502 gather
@@ List.replicate ~num
:n instr_cgetn
;
506 (* Note that this takes an Ast.expr, not a Nast.expr. *)
508 | A.Float
(_, litstr
) -> instr_double litstr
509 | A.String
(_, litstr
) -> instr_string litstr
510 (* TODO deal with integer out of range *)
511 | A.Int
(_, litstr
) -> instr_int_of_string litstr
512 | A.Null
-> instr_null
513 | A.False
-> instr_false
514 | A.True
-> instr_true
515 | A.Lvar
(_, x
) -> from_local x
516 | A.Class_const
((_, cid
), (_, id
)) -> emit_class_const cid id
517 | A.Unop
(op, e
) -> emit_unop
op e
518 | A.Binop
(op, e1
, e2
) -> emit_binop
op e1 e2
519 | A.Pipe
(e1
, e2
) -> emit_pipe e1 e2
520 | A.Dollardollar
-> instr_cgetl2
Local.Pipe
521 | A.InstanceOf
(e1
, e2
) -> emit_instanceof e1 e2
522 | A.NullCoalesce
(e1
, e2
) -> emit_null_coalesce e1 e2
523 | A.Cast
((_, hint
), e
) -> emit_cast hint e
524 | A.Eif
(etest
, etrue
, efalse
) ->
525 emit_conditional_expression etest etrue efalse
526 | A.Expr_list
es -> gather
@@ List.map
es ~f
:from_expr
527 | A.Call
((p, A.Id
(_, "tuple")), es, _) -> emit_tuple
p es
528 | A.Call
_ -> emit_call_expr expr
529 | A.New
(typeexpr
, args
, uargs
) -> emit_new typeexpr args uargs
530 | A.Array
es -> emit_collection expr
es
533 |> List.map ~f
:(fun (e1
, e2
) -> A.AFkvalue
(e1
, e2
))
534 |> emit_collection expr
537 |> List.map ~f
:(fun e
-> A.AFvalue e
)
538 |> emit_collection expr
539 | A.Collection
((pos
, name
), fields
) ->
540 emit_named_collection expr pos name fields
541 | A.Array_get
(base_expr
, opt_elem_expr
) ->
542 emit_array_get None base_expr opt_elem_expr
543 | A.Clone e
-> emit_clone e
544 | A.Shape
fl -> emit_shape expr
fl
545 | A.Obj_get
(expr
, prop
, nullflavor
) -> emit_obj_get None expr prop nullflavor
546 | A.Await e
-> emit_await e
547 | A.Yield e
-> emit_yield e
548 | A.Yield_break
-> emit_yield_break
()
550 failwith
"expected Lfun to be converted to Efun during closure conversion"
551 | A.Efun
(fundef
, ids
) -> emit_lambda fundef ids
552 | A.Class_get
((_, cid
), (_, id
)) -> emit_class_get None cid id
553 | A.String2
es -> emit_string2
es
554 | A.Unsafeexpr e
-> from_expr e
555 | A.Id id
-> emit_id id
556 | A.Xml
(id
, attributes, children
) ->
557 emit_xhp
(fst expr
) id
attributes children
558 | A.Import
(flavor
, e
) -> emit_import flavor e
559 | A.Lvarvar
(n
, id
) -> emit_lvarvar n id
561 | A.Id_type_arguments
(_, _) -> emit_nyi "id_type_arguments"
562 | A.List
_ -> emit_nyi "list"
564 and emit_static_collection ~transform_to_collection expr
es =
565 let a_label = Label.get_next_data_label
() in
566 (* Arrays can either contains values or key/value pairs *)
567 let need_index = match snd expr
with
568 | A.Collection
((_, "vec"), _)
569 | A.Collection
((_, "keyset"), _) -> false
576 ~f
:(fun (index
, l
) x
->
577 let open Constant_folder
in
578 (index
+ 1, match x
with
579 | A.AFvalue e
when need_index ->
580 literal_from_expr e
:: Int
(Int64.of_int index
) :: l
582 literal_from_expr e
:: l
583 | A.AFkvalue
(k
,v
) ->
584 literal_from_expr v
:: literal_from_expr k
:: l
)
587 let es = List.rev
es in
588 let lit_constructor = match snd expr
with
589 | A.Array
_ -> Array
(a_label, es)
590 | A.Collection
((_, "dict"), _) -> Dict
(a_label, es)
591 | A.Collection
((_, "vec"), _) -> Vec
(a_label, es)
592 | A.Collection
((_, "keyset"), _) -> Keyset
(a_label, es)
593 | _ -> failwith
"impossible"
595 let transform_instr =
596 match transform_to_collection
with
597 | Some n
-> instr_colfromarray n
601 instr
(ILitConst
lit_constructor);
605 (* transform_to_collection argument keeps track of
606 * what collection to transform to *)
607 and emit_dynamic_collection ~transform_to_collection expr
es =
609 List.for_all
es ~f
:(function A.AFkvalue
_ -> false | _ -> true)
611 let count = List.length
es in
612 if is_only_values && transform_to_collection
= None
then begin
613 let lit_constructor = match snd expr
with
614 | A.Array
_ -> NewPackedArray
count
615 | A.Collection
((_, "vec"), _) -> NewVecArray
count
616 | A.Collection
((_, "keyset"), _) -> NewKeysetArray
count
617 | _ -> failwith
"impossible"
622 ~f
:(function A.AFvalue e
-> from_expr e
| _ -> failwith
"impossible");
623 instr
@@ ILitConst
lit_constructor;
626 let lit_constructor = match snd expr
with
627 | A.Array
_ -> NewMixedArray
count
628 | A.Collection
((_, "dict"), _) -> NewDictArray
count
629 | _ -> failwith
"impossible"
631 let transform_instr =
632 match transform_to_collection
with
633 | Some n
-> instr_colfromarray n
637 if transform_to_collection
= None
638 then instr_add_new_elemc
639 else instr_col_add_new_elemc
642 (instr
@@ ILitConst
lit_constructor) :: transform_instr ::
643 (List.map
es ~f
:(expr_and_newc add_elem_instr instr_add_elemc
))
646 and emit_named_collection expr pos name fields
=
648 | "dict" | "vec" | "keyset" -> emit_collection expr fields
649 | "Vector" | "ImmVector" ->
650 let collection_type = collection_type name
in
652 emit_collection
(pos
, A.Collection
((pos
, "vec"), fields
)) fields
;
653 instr_colfromarray
collection_type;
655 | "Set" | "ImmSet" | "Map" | "ImmMap" ->
656 let collection_type = collection_type name
in
658 then instr_newcol
collection_type
661 ~transform_to_collection
:collection_type
662 (pos
, A.Array fields
)
665 let collection_type = collection_type name
in
666 let values = gather
@@ List.map
669 expr_and_newc instr_col_add_new_elemc instr_col_add_new_elemc x
)
672 instr_newcol
collection_type;
675 | _ -> failwith
@@ "collection: " ^ name ^
" does not exist"
677 and emit_collection ?
(transform_to_collection
) expr
es =
678 if is_literal_afield_list
es then
679 emit_static_collection ~transform_to_collection expr
es
681 emit_dynamic_collection ~transform_to_collection expr
es
683 and emit_pipe e1 e2
=
685 begin fun temp _break_label
->
686 let rewrite_dollardollar e
=
689 | IGet
(CGetL2
Local.Pipe
) ->
692 InstrSeq.map e ~f
:rewriter in
693 rewrite_dollardollar (from_expr e2
)
696 and emit_logical_and e1 e2
=
697 let left_is_false = Label.next_regular
() in
698 let right_is_true = Label.next_regular
() in
699 let its_done = Label.next_regular
() in
702 instr_jmpz
left_is_false;
704 instr_jmpnz
right_is_true;
705 instr_label
left_is_false;
708 instr_label
right_is_true;
710 instr_label
its_done ]
712 and emit_logical_or e1 e2
=
713 let its_true = Label.next_regular
() in
714 let its_done = Label.next_regular
() in
717 instr_jmpnz
its_true;
719 instr_jmpnz
its_true;
722 instr_label
its_true;
724 instr_label
its_done ]
726 and emit_quiet_expr
(_, expr_
as expr
) =
729 instr_cgetquietl
(Local.Named x
)
733 (* Emit code for e1[e2].
734 * If param_num_opt = Some i
735 * then this is the i'th parameter to a function
737 and emit_array_get param_num_opt base_expr opt_elem_expr
=
738 let elem_expr_instrs, elem_stack_size
= emit_elem_instrs opt_elem_expr
in
739 let base_expr_instrs, base_setup_instrs
, base_stack_size
=
740 emit_base
MemberOpMode.Warn elem_stack_size param_num_opt base_expr
in
741 let mk = get_elem_member_key
0 opt_elem_expr
in
742 let total_stack_size = elem_stack_size
+ base_stack_size
in
745 match param_num_opt
with
746 | None
-> QueryM
(total_stack_size, QueryOp.CGet
, mk)
747 | Some i
-> FPassM
(i
, total_stack_size, mk)
756 (* Emit code for e1->e2 or e1?->e2.
757 * If param_num_opt = Some i
758 * then this is the i'th parameter to a function
760 and emit_obj_get param_num_opt expr prop null_flavor
=
761 let prop_expr_instrs, prop_stack_size
= emit_prop_instrs prop
in
762 let base_expr_instrs, base_setup_instrs
, base_stack_size
=
763 emit_base
MemberOpMode.Warn prop_stack_size param_num_opt expr
in
764 let mk = get_prop_member_key null_flavor
0 prop
in
765 let total_stack_size = prop_stack_size
+ base_stack_size
in
768 match param_num_opt
with
769 | None
-> QueryM
(total_stack_size, QueryOp.CGet
, mk)
770 | Some i
-> FPassM
(i
, total_stack_size, mk)
779 and emit_elem_instrs opt_elem_expr
=
780 match opt_elem_expr
with
781 (* These all have special inline versions of member keys *)
782 | Some
(_, (A.Lvar
_ | A.Int
_ | A.String
_)) -> empty
, 0
783 | Some expr
-> from_expr expr
, 1
786 and emit_prop_instrs
(_, expr_
as expr
) =
788 (* These all have special inline versions of member keys *)
789 | A.Lvar
_ | A.Id
_ -> empty
, 0
790 | _ -> from_expr expr
, 1
792 (* Get the member key for an array element expression: the `elem` in
793 * expressions of the form `base[elem]`.
794 * If the array element is missing, use the special key `W`.
796 and get_elem_member_key stack_index opt_expr
=
798 (* Special case for local *)
799 | Some
(_, A.Lvar
(_, x
)) -> MemberKey.EL
(Local.Named x
)
800 (* Special case for literal integer *)
801 | Some
(_, A.Int
(_, str
)) -> MemberKey.EI
(Int64.of_string str
)
802 (* Special case for literal string *)
803 | Some
(_, A.String
(_, str
)) -> MemberKey.ET str
805 | Some
_ -> MemberKey.EC stack_index
806 (* ELement missing (so it's array append) *)
807 | None
-> MemberKey.W
809 (* Get the member key for a property *)
810 and get_prop_member_key null_flavor stack_index prop_expr
=
812 (* Special case for known property name *)
813 | (_, A.Id
(_, str
)) ->
814 begin match null_flavor
with
815 | Ast.OG_nullthrows
-> MemberKey.PT str
816 | Ast.OG_nullsafe
-> MemberKey.QT str
818 (* Special case for local *)
819 | (_, A.Lvar
(_, x
)) -> MemberKey.PL
(Local.Named x
)
821 | _ -> MemberKey.PC stack_index
823 (* Emit code for a base expression `expr` that forms part of
824 * an element access `expr[elem]` or field access `expr->fld`.
825 * The instructions are divided into three sections:
826 * 1. base and element/property expression instructions:
827 * push non-trivial base and key values on the stack
828 * 2. base selector instructions: a sequence of Base/Dim instructions that
829 * actually constructs the base address from "member keys" that are inlined
830 * in the instructions, or pulled from the key values that
831 * were pushed on the stack in section 1.
832 * 3. (constructed by the caller) a final accessor e.g. QueryM or setter
833 * e.g. SetOpM instruction that has the final key inlined in the
834 * instruction, or pulled from the key values that were pushed on the
835 * stack in section 1.
836 * The function returns a triple (base_instrs, base_setup_instrs, stack_size)
837 * where base_instrs is section 1 above, base_setup_instrs is section 2, and
838 * stack_size is the number of values pushed onto the stack by section 1.
840 * For example, the r-value expression $arr[3][$ix+2]
842 * # Section 1, pushing the value of $ix+2 on the stack
846 * # Section 2, constructing the base address of $arr[3]
849 * # Section 3, indexing the array using the value at stack position 0 (EC:0)
852 and emit_base mode base_offset param_num_opt
(_, expr_
as expr
) =
854 | A.Lvar
(_, x
) when x
= SN.SpecialIdents.this
->
855 instr
(IMisc CheckThis
),
862 match param_num_opt
with
863 | None
-> BaseL
(Local.Named x
, mode
)
864 | Some i
-> FPassBaseL
(i
, Local.Named x
)
868 | A.Array_get
(base_expr
, opt_elem_expr
) ->
869 let elem_expr_instrs, elem_stack_size
= emit_elem_instrs opt_elem_expr
in
870 let base_expr_instrs, base_setup_instrs
, base_stack_size
=
871 emit_base mode
(base_offset
+ elem_stack_size
) param_num_opt base_expr
in
872 let mk = get_elem_member_key base_offset opt_elem_expr
in
873 let total_stack_size = base_stack_size
+ elem_stack_size
in
881 match param_num_opt
with
882 | None
-> Dim
(mode
, mk)
883 | Some i
-> FPassDim
(i
, mk)
888 | A.Obj_get
(base_expr
, prop_expr
, null_flavor
) ->
889 let prop_expr_instrs, prop_stack_size
= emit_prop_instrs prop_expr
in
890 let base_expr_instrs, base_setup_instrs
, base_stack_size
=
891 emit_base mode
(base_offset
+ prop_stack_size
) param_num_opt base_expr
in
892 let mk = get_prop_member_key null_flavor base_offset prop_expr
in
893 let total_stack_size = prop_stack_size
+ base_stack_size
in
896 match param_num_opt
with
897 | None
-> Dim
(mode
, mk)
898 | Some i
-> FPassDim
(i
, mk)
910 | A.Class_get
((_, cid
), (_, id
)) ->
911 let prop_expr_instrs = instr_string
(strip_dollar id
) in
917 instr
(IBase
(BaseSC
(base_offset
, 0)))
922 let base_expr_instrs, flavor
= emit_flavored_expr expr
in
924 instr
(IBase
(if flavor
= Flavor.Ref
925 then BaseR base_offset
else BaseC base_offset
)),
928 and instr_fpass kind i
=
930 | PassByRefKind.AllowCell
-> instr
(ICall
(FPassC i
))
931 | PassByRefKind.WarnOnCell
-> instr
(ICall
(FPassCW i
))
932 | PassByRefKind.ErrorOnCell
-> instr
(ICall
(FPassCE i
))
934 and instr_fpassr i
= instr
(ICall
(FPassR i
))
936 and emit_arg i
((_, expr_
) as e
) =
938 | A.Lvar
(_, x
) -> instr_fpassl i
(Local.Named x
)
940 | A.Array_get
(base_expr
, opt_elem_expr
) ->
941 emit_array_get
(Some i
) base_expr opt_elem_expr
943 | A.Obj_get
(e1
, e2
, nullflavor
) ->
944 emit_obj_get
(Some i
) e1 e2 nullflavor
946 | A.Class_get
((_, cid
), (_, id
)) ->
947 emit_class_get
(Some i
) cid id
950 let instrs, flavor
= emit_flavored_expr e
in
953 if flavor
= Flavor.Ref
955 else instr_fpass
(get_passByRefKind e
) i
958 and emit_ignored_expr e
=
959 let instrs, flavor
= emit_flavored_expr e
in
965 (* Emit code to construct the argument frame and then make the call *)
966 and emit_args_and_call args uargs
=
967 let all_args = args
@ uargs
in
968 let nargs = List.length
all_args in
970 gather
(List.mapi
all_args emit_arg
);
972 then instr
(ICall
(FCall
nargs))
973 else instr
(ICall
(FCallUnpack
nargs))
976 and emit_call_lhs
(_, expr_
as expr
) nargs =
978 | A.Obj_get
(obj
, (_, A.Id
(_, id
)), null_flavor
) ->
981 instr
(ICall
(FPushObjMethodD
(nargs, id
, null_flavor
)));
984 | A.Class_const
((_, cid
), (_, id
)) when cid
= SN.Classes.cStatic
->
987 instr
(IMisc
(LateBoundCls
0));
988 instr
(ICall
(FPushClsMethod
(nargs, 0)));
991 | A.Class_const
((_, cid
), (_, id
)) when cid
.[0] = '$'
->
994 instr
(IGet
(ClsRefGetL
(Local.Named cid
, 0)));
995 instr
(ICall
(FPushClsMethod
(nargs, 0)))
998 | A.Class_const
((_, cid
), (_, id
)) ->
999 instr
(ICall
(FPushClsMethodD
(nargs, id
, cid
)))
1002 instr
(ICall
(FPushFuncD
(nargs, id
)))
1007 instr
(ICall
(FPushFunc
nargs))
1010 and emit_call
(_, expr_
as expr
) args uargs
=
1011 let nargs = List.length args
+ List.length uargs
in
1014 emit_call_lhs expr
nargs;
1015 emit_args_and_call args uargs
;
1018 | A.Id
(_, id
) when id
= SN.SpecialFunctions.echo
->
1019 let instrs = gather
@@ List.mapi args
begin fun i arg
->
1023 if i
= nargs-1 then empty
else instr_popc
1028 begin match args
, istype_op id
with
1029 | [(_, A.Lvar
(_, arg_id
))], Some i
->
1030 instr
(IIsset
(IsTypeL
(Local.Named arg_id
, i
))),
1032 | [arg_expr
], Some i
->
1035 instr
(IIsset
(IsTypeC i
))
1042 (* Emit code for an expression that might leave a cell or reference on the
1043 * stack. Return which flavor it left.
1045 and emit_flavored_expr
(_, expr_
as expr
) =
1047 | A.Call
(e
, args
, uargs
) ->
1048 emit_call e args uargs
1050 from_expr expr
, Flavor.Cell
1052 and is_literal expr
=
1055 | A.Collection
((_, "vec"), afl
)
1056 | A.Collection
((_, "keyset"), afl
)
1057 | A.Collection
((_, "dict"), afl
) -> is_literal_afield_list afl
1066 and is_literal_afield_list afl
=
1068 ~f
:(function A.AFvalue e
-> is_literal e
1069 | A.AFkvalue
(k
,v
) -> is_literal k
&& is_literal v
)
1071 and emit_final_member_op stack_index
op mk =
1073 | LValOp.Set
-> instr
(IFinal
(SetM
(stack_index
, mk)))
1074 | LValOp.SetOp
op -> instr
(IFinal
(SetOpM
(stack_index
, op, mk)))
1075 | LValOp.IncDec
op -> instr
(IFinal
(IncDecM
(stack_index
, op, mk)))
1077 and emit_final_local_op
op lid
=
1079 | LValOp.Set
-> instr
(IMutator
(SetL lid
))
1080 | LValOp.SetOp
op -> instr
(IMutator
(SetOpL
(lid
, op)))
1081 | LValOp.IncDec
op -> instr
(IMutator
(IncDecL
(lid
, op)))
1083 and emit_final_static_op
op =
1085 | LValOp.Set
-> instr
(IMutator
(SetS
0))
1086 | LValOp.SetOp
op -> instr
(IMutator
(SetOpS
(op, 0)))
1087 | LValOp.IncDec
op -> instr
(IMutator
(IncDecS
(op, 0)))
1089 (* Given a local $local and a list of integer array indices i_1, ..., i_n,
1090 * generate code to extract the value of $local[i_n]...[i_1]:
1092 * Dim Warn EI:i_n ...
1094 * QueryM 0 CGet EI:i_1
1096 and emit_array_get_fixed local indices
=
1098 instr
(IBase
(BaseL
(local
, MemberOpMode.Warn
))) ::
1099 List.rev_mapi indices
(fun i ix
->
1100 let mk = MemberKey.EI
(Int64.of_int ix
) in
1102 then instr
(IFinal
(QueryM
(0, QueryOp.CGet
, mk)))
1103 else instr
(IBase
(Dim
(MemberOpMode.Warn
, mk))))
1106 (* Generate code for each lvalue assignment in a list destructuring expression.
1107 * Lvalues are assigned right-to-left, regardless of the nesting structure. So
1108 * list($a, list($b, $c)) = $d
1109 * and list(list($a, $b), $c) = $d
1110 * will both assign to $c, $b and $a in that order.
1112 and emit_lval_op_list local indices expr
=
1114 | (_, A.List exprs
) ->
1117 List.mapi exprs
(fun i expr
-> emit_lval_op_list local
(i
::indices
) expr
)
1119 (* Generate code to access the element from the array *)
1120 let access_instrs = emit_array_get_fixed local indices
in
1121 (* Generate code to assign to the lvalue *)
1122 let assign_instrs = emit_lval_op_nonlist
LValOp.Set expr
access_instrs 1 in
1128 (* Emit code for an l-value operation *)
1129 and emit_lval_op
op expr1 opt_expr2
=
1130 match op, expr1
, opt_expr2
with
1131 (* Special case for list destructuring, only on assignment *)
1132 | LValOp.Set
, (_, A.List
_), Some expr2
->
1133 stash_in_local ~leave_on_stack
:true expr2
1134 begin fun local _break_label
->
1135 emit_lval_op_list local
[] expr1
1138 let rhs_instrs, rhs_stack_size
=
1139 match opt_expr2
with
1141 | Some e
-> from_expr e
, 1 in
1142 emit_lval_op_nonlist
op expr1
rhs_instrs rhs_stack_size
1144 and emit_lval_op_nonlist
op expr1
rhs_instrs rhs_stack_size
=
1146 | (_, A.Lvar
(_, id
)) ->
1149 emit_final_local_op
op (Local.Named id
)
1152 | (_, A.Array_get
(base_expr
, opt_elem_expr
)) ->
1153 let elem_expr_instrs, elem_stack_size
= emit_elem_instrs opt_elem_expr
in
1154 let base_offset = elem_stack_size
+ rhs_stack_size
in
1155 let base_expr_instrs, base_setup_instrs
, base_stack_size
=
1156 emit_base
MemberOpMode.Define
base_offset None base_expr
in
1157 let mk = get_elem_member_key rhs_stack_size opt_elem_expr
in
1158 let total_stack_size = elem_stack_size
+ base_stack_size
in
1159 let final_instr = emit_final_member_op
total_stack_size op mk in
1168 | (_, A.Obj_get
(e1
, e2
, null_flavor
)) ->
1169 let prop_expr_instrs, prop_stack_size
= emit_prop_instrs e2
in
1170 let base_offset = prop_stack_size
+ rhs_stack_size
in
1171 let base_expr_instrs, base_setup_instrs
, base_stack_size
=
1172 emit_base
MemberOpMode.Define
base_offset None e1
in
1173 let mk = get_prop_member_key null_flavor rhs_stack_size e2
in
1174 let total_stack_size = prop_stack_size
+ base_stack_size
in
1175 let final_instr = emit_final_member_op
total_stack_size op mk in
1184 | (_, A.Class_get
((_, cid
), (_, id
))) ->
1185 let prop_expr_instrs = instr_string
(strip_dollar id
) in
1186 let final_instr = emit_final_static_op
op in
1196 emit_nyi "lval expression";
1200 and emit_unop
op e
=
1201 let ints_overflow_to_ints =
1202 Hhbc_options.ints_overflow_to_ints !compiler_options in
1204 | A.Utild
-> gather
[from_expr e
; instr
(IOp BitNot
)]
1205 | A.Unot
-> gather
[from_expr e
; instr
(IOp Not
)]
1207 [instr
(ILitConst
(Int
(Int64.zero
)));
1209 instr
(IOp
(if ints_overflow_to_ints then Add
else AddO
))]
1210 | A.Uminus
-> gather
1211 [instr
(ILitConst
(Int
(Int64.zero
)));
1213 instr
(IOp
(if ints_overflow_to_ints then Sub
else SubO
))]
1214 | A.Uincr
| A.Udecr
| A.Upincr
| A.Updecr
->
1215 begin match unop_to_incdec_op op with
1216 | None
-> emit_nyi "incdec"
1218 emit_lval_op
(LValOp.IncDec incdec_op
) e None
1221 emit_nyi "references"
1223 and from_exprs exprs
=
1224 gather
(List.map exprs from_expr
)
1226 and stash_in_local ?
(leave_on_stack
=false) e f
=
1227 let break_label = Label.next_regular
() in
1229 | (_, A.Lvar
(_, id
)) ->
1231 f
(Local.Named id
) break_label;
1232 instr_label
break_label;
1235 let temp = Local.get_unnamed_local
() in
1236 let fault_label = Label.next_fault
() in
1244 (f
temp break_label)
1249 instr_label
break_label;
1250 if leave_on_stack
then instr_pushl
temp else instr_unsetl
temp