2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/compiler/analysis/emitter.h"
18 #include <boost/algorithm/string/predicate.hpp>
29 #include "folly/MapUtil.h"
30 #include "folly/Memory.h"
31 #include "folly/ScopeGuard.h"
33 #include "hphp/compiler/builtin_symbols.h"
34 #include "hphp/compiler/analysis/class_scope.h"
35 #include "hphp/compiler/analysis/file_scope.h"
36 #include "hphp/compiler/analysis/function_scope.h"
37 #include "hphp/compiler/expression/array_element_expression.h"
38 #include "hphp/compiler/expression/array_pair_expression.h"
39 #include "hphp/compiler/expression/assignment_expression.h"
40 #include "hphp/compiler/expression/binary_op_expression.h"
41 #include "hphp/compiler/expression/class_constant_expression.h"
42 #include "hphp/compiler/expression/closure_expression.h"
43 #include "hphp/compiler/expression/constant_expression.h"
44 #include "hphp/compiler/expression/dynamic_variable.h"
45 #include "hphp/compiler/expression/encaps_list_expression.h"
46 #include "hphp/compiler/expression/expression_list.h"
47 #include "hphp/compiler/expression/include_expression.h"
48 #include "hphp/compiler/expression/list_assignment.h"
49 #include "hphp/compiler/expression/modifier_expression.h"
50 #include "hphp/compiler/expression/new_object_expression.h"
51 #include "hphp/compiler/expression/object_method_expression.h"
52 #include "hphp/compiler/expression/object_property_expression.h"
53 #include "hphp/compiler/expression/parameter_expression.h"
54 #include "hphp/compiler/expression/qop_expression.h"
55 #include "hphp/compiler/expression/scalar_expression.h"
56 #include "hphp/compiler/expression/simple_variable.h"
57 #include "hphp/compiler/expression/simple_function_call.h"
58 #include "hphp/compiler/expression/static_member_expression.h"
59 #include "hphp/compiler/expression/unary_op_expression.h"
60 #include "hphp/compiler/expression/yield_expression.h"
61 #include "hphp/compiler/expression/await_expression.h"
62 #include "hphp/compiler/expression/query_expression.h"
63 #include "hphp/compiler/statement/block_statement.h"
64 #include "hphp/compiler/statement/break_statement.h"
65 #include "hphp/compiler/statement/case_statement.h"
66 #include "hphp/compiler/statement/catch_statement.h"
67 #include "hphp/compiler/statement/class_constant.h"
68 #include "hphp/compiler/statement/class_variable.h"
69 #include "hphp/compiler/statement/do_statement.h"
70 #include "hphp/compiler/statement/echo_statement.h"
71 #include "hphp/compiler/statement/exp_statement.h"
72 #include "hphp/compiler/statement/for_statement.h"
73 #include "hphp/compiler/statement/foreach_statement.h"
74 #include "hphp/compiler/statement/finally_statement.h"
75 #include "hphp/compiler/statement/function_statement.h"
76 #include "hphp/compiler/statement/global_statement.h"
77 #include "hphp/compiler/statement/goto_statement.h"
78 #include "hphp/compiler/statement/if_branch_statement.h"
79 #include "hphp/compiler/statement/if_statement.h"
80 #include "hphp/compiler/statement/label_statement.h"
81 #include "hphp/compiler/statement/method_statement.h"
82 #include "hphp/compiler/statement/return_statement.h"
83 #include "hphp/compiler/statement/statement_list.h"
84 #include "hphp/compiler/statement/static_statement.h"
85 #include "hphp/compiler/statement/switch_statement.h"
86 #include "hphp/compiler/statement/try_statement.h"
87 #include "hphp/compiler/statement/unset_statement.h"
88 #include "hphp/compiler/statement/while_statement.h"
89 #include "hphp/compiler/statement/use_trait_statement.h"
90 #include "hphp/compiler/statement/class_require_statement.h"
91 #include "hphp/compiler/statement/trait_prec_statement.h"
92 #include "hphp/compiler/statement/trait_alias_statement.h"
93 #include "hphp/compiler/statement/typedef_statement.h"
94 #include "hphp/compiler/parser/parser.h"
95 #include "hphp/hhbbc/hhbbc.h"
97 #include "hphp/util/safe-cast.h"
98 #include "hphp/util/logger.h"
99 #include "hphp/util/job-queue.h"
100 #include "hphp/parser/hphp.tab.hpp"
101 #include "hphp/runtime/base/repo-auth-type.h"
102 #include "hphp/runtime/vm/bytecode.h"
103 #include "hphp/runtime/vm/native.h"
104 #include "hphp/runtime/vm/repo.h"
105 #include "hphp/runtime/vm/repo-global-data.h"
106 #include "hphp/runtime/vm/as.h"
107 #include "hphp/runtime/base/stats.h"
108 #include "hphp/runtime/base/static-string-table.h"
109 #include "hphp/runtime/base/runtime-option.h"
110 #include "hphp/runtime/base/zend-string.h"
111 #include "hphp/runtime/base/zend-functions.h"
112 #include "hphp/runtime/base/type-conversions.h"
113 #include "hphp/runtime/base/builtin-functions.h"
114 #include "hphp/runtime/base/variable-serializer.h"
115 #include "hphp/runtime/base/program-functions.h"
116 #include "hphp/runtime/base/unit-cache.h"
117 #include "hphp/runtime/ext_hhvm/ext_hhvm.h"
118 #include "hphp/runtime/ext_zend_compat/hhvm/zend-wrap-func.h"
119 #include "hphp/runtime/vm/preclass-emitter.h"
121 #include "hphp/system/systemlib.h"
125 ///////////////////////////////////////////////////////////////////////////////
127 TRACE_SET_MOD(emitter
)
129 using uchar
= unsigned char;
132 static const char None
= 0x00;
135 * We don't actually track the U flavor (we treat it as a C),
136 * because there's nothing important to do with it for emission.
137 * The verifier will check they are only created at the appropriate
140 static const char C
= 0x01; // Cell symbolic flavor
141 static const char V
= 0x02; // Var symbolic flavor
142 static const char A
= 0x03; // Classref symbolic flavor
143 static const char R
= 0x04; // Return value symbolic flavor
144 static const char F
= 0x05; // Function argument symbolic flavor
145 static const char L
= 0x06; // Local symbolic flavor
146 static const char T
= 0x07; // String literal symbolic flavor
147 static const char I
= 0x08; // int literal symbolic flavor
148 static const char H
= 0x09; // $this symbolic flavor
150 static const char N
= 0x10; // Name marker
151 static const char G
= 0x20; // Global name marker
152 static const char E
= 0x30; // Element marker
153 static const char W
= 0x40; // New element marker
154 static const char P
= 0x50; // Property marker
155 static const char S
= 0x60; // Static property marker
156 static const char M
= 0x70; // Non elem/prop/W part of M-vector
157 static const char K
= 0x80; // Marker for information about a class base
159 static const char CN
= C
| N
;
160 static const char CG
= C
| G
;
161 static const char CS
= C
| S
;
162 static const char LN
= L
| N
;
163 static const char LG
= L
| G
;
164 static const char LS
= L
| S
;
165 static const char AM
= A
| M
;
167 char GetSymFlavor(char sym
) { return (sym
& 0x0F); }
168 char GetMarker(char sym
) { return (sym
& 0xF0); }
169 std::string
ToString(char sym
) {
170 char symFlavor
= StackSym::GetSymFlavor(sym
);
173 case StackSym::C
: res
= "C"; break;
174 case StackSym::V
: res
= "V"; break;
175 case StackSym::A
: res
= "A"; break;
176 case StackSym::R
: res
= "R"; break;
177 case StackSym::F
: res
= "F"; break;
178 case StackSym::L
: res
= "L"; break;
179 case StackSym::T
: res
= "T"; break;
180 case StackSym::I
: res
= "I"; break;
181 case StackSym::H
: res
= "H"; break;
184 char marker
= StackSym::GetMarker(sym
);
186 case StackSym::N
: res
+= "N"; break;
187 case StackSym::G
: res
+= "G"; break;
188 case StackSym::E
: res
+= "E"; break;
189 case StackSym::W
: res
+= "W"; break;
190 case StackSym::P
: res
+= "P"; break;
191 case StackSym::S
: res
+= "S"; break;
192 case StackSym::K
: res
+= "K"; break;
196 if (sym
== StackSym::None
) {
206 //=============================================================================
209 #define InvariantViolation(...) do { \
210 Logger::Warning(__VA_ARGS__); \
211 Logger::Warning("Eval stack at the time of error: %s", \
212 m_evalStack.pretty().c_str()); \
216 // RAII guard for function creation.
218 EmitterVisitor
* m_ev
;
223 FuncFinisher(EmitterVisitor
* ev
, Emitter
& e
, FuncEmitter
* fe
)
224 : m_ev(ev
), m_e(e
), m_fe(fe
) {
225 TRACE(2, "FuncFinisher constructed: %s\n", m_fe
->name
->data());
229 TRACE(2, "Finishing func: %s\n", m_fe
->name
->data());
230 m_ev
->finishFunc(m_e
, m_fe
);
234 // RAII guard for temporarily overriding an Emitter's location
235 class LocationGuard
{
240 LocationGuard(Emitter
& e
, LocationPtr newLoc
)
241 : m_e(e
), m_loc(e
.getTempLocation()) {
242 if (newLoc
) m_e
.setTempLocation(newLoc
);
245 m_e
.setTempLocation(m_loc
);
249 // Count the number of stack elements in an immediate vector.
250 static int32_t countStackValues(const std::vector
<uchar
>& immVec
) {
251 assert(!immVec
.empty());
254 const uint8_t* vec
= &immVec
[0];
256 // Count the location; the LS location type accounts for up to two
257 // values on the stack, all other location types account for at most
258 // one value on the stack. Subtract the number that are actually
260 const LocationCode locCode
= LocationCode(*vec
++);
261 count
+= numLocationCodeStackVals(locCode
);
262 const int numLocImms
= numLocationCodeImms(locCode
);
263 for (int i
= 0; i
< numLocImms
; ++i
) {
264 decodeVariableSizeImm(&vec
);
267 // Count each of the members; MEC and MPC account for one value on
268 // the stack, while MW/MEL/MPL/MET/MPT/MEI don't account for any
269 // values on the stack.
270 while (vec
- &immVec
[0] < int(immVec
.size())) {
271 MemberCode code
= MemberCode(*vec
++);
272 if (memberCodeHasImm(code
)) {
273 decodeMemberCodeImm(&vec
, code
);
274 } else if (code
!= MW
) {
278 assert(vec
- &immVec
[0] == int(immVec
.size()));
282 #define O(name, imm, pop, push, flags) \
283 void Emitter::name(imm) { \
284 auto const opcode = Op::name; \
285 Offset curPos UNUSED = getUnitEmitter().bcPos(); \
286 getEmitterVisitor().prepareEvalStack(); \
288 const int nIn UNUSED = COUNT_##pop; \
291 getUnitEmitter().emitOp(Op##name); \
293 getUnitEmitter().recordSourceLocation(m_tempLoc ? m_tempLoc.get() : \
294 m_node->getLocation().get(), curPos); \
295 if (flags & TF) getEmitterVisitor().restoreJumpTargetEvalStack(); \
296 if (opcode == Op::FCall) getEmitterVisitor().recordCall(); \
297 getEmitterVisitor().setPrevOpcode(opcode); \
301 #define COUNT_ONE(t) 1
302 #define COUNT_TWO(t1,t2) 2
303 #define COUNT_THREE(t1,t2,t3) 3
304 #define COUNT_FOUR(t1,t2,t3,t4) 4
305 #define COUNT_MMANY 0
306 #define COUNT_C_MMANY 0
307 #define COUNT_R_MMANY 0
308 #define COUNT_V_MMANY 0
309 #define COUNT_FMANY 0
310 #define COUNT_CVMANY 0
311 #define COUNT_CVUMANY 0
312 #define COUNT_CMANY 0
313 #define COUNT_SMANY 0
317 #define TWO(t1, t2) \
318 DEC_##t1 a1, DEC_##t2 a2
319 #define THREE(t1, t2, t3) \
320 DEC_##t1 a1, DEC_##t2 a2, DEC_##t3 a3
321 #define FOUR(t1, t2, t3, t4) \
322 DEC_##t1 a1, DEC_##t2 a2, DEC_##t3 a3, DEC_##t4 a4
324 #define DEC_MA std::vector<uchar>
325 #define DEC_BLA std::vector<Label*>&
326 #define DEC_SLA std::vector<StrOff>&
327 #define DEC_ILA std::vector<IterPair>&
328 #define DEC_IVA int32_t
329 #define DEC_LA int32_t
330 #define DEC_IA int32_t
331 #define DEC_I64A int64_t
332 #define DEC_DA double
333 #define DEC_SA const StringData*
334 #define DEC_RATA RepoAuthType
335 #define DEC_AA ArrayData*
336 #define DEC_BA Label&
337 #define DEC_OA(type) type
338 #define DEC_VSA std::vector<std::string>&
343 #define POP_TWO(t1, t2) \
346 #define POP_THREE(t1, t2, t3) \
350 #define POP_FOUR(t1, t2, t3, t4) \
356 getEmitterVisitor().popEvalStackMMany()
357 #define POP_C_MMANY \
358 getEmitterVisitor().popEvalStack(StackSym::C); \
359 getEmitterVisitor().popEvalStackMMany()
360 #define POP_V_MMANY \
361 getEmitterVisitor().popEvalStack(StackSym::V); \
362 getEmitterVisitor().popEvalStackMMany()
363 #define POP_R_MMANY \
364 getEmitterVisitor().popEvalStack(StackSym::R); \
365 getEmitterVisitor().popEvalStackMMany()
367 getEmitterVisitor().popEvalStackMany(a1, StackSym::F)
369 getEmitterVisitor().popEvalStackCVMany(a1)
370 #define POP_CVUMANY POP_CVMANY
372 getEmitterVisitor().popEvalStackMany(a1, StackSym::C)
374 getEmitterVisitor().popEvalStackMany(a1.size(), StackSym::C)
376 #define POP_CV(i) getEmitterVisitor().popEvalStack(StackSym::C, i, curPos)
377 #define POP_VV(i) getEmitterVisitor().popEvalStack(StackSym::V, i, curPos)
378 #define POP_AV(i) getEmitterVisitor().popEvalStack(StackSym::A, i, curPos)
379 #define POP_RV(i) getEmitterVisitor().popEvalStack(StackSym::R, i, curPos)
380 #define POP_FV(i) getEmitterVisitor().popEvalStack(StackSym::F, i, curPos)
382 // Pop of virtual "locs" on the stack that turn into immediates.
383 #define POP_LA_ONE(t) \
385 #define POP_LA_TWO(t1, t2) \
388 #define POP_LA_THREE(t1, t2, t3) \
392 #define POP_LA_FOUR(t1, t2, t3, t4) \
400 #define POP_LA_BLA(i)
401 #define POP_LA_SLA(i)
402 #define POP_LA_ILA(i)
403 #define POP_LA_IVA(i)
405 #define POP_LA_I64A(i)
408 #define POP_LA_RATA(i)
411 #define POP_LA_IMPL(x)
412 #define POP_LA_OA(i) POP_LA_IMPL
413 #define POP_LA_VSA(i)
415 #define POP_LA_LA(i) \
416 getEmitterVisitor().popSymbolicLocal(opcode, i, curPos)
419 #define PUSH_ONE(t) \
421 #define PUSH_TWO(t1, t2) \
424 #define PUSH_THREE(t1, t2, t3) \
428 #define PUSH_FOUR(t1, t2, t3, t4) \
433 #define PUSH_INS_1(t) PUSH_INS_1_##t
434 #define PUSH_INS_2(t) PUSH_INS_2_##t
436 #define PUSH_CV getEmitterVisitor().pushEvalStack(StackSym::C)
437 #define PUSH_UV PUSH_CV
438 #define PUSH_VV getEmitterVisitor().pushEvalStack(StackSym::V)
439 #define PUSH_AV getEmitterVisitor().pushEvalStack(StackSym::A)
440 #define PUSH_RV getEmitterVisitor().pushEvalStack(StackSym::R)
441 #define PUSH_FV getEmitterVisitor().pushEvalStack(StackSym::F)
443 #define PUSH_INS_1_CV \
444 getEmitterVisitor().getEvalStack().insertAt(1, StackSym::C);
445 #define PUSH_INS_1_AV \
446 getEmitterVisitor().getEvalStack().insertAt(1, StackSym::A);
448 #define PUSH_INS_2_CV \
449 getEmitterVisitor().getEvalStack().insertAt(2, StackSym::C);
452 #define IMPL_ONE(t) \
454 #define IMPL_TWO(t1, t2) \
457 #define IMPL_THREE(t1, t2, t3) \
461 #define IMPL_FOUR(t1, t2, t3, t4) \
467 #define IMPL_MA(var) do { \
468 getUnitEmitter().emitInt32(var.size()); \
469 getUnitEmitter().emitInt32(countStackValues(var)); \
470 for (unsigned int i = 0; i < var.size(); ++i) { \
471 getUnitEmitter().emitByte(var[i]); \
474 #define IMPL1_MA IMPL_MA(a1)
475 #define IMPL2_MA IMPL_MA(a2)
476 #define IMPL3_MA IMPL_MA(a3)
477 #define IMPL4_MA IMPL_MA(a4)
479 #define IMPL_BLA(var) do { \
480 getUnitEmitter().emitInt32(var.size()); \
481 for (unsigned int i = 0; i < var.size(); ++i) { \
485 #define IMPL1_BLA IMPL_BLA(a1)
486 #define IMPL2_BLA IMPL_BLA(a2)
487 #define IMPL3_BLA IMPL_BLA(a3)
488 #define IMPL4_BLA IMPL_BLA(a4)
490 #define IMPL_ILA(var) do { \
491 auto& ue = getUnitEmitter(); \
492 ue.emitInt32(var.size()); \
493 for (auto& i : var) { \
494 ue.emitInt32(i.kind); \
495 ue.emitInt32(i.id); \
498 #define IMPL1_ILA IMPL_ILA(a1)
499 #define IMPL2_ILA IMPL_ILA(a2)
500 #define IMPL3_ILA IMPL_ILA(a3)
501 #define IMPL4_ILA IMPL_ILA(a4)
503 #define IMPL_SLA(var) do { \
504 auto& ue = getUnitEmitter(); \
505 ue.emitInt32(var.size()); \
506 for (auto& i : var) { \
507 ue.emitInt32(i.str); \
511 #define IMPL1_SLA IMPL_SLA(a1)
512 #define IMPL2_SLA IMPL_SLA(a2)
513 #define IMPL3_SLA IMPL_SLA(a3)
515 #define IMPL_VSA(var) do { \
516 auto n = var.size(); \
517 getUnitEmitter().emitInt32(n); \
518 for (size_t i = 0; i < n; ++i) { \
519 IMPL_SA((HPHP::String(var[i])).get()); \
522 #define IMPL1_VSA IMPL_VSA(a1)
523 #define IMPL2_VSA IMPL_VSA(a2)
524 #define IMPL3_VSA IMPL_VSA(a3)
525 #define IMPL4_VSA IMPL_VSA(a4)
527 #define IMPL_IVA(var) do { \
528 getUnitEmitter().emitIVA(var); \
530 #define IMPL1_IVA IMPL_IVA(a1)
531 #define IMPL2_IVA IMPL_IVA(a2)
532 #define IMPL3_IVA IMPL_IVA(a3)
533 #define IMPL4_IVA IMPL_IVA(a4)
535 #define IMPL1_LA IMPL_IVA(a1)
536 #define IMPL2_LA IMPL_IVA(a2)
537 #define IMPL3_LA IMPL_IVA(a3)
538 #define IMPL4_LA IMPL_IVA(a4)
540 #define IMPL1_IA IMPL_IVA(a1)
541 #define IMPL2_IA IMPL_IVA(a2)
542 #define IMPL3_IA IMPL_IVA(a3)
543 #define IMPL4_IA IMPL_IVA(a4)
545 #define IMPL_I64A(var) getUnitEmitter().emitInt64(var)
546 #define IMPL1_I64A IMPL_I64A(a1)
547 #define IMPL2_I64A IMPL_I64A(a2)
548 #define IMPL3_I64A IMPL_I64A(a3)
549 #define IMPL4_I64A IMPL_I64A(a4)
551 #define IMPL_SA(var) \
552 getUnitEmitter().emitInt32(getUnitEmitter().mergeLitstr(var))
553 #define IMPL1_SA IMPL_SA(a1)
554 #define IMPL2_SA IMPL_SA(a2)
555 #define IMPL3_SA IMPL_SA(a3)
556 #define IMPL4_SA IMPL_SA(a4)
558 // Emitting RATAs isn't supported here right now. (They're only
559 // created in hhbbc.)
560 #define IMPL_RATA(var) not_reached()
561 #define IMPL1_RATA IMPL_RATA(a1)
562 #define IMPL2_RATA IMPL_RATA(a2)
563 #define IMPL3_RATA IMPL_RATA(a3)
564 #define IMPL4_RATA IMPL_RATA(a4)
566 #define IMPL_AA(var) \
567 getUnitEmitter().emitInt32(getUnitEmitter().mergeArray(var))
568 #define IMPL1_AA IMPL_AA(a1)
569 #define IMPL2_AA IMPL_AA(a2)
570 #define IMPL3_AA IMPL_AA(a3)
571 #define IMPL4_AA IMPL_AA(a4)
573 #define IMPL_DA(var) getUnitEmitter().emitDouble(var)
574 #define IMPL1_DA IMPL_DA(a1)
575 #define IMPL2_DA IMPL_DA(a2)
576 #define IMPL3_DA IMPL_DA(a3)
577 #define IMPL4_DA IMPL_DA(a4)
579 #define IMPL_BA(var) \
580 if ((var).getAbsoluteOffset() == InvalidAbsoluteOffset) { \
581 /* For forward jumps, we store information about the */ \
582 /* current instruction in the Label. When the Label is */ \
583 /* set, it will fix up any instructions that reference */ \
584 /* it, and then it will call recordJumpTarget */ \
585 (var).bind(getEmitterVisitor(), curPos, getUnitEmitter().bcPos()); \
587 /* For backward jumps, we simply call recordJumpTarget */ \
588 getEmitterVisitor().recordJumpTarget((var).getAbsoluteOffset()); \
590 getUnitEmitter().emitInt32((var).getAbsoluteOffset() - curPos);
591 #define IMPL1_BA IMPL_BA(a1)
592 #define IMPL2_BA IMPL_BA(a2)
593 #define IMPL3_BA IMPL_BA(a3)
594 #define IMPL4_BA IMPL_BA(a4)
596 #define IMPL_OA(var) getUnitEmitter().emitByte(static_cast<uint8_t>(var))
597 #define IMPL1_OA(type) IMPL_OA(a1)
598 #define IMPL2_OA(type) IMPL_OA(a2)
599 #define IMPL3_OA(type) IMPL_OA(a3)
600 #define IMPL4_OA(type) IMPL_OA(a4)
743 static void checkJmpTargetEvalStack(const SymbolicStack
& source
,
744 const SymbolicStack
& dest
) {
745 if (source
.size() != dest
.size()) {
746 Logger::Warning("Emitter detected a point in the bytecode where the "
747 "depth of the stack is not the same for all possible "
748 "control flow paths. source size: %d. dest size: %d",
751 Logger::Warning("src stack : %s", source
.pretty().c_str());
752 Logger::Warning("dest stack: %s", dest
.pretty().c_str());
757 for (unsigned int i
= 0; i
< source
.size(); ++i
) {
758 char flavor
= StackSym::GetSymFlavor(source
.get(i
));
759 bool matches
= source
.get(i
) == dest
.get(i
) &&
760 (flavor
!= StackSym::L
|| source
.getLoc(i
) == dest
.getLoc(i
)) &&
761 (flavor
!= StackSym::T
|| source
.getName(i
) == dest
.getName(i
)) &&
762 (flavor
!= StackSym::I
|| source
.getInt(i
) == dest
.getInt(i
));
764 Logger::Warning("Emitter detected a point in the bytecode where the "
765 "symbolic flavor of a slot on the stack is not the same "
766 "for all possible control flow paths");
767 Logger::Warning("src stack : %s", source
.pretty().c_str());
768 Logger::Warning("dest stack: %s", dest
.pretty().c_str());
775 std::string
SymbolicStack::pretty() const {
776 std::ostringstream out
;
777 out
<< " [" << std::hex
;
779 for (size_t i
= 0; i
< m_symStack
.size(); ++i
) {
780 while (j
< m_actualStack
.size() && m_actualStack
[j
] < int(i
)) {
783 if (j
< m_actualStack
.size() && m_actualStack
[j
] == int(i
)) {
786 out
<< StackSym::ToString(m_symStack
[i
].sym
);
787 char flavor
= StackSym::GetSymFlavor(m_symStack
[i
].sym
);
788 if (flavor
== StackSym::L
|| flavor
== StackSym::I
) {
789 out
<< ":" << m_symStack
[i
].intval
;
790 } else if (flavor
== StackSym::T
) {
791 out
<< ":" << m_symStack
[i
].metaData
.name
->data();
798 void SymbolicStack::push(char sym
) {
799 if (sym
!= StackSym::W
&& sym
!= StackSym::K
&& sym
!= StackSym::L
&&
800 sym
!= StackSym::T
&& sym
!= StackSym::I
&& sym
!= StackSym::H
) {
801 m_actualStack
.push_back(m_symStack
.size());
802 *m_actualStackHighWaterPtr
= MAX(*m_actualStackHighWaterPtr
,
803 (int)m_actualStack
.size());
805 m_symStack
.push_back(SymEntry(sym
));
808 void SymbolicStack::pop() {
809 // TODO(drew): assert eval stack unknown is false?
810 assert(!m_symStack
.empty());
811 char sym
= m_symStack
.back().sym
;
812 char flavor
= StackSym::GetSymFlavor(sym
);
813 if (StackSym::GetMarker(sym
) != StackSym::W
&&
814 flavor
!= StackSym::L
&& flavor
!= StackSym::T
&& flavor
!= StackSym::I
&&
815 flavor
!= StackSym::H
) {
816 assert(!m_actualStack
.empty());
817 m_actualStack
.pop_back();
819 m_symStack
.pop_back();
822 char SymbolicStack::top() const {
823 assert(!m_symStack
.empty());
824 return m_symStack
.back().sym
;
827 char SymbolicStack::get(int index
) const {
828 assert(index
>= 0 && index
< (int)m_symStack
.size());
829 return m_symStack
[index
].sym
;
832 const StringData
* SymbolicStack::getName(int index
) const {
833 assert(index
>= 0 && index
< (int)m_symStack
.size());
834 if (m_symStack
[index
].metaType
== META_LITSTR
) {
835 return m_symStack
[index
].metaData
.name
;
840 const StringData
* SymbolicStack::getClsName(int index
) const {
841 assert(index
>= 0 && index
< (int)m_symStack
.size());
842 return m_symStack
[index
].className
;
845 bool SymbolicStack::isCls(int index
) const {
846 assert(index
>= 0 && index
< (int)m_symStack
.size());
847 return m_symStack
[index
].className
!= nullptr;
850 void SymbolicStack::setString(const StringData
* s
) {
851 assert(m_symStack
.size());
852 SymEntry
& se
= m_symStack
.back();
853 if (se
.metaType
== META_LITSTR
) {
854 assert(se
.metaData
.name
== s
);
856 assert(se
.metaType
== META_NONE
);
858 se
.metaData
.name
= s
;
859 se
.metaType
= META_LITSTR
;
862 void SymbolicStack::setKnownCls(const StringData
* s
, bool nonNull
) {
863 assert(m_symStack
.size());
864 SymEntry
& se
= m_symStack
.back();
865 assert(!se
.className
|| se
.className
== s
);
869 void SymbolicStack::setInt(int64_t v
) {
870 assert(m_symStack
.size());
871 m_symStack
.back().intval
= v
;
874 void SymbolicStack::cleanTopMeta() {
875 SymEntry
& se
= m_symStack
.back();
876 se
.clsBaseType
= CLS_INVALID
;
877 se
.metaType
= META_NONE
;
880 void SymbolicStack::setClsBaseType(ClassBaseType type
) {
881 assert(!m_symStack
.empty());
882 m_symStack
.back().clsBaseType
= type
;
885 void SymbolicStack::setUnnamedLocal(int index
,
888 assert(size_t(index
) < m_symStack
.size());
889 assert(m_symStack
[index
].sym
== StackSym::K
);
890 assert(m_symStack
[index
].clsBaseType
== CLS_UNNAMED_LOCAL
);
891 m_symStack
[index
].intval
= localId
;
892 m_symStack
[index
].unnamedLocalStart
= startOff
;
895 void SymbolicStack::set(int index
, char sym
) {
896 assert(index
>= 0 && index
< (int)m_symStack
.size());
897 // XXX Add assert in debug build to make sure W is not getting
898 // written or overwritten by something else
899 m_symStack
[index
].sym
= sym
;
902 unsigned SymbolicStack::size() const {
903 return m_symStack
.size();
906 bool SymbolicStack::empty() const {
907 return m_symStack
.empty();
910 void SymbolicStack::clear() {
912 m_actualStack
.clear();
916 void SymbolicStack::consumeBelowTop(int depth
) {
917 if (int(m_symStack
.size()) < depth
+ 1) {
919 "Emitter tried to consumeBelowTop() when the symbolic "
920 "stack did not have enough elements in it.");
924 assert(int(m_symStack
.size()) >= depth
+ 1);
925 int index
= m_symStack
.size() - depth
- 1;
926 m_symStack
.erase(m_symStack
.begin() + index
);
929 * Update any indexes into the actual stack that pointed to or past
932 * (In practice they should all be past---we don't currently ever
933 * remove below the top for actual stack elements.)
935 for (size_t i
= 0; i
< m_actualStack
.size(); ++i
) {
936 if (m_actualStack
[i
] >= index
) {
942 int SymbolicStack::getActualPos(int vpos
) const {
943 assert(vpos
>= 0 && vpos
< int(m_symStack
.size()));
944 assert(!m_actualStack
.empty());
945 for (int j
= int(m_actualStack
.size()) - 1; j
>= 0; --j
) {
946 if (m_actualStack
[j
] == vpos
) {
953 char SymbolicStack::getActual(int index
) const {
954 assert(index
>= 0 && index
< (int)m_actualStack
.size());
955 return get(m_actualStack
[index
]);
958 void SymbolicStack::setActual(int index
, char sym
) {
959 assert(index
>= 0 && index
< (int)m_actualStack
.size());
960 set(m_actualStack
[index
], sym
);
963 SymbolicStack::ClassBaseType
964 SymbolicStack::getClsBaseType(int index
) const {
965 assert(m_symStack
.size() > size_t(index
));
966 assert(m_symStack
[index
].sym
== StackSym::K
);
967 assert(m_symStack
[index
].clsBaseType
!= CLS_INVALID
);
968 return m_symStack
[index
].clsBaseType
;
971 int SymbolicStack::getLoc(int index
) const {
972 assert(m_symStack
.size() > size_t(index
));
973 assert(StackSym::GetSymFlavor(m_symStack
[index
].sym
) == StackSym::L
||
974 m_symStack
[index
].clsBaseType
== CLS_NAMED_LOCAL
||
975 m_symStack
[index
].clsBaseType
== CLS_UNNAMED_LOCAL
);
976 assert(m_symStack
[index
].intval
!= -1);
977 return m_symStack
[index
].intval
;
980 int64_t SymbolicStack::getInt(int index
) const {
981 assert(m_symStack
.size() > size_t(index
));
982 assert(StackSym::GetSymFlavor(m_symStack
[index
].sym
) == StackSym::I
);
983 return m_symStack
[index
].intval
;
986 Offset
SymbolicStack::getUnnamedLocStart(int index
) const {
987 assert(m_symStack
.size() > size_t(index
));
988 assert(m_symStack
[index
].sym
== StackSym::K
);
989 assert(m_symStack
[index
].clsBaseType
== CLS_UNNAMED_LOCAL
);
990 return m_symStack
[index
].unnamedLocalStart
;
993 // Insert an element in the actual stack at the specified depth of the
995 void SymbolicStack::insertAt(int depth
, char sym
) {
996 assert(depth
<= sizeActual() && depth
> 0);
997 int virtIdx
= m_actualStack
[sizeActual() - depth
];
999 m_symStack
.insert(m_symStack
.begin() + virtIdx
, SymEntry(sym
));
1000 m_actualStack
.insert(m_actualStack
.end() - depth
, virtIdx
);
1002 for (size_t i
= sizeActual() - depth
+ 1; i
< m_actualStack
.size(); ++i
) {
1007 int SymbolicStack::sizeActual() const {
1008 return m_actualStack
.size();
1011 void SymbolicStack::pushFDesc() {
1012 m_fdescCount
+= kNumActRecCells
;
1013 *m_fdescHighWaterPtr
= MAX(*m_fdescHighWaterPtr
, m_fdescCount
);
1016 void SymbolicStack::popFDesc() {
1017 m_fdescCount
-= kNumActRecCells
;
1020 void Label::set(Emitter
& e
) {
1023 "Label::set was called more than once on the same "
1024 "Label; originally set to %d; now %d",
1026 e
.getUnitEmitter().bcPos());
1029 m_off
= e
.getUnitEmitter().bcPos();
1030 // Fix up any forward jumps that reference to this Label
1031 for (std::vector
<std::pair
<Offset
, Offset
> >::const_iterator it
=
1032 m_emittedOffs
.begin(); it
!= m_emittedOffs
.end(); ++it
) {
1033 e
.getUnitEmitter().emitInt32(m_off
- it
->first
, it
->second
);
1035 EmitterVisitor
& ev
= e
.getEmitterVisitor();
1036 if (!m_emittedOffs
.empty()) {
1037 // If there were forward jumps that referenced this Label,
1038 // compare the the eval stack from the first foward jump we
1039 // saw with the current eval stack
1040 if (!ev
.evalStackIsUnknown()) {
1041 checkJmpTargetEvalStack(m_evalStack
, ev
.getEvalStack());
1043 // Assume the current eval stack matches that of the forward branch
1044 ev
.setEvalStack(m_evalStack
);
1046 // Fix up the EmitterVisitor's table of jump targets
1047 ev
.recordJumpTarget(m_off
, ev
.getEvalStack());
1049 // There were no forward jumps that referenced this label
1050 ev
.prepareEvalStack();
1051 // Fix up the EmitterVisitor's table of jump targets
1052 ev
.recordJumpTarget(m_off
, ev
.getEvalStack());
1056 bool Label::isUsed() {
1057 return (m_off
!= InvalidAbsoluteOffset
|| !m_emittedOffs
.empty());
1060 void Label::bind(EmitterVisitor
& ev
, Offset instrAddr
, Offset offAddr
) {
1061 if (m_off
!= InvalidAbsoluteOffset
) {
1062 InvariantViolation("Label::bind was called on a Label that has already "
1067 bool labelHasEvalStack
= !m_emittedOffs
.empty();
1068 m_emittedOffs
.push_back(std::pair
<Offset
, Offset
>(instrAddr
, offAddr
));
1069 if (labelHasEvalStack
) {
1070 checkJmpTargetEvalStack(m_evalStack
, ev
.getEvalStack());
1072 m_evalStack
= ev
.getEvalStack();
1076 struct FPIRegionRecorder
{
1077 FPIRegionRecorder(EmitterVisitor
* ev
, UnitEmitter
& ue
, SymbolicStack
& stack
,
1079 : m_ev(ev
), m_ue(ue
), m_stack(stack
), m_fpOff(m_stack
.sizeActual()),
1081 m_stack
.pushFDesc();
1083 ~FPIRegionRecorder() {
1085 m_ev
->newFPIRegion(m_start
, m_ue
.bcPos(), m_fpOff
);
1088 EmitterVisitor
* m_ev
;
1090 SymbolicStack
& m_stack
;
1095 //=============================================================================
1098 const int ControlTarget::k_unsetState
= -1;
1100 ControlTarget::ControlTarget(EmitterVisitor
* router
)
1101 : m_visitor(router
), m_label(), m_state(k_unsetState
) {
1102 assert(m_visitor
!= nullptr);
1105 ControlTarget::~ControlTarget() {
1106 // The scope of states used in finally router is controled
1107 // using shared pointer refcounting. State numbers can be reused once
1108 // all the references are released.
1109 if (isRegistered()) {
1110 m_visitor
->unregisterControlTarget(this);
1111 m_state
= k_unsetState
;
1115 bool ControlTarget::isRegistered() {
1116 return m_state
!= k_unsetState
;
1119 //=============================================================================
1122 Region::Region(Region::Kind kind
, RegionPtr parent
)
1125 m_iterKind(KindOfIter
),
1130 EmitterVisitor::registerReturn(StatementPtr s
, Region
* region
, char sym
) {
1133 for (r
= region
; true; r
= r
->m_parent
.get()) {
1135 if (r
->isFinally()) {
1136 throw EmitterVisitor::IncludeTimeFatalException(s
,
1137 "Return inside a finally block is not supported");
1139 if (r
->m_returnTargets
.count(sym
)) {
1140 // We registered the control target before. Just return the existing one.
1141 t
= r
->m_returnTargets
[sym
].target
;
1144 // Haven't registered the control target with region r yet.
1145 if (r
->m_parent
.get() == nullptr) {
1146 // Top of the region hierarchy - allocate a fresh control target.
1147 t
= std::make_shared
<ControlTarget
>(this);
1148 r
= r
->m_parent
.get();
1152 assert(t
!= nullptr);
1153 if (!t
->isRegistered()) {
1154 registerControlTarget(t
.get());
1156 // For all entries we visited that did not have this control target in
1157 // m_returnTargets, add this control target to these entries' m_returnTargets
1158 // fields as appropriate.
1160 for (r
= region
; r
!= end
; r
= r
->m_parent
.get()) {
1161 r
->m_returnTargets
[sym
] = ControlTargetInfo(t
, r
->isTryFinally());
1166 EmitterVisitor::registerGoto(StatementPtr s
, Region
* region
, StringData
* name
,
1170 for (r
= region
; true; r
= r
->m_parent
.get()) {
1172 if (r
->isFinally()) {
1173 throw EmitterVisitor::IncludeTimeFatalException(s
,
1174 "Goto inside a finally block is not supported");
1176 if (r
->m_gotoTargets
.count(name
)) {
1177 // We registered the control target before. Just return the existing one.
1178 t
= r
->m_gotoTargets
[name
].target
;
1179 if (alloc
&& r
->isTryFinally()) {
1180 r
->m_gotoTargets
[name
].used
= true;
1184 // Haven't registered the control target in this region yet.
1185 if (r
->m_parent
.get() == nullptr) {
1186 // Top of the region hierarchy - allocate a fresh control target.
1187 t
= std::make_shared
<ControlTarget
>(this);
1188 r
= r
->m_parent
.get();
1192 assert(t
!= nullptr);
1193 if (alloc
&& !t
->isRegistered()) {
1194 registerControlTarget(t
.get());
1196 // For all entries we visited that did not have this control target in
1197 // m_gotoTargets, add this control target to these entries' m_gotoTargets
1198 // fields as appropriate.
1200 for (r
= region
; r
!= end
; r
= r
->m_parent
.get()) {
1201 r
->m_gotoTargets
[name
] = ControlTargetInfo(t
, alloc
&& r
->isTryFinally());
1206 void EmitterVisitor::registerYieldAwait(ExpressionPtr e
) {
1207 Region
* region
= m_regions
.back().get();
1208 for (; region
; region
= region
->m_parent
.get()) {
1209 if (region
->isFinally()) {
1210 throw EmitterVisitor::IncludeTimeFatalException(e
,
1211 "Yield expression inside a finally block is not supported");
1217 EmitterVisitor::registerBreak(StatementPtr s
, Region
* region
, int depth
,
1223 for (r
= region
; true; r
= r
->m_parent
.get()) {
1225 if (r
->isFinally()) {
1226 throw EmitterVisitor::IncludeTimeFatalException(s
,
1227 "Break jump is not allowed to leave a finally block");
1229 if (r
->m_breakTargets
.count(d
)) {
1230 // We registered the control target before. Just return the existing one.
1231 t
= r
->m_breakTargets
[d
].target
;
1232 if (alloc
&& r
->isTryFinally()) {
1233 r
->m_breakTargets
[d
].used
= true;
1237 if (r
->m_kind
!= Region::Kind::LoopOrSwitch
) {
1241 // We should never reach this case if alloc == true, since the loop or
1242 // switch should have registered its break target in advance
1244 // If this is a loop, and depth is one, just allocate a fresh
1245 // control target, since there are no more entries to delegate to.
1246 t
= std::make_shared
<ControlTarget
>(this);
1247 r
= r
->m_parent
.get();
1250 // Otherwise, delegate to the parent. One break level has been
1251 // taken care of by this region.
1254 assert(t
!= nullptr);
1256 if (!t
->isRegistered()) {
1257 registerControlTarget(t
.get());
1260 // For all of the entries that did not have this control target in
1261 // m_breakTargets, add this control target to these entries' m_breakTargets
1262 // fields as appropriate.
1264 for (r
= region
; r
!= end
; r
= r
->m_parent
.get()) {
1265 r
->m_breakTargets
[depth
] =
1266 ControlTargetInfo(t
, alloc
&& r
->isTryFinally());
1267 if (r
->m_kind
== Region::Kind::LoopOrSwitch
) {
1275 EmitterVisitor::registerContinue(StatementPtr s
, Region
* region
, int depth
,
1281 for (r
= region
; true; r
= r
->m_parent
.get()) {
1283 if (r
->isFinally()) {
1284 throw EmitterVisitor::IncludeTimeFatalException(s
,
1285 "Continue jump is not allowed to leave a finally block");
1287 if (r
->m_continueTargets
.count(d
)) {
1288 // We registered the control target before. Just return the existing one.
1289 t
= r
->m_continueTargets
[d
].target
;
1290 if (alloc
&& r
->isTryFinally()) {
1291 r
->m_continueTargets
[d
].used
= true;
1295 if (r
->m_kind
!= Region::Kind::LoopOrSwitch
) {
1299 // We should never reach this case if alloc == true, since the loop or
1300 // switch should have registered its continue target in advance
1302 t
= std::make_shared
<ControlTarget
>(this);
1303 r
= r
->m_parent
.get();
1306 // Otherwise, delegate to the parent. One continue level has been
1307 // taken care of by this region.
1310 assert(t
!= nullptr);
1311 if (alloc
&& !t
->isRegistered()) {
1312 registerControlTarget(t
.get());
1314 // For all of the entries that did not have this control target in
1315 // m_continueTargets, add this control target to these entries'
1316 // m_continueTargets fields as appropriate.
1318 for (r
= region
; r
!= end
; r
= r
->m_parent
.get()) {
1319 r
->m_continueTargets
[depth
] =
1320 ControlTargetInfo(t
, alloc
&& r
->isTryFinally());
1321 if (r
->m_kind
== Region::Kind::LoopOrSwitch
) {
1328 int Region::getCaseCount() {
1329 int count
= 1; // The fall-through case.
1330 for (auto& t
: m_returnTargets
) {
1331 if (t
.second
.target
->isRegistered()) ++count
;
1333 for (auto& t
: m_breakTargets
) {
1334 if (t
.second
.target
->isRegistered()) ++count
;
1336 for (auto& t
: m_continueTargets
) {
1337 if (t
.second
.target
->isRegistered()) ++count
;
1339 for (auto& t
: m_gotoTargets
) {
1340 if (t
.second
.target
->isRegistered()) ++count
;
1345 void EmitterVisitor::emitIterFree(Emitter
& e
, IterVec
& iters
) {
1346 for (auto& iter
: iters
) {
1347 assert(iter
.id
!= -1);
1348 if (iter
.kind
== KindOfMIter
) {
1349 e
.MIterFree(iter
.id
);
1351 assert(iter
.kind
== KindOfIter
);
1352 e
.IterFree(iter
.id
);
1357 void EmitterVisitor::emitJump(Emitter
& e
, IterVec
& iters
, Label
& target
) {
1358 if (!iters
.empty()) {
1359 e
.IterBreak(iters
, target
);
1366 void EmitterVisitor::emitReturn(Emitter
& e
, char sym
, bool hasConstraint
,
1368 Region
* region
= m_regions
.back().get();
1369 registerReturn(s
, region
, sym
);
1370 assert(getEvalStack().size() == 1);
1371 assert(region
->m_returnTargets
.count(sym
));
1373 for (Region
* r
= region
; true; r
= r
->m_parent
.get()) {
1374 auto& t
= r
->m_returnTargets
[sym
].target
;
1375 if (r
->m_parent
== nullptr) {
1376 // At the top of the hierarchy, no more finally blocks to run.
1377 // Free the pending iterators and perform the actual return.
1378 emitIterFree(e
, iters
);
1379 if (sym
== StackSym::C
) {
1380 if (hasConstraint
) {
1385 assert(sym
== StackSym::V
);
1386 if (hasConstraint
) {
1394 if (r
->isTryFinally()) {
1395 // We encountered a try block - a finally needs to be run
1396 // before returning.
1397 Id stateLocal
= getStateLocal();
1398 Id retLocal
= getRetLocal();
1399 // Set the unnamed "state" local to the appropriate identifier
1400 emitVirtualLocal(retLocal
);
1401 assert(t
->isRegistered());
1405 // Emit code stashing the current return value in the "ret" unnamed
1407 if (sym
== StackSym::C
) {
1408 // For legacy purposes, SetL expects its immediate argument to
1409 // be present on the symbolic stack. In reality, retLocal is
1410 // an immediate argument. The following pop and push instructions
1411 // ensure that the arguments are place on the symbolic stack
1412 // in a correct order. In reality the following three calls are
1414 popEvalStack(StackSym::C
);
1415 emitVirtualLocal(retLocal
);
1416 pushEvalStack(StackSym::C
);
1420 assert(sym
== StackSym::V
);
1421 popEvalStack(StackSym::V
);
1422 emitVirtualLocal(retLocal
);
1423 pushEvalStack(StackSym::V
);
1427 emitJump(e
, iters
, r
->m_finallyLabel
);
1430 if (r
->isForeach()) {
1431 iters
.push_back(IterPair(r
->m_iterKind
, r
->m_iterId
));
1436 void EmitterVisitor::emitGoto(Emitter
& e
, StringData
* name
, StatementPtr s
) {
1437 Region
* region
= m_regions
.back().get();
1438 registerGoto(s
, region
, name
, true);
1439 assert(!region
->isFinally());
1440 assert(region
->m_gotoTargets
.count(name
));
1442 for (Region
* r
= region
; true; r
= r
->m_parent
.get()) {
1443 auto t
= r
->m_gotoTargets
[name
].target
;
1444 if (r
->m_gotoLabels
.count(name
)) {
1445 // If only the destination label is within the statement
1446 // associated with the current statement, just perform a
1447 // direct jump. Free the pending iterators on the way.
1448 emitJump(e
, iters
, t
->m_label
);
1451 if (r
->isTryFinally()) {
1452 // We came across a try region, need for run a finally block.
1453 // Store appropriate value inside the state local.
1454 Id stateLocal
= getStateLocal();
1455 emitVirtualLocal(stateLocal
);
1456 assert(t
->isRegistered());
1460 // Jump to the finally block and free any pending iterators on the
1462 emitJump(e
, iters
, r
->m_finallyLabel
);
1465 if (r
->isForeach()) {
1466 iters
.push_back(IterPair(r
->m_iterKind
, r
->m_iterId
));
1471 void EmitterVisitor::emitBreak(Emitter
& e
, int depth
, StatementPtr s
) {
1472 Region
* region
= m_regions
.back().get();
1473 registerBreak(s
, region
, depth
, true);
1475 assert(!region
->isFinally());
1476 assert(region
->m_parent
!= nullptr);
1477 assert(region
->m_breakTargets
.count(depth
));
1480 for (Region
* r
= region
; true; r
= r
->m_parent
.get()) {
1481 auto t
= r
->m_breakTargets
[depth
].target
;
1482 if (r
->isTryFinally()) {
1483 // Encountered a try block, need to run finally.
1484 assert(r
->m_breakTargets
.count(depth
));
1485 assert(t
->isRegistered());
1486 Id stateLocal
= getStateLocal();
1487 emitVirtualLocal(stateLocal
);
1491 emitJump(e
, iters
, r
->m_finallyLabel
);
1494 if (r
->m_kind
!= Region::Kind::LoopOrSwitch
) {
1497 // Free iterator for the current loop whether or not
1498 // this is the last loop that we jump out of.
1499 if (r
->isForeach()) {
1500 iters
.push_back(IterPair(r
->m_iterKind
, r
->m_iterId
));
1503 // Last loop to jumpt out of. Performa direct jump to the
1504 // break lable and free any pending iterators left.
1505 emitJump(e
, iters
, t
->m_label
);
1512 void EmitterVisitor::emitContinue(Emitter
& e
, int depth
, StatementPtr s
) {
1513 Region
* region
= m_regions
.back().get();
1514 registerContinue(s
, region
, depth
, true);
1516 assert(!region
->isFinally());
1517 assert(region
->m_parent
!= nullptr);
1518 assert(region
->m_continueTargets
.count(depth
));
1521 for (Region
* r
= region
; true; r
= r
->m_parent
.get()) {
1522 auto t
= r
->m_continueTargets
[depth
].target
;
1523 if (r
->isTryFinally()) {
1524 // Encountered a try block, need to run finally.
1525 assert(r
->m_continueTargets
.count(depth
));
1526 Id stateLocal
= getStateLocal();
1527 emitVirtualLocal(stateLocal
);
1528 assert(t
->isRegistered());
1532 emitJump(e
, iters
, r
->m_finallyLabel
);
1535 if (r
->m_kind
!= Region::Kind::LoopOrSwitch
) {
1539 // Last level. Don't free the iterator for the current loop
1540 // however free any earlier pending iterators.
1541 emitJump(e
, iters
, t
->m_label
);
1544 // Only free the iterator for the current loop if this is
1545 // NOT the last level to continue out of.
1546 if (r
->isForeach()) {
1547 iters
.push_back(IterPair(r
->m_iterKind
, r
->m_iterId
));
1553 void EmitterVisitor::emitFinallyEpilogue(Emitter
& e
, Region
* region
) {
1554 assert(region
!= nullptr);
1555 assert(region
->isTryFinally());
1556 assert(region
->m_finallyLabel
.isSet());
1557 int count
= region
->getCaseCount();
1561 // If there is only one case (the fall-through case) then we're done
1565 // Otherwise, we need to emit some conditional jumps/switches to handle
1566 // the different cases. We start by builing up a vector of Label* that
1567 // we'll use for the Switch instruction and/or for conditional branches.
1568 int maxState
= region
->getMaxState();
1569 std::vector
<Label
*> cases
;
1570 while (cases
.size() <= maxState
) {
1571 cases
.push_back(new Label());
1573 // Now that we have our vector of Label*'s ready, we can emit a
1574 // Switch instruction and/or conditional branches, and we can
1575 // emit the body of each case.
1576 Id stateLocal
= getStateLocal();
1577 emitVirtualLocal(stateLocal
);
1578 e
.IssetL(stateLocal
);
1581 // A switch is needed since there are more than two cases.
1582 emitVirtualLocal(stateLocal
);
1583 e
.CGetL(stateLocal
);
1584 e
.Switch(cases
, 0, 0);
1586 for (auto& p
: region
->m_returnTargets
) {
1587 if (p
.second
.used
) emitReturnTrampoline(e
, region
, cases
, p
.first
);
1589 assert(region
->isTryFinally());
1590 int max_depth
= region
->getBreakContinueDepth();
1591 for (int i
= 1; i
<= max_depth
; ++i
) {
1592 if (region
->isBreakUsed(i
)) emitBreakTrampoline(e
, region
, cases
, i
);
1593 if (region
->isContinueUsed(i
)) emitContinueTrampoline(e
, region
, cases
, i
);
1595 for (auto& p
: region
->m_gotoTargets
) {
1596 if (p
.second
.used
) emitGotoTrampoline(e
, region
, cases
, p
.first
);
1598 for (auto c
: cases
) {
1604 void EmitterVisitor::emitReturnTrampoline(Emitter
& e
,
1606 std::vector
<Label
*>& cases
,
1608 assert(region
->isTryFinally());
1609 assert(region
->m_parent
!= nullptr);
1610 assert(region
->m_returnTargets
.count(sym
));
1611 auto& t
= region
->m_returnTargets
[sym
].target
;
1612 cases
[t
->m_state
]->set(e
);
1615 // We are emitting a case in a finally epilogue, therefore skip
1616 // the current try region and start from its parent
1617 for (region
= region
->m_parent
.get(); true; region
= region
->m_parent
.get()) {
1618 assert(region
->m_returnTargets
.count(sym
));
1619 assert(region
->m_returnTargets
[sym
].target
->isRegistered());
1620 // Add pending iterator if applicable
1621 if (region
->isForeach()) {
1622 iters
.push_back(IterPair(region
->m_iterKind
, region
->m_iterId
));
1624 if (region
->m_parent
== nullptr) {
1625 // At the bottom of the hierarchy. Restore the return value
1626 // and perform the actual return.
1627 Id retLocal
= getRetLocal();
1628 emitVirtualLocal(retLocal
);
1629 if (sym
== StackSym::C
) {
1633 assert(sym
== StackSym::V
);
1639 if (region
->isTryFinally()) {
1640 // Encountered another try block, jump to its finally and free
1641 // iterators on the way.
1642 emitJump(e
, iters
, region
->m_finallyLabel
);
1648 void EmitterVisitor::emitGotoTrampoline(Emitter
& e
,
1650 std::vector
<Label
*>& cases
,
1652 assert(region
->m_gotoTargets
.count(name
));
1653 auto t
= region
->m_gotoTargets
[name
].target
;
1654 cases
[t
->m_state
]->set(e
);
1655 assert(region
->m_parent
!= nullptr);
1657 for (region
= region
->m_parent
.get(); true; region
= region
->m_parent
.get()) {
1658 assert(region
->m_gotoTargets
.count(name
));
1659 auto t
= region
->m_gotoTargets
[name
].target
;
1660 if (region
->m_parent
->m_gotoLabels
.count(name
)) {
1661 // If only there is the appropriate label inside the current region
1663 Id stateLocal
= getStateLocal();
1664 emitVirtualLocal(stateLocal
);
1665 // We need to unset the state unnamed local in order to correctly
1666 // fall through any future finally blocks.
1667 e
.UnsetL(stateLocal
);
1668 // Jump to the label and free any pending iterators.
1669 emitJump(e
, iters
, t
->m_label
);
1672 if (region
->isTryFinally()) {
1673 // Encountered a finally block, jump and free any pending iterators
1674 emitJump(e
, iters
, region
->m_finallyLabel
);
1677 // Otherwise we will be jumping out of the current context,
1678 // therefore if we are in a loop, we need to free the iterator.
1679 if (region
->isForeach()) {
1680 iters
.push_back(IterPair(region
->m_iterKind
, region
->m_iterId
));
1682 // We should never break out of a function, therefore there
1683 // should always be a parent
1684 assert(region
->m_parent
!= nullptr);
1688 void EmitterVisitor::emitBreakTrampoline(Emitter
& e
, Region
* region
,
1689 std::vector
<Label
*>& cases
,
1692 assert(region
->isTryFinally());
1693 assert(region
->m_breakTargets
.count(depth
));
1694 auto t
= region
->m_breakTargets
[depth
].target
;
1695 cases
[t
->m_state
]->set(e
);
1696 assert(region
->m_parent
!= nullptr);
1698 for (region
= region
->m_parent
.get(); true; region
= region
->m_parent
.get()) {
1700 assert(!region
->isFinally());
1701 assert(region
->m_parent
!= nullptr);
1702 assert(region
->m_breakTargets
.count(depth
));
1703 auto t
= region
->m_breakTargets
[depth
].target
;
1704 if (region
->isTryFinally()) {
1705 // We encountered another try block, jump to the corresponding
1706 // finally, freeing any iterators on the way.
1707 emitJump(e
, iters
, region
->m_finallyLabel
);
1710 if (region
->m_kind
!= Region::Kind::LoopOrSwitch
) {
1713 // Whether or not this is the last loop to break out of, we
1714 // will be freeing the current iterator
1715 if (region
->isForeach()) {
1716 iters
.push_back(IterPair(region
->m_iterKind
, region
->m_iterId
));
1719 // This is the last loop to break out of. Unset the state local in
1720 // order to correctly fall through any future finally blocks
1721 Id stateLocal
= getStateLocal();
1722 emitVirtualLocal(stateLocal
);
1723 e
.UnsetL(stateLocal
);
1724 // Jump to the break label and free any pending iterators on the
1726 emitJump(e
, iters
, t
->m_label
);
1729 // Otherwise just delegate to the parent. One loop level has been
1735 void EmitterVisitor::emitContinueTrampoline(Emitter
& e
, Region
* region
,
1736 std::vector
<Label
*>& cases
,
1739 assert(region
->isTryFinally());
1740 assert(region
->m_continueTargets
.count(depth
));
1741 auto t
= region
->m_continueTargets
[depth
].target
;
1742 cases
[t
->m_state
]->set(e
);
1743 assert(region
->m_parent
!= nullptr);
1745 for (region
= region
->m_parent
.get(); true; region
= region
->m_parent
.get()) {
1747 assert(region
->m_parent
!= nullptr);
1748 assert(!region
->isFinally());
1749 auto t
= region
->m_continueTargets
[depth
].target
;
1750 if (region
->isTryFinally()) {
1751 emitJump(e
, iters
, region
->m_finallyLabel
);
1754 if (region
->m_kind
!= Region::Kind::LoopOrSwitch
) {
1758 // This is the last loop level to continue out of. Don't free the
1759 // iterator for the current loop. We need to free the state unnamed
1760 // local in order to fall through any future finallies correctly
1761 Id stateLocal
= getStateLocal();
1762 emitVirtualLocal(stateLocal
);
1763 e
.UnsetL(stateLocal
);
1764 // Jump to the continue label and free any pending iterators
1765 emitJump(e
, iters
, t
->m_label
);
1768 // This is not the last loop level, therefore the current
1769 // iterator should be freed.
1770 if (region
->isForeach()) {
1771 iters
.push_back(IterPair(region
->m_iterKind
, region
->m_iterId
));
1777 int Region::getMaxBreakContinueDepth() {
1778 if (m_parent
== nullptr || isFinally()) {
1780 } else if (m_kind
== Region::Kind::LoopOrSwitch
) {
1781 return m_parent
->getMaxBreakContinueDepth() + 1;
1783 return m_parent
->getMaxBreakContinueDepth();
1787 int Region::getBreakContinueDepth() {
1789 for (auto& p
: m_breakTargets
) {
1790 depth
= std::max(depth
, p
.first
);
1792 for (auto& p
: m_continueTargets
) {
1793 depth
= std::max(depth
, p
.first
);
1798 int Region::getMaxState() {
1800 for (auto& p
: m_returnTargets
) {
1801 if (p
.second
.used
) {
1802 maxState
= std::max(maxState
, p
.second
.target
->m_state
);
1805 int max_depth
= getBreakContinueDepth();
1806 for (int i
= 1; i
<= max_depth
; ++i
) {
1807 if (isBreakUsed(i
)) {
1808 maxState
= std::max(maxState
, m_breakTargets
[i
].target
->m_state
);
1810 if (isContinueUsed(i
)) {
1811 maxState
= std::max(maxState
, m_continueTargets
[i
].target
->m_state
);
1814 for (auto& p
: m_gotoTargets
) {
1815 if (p
.second
.used
) {
1816 maxState
= std::max(maxState
, p
.second
.target
->m_state
);
1823 EmitterVisitor::createRegion(StatementPtr s
, Region::Kind kind
) {
1824 RegionPtr parent
= nullptr;
1825 if (kind
!= Region::Kind::FuncBody
&& kind
!= Region::Kind::FaultFunclet
&&
1826 kind
!= Region::Kind::Global
&& !m_regions
.empty()) {
1827 parent
= m_regions
.back();
1829 auto region
= std::make_shared
<Region
>(kind
, parent
);
1830 // We preregister all the labels occurring in the provided statement
1831 // ahead of the time. Therefore at the time of emitting the actual
1832 // goto instructions we can reliably tell which finally blocks to
1834 for (auto& label
: s
->getLabelScope()->getLabels()) {
1835 StringData
* nName
= makeStaticString(label
.getName().c_str());
1836 if (!region
->m_gotoLabels
.count(nName
)) {
1837 region
->m_gotoLabels
.insert(nName
);
1843 void EmitterVisitor::enterRegion(RegionPtr region
) {
1844 assert(region
!= nullptr);
1845 m_regions
.push_back(region
);
1848 void EmitterVisitor::leaveRegion(RegionPtr region
) {
1849 assert(region
!= nullptr);
1850 assert(m_regions
.size() > 0);
1851 assert(m_regions
.back() == region
);
1852 m_regions
.pop_back();
1855 void EmitterVisitor::registerControlTarget(ControlTarget
* t
) {
1856 assert(!t
->isRegistered());
1858 while (m_states
.count(state
)) {
1861 m_states
.insert(state
);
1865 void EmitterVisitor::unregisterControlTarget(ControlTarget
* t
) {
1866 assert(t
->isRegistered());
1867 int state
= t
->m_state
;
1868 assert(m_states
.count(state
));
1869 m_states
.erase(state
);
1870 t
->m_state
= ControlTarget::k_unsetState
;
1873 //=============================================================================
1876 EmitterVisitor::EmittedClosures
EmitterVisitor::s_emittedClosures
;
1878 EmitterVisitor::EmitterVisitor(UnitEmitter
& ue
)
1879 : m_ue(ue
), m_curFunc(ue
.getMain()),
1880 m_evalStackIsUnknown(false),
1881 m_actualStackHighWater(0), m_fdescHighWater(0), m_stateLocal(-1),
1883 m_prevOpcode
= OpLowInvalid
;
1884 m_evalStack
.m_actualStackHighWaterPtr
= &m_actualStackHighWater
;
1885 m_evalStack
.m_fdescHighWaterPtr
= &m_fdescHighWater
;
1888 EmitterVisitor::~EmitterVisitor() {
1889 // If a fatal occurs during emission, some extra cleanup is necessary.
1890 for (std::deque
<CatchRegion
*>::const_iterator it
= m_catchRegions
.begin();
1891 it
!= m_catchRegions
.end(); ++it
) {
1896 bool EmitterVisitor::checkIfStackEmpty(const char* forInstruction
) const {
1897 if (m_evalStack
.empty()) {
1898 InvariantViolation("Emitter tried to emit a %s instruction when the "
1899 "evaluation stack is empty (at offset %d)",
1907 void EmitterVisitor::unexpectedStackSym(char sym
, const char* where
) const {
1908 InvariantViolation("Emitter encountered an unexpected StackSym \"%s\""
1909 " in %s() (at offset %d)",
1910 StackSym::ToString(sym
).c_str(),
1915 void EmitterVisitor::popEvalStack(char expected
, int arg
, int pos
) {
1916 // Pop a value off of the evaluation stack, and verify that it
1917 // matches the specified symbolic flavor
1918 if (m_evalStack
.size() == 0) {
1919 InvariantViolation("Emitter emitted an instruction that tries to consume "
1920 "a value from the stack when the stack is empty "
1921 "(expected symbolic flavor \"%s\" at offset %d)",
1922 StackSym::ToString(expected
).c_str(),
1927 char sym
= m_evalStack
.top();
1928 char actual
= StackSym::GetSymFlavor(sym
);
1930 if (actual
!= expected
) {
1932 "Emitter emitted an instruction that tries to consume a "
1933 "value from the stack when the top of the stack does not "
1934 "match the symbolic flavor that the instruction expects "
1935 "(expected symbolic flavor \"%s\", actual symbolic flavor \"%s\" "
1937 StackSym::ToString(expected
).c_str(),
1938 StackSym::ToString(actual
).c_str(),
1943 void EmitterVisitor::popSymbolicLocal(Op op
, int arg
, int pos
) {
1944 // Special case for instructions that consume the loc below the
1947 if (op
== OpCGetL3
) {
1949 } else if (op
== OpCGetL2
) {
1953 if (belowTop
!= -1) {
1954 char symFlavor
= StackSym::GetSymFlavor(
1955 m_evalStack
.get(m_evalStack
.size() - belowTop
));
1956 if (symFlavor
!= StackSym::L
) {
1957 InvariantViolation("Operation tried to remove a local below the top of"
1958 " the symbolic stack but instead found \"%s\"",
1959 StackSym::ToString(symFlavor
).c_str());
1961 m_evalStack
.consumeBelowTop(belowTop
- 1);
1963 popEvalStack(StackSym::L
);
1967 void EmitterVisitor::popEvalStackMMany() {
1968 while (!m_evalStack
.empty()) {
1969 char sym
= m_evalStack
.top();
1970 char symFlavor
= StackSym::GetSymFlavor(sym
);
1971 char marker
= StackSym::GetMarker(sym
);
1972 if (marker
== StackSym::E
|| marker
== StackSym::P
) {
1973 if (symFlavor
!= StackSym::C
&& symFlavor
!= StackSym::L
&&
1974 symFlavor
!= StackSym::T
&& symFlavor
!= StackSym::I
) {
1976 "Emitter emitted an instruction that tries to consume "
1977 "a value from the stack when the top of the stack "
1978 "does not match the symbolic flavor that the instruction "
1979 "expects (expected symbolic flavor \"C\", \"L\", \"T\", or \"I\", "
1980 "actual symbolic flavor \"%s\" at offset %d)",
1981 StackSym::ToString(symFlavor
).c_str(),
1984 } else if (marker
== StackSym::W
) {
1985 if (symFlavor
!= StackSym::None
) {
1987 "Emitter emitted an instruction that tries to consume "
1988 "a value from the stack when the top of the stack "
1989 "does not match the symbolic flavor that the instruction "
1990 "expects (expected symbolic flavor \"None\", actual "
1991 "symbolic flavor \"%s\" at offset %d)",
1992 StackSym::ToString(symFlavor
).c_str(),
1995 } else if (marker
== StackSym::M
) {
1996 assert(symFlavor
== StackSym::A
);
2003 if (m_evalStack
.empty()) {
2004 InvariantViolation("Emitter emitted an instruction that tries to consume "
2005 "a value from the stack when the stack is empty "
2010 char sym
= m_evalStack
.top();
2011 char symFlavor
= StackSym::GetSymFlavor(sym
);
2013 if (symFlavor
!= StackSym::C
&& symFlavor
!= StackSym::L
&&
2014 symFlavor
!= StackSym::R
&& symFlavor
!= StackSym::H
) {
2016 "Emitter emitted an instruction that tries to consume a "
2017 "value from the stack when the top of the stack does not "
2018 "match the symbolic flavor that the instruction expects "
2019 "(expected symbolic flavor \"C\", \"L\", \"R\", or \"H\", actual "
2020 "symbolic flavor \"%s\" at offset %d)",
2021 StackSym::ToString(symFlavor
).c_str(),
2026 void EmitterVisitor::popEvalStackMany(int len
, char symFlavor
) {
2027 for (int i
= 0; i
< len
; ++i
) {
2028 popEvalStack(symFlavor
);
2032 void EmitterVisitor::popEvalStackCVMany(int len
) {
2033 for (int i
= 0; i
< len
; i
++) {
2034 if (m_evalStack
.size() == 0) {
2035 InvariantViolation("Emitter emitted an instruction that tries to consume "
2036 "a value from the stack when the stack is empty "
2037 "(expected symbolic flavor C or V at offset %d)",
2042 char sym
= m_evalStack
.top();
2043 char actual
= StackSym::GetSymFlavor(sym
);
2045 if (actual
!= StackSym::C
&& actual
!= StackSym::V
) {
2047 "Emitter emitted an instruction that tries to consume a "
2048 "value from the stack when the top of the stack does not "
2049 "match the symbolic flavor that the instruction expects "
2050 "(expected symbolic flavor C or V, actual symbolic flavor \"%s\" "
2052 StackSym::ToString(actual
).c_str(),
2058 void EmitterVisitor::pushEvalStack(char symFlavor
) {
2059 // Push a value from the evaluation stack with the specified
2061 m_evalStack
.push(symFlavor
);
2064 void EmitterVisitor::peekEvalStack(char expected
, int depthActual
) {
2065 int posActual
= (m_evalStack
.sizeActual() - depthActual
- 1);
2066 if (posActual
>= 0 && posActual
< (int)m_evalStack
.sizeActual()) {
2067 char sym
= m_evalStack
.getActual(posActual
);
2068 char actual
= StackSym::GetSymFlavor(sym
);
2069 if (actual
!= expected
) {
2071 "Emitter emitted an instruction that tries to consume a "
2072 "value from the stack whose symbolic flavor does not match "
2073 "the symbolic flavor that the instruction expects (expected "
2074 "symbolic flavor \"%s\", actual symbolic flavor \"%s\" at "
2076 StackSym::ToString(expected
).c_str(),
2077 StackSym::ToString(actual
).c_str(),
2083 void EmitterVisitor::pokeEvalStack(char symFlavor
, int depthActual
) {
2084 int sizeActual
= m_evalStack
.sizeActual();
2085 int posActual
= sizeActual
- depthActual
- 1;
2086 if (posActual
>= 0 && posActual
< sizeActual
) {
2087 m_evalStack
.setActual(posActual
, symFlavor
);
2092 * Prior to making any changes to the evaluation stack in between
2093 * instructions, this function should be called.
2095 * What this handles is recording the evaluation stack state at
2096 * instruction boundaries so that jumps know what their stack state
2097 * will be at the destination.
2099 * When m_evalStackIsUnknown, it means we have hit a place in the
2100 * bytecode where the offset cannot be reached via fallthrough, but no
2101 * forward jumps targeted it either. In this case, the stack must be
2102 * empty, and we need to record that this is the case so that later
2103 * backward jumps can check this is the case at their jump site.
2105 void EmitterVisitor::prepareEvalStack() {
2106 if (m_evalStackIsUnknown
) {
2107 if (!m_evalStack
.empty()) {
2108 InvariantViolation("Emitter expected to have an empty evaluation "
2109 "stack because the eval stack was unknown, but "
2110 "it was non-empty.");
2113 // Record that we are assuming that the eval stack is empty
2114 recordJumpTarget(m_ue
.bcPos(), m_evalStack
);
2115 m_evalStackIsUnknown
= false;
2119 void EmitterVisitor::recordJumpTarget(Offset target
,
2120 const SymbolicStack
& evalStack
) {
2121 if (target
== InvalidAbsoluteOffset
) {
2123 "Offset passed to EmitterVisitor::recordJumpTarget was invalid");
2125 auto it
= m_jumpTargetEvalStacks
.find(target
);
2126 if (it
== m_jumpTargetEvalStacks
.end()) {
2127 m_jumpTargetEvalStacks
[target
] = evalStack
;
2130 checkJmpTargetEvalStack(evalStack
, it
->second
);
2133 void EmitterVisitor::restoreJumpTargetEvalStack() {
2134 m_evalStack
.clear();
2135 auto it
= m_jumpTargetEvalStacks
.find(m_ue
.bcPos());
2136 if (it
== m_jumpTargetEvalStacks
.end()) {
2137 m_evalStackIsUnknown
= true;
2140 m_evalStack
= it
->second
;
2143 void EmitterVisitor::recordCall() {
2144 m_curFunc
->containsCalls
= true;
2147 bool EmitterVisitor::isJumpTarget(Offset target
) {
2148 // Returns true iff one of the following conditions is true:
2149 // 1) We have seen an instruction that jumps to the specified offset
2150 // 2) We know of a Label that has been set to the specified offset
2151 // 3) We have seen a try region that ends at the specified offset
2152 auto it
= m_jumpTargetEvalStacks
.find(target
);
2153 return (it
!= m_jumpTargetEvalStacks
.end());
2156 class IterFreeThunklet
: public Thunklet
{
2158 IterFreeThunklet(Id iterId
, bool itRef
)
2159 : m_id(iterId
), m_itRef(itRef
) {}
2160 virtual void emit(Emitter
& e
) {
2174 * A thunklet for the fault region protecting a silenced (@) expression.
2176 class RestoreErrorReportingThunklet
: public Thunklet
{
2178 explicit RestoreErrorReportingThunklet(Id loc
)
2179 : m_oldLevelLoc(loc
) {}
2180 virtual void emit(Emitter
& e
) {
2181 e
.getEmitterVisitor().emitRestoreErrorReporting(e
, m_oldLevelLoc
);
2188 class UnsetUnnamedLocalThunklet
: public Thunklet
{
2190 explicit UnsetUnnamedLocalThunklet(Id loc
)
2192 virtual void emit(Emitter
& e
) {
2193 e
.getEmitterVisitor().emitVirtualLocal(m_loc
);
2194 e
.getEmitterVisitor().emitUnset(e
);
2201 class FinallyThunklet
: public Thunklet
{
2203 explicit FinallyThunklet(FinallyStatementPtr finallyStatement
,
2205 : m_finallyStatement(finallyStatement
), m_numLiveIters(numLiveIters
) {}
2206 virtual void emit(Emitter
& e
) {
2207 auto& visitor
= e
.getEmitterVisitor();
2209 visitor
.createRegion(m_finallyStatement
, Region::Kind::FaultFunclet
);
2210 visitor
.enterRegion(region
);
2211 SCOPE_EXIT
{ visitor
.leaveRegion(region
); };
2212 Id stateLocal
= visitor
.getStateLocal();
2213 visitor
.emitVirtualLocal(stateLocal
);
2214 e
.UnsetL(stateLocal
);
2215 Id retLocal
= visitor
.getStateLocal();
2216 visitor
.emitVirtualLocal(retLocal
);
2218 auto* func
= visitor
.getFuncEmitter();
2219 int oldNumLiveIters
= func
->numLiveIterators();
2220 func
->setNumLiveIterators(m_numLiveIters
);
2221 SCOPE_EXIT
{ func
->setNumLiveIterators(oldNumLiveIters
); };
2222 visitor
.visit(m_finallyStatement
);
2226 FinallyStatementPtr m_finallyStatement
;
2231 * Helper to deal with emitting list assignment and keeping track of some
2232 * associated info. A list assignment can be thought of as a list of "index
2233 * chains"; that is, sequences of indices that should be accessed for each
2234 * bottom-level expression in the list assignment. We recursively walk down the
2235 * LHS, building up index chains and copying them into the top-level list as we
2236 * reach the leaves of the tree.
2238 void EmitterVisitor::listAssignmentVisitLHS(Emitter
& e
, ExpressionPtr exp
,
2239 IndexChain
& indexChain
,
2240 std::vector
<IndexChain
*>& all
) {
2246 if (exp
->is(Expression::KindOfListAssignment
)) {
2247 // Nested assignment
2248 ListAssignmentPtr la
= static_pointer_cast
<ListAssignment
>(exp
);
2249 ExpressionListPtr lhs
= la
->getVariables();
2250 int n
= lhs
->getCount();
2251 for (int i
= 0; i
< n
; ++i
) {
2252 indexChain
.push_back(i
);
2253 listAssignmentVisitLHS(e
, (*lhs
)[i
], indexChain
, all
);
2254 indexChain
.pop_back();
2257 // Reached a "leaf". Lock in this index chain and deal with this exp.
2258 assert(!indexChain
.empty());
2259 all
.push_back(new IndexChain(indexChain
));
2261 emitClsIfSPropBase(e
);
2265 void EmitterVisitor::listAssignmentAssignElements(
2267 std::vector
<IndexChain
*>& indexChains
,
2268 std::function
<void()> emitSrc
2270 for (int i
= (int)indexChains
.size() - 1; i
>= 0; --i
) {
2271 IndexChain
* currIndexChain
= indexChains
[i
];
2272 if (currIndexChain
->empty()) {
2276 if (emitSrc
== nullptr) {
2280 for (int j
= 0; j
< (int)currIndexChain
->size(); ++j
) {
2281 m_evalStack
.push(StackSym::I
);
2282 m_evalStack
.setInt((*currIndexChain
)[j
]);
2290 delete currIndexChain
;
2295 * visitIfCondition() serves as a helper method for visiting the condition
2296 * of an "if" statement. This function recursively visits each node in an
2297 * expression, keeping track of where to jump if an "and" or "or" expression
2298 * short circuits. If an expression other than "and", "or", or "not" is
2299 * encountered, this method calls EmitterVisitor::visit() to handle the
2302 void EmitterVisitor::visitIfCondition(
2303 ExpressionPtr cond
, Emitter
& e
, Label
& tru
, Label
& fals
,
2304 bool truFallthrough
) {
2306 BinaryOpExpressionPtr
binOpNode(
2307 dynamic_pointer_cast
<BinaryOpExpression
>(cond
));
2310 int op
= binOpNode
->getOp();
2311 // Short circuit && and ||
2312 if (op
== T_LOGICAL_OR
|| op
== T_LOGICAL_AND
||
2313 op
== T_BOOLEAN_OR
|| op
== T_BOOLEAN_AND
) {
2314 bool isOr
= (op
== T_LOGICAL_OR
|| op
== T_BOOLEAN_OR
);
2316 ExpressionPtr lhs
= binOpNode
->getExp1();
2318 Emitter
lhsEmitter(lhs
, m_ue
, *this);
2319 visitIfCondition(lhs
, lhsEmitter
, tru
, localLabel
, false);
2320 // falls through if that condition was false
2322 Emitter
lhsEmitter(lhs
, m_ue
, *this);
2323 visitIfCondition(lhs
, lhsEmitter
, localLabel
, fals
, true);
2324 // falls through if that condition was true
2326 if (localLabel
.isUsed()) {
2329 if (currentPositionIsReachable()) {
2330 ExpressionPtr rhs
= binOpNode
->getExp2();
2331 Emitter
rhsEmitter(rhs
, m_ue
, *this);
2332 visitIfCondition(rhs
, rhsEmitter
, tru
, fals
, truFallthrough
);
2338 UnaryOpExpressionPtr
unOpNode(dynamic_pointer_cast
<UnaryOpExpression
>(cond
));
2340 int op
= unOpNode
->getOp();
2343 ExpressionPtr val
= unOpNode
->getExpression();
2344 Emitter
valEmitter(val
, m_ue
, *this);
2345 visitIfCondition(val
, valEmitter
, fals
, tru
, !truFallthrough
);
2351 if (cond
->getScalarValue(val
)) {
2352 if (truFallthrough
) {
2353 if (!val
.toBoolean()) e
.Jmp(fals
);
2355 if (val
.toBoolean()) e
.Jmp(tru
);
2361 emitConvertToCell(e
);
2362 if (truFallthrough
) {
2369 // Assigns ids to all of the local variables eagerly. This gives us the
2370 // nice property that all named local variables will be assigned ids
2371 // 0 through k-1, while any unnamed local variable will have an id >= k.
2372 void EmitterVisitor::assignLocalVariableIds(FunctionScopePtr fs
) {
2373 VariableTablePtr variables
= fs
->getVariables();
2374 std::vector
<std::string
> localNames
;
2375 variables
->getLocalVariableNames(localNames
);
2376 for (int i
= 0; i
< (int)localNames
.size(); ++i
) {
2377 StringData
* nLiteral
= makeStaticString(localNames
[i
].c_str());
2378 m_curFunc
->allocVarId(nLiteral
);
2382 void EmitterVisitor::assignFinallyVariableIds() {
2383 assert(m_stateLocal
< 0);
2384 m_stateLocal
= m_curFunc
->allocUnnamedLocal();
2385 assert(m_retLocal
< 0);
2386 m_retLocal
= m_curFunc
->allocUnnamedLocal();
2389 void EmitterVisitor::visit(FileScopePtr file
) {
2390 const std::string
& filename
= file
->getName();
2391 m_ue
.m_filepath
= makeStaticString(filename
);
2392 m_ue
.m_isHHFile
= file
->isHHFile();
2394 FunctionScopePtr
func(file
->getPseudoMain());
2398 assignLocalVariableIds(func
);
2400 AnalysisResultPtr
ar(file
->getContainingProgram());
2402 MethodStatementPtr
m(dynamic_pointer_cast
<MethodStatement
>(func
->getStmt()));
2404 StatementListPtr
stmts(m
->getStmts());
2407 Emitter
e(m
, m_ue
, *this);
2409 int i
, nk
= stmts
->getCount();
2410 for (i
= 0; i
< nk
; i
++) {
2411 StatementPtr s
= (*stmts
)[i
];
2412 if (MethodStatementPtr meth
= dynamic_pointer_cast
<MethodStatement
>(s
)) {
2414 postponeMeth(meth
, nullptr, true);
2418 FunctionScopePtr fsp
= m
->getFunctionScope();
2419 if (fsp
->needsLocalThis()) {
2420 static const StringData
* thisStr
= makeStaticString("this");
2421 Id thisId
= m_curFunc
->lookupVarId(thisStr
);
2422 emitVirtualLocal(thisId
);
2423 e
.InitThisLoc(thisId
);
2425 if (fsp
->needsFinallyLocals()) {
2426 assignFinallyVariableIds();
2428 FuncFinisher
ff(this, e
, m_curFunc
);
2429 TypedValue mainReturn
;
2430 mainReturn
.m_type
= KindOfInvalid
;
2431 bool notMergeOnly
= false;
2433 if (Option::UseHHBBC
&& SystemLib::s_inited
) notMergeOnly
= true;
2435 auto region
= createRegion(stmts
, Region::Kind::Global
);
2436 enterRegion(region
);
2437 SCOPE_EXIT
{ leaveRegion(region
); };
2439 for (i
= 0; i
< nk
; i
++) {
2440 StatementPtr s
= (*stmts
)[i
];
2441 switch (s
->getKindOf()) {
2442 case Statement::KindOfMethodStatement
:
2443 case Statement::KindOfFunctionStatement
:
2445 case Statement::KindOfInterfaceStatement
:
2446 case Statement::KindOfClassStatement
: {
2447 // Handle classes directly here, since only top-level classes are
2449 ClassScopePtr cNode
= s
->getClassScope();
2450 emitClass(e
, cNode
, true);
2451 if (cNode
->getFatalMessage()) {
2452 notMergeOnly
= true;
2456 case Statement::KindOfTypedefStatement
:
2457 emitTypedef(e
, static_pointer_cast
<TypedefStatement
>(s
));
2458 notMergeOnly
= true; // TODO(#2103206): typedefs should be mergable
2460 case Statement::KindOfReturnStatement
:
2461 if (mainReturn
.m_type
!= KindOfInvalid
) break;
2465 tvWriteUninit(&mainReturn
);
2466 m_ue
.m_returnSeen
= true;
2471 ReturnStatementPtr
r(static_pointer_cast
<ReturnStatement
>(s
));
2472 Variant
v((Variant::NullInit()));
2473 if (r
->getRetExp() &&
2474 !r
->getRetExp()->getScalarValue(v
)) {
2475 tvWriteUninit(&mainReturn
);
2476 notMergeOnly
= true;
2480 v
= String(makeStaticString(v
.asCStrRef().get()));
2481 } else if (v
.isArray()) {
2482 v
= Array(ArrayData::GetScalarArray(v
.asCArrRef().get()));
2484 assert(v
.isInitialized());
2485 assert(!IS_REFCOUNTED_TYPE(v
.getType()));
2487 mainReturn
= *v
.asCell();
2488 m_ue
.m_returnSeen
= true;
2491 case Statement::KindOfExpStatement
:
2492 if (mainReturn
.m_type
== KindOfInvalid
) {
2494 static_pointer_cast
<ExpStatement
>(s
)->getExpression();
2495 switch (e
->getKindOf()) {
2496 case Expression::KindOfSimpleFunctionCall
: {
2497 SimpleFunctionCallPtr func
=
2498 static_pointer_cast
<SimpleFunctionCall
>(e
);
2501 if (func
->isSimpleDefine(&name
, &tv
)) {
2502 auto k
= func
->isDefineWithoutImpl(ar
)
2503 ? Unit::MergeKind::PersistentDefine
2504 : Unit::MergeKind::Define
;
2505 if (tv
.m_type
== KindOfUninit
) {
2506 tv
.m_type
= KindOfNull
;
2508 m_ue
.pushMergeableDef(k
, name
, tv
);
2514 case Expression::KindOfAssignmentExpression
: {
2515 AssignmentExpressionPtr
ae(
2516 static_pointer_cast
<AssignmentExpression
>(e
));
2519 if (ae
->isSimpleGlobalAssign(&name
, &tv
)) {
2520 m_ue
.pushMergeableDef(Unit::MergeKind::Global
, name
, tv
);
2526 case Expression::KindOfIncludeExpression
: {
2527 IncludeExpressionPtr inc
=
2528 static_pointer_cast
<IncludeExpression
>(e
);
2529 if (inc
->isReqLit()) {
2530 if (FileScopeRawPtr f
= inc
->getIncludedFile(ar
)) {
2531 if (StatementListPtr sl
= f
->getStmt()) {
2532 FunctionScopeRawPtr ps DEBUG_ONLY
=
2533 sl
->getFunctionScope();
2534 assert(ps
&& ps
->inPseudoMain());
2535 m_ue
.pushMergeableInclude(
2536 Unit::MergeKind::ReqDoc
,
2537 makeStaticString(inc
->includePath()));
2550 if (mainReturn
.m_type
!= KindOfInvalid
) break;
2551 notMergeOnly
= true;
2556 if (!notMergeOnly
) {
2557 m_ue
.m_mergeOnly
= true;
2558 if (mainReturn
.m_type
== KindOfInvalid
) {
2559 tvWriteUninit(&mainReturn
);
2560 if (boost::algorithm::ends_with(filename
, EVAL_FILENAME_SUFFIX
)) {
2561 tvAsVariant(&mainReturn
) = init_null();
2563 tvAsVariant(&mainReturn
) = 1;
2566 m_ue
.m_mainReturn
= mainReturn
;
2569 // Pseudo-main returns the integer value 1 by default. If the
2570 // current position in the bytecode is reachable, emit code to
2572 if (currentPositionIsReachable()) {
2573 LocationPtr
loc(new Location());
2574 e
.setTempLocation(loc
);
2575 if (boost::algorithm::ends_with(filename
, EVAL_FILENAME_SUFFIX
)) {
2581 e
.setTempLocation(LocationPtr());
2585 if (!m_evalStack
.empty()) {
2586 InvariantViolation("Eval stack was not empty as expected before "
2587 "emitPostponed* phase");
2591 emitPostponedMeths();
2592 emitPostponedCtors();
2593 emitPostponedPinits();
2594 emitPostponedSinits();
2595 emitPostponedCinits();
2598 static StringData
* getClassName(ExpressionPtr e
) {
2599 ClassScopeRawPtr cls
;
2601 cls
= e
->getOriginalClass();
2602 if (TypePtr t
= e
->getAssertedType()) {
2603 if (t
->isSpecificObject()) {
2604 AnalysisResultConstPtr ar
= e
->getScope()->getContainingProgram();
2605 ClassScopeRawPtr c2
= t
->getClass(ar
, e
->getScope());
2606 if (c2
&& c2
->derivesFrom(ar
, cls
->getName(), true, false)) {
2611 } else if (TypePtr t
= e
->getActualType()) {
2612 if (t
->isSpecificObject()) {
2613 cls
= t
->getClass(e
->getScope()->getContainingProgram(), e
->getScope());
2616 if (cls
&& !cls
->isTrait()) {
2617 return makeStaticString(cls
->getOriginalName());
2622 void EmitterVisitor::fixReturnType(Emitter
& e
, FunctionCallPtr fn
,
2623 Func
* builtinFunc
) {
2625 if (fn
->hasAnyContext(Expression::RefValue
|
2626 Expression::DeepReference
|
2627 Expression::LValue
|
2628 Expression::OprLValue
|
2629 Expression::UnsetContext
)) {
2633 ref
= (builtinFunc
->attrs() & AttrReference
) != 0;
2634 } else if (fn
->isValid() && fn
->getFuncScope()) {
2635 ref
= fn
->getFuncScope()->isRefReturn();
2636 } else if (!fn
->getName().empty()) {
2637 FunctionScope::FunctionInfoPtr fi
=
2638 FunctionScope::GetFunctionInfo(fn
->getName());
2639 if (!fi
|| !fi
->getMaybeRefReturn()) ref
= false;
2642 if (!fn
->isUnused() &&
2644 (!ref
|| !fn
->hasAnyContext(Expression::AccessContext
|
2645 Expression::ObjectContext
))) {
2646 /* we dont support V in M-vectors, so leave it as an R in that
2648 assert(m_evalStack
.get(m_evalStack
.size() - 1) == StackSym::R
);
2657 void EmitterVisitor::visitKids(ConstructPtr c
) {
2658 for (int i
= 0, nk
= c
->getKidCount(); i
< nk
; i
++) {
2659 ConstructPtr
kid(c
->getNthKid(i
));
2665 bool checkKeys(ExpressionPtr init_expr
, bool check_size
, Fun fun
) {
2666 if (init_expr
->getKindOf() != Expression::KindOfExpressionList
) {
2670 auto el
= static_pointer_cast
<ExpressionList
>(init_expr
);
2671 int n
= el
->getCount();
2672 if (n
< 1 || (check_size
&& n
> MixedArray::MaxMakeSize
)) {
2676 for (int i
= 0, n
= el
->getCount(); i
< n
; ++i
) {
2677 ExpressionPtr ex
= (*el
)[i
];
2678 if (ex
->getKindOf() != Expression::KindOfArrayPairExpression
) {
2681 auto ap
= static_pointer_cast
<ArrayPairExpression
>(ex
);
2682 if (ap
->isRef()) return false;
2683 if (!fun(ap
)) return false;
2689 * isPackedInit() returns true if this expression list looks like an
2690 * array with no keys and no ref values; e.g. array(x,y,z).
2692 * In this case we can NewPackedArray to create the array. The elements are
2693 * pushed on the stack, so we arbitrarily limit this to a small multiple of
2694 * MixedArray::SmallSize (12).
2696 bool isPackedInit(ExpressionPtr init_expr
, int* size
,
2697 bool check_size
= true) {
2699 return checkKeys(init_expr
, check_size
, [&](ArrayPairExpressionPtr ap
) {
2702 // If we have a key...
2703 if (ap
->getName() != nullptr) {
2704 // ...and it has no scalar value, bail.
2705 if (!ap
->getScalarValue(key
)) return false;
2707 if (key
.isInteger()) {
2708 // If it's an integer key, check if it's the next packed index.
2709 if (key
.asInt64Val() != *size
) return false;
2711 // Give up if it's not a string.
2712 if (!key
.isString()) return false;
2714 int64_t i
; double d
;
2715 auto numtype
= key
.getStringData()->isNumericWithVal(i
, d
, false);
2717 // If it's a string of the next packed index,
2718 if (numtype
!= KindOfInt64
|| i
!= *size
) return false;
2728 * isStructInit() is like isPackedInit(), but returns true if the keys are
2729 * all static strings with no duplicates.
2731 bool isStructInit(ExpressionPtr init_expr
, std::vector
<std::string
>& keys
) {
2732 return checkKeys(init_expr
, true, [&](ArrayPairExpressionPtr ap
) {
2733 auto key
= ap
->getName();
2734 if (key
== nullptr || !key
->isLiteralString()) return false;
2735 auto name
= key
->getLiteralString();
2738 auto kind
= is_numeric_string(name
.data(), name
.size(), &ival
, &dval
, 0);
2739 if (kind
!= KindOfNull
) return false; // don't allow numeric keys
2740 if (std::find(keys
.begin(), keys
.end(), name
) != keys
.end()) return false;
2741 keys
.push_back(name
);
2746 bool EmitterVisitor::visit(ConstructPtr node
) {
2747 if (!node
) return false;
2749 Emitter
e(node
, m_ue
, *this);
2751 if (StatementPtr s
= dynamic_pointer_cast
<Statement
>(node
)) {
2752 switch (s
->getKindOf()) {
2753 case Statement::KindOfBlockStatement
:
2754 case Statement::KindOfStatementList
:
2758 case Statement::KindOfTypedefStatement
: {
2759 emitMakeUnitFatal(e
, "Type statements are currently only allowed at "
2764 case Statement::KindOfContinueStatement
:
2765 case Statement::KindOfBreakStatement
: {
2766 BreakStatementPtr
bs(static_pointer_cast
<BreakStatement
>(s
));
2767 uint64_t destLevel
= bs
->getDepth();
2769 if (destLevel
> m_regions
.back()->getMaxBreakContinueDepth()) {
2770 std::ostringstream msg
;
2771 msg
<< "Cannot break/continue " << destLevel
<< " level";
2772 if (destLevel
> 1) {
2775 emitMakeUnitFatal(e
, msg
.str().c_str());
2779 if (bs
->is(Statement::KindOfBreakStatement
)) {
2780 emitBreak(e
, destLevel
, bs
);
2782 emitContinue(e
, destLevel
, bs
);
2788 case Statement::KindOfDoStatement
: {
2789 auto region
= createRegion(s
, Region::Kind::LoopOrSwitch
);
2790 DoStatementPtr
ds(static_pointer_cast
<DoStatement
>(s
));
2794 registerContinue(ds
, region
.get(), 1, false)->m_label
;
2796 registerBreak(ds
, region
.get(), 1, false)->m_label
;
2798 enterRegion(region
);
2799 SCOPE_EXIT
{ leaveRegion(region
); };
2800 visit(ds
->getBody());
2804 ExpressionPtr c
= ds
->getCondExp();
2805 Emitter
condEmitter(c
, m_ue
, *this);
2806 visitIfCondition(c
, condEmitter
, top
, exit
, false);
2809 if (exit
.isUsed()) exit
.set(e
);
2813 case Statement::KindOfCaseStatement
: {
2814 // Should never be called. Handled in visitSwitch.
2818 case Statement::KindOfCatchStatement
: {
2819 // Store the current exception object in the appropriate local variable
2820 CatchStatementPtr
cs(static_pointer_cast
<CatchStatement
>(node
));
2821 StringData
* vName
= makeStaticString(cs
->getVariable()->getName());
2822 Id i
= m_curFunc
->lookupVarId(vName
);
2823 emitVirtualLocal(i
);
2827 visit(cs
->getStmt());
2831 case Statement::KindOfEchoStatement
: {
2832 EchoStatementPtr
es(static_pointer_cast
<EchoStatement
>(node
));
2833 ExpressionListPtr exps
= es
->getExpressionList();
2834 int count
= exps
->getCount();
2835 for (int i
= 0; i
< count
; i
++) {
2837 emitConvertToCell(e
);
2844 case Statement::KindOfExpStatement
: {
2845 ExpStatementPtr
es(static_pointer_cast
<ExpStatement
>(s
));
2846 if (visit(es
->getExpression())) {
2852 case Statement::KindOfForStatement
: {
2853 auto region
= createRegion(s
, Region::Kind::LoopOrSwitch
);
2854 ForStatementPtr
fs(static_pointer_cast
<ForStatement
>(s
));
2856 if (visit(fs
->getInitExp())) {
2860 Label
& preInc
= registerContinue(fs
, region
.get(), 1, false)->m_label
;
2861 Label
& fail
= registerBreak(fs
, region
.get(), 1, false)->m_label
;
2862 if (ExpressionPtr condExp
= fs
->getCondExp()) {
2864 Emitter
condEmitter(condExp
, m_ue
, *this);
2865 visitIfCondition(condExp
, condEmitter
, tru
, fail
, true);
2866 if (tru
.isUsed()) tru
.set(e
);
2869 enterRegion(region
);
2870 SCOPE_EXIT
{ leaveRegion(region
); };
2871 visit(fs
->getBody());
2874 if (visit(fs
->getIncExp())) {
2878 if (fail
.isUsed()) fail
.set(e
);
2882 case Statement::KindOfForEachStatement
: {
2883 ForEachStatementPtr
fe(static_pointer_cast
<ForEachStatement
>(node
));
2884 if (fe
->isAwaitAs()) {
2885 emitForeachAwaitAs(e
, fe
);
2892 case Statement::KindOfGlobalStatement
: {
2893 ExpressionListPtr
vars(
2894 static_pointer_cast
<GlobalStatement
>(node
)->getVars());
2895 for (int i
= 0, n
= vars
->getCount(); i
< n
; i
++) {
2896 ExpressionPtr
var((*vars
)[i
]);
2897 if (var
->is(Expression::KindOfSimpleVariable
)) {
2898 SimpleVariablePtr
sv(static_pointer_cast
<SimpleVariable
>(var
));
2899 if (sv
->isSuperGlobal()) {
2902 StringData
* nLiteral
= makeStaticString(sv
->getName());
2903 Id i
= m_curFunc
->lookupVarId(nLiteral
);
2904 emitVirtualLocal(i
);
2910 } else if (var
->is(Expression::KindOfDynamicVariable
)) {
2911 // global $<exp> =& $GLOBALS[<exp>]
2912 DynamicVariablePtr
dv(static_pointer_cast
<DynamicVariable
>(var
));
2913 // Get the variable name as a cell, for the LHS
2914 visit(dv
->getSubExpression());
2915 emitConvertToCell(e
);
2916 // Copy the variable name, for indexing into $GLOBALS
2930 case Statement::KindOfIfStatement
: {
2931 IfStatementPtr
ifp(static_pointer_cast
<IfStatement
>(node
));
2932 StatementListPtr
branches(ifp
->getIfBranches());
2933 int nb
= branches
->getCount();
2935 for (int i
= 0; i
< nb
; i
++) {
2936 IfBranchStatementPtr
branch(
2937 static_pointer_cast
<IfBranchStatement
>((*branches
)[i
]));
2939 if (branch
->getCondition()) {
2941 Emitter
condEmitter(branch
->getCondition(), m_ue
, *this);
2942 visitIfCondition(branch
->getCondition(), condEmitter
,
2948 visit(branch
->getStmt());
2949 if (currentPositionIsReachable() && i
+ 1 < nb
) {
2952 if (fals
.isUsed()) {
2956 if (done
.isUsed()) {
2962 case Statement::KindOfIfBranchStatement
:
2963 not_reached(); // handled by KindOfIfStatement
2965 case Statement::KindOfReturnStatement
: {
2966 ReturnStatementPtr
r(static_pointer_cast
<ReturnStatement
>(node
));
2968 char retSym
= StackSym::C
;
2969 if (visit(r
->getRetExp())) {
2970 if (r
->getRetExp()->getContext() & Expression::RefValue
) {
2971 emitConvertToVar(e
);
2972 retSym
= StackSym::V
;
2974 emitConvertToCell(e
);
2980 assert(m_evalStack
.size() == 1);
2981 bool hasConstraint
= m_curFunc
->retTypeConstraint
.hasConstraint();
2984 if (m_curFunc
->isAsync
|| m_curFunc
->isGenerator
) {
2985 assert(retSym
== StackSym::C
);
2986 hasConstraint
= false;
2989 emitReturn(e
, retSym
, hasConstraint
, r
);
2993 case Statement::KindOfStaticStatement
: {
2994 ExpressionListPtr
vars(
2995 static_pointer_cast
<StaticStatement
>(node
)->getVars());
2996 for (int i
= 0, n
= vars
->getCount(); i
< n
; i
++) {
2997 ExpressionPtr
se((*vars
)[i
]);
2998 assert(se
->is(Expression::KindOfAssignmentExpression
));
2999 AssignmentExpressionPtr
ae(
3000 static_pointer_cast
<AssignmentExpression
>(se
));
3001 ExpressionPtr
var(ae
->getVariable());
3002 ExpressionPtr
value(ae
->getValue());
3003 assert(var
->is(Expression::KindOfSimpleVariable
));
3004 SimpleVariablePtr
sv(static_pointer_cast
<SimpleVariable
>(var
));
3005 StringData
* name
= makeStaticString(sv
->getName());
3006 Id local
= m_curFunc
->lookupVarId(name
);
3008 if (m_staticEmitted
.insert(sv
->getName()).second
) {
3009 Func::SVInfo svInfo
;
3011 std::ostringstream os
;
3012 CodeGenerator
cg(&os
, CodeGenerator::PickledPHP
);
3013 AnalysisResultPtr
ar(new AnalysisResult());
3014 value
->outputPHP(cg
, ar
);
3015 svInfo
.phpCode
= makeStaticString(os
.str());
3016 m_curFunc
->staticVars
.push_back(svInfo
);
3019 if (value
->isScalar()) {
3020 emitVirtualLocal(local
);
3022 emitConvertToCell(e
);
3023 e
.StaticLocInit(local
, name
);
3026 emitVirtualLocal(local
);
3027 e
.StaticLoc(local
, name
);
3030 emitVirtualLocal(local
);
3032 emitConvertToCell(e
);
3042 case Statement::KindOfSwitchStatement
: {
3043 auto region
= createRegion(s
, Region::Kind::LoopOrSwitch
);
3044 SwitchStatementPtr
sw(static_pointer_cast
<SwitchStatement
>(node
));
3046 StatementListPtr
cases(sw
->getCases());
3048 visit(sw
->getExp());
3052 uint ncase
= cases
->getCount();
3053 std::vector
<Label
> caseLabels(ncase
);
3054 Label
& brkTarget
= registerBreak(sw
, region
.get(), 1, false)->m_label
;
3056 registerContinue(sw
, region
.get(), 1, false)->m_label
;
3057 // There are two different ways this can go. If the subject is a simple
3058 // variable, then we have to evaluate it every time we compare against a
3059 // case condition. Otherwise, we evaluate it once and store it in an
3060 // unnamed local. This is because (a) switch statements are equivalent
3061 // to a series of if-elses, and (b) Zend has some weird evaluation order
3062 // rules. For example, "$a == ++$a" is true but "$a[0] == ++$a[0]" is
3063 // false. In particular, if a case condition modifies the switch
3064 // subject, things behave differently depending on whether the subject
3065 // is a simple variable.
3066 ExpressionPtr subject
= sw
->getExp();
3067 bool simpleSubject
= subject
->is(Expression::KindOfSimpleVariable
)
3068 && !static_pointer_cast
<SimpleVariable
>(subject
)->getAlwaysStash();
3070 Offset start
= InvalidAbsoluteOffset
;
3072 bool enabled
= RuntimeOption::EnableEmitSwitch
;
3073 SimpleFunctionCallPtr
3074 call(dynamic_pointer_cast
<SimpleFunctionCall
>(subject
));
3077 bool didSwitch
= false;
3079 DataType stype
= analyzeSwitch(sw
, state
);
3080 if (stype
!= KindOfInvalid
) {
3081 e
.incStat(stype
== KindOfInt64
? Stats::Switch_Integer
3082 : Stats::Switch_String
,
3084 if (state
.cases
.empty()) {
3085 // If there are no non-default cases, evaluate the subject for
3086 // side effects and fall through. If there's a default case it
3087 // will be emitted immediately after this.
3088 visit(sw
->getExp());
3090 } else if (stype
== KindOfInt64
) {
3091 emitIntegerSwitch(e
, sw
, caseLabels
, brkTarget
, state
);
3093 assert(IS_STRING_TYPE(stype
));
3094 emitStringSwitch(e
, sw
, caseLabels
, brkTarget
, state
);
3100 e
.incStat(Stats::Switch_Generic
, 1);
3101 if (!simpleSubject
) {
3102 // Evaluate the subject once and stash it in a local
3103 tempLocal
= m_curFunc
->allocUnnamedLocal();
3104 emitVirtualLocal(tempLocal
);
3106 emitConvertToCell(e
);
3109 start
= m_ue
.bcPos();
3113 for (uint i
= 0; i
< ncase
; i
++) {
3114 CaseStatementPtr
c(static_pointer_cast
<CaseStatement
>((*cases
)[i
]));
3115 ExpressionPtr condition
= c
->getCondition();
3117 if (simpleSubject
) {
3118 // Evaluate the subject every time.
3120 emitConvertToCellOrLoc(e
);
3122 emitConvertToCell(e
);
3123 emitConvertSecondToCell(e
);
3125 emitVirtualLocal(tempLocal
);
3128 emitConvertToCell(e
);
3131 e
.JmpNZ(caseLabels
[i
]);
3132 } else if (LIKELY(defI
== -1)) {
3136 throw IncludeTimeFatalException(
3137 c
, "Switch statements may only contain one default: clause");
3141 e
.Jmp(caseLabels
[defI
]);
3146 for (uint i
= 0; i
< ncase
; i
++) {
3147 caseLabels
[i
].set(e
);
3148 CaseStatementPtr
c(static_pointer_cast
<CaseStatement
>((*cases
)[i
]));
3149 enterRegion(region
);
3150 SCOPE_EXIT
{ leaveRegion(region
); };
3151 visit(c
->getStatement());
3153 if (brkTarget
.isUsed()) brkTarget
.set(e
);
3154 if (contTarget
.isUsed()) contTarget
.set(e
);
3155 if (!didSwitch
&& !simpleSubject
) {
3156 // Null out temp local, to invoke any needed refcounting
3157 assert(tempLocal
>= 0);
3158 assert(start
!= InvalidAbsoluteOffset
);
3159 newFaultRegionAndFunclet(start
, m_ue
.bcPos(),
3160 new UnsetUnnamedLocalThunklet(tempLocal
));
3161 emitVirtualLocal(tempLocal
);
3163 m_curFunc
->freeUnnamedLocal(tempLocal
);
3168 case Statement::KindOfThrowStatement
: {
3170 emitConvertToCell(e
);
3175 case Statement::KindOfFinallyStatement
: {
3176 auto region
= createRegion(s
, Region::Kind::Finally
);
3177 enterRegion(region
);
3178 SCOPE_EXIT
{ leaveRegion(region
); };
3180 FinallyStatementPtr fs
= static_pointer_cast
<FinallyStatement
>(node
);
3181 visit(fs
->getBody());
3185 case Statement::KindOfTryStatement
: {
3186 auto region
= createRegion(s
, Region::Kind::TryFinally
);
3187 if (!m_evalStack
.empty()) {
3189 "Emitter detected that the evaluation stack is not empty "
3190 "at the beginning of a try region: %d", m_ue
.bcPos());
3193 TryStatementPtr ts
= static_pointer_cast
<TryStatement
>(node
);
3195 FinallyStatementPtr
f(static_pointer_cast
<FinallyStatement
>
3196 (ts
->getFinally()));
3198 Offset start
= m_ue
.bcPos();
3204 enterRegion(region
);
3208 leaveRegion(region
);
3212 visit(ts
->getBody());
3214 StatementListPtr catches
= ts
->getCatches();
3215 int catch_count
= catches
->getCount();
3216 if (catch_count
> 0) {
3217 // include the jump out of the try-catch block in the
3218 // exception handler address range
3222 if (!m_evalStack
.empty()) {
3223 InvariantViolation("Emitter detected that the evaluation stack "
3224 "is not empty at the end of a try region: %d",
3228 if (catch_count
> 0) {
3229 CatchRegion
* r
= new CatchRegion(start
, end
);
3230 m_catchRegions
.push_back(r
);
3232 bool firstHandler
= true;
3233 for (int i
= 0; i
< catch_count
; i
++) {
3234 CatchStatementPtr
c(static_pointer_cast
<CatchStatement
>
3236 StringData
* eName
= makeStaticString(c
->getClassName());
3238 // If there's already a catch of this class, skip;
3239 // the first one wins
3240 if (r
->m_names
.find(eName
) == r
->m_names
.end()) {
3241 // Don't let execution of the try body, or the
3242 // previous catch body,
3244 if (!firstHandler
) {
3247 firstHandler
= false;
3250 Label
* label
= new Label(e
);
3251 r
->m_names
.insert(eName
);
3252 r
->m_catchLabels
.push_back(std::pair
<StringData
*, Label
*>(eName
,
3260 Offset end_catches
= m_ue
.bcPos();
3261 if (after
.isUsed()) after
.set(e
);
3264 region
->m_finallyLabel
.set(e
);
3266 emitFinallyEpilogue(e
, region
.get());
3267 auto func
= getFunclet(f
);
3268 if (func
== nullptr) {
3270 new FinallyThunklet(f
, m_curFunc
->numLiveIterators());
3271 func
= addFunclet(f
, thunklet
);
3273 newFaultRegion(start
, end_catches
, &func
->m_entry
);
3279 case Statement::KindOfUnsetStatement
: {
3280 ExpressionListPtr
exps(
3281 static_pointer_cast
<UnsetStatement
>(node
)->getExps());
3282 for (int i
= 0, n
= exps
->getCount(); i
< n
; i
++) {
3283 emitVisitAndUnset(e
, (*exps
)[i
]);
3288 case Statement::KindOfWhileStatement
: {
3289 auto region
= createRegion(s
, Region::Kind::LoopOrSwitch
);
3290 WhileStatementPtr
ws(static_pointer_cast
<WhileStatement
>(s
));
3291 Label
& preCond
= registerContinue(ws
, region
.get(), 1, false)->m_label
;
3293 Label
& fail
= registerBreak(ws
, region
.get(), 1, false)->m_label
;
3296 ExpressionPtr
c(ws
->getCondExp());
3297 Emitter
condEmitter(c
, m_ue
, *this);
3298 visitIfCondition(c
, condEmitter
, tru
, fail
, true);
3299 if (tru
.isUsed()) tru
.set(e
);
3302 enterRegion(region
);
3303 SCOPE_EXIT
{ leaveRegion(region
); };
3304 visit(ws
->getBody());
3307 if (fail
.isUsed()) fail
.set(e
);
3311 case Statement::KindOfInterfaceStatement
:
3312 case Statement::KindOfClassStatement
: {
3313 emitClass(e
, node
->getClassScope(), false);
3317 case Statement::KindOfClassVariable
:
3318 case Statement::KindOfClassConstant
:
3319 case Statement::KindOfMethodStatement
:
3320 // handled by emitClass
3323 case Statement::KindOfFunctionStatement
: {
3324 MethodStatementPtr
m(static_pointer_cast
<MethodStatement
>(node
));
3325 // Only called for fn defs not on the top level
3326 assert(!node
->getClassScope()); // Handled directly by emitClass().
3327 StringData
* nName
= makeStaticString(m
->getOriginalName());
3328 FuncEmitter
* fe
= m_ue
.newFuncEmitter(nName
);
3329 e
.DefFunc(fe
->id());
3330 postponeMeth(m
, fe
, false);
3334 case Statement::KindOfGotoStatement
: {
3335 GotoStatementPtr
g(static_pointer_cast
<GotoStatement
>(node
));
3336 StringData
* nName
= makeStaticString(g
->label());
3337 emitGoto(e
, nName
, g
);
3341 case Statement::KindOfLabelStatement
: {
3342 LabelStatementPtr
l(static_pointer_cast
<LabelStatement
>(node
));
3343 StringData
* nName
= makeStaticString(l
->label());
3344 registerGoto(l
, m_regions
.back().get(), nName
, false)
3348 case Statement::KindOfUseTraitStatement
:
3349 case Statement::KindOfClassRequireStatement
:
3350 case Statement::KindOfTraitPrecStatement
:
3351 case Statement::KindOfTraitAliasStatement
: {
3356 ExpressionPtr ex
= static_pointer_cast
<Expression
>(node
);
3357 switch (ex
->getKindOf()) {
3358 case Expression::KindOfUnaryOpExpression
: {
3359 UnaryOpExpressionPtr
u(static_pointer_cast
<UnaryOpExpression
>(node
));
3360 int op
= u
->getOp();
3362 if (op
== T_UNSET
) {
3363 // php doesnt have an unset expression, but hphp's optimizations
3364 // sometimes introduce them
3365 ExpressionPtr
exp(u
->getExpression());
3366 if (exp
->is(Expression::KindOfExpressionList
)) {
3367 ExpressionListPtr
exps(
3368 static_pointer_cast
<ExpressionList
>(exp
));
3369 if (exps
->getListKind() == ExpressionList::ListKindParam
) {
3370 for (int i
= 0, n
= exps
->getCount(); i
< n
; i
++) {
3371 emitVisitAndUnset(e
, (*exps
)[i
]);
3377 emitVisitAndUnset(e
, exp
);
3381 if (op
== T_ARRAY
) {
3383 std::vector
<std::string
> keys
;
3385 if (u
->isScalar()) {
3389 if (m_staticArrays
.empty()) {
3390 e
.Array(tv
.m_data
.parr
);
3393 } else if (isPackedInit(u
->getExpression(), &num_elems
)) {
3394 // evaluate array values onto stack
3395 auto el
= static_pointer_cast
<ExpressionList
>(u
->getExpression());
3396 for (int i
= 0; i
< num_elems
; i
++) {
3397 auto ap
= static_pointer_cast
<ArrayPairExpression
>((*el
)[i
]);
3398 visit(ap
->getValue());
3399 emitConvertToCell(e
);
3401 e
.NewPackedArray(num_elems
);
3403 } else if (isStructInit(u
->getExpression(), keys
)) {
3404 auto el
= static_pointer_cast
<ExpressionList
>(u
->getExpression());
3405 for (int i
= 0, n
= keys
.size(); i
< n
; i
++) {
3406 auto ap
= static_pointer_cast
<ArrayPairExpression
>((*el
)[i
]);
3407 visit(ap
->getValue());
3408 emitConvertToCell(e
);
3410 e
.NewStructArray(keys
);
3413 assert(m_staticArrays
.empty());
3414 auto capacityHint
= MixedArray::SmallSize
;
3416 ExpressionPtr ex
= u
->getExpression();
3417 if (ex
->getKindOf() == Expression::KindOfExpressionList
) {
3418 auto el
= static_pointer_cast
<ExpressionList
>(ex
);
3420 int capacity
= el
->getCount();
3422 capacityHint
= capacity
;
3426 if (isPackedInit(ex
, &num_elems
, false /* ignore size */)) {
3427 e
.NewArray(capacityHint
);
3429 e
.NewMixedArray(capacityHint
);
3434 } else if (op
== T_VARRAY
|| op
== T_MIARRAY
|| op
== T_MSARRAY
) {
3435 assert(m_staticArrays
.empty());
3436 auto capacityHint
= MixedArray::SmallSize
;
3438 ExpressionPtr ex
= u
->getExpression();
3439 if (ex
->getKindOf() == Expression::KindOfExpressionList
) {
3440 auto el
= static_pointer_cast
<ExpressionList
>(ex
);
3442 int capacity
= el
->getCount();
3444 capacityHint
= capacity
;
3448 if (op
== T_VARRAY
) {
3449 e
.NewVArray(capacityHint
);
3450 } else if (op
== T_MIARRAY
) {
3451 e
.NewMIArray(capacityHint
);
3453 assert(op
== T_MSARRAY
);
3454 e
.NewMSArray(capacityHint
);
3459 } else if (op
== T_ISSET
) {
3460 ExpressionListPtr list
=
3461 dynamic_pointer_cast
<ExpressionList
>(u
->getExpression());
3463 // isset($a, $b, ...) ==> isset($a) && isset($b) && ...
3465 int n
= list
->getCount();
3466 for (int i
= 0; i
< n
- 1; ++i
) {
3473 // Treat the last one specially; let it fall through
3474 visit((*list
)[n
- 1]);
3479 visit(u
->getExpression());
3483 } else if (op
== '+' || op
== '-') {
3487 Id oldErrorLevelLoc
= -1;
3488 Offset start
= InvalidAbsoluteOffset
;
3490 oldErrorLevelLoc
= m_curFunc
->allocUnnamedLocal();
3491 emitVirtualLocal(oldErrorLevelLoc
);
3492 auto idx
= m_evalStack
.size() - 1;
3493 e
.Silence(m_evalStack
.getLoc(idx
), SilenceOp::Start
);
3494 start
= m_ue
.bcPos();
3497 ExpressionPtr exp
= u
->getExpression();
3498 if (exp
&& visit(exp
)) {
3499 if (op
!= T_EMPTY
&& op
!= T_INC
&& op
!= T_DEC
) {
3500 emitConvertToCell(e
);
3502 } else if (op
== T_EXIT
) {
3503 // exit without an expression is treated as exit(0)
3506 // __FILE__ and __DIR__ are special unary ops that don't
3508 assert(op
== T_FILE
|| op
== T_DIR
);
3513 // $this++ is a no-op
3514 if (auto var
= dynamic_pointer_cast
<SimpleVariable
>(exp
)) {
3515 if (var
->isThis()) break;
3518 auto const cop
= [&] {
3520 if (RuntimeOption::IntsOverflowToInts
) {
3521 return u
->getFront() ? IncDecOp::PreInc
: IncDecOp::PostInc
;
3523 return u
->getFront() ? IncDecOp::PreIncO
: IncDecOp::PostIncO
;
3525 if (RuntimeOption::IntsOverflowToInts
) {
3526 return u
->getFront() ? IncDecOp::PreDec
: IncDecOp::PostDec
;
3528 return u
->getFront() ? IncDecOp::PreDecO
: IncDecOp::PostDecO
;
3533 case T_EMPTY
: emitEmpty(e
); break;
3534 case T_CLONE
: e
.Clone(); break;
3536 RuntimeOption::IntsOverflowToInts
? e
.Add() : e
.AddO();
3539 RuntimeOption::IntsOverflowToInts
? e
.Sub() : e
.SubO();
3541 case '!': e
.Not(); break;
3542 case '~': e
.BitNot(); break;
3544 case T_INT_CAST
: e
.CastInt(); break;
3545 case T_DOUBLE_CAST
: e
.CastDouble(); break;
3546 case T_STRING_CAST
: e
.CastString(); break;
3547 case T_ARRAY_CAST
: e
.CastArray(); break;
3548 case T_OBJECT_CAST
: e
.CastObject(); break;
3549 case T_BOOL_CAST
: e
.CastBool(); break;
3550 case T_UNSET_CAST
: emitPop(e
); e
.Null(); break;
3551 case T_EXIT
: e
.Exit(); break;
3553 assert(oldErrorLevelLoc
>= 0);
3554 assert(start
!= InvalidAbsoluteOffset
);
3555 newFaultRegionAndFunclet(start
, m_ue
.bcPos(),
3556 new RestoreErrorReportingThunklet(oldErrorLevelLoc
));
3557 emitRestoreErrorReporting(e
, oldErrorLevelLoc
);
3558 m_curFunc
->freeUnnamedLocal(oldErrorLevelLoc
);
3561 case T_PRINT
: e
.Print(); break;
3562 case T_EVAL
: e
.Eval(); break;
3577 case Expression::KindOfAssignmentExpression
: {
3578 AssignmentExpressionPtr
ae(
3579 static_pointer_cast
<AssignmentExpression
>(node
));
3580 ExpressionPtr rhs
= ae
->getValue();
3582 Offset start
= InvalidAbsoluteOffset
;
3584 if (ae
->isRhsFirst()) {
3585 assert(!rhs
->hasContext(Expression::RefValue
));
3586 tempLocal
= emitVisitAndSetUnnamedL(e
, rhs
);
3587 start
= m_ue
.bcPos();
3590 visit(ae
->getVariable());
3591 emitClsIfSPropBase(e
);
3593 if (ae
->isRhsFirst()) {
3594 emitPushAndFreeUnnamedL(e
, tempLocal
, start
);
3599 if (rhs
->hasContext(Expression::RefValue
)) {
3600 emitConvertToVar(e
);
3602 if (ae
->hasAnyContext(Expression::AccessContext
|
3603 Expression::ObjectContext
|
3604 Expression::ExistContext
)) {
3606 * hphpc optimizations can result in
3607 * ($x =& $y)->foo or ($x =& $y)['foo'] or empty($x =& $y)
3609 emitConvertToCellIfVar(e
);
3612 emitConvertToCell(e
);
3618 case Expression::KindOfBinaryOpExpression
: {
3619 BinaryOpExpressionPtr
b(static_pointer_cast
<BinaryOpExpression
>(node
));
3620 int op
= b
->getOp();
3621 if (b
->isAssignmentOp()) {
3622 visit(b
->getExp1());
3623 emitClsIfSPropBase(e
);
3624 visit(b
->getExp2());
3625 emitConvertToCell(e
);
3630 if (b
->isShortCircuitOperator()) {
3631 Label tru
, fls
, done
;
3632 visitIfCondition(b
, e
, tru
, fls
, false);
3633 if (fls
.isUsed()) fls
.set(e
);
3634 if (currentPositionIsReachable()) {
3638 if (tru
.isUsed()) tru
.set(e
);
3639 if (currentPositionIsReachable()) {
3646 if (op
== T_INSTANCEOF
) {
3647 visit(b
->getExp1());
3648 emitConvertToCell(e
);
3649 ExpressionPtr second
= b
->getExp2();
3650 if (second
->isScalar()) {
3651 ScalarExpressionPtr scalar
=
3652 dynamic_pointer_cast
<ScalarExpression
>(second
);
3653 bool notQuoted
= scalar
&& !scalar
->isQuoted();
3654 std::string s
= second
->getLiteralString();
3655 if (s
== "static" && notQuoted
) {
3656 // Can't resolve this to a literal name at emission time
3657 static const StringData
* fname
3658 = makeStaticString("get_called_class");
3659 Offset fpiStart
= m_ue
.bcPos();
3660 e
.FPushFuncD(0, fname
);
3662 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
3667 } else if (s
!= "") {
3668 ClassScopeRawPtr cls
= second
->getOriginalClass();
3669 bool isTrait
= cls
&& cls
->isTrait();
3670 bool isSelf
= s
== "self" && notQuoted
;
3671 bool isParent
= s
== "parent" && notQuoted
;
3673 if (isTrait
&& (isSelf
|| isParent
)) {
3674 emitConvertToCell(e
);
3675 if (s
== "self" && notQuoted
) {
3677 } else if (s
== "parent" && notQuoted
) {
3686 s
= cls
->getOriginalName();
3687 } else if (isParent
) {
3688 s
= cls
->getOriginalParent();
3692 StringData
* nLiteral
= makeStaticString(s
);
3693 e
.InstanceOfD(nLiteral
);
3696 visit(b
->getExp2());
3697 emitConvertToCell(e
);
3701 visit(b
->getExp2());
3702 emitConvertToCell(e
);
3708 if (op
== T_COLLECTION
) {
3709 ScalarExpressionPtr cls
=
3710 static_pointer_cast
<ScalarExpression
>(b
->getExp1());
3712 ExpressionListPtr el
;
3714 el
= static_pointer_cast
<ExpressionList
>(b
->getExp2());
3715 nElms
= el
->getCount();
3717 const std::string
* clsName
= nullptr;
3718 cls
->getString(clsName
);
3719 int cType
= Collection::stringToType(*clsName
);
3720 if (cType
== Collection::PairType
) {
3722 throw IncludeTimeFatalException(b
,
3723 "Pair objects must have exactly 2 elements");
3725 } else if (cType
== Collection::InvalidType
) {
3726 throw IncludeTimeFatalException(b
,
3727 "Cannot use collection initialization for non-collection class");
3729 bool kvPairs
= (cType
== Collection::MapType
||
3730 cType
== Collection::ImmMapType
);
3731 e
.NewCol(cType
, nElms
);
3733 for (int i
= 0; i
< nElms
; i
++) {
3734 ArrayPairExpressionPtr
ap(
3735 static_pointer_cast
<ArrayPairExpression
>((*el
)[i
]));
3736 ExpressionPtr key
= ap
->getName();
3738 throw IncludeTimeFatalException(ap
,
3739 "Keys must be specified for Map initialization");
3742 emitConvertToCell(e
);
3743 visit(ap
->getValue());
3744 emitConvertToCell(e
);
3748 for (int i
= 0; i
< nElms
; i
++) {
3749 ArrayPairExpressionPtr
ap(
3750 static_pointer_cast
<ArrayPairExpression
>((*el
)[i
]));
3751 ExpressionPtr key
= ap
->getName();
3753 throw IncludeTimeFatalException(ap
,
3754 "Keys may not be specified for Vector, Set, or Pair "
3757 visit(ap
->getValue());
3758 emitConvertToCell(e
);
3765 visit(b
->getExp1());
3766 emitConvertToCellOrLoc(e
);
3767 visit(b
->getExp2());
3768 emitConvertToCell(e
);
3769 emitConvertSecondToCell(e
);
3771 case T_LOGICAL_XOR
: e
.Xor(); break;
3772 case '|': e
.BitOr(); break;
3773 case '&': e
.BitAnd(); break;
3774 case '^': e
.BitXor(); break;
3775 case '.': e
.Concat(); break;
3777 RuntimeOption::IntsOverflowToInts
? e
.Add() : e
.AddO();
3780 RuntimeOption::IntsOverflowToInts
? e
.Sub() : e
.SubO();
3783 RuntimeOption::IntsOverflowToInts
? e
.Mul() : e
.MulO();
3785 case '/': e
.Div(); break;
3786 case '%': e
.Mod(); break;
3787 case T_SL
: e
.Shl(); break;
3788 case T_SR
: e
.Shr(); break;
3789 case T_IS_IDENTICAL
: e
.Same(); break;
3790 case T_IS_NOT_IDENTICAL
: e
.NSame(); break;
3791 case T_IS_EQUAL
: e
.Eq(); break;
3792 case T_IS_NOT_EQUAL
: e
.Neq(); break;
3793 case '<': e
.Lt(); break;
3794 case T_IS_SMALLER_OR_EQUAL
: e
.Lte(); break;
3795 case '>': e
.Gt(); break;
3796 case T_IS_GREATER_OR_EQUAL
: e
.Gte(); break;
3797 case T_POW
: e
.Pow(); break;
3798 default: assert(false);
3803 case Expression::KindOfClassConstantExpression
: {
3804 ClassConstantExpressionPtr
cc(
3805 static_pointer_cast
<ClassConstantExpression
>(node
));
3806 auto const nName
= makeStaticString(cc
->getConName());
3807 auto const getOriginalClassName
= [&] {
3808 const std::string
& clsName
= cc
->getOriginalClassName();
3809 return makeStaticString(clsName
);
3812 // We treat ::class as a class constant in the AST and the
3813 // parser, but at the bytecode and runtime level it isn't
3815 auto const emitClsCns
= [&] {
3816 if (cc
->isColonColonClass()) {
3822 auto const noClassAllowed
= [&] {
3823 auto const nCls
= getOriginalClassName();
3824 std::ostringstream s
;
3825 s
<< "Cannot access " << nCls
->data() << "::" << nName
->data() <<
3826 " when no class scope is active";
3827 throw IncludeTimeFatalException(e
.getNode(), s
.str().c_str());
3830 if (cc
->isStatic()) {
3834 } else if (cc
->getClass()) {
3836 ExpressionPtr
cls(cc
->getClass());
3840 } else if (cc
->getOriginalClass() &&
3841 !cc
->getOriginalClass()->isTrait()) {
3842 // C::Constant inside a class
3843 auto nCls
= getOriginalClassName();
3844 if (cc
->isColonColonClass()) {
3847 e
.ClsCnsD(nName
, nCls
);
3849 } else if (cc
->isSelf()) {
3850 // self::Constant inside trait or pseudomain
3852 if (cc
->isColonColonClass() &&
3853 cc
->getFunctionScope()->inPseudoMain()) {
3857 } else if (cc
->isParent()) {
3858 // parent::Constant inside trait or pseudomain
3860 if (cc
->isColonColonClass() &&
3861 cc
->getFunctionScope()->inPseudoMain()) {
3866 // C::Constant inside a trait or pseudomain
3867 // Be careful to keep this case here after the isSelf and
3868 // isParent cases because StaticClassName::resolveClass()
3869 // will set cc->originalClassName to the trait's name for
3870 // the isSelf and isParent cases, but self and parent must
3871 // be resolved dynamically when used inside of traits.
3872 auto nCls
= getOriginalClassName();
3873 if (cc
->isColonColonClass()) noClassAllowed();
3874 e
.ClsCnsD(nName
, nCls
);
3879 case Expression::KindOfConstantExpression
: {
3880 ConstantExpressionPtr
c(static_pointer_cast
<ConstantExpression
>(node
));
3883 } else if (c
->isBoolean()) {
3884 if (c
->getBooleanValue()) {
3891 std::string nameStr
= c
->getOriginalName();
3892 StringData
* nName
= makeStaticString(nameStr
);
3893 if (c
->hadBackslash()) {
3896 const std::string
& nonNSName
= c
->getNonNSOriginalName();
3897 if (nonNSName
!= nameStr
) {
3898 StringData
* nsName
= nName
;
3899 nName
= makeStaticString(nonNSName
);
3900 e
.CnsU(nsName
, nName
);
3902 e
.Cns(makeStaticString(c
->getName()));
3909 case Expression::KindOfEncapsListExpression
: {
3910 EncapsListExpressionPtr
el(
3911 static_pointer_cast
<EncapsListExpression
>(node
));
3912 ExpressionListPtr
args(el
->getExpressions());
3913 int n
= args
? args
->getCount() : 0;
3915 FPIRegionRecorder
* fpi
= nullptr;
3916 if (el
->getType() == '`') {
3917 const static StringData
* s_shell_exec
=
3918 makeStaticString("shell_exec");
3919 Offset fpiStart
= m_ue
.bcPos();
3920 e
.FPushFuncD(1, s_shell_exec
);
3921 fpi
= new FPIRegionRecorder(this, m_ue
, m_evalStack
, fpiStart
);
3925 visit((*args
)[i
++]);
3926 emitConvertToCellOrLoc(e
);
3928 emitConvertToCell(e
);
3932 visit((*args
)[i
++]);
3933 emitConvertToCell(e
);
3934 emitConvertSecondToCell(e
);
3939 e
.String(staticEmptyString());
3942 if (el
->getType() == '`') {
3943 emitConvertToCell(e
);
3951 case Expression::KindOfArrayElementExpression
: {
3952 ArrayElementExpressionPtr
ae(
3953 static_pointer_cast
<ArrayElementExpression
>(node
));
3954 if (!ae
->isSuperGlobal() || !ae
->getOffset()) {
3955 visit(ae
->getVariable());
3956 // XHP syntax allows for expressions like "($a =& $b)[0]". We
3957 // handle this by unboxing the var produced by "($a =& $b)".
3958 emitConvertToCellIfVar(e
);
3961 ExpressionPtr offset
= ae
->getOffset();
3963 if (!ae
->isSuperGlobal() && offset
&&
3964 offset
->getScalarValue(v
) && (v
.isInteger() || v
.isString())) {
3966 m_evalStack
.push(StackSym::T
);
3967 m_evalStack
.setString(
3968 makeStaticString(v
.toCStrRef().get()));
3970 m_evalStack
.push(StackSym::I
);
3971 m_evalStack
.setInt(v
.asInt64Val());
3974 } else if (visit(offset
)) {
3975 emitConvertToCellOrLoc(e
);
3976 if (ae
->isSuperGlobal()) {
3984 if (!ae
->hasAnyContext(Expression::AccessContext
|
3985 Expression::ObjectContext
)) {
3986 m_tempLoc
= ae
->getLocation();
3991 case Expression::KindOfSimpleFunctionCall
: {
3992 SimpleFunctionCallPtr
call(
3993 static_pointer_cast
<SimpleFunctionCall
>(node
));
3994 ExpressionListPtr params
= call
->getParams();
3996 if (call
->isFatalFunction()) {
3997 if (params
&& params
->getCount() == 1) {
3998 ExpressionPtr p
= (*params
)[0];
4000 if (p
->getScalarValue(v
)) {
4001 assert(v
.isString());
4002 StringData
* msg
= makeStaticString(v
.toString());
4003 auto exn
= IncludeTimeFatalException(call
, "%s", msg
->data());
4004 exn
.setParseFatal(call
->isParseFatalFunction());
4009 } else if (emitCallUserFunc(e
, call
)) {
4011 } else if (call
->isCallToFunction("array_key_exists")) {
4012 if (params
&& params
->getCount() == 2) {
4013 visit((*params
)[0]);
4014 emitConvertToCell(e
);
4015 visit((*params
)[1]);
4016 emitConvertToCell(e
);
4017 call
->changeToBytecode();
4021 } else if (call
->isCallToFunction("idx") &&
4022 call
->isOptimizable() &&
4023 systemlibDefinesIdx
&&
4024 !Option::JitEnableRenameFunction
) {
4025 if (params
&& (params
->getCount() == 2 || params
->getCount() == 3)) {
4026 visit((*params
)[0]);
4027 emitConvertToCell(e
);
4028 visit((*params
)[1]);
4029 emitConvertToCell(e
);
4030 if (params
->getCount() == 2) {
4033 visit((*params
)[2]);
4034 emitConvertToCell(e
);
4036 call
->changeToBytecode();
4040 } else if (call
->isCallToFunction("hphp_array_idx")) {
4041 if (params
&& params
->getCount() == 3) {
4042 visit((*params
)[0]);
4043 emitConvertToCell(e
);
4044 visit((*params
)[1]);
4045 emitConvertToCell(e
);
4046 visit((*params
)[2]);
4047 emitConvertToCell(e
);
4048 call
->changeToBytecode();
4052 } else if (call
->isCallToFunction("strlen")) {
4053 if (params
&& params
->getCount() == 1) {
4054 visit((*params
)[0]);
4055 emitConvertToCell(e
);
4056 call
->changeToBytecode();
4060 } else if (call
->isCallToFunction("floor")) {
4061 if (params
&& params
->getCount() == 1) {
4062 visit((*params
)[0]);
4063 emitConvertToCell(e
);
4064 call
->changeToBytecode();
4068 } else if (call
->isCallToFunction("ceil")) {
4069 if (params
&& params
->getCount() == 1) {
4070 visit((*params
)[0]);
4071 emitConvertToCell(e
);
4072 call
->changeToBytecode();
4076 } else if (call
->isCallToFunction("sqrt")) {
4077 if (params
&& params
->getCount() == 1) {
4078 visit((*params
)[0]);
4079 emitConvertToCell(e
);
4080 call
->changeToBytecode();
4084 } else if (call
->isCallToFunction("define")) {
4085 if (params
&& params
->getCount() == 2) {
4086 ExpressionPtr p0
= (*params
)[0];
4088 if (p0
->getScalarValue(v0
) && v0
.isString()) {
4089 const StringData
* cname
=
4090 makeStaticString(v0
.toString());
4091 visit((*params
)[1]);
4092 emitConvertToCell(e
);
4097 } else if (emitSystemLibVarEnvFunc(e
, call
)) {
4099 } else if (call
->isCallToFunction("array_slice") &&
4100 params
&& params
->getCount() == 2 &&
4101 !Option::JitEnableRenameFunction
) {
4102 ExpressionPtr p0
= (*params
)[0];
4103 ExpressionPtr p1
= (*params
)[1];
4105 if (p0
->getKindOf() == Expression::KindOfSimpleFunctionCall
&&
4106 p1
->getScalarValue(v1
) && v1
.isInteger()) {
4107 SimpleFunctionCallPtr
innerCall(
4108 static_pointer_cast
<SimpleFunctionCall
>(p0
));
4109 ExpressionListPtr innerParams
= innerCall
->getParams();
4110 if (innerCall
->isCallToFunction("func_get_args") &&
4111 (!innerParams
|| innerParams
->getCount() == 0)) {
4112 params
->removeElement(0);
4113 emitFuncCall(e
, innerCall
,
4114 "__SystemLib\\func_slice_args", params
);
4119 } else if (call
->isCallToFunction("abs")) {
4120 if (params
&& params
->getCount() == 1) {
4121 visit((*params
)[0]);
4122 emitConvertToCell(e
);
4123 call
->changeToBytecode();
4127 } else if ((call
->isCallToFunction("class_exists") ||
4128 call
->isCallToFunction("interface_exists") ||
4129 call
->isCallToFunction("trait_exists"))
4131 && (params
->getCount() == 1 || params
->getCount() == 2)) {
4133 emitNameString(e
, (*params
)[0]);
4134 emitConvertToCell(e
);
4137 // Push autoload, defaulting to true
4138 if (params
->getCount() == 1) {
4141 visit((*params
)[1]);
4142 emitConvertToCell(e
);
4145 if (call
->isCallToFunction("class_exists")) {
4146 e
.OODeclExists(OODeclExistsOp::Class
);
4147 } else if (call
->isCallToFunction("interface_exists")) {
4148 e
.OODeclExists(OODeclExistsOp::Interface
);
4150 assert(call
->isCallToFunction("trait_exists"));
4151 e
.OODeclExists(OODeclExistsOp::Trait
);
4154 } else if (call
->isCallToFunction("get_class") &&
4156 call
->getClassScope() &&
4157 !call
->getClassScope()->isTrait()) {
4159 makeStaticString(call
->getClassScope()->getOriginalName());
4163 #define TYPE_CONVERT_INSTR(what, What) \
4164 else if (call->isCallToFunction(#what"val") && \
4165 params && params->getCount() == 1) { \
4166 visit((*params)[0]); \
4167 emitConvertToCell(e); \
4171 TYPE_CONVERT_INSTR(bool, Bool
)
4172 TYPE_CONVERT_INSTR(int, Int
)
4173 TYPE_CONVERT_INSTR(double, Double
)
4174 TYPE_CONVERT_INSTR(float, Double
)
4175 TYPE_CONVERT_INSTR(str
, String
)
4176 #undef TYPE_CONVERT_INSTR
4178 #define TYPE_CHECK_INSTR(what, What) \
4179 else if (call->isCallToFunction("is_"#what) && \
4180 params && params->getCount() == 1) { \
4181 visit((*call->getParams())[0]); \
4182 emitIsType(e, IsTypeOp::What); \
4186 TYPE_CHECK_INSTR(null
, Null
)
4187 TYPE_CHECK_INSTR(object
, Obj
)
4188 TYPE_CHECK_INSTR(array
, Arr
)
4189 TYPE_CHECK_INSTR(string
, Str
)
4190 TYPE_CHECK_INSTR(int, Int
)
4191 TYPE_CHECK_INSTR(integer
, Int
)
4192 TYPE_CHECK_INSTR(long, Int
)
4193 TYPE_CHECK_INSTR(bool, Bool
)
4194 TYPE_CHECK_INSTR(double, Dbl
)
4195 TYPE_CHECK_INSTR(real
, Dbl
)
4196 TYPE_CHECK_INSTR(float, Dbl
)
4197 TYPE_CHECK_INSTR(scalar
, Scalar
)
4198 #undef TYPE_CHECK_INSTR
4201 case Expression::KindOfDynamicFunctionCall
: {
4202 emitFuncCall(e
, static_pointer_cast
<FunctionCall
>(node
));
4206 case Expression::KindOfIncludeExpression
: {
4207 IncludeExpressionPtr
ie(static_pointer_cast
<IncludeExpression
>(node
));
4208 if (ie
->isReqLit()) {
4209 StringData
* nValue
= makeStaticString(ie
->includePath());
4212 visit(ie
->getExpression());
4213 emitConvertToCell(e
);
4215 switch (ie
->getOp()) {
4219 case T_INCLUDE_ONCE
:
4225 case T_REQUIRE_ONCE
:
4226 if (ie
->isDocumentRoot()) {
4236 case Expression::KindOfListAssignment
: {
4237 ListAssignmentPtr
la(static_pointer_cast
<ListAssignment
>(node
));
4238 ExpressionPtr rhs
= la
->getArray();
4240 // listAssignmentVisitLHS should have handled this
4243 bool nullRHS
= la
->getRHSKind() == ListAssignment::Null
;
4244 // Assign RHS to temp local, unless it's already a simple variable
4245 bool simpleRHS
= rhs
->is(Expression::KindOfSimpleVariable
)
4246 && !static_pointer_cast
<SimpleVariable
>(rhs
)->getAlwaysStash();
4248 Offset start
= InvalidAbsoluteOffset
;
4250 if (!simpleRHS
&& la
->isRhsFirst()) {
4251 tempLocal
= emitVisitAndSetUnnamedL(e
, rhs
);
4252 start
= m_ue
.bcPos();
4255 // We use "index chains" to deal with nested list assignment. We will
4256 // end up with one index chain per expression we need to assign to.
4257 // The helper function will populate indexChains.
4258 std::vector
<IndexChain
*> indexChains
;
4259 IndexChain workingChain
;
4260 listAssignmentVisitLHS(e
, la
, workingChain
, indexChains
);
4262 if (!simpleRHS
&& !la
->isRhsFirst()) {
4263 assert(tempLocal
== -1);
4264 assert(start
== InvalidAbsoluteOffset
);
4265 tempLocal
= emitVisitAndSetUnnamedL(e
, rhs
);
4266 start
= m_ue
.bcPos();
4271 listAssignmentAssignElements(e
, indexChains
, nullptr);
4272 } else if (simpleRHS
) {
4273 listAssignmentAssignElements(e
, indexChains
, [&] { visit(rhs
); });
4275 listAssignmentAssignElements(
4277 [&] { emitVirtualLocal(tempLocal
); }
4281 // Leave the RHS on the stack
4285 emitPushAndFreeUnnamedL(e
, tempLocal
, start
);
4291 case Expression::KindOfNewObjectExpression
: {
4292 NewObjectExpressionPtr
ne(
4293 static_pointer_cast
<NewObjectExpression
>(node
));
4294 ExpressionListPtr
params(ne
->getParams());
4295 int numParams
= params
? params
->getCount() : 0;
4296 ClassScopeRawPtr cls
= ne
->getOriginalClass();
4299 if (ne
->isStatic()) {
4302 fpiStart
= m_ue
.bcPos();
4303 e
.FPushCtor(numParams
);
4304 } else if (ne
->getOriginalName().empty()) {
4306 visit(ne
->getNameExp());
4308 fpiStart
= m_ue
.bcPos();
4309 e
.FPushCtor(numParams
);
4310 } else if ((ne
->isSelf() || ne
->isParent()) &&
4311 (!cls
|| cls
->isTrait() ||
4312 (ne
->isParent() && cls
->getOriginalParent().empty()))) {
4314 // new self() inside a trait or code statically not inside any class
4317 // new parent() inside a trait, code statically not inside any
4318 // class, or a class with no parent
4321 fpiStart
= m_ue
.bcPos();
4322 e
.FPushCtor(numParams
);
4324 // new C() inside trait or pseudomain
4325 fpiStart
= m_ue
.bcPos();
4326 e
.FPushCtorD(numParams
,
4327 makeStaticString(ne
->getOriginalClassName()));
4331 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
4332 for (int i
= 0; i
< numParams
; i
++) {
4333 emitFuncCallArg(e
, (*params
)[i
], i
);
4337 if (ne
->hasUnpack()) {
4338 e
.FCallUnpack(numParams
);
4346 case Expression::KindOfObjectMethodExpression
: {
4347 ObjectMethodExpressionPtr
om(
4348 static_pointer_cast
<ObjectMethodExpression
>(node
));
4351 visit(om
->getObject());
4352 m_tempLoc
= om
->getLocation();
4353 emitConvertToCell(e
);
4354 ExpressionListPtr
params(om
->getParams());
4355 int numParams
= params
? params
->getCount() : 0;
4357 Offset fpiStart
= 0;
4358 ExpressionPtr methName
= om
->getNameExp();
4359 bool useDirectForm
= false;
4360 if (methName
->is(Expression::KindOfScalarExpression
)) {
4361 ScalarExpressionPtr
sval(
4362 static_pointer_cast
<ScalarExpression
>(methName
));
4363 const std::string
& methStr
= sval
->getOriginalLiteralString();
4364 if (!methStr
.empty()) {
4367 // Use getOriginalLiteralString(), which hasn't been
4368 // case-normalized, since __call() needs to preserve
4370 StringData
* nameLiteral
= makeStaticString(methStr
);
4371 fpiStart
= m_ue
.bcPos();
4375 om
->isNullSafe() ? ObjMethodOp::NullSafe
: ObjMethodOp::NullThrows
4377 useDirectForm
= true;
4380 if (!useDirectForm
) {
4384 emitConvertToCell(e
);
4385 fpiStart
= m_ue
.bcPos();
4388 om
->isNullSafe() ? ObjMethodOp::NullSafe
: ObjMethodOp::NullThrows
4392 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
4395 for (int i
= 0; i
< numParams
; i
++) {
4396 emitFuncCallArg(e
, (*params
)[i
], i
);
4399 if (om
->hasUnpack()) {
4400 e
.FCallUnpack(numParams
);
4404 if (Option::WholeProgram
) {
4405 fixReturnType(e
, om
);
4410 case Expression::KindOfObjectPropertyExpression
: {
4411 ObjectPropertyExpressionPtr
op(
4412 static_pointer_cast
<ObjectPropertyExpression
>(node
));
4413 ExpressionPtr obj
= op
->getObject();
4414 SimpleVariablePtr sv
= dynamic_pointer_cast
<SimpleVariable
>(obj
);
4415 if (sv
&& sv
->isThis() && sv
->hasContext(Expression::ObjectContext
)) {
4417 m_evalStack
.push(StackSym::H
);
4421 StringData
* clsName
= getClassName(op
->getObject());
4423 m_evalStack
.setKnownCls(clsName
, false);
4425 emitNameString(e
, op
->getProperty(), true);
4426 if (!op
->hasAnyContext(Expression::AccessContext
|
4427 Expression::ObjectContext
)) {
4428 m_tempLoc
= op
->getLocation();
4434 case Expression::KindOfQOpExpression
: {
4435 QOpExpressionPtr
q(static_pointer_cast
<QOpExpression
>(node
));
4437 // <expr> ? <expr> : <expr>
4438 Label tru
, fals
, done
;
4440 Emitter
condEmitter(q
->getCondition(), m_ue
, *this);
4441 visitIfCondition(q
->getCondition(), condEmitter
,
4447 if (currentPositionIsReachable()) {
4449 emitConvertToCell(e
);
4452 if (fals
.isUsed()) fals
.set(e
);
4453 if (currentPositionIsReachable()) {
4455 emitConvertToCell(e
);
4457 if (done
.isUsed()) {
4459 m_evalStack
.cleanTopMeta();
4464 visit(q
->getCondition());
4465 emitConvertToCell(e
);
4470 emitConvertToCell(e
);
4472 m_evalStack
.cleanTopMeta();
4477 case Expression::KindOfScalarExpression
: {
4479 ex
->getScalarValue(v
);
4480 switch (v
.getType()) {
4482 case KindOfStaticString
:
4484 StringData
* nValue
= makeStaticString(v
.getStringData());
4489 e
.Int(v
.getInt64());
4492 e
.Double(v
.getDouble()); break;
4499 case Expression::KindOfSimpleVariable
: {
4500 SimpleVariablePtr
sv(static_pointer_cast
<SimpleVariable
>(node
));
4502 if (sv
->hasContext(Expression::ObjectContext
)) {
4504 } else if (sv
->getFunctionScope()->needsLocalThis()) {
4505 static const StringData
* thisStr
= makeStaticString("this");
4506 Id thisId
= m_curFunc
->lookupVarId(thisStr
);
4507 emitVirtualLocal(thisId
);
4509 if (sv
->isGuarded()) {
4512 auto const subop
= sv
->hasContext(Expression::ExistContext
)
4513 ? BareThisOp::NoNotice
4514 : BareThisOp::Notice
;
4519 StringData
* nLiteral
= makeStaticString(sv
->getName());
4520 if (sv
->isSuperGlobal()) {
4525 Id i
= m_curFunc
->lookupVarId(nLiteral
);
4526 emitVirtualLocal(i
);
4527 if (sv
->getAlwaysStash() &&
4528 !sv
->hasAnyContext(Expression::ExistContext
|
4529 Expression::RefValue
|
4530 Expression::LValue
|
4531 Expression::RefParameter
)) {
4532 emitConvertToCell(e
);
4539 case Expression::KindOfDynamicVariable
: {
4540 DynamicVariablePtr
dv(static_pointer_cast
<DynamicVariable
>(node
));
4541 visit(dv
->getSubExpression());
4542 emitConvertToCellOrLoc(e
);
4547 case Expression::KindOfStaticMemberExpression
: {
4548 StaticMemberExpressionPtr
sm(
4549 static_pointer_cast
<StaticMemberExpression
>(node
));
4550 emitVirtualClassBase(e
, sm
.get());
4551 emitNameString(e
, sm
->getExp());
4556 case Expression::KindOfArrayPairExpression
: {
4557 ArrayPairExpressionPtr
ap(
4558 static_pointer_cast
<ArrayPairExpression
>(node
));
4560 ExpressionPtr key
= ap
->getName();
4561 if (!m_staticArrays
.empty()) {
4562 ExpressionPtr val
= ap
->getValue();
4565 initScalar(tvVal
, val
);
4567 if (key
!= nullptr) {
4568 assert(key
->isScalar());
4569 TypedValue tvKey
= make_tv
<KindOfNull
>();
4570 if (!key
->getScalarValue(tvAsVariant(&tvKey
))) {
4571 InvariantViolation("Expected scalar value for array key\n");
4574 m_staticArrays
.back().set(tvAsCVarRef(&tvKey
),
4575 tvAsVariant(&tvVal
));
4577 m_staticArrays
.back().append(tvAsCVarRef(&tvVal
));
4580 // Assume new array is on top of stack
4581 bool hasKey
= (bool)key
;
4584 emitConvertToCellOrLoc(e
);
4586 visit(ap
->getValue());
4588 emitConvertToVar(e
);
4590 emitConvertSecondToCell(e
);
4596 emitConvertToCell(e
);
4598 emitConvertSecondToCell(e
);
4607 case Expression::KindOfExpressionList
: {
4608 ExpressionListPtr
el(static_pointer_cast
<ExpressionList
>(node
));
4609 int nelem
= el
->getCount(), i
;
4610 bool pop
= el
->getListKind() != ExpressionList::ListKindParam
;
4611 int keep
= el
->getListKind() == ExpressionList::ListKindLeft
?
4614 for (i
= 0; i
< nelem
; i
++) {
4615 ExpressionPtr
p((*el
)[i
]);
4617 if (pop
&& i
!= keep
) {
4626 case Expression::KindOfParameterExpression
: {
4629 case Expression::KindOfModifierExpression
: {
4632 case Expression::KindOfUserAttribute
: {
4635 case Expression::KindOfClosureExpression
: {
4636 // Closures are implemented by anonymous classes that extend Closure.
4637 // There is one anonymous class per closure body.
4638 ClosureExpressionPtr
ce(static_pointer_cast
<ClosureExpression
>(node
));
4640 // Build a convenient list of use-variables. Each one corresponds to:
4641 // (a) an instance variable, to store the value until call time
4642 // (b) a parameter of the generated constructor
4643 // (c) an argument to the constructor at the definition site
4644 // (d) a line of code in the generated constructor;
4645 // (e) a line of code in the generated prologue to the closure body
4646 ExpressionListPtr
useList(ce
->getClosureVariables());
4647 ClosureUseVarVec useVars
;
4648 int useCount
= (useList
? useList
->getCount() : 0);
4650 for (int i
= 0; i
< useCount
; ++i
) {
4651 ParameterExpressionPtr
var(
4652 static_pointer_cast
<ParameterExpression
>((*useList
)[i
]));
4653 StringData
* varName
= makeStaticString(var
->getName());
4654 useVars
.push_back(ClosureUseVar(varName
, var
->isRef()));
4658 // We're still at the closure definition site. Emit code to instantiate
4659 // the new anonymous class, with the use variables as arguments.
4660 ExpressionListPtr
valuesList(ce
->getClosureValues());
4661 for (int i
= 0; i
< useCount
; ++i
) {
4662 emitBuiltinCallArg(e
, (*valuesList
)[i
], i
, useVars
[i
].second
);
4665 // The parser generated a unique name for the function,
4666 // use that for the class
4667 std::string clsName
= ce
->getClosureFunction()->getOriginalName();
4669 if (m_curFunc
->isPseudoMain()) {
4670 std::ostringstream oss
;
4671 oss
<< clsName
<< '$' << std::hex
<<
4672 m_curFunc
->ue().md5().q
[1] << m_curFunc
->ue().md5().q
[0] << '$';
4673 clsName
= oss
.str();
4676 if (Option::WholeProgram
) {
4679 EmittedClosures::accessor acc
;
4680 s_emittedClosures
.insert(acc
, makeStaticString(clsName
));
4681 my_id
= ++acc
->second
;
4684 // The closure was from a trait, so we need a unique name in the
4685 // implementing class. _ is different from the #, which is used for
4686 // many closures in the same func in ParserBase::newClosureName
4687 folly::toAppend('_', my_id
, &clsName
);
4691 auto ssClsName
= makeStaticString(clsName
);
4692 e
.CreateCl(useCount
, ssClsName
);
4694 // From here on out, we're creating a new class to hold the closure.
4695 const static StringData
* parentName
= makeStaticString("Closure");
4696 const Location
* sLoc
= ce
->getLocation().get();
4697 PreClassEmitter
* pce
= m_ue
.newPreClassEmitter(
4698 ssClsName
, PreClass::ClosureHoistable
);
4700 auto const attrs
= AttrNoOverride
| AttrUnique
| AttrPersistent
;
4702 pce
->init(sLoc
->line0
, sLoc
->line1
, m_ue
.bcPos(),
4703 attrs
, parentName
, nullptr);
4705 // Instance properties---one for each use var, and one for
4706 // each static local.
4708 tvWriteUninit(&uninit
);
4709 for (auto& useVar
: useVars
) {
4710 pce
->addProperty(useVar
.first
, AttrPrivate
, nullptr, nullptr,
4711 &uninit
, RepoAuthType
{});
4714 // The __invoke method. This is the body of the closure, preceded by
4715 // code that pulls the object's instance variables into locals.
4716 static const StringData
* invokeName
= makeStaticString("__invoke");
4717 FuncEmitter
* invoke
= m_ue
.newMethodEmitter(invokeName
, pce
);
4718 invoke
->isClosureBody
= true;
4719 pce
->addMethod(invoke
);
4720 MethodStatementPtr
body(
4721 static_pointer_cast
<MethodStatement
>(ce
->getClosureFunction()));
4722 postponeMeth(body
, invoke
, false, new ClosureUseVarVec(useVars
));
4726 case Expression::KindOfYieldExpression
: {
4727 YieldExpressionPtr
y(static_pointer_cast
<YieldExpression
>(node
));
4729 registerYieldAwait(y
);
4730 assert(m_evalStack
.size() == 0);
4732 // evaluate key passed to yield, if applicable
4733 ExpressionPtr keyExp
= y
->getKeyExpression();
4735 m_curFunc
->isPairGenerator
= true;
4737 emitConvertToCell(e
);
4740 // evaluate value expression passed to yield
4741 visit(y
->getValueExpression());
4742 emitConvertToCell(e
);
4744 // suspend generator
4746 assert(m_evalStack
.size() == 2);
4749 assert(m_evalStack
.size() == 1);
4753 // continue with the received result on the stack
4754 assert(m_evalStack
.size() == 1);
4757 case Expression::KindOfAwaitExpression
: {
4758 AwaitExpressionPtr
await(static_pointer_cast
<AwaitExpression
>(node
));
4760 registerYieldAwait(await
);
4761 assert(m_evalStack
.size() == 0);
4763 // If we know statically that it's a subtype of WaitHandle, we
4764 // don't need to make a call.
4765 bool const isKnownWaitHandle
= [&] {
4766 auto const expr
= await
->getExpression();
4767 auto const ar
= expr
->getScope()->getContainingProgram();
4768 auto const type
= expr
->getActualType();
4769 return type
&& Type::SubType(ar
, type
,
4770 Type::GetType(Type::KindOfObject
, "WaitHandle"));
4775 // evaluate expression passed to await
4776 visit(await
->getExpression());
4777 emitConvertToCell(e
);
4779 // if expr is null, just continue
4781 emitIsType(e
, IsTypeOp::Null
);
4784 if (!isKnownWaitHandle
) {
4786 StringData
* nWaitHandle
= makeStaticString("WaitHandle");
4789 e
.InstanceOfD(nWaitHandle
);
4791 emitConstMethodCallNoParams(e
, "getWaitHandle");
4794 assert(m_evalStack
.size() == 1);
4796 // TODO(#3197024): if isKnownWaitHandle, we should put an
4797 // AssertObjStk so the Await type check can be avoided.
4798 e
.Await(m_pendingIters
.size());
4803 case Expression::KindOfQueryExpression
: {
4804 QueryExpressionPtr
query(static_pointer_cast
<QueryExpression
>(node
));
4805 auto args
= *query
->getQueryArguments();
4806 auto numArgs
= args
.getCount();
4808 emitConvertToCell(e
);
4809 auto fpiStart
= m_ue
.bcPos();
4810 StringData
* executeQuery
= makeStaticString("executeQuery");
4811 e
.FPushObjMethodD(numArgs
+1, executeQuery
, ObjMethodOp::NullThrows
);
4813 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
4814 e
.String(query
->getQueryString());
4815 emitFPass(e
, 0, PassByRefKind::ErrorOnCell
);
4816 auto selectCallback
= query
->getSelectClosure();
4817 if (selectCallback
!= nullptr) {
4818 visit(selectCallback
);
4819 emitConvertToCell(e
);
4823 emitFPass(e
, 1, PassByRefKind::ErrorOnCell
);
4824 for (int i
= 1; i
< numArgs
; i
++) {
4826 emitConvertToCell(e
);
4827 emitFPass(e
, i
+1, PassByRefKind::ErrorOnCell
);
4834 case Expression::KindOfFromClause
:
4835 case Expression::KindOfLetClause
:
4836 case Expression::KindOfWhereClause
:
4837 case Expression::KindOfSelectClause
:
4838 case Expression::KindOfIntoClause
:
4839 case Expression::KindOfJoinClause
:
4840 case Expression::KindOfGroupClause
:
4841 case Expression::KindOfOrderbyClause
:
4842 case Expression::KindOfOrdering
: {
4851 void EmitterVisitor::emitConstMethodCallNoParams(Emitter
& e
, string name
) {
4852 auto const nameLit
= makeStaticString(name
);
4853 auto const fpiStart
= m_ue
.bcPos();
4854 e
.FPushObjMethodD(0, nameLit
, ObjMethodOp::NullThrows
);
4856 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
4859 emitConvertToCell(e
);
4862 int EmitterVisitor::scanStackForLocation(int iLast
) {
4864 assert(iLast
< (int)m_evalStack
.size());
4865 for (int i
= iLast
; i
>= 0; --i
) {
4866 char marker
= StackSym::GetMarker(m_evalStack
.get(i
));
4867 if (marker
!= StackSym::E
&& marker
!= StackSym::W
&&
4868 marker
!= StackSym::P
&& marker
!= StackSym::M
) {
4872 InvariantViolation("Emitter expected a location on the stack but none "
4873 "was found (at offset %d)",
4878 void EmitterVisitor::buildVectorImm(std::vector
<uchar
>& vectorImm
,
4879 int iFirst
, int iLast
, bool allowW
,
4881 assert(iFirst
>= 0);
4882 assert(iFirst
<= iLast
);
4883 assert(iLast
< (int)m_evalStack
.size());
4885 vectorImm
.reserve(iLast
- iFirst
+ 1);
4890 * Because of php's order of evaluation rules, we store the classref
4891 * for certain types of S-vectors at the end, instead of the front.
4892 * See emitCls for details.
4896 char sym
= m_evalStack
.get(iFirst
);
4897 char symFlavor
= StackSym::GetSymFlavor(sym
);
4898 char marker
= StackSym::GetMarker(sym
);
4901 if (symFlavor
== StackSym::C
) {
4902 vectorImm
.push_back(LNC
);
4903 } else if (symFlavor
== StackSym::L
) {
4904 vectorImm
.push_back(LNL
);
4910 if (symFlavor
== StackSym::C
) {
4911 vectorImm
.push_back(LGC
);
4912 } else if (symFlavor
== StackSym::L
) {
4913 vectorImm
.push_back(LGL
);
4919 if (symFlavor
!= StackSym::L
&& symFlavor
!= StackSym::C
) {
4920 unexpectedStackSym(sym
, "S-vector base, prop name");
4922 if (m_evalStack
.get(iLast
) != StackSym::AM
) {
4923 unexpectedStackSym(sym
, "S-vector base, class ref");
4925 const bool curIsLoc
= symFlavor
== StackSym::L
;
4926 vectorImm
.push_back(curIsLoc
? LSL
: LSC
);
4928 case StackSym::None
: {
4929 if (symFlavor
== StackSym::L
) {
4930 vectorImm
.push_back(LL
);
4931 } else if (symFlavor
== StackSym::C
) {
4932 vectorImm
.push_back(LC
);
4933 } else if (symFlavor
== StackSym::R
) {
4934 vectorImm
.push_back(LR
);
4935 } else if (symFlavor
== StackSym::H
) {
4936 vectorImm
.push_back(LH
);
4945 if (symFlavor
== StackSym::L
) {
4946 encodeIvaToVector(vectorImm
, m_evalStack
.getLoc(iFirst
));
4951 while (i
<= iLast
) {
4952 char sym
= m_evalStack
.get(i
);
4953 char symFlavor
= StackSym::GetSymFlavor(sym
);
4954 char marker
= StackSym::GetMarker(sym
);
4957 if (const StringData
* name
= m_evalStack
.getName(i
)) {
4958 strid
= m_ue
.mergeLitstr(name
);
4963 assert(symFlavor
== StackSym::A
);
4967 if (symFlavor
== StackSym::L
) {
4968 vectorImm
.push_back(MEL
);
4969 } else if (symFlavor
== StackSym::T
) {
4970 vectorImm
.push_back(MET
);
4971 } else if (symFlavor
== StackSym::I
) {
4972 vectorImm
.push_back(MEI
);
4974 vectorImm
.push_back(MEC
);
4979 vectorImm
.push_back(MW
);
4981 throw IncludeTimeFatalException(e
.getNode(),
4982 "Cannot use [] for reading");
4986 if (symFlavor
== StackSym::L
) {
4987 vectorImm
.push_back(MPL
);
4988 } else if (symFlavor
== StackSym::T
) {
4989 vectorImm
.push_back(MPT
);
4991 vectorImm
.push_back(MPC
);
4997 default: assert(false); break;
5000 if (symFlavor
== StackSym::L
) {
5001 encodeIvaToVector(vectorImm
, m_evalStack
.getLoc(i
));
5002 } else if (symFlavor
== StackSym::T
) {
5003 assert(strid
!= -1);
5004 encodeToVector
<int32_t>(vectorImm
, strid
);
5005 } else if (symFlavor
== StackSym::I
) {
5006 encodeToVector
<int64_t>(vectorImm
, m_evalStack
.getInt(i
));
5010 if (marker
!= StackSym::W
) ++metaI
;
5014 void EmitterVisitor::emitPop(Emitter
& e
) {
5015 if (checkIfStackEmpty("Pop*")) return;
5016 LocationGuard
loc(e
, m_tempLoc
);
5019 emitClsIfSPropBase(e
);
5020 int iLast
= m_evalStack
.size()-1;
5021 int i
= scanStackForLocation(iLast
);
5024 char sym
= m_evalStack
.get(i
);
5025 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
5027 case StackSym::L
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5028 case StackSym::C
: e
.PopC(); break;
5029 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5030 case StackSym::CN
: e
.CGetN(); e
.PopC(); break;
5031 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5032 case StackSym::CG
: e
.CGetG(); e
.PopC(); break;
5033 case StackSym::LS
: e
.CGetL2(m_evalStack
.getLoc(i
)); // fall through
5034 case StackSym::CS
: e
.CGetS(); e
.PopC(); break;
5035 case StackSym::V
: e
.PopV(); break;
5036 case StackSym::R
: e
.PopR(); break;
5038 unexpectedStackSym(sym
, "emitPop");
5043 std::vector
<uchar
> vectorImm
;
5044 buildVectorImm(vectorImm
, i
, iLast
, false, e
);
5050 void EmitterVisitor::emitCGetL2(Emitter
& e
) {
5051 assert(m_evalStack
.size() >= 2);
5052 assert(m_evalStack
.sizeActual() >= 1);
5053 assert(StackSym::GetSymFlavor(m_evalStack
.get(m_evalStack
.size() - 2))
5055 int localIdx
= m_evalStack
.getLoc(m_evalStack
.size() - 2);
5059 void EmitterVisitor::emitCGetL3(Emitter
& e
) {
5060 assert(m_evalStack
.size() >= 3);
5061 assert(m_evalStack
.sizeActual() >= 2);
5062 assert(StackSym::GetSymFlavor(m_evalStack
.get(m_evalStack
.size() - 3))
5064 int localIdx
= m_evalStack
.getLoc(m_evalStack
.size() - 3);
5068 void EmitterVisitor::emitPushL(Emitter
& e
) {
5069 assert(m_evalStack
.size() >= 1);
5070 auto const idx
= m_evalStack
.size() - 1;
5071 assert(StackSym::GetSymFlavor(m_evalStack
.get(idx
)) == StackSym::L
);
5072 e
.PushL(m_evalStack
.getLoc(idx
));
5075 void EmitterVisitor::emitAGet(Emitter
& e
) {
5076 if (checkIfStackEmpty("AGet*")) return;
5078 emitConvertToCellOrLoc(e
);
5079 switch (char sym
= m_evalStack
.top()) {
5081 e
.AGetL(m_evalStack
.getLoc(m_evalStack
.size() - 1));
5087 unexpectedStackSym(sym
, "emitAGet");
5091 void EmitterVisitor::emitCGet(Emitter
& e
) {
5092 if (checkIfStackEmpty("CGet*")) return;
5093 LocationGuard
loc(e
, m_tempLoc
);
5096 emitClsIfSPropBase(e
);
5097 int iLast
= m_evalStack
.size()-1;
5098 int i
= scanStackForLocation(iLast
);
5101 char sym
= m_evalStack
.get(i
);
5102 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
5104 case StackSym::L
: e
.CGetL(m_evalStack
.getLoc(i
)); break;
5105 case StackSym::C
: /* nop */ break;
5106 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5107 case StackSym::CN
: e
.CGetN(); break;
5108 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5109 case StackSym::CG
: e
.CGetG(); break;
5110 case StackSym::LS
: e
.CGetL2(m_evalStack
.getLoc(i
)); // fall through
5111 case StackSym::CS
: e
.CGetS(); break;
5112 case StackSym::V
: e
.Unbox(); break;
5113 case StackSym::R
: e
.UnboxR(); break;
5115 unexpectedStackSym(sym
, "emitCGet");
5120 std::vector
<uchar
> vectorImm
;
5121 buildVectorImm(vectorImm
, i
, iLast
, false, e
);
5126 void EmitterVisitor::emitVGet(Emitter
& e
) {
5127 if (checkIfStackEmpty("VGet*")) return;
5128 LocationGuard
loc(e
, m_tempLoc
);
5131 emitClsIfSPropBase(e
);
5132 int iLast
= m_evalStack
.size()-1;
5133 int i
= scanStackForLocation(iLast
);
5136 char sym
= m_evalStack
.get(i
);
5137 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
5139 case StackSym::L
: e
.VGetL(m_evalStack
.getLoc(i
)); break;
5140 case StackSym::C
: e
.Box(); break;
5141 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5142 case StackSym::CN
: e
.VGetN(); break;
5143 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5144 case StackSym::CG
: e
.VGetG(); break;
5145 case StackSym::LS
: e
.CGetL2(m_evalStack
.getLoc(i
)); // fall through
5146 case StackSym::CS
: e
.VGetS(); break;
5147 case StackSym::V
: /* nop */ break;
5148 case StackSym::R
: e
.BoxR(); break;
5150 unexpectedStackSym(sym
, "emitVGet");
5155 std::vector
<uchar
> vectorImm
;
5156 buildVectorImm(vectorImm
, i
, iLast
, true, e
);
5161 Id
EmitterVisitor::emitVisitAndSetUnnamedL(Emitter
& e
, ExpressionPtr exp
) {
5163 emitConvertToCell(e
);
5165 return emitSetUnnamedL(e
);
5168 Id
EmitterVisitor::emitSetUnnamedL(Emitter
& e
) {
5169 // HACK: emitVirtualLocal would pollute m_evalStack before visiting exp,
5170 // YieldExpression won't be happy
5171 Id tempLocal
= m_curFunc
->allocUnnamedLocal();
5172 auto& ue
= e
.getUnitEmitter();
5174 ue
.emitIVA(tempLocal
);
5180 void EmitterVisitor::emitPushAndFreeUnnamedL(Emitter
& e
, Id tempLocal
,
5182 assert(tempLocal
>= 0);
5183 assert(start
!= InvalidAbsoluteOffset
);
5184 newFaultRegionAndFunclet(start
, m_ue
.bcPos(),
5185 new UnsetUnnamedLocalThunklet(tempLocal
));
5186 emitVirtualLocal(tempLocal
);
5188 m_curFunc
->freeUnnamedLocal(tempLocal
);
5191 EmitterVisitor::PassByRefKind
5192 EmitterVisitor::getPassByRefKind(ExpressionPtr exp
) {
5193 auto permissiveKind
= PassByRefKind::AllowCell
;
5195 // The PassByRefKind of a list assignment expression is determined
5196 // by the PassByRefKind of the RHS. This loop will repeatedly recurse
5197 // on the RHS until it encounters an expression other than a list
5198 // assignment expression.
5199 while (exp
->is(Expression::KindOfListAssignment
)) {
5200 ListAssignmentPtr
la(static_pointer_cast
<ListAssignment
>(exp
));
5201 exp
= la
->getArray();
5202 permissiveKind
= PassByRefKind::WarnOnCell
;
5205 // this only happens for calls that have been morphed into bytecode
5206 // e.g. idx(), abs(), strlen(), etc..
5207 // It is to allow the following code to work
5208 // function f(&$arg) {...}
5209 // f(idx($array, 'key')); <- this fails otherwise
5210 if (exp
->allowCellByRef()) {
5211 return PassByRefKind::AllowCell
;
5214 switch (exp
->getKindOf()) {
5215 case Expression::KindOfNewObjectExpression
:
5216 case Expression::KindOfIncludeExpression
:
5217 case Expression::KindOfSimpleVariable
:
5218 // New and include/require
5219 return PassByRefKind::AllowCell
;
5220 case Expression::KindOfArrayElementExpression
:
5221 // Allow if bare; warn if inside list assignment
5222 return permissiveKind
;
5223 case Expression::KindOfAssignmentExpression
:
5224 // Assignment (=) and binding assignment (=&)
5225 return PassByRefKind::WarnOnCell
;
5226 case Expression::KindOfBinaryOpExpression
: {
5227 BinaryOpExpressionPtr
b(static_pointer_cast
<BinaryOpExpression
>(exp
));
5228 // Assignment op (+=, -=, *=, etc)
5229 if (b
->isAssignmentOp()) return PassByRefKind::WarnOnCell
;
5231 case Expression::KindOfUnaryOpExpression
: {
5232 UnaryOpExpressionPtr
u(static_pointer_cast
<UnaryOpExpression
>(exp
));
5233 int op
= u
->getOp();
5234 if (op
== T_CLONE
) {
5236 return PassByRefKind::AllowCell
;
5237 } else if (op
== '@' || op
== T_EVAL
||
5238 ((op
== T_INC
|| op
== T_DEC
) && u
->getFront())) {
5239 // Silence operator, eval, preincrement, and predecrement
5240 return PassByRefKind::WarnOnCell
;
5243 case Expression::KindOfExpressionList
: {
5244 ExpressionListPtr
el(static_pointer_cast
<ExpressionList
>(exp
));
5245 if (el
->getListKind() != ExpressionList::ListKindParam
) {
5246 return PassByRefKind::WarnOnCell
;
5253 return PassByRefKind::ErrorOnCell
;
5256 void EmitterVisitor::emitBuiltinCallArg(Emitter
& e
,
5261 if (checkIfStackEmpty("Builtin arg*")) return;
5270 void EmitterVisitor::emitBuiltinDefaultArg(Emitter
& e
, Variant
& v
,
5271 DataType t
, int paramId
) {
5272 switch (v
.getType()) {
5274 case KindOfStaticString
: {
5275 StringData
*nValue
= makeStaticString(v
.getStringData());
5280 e
.Int(v
.getInt64());
5283 e
.Double(v
.toDouble());
5286 if (v
.getBoolean()) {
5295 case KindOfStaticString
:
5297 case KindOfResource
:
5309 e
.Array(v
.getArrayData());
5316 void EmitterVisitor::emitFuncCallArg(Emitter
& e
,
5320 if (checkIfStackEmpty("FPass*")) return;
5322 // TODO(4599379): if dealing with an unpack, here is where we'd want to
5323 // emit a bytecode to traverse any containers;
5324 // TODO(4599368): if dealing with an unpack, would need to kick out of
5325 // the pass-by-ref behavior and defer that to FCallUnpack
5327 emitFPass(e
, paramId
, getPassByRefKind(exp
));
5330 void EmitterVisitor::emitFPass(Emitter
& e
, int paramId
,
5331 PassByRefKind passByRefKind
) {
5332 if (checkIfStackEmpty("FPass*")) return;
5333 LocationGuard
locGuard(e
, m_tempLoc
);
5336 emitClsIfSPropBase(e
);
5337 int iLast
= m_evalStack
.size()-1;
5338 int i
= scanStackForLocation(iLast
);
5341 char sym
= m_evalStack
.get(i
);
5342 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
5344 case StackSym::L
: e
.FPassL(paramId
, m_evalStack
.getLoc(i
)); break;
5346 switch (passByRefKind
) {
5347 case PassByRefKind::AllowCell
: e
.FPassC(paramId
); break;
5348 case PassByRefKind::WarnOnCell
: e
.FPassCW(paramId
); break;
5349 case PassByRefKind::ErrorOnCell
: e
.FPassCE(paramId
); break;
5350 default: assert(false);
5353 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5354 case StackSym::CN
: e
.FPassN(paramId
); break;
5355 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5356 case StackSym::CG
: e
.FPassG(paramId
); break;
5357 case StackSym::LS
: e
.CGetL2(m_evalStack
.getLoc(i
)); // fall through
5358 case StackSym::CS
: e
.FPassS(paramId
); break;
5359 case StackSym::V
: e
.FPassV(paramId
); break;
5360 case StackSym::R
: e
.FPassR(paramId
); break;
5362 unexpectedStackSym(sym
, "emitFPass");
5367 std::vector
<uchar
> vectorImm
;
5368 buildVectorImm(vectorImm
, i
, iLast
, true, e
);
5369 e
.FPassM(paramId
, vectorImm
);
5373 void EmitterVisitor::emitIsset(Emitter
& e
) {
5374 if (checkIfStackEmpty("Isset*")) return;
5376 emitClsIfSPropBase(e
);
5377 int iLast
= m_evalStack
.size()-1;
5378 int i
= scanStackForLocation(iLast
);
5381 char sym
= m_evalStack
.get(i
);
5382 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
5384 case StackSym::L
: e
.IssetL(m_evalStack
.getLoc(i
)); break;
5385 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5386 case StackSym::CN
: e
.IssetN(); break;
5387 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5388 case StackSym::CG
: e
.IssetG(); break;
5389 case StackSym::LS
: e
.CGetL2(m_evalStack
.getLoc(i
)); // fall through
5390 case StackSym::CS
: e
.IssetS(); break;
5391 //XXX: Zend does not allow isset() on the result
5392 // of a function call. We allow it here so that emitted
5393 // code is valid. Once the parser handles this correctly,
5394 // the R and C cases can go.
5395 case StackSym::R
: e
.UnboxR(); // fall through
5397 e
.IsTypeC(IsTypeOp::Null
);
5401 unexpectedStackSym(sym
, "emitIsset");
5406 std::vector
<uchar
> vectorImm
;
5407 buildVectorImm(vectorImm
, i
, iLast
, false, e
);
5408 e
.IssetM(vectorImm
);
5412 void EmitterVisitor::emitIsType(Emitter
& e
, IsTypeOp op
) {
5413 if (checkIfStackEmpty("IsType")) return;
5415 emitConvertToCellOrLoc(e
);
5416 switch (char sym
= m_evalStack
.top()) {
5418 e
.IsTypeL(m_evalStack
.getLoc(m_evalStack
.size() - 1), op
);
5424 unexpectedStackSym(sym
, "emitIsType");
5428 void EmitterVisitor::emitEmpty(Emitter
& e
) {
5429 if (checkIfStackEmpty("Empty*")) return;
5431 emitClsIfSPropBase(e
);
5432 int iLast
= m_evalStack
.size()-1;
5433 int i
= scanStackForLocation(iLast
);
5436 char sym
= m_evalStack
.get(i
);
5437 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
5439 case StackSym::L
: e
.EmptyL(m_evalStack
.getLoc(i
)); break;
5440 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5441 case StackSym::CN
: e
.EmptyN(); break;
5442 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5443 case StackSym::CG
: e
.EmptyG(); break;
5444 case StackSym::LS
: e
.CGetL2(m_evalStack
.getLoc(i
)); // fall through
5445 case StackSym::CS
: e
.EmptyS(); break;
5446 case StackSym::R
: e
.UnboxR(); // fall through
5447 case StackSym::C
: e
.Not(); break;
5449 unexpectedStackSym(sym
, "emitEmpty");
5454 std::vector
<uchar
> vectorImm
;
5455 buildVectorImm(vectorImm
, i
, iLast
, false, e
);
5456 e
.EmptyM(vectorImm
);
5460 void EmitterVisitor::emitUnset(Emitter
& e
,
5461 ExpressionPtr exp
/* = ExpressionPtr() */) {
5462 if (checkIfStackEmpty("Unset*")) return;
5464 emitClsIfSPropBase(e
);
5465 int iLast
= m_evalStack
.size()-1;
5466 int i
= scanStackForLocation(iLast
);
5469 char sym
= m_evalStack
.get(i
);
5470 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
5472 case StackSym::L
: e
.UnsetL(m_evalStack
.getLoc(i
)); break;
5473 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5474 case StackSym::CN
: e
.UnsetN(); break;
5475 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5476 case StackSym::CG
: e
.UnsetG(); break;
5477 case StackSym::LS
: // fall through
5478 case StackSym::CS
: {
5481 std::ostringstream s
;
5482 s
<< "Attempt to unset static property " << exp
->getText();
5483 emitMakeUnitFatal(e
, s
.str().c_str());
5487 unexpectedStackSym(sym
, "emitUnset");
5492 std::vector
<uchar
> vectorImm
;
5493 buildVectorImm(vectorImm
, i
, iLast
, false, e
);
5494 e
.UnsetM(vectorImm
);
5498 void EmitterVisitor::emitVisitAndUnset(Emitter
& e
, ExpressionPtr exp
) {
5503 void EmitterVisitor::emitSet(Emitter
& e
) {
5504 if (checkIfStackEmpty("Set*")) return;
5506 int iLast
= m_evalStack
.size()-2;
5507 int i
= scanStackForLocation(iLast
);
5510 char sym
= m_evalStack
.get(i
);
5511 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
5513 case StackSym::L
: e
.SetL(m_evalStack
.getLoc(i
)); break;
5514 case StackSym::LN
: emitCGetL2(e
); // fall through
5515 case StackSym::CN
: e
.SetN(); break;
5516 case StackSym::LG
: emitCGetL2(e
); // fall through
5517 case StackSym::CG
: e
.SetG(); break;
5518 case StackSym::LS
: emitCGetL3(e
); // fall through
5519 case StackSym::CS
: e
.SetS(); break;
5521 unexpectedStackSym(sym
, "emitSet");
5526 std::vector
<uchar
> vectorImm
;
5527 buildVectorImm(vectorImm
, i
, iLast
, true, e
);
5532 void EmitterVisitor::emitSetOp(Emitter
& e
, int tokenOp
) {
5533 if (checkIfStackEmpty("SetOp*")) return;
5535 auto ifIntOverflow
= [](SetOpOp trueVal
, SetOpOp falseVal
) {
5536 return RuntimeOption::IntsOverflowToInts
? trueVal
: falseVal
;
5539 auto const op
= [&] {
5542 return ifIntOverflow(SetOpOp::PlusEqual
, SetOpOp::PlusEqualO
);
5544 return ifIntOverflow(SetOpOp::MinusEqual
, SetOpOp::MinusEqualO
);
5546 return ifIntOverflow(SetOpOp::MulEqual
, SetOpOp::MulEqualO
);
5547 case T_POW_EQUAL
: return SetOpOp::PowEqual
;
5548 case T_DIV_EQUAL
: return SetOpOp::DivEqual
;
5549 case T_CONCAT_EQUAL
: return SetOpOp::ConcatEqual
;
5550 case T_MOD_EQUAL
: return SetOpOp::ModEqual
;
5551 case T_AND_EQUAL
: return SetOpOp::AndEqual
;
5552 case T_OR_EQUAL
: return SetOpOp::OrEqual
;
5553 case T_XOR_EQUAL
: return SetOpOp::XorEqual
;
5554 case T_SL_EQUAL
: return SetOpOp::SlEqual
;
5555 case T_SR_EQUAL
: return SetOpOp::SrEqual
;
5561 int iLast
= m_evalStack
.size()-2;
5562 int i
= scanStackForLocation(iLast
);
5565 char sym
= m_evalStack
.get(i
);
5566 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
5568 case StackSym::L
: e
.SetOpL(m_evalStack
.getLoc(i
), op
); break;
5569 case StackSym::LN
: emitCGetL2(e
); // fall through
5570 case StackSym::CN
: e
.SetOpN(op
); break;
5571 case StackSym::LG
: emitCGetL2(e
); // fall through
5572 case StackSym::CG
: e
.SetOpG(op
); break;
5573 case StackSym::LS
: emitCGetL3(e
); // fall through
5574 case StackSym::CS
: e
.SetOpS(op
); break;
5576 unexpectedStackSym(sym
, "emitSetOp");
5581 std::vector
<uchar
> vectorImm
;
5582 buildVectorImm(vectorImm
, i
, iLast
, true, e
);
5583 e
.SetOpM(op
, vectorImm
);
5587 void EmitterVisitor::emitBind(Emitter
& e
) {
5588 if (checkIfStackEmpty("Bind*")) return;
5590 int iLast
= m_evalStack
.size()-2;
5591 int i
= scanStackForLocation(iLast
);
5594 char sym
= m_evalStack
.get(i
);
5595 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
5597 case StackSym::L
: e
.BindL(m_evalStack
.getLoc(i
)); break;
5598 case StackSym::LN
: emitCGetL2(e
); // fall through
5599 case StackSym::CN
: e
.BindN(); break;
5600 case StackSym::LG
: emitCGetL2(e
); // fall through
5601 case StackSym::CG
: e
.BindG(); break;
5602 case StackSym::LS
: emitCGetL3(e
); // fall through
5603 case StackSym::CS
: e
.BindS(); break;
5605 unexpectedStackSym(sym
, "emitBind");
5610 std::vector
<uchar
> vectorImm
;
5611 buildVectorImm(vectorImm
, i
, iLast
, true, e
);
5616 void EmitterVisitor::emitIncDec(Emitter
& e
, IncDecOp op
) {
5617 if (checkIfStackEmpty("IncDec*")) return;
5619 emitClsIfSPropBase(e
);
5620 int iLast
= m_evalStack
.size()-1;
5621 int i
= scanStackForLocation(iLast
);
5624 char sym
= m_evalStack
.get(i
);
5625 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
5627 case StackSym::L
: e
.IncDecL(m_evalStack
.getLoc(i
), op
); break;
5628 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5629 case StackSym::CN
: e
.IncDecN(op
); break;
5630 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
5631 case StackSym::CG
: e
.IncDecG(op
); break;
5632 case StackSym::LS
: e
.CGetL2(m_evalStack
.getLoc(i
)); // fall through
5633 case StackSym::CS
: e
.IncDecS(op
); break;
5635 unexpectedStackSym(sym
, "emitIncDec");
5640 std::vector
<uchar
> vectorImm
;
5641 buildVectorImm(vectorImm
, i
, iLast
, true, e
);
5642 e
.IncDecM(op
, vectorImm
);
5646 void EmitterVisitor::emitConvertToCell(Emitter
& e
) {
5650 void EmitterVisitor::emitConvertSecondToCell(Emitter
& e
) {
5651 if (m_evalStack
.size() <= 1) {
5653 "Emitter encounted an empty evaluation stack when inside "
5654 "the emitConvertSecondToCell() function (at offset %d)",
5658 char sym
= m_evalStack
.get(m_evalStack
.size() - 2);
5659 char symFlavor
= StackSym::GetSymFlavor(sym
);
5660 if (symFlavor
== StackSym::C
) {
5662 } else if (symFlavor
== StackSym::L
) {
5665 // emitConvertSecondToCell() should never be used for symbolic flavors
5666 // other than C or L
5668 "Emitter encountered an unsupported StackSym \"%s\" on "
5669 "the evaluation stack inside the emitConvertSecondToCell()"
5670 " function (at offset %d)",
5671 StackSym::ToString(sym
).c_str(),
5676 void EmitterVisitor::emitConvertToCellIfVar(Emitter
& e
) {
5677 if (!m_evalStack
.empty()) {
5678 char sym
= m_evalStack
.top();
5679 if (sym
== StackSym::V
) {
5680 emitConvertToCell(e
);
5685 void EmitterVisitor::emitConvertToCellOrLoc(Emitter
& e
) {
5686 if (m_evalStack
.empty()) {
5688 "Emitter encounted an empty evaluation stack when inside "
5689 "the emitConvertToCellOrLoc() function (at offset %d)",
5693 char sym
= m_evalStack
.top();
5694 if (sym
== StackSym::L
) {
5695 // If the top of stack is a loc that is not marked, do nothing
5697 // Otherwise, call emitCGet to convert the top of stack to cell
5702 void EmitterVisitor::emitConvertToVar(Emitter
& e
) {
5707 * Class bases are stored on the symbolic stack in a "virtual" way so
5708 * we can resolve them later (here) in order to properly handle php
5714 * $cls::$x[0][f()] = g();
5716 * We need to evaluate f(), then resolve $cls to an A (possibly
5717 * invoking an autoload handler), then evaluate g(), then do the set.
5719 * Complex cases involve unnamed local temporaries. For example, in:
5721 * ${func()}::${f()} = g();
5723 * We'll emit code which calls func() and stashes the result in a
5724 * unnamed local. Then we call f(), then we turn the unnamed local
5725 * into an 'A' so that autoload handlers will run after f(). Then g()
5726 * is evaluated and then the set happens.
5728 void EmitterVisitor::emitResolveClsBase(Emitter
& e
, int pos
) {
5729 switch (m_evalStack
.getClsBaseType(pos
)) {
5730 case SymbolicStack::CLS_STRING_NAME
:
5731 e
.String(m_evalStack
.getName(pos
));
5734 case SymbolicStack::CLS_LATE_BOUND
:
5737 case SymbolicStack::CLS_SELF
:
5740 case SymbolicStack::CLS_PARENT
:
5743 case SymbolicStack::CLS_NAMED_LOCAL
: {
5744 int loc
= m_evalStack
.getLoc(pos
);
5745 emitVirtualLocal(loc
);
5749 case SymbolicStack::CLS_UNNAMED_LOCAL
: {
5750 int loc
= m_evalStack
.getLoc(pos
);
5751 emitVirtualLocal(loc
);
5753 emitVirtualLocal(loc
);
5755 newFaultRegionAndFunclet(m_evalStack
.getUnnamedLocStart(pos
),
5757 new UnsetUnnamedLocalThunklet(loc
));
5758 m_curFunc
->freeUnnamedLocal(loc
);
5761 case SymbolicStack::CLS_INVALID
:
5766 m_evalStack
.consumeBelowTop(m_evalStack
.size() - pos
- 1);
5769 void EmitterVisitor::emitClsIfSPropBase(Emitter
& e
) {
5770 // If the eval stack is empty, then there is no work to do
5771 if (m_evalStack
.empty()) return;
5773 // Scan past any values marked with the Elem, NewElem, or Prop markers
5774 int pos
= m_evalStack
.size() - 1;
5776 char marker
= StackSym::GetMarker(m_evalStack
.get(pos
));
5777 if (marker
!= StackSym::E
&& marker
!= StackSym::W
&&
5778 marker
!= StackSym::P
) {
5783 InvariantViolation("Emitter expected a location on the stack but none "
5784 "was found (at offset %d)",
5789 // After scanning, if we did not find a value marked with the SProp
5790 // marker then there is no work to do
5791 if (StackSym::GetMarker(m_evalStack
.get(pos
)) != StackSym::S
) {
5798 "Emitter emitted an instruction that tries to consume "
5799 "a value from the stack when the stack is empty "
5800 "(expected symbolic flavor \"C\" or \"L\" at offset %d)",
5804 emitResolveClsBase(e
, pos
);
5805 m_evalStack
.set(m_evalStack
.size() - 1,
5806 m_evalStack
.get(m_evalStack
.size() - 1) | StackSym::M
);
5809 DataType
EmitterVisitor::analyzeSwitch(SwitchStatementPtr sw
,
5810 SwitchState
& state
) {
5811 auto& caseMap
= state
.cases
;
5812 DataType t
= KindOfUninit
;
5813 StatementListPtr
cases(sw
->getCases());
5814 const int ncase
= cases
->getCount();
5816 // Bail if the cases aren't homogeneous
5817 for (int i
= 0; i
< ncase
; ++i
) {
5818 CaseStatementPtr
c(static_pointer_cast
<CaseStatement
>((*cases
)[i
]));
5819 ExpressionPtr condition
= c
->getCondition();
5823 if (condition
->getScalarValue(cval
)) {
5824 caseType
= cval
.getType();
5825 if (caseType
== KindOfStaticString
) caseType
= KindOfString
;
5826 if ((caseType
!= KindOfInt64
&& caseType
!= KindOfString
) ||
5827 !IMPLIES(t
!= KindOfUninit
, caseType
== t
)) {
5828 return KindOfInvalid
;
5832 return KindOfInvalid
;
5836 if (t
== KindOfInt64
) {
5837 n
= cval
.asInt64Val();
5840 always_assert(t
== KindOfString
);
5841 n
= m_ue
.mergeLitstr(cval
.asStrRef().get());
5842 isNonZero
= false; // not used for string switches
5844 if (!caseMap
.count(n
)) {
5845 // If 'case n:' appears multiple times, only the first will
5848 if (t
== KindOfString
) {
5849 // We have to preserve the original order of the cases for string
5850 // switches because of insane things like 0 being equal to any string
5851 // that is not a nonzero numeric string.
5852 state
.caseOrder
.push_back(StrCase(safe_cast
<Id
>(n
), i
));
5855 if (state
.nonZeroI
== -1 && isNonZero
) {
5856 // true is equal to any non-zero integer, so to preserve php's
5857 // switch semantics we have to remember the first non-zero
5858 // case to appear in the source text
5861 } else if (LIKELY(state
.defI
== -1)) {
5864 // Multiple defaults are not allowed
5865 throw IncludeTimeFatalException(
5866 c
, "Switch statements may only contain one default: clause");
5870 if (t
== KindOfInt64
) {
5871 int64_t base
= caseMap
.begin()->first
;
5872 int64_t nTargets
= caseMap
.rbegin()->first
- base
+ 1;
5873 // Fail if the cases are too sparse
5874 if ((float)caseMap
.size() / nTargets
< 0.5) {
5875 return KindOfInvalid
;
5877 } else if (t
== KindOfString
) {
5878 if (caseMap
.size() < kMinStringSwitchCases
) {
5879 return KindOfInvalid
;
5886 void EmitterVisitor::emitIntegerSwitch(Emitter
& e
, SwitchStatementPtr sw
,
5887 std::vector
<Label
>& caseLabels
,
5888 Label
& done
, const SwitchState
& state
) {
5889 auto& caseMap
= state
.cases
;
5890 int64_t base
= caseMap
.begin()->first
;
5891 int64_t nTargets
= caseMap
.rbegin()->first
- base
+ 1;
5893 // It's on. Map case values to Labels, filling in the blanks as
5895 Label
* defLabel
= state
.defI
== -1 ? &done
: &caseLabels
[state
.defI
];
5896 std::vector
<Label
*> labels(nTargets
+ 2);
5897 for (int i
= 0; i
< nTargets
; ++i
) {
5898 if (auto const caseIdx
= folly::get_ptr(caseMap
, base
+ i
)) {
5899 labels
[i
] = &caseLabels
[*caseIdx
];
5901 labels
[i
] = defLabel
;
5905 // Fill in offsets for the first non-zero case and default
5906 labels
[labels
.size() - 2] =
5907 state
.nonZeroI
== -1 ? defLabel
: &caseLabels
[state
.nonZeroI
];
5908 labels
[labels
.size() - 1] = defLabel
;
5910 visit(sw
->getExp());
5911 emitConvertToCell(e
);
5912 e
.Switch(labels
, base
, 1);
5915 void EmitterVisitor::emitStringSwitch(Emitter
& e
, SwitchStatementPtr sw
,
5916 std::vector
<Label
>& caseLabels
,
5917 Label
& done
, const SwitchState
& state
) {
5918 std::vector
<Emitter::StrOff
> labels
;
5919 for (auto& pair
: state
.caseOrder
) {
5920 labels
.push_back(Emitter::StrOff(pair
.first
, &caseLabels
[pair
.second
]));
5923 // Default case comes last
5924 Label
* defLabel
= state
.defI
== -1 ? &done
: &caseLabels
[state
.defI
];
5925 labels
.push_back(Emitter::StrOff(-1, defLabel
));
5927 visit(sw
->getExp());
5928 emitConvertToCell(e
);
5932 void EmitterVisitor::markElem(Emitter
& e
) {
5933 if (m_evalStack
.empty()) {
5934 InvariantViolation("Emitter encountered an empty evaluation stack inside"
5935 " the markElem function (at offset %d)",
5939 char sym
= m_evalStack
.top();
5940 if (sym
== StackSym::C
|| sym
== StackSym::L
|| sym
== StackSym::T
||
5941 sym
== StackSym::I
) {
5942 m_evalStack
.set(m_evalStack
.size()-1, (sym
| StackSym::E
));
5945 "Emitter encountered an unsupported StackSym \"%s\" on "
5946 "the evaluation stack inside the markElem function (at "
5948 StackSym::ToString(sym
).c_str(),
5953 void EmitterVisitor::markNewElem(Emitter
& e
) {
5954 m_evalStack
.push(StackSym::W
);
5957 void EmitterVisitor::markProp(Emitter
& e
) {
5958 if (m_evalStack
.empty()) {
5960 "Emitter encountered an empty evaluation stack inside "
5961 "the markProp function (at offset %d)",
5965 char sym
= m_evalStack
.top();
5966 if (sym
== StackSym::C
|| sym
== StackSym::L
|| sym
== StackSym::T
) {
5967 m_evalStack
.set(m_evalStack
.size()-1, (sym
| StackSym::P
));
5970 "Emitter encountered an unsupported StackSym \"%s\" on "
5971 "the evaluation stack inside the markProp function (at "
5973 StackSym::ToString(sym
).c_str(),
5978 void EmitterVisitor::markSProp(Emitter
& e
) {
5979 if (m_evalStack
.empty()) {
5981 "Emitter encountered an empty evaluation stack inside "
5982 "the markSProp function (at offset %d)",
5986 char sym
= m_evalStack
.top();
5987 if (sym
== StackSym::C
|| sym
== StackSym::L
) {
5988 m_evalStack
.set(m_evalStack
.size()-1, (sym
| StackSym::S
));
5991 "Emitter encountered an unsupported StackSym \"%s\" on "
5992 "the evaluation stack inside the markSProp function "
5994 StackSym::ToString(sym
).c_str(),
5999 #define MARK_NAME_BODY(index, requiredStackSize) \
6000 if (m_evalStack.size() < requiredStackSize) { \
6001 InvariantViolation( \
6002 "Emitter encountered an evaluation stack with %d " \
6003 "elements inside the %s function (at offset %d)", \
6004 m_evalStack.size(), __FUNCTION__, m_ue.bcPos()); \
6007 char sym = m_evalStack.get(index); \
6008 if (sym == StackSym::C || sym == StackSym::L) { \
6009 m_evalStack.set(index, (sym | StackSym::N)); \
6011 InvariantViolation( \
6012 "Emitter encountered an unsupported StackSym \"%s\" " \
6013 "on the evaluation stack inside the %s function (at " \
6015 StackSym::ToString(sym).c_str(), __FUNCTION__, \
6019 void EmitterVisitor::markName(Emitter
& e
) {
6020 int index
= m_evalStack
.size() - 1;
6021 MARK_NAME_BODY(index
, 1);
6024 void EmitterVisitor::markNameSecond(Emitter
& e
) {
6025 int index
= m_evalStack
.size() - 2;
6026 MARK_NAME_BODY(index
, 2);
6029 #undef MARK_NAME_BODY
6031 void EmitterVisitor::markGlobalName(Emitter
& e
) {
6032 if (m_evalStack
.empty()) {
6034 "Emitter encountered an empty evaluation stack inside "
6035 "the markGlobalName function (at offset %d)",
6039 char sym
= m_evalStack
.top();
6040 if (sym
== StackSym::C
|| sym
== StackSym::L
) {
6041 m_evalStack
.set(m_evalStack
.size()-1, (sym
| StackSym::G
));
6044 "Emitter encountered an unsupported StackSym \"%s\" on "
6045 "the evaluation stack inside the markGlobalName function "
6047 StackSym::ToString(sym
).c_str(),
6052 void EmitterVisitor::emitNameString(Emitter
& e
, ExpressionPtr n
,
6053 bool allowLiteral
) {
6055 if (n
->getScalarValue(v
) && v
.isString()) {
6056 StringData
* nLiteral
= makeStaticString(v
.toCStrRef().get());
6058 m_evalStack
.push(StackSym::T
);
6062 m_evalStack
.setString(nLiteral
);
6065 emitConvertToCellOrLoc(e
);
6069 void EmitterVisitor::postponeMeth(MethodStatementPtr m
, FuncEmitter
* fe
,
6071 ClosureUseVarVec
* useVars
/* = NULL */) {
6072 m_postponedMeths
.push_back(PostponedMeth(m
, fe
, top
, useVars
));
6075 void EmitterVisitor::postponeCtor(InterfaceStatementPtr is
, FuncEmitter
* fe
) {
6076 m_postponedCtors
.push_back(PostponedCtor(is
, fe
));
6079 void EmitterVisitor::postponePinit(InterfaceStatementPtr is
, FuncEmitter
* fe
,
6081 m_postponedPinits
.push_back(PostponedNonScalars(is
, fe
, v
));
6084 void EmitterVisitor::postponeSinit(InterfaceStatementPtr is
, FuncEmitter
* fe
,
6086 m_postponedSinits
.push_back(PostponedNonScalars(is
, fe
, v
));
6089 void EmitterVisitor::postponeCinit(InterfaceStatementPtr is
, FuncEmitter
* fe
,
6091 m_postponedCinits
.push_back(PostponedNonScalars(is
, fe
, v
));
6094 static Attr
buildAttrs(ModifierExpressionPtr mod
, bool isRef
= false) {
6095 int attrs
= AttrNone
;
6097 attrs
|= AttrReference
;
6100 attrs
|= mod
->isPublic() ? AttrPublic
:
6101 mod
->isPrivate() ? AttrPrivate
:
6102 mod
->isProtected() ? AttrProtected
: AttrNone
;
6103 if (mod
->isStatic()) {
6104 attrs
|= AttrStatic
;
6106 if (mod
->isAbstract()) {
6107 attrs
|= AttrAbstract
;
6109 if (mod
->isFinal()) {
6117 * <<__HipHopSpecific>> user attribute marks funcs/methods as HipHop specific
6119 * <<__IsFoldable>> Function has no side-effects and may be called at
6120 * compile time with constant input to get deterministic output.
6123 s_HipHopSpecific("__HipHopSpecific"),
6124 s_IsFoldable("__IsFoldable"),
6125 s_ParamCoerceModeNull("__ParamCoerceModeNull"),
6126 s_ParamCoerceModeFalse("__ParamCoerceModeFalse");
6128 static void parseUserAttributes(FuncEmitter
* fe
, Attr
& attrs
) {
6129 if (fe
->userAttributes
.count(s_HipHopSpecific
.get())) {
6130 attrs
= attrs
| AttrHPHPSpecific
;
6132 if (fe
->userAttributes
.count(s_IsFoldable
.get())) {
6133 attrs
= attrs
| AttrIsFoldable
;
6135 if (fe
->userAttributes
.count(s_ParamCoerceModeNull
.get())) {
6136 attrs
= attrs
| AttrParamCoerceModeNull
;
6137 } else if (fe
->userAttributes
.count(s_ParamCoerceModeFalse
.get())) {
6138 attrs
= attrs
| AttrParamCoerceModeFalse
;
6142 static Attr
buildMethodAttrs(MethodStatementPtr meth
, FuncEmitter
* fe
,
6143 bool top
, bool allowOverride
) {
6144 FunctionScopePtr funcScope
= meth
->getFunctionScope();
6145 ModifierExpressionPtr
mod(meth
->getModifiers());
6146 Attr attrs
= buildAttrs(mod
, meth
->isRef());
6148 if (allowOverride
) {
6149 attrs
= attrs
| AttrAllowOverride
;
6152 // if hasCallToGetArgs() or if mayUseVV
6153 if (meth
->hasCallToGetArgs() || funcScope
->mayUseVV()) {
6154 attrs
= attrs
| AttrMayUseVV
;
6157 auto fullName
= meth
->getOriginalFullName();
6158 auto it
= Option::FunctionSections
.find(fullName
);
6159 if ((it
!= Option::FunctionSections
.end() && it
->second
== "hot") ||
6160 (RuntimeOption::EvalRandomHotFuncs
&&
6161 (hash_string_i(fullName
.c_str()) & 8))) {
6162 attrs
= attrs
| AttrHot
;
6165 if (Option::WholeProgram
) {
6166 if (!funcScope
->isRedeclaring()) {
6167 attrs
= attrs
| AttrUnique
;
6169 (!funcScope
->isVolatile() ||
6170 funcScope
->isPersistent())) {
6171 attrs
= attrs
| AttrPersistent
;
6174 if (meth
->getClassScope() && !funcScope
->hasOverride()) {
6175 attrs
= attrs
| AttrNoOverride
;
6177 if (funcScope
->isSystem()) {
6178 assert((attrs
& AttrPersistent
) || meth
->getClassScope());
6179 attrs
= attrs
| AttrBuiltin
;
6181 } else if (!SystemLib::s_inited
) {
6182 // we're building systemlib. everything is unique
6183 attrs
= attrs
| AttrBuiltin
| AttrUnique
| AttrPersistent
;
6186 // For closures, the MethodStatement didn't have real attributes; enforce
6187 // that the __invoke method is public here
6188 if (fe
->isClosureBody
) {
6189 assert(!(attrs
& (AttrProtected
| AttrPrivate
)));
6190 attrs
= attrs
| AttrPublic
;
6193 // Coerce memoized methods to private. This is needed for code that uses
6194 // parent:: to call through to the correct underlying function
6195 if (meth
->is(Statement::KindOfMethodStatement
) && fe
->isMemoizeImpl
) {
6196 attrs
= static_cast<Attr
>(attrs
& ~(AttrPublic
| AttrProtected
));
6197 attrs
= attrs
| AttrPrivate
;
6200 parseUserAttributes(fe
, attrs
);
6201 // Not supported except in __Native functions
6202 attrs
= static_cast<Attr
>(
6203 attrs
& ~(AttrParamCoerceModeNull
| AttrParamCoerceModeFalse
));
6209 * The code below is used for both, function/method parameter type as well as
6210 * for function/method return type.
6212 static TypeConstraint
6213 determine_type_constraint_from_annot(const TypeAnnotationPtr annot
,
6216 auto flags
= TypeConstraint::ExtendedHint
| TypeConstraint::HHType
;
6218 // We only care about a subset of extended type constaints:
6219 // typevar, nullable, soft, return types.
6221 // For everything else, we return {}. We also return {} for annotations
6222 // we don't know how to handle.
6223 if (annot
->isFunction() || annot
->isMixed()) {
6226 if (annot
->isTypeVar()) {
6227 flags
= flags
| TypeConstraint::TypeVar
;
6229 if (annot
->isNullable()) {
6230 flags
= flags
| TypeConstraint::Nullable
;
6232 if (annot
->isSoft()) {
6233 flags
= flags
| TypeConstraint::Soft
;
6236 (flags
== (TypeConstraint::ExtendedHint
| TypeConstraint::HHType
))) {
6240 auto strippedName
= annot
->stripNullable().stripSoft().vanillaName();
6242 return TypeConstraint
{
6243 makeStaticString(strippedName
),
6251 static TypeConstraint
6252 determine_type_constraint(const ParameterExpressionPtr
& par
) {
6253 if (par
->hasTypeHint()) {
6254 auto ce
= dynamic_pointer_cast
<ConstantExpression
>(par
->defaultValue());
6255 auto flags
= TypeConstraint::NoFlags
;
6256 if (ce
&& ce
->isNull()) {
6257 flags
= flags
|TypeConstraint::Nullable
;
6259 if (par
->hhType()) {
6260 flags
= flags
|TypeConstraint::HHType
;
6262 return TypeConstraint
{
6263 makeStaticString(par
->getOriginalTypeHint()),
6268 return determine_type_constraint_from_annot(par
->annotation(), false);
6271 void EmitterVisitor::emitPostponedMeths() {
6272 vector
<FuncEmitter
*> top_fes
;
6273 while (!m_postponedMeths
.empty()) {
6274 assert(m_actualStackHighWater
== 0);
6275 assert(m_fdescHighWater
== 0);
6276 PostponedMeth
& p
= m_postponedMeths
.front();
6277 MethodStatementPtr meth
= p
.m_meth
;
6278 FuncEmitter
* fe
= p
.m_fe
;
6282 const StringData
* methName
= makeStaticString(meth
->getOriginalName());
6283 fe
= new FuncEmitter(m_ue
, -1, -1, methName
);
6284 auto oldFunc
= m_topMethodEmitted
.find(meth
->getOriginalName());
6285 if (oldFunc
!= m_topMethodEmitted
.end()) {
6286 throw IncludeTimeFatalException(
6288 "Cannot redeclare %s() (previously declared in %s:%d)",
6289 meth
->getOriginalName().c_str(),
6290 oldFunc
->second
->ue().m_filepath
->data(),
6291 oldFunc
->second
->getLocation().second
);
6293 m_topMethodEmitted
.emplace(meth
->getOriginalName(), fe
);
6296 top_fes
.push_back(fe
);
6299 auto funcScope
= meth
->getFunctionScope();
6301 fe
->isAsync
= funcScope
->isAsync();
6302 fe
->isGenerator
= funcScope
->isGenerator();
6304 if (funcScope
->userAttributes().count("__Memoize")) {
6305 auto const originalName
= fe
->name
;
6306 auto const rewrittenName
= makeStaticString(
6307 folly::sformat("{}$memoize_impl", fe
->name
->data()));
6308 FuncEmitter
* memoizeFe
= nullptr;
6309 auto const propName
= makeStaticString(
6310 folly::sformat("{}$memoize_cache", fe
->name
->data()));
6312 if (meth
->is(Statement::KindOfFunctionStatement
)) {
6314 throw IncludeTimeFatalException(meth
,
6315 "<<__Memoize>> cannot be applied to closures and inline functions");
6318 memoizeFe
= new FuncEmitter(m_ue
, -1, -1, originalName
);
6319 fe
->name
= rewrittenName
;
6320 top_fes
.push_back(memoizeFe
);
6322 PreClassEmitter
*pce
= fe
->pce();
6324 // Add a property to hold the memoized results
6326 if (!meth
->getParams() || meth
->getParams()->getCount() == 0) {
6327 tvWriteNull(&tvProp
);
6329 tvProp
= make_tv
<KindOfArray
>(staticEmptyArray());
6331 Attr attrs
= AttrPrivate
;
6332 attrs
= attrs
| (funcScope
->isStatic() ? AttrStatic
: AttrNone
);
6333 pce
->addProperty(propName
, attrs
, nullptr, nullptr,
6334 &tvProp
, RepoAuthType
{});
6336 // Rename the method and create a new method with the original name
6337 pce
->renameMethod(originalName
, rewrittenName
);
6338 memoizeFe
= m_ue
.newMethodEmitter(originalName
, pce
);
6339 bool added UNUSED
= pce
->addMethod(memoizeFe
);
6343 // Emit the new method that handles the memoization
6344 m_curFunc
= memoizeFe
;
6345 m_curFunc
->isMemoizeWrapper
= true;
6346 emitMethodMetadata(meth
, p
.m_closureUseVars
, p
.m_top
);
6347 emitMemoizeMethod(meth
, rewrittenName
, propName
);
6349 // Switch back to the original method and mark it as a memoize
6352 m_curFunc
->isMemoizeImpl
= true;
6355 if (funcScope
->isNative()) {
6356 bindNativeFunc(meth
, fe
);
6358 emitMethodMetadata(meth
, p
.m_closureUseVars
, p
.m_top
);
6362 if (fe
->isClosureBody
) {
6364 tvWriteUninit(&uninit
);
6365 for (auto& sv
: m_curFunc
->staticVars
) {
6366 auto const str
= makeStaticString(
6367 folly::format("86static_{}", sv
.name
->data()).str());
6368 fe
->pce()->addProperty(str
, AttrPrivate
, nullptr, nullptr,
6369 &uninit
, RepoAuthType
{});
6373 delete p
.m_closureUseVars
;
6374 m_postponedMeths
.pop_front();
6377 for (size_t i
= 0; i
< top_fes
.size(); i
++) {
6378 m_ue
.appendTopEmitter(top_fes
[i
]);
6382 void EmitterVisitor::bindUserAttributes(MethodStatementPtr meth
,
6384 bool &allowOverride
) {
6385 auto const& userAttrs
= meth
->getFunctionScope()->userAttributes();
6386 for (auto& attr
: userAttrs
) {
6387 if (attr
.first
== "__Overridable") {
6388 allowOverride
= true;
6391 const StringData
* uaName
= makeStaticString(attr
.first
);
6392 ExpressionPtr uaValue
= attr
.second
;
6394 assert(uaValue
->isScalar());
6396 initScalar(tv
, uaValue
);
6397 fe
->userAttributes
[uaName
] = tv
;
6401 void EmitterVisitor::bindNativeFunc(MethodStatementPtr meth
,
6403 if (SystemLib::s_inited
&&
6404 !(Option::WholeProgram
&& meth
->isSystem())) {
6405 throw IncludeTimeFatalException(meth
,
6406 "Native functions/methods may only be defined in systemlib");
6409 auto modifiers
= meth
->getModifiers();
6410 bool allowOverride
= false;
6411 bindUserAttributes(meth
, fe
, allowOverride
);
6413 Attr attributes
= AttrBuiltin
| AttrNative
| AttrUnique
| AttrPersistent
;
6414 if (meth
->isRef()) {
6415 attributes
= attributes
| AttrReference
;
6417 auto pce
= fe
->pce();
6419 if (modifiers
->isStatic()) {
6420 attributes
= attributes
| AttrStatic
;
6422 if (modifiers
->isFinal()) {
6423 attributes
= attributes
| AttrFinal
;
6425 if (modifiers
->isAbstract()) {
6426 attributes
= attributes
| AttrAbstract
;
6428 if (modifiers
->isPrivate()) {
6429 attributes
= attributes
| AttrPrivate
;
6431 attributes
= attributes
| (modifiers
->isProtected()
6432 ? AttrProtected
: AttrPublic
);
6435 if (allowOverride
) {
6436 attributes
= attributes
| AttrAllowOverride
;
6439 parseUserAttributes(fe
, attributes
);
6440 if (!(attributes
& (AttrParamCoerceModeFalse
| AttrParamCoerceModeNull
))) {
6441 attributes
= attributes
| AttrParamCoerceModeNull
;
6444 const Location
* sLoc
= meth
->getLocation().get();
6445 fe
->setLocation(sLoc
->line0
, sLoc
->line1
);
6446 fe
->docComment
= makeStaticString(
6447 Option::GenerateDocComments
? meth
->getDocComment().c_str() : ""
6449 fe
->returnType
= meth
->retTypeAnnotation()->dataType();
6450 fe
->retUserType
= makeStaticString(meth
->getReturnTypeConstraint());
6452 FunctionScopePtr funcScope
= meth
->getFunctionScope();
6453 const char *funcname
= funcScope
->getName().c_str();
6454 const char *classname
= pce
? pce
->name()->data() : nullptr;
6455 BuiltinFunction nif
= Native::GetBuiltinFunction(funcname
, classname
,
6456 modifiers
->isStatic());
6457 BuiltinFunction bif
;
6459 bif
= Native::unimplementedWrapper
;
6461 int nativeAttrs
= fe
->parseNativeAttributes(attributes
);
6462 if (nativeAttrs
& Native::AttrZendCompat
) {
6463 bif
= zend_wrap_func
;
6465 if (nativeAttrs
& Native::AttrActRec
) {
6466 // Call this native function with a raw ActRec*
6467 // rather than pulling out args for normal func calling
6471 bool usesDouble
= false, variadic
= funcScope
->hasVariadicParam();
6472 auto params
= meth
->getParams();
6473 int numParams
= params
? params
->getCount() : 0;
6474 // Ignore looking at the variadic capture param's type
6475 // Since as far as the ABI is concerned, it's always array
6476 if (variadic
) numParams
--;
6477 assert(numParams
>= 0);
6479 // Does this method take doubles as arguments?
6480 for (int i
= 0; i
< numParams
; ++i
) {
6481 ParameterExpressionPtr
par(
6482 static_pointer_cast
<ParameterExpression
>((*params
)[i
]));
6483 auto const typeAnnotation
= par
->annotation();
6484 if (typeAnnotation
&& typeAnnotation
->dataType() == KindOfDouble
) {
6489 bif
= Native::getWrapper(pce
, usesDouble
, variadic
);
6493 Emitter
e(meth
, m_ue
, *this);
6496 Offset base
= m_ue
.bcPos();
6497 fe
->setBuiltinFunc(bif
, nif
, attributes
, base
);
6498 fillFuncEmitterParams(fe
, meth
->getParams(), true);
6500 FuncFinisher
ff(this, e
, fe
);
6501 emitMethodDVInitializers(e
, meth
, topOfBody
);
6504 void EmitterVisitor::emitMethodMetadata(MethodStatementPtr meth
,
6505 ClosureUseVarVec
* useVars
,
6507 FuncEmitter
* fe
= m_curFunc
;
6508 bool allowOverride
= false;
6509 bindUserAttributes(meth
, fe
, allowOverride
);
6511 // assign ids to parameters (all methods)
6512 int numParam
= meth
->getParams() ? meth
->getParams()->getCount() : 0;
6513 for (int i
= 0; i
< numParam
; i
++) {
6514 ParameterExpressionPtr
par(
6515 static_pointer_cast
<ParameterExpression
>((*meth
->getParams())[i
]));
6516 fe
->allocVarId(makeStaticString(par
->getName()));
6519 // assign ids to 0Closure and use parameters (closures)
6520 if (fe
->isClosureBody
) {
6521 fe
->allocVarId(makeStaticString("0Closure"));
6523 for (auto& useVar
: *useVars
) {
6524 fe
->allocVarId(useVar
.first
);
6528 // assign ids to local variables
6529 if (!fe
->isMemoizeWrapper
) {
6530 assignLocalVariableIds(meth
->getFunctionScope());
6533 // add parameter info
6534 fillFuncEmitterParams(fe
, meth
->getParams(),
6535 meth
->getFunctionScope()->isParamCoerceMode());
6537 // copy declared return type (hack)
6538 fe
->retUserType
= makeStaticString(meth
->getReturnTypeConstraint());
6540 auto annot
= meth
->retTypeAnnotation();
6541 // Ideally we should handle the void case in TypeConstraint::check. This
6542 // should however get done in a different diff, since it could impact
6543 // perf in a negative way (#3145038)
6544 if (annot
&& !annot
->isVoid() && !annot
->isThis()) {
6545 fe
->retTypeConstraint
= determine_type_constraint_from_annot(annot
, true);
6548 // add the original filename for flattened traits
6549 auto const originalFilename
= meth
->getOriginalFilename();
6550 if (!originalFilename
.empty()) {
6551 fe
->originalFilename
= makeStaticString(originalFilename
);
6554 const Location
* sLoc
= meth
->getLocation().get();
6555 StringData
* methDoc
= Option::GenerateDocComments
?
6556 makeStaticString(meth
->getDocComment()) : staticEmptyString();
6558 fe
->init(sLoc
->line0
,
6561 buildMethodAttrs(meth
, fe
, top
, allowOverride
),
6565 if (meth
->getFunctionScope()->needsFinallyLocals()) {
6566 assignFinallyVariableIds();
6570 void EmitterVisitor::fillFuncEmitterParams(FuncEmitter
* fe
,
6571 ExpressionListPtr params
,
6572 bool coerce_params
/*= false */) {
6573 int numParam
= params
? params
->getCount() : 0;
6574 for (int i
= 0; i
< numParam
; i
++) {
6575 ParameterExpressionPtr
par(
6576 static_pointer_cast
<ParameterExpression
>((*params
)[i
]));
6577 StringData
* parName
= makeStaticString(par
->getName());
6579 FuncEmitter::ParamInfo pi
;
6580 auto const typeConstraint
= determine_type_constraint(par
);
6581 if (typeConstraint
.hasConstraint()) {
6582 pi
.typeConstraint
= typeConstraint
;
6584 if (coerce_params
) {
6585 if (auto const typeAnnotation
= par
->annotation()) {
6586 pi
.builtinType
= typeAnnotation
->dataType();
6590 if (par
->hasUserType()) {
6591 pi
.userType
= makeStaticString(par
->getUserTypeHint());
6594 // Store info about the default value if there is one.
6595 if (par
->isOptional()) {
6596 const StringData
* phpCode
;
6597 ExpressionPtr vNode
= par
->defaultValue();
6598 if (vNode
->isScalar()) {
6600 initScalar(dv
, vNode
);
6601 pi
.defaultValue
= dv
;
6603 std::string orig
= vNode
->getComment();
6605 // Simple case: it's a scalar value so we just serialize it
6606 VariableSerializer
vs(VariableSerializer::Type::PHPOutput
);
6607 String result
= vs
.serialize(tvAsCVarRef(&dv
), true);
6608 phpCode
= makeStaticString(result
.get());
6610 // This was optimized from a Constant, or ClassConstant
6611 // use the original string
6612 phpCode
= makeStaticString(orig
);
6615 // Non-scalar, so we have to output PHP from the AST node
6616 std::ostringstream os
;
6617 CodeGenerator
cg(&os
, CodeGenerator::PickledPHP
);
6618 AnalysisResultPtr
ar(new AnalysisResult());
6619 vNode
->outputPHP(cg
, ar
);
6620 phpCode
= makeStaticString(os
.str());
6622 pi
.phpCode
= phpCode
;
6625 ExpressionListPtr paramUserAttrs
=
6626 dynamic_pointer_cast
<ExpressionList
>(par
->userAttributeList());
6627 if (paramUserAttrs
) {
6628 for (int j
= 0; j
< paramUserAttrs
->getCount(); ++j
) {
6629 UserAttributePtr a
= dynamic_pointer_cast
<UserAttribute
>(
6630 (*paramUserAttrs
)[j
]);
6631 StringData
* uaName
= makeStaticString(a
->getName());
6632 ExpressionPtr uaValue
= a
->getExp();
6634 assert(uaValue
->isScalar());
6636 initScalar(tv
, uaValue
);
6637 pi
.userAttributes
[uaName
] = tv
;
6641 pi
.byRef
= par
->isRef();
6642 pi
.variadic
= par
->isVariadic();
6643 fe
->appendParam(parName
, pi
);
6647 void EmitterVisitor::emitMethodPrologue(Emitter
& e
, MethodStatementPtr meth
) {
6648 FunctionScopePtr funcScope
= meth
->getFunctionScope();
6650 if (!m_curFunc
->isMemoizeWrapper
&&
6651 funcScope
->needsLocalThis() && !funcScope
->isStatic()) {
6652 assert(!m_curFunc
->top
);
6653 static const StringData
* thisStr
= makeStaticString("this");
6654 Id thisId
= m_curFunc
->lookupVarId(thisStr
);
6655 emitVirtualLocal(thisId
);
6656 e
.InitThisLoc(thisId
);
6659 if (!m_curFunc
->isMemoizeImpl
) {
6660 for (uint i
= 0; i
< m_curFunc
->params
.size(); i
++) {
6661 const TypeConstraint
& tc
= m_curFunc
->params
[i
].typeConstraint
;
6662 if (!tc
.hasConstraint()) continue;
6663 emitVirtualLocal(i
);
6664 e
.VerifyParamType(i
);
6668 if (funcScope
->isAbstract()) {
6669 std::ostringstream s
;
6670 s
<< "Cannot call abstract method " << meth
->getOriginalFullName() << "()";
6671 emitMakeUnitFatal(e
, s
.str().c_str(), FatalOp::RuntimeOmitFrame
);
6675 void EmitterVisitor::emitMethod(MethodStatementPtr meth
) {
6676 auto region
= createRegion(meth
, Region::Kind::FuncBody
);
6677 enterRegion(region
);
6678 SCOPE_EXIT
{ leaveRegion(region
); };
6680 Emitter
e(meth
, m_ue
, *this);
6682 emitMethodPrologue(e
, meth
);
6684 // emit code to create generator object
6685 if (m_curFunc
->isGenerator
) {
6691 visit(meth
->getStmts());
6692 assert(m_evalStack
.size() == 0);
6694 // if the current position is reachable, emit code to return null
6695 if (currentPositionIsReachable()) {
6696 LocationPtr
loc(new Location(*meth
->getLocation().get()));
6697 loc
->line0
= loc
->line1
;
6698 loc
->char0
= loc
->char1
-1;
6699 e
.setTempLocation(loc
);
6702 e
.setTempLocation(LocationPtr());
6705 FuncFinisher
ff(this, e
, m_curFunc
);
6706 if (!m_curFunc
->isMemoizeImpl
) {
6707 emitMethodDVInitializers(e
, meth
, topOfBody
);
6711 void EmitterVisitor::emitMethodDVInitializers(Emitter
& e
,
6712 MethodStatementPtr
& meth
,
6714 bool hasOptional
= false;
6715 ExpressionListPtr params
= meth
->getParams();
6716 int numParam
= params
? params
->getCount() : 0;
6717 for (int i
= 0; i
< numParam
; i
++) {
6718 ParameterExpressionPtr
par(
6719 static_pointer_cast
<ParameterExpression
>((*params
)[i
]));
6720 if (par
->isOptional()) {
6722 Label
entryPoint(e
);
6723 emitVirtualLocal(i
);
6724 visit(par
->defaultValue());
6728 m_curFunc
->params
[i
].funcletOff
= entryPoint
.getAbsoluteOffset();
6731 if (hasOptional
) e
.JmpNS(topOfBody
);
6734 void EmitterVisitor::emitMemoizeProp(Emitter
& e
,
6735 MethodStatementPtr meth
,
6736 const StringData
* propName
,
6738 const std::vector
<Id
>& paramIDs
,
6740 if (meth
->is(Statement::KindOfFunctionStatement
)) {
6741 emitVirtualLocal(localID
);
6742 } else if (meth
->getFunctionScope()->isStatic()) {
6743 m_evalStack
.push(StackSym::K
);
6744 m_evalStack
.setClsBaseType(SymbolicStack::CLS_SELF
);
6748 m_evalStack
.push(StackSym::H
);
6749 m_evalStack
.setKnownCls(m_curFunc
->pce()->name(), false);
6750 m_evalStack
.push(StackSym::T
);
6751 m_evalStack
.setString(propName
);
6755 assert(numParams
<= paramIDs
.size());
6756 for (uint i
= 0; i
< numParams
; i
++) {
6757 emitVirtualLocal(paramIDs
[i
]);
6762 void EmitterVisitor::emitMemoizeMethod(MethodStatementPtr meth
,
6763 const StringData
* methName
,
6764 const StringData
* propName
) {
6765 if (meth
->getFunctionScope()->isRefReturn()) {
6766 throw IncludeTimeFatalException(meth
,
6767 "<<__Memoize>> cannot be used on functions that return by reference");
6769 if (meth
->getFunctionScope()->allowsVariableArguments()) {
6770 throw IncludeTimeFatalException(meth
,
6771 "<<__Memoize>> cannot be used on functions with variable arguments");
6774 bool isFunc
= meth
->is(Statement::KindOfFunctionStatement
);
6775 int numParams
= m_curFunc
->params
.size();
6776 int staticLocalID
= 0;
6778 auto region
= createRegion(meth
, Region::Kind::FuncBody
);
6779 enterRegion(region
);
6780 SCOPE_EXIT
{ leaveRegion(region
); };
6782 Emitter
e(meth
, m_ue
, *this);
6786 emitMethodPrologue(e
, meth
);
6790 // static ${propName} = {numParams > 0 ? array() : null};
6791 staticLocalID
= m_curFunc
->allocUnnamedLocal();
6792 emitVirtualLocal(staticLocalID
);
6793 if (numParams
== 0) {
6796 e
.Array(staticEmptyArray());
6798 e
.StaticLocInit(staticLocalID
, propName
);
6799 } else if (!meth
->getFunctionScope()->isStatic()) {
6803 std::vector
<Id
> paramNumToLocalID(numParams
);
6805 if (numParams
== 0) {
6806 // if (${propName} !== null)
6807 emitMemoizeProp(e
, meth
, propName
, staticLocalID
, paramNumToLocalID
, 0);
6808 emitIsType(e
, IsTypeOp::Null
);
6811 auto serMethName
= makeStaticString("HH\\serialize_memoize_param");
6813 // Serialize all the params into something we can use for the key
6814 for (int i
= 0; i
< numParams
; i
++) {
6815 if (m_curFunc
->params
[i
].byRef
) {
6816 throw IncludeTimeFatalException(meth
,
6817 "<<__Memoize>> cannot be used on functions with args passed by "
6821 const TypeConstraint
&tc
= m_curFunc
->params
[i
].typeConstraint
;
6823 tc
.underlyingDataType() == DataType::KindOfBoolean
||
6824 tc
.underlyingDataType() == DataType::KindOfInt64
||
6825 (tc
.underlyingDataType() == DataType::KindOfString
&& !tc
.isNullable());
6826 blessedType
&= tc
.hasConstraint() && tc
.isPrecise() && !tc
.isSoft();
6829 paramNumToLocalID
[i
] = i
;
6833 // We don't know statically that the param is useable as a key. So:
6834 // 1: Check if it's an int. If so, use it directly
6835 // 2: Otherwise, call the serialization function
6836 // 3: In either case, store the result in a new local
6837 Label notInt
, storeLocal
;
6838 paramNumToLocalID
[i
] = m_curFunc
->allocUnnamedLocal();
6840 // $valN = is_int($paramN) ?
6841 // $paramN : HH\serialize_memoize_param($paramN);
6842 emitVirtualLocal(paramNumToLocalID
[i
]);
6843 emitVirtualLocal(i
);
6844 emitIsType(e
, IsTypeOp::Int
);
6847 emitVirtualLocal(i
);
6852 auto fpiStart
= m_ue
.bcPos();
6853 e
.FPushFuncD(1, serMethName
);
6855 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
6856 emitVirtualLocal(i
);
6857 emitFPass(e
, 0, PassByRefKind::ErrorOnCell
);
6860 emitConvertToCell(e
);
6867 // isset returns false for null values. Given that:
6868 // - If we know that we can't return null, we can do a single isset check
6869 // - If we could return null, but there's only one arg, we can do a single
6870 // array_key_exists() check
6871 // - Otherwise we need an isset check to make sure we can dereference the
6872 // first N - 1 args, and then an array_key_exists() check
6874 m_curFunc
->retTypeConstraint
.hasConstraint() &&
6875 !m_curFunc
->retTypeConstraint
.isSoft() &&
6876 !m_curFunc
->retTypeConstraint
.isNullable();
6878 if (numParams
> 1 || noRetNull
) {
6879 // if (isset(${propName}[$param1]...[noRetNull ? $paramN : $paramN-1]))
6880 emitMemoizeProp(e
, meth
, propName
, staticLocalID
, paramNumToLocalID
,
6881 noRetNull
? numParams
: numParams
- 1);
6887 // if (array_key_exists($paramN, ${propName}[$param1][...][$paramN-1]))
6888 emitVirtualLocal(paramNumToLocalID
[numParams
- 1]);
6890 emitMemoizeProp(e
, meth
, propName
, staticLocalID
, paramNumToLocalID
,
6898 // return $<propName>[$param1][...][$paramN]
6899 emitMemoizeProp(e
, meth
, propName
, staticLocalID
, paramNumToLocalID
,
6904 // Otherwise, call the memoized func, store the result, and return it
6906 emitMemoizeProp(e
, meth
, propName
, staticLocalID
, paramNumToLocalID
,
6908 auto fpiStart
= m_ue
.bcPos();
6910 e
.FPushFuncD(numParams
, methName
);
6911 } else if (meth
->getFunctionScope()->isStatic()) {
6912 emitClsIfSPropBase(e
);
6913 e
.FPushClsMethodD(numParams
, methName
, m_curFunc
->pce()->name());
6916 fpiStart
= m_ue
.bcPos();
6917 e
.FPushObjMethodD(numParams
, methName
, ObjMethodOp::NullThrows
);
6920 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
6921 for (uint i
= 0; i
< m_curFunc
->params
.size(); i
++) {
6922 emitVirtualLocal(i
);
6923 emitFPass(e
, i
, PassByRefKind::ErrorOnCell
);
6927 emitConvertToCell(e
);
6932 assert(m_evalStack
.size() == 0);
6934 FuncFinisher
ff(this, e
, m_curFunc
);
6935 emitMethodDVInitializers(e
, meth
, topOfBody
);
6938 void EmitterVisitor::emitPostponedCtors() {
6939 while (!m_postponedCtors
.empty()) {
6940 PostponedCtor
& p
= m_postponedCtors
.front();
6942 Attr attrs
= AttrPublic
;
6943 if (!SystemLib::s_inited
|| p
.m_is
->getClassScope()->isSystem()) {
6944 attrs
= attrs
| AttrBuiltin
;
6946 StringData
* methDoc
= staticEmptyString();
6947 const Location
* sLoc
= p
.m_is
->getLocation().get();
6948 p
.m_fe
->init(sLoc
->line0
, sLoc
->line1
, m_ue
.bcPos(), attrs
, false, methDoc
);
6949 Emitter
e(p
.m_is
, m_ue
, *this);
6950 FuncFinisher
ff(this, e
, p
.m_fe
);
6954 m_postponedCtors
.pop_front();
6958 void EmitterVisitor::emitPostponedPSinit(PostponedNonScalars
& p
, bool pinit
) {
6959 Attr attrs
= (Attr
)(AttrPrivate
| AttrStatic
);
6960 if (!SystemLib::s_inited
|| p
.m_is
->getClassScope()->isSystem()) {
6961 attrs
= attrs
| AttrBuiltin
;
6963 StringData
* methDoc
= staticEmptyString();
6964 const Location
* sLoc
= p
.m_is
->getLocation().get();
6965 p
.m_fe
->init(sLoc
->line0
, sLoc
->line1
, m_ue
.bcPos(), attrs
, false, methDoc
);
6967 Emitter
e(p
.m_is
, m_ue
, *this);
6968 FuncFinisher
ff(this, e
, p
.m_fe
);
6970 // Private instance and static properties are initialized using
6972 size_t nProps
= p
.m_vec
->size();
6974 for (size_t i
= 0; i
< nProps
; ++i
) {
6975 const StringData
* propName
=
6976 makeStaticString(((*p
.m_vec
)[i
]).first
);
6979 InitPropOp op
= InitPropOp::NonStatic
;
6980 const PreClassEmitter::Prop
& preProp
=
6981 p
.m_fe
->pce()->lookupProp(propName
);
6982 if ((preProp
.attrs() & AttrStatic
) == AttrStatic
) {
6983 op
= InitPropOp::Static
;
6984 } else if ((preProp
.attrs() & (AttrPrivate
|AttrStatic
)) != AttrPrivate
) {
6985 e
.CheckProp(const_cast<StringData
*>(propName
));
6988 visit((*p
.m_vec
)[i
].second
);
6989 e
.InitProp(const_cast<StringData
*>(propName
), op
);
6996 void EmitterVisitor::emitPostponedPinits() {
6997 while (!m_postponedPinits
.empty()) {
6998 PostponedNonScalars
& p
= m_postponedPinits
.front();
6999 emitPostponedPSinit(p
, true);
7000 p
.release(); // Manually trigger memory cleanup.
7001 m_postponedPinits
.pop_front();
7005 void EmitterVisitor::emitPostponedSinits() {
7006 while (!m_postponedSinits
.empty()) {
7007 PostponedNonScalars
& p
= m_postponedSinits
.front();
7008 emitPostponedPSinit(p
, false);
7009 p
.release(); // Manually trigger memory cleanup.
7010 m_postponedSinits
.pop_front();
7014 void EmitterVisitor::emitPostponedCinits() {
7015 while (!m_postponedCinits
.empty()) {
7016 PostponedNonScalars
& p
= m_postponedCinits
.front();
7018 Attr attrs
= (Attr
)(AttrPrivate
| AttrStatic
);
7019 if (!SystemLib::s_inited
|| p
.m_is
->getClassScope()->isSystem()) {
7020 attrs
= attrs
| AttrBuiltin
;
7022 StringData
* methDoc
= staticEmptyString();
7023 const Location
* sLoc
= p
.m_is
->getLocation().get();
7024 p
.m_fe
->init(sLoc
->line0
, sLoc
->line1
, m_ue
.bcPos(), attrs
, false, methDoc
);
7025 static const StringData
* s_constName
= makeStaticString("constName");
7026 p
.m_fe
->appendParam(s_constName
, FuncEmitter::ParamInfo());
7028 Emitter
e(p
.m_is
, m_ue
, *this);
7029 FuncFinisher
ff(this, e
, p
.m_fe
);
7031 // Generate HHBC of the structure:
7033 // private static function 86cinit(constName) {
7034 // if (constName == "FOO") {
7035 // return <expr for FOO>;
7036 // } else if (constName == "BAR") {
7037 // return <expr for BAR>;
7038 // } else { # (constName == "BAZ")
7039 // return <expr for BAZ>;
7042 size_t nConsts
= p
.m_vec
->size();
7043 assert(nConsts
> 0);
7045 for (size_t i
= 0; i
< nConsts
- 1; ++i
) {
7048 emitVirtualLocal(0);
7050 e
.String((StringData
*)makeStaticString(((*p
.m_vec
)[i
]).first
));
7054 visit((*p
.m_vec
)[i
].second
);
7059 visit((*p
.m_vec
)[nConsts
-1].second
);
7063 p
.release(); // Manually trigger memory cleanup.
7064 m_postponedCinits
.pop_front();
7068 void EmitterVisitor::emitVirtualLocal(int localId
) {
7071 m_evalStack
.push(StackSym::L
);
7072 m_evalStack
.setInt(localId
);
7075 static bool isNormalLocalVariable(const ExpressionPtr
& expr
) {
7076 SimpleVariable
* sv
= static_cast<SimpleVariable
*>(expr
.get());
7077 return (expr
->is(Expression::KindOfSimpleVariable
) &&
7078 !sv
->isSuperGlobal() &&
7082 template<class Expr
>
7083 void EmitterVisitor::emitVirtualClassBase(Emitter
& e
, Expr
* node
) {
7086 m_evalStack
.push(StackSym::K
);
7087 auto const func
= node
->getOriginalFunction();
7089 if (node
->isStatic()) {
7090 m_evalStack
.setClsBaseType(SymbolicStack::CLS_LATE_BOUND
);
7091 } else if (node
->getClass()) {
7092 const ExpressionPtr
& expr
= node
->getClass();
7093 if (isNormalLocalVariable(expr
)) {
7094 SimpleVariable
* sv
= static_cast<SimpleVariable
*>(expr
.get());
7095 StringData
* name
= makeStaticString(sv
->getName());
7096 Id locId
= m_curFunc
->lookupVarId(name
);
7097 m_evalStack
.setClsBaseType(SymbolicStack::CLS_NAMED_LOCAL
);
7098 m_evalStack
.setInt(locId
);
7101 * More complex expressions get stashed into an unnamed local so
7102 * we can evaluate them at the proper time.
7104 * See emitResolveClsBase() for examples.
7106 int unnamedLoc
= m_curFunc
->allocUnnamedLocal();
7107 int clsBaseIdx
= m_evalStack
.size() - 1;
7108 m_evalStack
.setClsBaseType(SymbolicStack::CLS_UNNAMED_LOCAL
);
7109 emitVirtualLocal(unnamedLoc
);
7110 visit(node
->getClass());
7111 emitConvertToCell(e
);
7113 m_evalStack
.setUnnamedLocal(clsBaseIdx
, unnamedLoc
, m_ue
.bcPos());
7116 } else if (!node
->getOriginalClass() ||
7117 node
->getOriginalClass()->isTrait() ||
7118 (func
&& func
->isClosure())) {
7119 // In a trait, a potentially rebound closure or psuedo-main, we can't
7120 // resolve self:: or parent:: yet, so we emit special instructions that do
7122 if (node
->isParent()) {
7123 m_evalStack
.setClsBaseType(SymbolicStack::CLS_PARENT
);
7124 } else if (node
->isSelf()) {
7125 m_evalStack
.setClsBaseType(SymbolicStack::CLS_SELF
);
7127 m_evalStack
.setClsBaseType(SymbolicStack::CLS_STRING_NAME
);
7128 m_evalStack
.setString(
7129 makeStaticString(node
->getOriginalClassName()));
7131 } else if (node
->isParent() &&
7132 node
->getOriginalClass()->getOriginalParent().empty()) {
7133 // parent:: in a class without a parent. We'll emit a Parent
7134 // opcode because it can handle this error case.
7135 m_evalStack
.setClsBaseType(SymbolicStack::CLS_PARENT
);
7137 m_evalStack
.setClsBaseType(SymbolicStack::CLS_STRING_NAME
);
7138 m_evalStack
.setString(
7139 makeStaticString(node
->getOriginalClassName()));
7143 bool EmitterVisitor::emitSystemLibVarEnvFunc(Emitter
& e
,
7144 SimpleFunctionCallPtr call
) {
7145 if (call
->isCallToFunction("extract")) {
7146 emitFuncCall(e
, call
,
7147 "__SystemLib\\extract", call
->getParams());
7149 } else if (call
->isCallToFunction("parse_str")) {
7150 emitFuncCall(e
, call
, "__SystemLib\\parse_str", call
->getParams());
7152 } else if (call
->isCallToFunction("compact")) {
7153 emitFuncCall(e
, call
,
7154 "__SystemLib\\compact_sl", call
->getParams());
7156 } else if (call
->isCallToFunction("get_defined_vars")) {
7157 emitFuncCall(e
, call
,
7158 "__SystemLib\\get_defined_vars", call
->getParams());
7160 } else if (call
->isCallToFunction("func_get_args")) {
7161 emitFuncCall(e
, call
,
7162 "__SystemLib\\func_get_args_sl", call
->getParams());
7164 } else if (call
->isCallToFunction("func_get_arg")) {
7165 emitFuncCall(e
, call
,
7166 "__SystemLib\\func_get_arg_sl", call
->getParams());
7168 } else if (call
->isCallToFunction("func_num_args")) {
7169 emitFuncCall(e
, call
,
7170 "__SystemLib\\func_num_arg_", call
->getParams());
7176 bool EmitterVisitor::emitCallUserFunc(Emitter
& e
, SimpleFunctionCallPtr func
) {
7179 int minParams
, maxParams
;
7180 CallUserFuncFlags flags
;
7182 { "call_user_func", 1, INT_MAX
, CallUserFuncPlain
},
7183 { "call_user_func_array", 2, 2, CallUserFuncArray
},
7184 { "forward_static_call", 1, INT_MAX
, CallUserFuncForward
},
7185 { "forward_static_call_array", 2, 2, CallUserFuncForwardArray
},
7186 { "fb_call_user_func_safe", 1, INT_MAX
, CallUserFuncSafe
},
7187 { "fb_call_user_func_array_safe", 2, 2, CallUserFuncSafeArray
},
7188 { "fb_call_user_func_safe_return", 2, INT_MAX
, CallUserFuncSafeReturn
},
7191 ExpressionListPtr params
= func
->getParams();
7192 if (!params
) return false;
7193 int nParams
= params
->getCount();
7194 if (!nParams
) return false;
7195 CallUserFuncFlags flags
= CallUserFuncNone
;
7196 for (unsigned i
= 0; i
< sizeof(cufTab
) / sizeof(cufTab
[0]); i
++) {
7197 if (func
->isCallToFunction(cufTab
[i
].name
) &&
7198 nParams
>= cufTab
[i
].minParams
&&
7199 nParams
<= cufTab
[i
].maxParams
) {
7200 flags
= cufTab
[i
].flags
;
7204 if (flags
== CallUserFuncNone
) return false;
7205 if (func
->hasUnpack()) {
7206 throw EmitterVisitor::IncludeTimeFatalException(
7208 "Using argument unpacking for a call_user_func is not supported"
7212 ExpressionPtr callable
= (*params
)[0];
7214 emitConvertToCell(e
);
7215 Offset fpiStart
= m_ue
.bcPos();
7216 if (flags
& CallUserFuncForward
) {
7217 e
.FPushCufF(nParams
- param
);
7218 } else if (flags
& CallUserFuncSafe
) {
7219 if (flags
& CallUserFuncReturn
) {
7220 assert(nParams
>= 2);
7221 visit((*params
)[param
++]);
7222 emitConvertToCell(e
);
7226 fpiStart
= m_ue
.bcPos();
7227 e
.FPushCufSafe(nParams
- param
);
7229 e
.FPushCuf(nParams
- param
);
7233 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
7234 for (int i
= param
; i
< nParams
; i
++) {
7235 visit((*params
)[i
]);
7236 emitConvertToCell(e
);
7237 e
.FPassC(i
- param
);
7241 if (flags
& CallUserFuncArray
) {
7244 e
.FCall(nParams
- param
);
7246 if (flags
& CallUserFuncSafe
) {
7247 if (flags
& CallUserFuncReturn
) {
7256 Func
* EmitterVisitor::canEmitBuiltinCall(const std::string
& name
,
7258 if (Option::JitEnableRenameFunction
) {
7261 if (Option::DynamicInvokeFunctions
.size()) {
7262 if (Option::DynamicInvokeFunctions
.find(name
) !=
7263 Option::DynamicInvokeFunctions
.end()) {
7267 Func
* f
= Unit::lookupFunc(makeStaticString(name
));
7269 (f
->attrs() & AttrNoFCallBuiltin
) ||
7270 !f
->nativeFuncPtr() ||
7272 (f
->numParams() > Native::maxFCallBuiltinArgs()) ||
7273 (numParams
> f
->numParams()) ||
7274 f
->hasVariadicCaptureParam()) return nullptr;
7276 if ((f
->returnType() == KindOfDouble
) &&
7277 !Native::allowFCallBuiltinDoubles()) return nullptr;
7279 if (f
->methInfo()) {
7280 // IDL style builtin
7281 const ClassInfo::MethodInfo
* info
= f
->methInfo();
7282 if (info
->attribute
& (ClassInfo::NoFCallBuiltin
|
7283 ClassInfo::VariableArguments
|
7284 ClassInfo::RefVariableArguments
|
7285 ClassInfo::MixedVariableArguments
)) {
7288 } else if (!(f
->attrs() & AttrNative
)) {
7289 // HNI only enables Variable args via ActRec which in turn
7290 // is captured by the f->nativeFuncPtr() == nullptr,
7291 // so there's nothing additional to check in the HNI case
7295 bool allowDoubleArgs
= Native::allowFCallBuiltinDoubles();
7296 for (int i
= 0; i
< f
->numParams(); i
++) {
7297 if ((!allowDoubleArgs
) &&
7298 (f
->params()[i
].builtinType
== KindOfDouble
)) {
7301 if (i
>= numParams
) {
7302 if (f
->methInfo()) {
7304 auto pi
= f
->methInfo()->parameters
[i
];
7305 if (!pi
->valueLen
) {
7308 // unserializable default values such as TimeStamp::Current()
7309 // are serialized as kUnserializableString ("\x01")
7310 if (!strcmp(f
->methInfo()->parameters
[i
]->value
,
7311 kUnserializableString
)) return nullptr;
7314 auto &pi
= f
->params()[i
];
7315 if (!pi
.hasDefaultValue()) {
7318 if (pi
.defaultValue
.m_type
== KindOfUninit
) {
7319 // TODO: Resolve persistent constants
7329 void EmitterVisitor::emitFuncCall(Emitter
& e
, FunctionCallPtr node
,
7330 const char* nameOverride
,
7331 ExpressionListPtr paramsOverride
) {
7332 ExpressionPtr nameExp
= node
->getNameExp();
7333 const std::string
& nameStr
= nameOverride
? nameOverride
:
7334 node
->getOriginalName();
7335 ExpressionListPtr
params(paramsOverride
? paramsOverride
:
7337 int numParams
= params
? params
->getCount() : 0;
7338 auto const unpack
= node
->hasUnpack();
7339 assert(!paramsOverride
|| !unpack
);
7341 Func
* fcallBuiltin
= nullptr;
7342 StringData
* nLiteral
= nullptr;
7343 Offset fpiStart
= 0;
7344 if (node
->getClass() || !node
->getClassName().empty()) {
7345 bool isSelfOrParent
= node
->isSelf() || node
->isParent();
7346 if (!node
->isStatic() && !isSelfOrParent
&&
7347 !node
->getOriginalClassName().empty() && !nameStr
.empty()) {
7349 StringData
* cLiteral
=
7350 makeStaticString(node
->getOriginalClassName());
7351 StringData
* nLiteral
= makeStaticString(nameStr
);
7352 fpiStart
= m_ue
.bcPos();
7353 e
.FPushClsMethodD(numParams
, nLiteral
, cLiteral
);
7355 emitVirtualClassBase(e
, node
.get());
7356 if (!nameStr
.empty()) {
7358 StringData
* nLiteral
= makeStaticString(nameStr
);
7363 emitConvertToCell(e
);
7365 emitResolveClsBase(e
, m_evalStack
.size() - 2);
7366 fpiStart
= m_ue
.bcPos();
7367 if (isSelfOrParent
) {
7368 // self and parent are "forwarding" calls, so we need to
7369 // use FPushClsMethodF instead
7370 e
.FPushClsMethodF(numParams
);
7372 e
.FPushClsMethod(numParams
);
7375 } else if (!nameStr
.empty()) {
7377 nLiteral
= makeStaticString(nameStr
);
7379 fcallBuiltin
= canEmitBuiltinCall(nameStr
, numParams
);
7381 if (fcallBuiltin
&& (fcallBuiltin
->attrs() & AttrAllowOverride
)) {
7382 if (!Option::WholeProgram
||
7383 (node
->getFuncScope() && node
->getFuncScope()->isUserFunction())) {
7384 // In non-WholeProgram mode, we can't tell whether the function
7385 // will be overridden, so never use FCallBuiltin.
7386 // In WholeProgram mode, don't use FCallBuiltin if it *has* been
7388 fcallBuiltin
= nullptr;
7391 StringData
* nsName
= nullptr;
7392 if (!node
->hadBackslash() && !nameOverride
) {
7393 // nameOverride is to be used only when there's an exact function
7394 // to be called ... supporting a fallback doesn't make sense
7395 const std::string
& nonNSName
= node
->getNonNSOriginalName();
7396 if (nonNSName
!= nameStr
) {
7398 nLiteral
= makeStaticString(nonNSName
);
7399 fcallBuiltin
= nullptr;
7403 if (!fcallBuiltin
) {
7404 fpiStart
= m_ue
.bcPos();
7405 if (nsName
== nullptr) {
7406 e
.FPushFuncD(numParams
, nLiteral
);
7408 assert(!nameOverride
);
7409 e
.FPushFuncU(numParams
, nsName
, nLiteral
);
7415 emitConvertToCell(e
);
7416 // FPushFunc consumes method name from stack
7417 fpiStart
= m_ue
.bcPos();
7418 e
.FPushFunc(numParams
);
7421 assert(numParams
<= fcallBuiltin
->numParams());
7423 for (; i
< numParams
; i
++) {
7424 // for builtin calls, since we don't push the ActRec, we
7425 // must determine the reffiness statically
7426 bool byRef
= fcallBuiltin
->byRef(i
);
7427 emitBuiltinCallArg(e
, (*params
)[i
], i
, byRef
);
7429 if (fcallBuiltin
->methInfo()) {
7431 for (; i
< fcallBuiltin
->numParams(); i
++) {
7432 const ClassInfo::ParameterInfo
* pi
=
7433 fcallBuiltin
->methInfo()->parameters
[i
];
7434 Variant v
= unserialize_from_string(
7435 String(pi
->value
, pi
->valueLen
, CopyString
));
7436 emitBuiltinDefaultArg(e
, v
, pi
->argType
, i
);
7440 for (; i
< fcallBuiltin
->numParams(); i
++) {
7441 auto &pi
= fcallBuiltin
->params()[i
];
7442 assert(pi
.hasDefaultValue());
7443 auto &def
= pi
.defaultValue
;
7444 emitBuiltinDefaultArg(e
, tvAsVariant(const_cast<TypedValue
*>(&def
)),
7448 e
.FCallBuiltin(fcallBuiltin
->numParams(), numParams
, nLiteral
);
7451 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
7452 for (int i
= 0; i
< numParams
; i
++) {
7453 emitFuncCallArg(e
, (*params
)[i
], i
);
7457 e
.FCallUnpack(numParams
);
7462 if (Option::WholeProgram
|| fcallBuiltin
) {
7463 fixReturnType(e
, node
, fcallBuiltin
);
7467 void EmitterVisitor::emitClassTraitPrecRule(PreClassEmitter
* pce
,
7468 TraitPrecStatementPtr stmt
) {
7469 StringData
* traitName
= makeStaticString(stmt
->getTraitName());
7470 StringData
* methodName
= makeStaticString(stmt
->getMethodName());
7472 PreClass::TraitPrecRule
rule(traitName
, methodName
);
7474 std::set
<std::string
> otherTraitNames
;
7475 stmt
->getOtherTraitNames(otherTraitNames
);
7476 for (std::set
<std::string
>::iterator it
= otherTraitNames
.begin();
7477 it
!= otherTraitNames
.end(); it
++) {
7478 rule
.addOtherTraitName(makeStaticString(*it
));
7481 pce
->addTraitPrecRule(rule
);
7484 void EmitterVisitor::emitClassTraitAliasRule(PreClassEmitter
* pce
,
7485 TraitAliasStatementPtr stmt
) {
7486 StringData
* traitName
= makeStaticString(stmt
->getTraitName());
7487 StringData
* origMethName
= makeStaticString(stmt
->getMethodName());
7488 StringData
* newMethName
= makeStaticString(stmt
->getNewMethodName());
7489 // If there are no modifiers, buildAttrs() defaults to AttrPublic.
7490 // Here we don't want that. Instead, set AttrNone so that the modifiers of the
7491 // original method are preserved.
7492 Attr attr
= (stmt
->getModifiers()->getCount() == 0 ? AttrNone
:
7493 buildAttrs(stmt
->getModifiers()));
7495 PreClass::TraitAliasRule
rule(traitName
, origMethName
, newMethName
, attr
);
7497 pce
->addTraitAliasRule(rule
);
7500 void EmitterVisitor::emitClassUseTrait(PreClassEmitter
* pce
,
7501 UseTraitStatementPtr useStmt
) {
7502 StatementListPtr rules
= useStmt
->getStmts();
7503 for (int r
= 0; r
< rules
->getCount(); r
++) {
7504 StatementPtr rule
= (*rules
)[r
];
7505 TraitPrecStatementPtr precStmt
=
7506 dynamic_pointer_cast
<TraitPrecStatement
>(rule
);
7508 emitClassTraitPrecRule(pce
, precStmt
);
7510 TraitAliasStatementPtr aliasStmt
=
7511 dynamic_pointer_cast
<TraitAliasStatement
>(rule
);
7513 emitClassTraitAliasRule(pce
, aliasStmt
);
7518 void EmitterVisitor::emitTypedef(Emitter
& e
, TypedefStatementPtr td
) {
7519 auto const nullable
= td
->annot
->isNullable();
7520 auto const valueStr
= td
->annot
->stripNullable().vanillaName();
7522 td
->annot
->stripNullable().isFunction() ? KindOfAny
:
7523 td
->annot
->stripNullable().isMixed() ? KindOfAny
:
7524 !strcasecmp(valueStr
.c_str(), "array") ? KindOfArray
:
7525 !strcasecmp(valueStr
.c_str(), "HH\\int") ? KindOfInt64
:
7526 !strcasecmp(valueStr
.c_str(), "HH\\bool") ? KindOfBoolean
:
7527 !strcasecmp(valueStr
.c_str(), "HH\\string") ? KindOfString
:
7528 !strcasecmp(valueStr
.c_str(), "HH\\float") ? KindOfDouble
:
7531 // We have to merge the strings as litstrs to ensure namedentity
7533 auto const name
= makeStaticString(td
->name
);
7534 auto const value
= makeStaticString(valueStr
);
7535 m_ue
.mergeLitstr(name
);
7536 m_ue
.mergeLitstr(value
);
7540 record
.value
= value
;
7542 record
.nullable
= nullable
;
7543 record
.attrs
= AttrNone
;
7544 Id id
= m_ue
.addTypeAlias(record
);
7548 void EmitterVisitor::emitClass(Emitter
& e
,
7549 ClassScopePtr cNode
,
7552 const StringData
* fatal_msg
= cNode
->getFatalMessage();
7553 if (fatal_msg
!= nullptr) {
7554 e
.String(fatal_msg
);
7555 e
.Fatal(FatalOp::Runtime
);
7559 InterfaceStatementPtr
is(
7560 static_pointer_cast
<InterfaceStatement
>(cNode
->getStmt()));
7561 StringData
* className
= makeStaticString(cNode
->getOriginalName());
7562 StringData
* parentName
= makeStaticString(cNode
->getOriginalParent());
7563 StringData
* classDoc
= Option::GenerateDocComments
?
7564 makeStaticString(cNode
->getDocComment()) : staticEmptyString();
7565 Attr attr
= cNode
->isInterface() ? AttrInterface
:
7566 cNode
->isTrait() ? AttrTrait
:
7567 cNode
->isAbstract() ? AttrAbstract
:
7568 cNode
->isFinal() ? AttrFinal
:
7569 cNode
->isEnum() ? AttrEnum
:
7571 if (Option::WholeProgram
) {
7572 if (!cNode
->isRedeclaring() &&
7573 cNode
->derivesFromRedeclaring() == Derivation::Normal
) {
7574 attr
= attr
| AttrUnique
;
7575 if (!cNode
->isVolatile()) {
7576 attr
= attr
| AttrPersistent
;
7579 if (cNode
->isSystem()) {
7580 assert(attr
& AttrPersistent
);
7581 attr
= attr
| AttrBuiltin
;
7583 if (!cNode
->getAttribute(ClassScope::NotFinal
)) {
7584 attr
= attr
| AttrNoOverride
;
7586 if (cNode
->getUsedTraitNames().size()) {
7587 attr
= attr
| AttrNoExpandTrait
;
7589 } else if (!SystemLib::s_inited
) {
7590 // we're building systemlib. everything is unique
7591 attr
= attr
| AttrBuiltin
| AttrUnique
| AttrPersistent
;
7594 const Location
* sLoc
= is
->getLocation().get();
7595 const std::vector
<std::string
>& bases(cNode
->getBases());
7596 int firstInterface
= cNode
->getOriginalParent().empty() ? 0 : 1;
7597 int nInterfaces
= bases
.size();
7598 PreClass::Hoistable hoistable
= PreClass::NotHoistable
;
7600 if (SystemLib::s_inited
&& !cNode
->isSystem()) {
7601 if (nInterfaces
> firstInterface
7602 || cNode
->getUsedTraitNames().size()
7603 || cNode
->getClassRequiredExtends().size()
7604 || cNode
->getClassRequiredImplements().size()
7607 hoistable
= PreClass::Mergeable
;
7608 } else if (firstInterface
&&
7609 !m_hoistables
.count(cNode
->getOriginalParent())) {
7610 hoistable
= PreClass::MaybeHoistable
;
7613 if (hoistable
== PreClass::NotHoistable
) {
7614 hoistable
= attr
& AttrUnique
?
7615 PreClass::AlwaysHoistable
: PreClass::MaybeHoistable
;
7616 m_hoistables
.insert(cNode
->getOriginalName());
7619 PreClassEmitter
* pce
= m_ue
.newPreClassEmitter(className
, hoistable
);
7620 pce
->init(sLoc
->line0
, sLoc
->line1
, m_ue
.bcPos(), attr
, parentName
,
7622 LocationPtr
loc(new Location(*sLoc
));
7623 loc
->line1
= loc
->line0
;
7624 loc
->char1
= loc
->char0
;
7625 e
.setTempLocation(loc
);
7626 if (hoistable
!= PreClass::AlwaysHoistable
) {
7627 e
.DefCls(pce
->id());
7629 // To attach the line number to for error reporting.
7630 e
.DefClsNop(pce
->id());
7632 e
.setTempLocation(LocationPtr());
7633 for (int i
= firstInterface
; i
< nInterfaces
; ++i
) {
7634 pce
->addInterface(makeStaticString(bases
[i
]));
7637 const std::vector
<std::string
>& usedTraits
= cNode
->getUsedTraitNames();
7638 for (size_t i
= 0; i
< usedTraits
.size(); i
++) {
7639 pce
->addUsedTrait(makeStaticString(usedTraits
[i
]));
7641 if (cNode
->isTrait() || cNode
->isInterface()) {
7642 for (auto& reqExtends
: cNode
->getClassRequiredExtends()) {
7643 pce
->addClassRequirement(
7644 PreClass::ClassRequirement(makeStaticString(reqExtends
), true));
7646 for (auto& reqImplements
: cNode
->getClassRequiredImplements()) {
7647 pce
->addClassRequirement(
7648 PreClass::ClassRequirement(makeStaticString(reqImplements
), false));
7651 auto const& userAttrs
= cNode
->userAttributes();
7652 for (auto it
= userAttrs
.begin(); it
!= userAttrs
.end(); ++it
) {
7653 const StringData
* uaName
= makeStaticString(it
->first
);
7654 ExpressionPtr uaValue
= it
->second
;
7656 assert(uaValue
->isScalar());
7658 initScalar(tv
, uaValue
);
7659 pce
->addUserAttribute(uaName
, tv
);
7662 NonScalarVec
* nonScalarPinitVec
= nullptr;
7663 NonScalarVec
* nonScalarSinitVec
= nullptr;
7664 NonScalarVec
* nonScalarConstVec
= nullptr;
7665 if (StatementListPtr stmts
= is
->getStmts()) {
7666 int i
, n
= stmts
->getCount();
7667 for (i
= 0; i
< n
; i
++) {
7668 if (MethodStatementPtr meth
=
7669 dynamic_pointer_cast
<MethodStatement
>((*stmts
)[i
])) {
7670 StringData
* methName
= makeStaticString(meth
->getOriginalName());
7671 FuncEmitter
* fe
= m_ue
.newMethodEmitter(methName
, pce
);
7672 bool added UNUSED
= pce
->addMethod(fe
);
7674 postponeMeth(meth
, fe
, false);
7675 } else if (ClassVariablePtr cv
=
7676 dynamic_pointer_cast
<ClassVariable
>((*stmts
)[i
])) {
7677 ModifierExpressionPtr
mod(cv
->getModifiers());
7678 ExpressionListPtr
el(cv
->getVarList());
7679 Attr declAttrs
= buildAttrs(mod
);
7680 StringData
* typeConstraint
= makeStaticString(
7681 cv
->getTypeConstraint());
7682 int nVars
= el
->getCount();
7683 for (int ii
= 0; ii
< nVars
; ii
++) {
7684 ExpressionPtr
exp((*el
)[ii
]);
7685 ExpressionPtr vNode
;
7686 SimpleVariablePtr var
;
7687 if (exp
->is(Expression::KindOfAssignmentExpression
)) {
7688 AssignmentExpressionPtr
ae(
7689 static_pointer_cast
<AssignmentExpression
>(exp
));
7690 var
= static_pointer_cast
<SimpleVariable
>(ae
->getVariable());
7691 vNode
= ae
->getValue();
7693 var
= static_pointer_cast
<SimpleVariable
>(exp
);
7696 auto sym
= var
->getSymbol();
7697 bool maybePersistent
= Option::WholeProgram
&&
7698 pce
->attrs() & AttrPersistent
&&
7699 sym
&& !sym
->isIndirectAltered() && sym
->isStatic();
7701 auto const propName
= makeStaticString(var
->getName());
7702 auto const propDoc
= Option::GenerateDocComments
?
7703 makeStaticString(var
->getDocComment()) : staticEmptyString();
7705 // Some properties may need to be marked with the AttrDeepInit
7706 // attribute, while other properties should not be marked with
7707 // this attrbiute. We copy declAttrs into propAttrs for each loop
7708 // iteration so that we can safely add AttrDeepInit to propAttrs
7709 // without mutating the original declAttrs.
7710 Attr propAttrs
= declAttrs
;
7712 if (vNode
->isScalar()) {
7713 initScalar(tvVal
, vNode
);
7715 maybePersistent
= false;
7716 tvWriteUninit(&tvVal
);
7717 if (!(declAttrs
& AttrStatic
)) {
7718 if (requiresDeepInit(vNode
)) {
7719 propAttrs
= propAttrs
| AttrDeepInit
;
7721 if (nonScalarPinitVec
== nullptr) {
7722 nonScalarPinitVec
= new NonScalarVec();
7724 nonScalarPinitVec
->push_back(NonScalarPair(propName
, vNode
));
7726 if (nonScalarSinitVec
== nullptr) {
7727 nonScalarSinitVec
= new NonScalarVec();
7729 nonScalarSinitVec
->push_back(NonScalarPair(propName
, vNode
));
7733 tvWriteNull(&tvVal
);
7735 if (maybePersistent
) propAttrs
= propAttrs
| AttrPersistent
;
7737 pce
->addProperty(propName
, propAttrs
, typeConstraint
,
7738 propDoc
, &tvVal
, RepoAuthType
{});
7741 } else if (ClassConstantPtr cc
=
7742 dynamic_pointer_cast
<ClassConstant
>((*stmts
)[i
])) {
7743 ExpressionListPtr
el(cc
->getConList());
7744 StringData
* typeConstraint
= makeStaticString(
7745 cc
->getTypeConstraint());
7746 int nCons
= el
->getCount();
7747 for (int ii
= 0; ii
< nCons
; ii
++) {
7748 AssignmentExpressionPtr
ae(
7749 static_pointer_cast
<AssignmentExpression
>((*el
)[ii
]));
7750 ConstantExpressionPtr
con(
7751 static_pointer_cast
<ConstantExpression
>(ae
->getVariable()));
7752 ExpressionPtr
vNode(ae
->getValue());
7753 StringData
* constName
= makeStaticString(con
->getName());
7756 if (vNode
->isArray()) {
7757 throw IncludeTimeFatalException(
7758 cc
, "Arrays are not allowed in class constants");
7759 } else if (vNode
->isCollection()) {
7760 throw IncludeTimeFatalException(
7761 cc
, "Collections are not allowed in class constants");
7762 } else if (vNode
->isScalar()) {
7763 initScalar(tvVal
, vNode
);
7765 tvWriteUninit(&tvVal
);
7766 if (nonScalarConstVec
== nullptr) {
7767 nonScalarConstVec
= new NonScalarVec();
7769 nonScalarConstVec
->push_back(NonScalarPair(constName
, vNode
));
7771 // Store PHP source code for constant initializer.
7772 std::ostringstream os
;
7773 CodeGenerator
cg(&os
, CodeGenerator::PickledPHP
);
7774 AnalysisResultPtr
ar(new AnalysisResult());
7775 vNode
->outputPHP(cg
, ar
);
7776 bool added UNUSED
= pce
->addConstant(
7777 constName
, typeConstraint
, &tvVal
,
7778 makeStaticString(os
.str()));
7781 } else if (UseTraitStatementPtr useStmt
=
7782 dynamic_pointer_cast
<UseTraitStatement
>((*stmts
)[i
])) {
7783 emitClassUseTrait(pce
, useStmt
);
7788 if (!cNode
->getAttribute(ClassScope::HasConstructor
) &&
7789 !cNode
->getAttribute(ClassScope::ClassNameConstructor
)) {
7790 // cNode does not have a constructor; synthesize 86ctor() so that the class
7791 // will always have a method that can be called during construction.
7792 static const StringData
* methName
= makeStaticString("86ctor");
7793 FuncEmitter
* fe
= m_ue
.newMethodEmitter(methName
, pce
);
7794 bool added UNUSED
= pce
->addMethod(fe
);
7796 postponeCtor(is
, fe
);
7799 if (nonScalarPinitVec
!= nullptr) {
7800 // Non-scalar property initializers require 86pinit() for run-time
7801 // initialization support.
7802 static const StringData
* methName
= makeStaticString("86pinit");
7803 FuncEmitter
* fe
= m_ue
.newMethodEmitter(methName
, pce
);
7805 postponePinit(is
, fe
, nonScalarPinitVec
);
7808 if (nonScalarSinitVec
!= nullptr) {
7809 // Non-scalar property initializers require 86sinit() for run-time
7810 // initialization support.
7811 static const StringData
* methName
= makeStaticString("86sinit");
7812 FuncEmitter
* fe
= m_ue
.newMethodEmitter(methName
, pce
);
7814 postponeSinit(is
, fe
, nonScalarSinitVec
);
7817 if (nonScalarConstVec
!= nullptr) {
7818 // Non-scalar constant initializers require 86cinit() for run-time
7819 // initialization support.
7820 static const StringData
* methName
= makeStaticString("86cinit");
7821 FuncEmitter
* fe
= m_ue
.newMethodEmitter(methName
, pce
);
7822 assert(!(attr
& AttrTrait
));
7823 bool added UNUSED
= pce
->addMethod(fe
);
7825 postponeCinit(is
, fe
, nonScalarConstVec
);
7828 // If this is an enum, get its type constraint.
7829 if (cNode
->isEnum()) {
7830 ClassStatementPtr cs
= static_pointer_cast
<ClassStatement
>(is
);
7831 auto const typeConstraint
=
7832 determine_type_constraint_from_annot(cs
->getEnumBaseTy(), true);
7833 pce
->setEnumBaseTy(typeConstraint
);
7839 class ForeachIterGuard
{
7840 EmitterVisitor
& m_ev
;
7842 ForeachIterGuard(EmitterVisitor
& ev
,
7847 m_ev
.pushIterScope(iterId
, kind
);
7849 ~ForeachIterGuard() {
7850 m_ev
.popIterScope();
7856 void EmitterVisitor::emitForeachListAssignment(Emitter
& e
,
7857 ListAssignmentPtr la
,
7858 std::function
<void()> emitSrc
) {
7859 std::vector
<IndexChain
*> indexChains
;
7860 IndexChain workingChain
;
7861 listAssignmentVisitLHS(e
, la
, workingChain
, indexChains
);
7863 if (indexChains
.size() == 0) {
7864 throw IncludeTimeFatalException(la
, "Cannot use empty list");
7867 listAssignmentAssignElements(e
, indexChains
, emitSrc
);
7870 void EmitterVisitor::emitForeach(Emitter
& e
,
7871 ForEachStatementPtr fe
) {
7872 auto region
= createRegion(fe
, Region::Kind::LoopOrSwitch
);
7873 ExpressionPtr
ae(fe
->getArrayExp());
7874 ExpressionPtr
val(fe
->getValueExp());
7875 ExpressionPtr
key(fe
->getNameExp());
7876 StatementPtr
body(fe
->getBody());
7879 bool strong
= fe
->isStrong();
7880 Label
& exit
= registerBreak(fe
, region
.get(), 1, false)->m_label
;
7881 Label
& next
= registerContinue(fe
, region
.get(), 1, false)->m_label
;
7884 Id itId
= m_curFunc
->allocIterator();
7885 ForeachIterGuard
fig(*this, itId
, strong
? KindOfMIter
: KindOfIter
);
7886 bool simpleCase
= (!key
|| isNormalLocalVariable(key
)) &&
7887 isNormalLocalVariable(val
);
7888 bool listKey
= key
? key
->is(Expression::KindOfListAssignment
) : false;
7889 bool listVal
= val
->is(Expression::KindOfListAssignment
);
7892 SimpleVariablePtr
svVal(static_pointer_cast
<SimpleVariable
>(val
));
7893 StringData
* name
= makeStaticString(svVal
->getName());
7894 valTempLocal
= m_curFunc
->lookupVarId(name
);
7896 SimpleVariablePtr
svKey(static_pointer_cast
<SimpleVariable
>(key
));
7897 name
= makeStaticString(svKey
->getName());
7898 keyTempLocal
= m_curFunc
->lookupVarId(name
);
7900 // Meta info on the key local will confuse the translator (and
7901 // wouldn't be useful anyway)
7902 m_evalStack
.cleanTopMeta();
7908 // Meta info on the value local will confuse the translator (and
7909 // wouldn't be useful anyway)
7910 m_evalStack
.cleanTopMeta();
7913 emitConvertToVar(e
);
7915 e
.MIterInitK(itId
, exit
, valTempLocal
, keyTempLocal
);
7917 e
.MIterInit(itId
, exit
, valTempLocal
);
7920 emitConvertToCell(e
);
7922 e
.IterInitK(itId
, exit
, valTempLocal
, keyTempLocal
);
7924 e
.IterInit(itId
, exit
, valTempLocal
);
7929 bIterStart
= m_ue
.bcPos();
7931 keyTempLocal
= key
? m_curFunc
->allocUnnamedLocal() : -1;
7932 valTempLocal
= m_curFunc
->allocUnnamedLocal();
7934 emitVirtualLocal(keyTempLocal
);
7936 emitVirtualLocal(valTempLocal
);
7940 emitConvertToVar(e
);
7942 emitConvertToCell(e
);
7947 e
.MIterInitK(itId
, exit
, valTempLocal
, keyTempLocal
);
7949 e
.MIterInit(itId
, exit
, valTempLocal
);
7953 e
.IterInitK(itId
, exit
, valTempLocal
, keyTempLocal
);
7955 e
.IterInit(itId
, exit
, valTempLocal
);
7959 // At this point, valTempLocal and keyTempLocal if applicable, contain the
7960 // key and value for the iterator.
7962 bIterStart
= m_ue
.bcPos();
7963 if (key
&& !listKey
) {
7967 emitForeachListAssignment(
7969 ListAssignmentPtr(static_pointer_cast
<ListAssignment
>(val
)),
7970 [&] { emitVirtualLocal(valTempLocal
); }
7974 emitVirtualLocal(valTempLocal
);
7984 emitVirtualLocal(valTempLocal
);
7986 newFaultRegionAndFunclet(bIterStart
, m_ue
.bcPos(),
7987 new UnsetUnnamedLocalThunklet(valTempLocal
));
7989 assert(keyTempLocal
!= -1);
7991 emitForeachListAssignment(
7993 ListAssignmentPtr(static_pointer_cast
<ListAssignment
>(key
)),
7994 [&] { emitVirtualLocal(keyTempLocal
); }
7997 emitVirtualLocal(keyTempLocal
);
8002 emitVirtualLocal(keyTempLocal
);
8004 newFaultRegionAndFunclet(bIterStart
, m_ue
.bcPos(),
8005 new UnsetUnnamedLocalThunklet(keyTempLocal
));
8010 region
->m_iterId
= itId
;
8011 region
->m_iterKind
= strong
? KindOfMIter
: KindOfIter
;
8012 enterRegion(region
);
8013 SCOPE_EXIT
{ leaveRegion(region
); };
8014 if (body
) visit(body
);
8016 if (next
.isUsed()) {
8020 emitVirtualLocal(keyTempLocal
);
8021 // Meta info on the key local will confuse the translator (and
8022 // wouldn't be useful anyway)
8023 m_evalStack
.cleanTopMeta();
8025 emitVirtualLocal(valTempLocal
);
8026 // Meta info on the value local will confuse the translator (and
8027 // wouldn't be useful anyway)
8028 m_evalStack
.cleanTopMeta();
8031 e
.MIterNextK(itId
, start
, valTempLocal
, keyTempLocal
);
8033 e
.MIterNext(itId
, start
, valTempLocal
);
8037 e
.IterNextK(itId
, start
, valTempLocal
, keyTempLocal
);
8039 e
.IterNext(itId
, start
, valTempLocal
);
8042 newFaultRegionAndFunclet(bIterStart
, m_ue
.bcPos(),
8043 new IterFreeThunklet(itId
, strong
),
8044 { itId
, strong
? KindOfMIter
: KindOfIter
});
8046 m_curFunc
->freeUnnamedLocal(valTempLocal
);
8048 m_curFunc
->freeUnnamedLocal(keyTempLocal
);
8052 m_curFunc
->freeIterator(itId
);
8055 void EmitterVisitor::emitForeachAwaitAs(Emitter
& e
,
8056 ForEachStatementPtr fe
) {
8057 assert(!fe
->isStrong());
8058 auto region
= createRegion(fe
, Region::Kind::LoopOrSwitch
);
8059 Label
& exit
= registerBreak(fe
, region
.get(), 1, false)->m_label
;
8060 Label
& next
= registerContinue(fe
, region
.get(), 1, false)->m_label
;
8062 // Evaluate the AsyncIterator object and store it into unnamed local.
8063 auto const iterTempLocal
= m_curFunc
->allocUnnamedLocal();
8064 emitVirtualLocal(iterTempLocal
);
8065 visit(fe
->getArrayExp());
8066 emitConvertToCell(e
);
8068 auto const iterTempStartUse
= m_ue
.bcPos();
8070 // Make sure it actually is an AsyncIterator.
8071 e
.InstanceOfD(makeStaticString("HH\\AsyncIterator"));
8073 e
.String(makeStaticString(
8074 "Unable to iterate non-AsyncIterator asynchronously"));
8075 e
.Fatal(FatalOp::Runtime
);
8077 // Start of the next iteration.
8080 // Await the next value.
8081 emitVirtualLocal(iterTempLocal
);
8083 emitConstMethodCallNoParams(e
, "next");
8084 e
.Await(m_pendingIters
.size());
8085 auto const resultTempLocal
= emitSetUnnamedL(e
);
8087 // Did we finish yet?
8088 emitVirtualLocal(resultTempLocal
);
8089 emitIsType(e
, IsTypeOp::Null
);
8092 auto const populate
= [&](ExpressionPtr target
, int index
) {
8093 auto const emitSrc
= [&] {
8094 emitVirtualLocal(resultTempLocal
);
8095 m_evalStack
.push(StackSym::I
);
8096 m_evalStack
.setInt(index
);
8100 if (target
->is(Expression::KindOfListAssignment
)) {
8101 emitForeachListAssignment(
8103 ListAssignmentPtr(static_pointer_cast
<ListAssignment
>(target
)),
8107 // Obtain target to be set.
8110 // Put $result[index] on the stack.
8120 auto const resultTempStartUse
= m_ue
.bcPos();
8123 if (fe
->getNameExp()) {
8124 populate(fe
->getNameExp(), 0);
8128 populate(fe
->getValueExp(), 1);
8130 newFaultRegionAndFunclet(resultTempStartUse
, m_ue
.bcPos(),
8131 new UnsetUnnamedLocalThunklet(resultTempLocal
));
8132 emitVirtualLocal(resultTempLocal
);
8137 enterRegion(region
);
8138 SCOPE_EXIT
{ leaveRegion(region
); };
8139 if (fe
->getBody()) visit(fe
->getBody());
8142 // Continue iteration.
8148 emitVirtualLocal(resultTempLocal
);
8150 m_curFunc
->freeUnnamedLocal(resultTempLocal
);
8152 newFaultRegionAndFunclet(iterTempStartUse
, m_ue
.bcPos(),
8153 new UnsetUnnamedLocalThunklet(iterTempLocal
));
8154 emitVirtualLocal(iterTempLocal
);
8156 m_curFunc
->freeUnnamedLocal(iterTempLocal
);
8160 * Emits bytecode that restores the previous error reporting level after
8161 * evaluating a silenced (@) expression, or in the fault funclet protecting such
8162 * an expression. Requires a local variable id containing the previous error
8163 * reporting level. The whole silenced expression looks like this:
8164 * oldvalue = error_reporting(0)
8165 * ...evaluate silenced expression...
8166 * oldvalue = error_reporting(oldvalue)
8168 * error_reporting(oldvalue)
8170 void EmitterVisitor::emitRestoreErrorReporting(Emitter
& e
, Id oldLevelLoc
) {
8171 emitVirtualLocal(oldLevelLoc
);
8172 auto idx
= m_evalStack
.size() - 1;
8173 e
.Silence(m_evalStack
.getLoc(idx
), SilenceOp::End
);
8176 void EmitterVisitor::emitMakeUnitFatal(Emitter
& e
,
8179 const StringData
* sd
= makeStaticString(msg
);
8184 Funclet
* EmitterVisitor::addFunclet(StatementPtr stmt
, Thunklet
* body
) {
8185 Funclet
* f
= addFunclet(body
);
8186 m_memoizedFunclets
.insert(std::make_pair(stmt
, f
));
8190 Funclet
* EmitterVisitor::addFunclet(Thunklet
* body
) {
8191 m_funclets
.push_back(new Funclet(body
));
8192 return m_funclets
.back();
8195 Funclet
* EmitterVisitor::getFunclet(StatementPtr stmt
) {
8196 if (m_memoizedFunclets
.count(stmt
)) {
8197 return m_memoizedFunclets
[stmt
];
8203 void EmitterVisitor::emitFunclets(Emitter
& e
) {
8204 // TODO (#3271358): New fault funclets might appear while emitting
8205 // finally fault funclets. This is because we currently don't memoize
8206 // fault funclets other than finally fault fuclets. See task
8207 // description for more details.
8208 for (int i
= 0; i
< m_funclets
.size(); ++i
) {
8209 Funclet
* f
= m_funclets
[i
];
8213 f
->m_body
= nullptr;
8217 void EmitterVisitor::newFaultRegion(Offset start
,
8220 FaultIterInfo iter
) {
8221 auto r
= new FaultRegion(start
, end
, entry
, iter
.iterId
, iter
.kind
);
8222 m_faultRegions
.push_back(r
);
8225 void EmitterVisitor::newFaultRegionAndFunclet(Offset start
,
8228 FaultIterInfo iter
) {
8229 Funclet
* f
= addFunclet(t
);
8230 newFaultRegion(start
, end
, &f
->m_entry
, iter
);
8233 void EmitterVisitor::newFaultRegionAndFunclet(StatementPtr stmt
,
8237 FaultIterInfo iter
) {
8238 Funclet
* f
= addFunclet(stmt
, t
);
8239 newFaultRegion(start
, end
, &f
->m_entry
, iter
);
8242 void EmitterVisitor::newFPIRegion(Offset start
, Offset end
, Offset fpOff
) {
8243 FPIRegion
* r
= new FPIRegion(start
, end
, fpOff
);
8244 m_fpiRegions
.push_back(r
);
8247 void EmitterVisitor::copyOverCatchAndFaultRegions(FuncEmitter
* fe
) {
8248 for (auto& eh
: m_catchRegions
) {
8249 EHEnt
& e
= fe
->addEHEnt();
8250 e
.m_type
= EHEnt::Type::Catch
;
8251 e
.m_base
= eh
->m_start
;
8252 e
.m_past
= eh
->m_end
;
8253 assert(e
.m_base
!= kInvalidOffset
);
8254 assert(e
.m_past
!= kInvalidOffset
);
8256 for (auto& c
: eh
->m_catchLabels
) {
8257 Id id
= m_ue
.mergeLitstr(c
.first
);
8258 Offset off
= c
.second
->getAbsoluteOffset();
8259 e
.m_catches
.push_back(std::pair
<Id
, Offset
>(id
, off
));
8263 m_catchRegions
.clear();
8264 for (auto& fr
: m_faultRegions
) {
8265 EHEnt
& e
= fe
->addEHEnt();
8266 e
.m_type
= EHEnt::Type::Fault
;
8267 e
.m_base
= fr
->m_start
;
8268 e
.m_past
= fr
->m_end
;
8269 assert(e
.m_base
!= kInvalidOffset
);
8270 assert(e
.m_past
!= kInvalidOffset
);
8271 e
.m_iterId
= fr
->m_iterId
;
8272 e
.m_itRef
= fr
->m_iterKind
== KindOfMIter
;
8273 e
.m_fault
= fr
->m_func
->getAbsoluteOffset();
8274 assert(e
.m_fault
!= kInvalidOffset
);
8277 m_faultRegions
.clear();
8278 for (auto f
: m_funclets
) {
8282 m_memoizedFunclets
.clear();
8285 void EmitterVisitor::copyOverFPIRegions(FuncEmitter
* fe
) {
8286 for (std::deque
<FPIRegion
*>::iterator it
= m_fpiRegions
.begin();
8287 it
!= m_fpiRegions
.end(); ++it
) {
8288 FPIEnt
& e
= fe
->addFPIEnt();
8289 e
.m_fpushOff
= (*it
)->m_start
;
8290 e
.m_fcallOff
= (*it
)->m_end
;
8291 e
.m_fpOff
= (*it
)->m_fpOff
;
8294 m_fpiRegions
.clear();
8297 void EmitterVisitor::saveMaxStackCells(FuncEmitter
* fe
) {
8298 fe
->maxStackCells
= m_actualStackHighWater
+
8299 fe
->numIterators() * kNumIterCells
+
8302 m_actualStackHighWater
= 0;
8303 m_fdescHighWater
= 0;
8306 // Are you sure you mean to be calling this directly? Would FuncFinisher
8307 // be more appropriate?
8308 void EmitterVisitor::finishFunc(Emitter
& e
, FuncEmitter
* fe
) {
8310 saveMaxStackCells(fe
);
8311 copyOverCatchAndFaultRegions(fe
);
8312 copyOverFPIRegions(fe
);
8313 m_staticEmitted
.clear();
8314 Offset past
= e
.getUnitEmitter().bcPos();
8315 fe
->finish(past
, false);
8316 e
.getUnitEmitter().recordFunction(fe
);
8317 if (m_stateLocal
>= 0) {
8320 if (m_retLocal
>= 0) {
8325 void EmitterVisitor::initScalar(TypedValue
& tvVal
, ExpressionPtr val
) {
8326 assert(val
->isScalar());
8327 tvVal
.m_type
= KindOfUninit
;
8328 switch (val
->getKindOf()) {
8329 case Expression::KindOfConstantExpression
: {
8330 ConstantExpressionPtr
ce(static_pointer_cast
<ConstantExpression
>(val
));
8332 tvVal
.m_data
.num
= 0;
8333 tvVal
.m_type
= KindOfNull
;
8334 } else if (ce
->isBoolean()) {
8335 tvVal
= make_tv
<KindOfBoolean
>(ce
->getBooleanValue());
8336 } else if (ce
->isScalar()) {
8337 ce
->getScalarValue(tvAsVariant(&tvVal
));
8343 case Expression::KindOfScalarExpression
: {
8344 ScalarExpressionPtr sval
= static_pointer_cast
<ScalarExpression
>(val
);
8345 const std::string
* s
;
8346 if (sval
->getString(s
)) {
8347 StringData
* sd
= makeStaticString(*s
);
8348 tvVal
= make_tv
<KindOfString
>(sd
);
8352 if (sval
->getInt(i
)) {
8353 tvVal
= make_tv
<KindOfInt64
>(i
);
8357 if (sval
->getDouble(d
)) {
8358 tvVal
= make_tv
<KindOfDouble
>(d
);
8364 case Expression::KindOfUnaryOpExpression
: {
8365 UnaryOpExpressionPtr
u(static_pointer_cast
<UnaryOpExpression
>(val
));
8366 if (u
->getOp() == T_ARRAY
) {
8367 m_staticArrays
.push_back(Array::attach(MixedArray::MakeReserve(0)));
8368 visit(u
->getExpression());
8369 tvVal
= make_tv
<KindOfArray
>(
8370 ArrayData::GetScalarArray(m_staticArrays
.back().get())
8372 m_staticArrays
.pop_back();
8378 if (val
->getScalarValue(tvAsVariant(&tvVal
))) {
8379 if (tvAsVariant(&tvVal
).isArray()) {
8389 bool EmitterVisitor::requiresDeepInit(ExpressionPtr initExpr
) const {
8390 switch (initExpr
->getKindOf()) {
8391 case Expression::KindOfScalarExpression
:
8392 case Expression::KindOfConstantExpression
:
8393 case Expression::KindOfClassConstantExpression
:
8395 case Expression::KindOfUnaryOpExpression
: {
8396 UnaryOpExpressionPtr
u(
8397 static_pointer_cast
<UnaryOpExpression
>(initExpr
));
8398 if (u
->getOp() == T_ARRAY
) {
8399 ExpressionListPtr el
=
8400 static_pointer_cast
<ExpressionList
>(u
->getExpression());
8402 int n
= el
->getCount();
8403 for (int i
= 0; i
< n
; i
++) {
8404 ArrayPairExpressionPtr ap
=
8405 static_pointer_cast
<ArrayPairExpression
>((*el
)[i
]);
8406 ExpressionPtr key
= ap
->getName();
8407 if (requiresDeepInit(ap
->getValue()) ||
8408 (key
&& requiresDeepInit(key
))) {
8414 } else if (u
->getOp() == '+' || u
->getOp() == '-') {
8415 return requiresDeepInit(u
->getExpression());
8416 } else if (u
->getOp() == T_FILE
|| u
->getOp() == T_DIR
) {
8426 Thunklet::~Thunklet() {}
8428 static ConstructPtr
doOptimize(ConstructPtr c
, AnalysisResultConstPtr ar
) {
8429 for (int i
= 0, n
= c
->getKidCount(); i
< n
; i
++) {
8430 if (ConstructPtr k
= c
->getNthKid(i
)) {
8431 if (ConstructPtr rep
= doOptimize(k
, ar
)) {
8432 c
->setNthKid(i
, rep
);
8436 if (ExpressionPtr e
= dynamic_pointer_cast
<Expression
>(c
)) {
8437 switch (e
->getKindOf()) {
8438 case Expression::KindOfBinaryOpExpression
:
8439 case Expression::KindOfUnaryOpExpression
:
8440 case Expression::KindOfIncludeExpression
:
8441 case Expression::KindOfSimpleFunctionCall
:
8442 return e
->preOptimize(ar
);
8443 case Expression::KindOfClosureExpression
: {
8444 ClosureExpressionPtr cl
= static_pointer_cast
<ClosureExpression
>(e
);
8445 auto UNUSED exp
= doOptimize(cl
->getClosureFunction(), ar
);
8452 return ConstructPtr();
8455 static UnitEmitter
* emitHHBCUnitEmitter(AnalysisResultPtr ar
, FileScopePtr fsp
,
8457 if (fsp
->getPseudoMain() && !Option::WholeProgram
) {
8458 ar
->setPhase(AnalysisResult::FirstPreOptimize
);
8459 doOptimize(fsp
->getPseudoMain()->getStmt(), ar
);
8462 if (RuntimeOption::EvalDumpAst
) {
8463 if (fsp
->getPseudoMain()) {
8464 fsp
->getPseudoMain()->getStmt()->dump(0, ar
);
8468 MethodStatementPtr
msp(dynamic_pointer_cast
<MethodStatement
>(
8469 fsp
->getPseudoMain()->getStmt()));
8470 UnitEmitter
* ue
= new UnitEmitter(md5
);
8471 ue
->m_preloadPriority
= fsp
->preloadPriority();
8472 const Location
* sLoc
= msp
->getLocation().get();
8473 ue
->initMain(sLoc
->line0
, sLoc
->line1
);
8474 EmitterVisitor
ev(*ue
);
8477 } catch (EmitterVisitor::IncludeTimeFatalException
& ex
) {
8478 // Replace the unit with an empty one, but preserve its file path.
8479 UnitEmitter
* nue
= new UnitEmitter(md5
);
8480 nue
->initMain(sLoc
->line0
, sLoc
->line1
);
8481 nue
->m_filepath
= ue
->m_filepath
;
8485 EmitterVisitor
fev(*ue
);
8486 Emitter
emitter(ex
.m_node
, *ue
, fev
);
8487 FuncFinisher
ff(&fev
, emitter
, ue
->getMain());
8488 auto kind
= ex
.m_parseFatal
? FatalOp::Parse
: FatalOp::Runtime
;
8489 fev
.emitMakeUnitFatal(emitter
, ex
.getMessage().c_str(), kind
);
8496 const HhbcExtClassInfo
* info
;
8497 const ClassInfo
* ci
;
8500 const StaticString
s_systemlibNativeFunc("/:systemlib.nativefunc");
8501 const StaticString
s_systemlibNativeCls("/:systemlib.nativecls");
8503 const MD5
s_nativeFuncMD5("11111111111111111111111111111111");
8504 const MD5
s_nativeClassMD5("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
8506 static std::unique_ptr
<UnitEmitter
>
8507 emitHHBCNativeFuncUnit(const HhbcExtFuncInfo
* builtinFuncs
,
8508 ssize_t numBuiltinFuncs
) {
8509 auto ue
= folly::make_unique
<UnitEmitter
>(s_nativeFuncMD5
);
8510 ue
->m_filepath
= s_systemlibNativeFunc
.get();
8511 ue
->addTrivialPseudoMain();
8513 Attr attrs
= AttrBuiltin
| AttrUnique
| AttrPersistent
;
8515 Special function used by FPushCuf* when its argument
8518 StringData
* name
= makeStaticString("86null");
8519 FuncEmitter
* fe
= ue
->newFuncEmitter(name
);
8520 fe
->init(0, 0, ue
->bcPos(), attrs
, true, staticEmptyString());
8523 fe
->maxStackCells
= 1;
8524 fe
->finish(ue
->bcPos(), false);
8525 ue
->recordFunction(fe
);
8527 for (ssize_t i
= 0; i
< numBuiltinFuncs
; ++i
) {
8528 const HhbcExtFuncInfo
* info
= &builtinFuncs
[i
];
8529 StringData
* name
= makeStaticString(info
->m_name
);
8530 BuiltinFunction bif
= (BuiltinFunction
)info
->m_builtinFunc
;
8531 BuiltinFunction nif
= (BuiltinFunction
)info
->m_nativeFunc
;
8532 const ClassInfo::MethodInfo
* mi
= ClassInfo::FindFunction(name
);
8534 "MethodInfo not found; may be a problem with the .idl.json files");
8536 // We already provide array_map by the hhas systemlib. Rename
8537 // because that hhas implementation delegates back to the C++
8538 // implementation for some edge cases. This works for any
8539 // similarly defined function (already defined not as a builtin),
8540 // and requires that the hhas systemlib is already loaded.
8541 if (auto const existing
= Unit::lookupFunc(name
)) {
8542 if (!existing
->isCPPBuiltin()) {
8543 name
= makeStaticString("__builtin_" + name
->toCppString());
8547 FuncEmitter
* fe
= ue
->newFuncEmitter(name
);
8548 Offset base
= ue
->bcPos();
8549 fe
->setBuiltinFunc(mi
, bif
, nif
, base
);
8550 ue
->emitOp(OpNativeImpl
);
8551 assert(!fe
->numIterators());
8552 fe
->maxStackCells
= fe
->numLocals();
8554 fe
->finish(ue
->bcPos(), false);
8555 ue
->recordFunction(fe
);
8561 enum GeneratorMethod
{
8569 typedef hphp_hash_map
<const StringData
*, GeneratorMethod
,
8570 string_data_hash
, string_data_same
> ContMethMap
;
8572 static int32_t emitGeneratorMethod(UnitEmitter
& ue
,
8574 GeneratorMethod m
) {
8575 static const StringData
* valStr
= makeStaticString("value");
8577 Attr attrs
= (Attr
)(AttrBuiltin
| AttrPublic
);
8578 fe
->init(0, 0, ue
.bcPos(), attrs
, false, staticEmptyString());
8582 fe
->appendParam(valStr
, FuncEmitter::ParamInfo());
8584 // We always want these methods to be cloned with new funcids in
8585 // subclasses so we can burn Class*s and Func*s into the
8587 fe
->attrs
|= AttrClone
;
8589 // check generator status; send()/raise() also checks started
8590 ue
.emitOp(OpContCheck
);
8591 ue
.emitIVA(m
== METH_SEND
|| m
== METH_RAISE
);
8596 ue
.emitOp(OpContEnter
);
8599 ue
.emitOp(OpPushL
); ue
.emitIVA(0);
8600 ue
.emitOp(OpContEnter
);
8603 ue
.emitOp(OpPushL
); ue
.emitIVA(0);
8604 ue
.emitOp(OpContRaise
);
8610 // Backtrace has off-by-one bug when determining whether we are
8611 // in returning opcode; add Nop to avoid it
8617 ue
.emitOp(OpContValid
);
8621 case METH_CURRENT
: {
8622 ue
.emitOp(OpContCurrent
);
8627 ue
.emitOp(OpContKey
);
8636 return 1; // Above cases push at most one stack cell.
8639 static int32_t emitGetWaitHandleMethod(UnitEmitter
& ue
, FuncEmitter
* fe
) {
8640 Attr attrs
= (Attr
)(AttrBuiltin
| AttrPublic
);
8641 fe
->init(0, 0, ue
.bcPos(), attrs
, false, staticEmptyString());
8644 return 1; // we use one stack slot
8647 StaticString
s_construct("__construct");
8648 static std::unique_ptr
<UnitEmitter
>
8649 emitHHBCNativeClassUnit(const HhbcExtClassInfo
* builtinClasses
,
8650 ssize_t numBuiltinClasses
) {
8651 auto ue
= folly::make_unique
<UnitEmitter
>(s_nativeClassMD5
);
8652 ue
->m_filepath
= s_systemlibNativeCls
.get();
8653 ue
->addTrivialPseudoMain();
8655 ContMethMap asyncGenMethods
;
8656 asyncGenMethods
[makeStaticString("next")] = METH_NEXT
;
8657 asyncGenMethods
[makeStaticString("send")] = METH_SEND
;
8658 asyncGenMethods
[makeStaticString("raise")] = METH_RAISE
;
8660 ContMethMap genMethods
;
8661 genMethods
[makeStaticString("next")] = METH_NEXT
;
8662 genMethods
[makeStaticString("send")] = METH_SEND
;
8663 genMethods
[makeStaticString("raise")] = METH_RAISE
;
8664 genMethods
[makeStaticString("valid")] = METH_VALID
;
8665 genMethods
[makeStaticString("current")] = METH_CURRENT
;
8666 genMethods
[makeStaticString("key")] = METH_KEY
;
8668 // Build up extClassHash, a hashtable that maps class names to structures
8669 // containing C++ function pointers for the class's methods and constructors
8670 if (!Class::s_extClassHash
.empty()) {
8671 Class::s_extClassHash
.clear();
8673 for (long long i
= 0; i
< numBuiltinClasses
; ++i
) {
8674 const HhbcExtClassInfo
* info
= builtinClasses
+ i
;
8675 StringData
*s
= makeStaticString(info
->m_name
);
8676 Class::s_extClassHash
[s
] = info
;
8678 // If a given class has a base class, then we can't load that class
8679 // before we load the base class. Build up some structures so that
8680 // we can load the C++ builtin classes in the right order.
8681 std::vector
<Entry
> classEntries
;
8683 typedef hphp_hash_map
<StringData
*, std::vector
<Entry
>,
8684 string_data_hash
, string_data_isame
> PendingMap
;
8686 hphp_hash_map
<const StringData
*, const HhbcExtClassInfo
*,
8687 string_data_hash
, string_data_isame
>::iterator it
;
8688 for (it
= Class::s_extClassHash
.begin();
8689 it
!= Class::s_extClassHash
.end(); ++it
) {
8691 e
.name
= const_cast<StringData
*>(it
->first
);
8692 e
.info
= it
->second
;
8693 e
.ci
= ClassInfo::FindSystemClassInterfaceOrTrait(e
.name
);
8695 StringData
* parentName
8696 = makeStaticString(e
.ci
->getParentClass().get());
8697 if (parentName
->empty()) {
8698 // If this class doesn't have a base class, it's eligible to be
8700 classEntries
.push_back(e
);
8702 // If this class has a base class, we can't load it until its
8703 // base class has been loaded
8704 pending
[parentName
].push_back(e
);
8707 for (unsigned k
= 0; k
< classEntries
.size(); ++k
) {
8708 Entry
& e
= classEntries
[k
];
8709 // Any classes that derive from this class are now eligible to be
8711 PendingMap::iterator pendingIt
= pending
.find(e
.name
);
8712 if (pendingIt
!= pending
.end()) {
8713 for (unsigned i
= 0; i
< pendingIt
->second
.size(); ++i
) {
8714 classEntries
.push_back(pendingIt
->second
[i
]);
8716 pending
.erase(pendingIt
);
8719 assert(pending
.empty());
8722 for (unsigned int i
= 0; i
< classEntries
.size(); ++i
) {
8723 Entry
& e
= classEntries
[i
];
8724 StringData
* parentName
= makeStaticString(e
.ci
->getParentClass().get());
8725 PreClassEmitter
* pce
= ue
->newPreClassEmitter(e
.name
,
8726 PreClass::AlwaysHoistable
);
8727 pce
->init(0, 0, ue
->bcPos(), AttrBuiltin
|AttrUnique
|AttrPersistent
,
8728 parentName
, nullptr);
8729 pce
->setBuiltinClassInfo(
8731 e
.info
->m_instanceCtor
,
8732 e
.info
->m_instanceDtor
,
8733 BuiltinObjExtents
{ e
.info
->m_totalSize
, e
.info
->m_objectDataOffset
}
8736 ClassInfo::InterfaceVec intfVec
= e
.ci
->getInterfacesVec();
8737 for (unsigned i
= 0; i
< intfVec
.size(); ++i
) {
8738 const StringData
* intf
= makeStaticString(intfVec
[i
].get());
8739 pce
->addInterface(intf
);
8743 bool hasCtor
= false;
8744 for (ssize_t j
= 0; j
< e
.info
->m_methodCount
; ++j
) {
8745 const HhbcExtMethodInfo
* methodInfo
= &(e
.info
->m_methods
[j
]);
8746 static const StringData
* asyncGenCls
= makeStaticString("asyncgenerator");
8747 static const StringData
* generatorCls
= makeStaticString("generator");
8748 static const StringData
* waitHandleCls
= makeStaticString("waithandle");
8749 static const StringData
* gwhMeth
= makeStaticString("getwaithandle");
8750 StringData
* methName
= makeStaticString(methodInfo
->m_name
);
8751 GeneratorMethod
* cmeth
;
8753 FuncEmitter
* fe
= ue
->newMethodEmitter(methName
, pce
);
8755 auto stackPad
= int32_t{0};
8756 if (e
.name
->isame(asyncGenCls
) &&
8757 (cmeth
= folly::get_ptr(asyncGenMethods
, methName
))) {
8758 auto methCpy
= *cmeth
;
8759 stackPad
= emitGeneratorMethod(*ue
, fe
, methCpy
);
8760 } else if (e
.name
->isame(generatorCls
) &&
8761 (cmeth
= folly::get_ptr(genMethods
, methName
))) {
8762 auto methCpy
= *cmeth
;
8763 stackPad
= emitGeneratorMethod(*ue
, fe
, methCpy
);
8764 } else if (e
.name
->isame(waitHandleCls
) && methName
->isame(gwhMeth
)) {
8765 stackPad
= emitGetWaitHandleMethod(*ue
, fe
);
8767 if (e
.name
->isame(s_construct
.get())) {
8771 // Build the function
8772 BuiltinFunction bcf
= (BuiltinFunction
)methodInfo
->m_pGenericMethod
;
8773 auto nativeFunc
= methodInfo
->m_nativeFunc
;
8774 const ClassInfo::MethodInfo
* mi
=
8775 e
.ci
->getMethodInfo(std::string(methodInfo
->m_name
));
8776 Offset base
= ue
->bcPos();
8777 fe
->setBuiltinFunc(mi
,
8779 reinterpret_cast<BuiltinFunction
>(nativeFunc
),
8782 ue
->emitOp(OpNativeImpl
);
8784 Offset past
= ue
->bcPos();
8785 assert(!fe
->numIterators());
8786 fe
->maxStackCells
= fe
->numLocals() + stackPad
;
8787 fe
->finish(past
, false);
8788 ue
->recordFunction(fe
);
8791 static const StringData
* methName
= makeStaticString("86ctor");
8792 FuncEmitter
* fe
= ue
->newMethodEmitter(methName
, pce
);
8793 bool added UNUSED
= pce
->addMethod(fe
);
8795 fe
->init(0, 0, ue
->bcPos(), AttrBuiltin
|AttrPublic
,
8796 false, staticEmptyString());
8799 fe
->maxStackCells
= 1;
8800 fe
->finish(ue
->bcPos(), false);
8801 ue
->recordFunction(fe
);
8805 ClassInfo::ConstantVec cnsVec
= e
.ci
->getConstantsVec();
8806 for (unsigned i
= 0; i
< cnsVec
.size(); ++i
) {
8807 const ClassInfo::ConstantInfo
* cnsInfo
= cnsVec
[i
];
8811 val
= cnsInfo
->getValue();
8812 } catch (Exception
& e
) {
8816 cnsInfo
->name
.get(),
8818 (TypedValue
*)(&val
),
8819 staticEmptyString());
8823 ClassInfo::PropertyVec propVec
= e
.ci
->getPropertiesVec();
8824 for (unsigned i
= 0; i
< propVec
.size(); ++i
) {
8825 const ClassInfo::PropertyInfo
* propInfo
= propVec
[i
];
8827 int attr
= AttrNone
;
8828 if (propInfo
->attribute
& ClassInfo::IsProtected
) {
8829 attr
|= AttrProtected
;
8830 } else if (propInfo
->attribute
& ClassInfo::IsPrivate
) {
8831 attr
|= AttrPrivate
;
8835 if (propInfo
->attribute
& ClassInfo::IsStatic
) attr
|= AttrStatic
;
8838 tvWriteNull(&tvNull
);
8840 propInfo
->name
.get(),
8843 propInfo
->docComment
?
8844 makeStaticString(propInfo
->docComment
) : nullptr,
8855 static UnitEmitter
* emitHHBCVisitor(AnalysisResultPtr ar
, FileScopeRawPtr fsp
) {
8856 MD5 md5
= fsp
->getMd5();
8858 if (!Option::WholeProgram
) {
8859 // The passed-in ar is only useful in whole-program mode, so create a
8860 // distinct ar to be used only for emission of this unit, and perform
8861 // unit-level (non-global) optimization.
8862 ar
= AnalysisResultPtr(new AnalysisResult());
8863 fsp
->setOuterScope(ar
);
8865 ar
->setPhase(AnalysisResult::AnalyzeAll
);
8866 fsp
->analyzeProgram(ar
);
8869 UnitEmitter
* ue
= emitHHBCUnitEmitter(ar
, fsp
, md5
);
8870 assert(ue
!= nullptr);
8872 if (Option::GenerateTextHHBC
) {
8873 // TODO(#2973538): Move HHBC text generation to after all the
8874 // units are created, and get rid of the LitstrTable locking,
8875 // since it won't be needed in that case.
8876 LitstrTable::get().mutex().lock();
8877 LitstrTable::get().setReading();
8878 std::unique_ptr
<Unit
> unit(ue
->create());
8879 std::string fullPath
= AnalysisResult::prepareFile(
8880 ar
->getOutputPath().c_str(), Option::UserFilePrefix
+ fsp
->getName(),
8881 true, false) + ".hhbc.txt";
8883 std::ofstream
f(fullPath
.c_str());
8885 Logger::Error("Unable to open %s for write", fullPath
.c_str());
8887 CodeGenerator
cg(&f
, CodeGenerator::TextHHBC
);
8888 cg
.printf("Hash: %" PRIx64
"%016" PRIx64
"\n", md5
.q
[0], md5
.q
[1]);
8889 cg
.printRaw(unit
->toString().c_str());
8892 LitstrTable::get().setWriting();
8893 LitstrTable::get().mutex().unlock();
8899 class UEQ
: public Synchronizable
{
8901 void push(UnitEmitter
* ue
) {
8902 assert(ue
!= nullptr);
8904 m_ues
.push_back(ue
);
8907 UnitEmitter
* tryPop(long sec
, long long nsec
) {
8909 if (m_ues
.empty()) {
8910 // Check for empty() after wait(), in case of spurious wakeup.
8911 if (!wait(sec
, nsec
) || m_ues
.empty()) {
8915 assert(m_ues
.size() > 0);
8916 UnitEmitter
* ue
= m_ues
.front();
8917 assert(ue
!= nullptr);
8922 std::deque
<UnitEmitter
*> m_ues
;
8927 : public JobQueueWorker
<FileScopeRawPtr
, void*, true, true> {
8929 EmitterWorker() : m_ret(true) {}
8930 virtual void doJob(JobType job
) {
8932 AnalysisResultPtr ar
= ((AnalysisResult
*)m_context
)->shared_from_this();
8933 UnitEmitter
* ue
= emitHHBCVisitor(ar
, job
);
8934 if (Option::GenerateBinaryHHBC
) {
8939 } catch (Exception
&e
) {
8940 Logger::Error("%s", e
.getMessage().c_str());
8943 Logger::Error("Fatal: An unexpected exception was thrown");
8951 static void addEmitterWorker(AnalysisResultPtr ar
, StatementPtr sp
,
8953 ((JobQueueDispatcher
<EmitterWorker
>*)data
)->enqueue(sp
->getFileScope());
8957 commitGlobalData(std::unique_ptr
<ArrayTypeTable::Builder
> arrTable
) {
8958 auto gd
= Repo::GlobalData
{};
8959 gd
.HardTypeHints
= Option::HardTypeHints
;
8960 gd
.UsedHHBBC
= Option::UseHHBBC
;
8961 gd
.HardPrivatePropInference
= true;
8963 if (arrTable
) gd
.arrayTypeTable
.repopulate(*arrTable
);
8964 Repo::get().saveGlobalData(gd
);
8968 * This is the entry point for offline bytecode generation.
8970 void emitAllHHBC(AnalysisResultPtr ar
) {
8971 unsigned int threadCount
= Option::ParserThreadCount
;
8972 unsigned int nFiles
= ar
->getAllFilesVector().size();
8973 if (threadCount
> nFiles
) {
8974 threadCount
= nFiles
;
8976 if (!threadCount
) threadCount
= 1;
8978 LitstrTable::get().setWriting();
8980 /* there is a race condition in the first call to
8981 makeStaticString. Make sure we dont hit it */
8983 makeStaticString("");
8984 /* same for TypeConstraint */
8988 JobQueueDispatcher
<EmitterWorker
>
8989 dispatcher(threadCount
, true, 0, false, ar
.get());
8991 auto setPreloadPriority
= [ar
](const std::string
& f
, int p
) {
8992 auto fs
= ar
->findFileScope(f
);
8993 if (fs
) fs
->setPreloadPriority(p
);
8997 * Mark files that are referenced from the autoload map
8998 * so they get preloaded via preloadRepo.
8999 * Higher priorities are preloaded first.
9000 * Classes, then functions, then constants mimics
9001 * the order of our existing warmup scripts
9003 for (const auto& ent
: Option::AutoloadConstMap
) {
9004 setPreloadPriority(ent
.second
, 1);
9006 for (const auto& ent
: Option::AutoloadFuncMap
) {
9007 setPreloadPriority(ent
.second
, 2);
9009 for (const auto& ent
: Option::AutoloadClassMap
) {
9010 setPreloadPriority(ent
.second
, 3);
9014 ar
->visitFiles(addEmitterWorker
, &dispatcher
);
9016 std::vector
<std::unique_ptr
<UnitEmitter
>> ues
;
9018 if (Option::GenerateBinaryHHBC
) {
9019 // kBatchSize needs to strike a balance between reducing transaction commit
9020 // overhead (bigger batches are better), and limiting the cost incurred by
9021 // failed commits due to identical units that require rollback and retry
9022 // (smaller batches have less to lose). Empirical results indicate that a
9023 // value in the 2-10 range is reasonable.
9024 static const unsigned kBatchSize
= 8;
9026 // Gather up units created by the worker threads and commit them in
9029 bool inShutdown
= false;
9031 // Poll, but with a 100ms timeout so that this thread doesn't spin wildly
9032 // if it gets ahead of the workers.
9033 UnitEmitter
* ue
= s_ueq
.tryPop(0, 100 * 1000 * 1000);
9034 if ((didPop
= (ue
!= nullptr))) {
9035 ues
.push_back(std::unique_ptr
<UnitEmitter
>{ue
});
9037 if (!Option::UseHHBBC
&&
9038 (ues
.size() == kBatchSize
||
9039 (!didPop
&& inShutdown
&& ues
.size() > 0))) {
9040 batchCommit(std::move(ues
));
9043 inShutdown
= dispatcher
.pollEmpty();
9044 } else if (!didPop
) {
9045 assert(Option::UseHHBBC
|| ues
.size() == 0);
9050 if (!Option::UseHHBBC
) {
9051 commitGlobalData(std::unique_ptr
<ArrayTypeTable::Builder
>{});
9054 dispatcher
.waitEmpty();
9057 assert(Option::UseHHBBC
|| ues
.empty());
9059 // We need to put the native func units in the repo so hhbbc can
9061 auto nfunc
= emitHHBCNativeFuncUnit(hhbc_ext_funcs
, hhbc_ext_funcs_count
);
9062 auto ncls
= emitHHBCNativeClassUnit(hhbc_ext_classes
,
9063 hhbc_ext_class_count
);
9064 ues
.push_back(std::move(nfunc
));
9065 ues
.push_back(std::move(ncls
));
9067 if (!Option::UseHHBBC
) {
9068 batchCommit(std::move(ues
));
9072 auto pair
= HHBBC::whole_program(std::move(ues
));
9073 batchCommit(std::move(pair
.first
));
9074 commitGlobalData(std::move(pair
.second
));
9079 StringData
* hphp_compiler_serialize_code_model_for(String code
, String prefix
) {
9080 AnalysisResultPtr
ar(new AnalysisResult());
9081 auto statements
= Parser::ParseString(code
, ar
, nullptr, false);
9082 if (statements
!= nullptr) {
9083 LabelScopePtr
labelScope(new LabelScope());
9084 auto block
= BlockStatementPtr(
9086 BlockScopePtr(), labelScope
, statements
->getLocation(), statements
9089 std::ostringstream serialized
;
9090 CodeGenerator
cg(&serialized
, CodeGenerator::Output::CodeModel
);
9091 cg
.setAstClassPrefix(prefix
.data());
9092 block
->outputCodeModel(cg
);
9093 return StringData::Make(serialized
.str().c_str(),
9094 serialized
.str().length(),
9097 return StringData::Make();
9102 * This is the entry point from the runtime; i.e. online bytecode generation.
9103 * The 'filename' parameter may be NULL if there is no file associated with
9106 * Before being actually used, hphp_compiler_parse must be called with
9107 * a NULL `code' parameter to do initialization.
9110 Unit
* hphp_compiler_parse(const char* code
, int codeLen
, const MD5
& md5
,
9111 const char* filename
) {
9112 if (UNLIKELY(!code
)) {
9113 // Do initialization when code is null; see above.
9114 Option::EnableHipHopSyntax
= RuntimeOption::EnableHipHopSyntax
;
9115 Option::EnableZendCompat
= RuntimeOption::EnableZendCompat
;
9116 Option::JitEnableRenameFunction
= RuntimeOption::EvalJitEnableRenameFunction
;
9117 for (auto& i
: RuntimeOption::DynamicInvokeFunctions
) {
9118 Option::DynamicInvokeFunctions
.insert(i
);
9120 Option::IntsOverflowToInts
= RuntimeOption::IntsOverflowToInts
;
9121 Option::RecordErrors
= false;
9122 Option::ParseTimeOpts
= false;
9123 Option::WholeProgram
= false;
9124 Type::InitTypeHintMap();
9125 BuiltinSymbols::LoadSuperGlobals();
9131 UnitOrigin unitOrigin
= UnitOrigin::File
;
9134 unitOrigin
= UnitOrigin::Eval
;
9136 SCOPE_EXIT
{ SymbolTable::Purge(); };
9138 std::unique_ptr
<UnitEmitter
> ue
;
9139 // Check if this file contains raw hip hop bytecode instead of
9140 // php. This is dictated by a special file extension.
9141 if (RuntimeOption::EvalAllowHhas
) {
9142 if (const char* dot
= strrchr(filename
, '.')) {
9143 const char hhbc_ext
[] = "hhas";
9144 if (!strcmp(dot
+ 1, hhbc_ext
)) {
9145 ue
.reset(assemble_string(code
, codeLen
, filename
, md5
));
9151 auto parseit
= [=] (AnalysisResultPtr ar
) {
9152 Scanner
scanner(code
, codeLen
,
9153 RuntimeOption::GetScannerType(), filename
);
9154 Parser
parser(scanner
, filename
, ar
, codeLen
);
9156 return parser
.getFileScope();
9159 if (BuiltinSymbols::s_systemAr
) {
9160 parseit(BuiltinSymbols::s_systemAr
)->setMd5(md5
);
9163 AnalysisResultPtr
ar(new AnalysisResult());
9164 FileScopePtr fsp
= parseit(ar
);
9165 fsp
->setOuterScope(ar
);
9167 ar
->setPhase(AnalysisResult::AnalyzeAll
);
9168 fsp
->analyzeProgram(ar
);
9170 ue
.reset(emitHHBCUnitEmitter(ar
, fsp
, md5
));
9172 Repo::get().commitUnit(ue
.get(), unitOrigin
);
9174 auto const unit
= ue
->create();
9177 if (unit
->sn() == -1) {
9178 // the unit was not committed to the Repo, probably because
9179 // another thread did it first. Try to use the winner.
9180 Unit
* u
= Repo::get().loadUnit(filename
? filename
: "", md5
);
9187 } catch (const std::exception
&) {
9188 // extern "C" function should not be throwing exceptions...
9193 Unit
* hphp_build_native_func_unit(const HhbcExtFuncInfo
* builtinFuncs
,
9194 ssize_t numBuiltinFuncs
) {
9195 return emitHHBCNativeFuncUnit(builtinFuncs
, numBuiltinFuncs
)->create();
9198 Unit
* hphp_build_native_class_unit(const HhbcExtClassInfo
* builtinClasses
,
9199 ssize_t numBuiltinClasses
) {
9200 auto const ue
= emitHHBCNativeClassUnit(builtinClasses
, numBuiltinClasses
);
9203 * In RepoAuthoritative mode, we may have serialized additional
9204 * information in attr bits about the native units. We still
9205 * rebuild it, but we'll clobber attr bits with what static analysis
9208 * Note: this is a bit of a short-term hack that we won't need once
9209 * HNI conversion is completed. We only pull things out of the repo
9210 * here that we've explicitly decided we want.
9212 if (!RuntimeOption::RepoAuthoritative
) return ue
->create();
9213 auto const staticAnalysisUnit
= Repo::get().urp().loadEmitter(
9214 "/:systemlib:static_analysis",
9217 if (!staticAnalysisUnit
) return ue
->create();
9219 // Make a map of the preclasses in `ue', so we can find them.
9220 std::map
<const StringData
*,PreClassEmitter
*> uePreClasses
;
9221 for (auto id
= Id
{0}; id
< ue
->numPreClasses(); ++id
) {
9222 auto const pce
= ue
->pce(id
);
9224 !uePreClasses
.count(pce
->name()),
9225 "IDL-based native class unit is expected to only have unique "
9226 "classes. {} was non-unique.",
9229 uePreClasses
[pce
->name()] = pce
;
9233 staticAnalysisUnit
->numPreClasses() == uePreClasses
.size(),
9234 "Static analysis unit didn't have the same number of classes as "
9235 "our native class unit; repo probably build with a different hhvm build."
9238 // Right now, the only thing we do here is clobber all the Attr bits
9239 // with the ones we found from static analysis. (To get things like
9241 for (auto id
= Id
{0}; id
< staticAnalysisUnit
->numPreClasses(); ++id
) {
9242 auto const staticAnalysisPce
= staticAnalysisUnit
->pce(id
);
9243 auto const uePce
= uePreClasses
[staticAnalysisPce
->name()];
9246 "Static analysis unit had a PreClass we don't have at runtime ({}); "
9247 "repo probably was built with a different hhvm build.",
9248 staticAnalysisPce
->name()->data()
9250 uePce
->setAttrs(staticAnalysisPce
->attrs());
9253 return ue
->create();
9258 ///////////////////////////////////////////////////////////////////////////////