2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- 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 +----------------------------------------------------------------------+
17 #include "hphp/compiler/analysis/emitter.h"
18 #include "hphp/compiler/builtin_symbols.h"
19 #include "hphp/compiler/analysis/class_scope.h"
20 #include "hphp/compiler/analysis/file_scope.h"
21 #include "hphp/compiler/analysis/function_scope.h"
22 #include "hphp/compiler/analysis/peephole.h"
23 #include "hphp/compiler/expression/array_element_expression.h"
24 #include "hphp/compiler/expression/array_pair_expression.h"
25 #include "hphp/compiler/expression/assignment_expression.h"
26 #include "hphp/compiler/expression/binary_op_expression.h"
27 #include "hphp/compiler/expression/class_constant_expression.h"
28 #include "hphp/compiler/expression/closure_expression.h"
29 #include "hphp/compiler/expression/constant_expression.h"
30 #include "hphp/compiler/expression/dynamic_variable.h"
31 #include "hphp/compiler/expression/encaps_list_expression.h"
32 #include "hphp/compiler/expression/expression_list.h"
33 #include "hphp/compiler/expression/include_expression.h"
34 #include "hphp/compiler/expression/list_assignment.h"
35 #include "hphp/compiler/expression/modifier_expression.h"
36 #include "hphp/compiler/expression/new_object_expression.h"
37 #include "hphp/compiler/expression/object_method_expression.h"
38 #include "hphp/compiler/expression/object_property_expression.h"
39 #include "hphp/compiler/expression/parameter_expression.h"
40 #include "hphp/compiler/expression/qop_expression.h"
41 #include "hphp/compiler/expression/scalar_expression.h"
42 #include "hphp/compiler/expression/simple_variable.h"
43 #include "hphp/compiler/expression/simple_function_call.h"
44 #include "hphp/compiler/expression/static_member_expression.h"
45 #include "hphp/compiler/expression/unary_op_expression.h"
46 #include "hphp/compiler/expression/yield_expression.h"
47 #include "hphp/compiler/statement/break_statement.h"
48 #include "hphp/compiler/statement/case_statement.h"
49 #include "hphp/compiler/statement/catch_statement.h"
50 #include "hphp/compiler/statement/class_constant.h"
51 #include "hphp/compiler/statement/class_variable.h"
52 #include "hphp/compiler/statement/do_statement.h"
53 #include "hphp/compiler/statement/echo_statement.h"
54 #include "hphp/compiler/statement/exp_statement.h"
55 #include "hphp/compiler/statement/for_statement.h"
56 #include "hphp/compiler/statement/foreach_statement.h"
57 #include "hphp/compiler/statement/function_statement.h"
58 #include "hphp/compiler/statement/global_statement.h"
59 #include "hphp/compiler/statement/goto_statement.h"
60 #include "hphp/compiler/statement/if_branch_statement.h"
61 #include "hphp/compiler/statement/if_statement.h"
62 #include "hphp/compiler/statement/label_statement.h"
63 #include "hphp/compiler/statement/method_statement.h"
64 #include "hphp/compiler/statement/return_statement.h"
65 #include "hphp/compiler/statement/statement_list.h"
66 #include "hphp/compiler/statement/static_statement.h"
67 #include "hphp/compiler/statement/switch_statement.h"
68 #include "hphp/compiler/statement/try_statement.h"
69 #include "hphp/compiler/statement/unset_statement.h"
70 #include "hphp/compiler/statement/while_statement.h"
71 #include "hphp/compiler/statement/use_trait_statement.h"
72 #include "hphp/compiler/statement/trait_prec_statement.h"
73 #include "hphp/compiler/statement/trait_alias_statement.h"
74 #include "hphp/compiler/statement/typedef_statement.h"
75 #include "hphp/compiler/parser/parser.h"
77 #include "hphp/util/logger.h"
78 #include "hphp/util/util.h"
79 #include "hphp/util/job_queue.h"
80 #include "hphp/util/parser/hphp.tab.hpp"
81 #include "hphp/runtime/vm/bytecode.h"
82 #include "hphp/runtime/vm/repo.h"
83 #include "hphp/runtime/vm/as.h"
84 #include "hphp/runtime/base/stats.h"
85 #include "hphp/runtime/base/runtime_option.h"
86 #include "hphp/runtime/base/zend/zend_string.h"
87 #include "hphp/runtime/base/type_conversions.h"
88 #include "hphp/runtime/base/builtin_functions.h"
89 #include "hphp/runtime/base/variable_serializer.h"
90 #include "hphp/runtime/base/program_functions.h"
91 #include "hphp/runtime/eval/runtime/file_repository.h"
92 #include "hphp/runtime/ext_hhvm/ext_hhvm.h"
94 #include "hphp/system/lib/systemlib.h"
96 #include "folly/ScopeGuard.h"
105 ///////////////////////////////////////////////////////////////////////////////
107 TRACE_SET_MOD(emitter
)
109 using boost::dynamic_pointer_cast
;
110 using boost::static_pointer_cast
;
113 static const char None
= 0x00;
115 static const char C
= 0x01; // Cell symbolic flavor
116 static const char V
= 0x02; // Var symbolic flavor
117 static const char A
= 0x03; // Classref symbolic flavor
118 static const char R
= 0x04; // Return value symbolic flavor
119 static const char F
= 0x05; // Function argument symbolic flavor
120 static const char L
= 0x06; // Local symbolic flavor
121 static const char T
= 0x07; // String literal symbolic flavor
122 static const char I
= 0x08; // int literal symbolic flavor
123 static const char H
= 0x09; // $this symbolic flavor
125 static const char N
= 0x10; // Name marker
126 static const char G
= 0x20; // Global name marker
127 static const char E
= 0x30; // Element marker
128 static const char W
= 0x40; // New element marker
129 static const char P
= 0x50; // Property marker
130 static const char S
= 0x60; // Static property marker
131 static const char M
= 0x70; // Non elem/prop/W part of M-vector
132 static const char K
= 0x80; // Marker for information about a class base
134 static const char CN
= C
| N
;
135 static const char CG
= C
| G
;
136 static const char CE
= C
| E
;
137 static const char CP
= C
| P
;
138 static const char CS
= C
| S
;
139 static const char LN
= L
| N
;
140 static const char LG
= L
| G
;
141 static const char LE
= L
| E
;
142 static const char LP
= L
| P
;
143 static const char LS
= L
| S
;
144 static const char AM
= A
| M
;
146 char GetSymFlavor(char sym
) { return (sym
& 0x0F); }
147 char GetMarker(char sym
) { return (sym
& 0xF0); }
148 std::string
ToString(char sym
) {
149 char symFlavor
= StackSym::GetSymFlavor(sym
);
152 case StackSym::C
: res
= "C"; break;
153 case StackSym::V
: res
= "V"; break;
154 case StackSym::A
: res
= "A"; break;
155 case StackSym::R
: res
= "R"; break;
156 case StackSym::F
: res
= "F"; break;
157 case StackSym::L
: res
= "L"; break;
158 case StackSym::T
: res
= "T"; break;
159 case StackSym::I
: res
= "I"; break;
160 case StackSym::H
: res
= "H"; break;
163 char marker
= StackSym::GetMarker(sym
);
165 case StackSym::N
: res
+= "N"; break;
166 case StackSym::G
: res
+= "G"; break;
167 case StackSym::E
: res
+= "E"; break;
168 case StackSym::W
: res
+= "W"; break;
169 case StackSym::P
: res
+= "P"; break;
170 case StackSym::S
: res
+= "S"; break;
171 case StackSym::K
: res
+= "K"; break;
175 if (sym
== StackSym::None
) {
185 //=============================================================================
188 #define InvariantViolation(...) do { \
189 Logger::Warning(__VA_ARGS__); \
190 Logger::Warning("Eval stack at the time of error: %s", \
191 m_evalStack.pretty().c_str()); \
195 // RAII guard for function creation.
197 EmitterVisitor
* m_ev
;
202 FuncFinisher(EmitterVisitor
* ev
, Emitter
& e
, FuncEmitter
* fe
)
203 : m_ev(ev
), m_e(e
), m_fe(fe
) {
204 TRACE(1, "FuncFinisher constructed: %s %p\n", m_fe
->name()->data(), m_fe
);
208 TRACE(1, "Finishing func: %s %p\n", m_fe
->name()->data(), m_fe
);
209 m_ev
->finishFunc(m_e
, m_fe
);
213 // RAII guard for temporarily overriding an Emitter's location
214 class LocationGuard
{
219 LocationGuard(Emitter
& e
, LocationPtr newLoc
)
220 : m_e(e
), m_loc(e
.getTempLocation()) {
221 if (newLoc
) m_e
.setTempLocation(newLoc
);
224 m_e
.setTempLocation(m_loc
);
228 // Count the number of stack elements in an immediate vector.
229 static int32_t countStackValues(const std::vector
<uchar
>& immVec
) {
230 assert(!immVec
.empty());
233 const uint8_t* vec
= &immVec
[0];
235 // Count the location; the LS location type accounts for up to two
236 // values on the stack, all other location types account for at most
237 // one value on the stack. Subtract the number that are actually
239 const LocationCode locCode
= LocationCode(*vec
++);
240 count
+= numLocationCodeStackVals(locCode
);
241 const int numLocImms
= numLocationCodeImms(locCode
);
242 for (int i
= 0; i
< numLocImms
; ++i
) {
243 decodeVariableSizeImm(&vec
);
246 // Count each of the members; MEC and MPC account for one value on
247 // the stack, while MW/MEL/MPL/MET/MPT/MEI don't account for any
248 // values on the stack.
249 while (vec
- &immVec
[0] < int(immVec
.size())) {
250 MemberCode code
= MemberCode(*vec
++);
251 if (memberCodeHasImm(code
)) {
252 decodeMemberCodeImm(&vec
, code
);
253 } else if (code
!= MW
) {
257 assert(vec
- &immVec
[0] == int(immVec
.size()));
261 #define O(name, imm, pop, push, flags) \
262 void Emitter::name(imm) { \
263 const Opcode opcode = Op##name; \
264 Offset curPos UNUSED = getUnitEmitter().bcPos(); \
265 getEmitterVisitor().prepareEvalStack(); \
267 const int nIn UNUSED = COUNT_##pop; \
270 getUnitEmitter().emitOp(Op##name); \
272 getUnitEmitter().recordSourceLocation(m_tempLoc ? m_tempLoc.get() : \
273 m_node->getLocation().get(), curPos); \
274 if (flags & TF) getEmitterVisitor().restoreJumpTargetEvalStack(); \
275 if (isFCallStar(opcode)) getEmitterVisitor().recordCall(); \
276 getEmitterVisitor().setPrevOpcode(opcode); \
280 #define COUNT_ONE(t) 1
281 #define COUNT_TWO(t1,t2) 2
282 #define COUNT_THREE(t1,t2,t3) 3
283 #define COUNT_FOUR(t1,t2,t3,t4) 4
284 #define COUNT_LMANY() 0
285 #define COUNT_C_LMANY() 0
286 #define COUNT_V_LMANY() 0
287 #define COUNT_FMANY 0
288 #define COUNT_CMANY 0
292 #define TWO(t1, t2) \
293 DEC_##t1 a1, DEC_##t2 a2
294 #define THREE(t1, t2, t3) \
295 DEC_##t1 a1, DEC_##t2 a2, DEC_##t3 a3
296 #define FOUR(t1, t2, t3, t4) \
297 DEC_##t1 a1, DEC_##t2 a2, DEC_##t3 a3, DEC_##t4 a4
299 #define DEC_MA std::vector<uchar>
300 #define DEC_BLA std::vector<Label*>&
301 #define DEC_SLA std::vector<StrOff>&
302 #define DEC_IVA int32_t
303 #define DEC_HA int32_t
304 #define DEC_IA int32_t
305 #define DEC_I64A int64_t
306 #define DEC_DA double
307 #define DEC_SA const StringData*
308 #define DEC_AA ArrayData*
309 #define DEC_BA Label&
315 #define POP_TWO(t1, t2) \
318 #define POP_THREE(t1, t2, t3) \
322 #define POP_FOUR(t1, t2, t3, t4) \
327 #define POP_LMANY() \
328 getEmitterVisitor().popEvalStackLMany()
329 #define POP_C_LMANY() \
330 getEmitterVisitor().popEvalStack(StackSym::C); \
331 getEmitterVisitor().popEvalStackLMany()
332 #define POP_V_LMANY() \
333 getEmitterVisitor().popEvalStack(StackSym::V); \
334 getEmitterVisitor().popEvalStackLMany()
336 getEmitterVisitor().popEvalStackMany(a1, StackSym::F)
338 getEmitterVisitor().popEvalStackMany(a1, StackSym::C)
340 #define POP_CV(i) getEmitterVisitor().popEvalStack(StackSym::C, i, curPos)
341 #define POP_VV(i) getEmitterVisitor().popEvalStack(StackSym::V, i, curPos)
342 #define POP_AV(i) getEmitterVisitor().popEvalStack(StackSym::A, i, curPos)
343 #define POP_RV(i) getEmitterVisitor().popEvalStack(StackSym::R, i, curPos)
344 #define POP_FV(i) getEmitterVisitor().popEvalStack(StackSym::F, i, curPos)
346 // Pop of virtual "locs" on the stack that turn into immediates.
347 #define POP_HA_ONE(t) \
349 #define POP_HA_TWO(t1, t2) \
352 #define POP_HA_THREE(t1, t2, t3) \
356 #define POP_HA_FOUR(t1, t2, t3, t4) \
364 #define POP_HA_BLA(i)
365 #define POP_HA_SLA(i)
366 #define POP_HA_IVA(i)
368 #define POP_HA_I64A(i)
375 #define POP_HA_HA(i) \
376 getEmitterVisitor().popSymbolicLocal(opcode, i, curPos)
379 #define PUSH_ONE(t) \
381 #define PUSH_TWO(t1, t2) \
384 #define PUSH_THREE(t1, t2, t3) \
388 #define PUSH_FOUR(t1, t2, t3, t4) \
393 #define PUSH_INS_1(t) PUSH_INS_1_##t
394 #define PUSH_INS_2(t) PUSH_INS_2_##t
396 #define PUSH_CV getEmitterVisitor().pushEvalStack(StackSym::C)
397 #define PUSH_VV getEmitterVisitor().pushEvalStack(StackSym::V)
398 #define PUSH_AV getEmitterVisitor().pushEvalStack(StackSym::A)
399 #define PUSH_RV getEmitterVisitor().pushEvalStack(StackSym::R)
400 #define PUSH_FV getEmitterVisitor().pushEvalStack(StackSym::F)
402 #define PUSH_INS_1_CV \
403 getEmitterVisitor().getEvalStack().insertAt(1, StackSym::C);
404 #define PUSH_INS_1_AV \
405 getEmitterVisitor().getEvalStack().insertAt(1, StackSym::A);
407 #define PUSH_INS_2_CV \
408 getEmitterVisitor().getEvalStack().insertAt(2, StackSym::C);
411 #define IMPL_ONE(t) \
413 #define IMPL_TWO(t1, t2) \
416 #define IMPL_THREE(t1, t2, t3) \
420 #define IMPL_FOUR(t1, t2, t3, t4) \
426 #define IMPL_MA(var) do { \
427 getUnitEmitter().emitInt32(var.size()); \
428 getUnitEmitter().emitInt32(countStackValues(var)); \
429 for (unsigned int i = 0; i < var.size(); ++i) { \
430 getUnitEmitter().emitByte(var[i]); \
433 #define IMPL1_MA IMPL_MA(a1)
434 #define IMPL2_MA IMPL_MA(a2)
435 #define IMPL3_MA IMPL_MA(a3)
436 #define IMPL4_MA IMPL_MA(a4)
438 #define IMPL_BLA(var) do { \
439 getUnitEmitter().emitInt32(var.size()); \
440 for (unsigned int i = 0; i < var.size(); ++i) { \
444 #define IMPL1_BLA IMPL_BLA(a1)
445 #define IMPL2_BLA IMPL_BLA(a2)
446 #define IMPL3_BLA IMPL_BLA(a3)
447 #define IMPL4_BLA IMPL_BLA(a4)
449 #define IMPL_SLA(var) do { \
450 auto& ue = getUnitEmitter(); \
451 ue.emitInt32(var.size()); \
452 for (auto& i : var) { \
453 ue.emitInt32(i.str); \
457 #define IMPL1_SLA IMPL_SLA(a1)
458 #define IMPL2_SLA IMPL_SLA(a2)
459 #define IMPL3_SLA IMPL_SLA(a3)
461 #define IMPL_IVA(var) do { \
462 getUnitEmitter().emitIVA(var); \
464 #define IMPL1_IVA IMPL_IVA(a1)
465 #define IMPL2_IVA IMPL_IVA(a2)
466 #define IMPL3_IVA IMPL_IVA(a3)
467 #define IMPL4_IVA IMPL_IVA(a4)
469 #define IMPL1_HA IMPL_IVA(a1)
470 #define IMPL2_HA IMPL_IVA(a2)
471 #define IMPL3_HA IMPL_IVA(a3)
472 #define IMPL4_HA IMPL_IVA(a4)
474 #define IMPL1_IA IMPL_IVA(a1)
475 #define IMPL2_IA IMPL_IVA(a2)
476 #define IMPL3_IA IMPL_IVA(a3)
477 #define IMPL4_IA IMPL_IVA(a4)
479 #define IMPL_I64A(var) getUnitEmitter().emitInt64(var)
480 #define IMPL1_I64A IMPL_I64A(a1)
481 #define IMPL2_I64A IMPL_I64A(a2)
482 #define IMPL3_I64A IMPL_I64A(a3)
483 #define IMPL4_I64A IMPL_I64A(a4)
485 #define IMPL_SA(var) \
486 getUnitEmitter().emitInt32(getUnitEmitter().mergeLitstr(var))
487 #define IMPL1_SA IMPL_SA(a1)
488 #define IMPL2_SA IMPL_SA(a2)
489 #define IMPL3_SA IMPL_SA(a3)
490 #define IMPL4_SA IMPL_SA(a4)
492 #define IMPL_AA(var) \
493 getUnitEmitter().emitInt32(getUnitEmitter().mergeArray(var))
494 #define IMPL1_AA IMPL_AA(a1)
495 #define IMPL2_AA IMPL_AA(a2)
496 #define IMPL3_AA IMPL_AA(a3)
497 #define IMPL4_AA IMPL_AA(a4)
499 #define IMPL_DA(var) getUnitEmitter().emitDouble(var)
500 #define IMPL1_DA IMPL_DA(a1)
501 #define IMPL2_DA IMPL_DA(a2)
502 #define IMPL3_DA IMPL_DA(a3)
503 #define IMPL4_DA IMPL_DA(a4)
505 #define IMPL_BA(var) \
506 if ((var).getAbsoluteOffset() == InvalidAbsoluteOffset) { \
507 /* For forward jumps, we store information about the */ \
508 /* current instruction in the Label. When the Label is */ \
509 /* set, it will fix up any instructions that reference */ \
510 /* it, and then it will call recordJumpTarget */ \
511 (var).bind(getEmitterVisitor(), curPos, getUnitEmitter().bcPos()); \
513 /* For backward jumps, we simply call recordJumpTarget */ \
514 getEmitterVisitor().recordJumpTarget((var).getAbsoluteOffset()); \
516 getUnitEmitter().emitInt32((var).getAbsoluteOffset() - curPos);
517 #define IMPL1_BA IMPL_BA(a1)
518 #define IMPL2_BA IMPL_BA(a2)
519 #define IMPL3_BA IMPL_BA(a3)
520 #define IMPL4_BA IMPL_BA(a4)
522 #define IMPL_OA(var) getUnitEmitter().emitByte(var)
523 #define IMPL1_OA IMPL_OA(a1)
524 #define IMPL2_OA IMPL_OA(a2)
525 #define IMPL3_OA IMPL_OA(a3)
526 #define IMPL4_OA IMPL_OA(a4)
651 static void checkJmpTargetEvalStack(const SymbolicStack
& source
,
652 const SymbolicStack
& dest
) {
653 if (source
.size() != dest
.size()) {
654 Logger::Warning("Emitter detected a point in the bytecode where the "
655 "depth of the stack is not the same for all possible "
656 "control flow paths. source size: %d. dest size: %d",
659 Logger::Warning("src stack : %s", source
.pretty().c_str());
660 Logger::Warning("dest stack: %s", dest
.pretty().c_str());
665 for (unsigned int i
= 0; i
< source
.size(); ++i
) {
666 char flavor
= StackSym::GetSymFlavor(source
.get(i
));
667 bool matches
= source
.get(i
) == dest
.get(i
) &&
668 (flavor
!= StackSym::L
|| source
.getLoc(i
) == dest
.getLoc(i
)) &&
669 (flavor
!= StackSym::T
|| source
.getName(i
) == dest
.getName(i
)) &&
670 (flavor
!= StackSym::I
|| source
.getInt(i
) == dest
.getInt(i
));
672 Logger::Warning("Emitter detected a point in the bytecode where the "
673 "symbolic flavor of a slot on the stack is not the same "
674 "for all possible control flow paths");
675 Logger::Warning("src stack : %s", source
.pretty().c_str());
676 Logger::Warning("dest stack: %s", dest
.pretty().c_str());
683 std::string
SymbolicStack::pretty() const {
684 std::ostringstream out
;
685 out
<< " [" << std::hex
;
687 for (size_t i
= 0; i
< m_symStack
.size(); ++i
) {
688 while (j
< m_actualStack
.size() && m_actualStack
[j
] < int(i
)) {
691 if (j
< m_actualStack
.size() && m_actualStack
[j
] == int(i
)) {
694 out
<< StackSym::ToString(m_symStack
[i
].sym
);
695 char flavor
= StackSym::GetSymFlavor(m_symStack
[i
].sym
);
696 if (flavor
== StackSym::L
|| flavor
== StackSym::I
) {
697 out
<< ":" << m_symStack
[i
].intval
;
698 } else if (flavor
== StackSym::T
) {
699 out
<< ":" << m_symStack
[i
].metaData
.name
->data();
706 void SymbolicStack::push(char sym
) {
707 if (sym
!= StackSym::W
&& sym
!= StackSym::K
&& sym
!= StackSym::L
&&
708 sym
!= StackSym::T
&& sym
!= StackSym::I
&& sym
!= StackSym::H
) {
709 m_actualStack
.push_back(m_symStack
.size());
710 *m_actualStackHighWaterPtr
= MAX(*m_actualStackHighWaterPtr
,
711 (int)m_actualStack
.size());
713 m_symStack
.push_back(SymEntry(sym
));
716 void SymbolicStack::pop() {
717 // TODO(drew): assert eval stack unknown is false?
718 assert(!m_symStack
.empty());
719 char sym
= m_symStack
.back().sym
;
720 char flavor
= StackSym::GetSymFlavor(sym
);
721 if (StackSym::GetMarker(sym
) != StackSym::W
&&
722 flavor
!= StackSym::L
&& flavor
!= StackSym::T
&& flavor
!= StackSym::I
&&
723 flavor
!= StackSym::H
) {
724 assert(!m_actualStack
.empty());
725 m_actualStack
.pop_back();
727 m_symStack
.pop_back();
730 char SymbolicStack::top() const {
731 assert(!m_symStack
.empty());
732 return m_symStack
.back().sym
;
735 char SymbolicStack::get(int index
) const {
736 assert(index
>= 0 && index
< (int)m_symStack
.size());
737 return m_symStack
[index
].sym
;
740 const StringData
* SymbolicStack::getName(int index
) const {
741 assert(index
>= 0 && index
< (int)m_symStack
.size());
742 if (m_symStack
[index
].metaType
== META_LITSTR
) {
743 return m_symStack
[index
].metaData
.name
;
748 const StringData
* SymbolicStack::getClsName(int index
) const {
749 assert(index
>= 0 && index
< (int)m_symStack
.size());
750 return m_symStack
[index
].className
;
753 bool SymbolicStack::isCls(int index
) const {
754 assert(index
>= 0 && index
< (int)m_symStack
.size());
755 return m_symStack
[index
].className
!= nullptr;
758 void SymbolicStack::setString(const StringData
* s
) {
759 assert(m_symStack
.size());
760 SymEntry
& se
= m_symStack
.back();
761 if (se
.metaType
== META_LITSTR
) {
762 assert(se
.metaData
.name
== s
);
764 assert(se
.metaType
== META_NONE
);
766 se
.metaData
.name
= s
;
767 se
.metaType
= META_LITSTR
;
770 void SymbolicStack::setKnownCls(const StringData
* s
, bool nonNull
) {
771 assert(m_symStack
.size());
772 SymEntry
& se
= m_symStack
.back();
773 assert(!se
.className
|| se
.className
== s
);
774 if (se
.metaType
== META_DATA_TYPE
) {
775 assert(se
.metaData
.dt
== KindOfObject
);
779 se
.notNull
= se
.notNull
|| nonNull
;
782 void SymbolicStack::setNotRef() {
783 assert(m_symStack
.size());
784 SymEntry
& se
= m_symStack
.back();
788 bool SymbolicStack::getNotRef() const {
789 assert(m_symStack
.size());
790 const SymEntry
& se
= m_symStack
.back();
794 void SymbolicStack::setInt(int64_t v
) {
795 assert(m_symStack
.size());
796 m_symStack
.back().intval
= v
;
799 void SymbolicStack::setKnownType(DataType dt
, bool predicted
/* = false */) {
800 assert(m_symStack
.size());
801 SymEntry
& se
= m_symStack
.back();
803 assert(dt
== KindOfObject
);
806 assert(se
.metaType
== META_NONE
);
807 se
.metaType
= META_DATA_TYPE
;
810 se
.dtPredicted
= predicted
;
813 DataType
SymbolicStack::getKnownType(int index
, bool noRef
) const {
814 if (index
< 0) index
+= m_symStack
.size();
815 assert((unsigned)index
< m_symStack
.size());
816 const SymEntry
& se
= m_symStack
[index
];
817 if (!noRef
|| se
.notRef
) {
818 if (se
.className
&& se
.notNull
) {
820 } else if (se
.metaType
== META_DATA_TYPE
) {
821 return se
.metaData
.dt
;
824 return KindOfUnknown
;
827 bool SymbolicStack::isTypePredicted(int index
/* = -1, stack top */) const {
828 if (index
< 0) index
+= m_symStack
.size();
829 assert((unsigned)index
< m_symStack
.size());
830 return m_symStack
[index
].dtPredicted
;
833 void SymbolicStack::cleanTopMeta() {
834 SymEntry
& se
= m_symStack
.back();
835 se
.clsBaseType
= CLS_INVALID
;
836 se
.metaType
= META_NONE
;
840 void SymbolicStack::setClsBaseType(ClassBaseType type
) {
841 assert(!m_symStack
.empty());
842 m_symStack
.back().clsBaseType
= type
;
845 void SymbolicStack::setUnnamedLocal(int index
,
848 assert(size_t(index
) < m_symStack
.size());
849 assert(m_symStack
[index
].sym
== StackSym::K
);
850 assert(m_symStack
[index
].clsBaseType
== CLS_UNNAMED_LOCAL
);
851 m_symStack
[index
].intval
= localId
;
852 m_symStack
[index
].unnamedLocalStart
= startOff
;
855 void SymbolicStack::set(int index
, char sym
) {
856 assert(index
>= 0 && index
< (int)m_symStack
.size());
857 // XXX Add assert in debug build to make sure W is not getting
858 // written or overwritten by something else
859 m_symStack
[index
].sym
= sym
;
862 unsigned SymbolicStack::size() const {
863 return m_symStack
.size();
866 bool SymbolicStack::empty() const {
867 return m_symStack
.empty();
870 void SymbolicStack::clear() {
872 m_actualStack
.clear();
876 void SymbolicStack::consumeBelowTop(int depth
) {
877 if (int(m_symStack
.size()) < depth
+ 1) {
879 "Emitter tried to consumeBelowTop() when the symbolic "
880 "stack did not have enough elements in it.");
884 assert(int(m_symStack
.size()) >= depth
+ 1);
885 int index
= m_symStack
.size() - depth
- 1;
886 m_symStack
.erase(m_symStack
.begin() + index
);
889 * Update any indexes into the actual stack that pointed to or past
892 * (In practice they should all be past---we don't currently ever
893 * remove below the top for actual stack elements.)
895 for (size_t i
= 0; i
< m_actualStack
.size(); ++i
) {
896 if (m_actualStack
[i
] >= index
) {
902 int SymbolicStack::getActualPos(int vpos
) const {
903 assert(vpos
>= 0 && vpos
< int(m_symStack
.size()));
904 assert(!m_actualStack
.empty());
905 for (int j
= int(m_actualStack
.size()) - 1; j
>= 0; --j
) {
906 if (m_actualStack
[j
] == vpos
) {
913 char SymbolicStack::getActual(int index
) const {
914 assert(index
>= 0 && index
< (int)m_actualStack
.size());
915 return get(m_actualStack
[index
]);
918 void SymbolicStack::setActual(int index
, char sym
) {
919 assert(index
>= 0 && index
< (int)m_actualStack
.size());
920 set(m_actualStack
[index
], sym
);
923 SymbolicStack::ClassBaseType
924 SymbolicStack::getClsBaseType(int index
) const {
925 assert(m_symStack
.size() > size_t(index
));
926 assert(m_symStack
[index
].sym
== StackSym::K
);
927 assert(m_symStack
[index
].clsBaseType
!= CLS_INVALID
);
928 return m_symStack
[index
].clsBaseType
;
931 int SymbolicStack::getLoc(int index
) const {
932 assert(m_symStack
.size() > size_t(index
));
933 assert(StackSym::GetSymFlavor(m_symStack
[index
].sym
) == StackSym::L
||
934 m_symStack
[index
].clsBaseType
== CLS_NAMED_LOCAL
||
935 m_symStack
[index
].clsBaseType
== CLS_UNNAMED_LOCAL
);
936 assert(m_symStack
[index
].intval
!= -1);
937 return m_symStack
[index
].intval
;
940 int64_t SymbolicStack::getInt(int index
) const {
941 assert(m_symStack
.size() > size_t(index
));
942 assert(StackSym::GetSymFlavor(m_symStack
[index
].sym
) == StackSym::I
);
943 return m_symStack
[index
].intval
;
946 Offset
SymbolicStack::getUnnamedLocStart(int index
) const {
947 assert(m_symStack
.size() > size_t(index
));
948 assert(m_symStack
[index
].sym
== StackSym::K
);
949 assert(m_symStack
[index
].clsBaseType
== CLS_UNNAMED_LOCAL
);
950 return m_symStack
[index
].unnamedLocalStart
;
953 // Insert an element in the actual stack at the specified depth of the
955 void SymbolicStack::insertAt(int depth
, char sym
) {
956 assert(depth
<= sizeActual() && depth
> 0);
957 int virtIdx
= m_actualStack
[sizeActual() - depth
];
959 m_symStack
.insert(m_symStack
.begin() + virtIdx
, SymEntry(sym
));
960 m_actualStack
.insert(m_actualStack
.end() - depth
, virtIdx
);
962 for (size_t i
= sizeActual() - depth
+ 1; i
< m_actualStack
.size(); ++i
) {
967 int SymbolicStack::sizeActual() const {
968 return m_actualStack
.size();
971 void SymbolicStack::pushFDesc() {
972 m_fdescCount
+= kNumActRecCells
;
973 *m_fdescHighWaterPtr
= MAX(*m_fdescHighWaterPtr
, m_fdescCount
);
976 void SymbolicStack::popFDesc() {
977 m_fdescCount
-= kNumActRecCells
;
980 void Label::set(Emitter
& e
) {
983 "Label::set was called more than once on the same "
984 "Label; originally set to %d; now %d",
986 e
.getUnitEmitter().bcPos());
989 m_off
= e
.getUnitEmitter().bcPos();
990 // Fix up any forward jumps that reference to this Label
991 for (std::vector
<std::pair
<Offset
, Offset
> >::const_iterator it
=
992 m_emittedOffs
.begin(); it
!= m_emittedOffs
.end(); ++it
) {
993 e
.getUnitEmitter().emitInt32(m_off
- it
->first
, it
->second
);
995 EmitterVisitor
& ev
= e
.getEmitterVisitor();
996 if (!m_emittedOffs
.empty()) {
997 // If there were forward jumps that referenced this Label,
998 // compare the the eval stack from the first foward jump we
999 // saw with the current eval stack
1000 if (!ev
.evalStackIsUnknown()) {
1001 checkJmpTargetEvalStack(m_evalStack
, ev
.getEvalStack());
1003 // Assume the current eval stack matches that of the forward branch
1004 ev
.setEvalStack(m_evalStack
);
1006 // Fix up the EmitterVisitor's table of jump targets
1007 ev
.recordJumpTarget(m_off
, ev
.getEvalStack());
1009 // There were no forward jumps that referenced this label
1010 ev
.prepareEvalStack();
1011 // Fix up the EmitterVisitor's table of jump targets
1012 ev
.recordJumpTarget(m_off
, ev
.getEvalStack());
1016 bool Label::isUsed() {
1017 return (m_off
!= InvalidAbsoluteOffset
|| !m_emittedOffs
.empty());
1020 void Label::bind(EmitterVisitor
& ev
, Offset instrAddr
, Offset offAddr
) {
1021 if (m_off
!= InvalidAbsoluteOffset
) {
1022 InvariantViolation("Label::bind was called on a Label that has already "
1027 bool labelHasEvalStack
= !m_emittedOffs
.empty();
1028 m_emittedOffs
.push_back(std::pair
<Offset
, Offset
>(instrAddr
, offAddr
));
1029 if (labelHasEvalStack
) {
1030 checkJmpTargetEvalStack(m_evalStack
, ev
.getEvalStack());
1032 m_evalStack
= ev
.getEvalStack();
1036 struct FPIRegionRecorder
{
1037 FPIRegionRecorder(EmitterVisitor
* ev
, UnitEmitter
& ue
, SymbolicStack
& stack
,
1039 : m_ev(ev
), m_ue(ue
), m_stack(stack
), m_fpOff(m_stack
.sizeActual()),
1041 m_stack
.pushFDesc();
1043 ~FPIRegionRecorder() {
1045 m_ev
->newFPIRegion(m_start
, m_ue
.bcPos(), m_fpOff
);
1048 EmitterVisitor
* m_ev
;
1050 SymbolicStack
& m_stack
;
1055 //=============================================================================
1058 void MetaInfoBuilder::add(int pos
, Unit::MetaInfo::Kind kind
,
1059 bool mVector
, int arg
, Id data
) {
1061 if (arg
> 127) return;
1062 if (mVector
) arg
|= Unit::MetaInfo::VectorArg
;
1063 Vec
& info
= m_metaMap
[pos
];
1064 int i
= info
.size();
1065 if (kind
== Unit::MetaInfo::NopOut
) {
1067 } else if (i
== 1 && info
[0].m_kind
== Unit::MetaInfo::NopOut
) {
1069 } else if (kind
== Unit::MetaInfo::DataTypeInferred
||
1070 kind
== Unit::MetaInfo::DataTypePredicted
) {
1071 // Put DataType first, because if applyInputMetaData saw Class
1072 // first, it would call recordRead which mark the input as
1073 // needing a guard before we saw the DataType
1076 info
.insert(info
.begin() + i
, Unit::MetaInfo(kind
, arg
, data
));
1079 void MetaInfoBuilder::addKnownDataType(DataType dt
,
1084 if (dt
!= KindOfUnknown
) {
1085 Unit::MetaInfo::Kind dtKind
= (dtPredicted
?
1086 Unit::MetaInfo::DataTypePredicted
:
1087 Unit::MetaInfo::DataTypeInferred
);
1088 add(pos
, dtKind
, mVector
, arg
, dt
);
1092 void MetaInfoBuilder::deleteInfo(Offset bcOffset
) {
1093 m_metaMap
.erase(bcOffset
);
1096 void MetaInfoBuilder::setForUnit(UnitEmitter
& target
) const {
1097 int entries
= m_metaMap
.size();
1098 if (!entries
) return;
1100 vector
<Offset
> index1
;
1101 vector
<Offset
> index2
;
1102 vector
<uint8_t> data
;
1103 index1
.push_back(entries
);
1105 size_t sz1
= (2 + entries
) * sizeof(Offset
);
1106 size_t sz2
= (1 + entries
) * sizeof(Offset
);
1107 for (Map::const_iterator it
= m_metaMap
.begin(), end
= m_metaMap
.end();
1109 index1
.push_back(it
->first
);
1110 index2
.push_back(sz1
+ sz2
+ data
.size());
1112 const Vec
& v
= it
->second
;
1114 for (unsigned i
= 0; i
< v
.size(); i
++) {
1115 const Unit::MetaInfo
& mi
= v
[i
];
1116 data
.push_back(mi
.m_kind
);
1117 data
.push_back(mi
.m_arg
);
1118 if (mi
.m_data
< 0x80) {
1119 data
.push_back(mi
.m_data
<< 1);
1125 u
.val
= (mi
.m_data
<< 1) | 1;
1126 for (int j
= 0; j
< 4; j
++) {
1127 data
.push_back(u
.bytes
[j
]);
1132 index1
.push_back(INT_MAX
);
1133 index2
.push_back(sz1
+ sz2
+ data
.size());
1135 size_t size
= sz1
+ sz2
+ data
.size();
1136 uint8_t* meta
= (uint8_t*)malloc(size
);
1137 memcpy(meta
, &index1
[0], sz1
);
1138 memcpy(meta
+ sz1
, &index2
[0], sz2
);
1139 memcpy(meta
+ sz1
+ sz2
, &data
[0], data
.size());
1140 target
.setBcMeta(meta
, size
);
1144 EmitterVisitor::EmitterVisitor(UnitEmitter
& ue
)
1145 : m_ue(ue
), m_curFunc(ue
.getMain()), m_evalStackIsUnknown(false),
1146 m_actualStackHighWater(0), m_fdescHighWater(0), m_closureCounter(0) {
1147 m_prevOpcode
= OpLowInvalid
;
1148 m_evalStack
.m_actualStackHighWaterPtr
= &m_actualStackHighWater
;
1149 m_evalStack
.m_fdescHighWaterPtr
= &m_fdescHighWater
;
1152 EmitterVisitor::~EmitterVisitor() {
1153 for (LabelMap::const_iterator it
= m_methLabels
.begin();
1154 it
!= m_methLabels
.end(); it
++) {
1157 // If a fatal occurs during emission, some extra cleanup is necessary.
1158 for (std::deque
<ExnHandlerRegion
*>::const_iterator it
= m_exnHandlers
.begin();
1159 it
!= m_exnHandlers
.end(); ++it
) {
1164 bool EmitterVisitor::checkIfStackEmpty(const char* forInstruction
) const {
1165 if (m_evalStack
.empty()) {
1166 InvariantViolation("Emitter tried to emit a %s instruction when the "
1167 "evaluation stack is empty (at offset %d)",
1175 void EmitterVisitor::unexpectedStackSym(char sym
, const char* where
) const {
1176 InvariantViolation("Emitter encountered an unexpected StackSym \"%s\""
1177 " in %s() (at offset %d)",
1178 StackSym::ToString(sym
).c_str(),
1183 void EmitterVisitor::popEvalStack(char expected
, int arg
, int pos
) {
1184 // Pop a value off of the evaluation stack, and verify that it
1185 // matches the specified symbolic flavor
1186 if (m_evalStack
.size() == 0) {
1187 InvariantViolation("Emitter emitted an instruction that tries to consume "
1188 "a value from the stack when the stack is empty "
1189 "(expected symbolic flavor \"%s\" at offset %d)",
1190 StackSym::ToString(expected
).c_str(),
1195 if (arg
>= 0 && pos
>= 0 &&
1196 (expected
== StackSym::C
|| expected
== StackSym::R
)) {
1197 m_metaInfo
.addKnownDataType(m_evalStack
.getKnownType(),
1198 m_evalStack
.isTypePredicted(),
1202 char sym
= m_evalStack
.top();
1203 char actual
= StackSym::GetSymFlavor(sym
);
1205 if (actual
!= expected
) {
1207 "Emitter emitted an instruction that tries to consume a "
1208 "value from the stack when the top of the stack does not "
1209 "match the symbolic flavor that the instruction expects "
1210 "(expected symbolic flavor \"%s\", actual symbolic flavor \"%s\" "
1212 StackSym::ToString(expected
).c_str(),
1213 StackSym::ToString(actual
).c_str(),
1218 void EmitterVisitor::popSymbolicLocal(Opcode op
, int arg
, int pos
) {
1219 // Special case for instructions that consume the loc below the
1222 if (op
== OpCGetL3
) {
1224 } else if (op
== OpCGetL2
) {
1228 if (belowTop
!= -1) {
1229 char symFlavor
= StackSym::GetSymFlavor(
1230 m_evalStack
.get(m_evalStack
.size() - belowTop
));
1231 if (symFlavor
!= StackSym::L
) {
1232 InvariantViolation("Operation tried to remove a local below the top of"
1233 " the symbolic stack but instead found \"%s\"",
1234 StackSym::ToString(symFlavor
).c_str());
1236 m_evalStack
.consumeBelowTop(belowTop
- 1);
1238 if (arg
>= 0 && pos
>= 0) {
1239 m_metaInfo
.addKnownDataType(m_evalStack
.getKnownType(),
1240 m_evalStack
.isTypePredicted(),
1243 popEvalStack(StackSym::L
);
1247 void EmitterVisitor::popEvalStackLMany() {
1248 while (!m_evalStack
.empty()) {
1249 char sym
= m_evalStack
.top();
1250 char symFlavor
= StackSym::GetSymFlavor(sym
);
1251 char marker
= StackSym::GetMarker(sym
);
1252 if (marker
== StackSym::E
|| marker
== StackSym::P
) {
1253 if (symFlavor
!= StackSym::C
&& symFlavor
!= StackSym::L
&&
1254 symFlavor
!= StackSym::T
&& symFlavor
!= StackSym::I
) {
1256 "Emitter emitted an instruction that tries to consume "
1257 "a value from the stack when the top of the stack "
1258 "does not match the symbolic flavor that the instruction "
1259 "expects (expected symbolic flavor \"C\", \"L\", \"T\", or \"I\", "
1260 "actual symbolic flavor \"%s\" at offset %d)",
1261 StackSym::ToString(symFlavor
).c_str(),
1264 } else if (marker
== StackSym::W
) {
1265 if (symFlavor
!= StackSym::None
) {
1267 "Emitter emitted an instruction that tries to consume "
1268 "a value from the stack when the top of the stack "
1269 "does not match the symbolic flavor that the instruction "
1270 "expects (expected symbolic flavor \"None\", actual "
1271 "symbolic flavor \"%s\" at offset %d)",
1272 StackSym::ToString(symFlavor
).c_str(),
1275 } else if (marker
== StackSym::M
) {
1276 assert(symFlavor
== StackSym::A
);
1283 if (m_evalStack
.empty()) {
1284 InvariantViolation("Emitter emitted an instruction that tries to consume "
1285 "a value from the stack when the stack is empty "
1290 char sym
= m_evalStack
.top();
1291 char symFlavor
= StackSym::GetSymFlavor(sym
);
1293 if (symFlavor
!= StackSym::C
&& symFlavor
!= StackSym::L
&&
1294 symFlavor
!= StackSym::R
&& symFlavor
!= StackSym::H
) {
1296 "Emitter emitted an instruction that tries to consume a "
1297 "value from the stack when the top of the stack does not "
1298 "match the symbolic flavor that the instruction expects "
1299 "(expected symbolic flavor \"C\", \"L\", \"R\", or \"H\", actual "
1300 "symbolic flavor \"%s\" at offset %d)",
1301 StackSym::ToString(symFlavor
).c_str(),
1306 void EmitterVisitor::popEvalStackMany(int len
, char symFlavor
) {
1307 for (int i
= 0; i
< len
; ++i
) {
1308 popEvalStack(symFlavor
);
1312 void EmitterVisitor::pushEvalStack(char symFlavor
) {
1313 // Push a value from the evaluation stack with the specified
1315 m_evalStack
.push(symFlavor
);
1318 void EmitterVisitor::peekEvalStack(char expected
, int depthActual
) {
1319 int posActual
= (m_evalStack
.sizeActual() - depthActual
- 1);
1320 if (posActual
>= 0 && posActual
< (int)m_evalStack
.sizeActual()) {
1321 char sym
= m_evalStack
.getActual(posActual
);
1322 char actual
= StackSym::GetSymFlavor(sym
);
1323 if (actual
!= expected
) {
1325 "Emitter emitted an instruction that tries to consume a "
1326 "value from the stack whose symbolic flavor does not match "
1327 "the symbolic flavor that the instruction expects (expected "
1328 "symbolic flavor \"%s\", actual symbolic flavor \"%s\" at "
1330 StackSym::ToString(expected
).c_str(),
1331 StackSym::ToString(actual
).c_str(),
1337 void EmitterVisitor::pokeEvalStack(char symFlavor
, int depthActual
) {
1338 int sizeActual
= m_evalStack
.sizeActual();
1339 int posActual
= sizeActual
- depthActual
- 1;
1340 if (posActual
>= 0 && posActual
< sizeActual
) {
1341 m_evalStack
.setActual(posActual
, symFlavor
);
1346 * Prior to making any changes to the evaluation stack in between
1347 * instructions, this function should be called.
1349 * What this handles is recording the evaluation stack state at
1350 * instruction boundaries so that jumps know what their stack state
1351 * will be at the destination.
1353 * When m_evalStackIsUnknown, it means we have hit a place in the
1354 * bytecode where the offset cannot be reached via fallthrough, but no
1355 * forward jumps targeted it either. In this case, the stack must be
1356 * empty, and we need to record that this is the case so that later
1357 * backward jumps can check this is the case at their jump site.
1359 void EmitterVisitor::prepareEvalStack() {
1360 if (m_evalStackIsUnknown
) {
1361 if (!m_evalStack
.empty()) {
1362 InvariantViolation("Emitter expected to have an empty evaluation "
1363 "stack because the eval stack was unknown, but "
1364 "it was non-empty.");
1367 // Record that we are assuming that the eval stack is empty
1368 recordJumpTarget(m_ue
.bcPos(), m_evalStack
);
1369 m_evalStackIsUnknown
= false;
1373 void EmitterVisitor::recordJumpTarget(Offset target
,
1374 const SymbolicStack
& evalStack
) {
1375 if (target
== InvalidAbsoluteOffset
) {
1377 "Offset passed to EmitterVisitor::recordJumpTarget was invalid");
1379 hphp_hash_map
<Offset
, SymbolicStack
>::iterator it
=
1380 m_jumpTargetEvalStacks
.find(target
);
1381 if (it
== m_jumpTargetEvalStacks
.end()) {
1382 m_jumpTargetEvalStacks
[target
] = evalStack
;
1385 checkJmpTargetEvalStack(evalStack
, it
->second
);
1388 void EmitterVisitor::restoreJumpTargetEvalStack() {
1389 m_evalStack
.clear();
1390 hphp_hash_map
<Offset
, SymbolicStack
>::iterator it
=
1391 m_jumpTargetEvalStacks
.find(m_ue
.bcPos());
1392 if (it
== m_jumpTargetEvalStacks
.end()) {
1393 m_evalStackIsUnknown
= true;
1396 m_evalStack
= it
->second
;
1399 void EmitterVisitor::recordCall() {
1400 m_curFunc
->setContainsCalls();
1403 bool EmitterVisitor::isJumpTarget(Offset target
) {
1404 // Returns true iff one of the following conditions is true:
1405 // 1) We have seen an instruction that jumps to the specified offset
1406 // 2) We know of a Label that has been set to the specified offset
1407 // 3) We have seen a try region that ends at the specified offset
1408 hphp_hash_map
<Offset
, SymbolicStack
>::iterator it
=
1409 m_jumpTargetEvalStacks
.find(target
);
1410 return (it
!= m_jumpTargetEvalStacks
.end());
1413 #define CONTROL_BODY(brk, cnt, brkH, cntH) \
1414 ControlTargetPusher _cop(this, -1, false, brk, cnt, brkH, cntH)
1415 #define FOREACH_BODY(itId, itRef, brk, cnt, brkH, cntH) \
1416 ControlTargetPusher _cop(this, itId, itRef, brk, cnt, brkH, cntH)
1418 class IterFreeThunklet
: public Thunklet
{
1420 IterFreeThunklet(Id iterId
, bool itRef
) : m_id(iterId
), m_itRef(itRef
) {}
1421 virtual void emit(Emitter
& e
) {
1435 * A thunklet for the fault region protecting a silenced (@) expression.
1437 class RestoreErrorReportingThunklet
: public Thunklet
{
1439 explicit RestoreErrorReportingThunklet(Id loc
) : m_oldLevelLoc(loc
) {}
1440 virtual void emit(Emitter
& e
) {
1441 e
.getEmitterVisitor().emitRestoreErrorReporting(e
, m_oldLevelLoc
);
1448 class UnsetUnnamedLocalThunklet
: public Thunklet
{
1450 explicit UnsetUnnamedLocalThunklet(Id loc
) : m_loc(loc
) {}
1451 virtual void emit(Emitter
& e
) {
1452 e
.getEmitterVisitor().emitVirtualLocal(m_loc
);
1453 e
.getEmitterVisitor().emitUnset(e
);
1461 * Helper to deal with emitting list assignment and keeping track of some
1462 * associated info. A list assignment can be thought of as a list of "index
1463 * chains"; that is, sequences of indices that should be accessed for each
1464 * bottom-level expression in the list assignment. We recursively walk down the
1465 * LHS, building up index chains and copying them into the top-level list as we
1466 * reach the leaves of the tree.
1468 void EmitterVisitor::visitListAssignmentLHS(Emitter
& e
, ExpressionPtr exp
,
1469 IndexChain
& indexChain
,
1470 std::vector
<IndexChain
*>& all
) {
1476 if (exp
->is(Expression::KindOfListAssignment
)) {
1477 // Nested assignment
1478 ListAssignmentPtr la
= static_pointer_cast
<ListAssignment
>(exp
);
1479 ExpressionListPtr lhs
= la
->getVariables();
1480 int n
= lhs
->getCount();
1481 for (int i
= 0; i
< n
; ++i
) {
1482 indexChain
.push_back(i
);
1483 visitListAssignmentLHS(e
, (*lhs
)[i
], indexChain
, all
);
1484 indexChain
.pop_back();
1487 // Reached a "leaf". Lock in this index chain and deal with this exp.
1488 assert(!indexChain
.empty());
1489 all
.push_back(new IndexChain(indexChain
));
1491 emitClsIfSPropBase(e
);
1496 * visitIfCondition() serves as a helper method for visiting the condition
1497 * of an "if" statement. This function recursively visits each node in an
1498 * expression, keeping track of where to jump if an "and" or "or" expression
1499 * short circuits. If an expression other than "and", "or", or "not" is
1500 * encountered, this method calls EmitterVisitor::visit() to handle the
1503 void EmitterVisitor::visitIfCondition(
1504 ExpressionPtr cond
, Emitter
& e
, Label
& tru
, Label
& fals
,
1505 bool truFallthrough
) {
1507 BinaryOpExpressionPtr
binOpNode(
1508 dynamic_pointer_cast
<BinaryOpExpression
>(cond
));
1511 int op
= binOpNode
->getOp();
1512 // Short circuit && and ||
1513 if (op
== T_LOGICAL_OR
|| op
== T_LOGICAL_AND
||
1514 op
== T_BOOLEAN_OR
|| op
== T_BOOLEAN_AND
) {
1515 bool isOr
= (op
== T_LOGICAL_OR
|| op
== T_BOOLEAN_OR
);
1517 ExpressionPtr lhs
= binOpNode
->getExp1();
1519 Emitter
lhsEmitter(lhs
, m_ue
, *this);
1520 visitIfCondition(lhs
, lhsEmitter
, tru
, localLabel
, false);
1521 // falls through if that condition was false
1523 Emitter
lhsEmitter(lhs
, m_ue
, *this);
1524 visitIfCondition(lhs
, lhsEmitter
, localLabel
, fals
, true);
1525 // falls through if that condition was true
1527 if (localLabel
.isUsed()) {
1530 if (currentPositionIsReachable()) {
1531 ExpressionPtr rhs
= binOpNode
->getExp2();
1532 Emitter
rhsEmitter(rhs
, m_ue
, *this);
1533 visitIfCondition(rhs
, rhsEmitter
, tru
, fals
, truFallthrough
);
1539 UnaryOpExpressionPtr
unOpNode(dynamic_pointer_cast
<UnaryOpExpression
>(cond
));
1541 int op
= unOpNode
->getOp();
1544 ExpressionPtr val
= unOpNode
->getExpression();
1545 Emitter
valEmitter(val
, m_ue
, *this);
1546 visitIfCondition(val
, valEmitter
, fals
, tru
, !truFallthrough
);
1552 if (cond
->getScalarValue(val
)) {
1553 if (truFallthrough
) {
1554 if (!val
.toBoolean()) e
.Jmp(fals
);
1556 if (val
.toBoolean()) e
.Jmp(tru
);
1562 emitConvertToCell(e
);
1563 if (truFallthrough
) {
1570 void EmitterVisitor::assignLocalVariableIds(FunctionScopePtr fs
) {
1571 VariableTablePtr variables
= fs
->getVariables();
1572 std::vector
<std::string
> localNames
;
1573 variables
->getLocalVariableNames(localNames
);
1574 for (int i
= 0; i
< (int)localNames
.size(); ++i
) {
1575 StringData
* nLiteral
= StringData::GetStaticString(localNames
[i
].c_str());
1576 m_curFunc
->allocVarId(nLiteral
);
1580 void EmitterVisitor::visit(FileScopePtr file
) {
1581 const std::string
& filename
= file
->getName();
1582 m_ue
.setFilepath(StringData::GetStaticString(filename
));
1584 FunctionScopePtr
func(file
->getPseudoMain());
1588 // Assign ids to all of the local variables eagerly. This gives us the
1589 // nice property that all named local variables will be assigned ids
1590 // 0 through k-1, while any unnamed local variable will have an id >= k.
1591 assignLocalVariableIds(func
);
1593 AnalysisResultPtr
ar(file
->getContainingProgram());
1595 MethodStatementPtr
m(dynamic_pointer_cast
<MethodStatement
>(func
->getStmt()));
1597 StatementListPtr
stmts(m
->getStmts());
1600 Emitter
e(m
, m_ue
, *this);
1603 Label ctFatalWithPop
;
1604 int i
, nk
= stmts
->getCount();
1605 CONTROL_BODY(ctFatal
, ctFatal
, ctFatalWithPop
, ctFatalWithPop
);
1606 for (i
= 0; i
< nk
; i
++) {
1607 StatementPtr s
= (*stmts
)[i
];
1608 if (MethodStatementPtr meth
= dynamic_pointer_cast
<MethodStatement
>(s
)) {
1609 // Create label for use with fast calls
1610 const StringData
* methName
=
1611 StringData::GetStaticString(meth
->getOriginalName());
1612 m_methLabels
[methName
] = new Label();
1614 postponeMeth(meth
, nullptr, true);
1618 FunctionScopePtr fsp
= m
->getFunctionScope();
1619 if (fsp
->needsLocalThis()) {
1620 static const StringData
* thisStr
= StringData::GetStaticString("this");
1621 Id thisId
= m_curFunc
->lookupVarId(thisStr
);
1622 e
.InitThisLoc(thisId
);
1624 FuncFinisher
ff(this, e
, m_curFunc
);
1625 TypedValue mainReturn
;
1626 mainReturn
.m_type
= KindOfInvalid
;
1627 bool notMergeOnly
= false;
1628 PreClass::Hoistable allHoistable
= PreClass::AlwaysHoistable
;
1629 for (i
= 0; i
< nk
; i
++) {
1630 StatementPtr s
= (*stmts
)[i
];
1631 switch (s
->getKindOf()) {
1632 case Statement::KindOfMethodStatement
:
1633 case Statement::KindOfFunctionStatement
:
1635 case Statement::KindOfInterfaceStatement
:
1636 case Statement::KindOfClassStatement
: {
1637 // Handle classes directly here, since only top-level classes are
1639 ClassScopePtr cNode
= s
->getClassScope();
1640 PreClass::Hoistable h
= emitClass(e
, cNode
, true);
1641 if (h
< allHoistable
) allHoistable
= h
;
1644 case Statement::KindOfTypedefStatement
:
1645 emitTypedef(e
, static_pointer_cast
<TypedefStatement
>(s
));
1646 notMergeOnly
= true; // TODO(#2103206): typedefs should be mergable
1648 case Statement::KindOfReturnStatement
:
1649 if (mainReturn
.m_type
!= KindOfInvalid
) break;
1651 tvWriteUninit(&mainReturn
);
1655 ReturnStatementPtr
r(static_pointer_cast
<ReturnStatement
>(s
));
1656 Variant
v(Variant::nullInit
);
1657 if (r
->getRetExp() &&
1658 !r
->getRetExp()->getScalarValue(v
)) {
1659 tvWriteUninit(&mainReturn
);
1663 v
= String(StringData::GetStaticString(v
.asCStrRef().get()));
1664 } else if (v
.isArray()) {
1665 v
= Array(ArrayData::GetScalarArray(v
.asCArrRef().get()));
1667 assert(!IS_REFCOUNTED_TYPE(v
.getType()));
1669 mainReturn
= *v
.getTypedAccessor();
1673 case Statement::KindOfExpStatement
:
1674 if (mainReturn
.m_type
== KindOfInvalid
) {
1676 static_pointer_cast
<ExpStatement
>(s
)->getExpression();
1677 switch (e
->getKindOf()) {
1678 case Expression::KindOfSimpleFunctionCall
: {
1679 SimpleFunctionCallPtr func
=
1680 static_pointer_cast
<SimpleFunctionCall
>(e
);
1683 if (func
->isSimpleDefine(&name
, &tv
)) {
1684 UnitMergeKind k
= func
->isDefineWithoutImpl(ar
) ?
1685 UnitMergeKindPersistentDefine
: UnitMergeKindDefine
;
1686 if (tv
.m_type
== KindOfUninit
) {
1687 tv
.m_type
= KindOfNull
;
1689 m_ue
.pushMergeableDef(k
, name
, tv
);
1695 case Expression::KindOfAssignmentExpression
: {
1696 AssignmentExpressionPtr
ae(
1697 static_pointer_cast
<AssignmentExpression
>(e
));
1700 if (ae
->isSimpleGlobalAssign(&name
, &tv
)) {
1701 m_ue
.pushMergeableDef(UnitMergeKindGlobal
, name
, tv
);
1707 case Expression::KindOfIncludeExpression
: {
1708 IncludeExpressionPtr inc
=
1709 static_pointer_cast
<IncludeExpression
>(e
);
1710 if (inc
->isReqLit()) {
1711 if (FileScopeRawPtr f
= inc
->getIncludedFile(ar
)) {
1712 if (StatementListPtr sl
= f
->getStmt()) {
1713 FunctionScopeRawPtr ps DEBUG_ONLY
=
1714 sl
->getFunctionScope();
1715 assert(ps
&& ps
->inPseudoMain());
1716 UnitMergeKind kind
= UnitMergeKindReqDoc
;
1717 m_ue
.pushMergeableInclude(
1719 StringData::GetStaticString(inc
->includePath()));
1732 if (mainReturn
.m_type
!= KindOfInvalid
) break;
1735 notMergeOnly
= true;
1739 if (mainReturn
.m_type
== KindOfInvalid
) {
1740 tvWriteUninit(&mainReturn
);
1741 tvAsVariant(&mainReturn
) = 1;
1743 m_ue
.setMainReturn(&mainReturn
);
1744 m_ue
.setMergeOnly(!notMergeOnly
);
1745 // If the exitHnd label was used, we need to emit some extra code
1746 // to handle stray breaks
1748 if (ctFatal
.isUsed() || ctFatalWithPop
.isUsed()) {
1750 if (ctFatalWithPop
.isUsed()) {
1751 ctFatalWithPop
.set(e
);
1752 e
.PopC(); // the number of levels to jump up
1754 if (ctFatal
.isUsed()) {
1757 static const StringData
* msg
=
1758 StringData::GetStaticString("Cannot break/continue");
1762 if (exit
.isUsed()) {
1765 // Pseudo-main returns the integer value 1 by default. If the
1766 // current position in the bytecode is reachable, emit code to
1768 if (currentPositionIsReachable()) {
1774 if (!m_evalStack
.empty()) {
1775 InvariantViolation("Eval stack was not empty as expected before "
1776 "emitPostponed* phase");
1780 emitPostponedMeths();
1781 emitPostponedCtors();
1782 emitPostponedPinits();
1783 emitPostponedSinits();
1784 emitPostponedCinits();
1785 Peephole
peephole(m_ue
, m_metaInfo
);
1786 m_metaInfo
.setForUnit(m_ue
);
1789 static StringData
* getClassName(ExpressionPtr e
) {
1790 ClassScopeRawPtr cls
;
1792 cls
= e
->getOriginalClass();
1793 if (TypePtr t
= e
->getAssertedType()) {
1794 if (t
->isSpecificObject()) {
1795 AnalysisResultConstPtr ar
= e
->getScope()->getContainingProgram();
1796 ClassScopeRawPtr c2
= t
->getClass(ar
, e
->getScope());
1797 if (c2
&& c2
->derivesFrom(ar
, cls
->getName(), true, false)) {
1802 } else if (TypePtr t
= e
->getActualType()) {
1803 if (t
->isSpecificObject()) {
1804 cls
= t
->getClass(e
->getScope()->getContainingProgram(), e
->getScope());
1807 if (cls
&& !cls
->isTrait()) {
1808 return StringData::GetStaticString(cls
->getOriginalName());
1813 static DataType
getPredictedDataType(ExpressionPtr expr
) {
1814 if (!expr
->maybeInited()) {
1815 return KindOfUninit
;
1817 // Note that expr->isNonNull() may be false,
1818 // but that's ok since this is just a prediction.
1819 TypePtr act
= expr
->getActualType();
1821 return KindOfUnknown
;
1823 return act
->getDataType();
1826 void EmitterVisitor::fixReturnType(Emitter
& e
, FunctionCallPtr fn
,
1827 bool useFCallBuiltin
) {
1829 if (fn
->hasAnyContext(Expression::RefValue
|
1830 Expression::DeepReference
|
1831 Expression::LValue
|
1832 Expression::OprLValue
|
1833 Expression::UnsetContext
)) {
1836 bool voidReturn
= false;
1837 if (fn
->isValid() && fn
->getFuncScope()) {
1838 ref
= fn
->getFuncScope()->isRefReturn();
1839 if (!(fn
->getFuncScope()->getReturnType())) {
1842 } else if (!fn
->getName().empty()) {
1843 FunctionScope::FunctionInfoPtr fi
=
1844 FunctionScope::GetFunctionInfo(fn
->getName());
1845 if (!fi
|| !fi
->getMaybeRefReturn()) ref
= false;
1848 if (!fn
->isUnused() &&
1850 (!ref
|| !fn
->hasAnyContext(Expression::AccessContext
|
1851 Expression::ObjectContext
))) {
1852 /* we dont support V in M-vectors, so leave it as an R in that
1854 assert(m_evalStack
.get(m_evalStack
.size() - 1) == StackSym::R
);
1855 Offset cur
= m_ue
.bcPos();
1861 m_metaInfo
.add(cur
, Unit::MetaInfo::NopOut
, false, 0, 0);
1865 m_evalStack
.setKnownType(KindOfNull
, false /* inferred */);
1866 m_evalStack
.setNotRef();
1868 DataType dt
= getPredictedDataType(fn
);
1869 if (dt
!= KindOfUnknown
) {
1870 if (useFCallBuiltin
) {
1874 case KindOfDouble
: /* inferred */
1875 m_evalStack
.setKnownType(dt
, false);
1877 default: /* predicted */
1878 m_evalStack
.setKnownType(dt
, true);
1882 m_evalStack
.setKnownType(dt
, true /* predicted */);
1885 m_evalStack
.setNotRef();
1889 void EmitterVisitor::visitKids(ConstructPtr c
) {
1890 for (int i
= 0, nk
= c
->getKidCount(); i
< nk
; i
++) {
1891 ConstructPtr
kid(c
->getNthKid(i
));
1897 * isTupleInit() returns true if this expression list looks like a vanilla
1898 * tuple-shaped array with no keys, no ref values; e.g. array(x,y,z),
1899 * where we can NewTuple to create the array. In that case the elements are
1900 * pushed on the stack, so we arbitrarily limit this to a small multiple of
1901 * HphpArray::SmallSize (12).
1903 bool isTupleInit(ExpressionPtr init_expr
, int* cap
) {
1904 if (init_expr
->getKindOf() != Expression::KindOfExpressionList
) return false;
1905 ExpressionListPtr el
= static_pointer_cast
<ExpressionList
>(init_expr
);
1906 int n
= el
->getCount();
1907 if (n
< 1 || n
> int(4 * HphpArray::SmallSize
)) return false;
1908 for (int i
= 0, n
= el
->getCount(); i
< n
; ++i
) {
1909 ExpressionPtr ex
= (*el
)[i
];
1910 if (ex
->getKindOf() != Expression::KindOfArrayPairExpression
) return false;
1911 ArrayPairExpressionPtr ap
= static_pointer_cast
<ArrayPairExpression
>(ex
);
1912 if (ap
->getName() != nullptr || ap
->isRef()) return false;
1918 bool EmitterVisitor::visit(ConstructPtr node
) {
1919 bool ret
= visitImpl(node
);
1920 if (!Option::WholeProgram
|| !ret
) return ret
;
1921 ExpressionPtr e
= boost::dynamic_pointer_cast
<Expression
>(node
);
1922 if (!e
|| e
->isScalar()) return ret
;
1923 DataType dt
= KindOfUnknown
;
1924 if (!e
->maybeInited()) {
1926 } else if (node
->isNonNull()) {
1927 TypePtr act
= e
->getActualType();
1928 if (!act
) return ret
;
1929 dt
= act
->getDataType();
1930 if (dt
== KindOfUnknown
) return ret
;
1934 char sym
= m_evalStack
.top();
1935 if (StackSym::GetMarker(sym
)) return ret
;
1936 switch (StackSym::GetSymFlavor(sym
)) {
1938 m_evalStack
.setNotRef();
1939 m_evalStack
.setKnownType(dt
);
1942 if (dt
== KindOfUninit
||
1943 !e
->maybeRefCounted() ||
1944 (e
->is(Expression::KindOfSimpleVariable
) &&
1945 !static_pointer_cast
<SimpleVariable
>(e
)->couldBeAliased())) {
1946 m_evalStack
.setNotRef();
1948 m_evalStack
.setKnownType(dt
);
1955 void EmitterVisitor::emitFatal(Emitter
& e
, const char* message
) {
1956 const StringData
* msg
= StringData::GetStaticString(message
);
1961 bool EmitterVisitor::visitImpl(ConstructPtr node
) {
1962 if (!node
) return false;
1964 Emitter
e(node
, m_ue
, *this);
1966 if (StatementPtr s
= dynamic_pointer_cast
<Statement
>(node
)) {
1967 switch (s
->getKindOf()) {
1968 case Statement::KindOfBlockStatement
:
1969 case Statement::KindOfStatementList
:
1973 case Statement::KindOfTypedefStatement
: {
1974 emitFatal(e
, "Type statements are currently only allowed at "
1979 case Statement::KindOfContinueStatement
:
1980 case Statement::KindOfBreakStatement
: {
1981 BreakStatementPtr
bs(static_pointer_cast
<BreakStatement
>(s
));
1982 int64_t n
= bs
->getDepth();
1984 // Plain old "break;" or "continue;"
1985 if (m_controlTargets
.empty()) {
1986 static const StringData
* msg
=
1987 StringData::GetStaticString("Cannot break/continue 1 level");
1992 if (bs
->is(Statement::KindOfBreakStatement
)) {
1993 if (m_controlTargets
.front().m_itId
!= -1) {
1994 if (m_controlTargets
.front().m_itRef
) {
1995 e
.MIterFree(m_controlTargets
.front().m_itId
);
1997 e
.IterFree(m_controlTargets
.front().m_itId
);
2000 e
.Jmp(m_controlTargets
.front().m_brkTarg
);
2002 e
.Jmp(m_controlTargets
.front().m_cntTarg
);
2007 // Dynamic break/continue.
2009 // Depth can't be statically determined.
2010 visit(bs
->getExp());
2011 emitConvertToCell(e
);
2013 // Dynamic break/continue with statically known depth.
2014 if (n
> (int64_t)m_controlTargets
.size()) {
2015 std::ostringstream msg
;
2016 msg
<< "Cannot break/continue " << n
<< " levels";
2017 e
.String(StringData::GetStaticString(msg
.str()));
2023 if (bs
->is(Statement::KindOfBreakStatement
)) {
2024 e
.Jmp(m_controlTargets
.front().m_brkHand
);
2026 e
.Jmp(m_controlTargets
.front().m_cntHand
);
2031 case Statement::KindOfDoStatement
: {
2032 DoStatementPtr
ds(static_pointer_cast
<DoStatement
>(s
));
2039 CONTROL_BODY(exit
, condition
, brkHand
, cntHand
);
2040 visit(ds
->getBody());
2044 ExpressionPtr c
= ds
->getCondExp();
2045 Emitter
condEmitter(c
, m_ue
, *this);
2046 visitIfCondition(c
, condEmitter
, top
, exit
, false);
2049 if (brkHand
.isUsed() || cntHand
.isUsed()) {
2051 emitBreakHandler(e
, exit
, condition
, brkHand
, cntHand
);
2053 if (exit
.isUsed()) exit
.set(e
);
2057 case Statement::KindOfCaseStatement
: {
2058 // Should never be called. Handled in visitSwitch.
2062 case Statement::KindOfCatchStatement
: {
2063 // Store the current exception object in the appropriate local variable
2064 CatchStatementPtr
cs(static_pointer_cast
<CatchStatement
>(node
));
2066 StringData::GetStaticString(cs
->getVariable()->getName());
2067 Id i
= m_curFunc
->lookupVarId(vName
);
2068 emitVirtualLocal(i
);
2072 visit(cs
->getStmt());
2076 case Statement::KindOfEchoStatement
: {
2077 EchoStatementPtr
es(static_pointer_cast
<EchoStatement
>(node
));
2078 ExpressionListPtr exps
= es
->getExpressionList();
2079 int count
= exps
->getCount();
2080 for (int i
= 0; i
< count
; i
++) {
2082 emitConvertToCell(e
);
2089 case Statement::KindOfExpStatement
: {
2090 ExpStatementPtr
es(static_pointer_cast
<ExpStatement
>(s
));
2091 if (visit(es
->getExpression())) {
2097 case Statement::KindOfForStatement
: {
2098 ForStatementPtr
fs(static_pointer_cast
<ForStatement
>(s
));
2099 if (visit(fs
->getInitExp())) {
2107 if (ExpressionPtr condExp
= fs
->getCondExp()) {
2109 Emitter
condEmitter(condExp
, m_ue
, *this);
2110 visitIfCondition(condExp
, condEmitter
, tru
, fail
, true);
2111 if (tru
.isUsed()) tru
.set(e
);
2114 CONTROL_BODY(fail
, preInc
, brkHand
, cntHand
);
2115 visit(fs
->getBody());
2118 if (visit(fs
->getIncExp())) {
2122 emitBreakHandler(e
, fail
, preInc
, brkHand
, cntHand
);
2123 if (fail
.isUsed()) fail
.set(e
);
2127 case Statement::KindOfForEachStatement
: {
2128 ForEachStatementPtr
fe(static_pointer_cast
<ForEachStatement
>(node
));
2133 case Statement::KindOfGlobalStatement
: {
2134 ExpressionListPtr
vars(
2135 static_pointer_cast
<GlobalStatement
>(node
)->getVars());
2136 for (int i
= 0, n
= vars
->getCount(); i
< n
; i
++) {
2137 ExpressionPtr
var((*vars
)[i
]);
2138 if (var
->is(Expression::KindOfSimpleVariable
)) {
2139 SimpleVariablePtr
sv(static_pointer_cast
<SimpleVariable
>(var
));
2140 if (sv
->isSuperGlobal()) {
2143 StringData
* nLiteral
= StringData::GetStaticString(sv
->getName());
2144 Id i
= m_curFunc
->lookupVarId(nLiteral
);
2145 emitVirtualLocal(i
);
2151 } else if (var
->is(Expression::KindOfDynamicVariable
)) {
2152 // global $<exp> =& $GLOBALS[<exp>]
2153 DynamicVariablePtr
dv(static_pointer_cast
<DynamicVariable
>(var
));
2154 // Get the variable name as a cell, for the LHS
2155 visit(dv
->getSubExpression());
2156 emitConvertToCell(e
);
2157 // Copy the variable name, for indexing into $GLOBALS
2171 case Statement::KindOfIfStatement
: {
2172 IfStatementPtr
ifp(static_pointer_cast
<IfStatement
>(node
));
2173 StatementListPtr
branches(ifp
->getIfBranches());
2174 int nb
= branches
->getCount();
2176 for (int i
= 0; i
< nb
; i
++) {
2177 IfBranchStatementPtr
branch(
2178 static_pointer_cast
<IfBranchStatement
>((*branches
)[i
]));
2180 if (branch
->getCondition()) {
2182 Emitter
condEmitter(branch
->getCondition(), m_ue
, *this);
2183 visitIfCondition(branch
->getCondition(), condEmitter
,
2189 visit(branch
->getStmt());
2190 if (currentPositionIsReachable() && i
+ 1 < nb
) {
2193 if (fals
.isUsed()) {
2197 if (done
.isUsed()) {
2203 case Statement::KindOfIfBranchStatement
:
2204 not_reached(); // handled by KindOfIfStatement
2206 case Statement::KindOfReturnStatement
: {
2207 ReturnStatementPtr
r(static_pointer_cast
<ReturnStatement
>(node
));
2209 if (visit(r
->getRetExp())) {
2210 if (r
->getRetExp()->getContext() & Expression::RefValue
) {
2211 emitConvertToVar(e
);
2212 emitFreePendingIters(e
);
2215 emitConvertToCell(e
);
2216 emitFreePendingIters(e
);
2219 emitFreePendingIters(e
);
2223 if (m_curFunc
->isGenerator()) {
2225 m_metaInfo
.addKnownDataType(
2226 KindOfObject
, false, m_ue
.bcPos(), false, 1);
2227 assert(m_evalStack
.size() == 1);
2232 if (r
->isGuarded()) {
2233 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::GuardedThis
,
2237 // XXX: disabled until static analysis is more reliable: t2225399
2238 /*for (auto& l : r->nonRefcountedLocals()) {
2239 auto v = m_curFunc->lookupVarId(StringData::GetStaticString(l));
2240 m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::NonRefCounted,
2251 case Statement::KindOfStaticStatement
: {
2252 ExpressionListPtr
vars(
2253 static_pointer_cast
<StaticStatement
>(node
)->getVars());
2254 for (int i
= 0, n
= vars
->getCount(); i
< n
; i
++) {
2255 ExpressionPtr
se((*vars
)[i
]);
2256 assert(se
->is(Expression::KindOfAssignmentExpression
));
2257 AssignmentExpressionPtr
ae(
2258 static_pointer_cast
<AssignmentExpression
>(se
));
2259 ExpressionPtr
var(ae
->getVariable());
2260 ExpressionPtr
value(ae
->getValue());
2261 assert(var
->is(Expression::KindOfSimpleVariable
));
2262 SimpleVariablePtr
sv(static_pointer_cast
<SimpleVariable
>(var
));
2263 StringData
* name
= StringData::GetStaticString(sv
->getName());
2264 Id local
= m_curFunc
->lookupVarId(name
);
2266 Func::SVInfo svInfo
;
2268 std::ostringstream os
;
2269 CodeGenerator
cg(&os
, CodeGenerator::PickledPHP
);
2270 AnalysisResultPtr
ar(new AnalysisResult());
2271 value
->outputPHP(cg
, ar
);
2272 svInfo
.phpCode
= StringData::GetStaticString(os
.str());
2273 m_curFunc
->addStaticVar(svInfo
);
2275 if (value
->isScalar()) {
2277 emitConvertToCell(e
);
2278 e
.StaticLocInit(local
, name
);
2281 e
.StaticLoc(local
, name
);
2284 emitVirtualLocal(local
);
2286 emitConvertToCell(e
);
2296 case Statement::KindOfSwitchStatement
: {
2297 SwitchStatementPtr
sw(static_pointer_cast
<SwitchStatement
>(node
));
2298 StatementListPtr
cases(sw
->getCases());
2300 visit(sw
->getExp());
2304 uint ncase
= cases
->getCount();
2305 std::vector
<Label
> caseLabels(ncase
);
2310 // There are two different ways this can go. If the subject is a simple
2311 // variable, then we have to evaluate it every time we compare against a
2312 // case condition. Otherwise, we evaluate it once and store it in an
2313 // unnamed local. This is because (a) switch statements are equivalent
2314 // to a series of if-elses, and (b) Zend has some weird evaluation order
2315 // rules. For example, "$a == ++$a" is true but "$a[0] == ++$a[0]" is
2316 // false. In particular, if a case condition modifies the switch
2317 // subject, things behave differently depending on whether the subject
2318 // is a simple variable.
2319 ExpressionPtr subject
= sw
->getExp();
2320 bool simpleSubject
= subject
->is(Expression::KindOfSimpleVariable
)
2321 && !static_pointer_cast
<SimpleVariable
>(subject
)->getAlwaysStash();
2323 Offset start
= InvalidAbsoluteOffset
;
2325 bool enabled
= RuntimeOption::EnableEmitSwitch
;
2326 SimpleFunctionCallPtr
2327 call(dynamic_pointer_cast
<SimpleFunctionCall
>(subject
));
2330 bool didSwitch
= false;
2332 DataType stype
= analyzeSwitch(sw
, state
);
2333 if (stype
!= KindOfInvalid
) {
2334 e
.incStat(stype
== KindOfInt64
? Stats::Switch_Integer
2335 : Stats::Switch_String
,
2337 if (state
.cases
.empty()) {
2338 // If there are no non-default cases, evaluate the subject for
2339 // side effects and fall through. If there's a default case it
2340 // will be emitted immediately after this.
2341 visit(sw
->getExp());
2343 } else if (stype
== KindOfInt64
) {
2344 emitIntegerSwitch(e
, sw
, caseLabels
, done
, state
);
2346 assert(IS_STRING_TYPE(stype
));
2347 emitStringSwitch(e
, sw
, caseLabels
, done
, state
);
2353 e
.incStat(Stats::Switch_Generic
, 1);
2354 if (!simpleSubject
) {
2355 // Evaluate the subject once and stash it in a local
2356 tempLocal
= m_curFunc
->allocUnnamedLocal();
2357 emitVirtualLocal(tempLocal
);
2359 emitConvertToCell(e
);
2362 start
= m_ue
.bcPos();
2366 for (uint i
= 0; i
< ncase
; i
++) {
2367 CaseStatementPtr
c(static_pointer_cast
<CaseStatement
>((*cases
)[i
]));
2368 ExpressionPtr condition
= c
->getCondition();
2370 if (simpleSubject
) {
2371 // Evaluate the subject every time.
2373 emitConvertToCellOrLoc(e
);
2375 emitConvertToCell(e
);
2376 emitConvertSecondToCell(e
);
2378 emitVirtualLocal(tempLocal
);
2381 emitConvertToCell(e
);
2384 e
.JmpNZ(caseLabels
[i
]);
2386 // Default clause. The last one wins.
2391 e
.Jmp(caseLabels
[defI
]);
2396 for (uint i
= 0; i
< ncase
; i
++) {
2397 caseLabels
[i
].set(e
);
2398 CaseStatementPtr
c(static_pointer_cast
<CaseStatement
>((*cases
)[i
]));
2399 CONTROL_BODY(done
, done
, brkHand
, cntHand
);
2400 visit(c
->getStatement());
2402 if (brkHand
.isUsed() || cntHand
.isUsed()) {
2404 emitBreakHandler(e
, done
, done
, brkHand
, cntHand
);
2407 if (!didSwitch
&& !simpleSubject
) {
2408 // Null out temp local, to invoke any needed refcounting
2409 assert(tempLocal
>= 0);
2410 assert(start
!= InvalidAbsoluteOffset
);
2411 newFaultRegion(start
, m_ue
.bcPos(),
2412 new UnsetUnnamedLocalThunklet(tempLocal
));
2413 emitVirtualLocal(tempLocal
);
2415 m_curFunc
->freeUnnamedLocal(tempLocal
);
2420 case Statement::KindOfThrowStatement
: {
2422 emitConvertToCell(e
);
2427 case Statement::KindOfFinallyStatement
: {
2431 case Statement::KindOfTryStatement
: {
2432 if (!m_evalStack
.empty()) {
2434 "Emitter detected that the evaluation stack is not empty "
2435 "at the beginning of a try region: %d", m_ue
.bcPos());
2438 TryStatementPtr ts
= static_pointer_cast
<TryStatement
>(node
);
2440 Offset start
= m_ue
.bcPos();
2441 visit(ts
->getBody());
2442 // include the jump out of the try-catch block in the
2443 // exception handler address range
2445 Offset end
= m_ue
.bcPos();
2447 if (!m_evalStack
.empty()) {
2448 InvariantViolation("Emitter detected that the evaluation stack "
2449 "is not empty at the end of a try region: %d",
2453 StatementListPtr catches
= ts
->getCatches();
2454 ExnHandlerRegion
* r
= new ExnHandlerRegion(start
, end
);
2455 m_exnHandlers
.push_back(r
);
2457 int n
= catches
->getCount();
2458 bool firstHandler
= true;
2459 for (int i
= 0; i
< n
; i
++) {
2460 CatchStatementPtr
c(static_pointer_cast
<CatchStatement
>
2462 StringData
* eName
= StringData::GetStaticString(c
->getClassName());
2464 // If there's already a catch of this class, skip; the first one wins
2465 if (r
->m_names
.find(eName
) == r
->m_names
.end()) {
2466 // Don't let execution of the try body, or the previous catch body,
2468 if (!firstHandler
) {
2471 firstHandler
= false;
2474 Label
* label
= new Label(e
);
2475 r
->m_names
.insert(eName
);
2476 r
->m_catchLabels
.push_back(std::pair
<StringData
*, Label
*>(eName
,
2486 case Statement::KindOfUnsetStatement
: {
2487 ExpressionListPtr
exps(
2488 static_pointer_cast
<UnsetStatement
>(node
)->getExps());
2489 for (int i
= 0, n
= exps
->getCount(); i
< n
; i
++) {
2490 emitVisitAndUnset(e
, (*exps
)[i
]);
2495 case Statement::KindOfWhileStatement
: {
2496 WhileStatementPtr
ws(static_pointer_cast
<WhileStatement
>(s
));
2503 ExpressionPtr
c(ws
->getCondExp());
2504 Emitter
condEmitter(c
, m_ue
, *this);
2505 visitIfCondition(c
, condEmitter
, tru
, fail
, true);
2506 if (tru
.isUsed()) tru
.set(e
);
2509 CONTROL_BODY(fail
, preCond
, brkHand
, cntHand
);
2510 visit(ws
->getBody());
2513 emitBreakHandler(e
, fail
, preCond
, brkHand
, cntHand
);
2514 if (fail
.isUsed()) fail
.set(e
);
2518 case Statement::KindOfInterfaceStatement
:
2519 case Statement::KindOfClassStatement
: {
2520 emitClass(e
, node
->getClassScope(), false);
2524 case Statement::KindOfClassVariable
:
2525 case Statement::KindOfClassConstant
:
2526 case Statement::KindOfMethodStatement
:
2527 // handled by emitClass
2530 case Statement::KindOfFunctionStatement
: {
2531 MethodStatementPtr
m(static_pointer_cast
<MethodStatement
>(node
));
2532 // Only called for fn defs not on the top level
2533 StringData
* nName
= StringData::GetStaticString(m
->getOriginalName());
2534 if (m
->getFunctionScope()->isGenerator()) {
2535 if (m
->getFileScope() != m_file
) {
2536 // the generator's definition is in another file typically
2537 // because it was defined in a trait that got inlined into
2538 // a class in this file. Nothing to do - it will be output
2539 // with its own file.
2542 Label
*& label
= m_methLabels
[nName
];
2544 // its possible to see the same generator more than once
2545 label
= new Label();
2550 postponeMeth(m
, nullptr, true);
2552 assert(!node
->getClassScope()); // Handled directly by emitClass().
2553 FuncEmitter
* fe
= m_ue
.newFuncEmitter(nName
, false);
2554 e
.DefFunc(fe
->id());
2555 postponeMeth(m
, fe
, false);
2560 case Statement::KindOfGotoStatement
: {
2561 GotoStatementPtr
g(static_pointer_cast
<GotoStatement
>(node
));
2562 StringData
* nName
= StringData::GetStaticString(g
->label());
2563 e
.Jmp(m_gotoLabels
[nName
]);
2567 case Statement::KindOfLabelStatement
: {
2568 LabelStatementPtr
l(static_pointer_cast
<LabelStatement
>(node
));
2569 StringData
* nName
= StringData::GetStaticString(l
->label());
2570 Label
& lab
= m_gotoLabels
[nName
];
2574 case Statement::KindOfUseTraitStatement
:
2575 case Statement::KindOfTraitPrecStatement
:
2576 case Statement::KindOfTraitAliasStatement
: {
2581 ExpressionPtr ex
= static_pointer_cast
<Expression
>(node
);
2582 switch (ex
->getKindOf()) {
2583 case Expression::KindOfUnaryOpExpression
: {
2584 UnaryOpExpressionPtr
u(static_pointer_cast
<UnaryOpExpression
>(node
));
2585 int op
= u
->getOp();
2587 if (op
== T_UNSET
) {
2588 // php doesnt have an unset expression, but hphp's optimizations
2589 // sometimes introduce them
2590 ExpressionPtr
exp(u
->getExpression());
2591 if (exp
->is(Expression::KindOfExpressionList
)) {
2592 ExpressionListPtr
exps(
2593 static_pointer_cast
<ExpressionList
>(exp
));
2594 if (exps
->getListKind() == ExpressionList::ListKindParam
) {
2595 for (int i
= 0, n
= exps
->getCount(); i
< n
; i
++) {
2596 emitVisitAndUnset(e
, (*exps
)[i
]);
2602 emitVisitAndUnset(e
, exp
);
2606 if (op
== T_ARRAY
) {
2608 if (u
->isScalar()) {
2612 if (m_staticArrays
.size() == 0) {
2613 e
.Array(tv
.m_data
.parr
);
2615 } else if (isTupleInit(u
->getExpression(), &tuple_cap
)) {
2616 // evaluate array values onto stack
2617 ExpressionListPtr el
=
2618 static_pointer_cast
<ExpressionList
>(u
->getExpression());
2619 for (int i
= 0; i
< tuple_cap
; i
++) {
2620 ArrayPairExpressionPtr ap
=
2621 static_pointer_cast
<ArrayPairExpression
>((*el
)[i
]);
2622 visit(ap
->getValue());
2623 emitConvertToCell(e
);
2625 e
.NewTuple(tuple_cap
);
2627 assert(m_staticArrays
.size() == 0);
2628 ExpressionPtr ex
= u
->getExpression();
2629 if (ex
->getKindOf() == Expression::KindOfExpressionList
) {
2630 ExpressionListPtr
el(static_pointer_cast
<ExpressionList
>(ex
));
2631 int capacity
= el
->getCount();
2633 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::ArrayCapacity
,
2634 false, 0, capacity
);
2641 } else if (op
== T_ISSET
) {
2642 ExpressionListPtr list
=
2643 dynamic_pointer_cast
<ExpressionList
>(u
->getExpression());
2645 // isset($a, $b, ...) ==> isset($a) && isset($b) && ...
2647 int n
= list
->getCount();
2648 for (int i
= 0; i
< n
- 1; ++i
) {
2655 // Treat the last one specially; let it fall through
2656 visit((*list
)[n
- 1]);
2661 visit(u
->getExpression());
2665 } else if (op
== '+' || op
== '-') {
2669 Id oldErrorLevelLoc
= -1;
2670 Offset start
= InvalidAbsoluteOffset
;
2672 // XXX This ends up generating two boatloads of instructions; we may
2673 // be better off introducing new instructions for the silencer
2674 oldErrorLevelLoc
= m_curFunc
->allocUnnamedLocal();
2675 // Call error_reporting(0), and stash the return in an unnamed local
2676 emitVirtualLocal(oldErrorLevelLoc
);
2677 static const StringData
* s_error_reporting
=
2678 StringData::GetStaticString("error_reporting");
2679 Offset fpiStart
= m_ue
.bcPos();
2680 e
.FPushFuncD(1, s_error_reporting
);
2682 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
2684 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::NopOut
, false, 0, 0);
2689 emitSet(e
); // set $oldErrorLevelLoc
2691 start
= m_ue
.bcPos();
2694 ExpressionPtr exp
= u
->getExpression();
2695 if (exp
&& visit(exp
)) {
2696 if (op
!= T_EMPTY
&& op
!= T_INC
&& op
!= T_DEC
) {
2697 emitConvertToCell(e
);
2699 } else if (op
== T_EXIT
) {
2700 // exit without an expression is treated as exit(0)
2703 // __FILE__ and __DIR__ are special unary ops that don't
2705 assert(op
== T_FILE
|| op
== T_DIR
);
2710 int cop
= IncDec_invalid
;
2712 if (u
->getFront()) {
2718 if (u
->getFront()) {
2727 case T_EMPTY
: emitEmpty(e
); break;
2728 case T_CLONE
: e
.Clone(); break;
2729 case '+': e
.Add(); break;
2730 case '-': e
.Sub(); break;
2731 case '!': e
.Not(); break;
2732 case '~': e
.BitNot(); break;
2734 case T_INT_CAST
: e
.CastInt(); break;
2735 case T_DOUBLE_CAST
: e
.CastDouble(); break;
2736 case T_STRING_CAST
: e
.CastString(); break;
2737 case T_ARRAY_CAST
: e
.CastArray(); break;
2738 case T_OBJECT_CAST
: e
.CastObject(); break;
2739 case T_BOOL_CAST
: e
.CastBool(); break;
2740 case T_UNSET_CAST
: emitPop(e
); e
.Null(); break;
2741 case T_EXIT
: e
.Exit(); break;
2743 assert(oldErrorLevelLoc
>= 0);
2744 assert(start
!= InvalidAbsoluteOffset
);
2745 newFaultRegion(start
, m_ue
.bcPos(),
2746 new RestoreErrorReportingThunklet(oldErrorLevelLoc
));
2747 emitRestoreErrorReporting(e
, oldErrorLevelLoc
);
2748 m_curFunc
->freeUnnamedLocal(oldErrorLevelLoc
);
2751 case T_PRINT
: e
.Print(); break;
2752 case T_EVAL
: e
.Eval(); break;
2767 case Expression::KindOfAssignmentExpression
: {
2768 AssignmentExpressionPtr
ae(
2769 static_pointer_cast
<AssignmentExpression
>(node
));
2770 ExpressionPtr rhs
= ae
->getValue();
2772 Offset start
= InvalidAbsoluteOffset
;
2774 if (ae
->isRhsFirst()) {
2775 assert(!rhs
->hasContext(Expression::RefValue
));
2776 tempLocal
= emitVisitAndSetUnnamedL(e
, rhs
);
2777 start
= m_ue
.bcPos();
2780 visit(ae
->getVariable());
2781 emitClsIfSPropBase(e
);
2783 if (ae
->isRhsFirst()) {
2784 emitPushAndFreeUnnamedL(e
, tempLocal
, start
);
2789 if (rhs
->hasContext(Expression::RefValue
)) {
2790 emitConvertToVar(e
);
2792 if (ae
->hasAnyContext(Expression::AccessContext
|
2793 Expression::ObjectContext
|
2794 Expression::ExistContext
)) {
2796 * hphpc optimizations can result in
2797 * ($x =& $y)->foo or ($x =& $y)['foo'] or empty($x =& $y)
2799 emitConvertToCellIfVar(e
);
2802 emitConvertToCell(e
);
2808 case Expression::KindOfBinaryOpExpression
: {
2809 BinaryOpExpressionPtr
b(static_pointer_cast
<BinaryOpExpression
>(node
));
2810 int op
= b
->getOp();
2811 if (b
->isAssignmentOp()) {
2812 visit(b
->getExp1());
2813 emitClsIfSPropBase(e
);
2814 visit(b
->getExp2());
2815 emitConvertToCell(e
);
2820 if (b
->isShortCircuitOperator()) {
2821 Label tru
, fls
, done
;
2822 visitIfCondition(b
, e
, tru
, fls
, false);
2823 if (fls
.isUsed()) fls
.set(e
);
2824 if (currentPositionIsReachable()) {
2828 if (tru
.isUsed()) tru
.set(e
);
2829 if (currentPositionIsReachable()) {
2836 if (op
== T_INSTANCEOF
) {
2837 visit(b
->getExp1());
2838 emitConvertToCell(e
);
2839 ExpressionPtr second
= b
->getExp2();
2840 if (second
->isScalar()) {
2841 ScalarExpressionPtr scalar
=
2842 dynamic_pointer_cast
<ScalarExpression
>(second
);
2843 bool notQuoted
= scalar
&& !scalar
->isQuoted();
2844 std::string s
= second
->getLiteralString();
2845 if (s
== "static" && notQuoted
) {
2846 // Can't resolve this to a literal name at emission time
2847 static const StringData
* fname
2848 = StringData::GetStaticString("get_called_class");
2849 Offset fpiStart
= m_ue
.bcPos();
2850 e
.FPushFuncD(0, fname
);
2852 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
2857 } else if (s
!= "") {
2858 ClassScopeRawPtr cls
= second
->getOriginalClass();
2860 if (s
== "self" && notQuoted
) {
2861 s
= cls
->getOriginalName();
2862 } else if (s
== "parent" && notQuoted
) {
2863 s
= cls
->getOriginalParent();
2866 StringData
* nLiteral
= StringData::GetStaticString(s
);
2867 e
.InstanceOfD(nLiteral
);
2869 visit(b
->getExp2());
2870 emitConvertToCell(e
);
2874 visit(b
->getExp2());
2875 emitConvertToCell(e
);
2881 if (op
== T_COLLECTION
) {
2882 ScalarExpressionPtr cls
=
2883 static_pointer_cast
<ScalarExpression
>(b
->getExp1());
2885 ExpressionListPtr el
;
2887 el
= static_pointer_cast
<ExpressionList
>(b
->getExp2());
2888 nElms
= el
->getCount();
2890 const std::string
* clsName
= nullptr;
2891 cls
->getString(clsName
);
2893 if (!strcasecmp(clsName
->c_str(), "vector")) {
2894 cType
= Collection::VectorType
;
2895 } else if (!strcasecmp(clsName
->c_str(), "map")) {
2896 cType
= Collection::MapType
;
2897 } else if (!strcasecmp(clsName
->c_str(), "stablemap")) {
2898 cType
= Collection::StableMapType
;
2899 } else if (!strcasecmp(clsName
->c_str(), "set")) {
2900 cType
= Collection::SetType
;
2901 } else if (!strcasecmp(clsName
->c_str(), "pair")) {
2902 cType
= Collection::PairType
;
2904 throw IncludeTimeFatalException(b
,
2905 "Pair objects must have exactly 2 elements");
2908 throw IncludeTimeFatalException(b
,
2909 "Cannot use collection initialization for non-collection class");
2911 bool kvPairs
= (cType
== Collection::MapType
||
2912 cType
== Collection::StableMapType
);
2913 e
.NewCol(cType
, nElms
);
2915 for (int i
= 0; i
< nElms
; i
++) {
2916 ArrayPairExpressionPtr
ap(
2917 static_pointer_cast
<ArrayPairExpression
>((*el
)[i
]));
2918 ExpressionPtr key
= ap
->getName();
2920 throw IncludeTimeFatalException(ap
,
2921 "Keys must be specified for Map and StableMap "
2925 emitConvertToCell(e
);
2926 visit(ap
->getValue());
2927 emitConvertToCell(e
);
2931 for (int i
= 0; i
< nElms
; i
++) {
2932 ArrayPairExpressionPtr
ap(
2933 static_pointer_cast
<ArrayPairExpression
>((*el
)[i
]));
2934 ExpressionPtr key
= ap
->getName();
2936 throw IncludeTimeFatalException(ap
,
2937 "Keys may not be specified for Vector, Set, or Pair "
2940 visit(ap
->getValue());
2941 emitConvertToCell(e
);
2948 visit(b
->getExp1());
2949 emitConvertToCellOrLoc(e
);
2950 visit(b
->getExp2());
2951 emitConvertToCell(e
);
2952 emitConvertSecondToCell(e
);
2954 case T_LOGICAL_XOR
: e
.Xor(); break;
2955 case '|': e
.BitOr(); break;
2956 case '&': e
.BitAnd(); break;
2957 case '^': e
.BitXor(); break;
2958 case '.': e
.Concat(); break;
2959 case '+': e
.Add(); break;
2960 case '-': e
.Sub(); break;
2961 case '*': e
.Mul(); break;
2962 case '/': e
.Div(); break;
2963 case '%': e
.Mod(); break;
2964 case T_SL
: e
.Shl(); break;
2965 case T_SR
: e
.Shr(); break;
2966 case T_IS_IDENTICAL
: e
.Same(); break;
2967 case T_IS_NOT_IDENTICAL
: e
.NSame(); break;
2968 case T_IS_EQUAL
: e
.Eq(); break;
2969 case T_IS_NOT_EQUAL
: e
.Neq(); break;
2970 case '<': e
.Lt(); break;
2971 case T_IS_SMALLER_OR_EQUAL
: e
.Lte(); break;
2972 case '>': e
.Gt(); break;
2973 case T_IS_GREATER_OR_EQUAL
: e
.Gte(); break;
2974 default: assert(false);
2979 case Expression::KindOfClassConstantExpression
: {
2980 ClassConstantExpressionPtr
cc(
2981 static_pointer_cast
<ClassConstantExpression
>(node
));
2982 StringData
* nName
= StringData::GetStaticString(cc
->getConName());
2983 if (cc
->isStatic()) {
2987 } else if (cc
->getClass()) {
2989 ExpressionPtr
cls(cc
->getClass());
2993 } else if (cc
->getOriginalClass() &&
2994 !cc
->getOriginalClass()->isTrait()) {
2995 // C::Constant inside a class
2996 const std::string
& clsName
= cc
->getOriginalClassName();
2997 StringData
* nCls
= StringData::GetStaticString(clsName
);
2998 e
.ClsCnsD(nName
, nCls
);
2999 } else if (cc
->isSelf()) {
3000 // self::Constant inside trait or pseudomain
3003 } else if (cc
->isParent()) {
3004 // parent::Constant inside trait or pseudomain
3008 // C::Constant inside a trait or pseudomain
3009 // Be careful to keep this case here after the isSelf and
3010 // isParent cases because StaticClassName::resolveClass()
3011 // will set cc->originalClassName to the trait's name for
3012 // the isSelf and isParent cases, but self and parent must
3013 // be resolved dynamically when used inside of traits.
3014 const std::string
& clsName
= cc
->getOriginalClassName();
3015 StringData
* nCls
= StringData::GetStaticString(clsName
);
3016 e
.ClsCnsD(nName
, nCls
);
3021 case Expression::KindOfConstantExpression
: {
3022 ConstantExpressionPtr
c(static_pointer_cast
<ConstantExpression
>(node
));
3025 } else if (c
->isBoolean()) {
3026 if (c
->getBooleanValue()) {
3033 std::string nameStr
= c
->getOriginalName();
3034 StringData
* nName
= StringData::GetStaticString(nameStr
);
3035 if (c
->hadBackslash()) {
3038 const std::string
& nonNSName
= c
->getNonNSOriginalName();
3039 if (nonNSName
!= nameStr
) {
3040 StringData
* nsName
= nName
;
3041 nName
= StringData::GetStaticString(nonNSName
);
3042 e
.CnsU(nsName
, nName
);
3044 e
.Cns(StringData::GetStaticString(c
->getName()));
3051 case Expression::KindOfEncapsListExpression
: {
3052 EncapsListExpressionPtr
el(
3053 static_pointer_cast
<EncapsListExpression
>(node
));
3054 ExpressionListPtr
args(el
->getExpressions());
3055 int n
= args
? args
->getCount() : 0;
3057 FPIRegionRecorder
* fpi
= nullptr;
3058 if (el
->getType() == '`') {
3059 const static StringData
* s_shell_exec
=
3060 StringData::GetStaticString("shell_exec");
3061 Offset fpiStart
= m_ue
.bcPos();
3062 e
.FPushFuncD(1, s_shell_exec
);
3063 fpi
= new FPIRegionRecorder(this, m_ue
, m_evalStack
, fpiStart
);
3067 visit((*args
)[i
++]);
3068 emitConvertToCellOrLoc(e
);
3070 emitConvertToCell(e
);
3074 visit((*args
)[i
++]);
3075 emitConvertToCell(e
);
3076 emitConvertSecondToCell(e
);
3081 e
.String(empty_string
.get());
3084 if (el
->getType() == '`') {
3085 emitConvertToCell(e
);
3086 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::NopOut
, false, 0, 0);
3094 case Expression::KindOfArrayElementExpression
: {
3095 ArrayElementExpressionPtr
ae(
3096 static_pointer_cast
<ArrayElementExpression
>(node
));
3097 if (!ae
->isSuperGlobal() || !ae
->getOffset()) {
3098 visit(ae
->getVariable());
3099 // XHP syntax allows for expressions like "($a =& $b)[0]". We
3100 // handle this by unboxing the var produced by "($a =& $b)".
3101 emitConvertToCellIfVar(e
);
3104 ExpressionPtr offset
= ae
->getOffset();
3106 if (!ae
->isSuperGlobal() && offset
&&
3107 offset
->getScalarValue(v
) && (v
.isInteger() || v
.isString())) {
3109 m_evalStack
.push(StackSym::T
);
3110 m_evalStack
.setString(
3111 StringData::GetStaticString(v
.toCStrRef().get()));
3113 m_evalStack
.push(StackSym::I
);
3114 m_evalStack
.setInt(v
.asInt64Val());
3117 } else if (visit(offset
)) {
3118 emitConvertToCellOrLoc(e
);
3119 if (ae
->isSuperGlobal()) {
3127 if (!ae
->hasAnyContext(Expression::AccessContext
|
3128 Expression::ObjectContext
)) {
3129 m_tempLoc
= ae
->getLocation();
3134 case Expression::KindOfSimpleFunctionCall
: {
3135 SimpleFunctionCallPtr
call(
3136 static_pointer_cast
<SimpleFunctionCall
>(node
));
3137 ExpressionListPtr params
= call
->getParams();
3138 auto inputIsAnObject
= [&](int inputIndex
) {
3139 m_metaInfo
.addKnownDataType(KindOfObject
, /* predicted */ false,
3140 m_ue
.bcPos(), false, inputIndex
);
3143 if (call
->isFatalFunction()) {
3144 if (params
&& params
->getCount() == 1) {
3145 ExpressionPtr p
= (*params
)[0];
3147 if (p
->getScalarValue(v
)) {
3148 assert(v
.isString());
3149 StringData
* msg
= StringData::GetStaticString(v
.toString());
3150 throw IncludeTimeFatalException(call
, msg
->data());
3154 } else if (emitCallUserFunc(e
, call
)) {
3156 } else if (call
->isCallToFunction("array_key_exists")) {
3157 if (params
&& params
->getCount() == 2) {
3158 visit((*params
)[0]);
3159 emitConvertToCell(e
);
3160 visit((*params
)[1]);
3161 emitConvertToCell(e
);
3165 } else if (call
->isCallToFunction("strlen")) {
3166 if (params
&& params
->getCount() == 1) {
3167 visit((*params
)[0]);
3168 emitConvertToCell(e
);
3172 } else if (call
->isCallToFunction("define")) {
3173 if (params
&& params
->getCount() == 2) {
3174 ExpressionPtr p0
= (*params
)[0];
3176 if (p0
->getScalarValue(v0
) && v0
.isString()) {
3177 const StringData
* cname
=
3178 StringData::GetStaticString(v0
.toString());
3179 ExpressionPtr p1
= (*params
)[1];
3181 if (p1
->getScalarValue(v1
) && v1
.isAllowedAsConstantValue()) {
3182 m_ue
.addPreConst(cname
, *v1
.getTypedAccessor());
3184 m_ue
.addPreConst(cname
, *null_variant
.getTypedAccessor());
3188 emitConvertToCell(e
);
3193 } else if (call
->isCompilerCallToFunction("hphp_unpack_continuation")) {
3194 assert(!params
|| params
->getCount() == 0);
3195 int yieldLabelCount
= call
->getFunctionScope()->getYieldLabelCount();
3197 emitContinuationSwitch(e
, yieldLabelCount
);
3199 } else if (call
->isCompilerCallToFunction("hphp_create_continuation")) {
3200 assert(params
&& (params
->getCount() == 3 ||
3201 params
->getCount() == 4));
3202 ExpressionPtr name
= (*params
)[1];
3204 UNUSED
bool isScalar
= name
->getScalarValue(nameVar
);
3205 assert(isScalar
&& nameVar
.isString());
3206 const StringData
* nameStr
=
3207 StringData::GetStaticString(nameVar
.getStringData());
3208 bool callGetArgs
= params
->getCount() == 4;
3209 e
.CreateCont(callGetArgs
, nameStr
);
3211 } else if (call
->isCompilerCallToFunction("hphp_continuation_done")) {
3212 assert(params
&& params
->getCount() == 1);
3213 visit((*params
)[0]);
3214 emitConvertToCell(e
);
3216 assert(m_evalStack
.size() == 1);
3220 } else if ((call
->isCallToFunction("class_exists") ||
3221 call
->isCallToFunction("interface_exists") ||
3222 call
->isCallToFunction("trait_exists")) && params
&&
3223 (params
->getCount() == 1 || params
->getCount() == 2)) {
3225 emitNameString(e
, (*params
)[0]);
3226 emitConvertToCell(e
);
3228 // Push autoload, defaulting to true
3229 if (params
->getCount() == 1) {
3232 visit((*params
)[1]);
3233 emitConvertToCell(e
);
3235 if (call
->isCallToFunction("class_exists")) {
3237 } else if (call
->isCallToFunction("interface_exists")) {
3238 e
.InterfaceExists();
3240 assert(call
->isCallToFunction("trait_exists"));
3245 #define TYPE_CHECK_INSTR(what, What) \
3246 else if (call->isCallToFunction("is_"#what) && \
3247 params && params->getCount() == 1) { \
3248 visit((*call->getParams())[0]); \
3249 emitIs ## What(e); \
3252 TYPE_CHECK_INSTR(null
, Null
)
3253 TYPE_CHECK_INSTR(object
, Object
)
3254 TYPE_CHECK_INSTR(array
, Array
)
3255 TYPE_CHECK_INSTR(string
, String
)
3256 TYPE_CHECK_INSTR(int, Int
)
3257 TYPE_CHECK_INSTR(integer
, Int
)
3258 TYPE_CHECK_INSTR(long, Int
)
3259 TYPE_CHECK_INSTR(bool, Bool
)
3260 TYPE_CHECK_INSTR(double, Double
)
3261 TYPE_CHECK_INSTR(real
, Double
)
3262 TYPE_CHECK_INSTR(float, Double
)
3263 #undef TYPE_CHECK_INSTR
3266 case Expression::KindOfDynamicFunctionCall
: {
3267 emitFuncCall(e
, static_pointer_cast
<FunctionCall
>(node
));
3271 case Expression::KindOfIncludeExpression
: {
3272 IncludeExpressionPtr
ie(static_pointer_cast
<IncludeExpression
>(node
));
3273 if (ie
->isReqLit()) {
3274 StringData
* nValue
= StringData::GetStaticString(ie
->includePath());
3277 visit(ie
->getExpression());
3278 emitConvertToCell(e
);
3280 switch (ie
->getOp()) {
3284 case T_INCLUDE_ONCE
:
3290 case T_REQUIRE_ONCE
:
3291 if (ie
->isDocumentRoot()) {
3301 case Expression::KindOfListAssignment
: {
3302 ListAssignmentPtr
la(static_pointer_cast
<ListAssignment
>(node
));
3303 ExpressionPtr rhs
= la
->getArray();
3305 // visitListAssignmentLHS should have handled this
3308 bool nullRHS
= la
->getRHSKind() == ListAssignment::Null
;
3309 // Assign RHS to temp local, unless it's already a simple variable
3310 bool simpleRHS
= rhs
->is(Expression::KindOfSimpleVariable
)
3311 && !static_pointer_cast
<SimpleVariable
>(rhs
)->getAlwaysStash();
3313 Offset start
= InvalidAbsoluteOffset
;
3315 if (!simpleRHS
&& la
->isRhsFirst()) {
3316 tempLocal
= emitVisitAndSetUnnamedL(e
, rhs
);
3317 start
= m_ue
.bcPos();
3320 // We use "index chains" to deal with nested list assignment. We will
3321 // end up with one index chain per expression we need to assign to.
3322 // The helper function will populate indexChains.
3323 std::vector
<IndexChain
*> indexChains
;
3324 IndexChain workingChain
;
3325 visitListAssignmentLHS(e
, la
, workingChain
, indexChains
);
3327 if (!simpleRHS
&& !la
->isRhsFirst()) {
3328 assert(tempLocal
== -1);
3329 assert(start
== InvalidAbsoluteOffset
);
3330 tempLocal
= emitVisitAndSetUnnamedL(e
, rhs
);
3331 start
= m_ue
.bcPos();
3334 // Assign elements, right-to-left
3335 for (int i
= (int)indexChains
.size() - 1; i
>= 0; --i
) {
3336 IndexChain
* currIndexChain
= indexChains
[i
];
3337 if (currIndexChain
->empty()) {
3347 emitVirtualLocal(tempLocal
);
3349 for (int j
= 0; j
< (int)currIndexChain
->size(); ++j
) {
3350 m_evalStack
.push(StackSym::I
);
3351 m_evalStack
.setInt((*currIndexChain
)[j
]);
3359 delete currIndexChain
;
3362 // Leave the RHS on the stack
3366 emitPushAndFreeUnnamedL(e
, tempLocal
, start
);
3372 case Expression::KindOfNewObjectExpression
: {
3373 NewObjectExpressionPtr
ne(
3374 static_pointer_cast
<NewObjectExpression
>(node
));
3375 ExpressionListPtr
params(ne
->getParams());
3376 int numParams
= params
? params
->getCount() : 0;
3377 ClassScopeRawPtr cls
= ne
->getOriginalClass();
3380 if (ne
->isStatic()) {
3383 fpiStart
= m_ue
.bcPos();
3384 e
.FPushCtor(numParams
);
3385 } else if (ne
->getOriginalName().empty()) {
3387 visit(ne
->getNameExp());
3389 fpiStart
= m_ue
.bcPos();
3390 e
.FPushCtor(numParams
);
3391 } else if ((ne
->isSelf() || ne
->isParent()) &&
3392 (!cls
|| cls
->isTrait() ||
3393 (ne
->isParent() && cls
->getOriginalParent().empty()))) {
3395 // new self() inside a trait or code statically not inside any class
3398 // new parent() inside a trait, code statically not inside any
3399 // class, or a class with no parent
3402 fpiStart
= m_ue
.bcPos();
3403 e
.FPushCtor(numParams
);
3405 // new C() inside trait or pseudomain
3406 fpiStart
= m_ue
.bcPos();
3407 e
.FPushCtorD(numParams
,
3408 StringData::GetStaticString(ne
->getOriginalClassName()));
3412 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
3413 for (int i
= 0; i
< numParams
; i
++) {
3414 emitFuncCallArg(e
, (*params
)[i
], i
);
3419 if (Option::WholeProgram
) {
3420 FunctionScopePtr fs
= ne
->getFuncScope();
3421 if (fs
&& !fs
->getReturnType()) {
3422 m_evalStack
.setKnownType(KindOfNull
, false /* inferred */);
3423 m_evalStack
.setNotRef();
3430 case Expression::KindOfObjectMethodExpression
: {
3431 ObjectMethodExpressionPtr
om(
3432 static_pointer_cast
<ObjectMethodExpression
>(node
));
3435 visit(om
->getObject());
3436 m_tempLoc
= om
->getLocation();
3437 emitConvertToCell(e
);
3438 StringData
* clsName
= getClassName(om
->getObject());
3439 ExpressionListPtr
params(om
->getParams());
3440 int numParams
= params
? params
->getCount() : 0;
3442 Offset fpiStart
= 0;
3443 ExpressionPtr methName
= om
->getNameExp();
3444 bool useDirectForm
= false;
3445 if (methName
->is(Expression::KindOfScalarExpression
)) {
3446 ScalarExpressionPtr
sval(
3447 static_pointer_cast
<ScalarExpression
>(methName
));
3448 const std::string
& methStr
= sval
->getOriginalLiteralString();
3449 if (!methStr
.empty()) {
3452 // Use getOriginalLiteralString(), which hasn't been
3453 // case-normalized, since __call() needs to preserve
3455 StringData
* nameLiteral
= StringData::GetStaticString(methStr
);
3456 fpiStart
= m_ue
.bcPos();
3457 e
.FPushObjMethodD(numParams
, nameLiteral
);
3458 useDirectForm
= true;
3461 if (!useDirectForm
) {
3465 emitConvertToCell(e
);
3466 fpiStart
= m_ue
.bcPos();
3467 e
.FPushObjMethod(numParams
);
3470 Id id
= m_ue
.mergeLitstr(clsName
);
3471 m_metaInfo
.add(fpiStart
, Unit::MetaInfo::Class
, false,
3472 useDirectForm
? 0 : 1, id
);
3475 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
3478 for (int i
= 0; i
< numParams
; i
++) {
3479 emitFuncCallArg(e
, (*params
)[i
], i
);
3483 if (Option::WholeProgram
) {
3484 fixReturnType(e
, om
);
3489 case Expression::KindOfObjectPropertyExpression
: {
3490 ObjectPropertyExpressionPtr
op(
3491 static_pointer_cast
<ObjectPropertyExpression
>(node
));
3492 ExpressionPtr obj
= op
->getObject();
3493 SimpleVariablePtr sv
= dynamic_pointer_cast
<SimpleVariable
>(obj
);
3494 if (sv
&& sv
->isThis() && sv
->hasContext(Expression::ObjectContext
)) {
3495 if (sv
->isGuarded()) {
3496 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::GuardedThis
,
3500 m_evalStack
.push(StackSym::H
);
3504 StringData
* clsName
= getClassName(op
->getObject());
3506 m_evalStack
.setKnownCls(clsName
, false);
3508 emitNameString(e
, op
->getProperty(), true);
3509 if (!op
->hasAnyContext(Expression::AccessContext
|
3510 Expression::ObjectContext
)) {
3511 m_tempLoc
= op
->getLocation();
3517 case Expression::KindOfQOpExpression
: {
3518 QOpExpressionPtr
q(static_pointer_cast
<QOpExpression
>(node
));
3520 // <expr> ? <expr> : <expr>
3521 Label tru
, fals
, done
;
3523 Emitter
condEmitter(q
->getCondition(), m_ue
, *this);
3524 visitIfCondition(q
->getCondition(), condEmitter
,
3530 if (currentPositionIsReachable()) {
3532 emitConvertToCell(e
);
3535 if (fals
.isUsed()) fals
.set(e
);
3536 if (currentPositionIsReachable()) {
3538 emitConvertToCell(e
);
3540 if (done
.isUsed()) {
3542 m_evalStack
.cleanTopMeta();
3547 visit(q
->getCondition());
3548 emitConvertToCell(e
);
3553 emitConvertToCell(e
);
3555 m_evalStack
.cleanTopMeta();
3560 case Expression::KindOfScalarExpression
: {
3562 ex
->getScalarValue(v
);
3563 switch (v
.getType()) {
3565 case KindOfStaticString
:
3568 scalarExp(static_pointer_cast
<ScalarExpression
>(node
));
3569 // Inside traits, __class__ cannot be resolved yet,
3570 // so emit call to get_class.
3571 if (scalarExp
->getType() == T_CLASS_C
&&
3572 ex
->getFunctionScope()->getContainingClass() &&
3573 ex
->getFunctionScope()->getContainingClass()->isTrait()) {
3574 static const StringData
* fname
=
3575 StringData::GetStaticString("get_class");
3576 Offset fpiStart
= m_ue
.bcPos();
3577 e
.FPushFuncD(0, fname
);
3579 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
3584 StringData
* nValue
=
3585 StringData::GetStaticString(v
.getStringData());
3591 e
.Int(v
.getInt64());
3594 e
.Double(v
.getDouble()); break;
3601 case Expression::KindOfSimpleVariable
: {
3602 SimpleVariablePtr
sv(static_pointer_cast
<SimpleVariable
>(node
));
3604 if (sv
->hasContext(Expression::ObjectContext
)) {
3605 if (sv
->isGuarded()) {
3606 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::GuardedThis
,
3610 } else if (sv
->getFunctionScope()->needsLocalThis()) {
3611 static const StringData
* thisStr
=
3612 StringData::GetStaticString("this");
3613 Id thisId
= m_curFunc
->lookupVarId(thisStr
);
3614 emitVirtualLocal(thisId
);
3616 if (sv
->isGuarded()) {
3617 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::GuardedThis
,
3621 e
.BareThis(!sv
->hasContext(Expression::ExistContext
));
3625 StringData
* nLiteral
= StringData::GetStaticString(sv
->getName());
3626 if (sv
->isSuperGlobal()) {
3631 Id i
= m_curFunc
->lookupVarId(nLiteral
);
3632 emitVirtualLocal(i
);
3633 if (!sv
->couldBeAliased()) {
3634 m_evalStack
.setNotRef();
3636 if (sv
->getAlwaysStash() &&
3637 !sv
->hasAnyContext(Expression::ExistContext
|
3638 Expression::RefValue
|
3639 Expression::LValue
|
3640 Expression::RefParameter
)) {
3641 emitConvertToCell(e
);
3648 case Expression::KindOfDynamicVariable
: {
3649 DynamicVariablePtr
dv(static_pointer_cast
<DynamicVariable
>(node
));
3650 visit(dv
->getSubExpression());
3651 emitConvertToCellOrLoc(e
);
3656 case Expression::KindOfStaticMemberExpression
: {
3657 StaticMemberExpressionPtr
sm(
3658 static_pointer_cast
<StaticMemberExpression
>(node
));
3659 emitVirtualClassBase(e
, sm
.get());
3660 emitNameString(e
, sm
->getExp());
3665 case Expression::KindOfArrayPairExpression
: {
3666 ArrayPairExpressionPtr
ap(
3667 static_pointer_cast
<ArrayPairExpression
>(node
));
3669 ExpressionPtr key
= ap
->getName();
3670 if (m_staticArrays
.size()) {
3671 HphpArray
* a
= m_staticArrays
.back();
3672 ExpressionPtr val
= ap
->getValue();
3676 initScalar(tvVal
, val
);
3678 if (key
!= nullptr) {
3680 assert(key
->isScalar());
3682 if (key
->is(Expression::KindOfConstantExpression
)) {
3683 ConstantExpressionPtr
c(
3684 static_pointer_cast
<ConstantExpression
>(key
));
3686 // PHP casts null keys to "".
3687 tvKey
.m_data
.pstr
= empty_string
.get();
3688 tvKey
.m_type
= KindOfString
;
3689 } else if (c
->isBoolean()) {
3690 // PHP casts bool keys to 0 or 1
3691 tvKey
.m_data
.num
= c
->getBooleanValue() ? 1 : 0;
3692 tvKey
.m_type
= KindOfInt64
;
3694 // Handle INF and NAN
3695 assert(c
->isDouble());
3697 c
->getScalarValue(v
);
3698 tvKey
.m_data
.num
= v
.toInt64();
3699 tvKey
.m_type
= KindOfInt64
;
3701 } else if (key
->is(Expression::KindOfScalarExpression
)) {
3702 ScalarExpressionPtr
sval(
3703 static_pointer_cast
<ScalarExpression
>(key
));
3704 const std::string
* s
;
3707 if (sval
->getString(s
)) {
3708 StringData
* sd
= StringData::GetStaticString(*s
);
3710 if (sd
->isStrictlyInteger(n
)) {
3711 tvKey
.m_data
.num
= n
;
3712 tvKey
.m_type
= KindOfInt64
;
3714 tvKey
.m_data
.pstr
= sd
;
3715 tvKey
.m_type
= KindOfString
;
3717 } else if (sval
->getInt(i
)) {
3718 tvKey
.m_data
.num
= i
;
3719 tvKey
.m_type
= KindOfInt64
;
3720 } else if (sval
->getDouble(d
)) {
3721 tvKey
.m_data
.num
= toInt64(d
);
3722 tvKey
.m_type
= KindOfInt64
;
3729 a
->set(tvAsCVarRef(&tvKey
), tvAsVariant(&tvVal
), false);
3731 a
->append(tvAsCVarRef(&tvVal
), false);
3734 // Assume new array is on top of stack
3735 bool hasKey
= (bool)key
;
3738 emitConvertToCellOrLoc(e
);
3740 visit(ap
->getValue());
3742 emitConvertToVar(e
);
3744 emitConvertSecondToCell(e
);
3750 emitConvertToCell(e
);
3752 emitConvertSecondToCell(e
);
3761 case Expression::KindOfExpressionList
: {
3762 ExpressionListPtr
el(static_pointer_cast
<ExpressionList
>(node
));
3763 int nelem
= el
->getCount(), i
;
3764 bool pop
= el
->getListKind() != ExpressionList::ListKindParam
;
3765 int keep
= el
->getListKind() == ExpressionList::ListKindLeft
?
3768 for (i
= 0; i
< nelem
; i
++) {
3769 ExpressionPtr
p((*el
)[i
]);
3771 if (pop
&& i
!= keep
) {
3780 case Expression::KindOfParameterExpression
: {
3783 case Expression::KindOfModifierExpression
: {
3786 case Expression::KindOfUserAttribute
: {
3789 case Expression::KindOfClosureExpression
: {
3790 // Closures are implemented by anonymous classes that extend Closure.
3791 // There is one anonymous class per closure body.
3792 ClosureExpressionPtr
ce(static_pointer_cast
<ClosureExpression
>(node
));
3794 // Build a convenient list of use-variables. Each one corresponds to:
3795 // (a) an instance variable, to store the value until call time
3796 // (b) a parameter of the generated constructor
3797 // (c) an argument to the constructor at the definition site
3798 // (d) a line of code in the generated constructor;
3799 // (e) a line of code in the generated prologue to the closure body
3800 ExpressionListPtr
useList(ce
->getClosureVariables());
3801 ClosureUseVarVec useVars
;
3802 int useCount
= (useList
? useList
->getCount() : 0);
3804 for (int i
= 0; i
< useCount
; ++i
) {
3805 ParameterExpressionPtr
var(
3806 static_pointer_cast
<ParameterExpression
>((*useList
)[i
]));
3807 StringData
* varName
= StringData::GetStaticString(var
->getName());
3808 useVars
.push_back(ClosureUseVar(varName
, var
->isRef()));
3812 StringData
* className
= newClosureName();
3813 const static StringData
* parentName
=
3814 StringData::GetStaticString("Closure");
3815 const Location
* sLoc
= ce
->getLocation().get();
3816 PreClassEmitter
* pce
= m_ue
.newPreClassEmitter(
3817 className
, PreClass::AlwaysHoistable
);
3818 pce
->init(sLoc
->line0
, sLoc
->line1
, m_ue
.bcPos(),
3819 AttrUnique
| AttrPersistent
, parentName
, nullptr);
3821 // We're still at the closure definition site. Emit code to instantiate
3822 // the new anonymous class, with the use variables as arguments.
3823 ExpressionListPtr
valuesList(ce
->getClosureValues());
3824 for (int i
= 0; i
< useCount
; ++i
) {
3825 emitBuiltinCallArg(e
, (*valuesList
)[i
], i
, useVars
[i
].second
);
3827 e
.CreateCl(useCount
, className
);
3829 // From here on out, we're just building metadata for the closure.
3831 // Instance variables.
3833 tvWriteUninit(&uninit
);
3834 for (auto& useVar
: useVars
) {
3835 pce
->addProperty(useVar
.first
, AttrPrivate
, nullptr, nullptr,
3836 &uninit
, KindOfInvalid
);
3839 // The __invoke method. This is the body of the closure, preceded by
3840 // code that pulls the object's instance variables into locals.
3841 static const StringData
* invokeName
=
3842 StringData::GetStaticString("__invoke");
3843 FuncEmitter
* invoke
= m_ue
.newMethodEmitter(invokeName
, pce
);
3844 invoke
->setIsClosureBody(true);
3845 pce
->addMethod(invoke
);
3846 MethodStatementPtr
body(
3847 static_pointer_cast
<MethodStatement
>(ce
->getClosureFunction()));
3848 invoke
->setHasGeneratorAsBody(!!body
->getGeneratorFunc());
3849 postponeMeth(body
, invoke
, false, new ClosureUseVarVec(useVars
));
3853 case Expression::KindOfYieldExpression
: {
3854 YieldExpressionPtr
y(static_pointer_cast
<YieldExpression
>(node
));
3855 assert(m_evalStack
.size() == 0);
3857 // evaluate expression passed to yield
3858 visit(y
->getExpression());
3859 emitConvertToCell(e
);
3861 // pack continuation and set the return label
3862 assert(m_evalStack
.size() == 1);
3863 m_metaInfo
.addKnownDataType(
3864 KindOfObject
, false, m_ue
.bcPos(), false, 1);
3865 e
.PackCont(y
->getLabel());
3868 assert(m_evalStack
.size() == 0);
3871 // emit return label
3872 m_yieldLabels
[y
->getLabel()].set(e
);
3874 // check for exception and retrieve result
3875 assert(m_evalStack
.size() == 0);
3876 m_metaInfo
.addKnownDataType(
3877 KindOfObject
, false, m_ue
.bcPos(), false, 0);
3880 assert(m_evalStack
.size() == 1);
3889 int EmitterVisitor::scanStackForLocation(int iLast
) {
3891 assert(iLast
< (int)m_evalStack
.size());
3892 for (int i
= iLast
; i
>= 0; --i
) {
3893 char marker
= StackSym::GetMarker(m_evalStack
.get(i
));
3894 if (marker
!= StackSym::E
&& marker
!= StackSym::W
&&
3895 marker
!= StackSym::P
&& marker
!= StackSym::M
) {
3899 InvariantViolation("Emitter expected a location on the stack but none "
3900 "was found (at offset %d)",
3905 void EmitterVisitor::buildVectorImm(std::vector
<uchar
>& vectorImm
,
3906 int iFirst
, int iLast
, bool allowW
,
3908 assert(iFirst
>= 0);
3909 assert(iFirst
<= iLast
);
3910 assert(iLast
< (int)m_evalStack
.size());
3912 vectorImm
.reserve(iLast
- iFirst
+ 1);
3917 * Because of php's order of evaluation rules, we store the classref
3918 * for certain types of S-vectors at the end, instead of the front.
3919 * See emitCls for details.
3923 char sym
= m_evalStack
.get(iFirst
);
3924 char symFlavor
= StackSym::GetSymFlavor(sym
);
3925 char marker
= StackSym::GetMarker(sym
);
3926 m_metaInfo
.addKnownDataType(m_evalStack
.getKnownType(iFirst
),
3927 m_evalStack
.isTypePredicted(iFirst
),
3928 m_ue
.bcPos(), true, 0);
3929 if (const StringData
* cls
= m_evalStack
.getClsName(iFirst
)) {
3930 Id id
= m_ue
.mergeLitstr(cls
);
3931 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::Class
, true, 0, id
);
3935 if (symFlavor
== StackSym::C
) {
3936 vectorImm
.push_back(LNC
);
3937 } else if (symFlavor
== StackSym::L
) {
3938 vectorImm
.push_back(LNL
);
3944 if (symFlavor
== StackSym::C
) {
3945 vectorImm
.push_back(LGC
);
3946 } else if (symFlavor
== StackSym::L
) {
3947 vectorImm
.push_back(LGL
);
3953 if (symFlavor
!= StackSym::L
&& symFlavor
!= StackSym::C
) {
3954 unexpectedStackSym(sym
, "S-vector base, prop name");
3956 if (m_evalStack
.get(iLast
) != StackSym::AM
) {
3957 unexpectedStackSym(sym
, "S-vector base, class ref");
3959 const bool curIsLoc
= symFlavor
== StackSym::L
;
3960 vectorImm
.push_back(curIsLoc
? LSL
: LSC
);
3962 case StackSym::None
: {
3963 if (symFlavor
== StackSym::L
) {
3964 vectorImm
.push_back(LL
);
3965 } else if (symFlavor
== StackSym::C
) {
3966 vectorImm
.push_back(LC
);
3967 } else if (symFlavor
== StackSym::R
) {
3968 vectorImm
.push_back(LR
);
3969 } else if (symFlavor
== StackSym::H
) {
3970 vectorImm
.push_back(LH
);
3979 if (symFlavor
== StackSym::L
) {
3980 encodeIvaToVector(vectorImm
, m_evalStack
.getLoc(iFirst
));
3985 while (i
<= iLast
) {
3986 char sym
= m_evalStack
.get(i
);
3987 char symFlavor
= StackSym::GetSymFlavor(sym
);
3988 char marker
= StackSym::GetMarker(sym
);
3991 if (const StringData
* name
= m_evalStack
.getName(i
)) {
3992 strid
= m_ue
.mergeLitstr(name
);
3993 // If this string is an m-vector litstr it will be stored in the
3994 // m-vector later on in this function. Don't duplicate it in the
3996 if (symFlavor
!= StackSym::T
) {
3997 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::String
,
3998 true, metaI
, strid
);
4001 if (const StringData
* cls
= m_evalStack
.getClsName(i
)) {
4002 const int mcodeNum
= i
- (iFirst
+ 1);
4003 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::MVecPropClass
,
4004 false, mcodeNum
, m_ue
.mergeLitstr(cls
));
4006 m_metaInfo
.addKnownDataType(m_evalStack
.getKnownType(i
),
4007 m_evalStack
.isTypePredicted(i
),
4008 m_ue
.bcPos(), true, i
- iFirst
);
4012 assert(symFlavor
== StackSym::A
);
4016 if (symFlavor
== StackSym::L
) {
4017 vectorImm
.push_back(MEL
);
4018 } else if (symFlavor
== StackSym::T
) {
4019 vectorImm
.push_back(MET
);
4020 } else if (symFlavor
== StackSym::I
) {
4021 vectorImm
.push_back(MEI
);
4023 vectorImm
.push_back(MEC
);
4028 vectorImm
.push_back(MW
);
4030 throw IncludeTimeFatalException(e
.getNode(),
4031 "Cannot use [] for reading");
4035 if (symFlavor
== StackSym::L
) {
4036 vectorImm
.push_back(MPL
);
4037 } else if (symFlavor
== StackSym::T
) {
4038 vectorImm
.push_back(MPT
);
4040 vectorImm
.push_back(MPC
);
4046 default: assert(false); break;
4049 if (symFlavor
== StackSym::L
) {
4050 encodeIvaToVector(vectorImm
, m_evalStack
.getLoc(i
));
4051 } else if (symFlavor
== StackSym::T
) {
4052 assert(strid
!= -1);
4053 encodeToVector
<int32_t>(vectorImm
, strid
);
4054 } else if (symFlavor
== StackSym::I
) {
4055 encodeToVector
<int64_t>(vectorImm
, m_evalStack
.getInt(i
));
4059 if (marker
!= StackSym::W
) ++metaI
;
4063 void EmitterVisitor::emitPop(Emitter
& e
) {
4064 if (checkIfStackEmpty("Pop*")) return;
4065 LocationGuard
loc(e
, m_tempLoc
);
4068 emitClsIfSPropBase(e
);
4069 int iLast
= m_evalStack
.size()-1;
4070 int i
= scanStackForLocation(iLast
);
4073 char sym
= m_evalStack
.get(i
);
4074 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
4076 case StackSym::L
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4077 case StackSym::C
: e
.PopC(); break;
4078 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4079 case StackSym::CN
: e
.CGetN(); e
.PopC(); break;
4080 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4081 case StackSym::CG
: e
.CGetG(); e
.PopC(); break;
4082 case StackSym::LS
: e
.CGetL2(m_evalStack
.getLoc(i
)); // fall through
4083 case StackSym::CS
: e
.CGetS(); e
.PopC(); break;
4084 case StackSym::V
: e
.PopV(); break;
4085 case StackSym::R
: e
.PopR(); break;
4087 unexpectedStackSym(sym
, "emitPop");
4092 std::vector
<uchar
> vectorImm
;
4093 buildVectorImm(vectorImm
, i
, iLast
, false, e
);
4099 void EmitterVisitor::emitCGetL2(Emitter
& e
) {
4100 assert(m_evalStack
.size() >= 2);
4101 assert(m_evalStack
.sizeActual() >= 1);
4102 assert(StackSym::GetSymFlavor(m_evalStack
.get(m_evalStack
.size() - 2))
4104 int localIdx
= m_evalStack
.getLoc(m_evalStack
.size() - 2);
4108 void EmitterVisitor::emitCGetL3(Emitter
& e
) {
4109 assert(m_evalStack
.size() >= 3);
4110 assert(m_evalStack
.sizeActual() >= 2);
4111 assert(StackSym::GetSymFlavor(m_evalStack
.get(m_evalStack
.size() - 3))
4113 int localIdx
= m_evalStack
.getLoc(m_evalStack
.size() - 3);
4117 void EmitterVisitor::emitAGet(Emitter
& e
) {
4118 if (checkIfStackEmpty("AGet*")) return;
4120 emitConvertToCellOrLoc(e
);
4121 switch (char sym
= m_evalStack
.top()) {
4123 e
.AGetL(m_evalStack
.getLoc(m_evalStack
.size() - 1));
4129 unexpectedStackSym(sym
, "emitAGet");
4133 void EmitterVisitor::emitCGet(Emitter
& e
) {
4134 if (checkIfStackEmpty("CGet*")) return;
4135 LocationGuard
loc(e
, m_tempLoc
);
4138 emitClsIfSPropBase(e
);
4139 int iLast
= m_evalStack
.size()-1;
4140 int i
= scanStackForLocation(iLast
);
4143 char sym
= m_evalStack
.get(i
);
4144 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
4146 case StackSym::L
: e
.CGetL(m_evalStack
.getLoc(i
)); break;
4147 case StackSym::C
: /* nop */ break;
4148 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4149 case StackSym::CN
: e
.CGetN(); break;
4150 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4151 case StackSym::CG
: e
.CGetG(); break;
4152 case StackSym::LS
: e
.CGetL2(m_evalStack
.getLoc(i
)); // fall through
4153 case StackSym::CS
: e
.CGetS(); break;
4154 case StackSym::V
: e
.Unbox(); break;
4155 case StackSym::R
: e
.UnboxR(); break;
4157 unexpectedStackSym(sym
, "emitCGet");
4162 std::vector
<uchar
> vectorImm
;
4163 buildVectorImm(vectorImm
, i
, iLast
, false, e
);
4168 void EmitterVisitor::emitVGet(Emitter
& e
) {
4169 if (checkIfStackEmpty("VGet*")) return;
4170 LocationGuard
loc(e
, m_tempLoc
);
4173 emitClsIfSPropBase(e
);
4174 int iLast
= m_evalStack
.size()-1;
4175 int i
= scanStackForLocation(iLast
);
4178 char sym
= m_evalStack
.get(i
);
4179 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
4181 case StackSym::L
: e
.VGetL(m_evalStack
.getLoc(i
)); break;
4182 case StackSym::C
: e
.Box(); break;
4183 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4184 case StackSym::CN
: e
.VGetN(); break;
4185 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4186 case StackSym::CG
: e
.VGetG(); break;
4187 case StackSym::LS
: e
.CGetL2(m_evalStack
.getLoc(i
)); // fall through
4188 case StackSym::CS
: e
.VGetS(); break;
4189 case StackSym::V
: /* nop */ break;
4190 case StackSym::R
: e
.BoxR(); break;
4192 unexpectedStackSym(sym
, "emitVGet");
4197 std::vector
<uchar
> vectorImm
;
4198 buildVectorImm(vectorImm
, i
, iLast
, true, e
);
4203 Id
EmitterVisitor::emitVisitAndSetUnnamedL(Emitter
& e
, ExpressionPtr exp
) {
4205 emitConvertToCell(e
);
4207 // HACK: emitVirtualLocal would pollute m_evalStack before visiting exp,
4208 // YieldExpression won't be happy
4209 Id tempLocal
= m_curFunc
->allocUnnamedLocal();
4210 e
.getUnitEmitter().emitOp(OpSetL
);
4211 e
.getUnitEmitter().emitIVA(tempLocal
);
4217 void EmitterVisitor::emitPushAndFreeUnnamedL(Emitter
& e
, Id tempLocal
, Offset start
) {
4218 assert(tempLocal
>= 0);
4219 assert(start
!= InvalidAbsoluteOffset
);
4220 emitVirtualLocal(tempLocal
);
4222 newFaultRegion(start
, m_ue
.bcPos(), new UnsetUnnamedLocalThunklet(tempLocal
));
4223 emitVirtualLocal(tempLocal
);
4225 m_curFunc
->freeUnnamedLocal(tempLocal
);
4228 EmitterVisitor::PassByRefKind
EmitterVisitor::getPassByRefKind(ExpressionPtr exp
) {
4229 // The PassByRefKind of a list assignment expression is determined
4230 // by the PassByRefKind of the RHS. This loop will repeatedly recurse
4231 // on the RHS until it encounters an expression other than a list
4232 // assignment expression.
4233 while (exp
->is(Expression::KindOfListAssignment
)) {
4234 ListAssignmentPtr
la(static_pointer_cast
<ListAssignment
>(exp
));
4235 exp
= la
->getArray();
4237 switch (exp
->getKindOf()) {
4238 case Expression::KindOfNewObjectExpression
:
4239 case Expression::KindOfIncludeExpression
:
4240 // New and include/require
4241 return PassByRefKind::AllowCell
;
4242 case Expression::KindOfAssignmentExpression
:
4243 // Assignment (=) and binding assignment (=&)
4244 return PassByRefKind::WarnOnCell
;
4245 case Expression::KindOfBinaryOpExpression
: {
4246 BinaryOpExpressionPtr
b(static_pointer_cast
<BinaryOpExpression
>(exp
));
4247 // Assignment op (+=, -=, *=, etc)
4248 if (b
->isAssignmentOp()) return PassByRefKind::WarnOnCell
;
4250 case Expression::KindOfUnaryOpExpression
: {
4251 UnaryOpExpressionPtr
u(static_pointer_cast
<UnaryOpExpression
>(exp
));
4252 int op
= u
->getOp();
4253 if (op
== T_CLONE
) {
4255 return PassByRefKind::AllowCell
;
4256 } else if (op
== '@' || op
== T_EVAL
||
4257 ((op
== T_INC
|| op
== T_DEC
) && u
->getFront())) {
4258 // Silence operator, eval, preincrement, and predecrement
4259 return PassByRefKind::WarnOnCell
;
4262 case Expression::KindOfExpressionList
: {
4263 ExpressionListPtr
el(static_pointer_cast
<ExpressionList
>(exp
));
4264 if (el
->getListKind() != ExpressionList::ListKindParam
) {
4265 return PassByRefKind::WarnOnCell
;
4272 return PassByRefKind::ErrorOnCell
;
4275 void EmitterVisitor::emitBuiltinCallArg(Emitter
& e
,
4280 if (checkIfStackEmpty("BPass*")) return;
4283 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::NopOut
, false, 0, 0);
4287 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::NopOut
, false, 0, 0);
4293 void EmitterVisitor::emitBuiltinDefaultArg(Emitter
& e
, Variant
& v
,
4294 DataType t
, int paramId
) {
4295 switch (v
.getType()) {
4297 case KindOfStaticString
: {
4298 StringData
*nValue
= StringData::GetStaticString(v
.getStringData());
4303 e
.Int(v
.getInt64());
4306 if (v
.getBoolean()) {
4315 case KindOfStaticString
:
4328 e
.Array(v
.getArrayData());
4333 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::NopOut
, false, 0, 0);
4337 void EmitterVisitor::emitFuncCallArg(Emitter
& e
,
4341 if (checkIfStackEmpty("FPass*")) return;
4342 PassByRefKind passByRefKind
= getPassByRefKind(exp
);
4343 if (Option::WholeProgram
&& !exp
->hasAnyContext(Expression::InvokeArgument
|
4344 Expression::RefParameter
)) {
4345 if (exp
->hasContext(Expression::RefValue
)) {
4346 if (passByRefKind
== PassByRefKind::AllowCell
||
4347 m_evalStack
.get(m_evalStack
.size() - 1) != StackSym::C
) {
4349 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::NopOut
, false, 0, 0);
4355 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::NopOut
, false, 0, 0);
4360 emitFPass(e
, paramId
, getPassByRefKind(exp
));
4363 void EmitterVisitor::emitFPass(Emitter
& e
, int paramId
,
4364 PassByRefKind passByRefKind
) {
4365 if (checkIfStackEmpty("FPass*")) return;
4366 LocationGuard
locGuard(e
, m_tempLoc
);
4369 emitClsIfSPropBase(e
);
4370 int iLast
= m_evalStack
.size()-1;
4371 int i
= scanStackForLocation(iLast
);
4374 char sym
= m_evalStack
.get(i
);
4375 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
4377 case StackSym::L
: e
.FPassL(paramId
, m_evalStack
.getLoc(i
)); break;
4379 switch (passByRefKind
) {
4380 case PassByRefKind::AllowCell
: e
.FPassC(paramId
); break;
4381 case PassByRefKind::WarnOnCell
: e
.FPassCW(paramId
); break;
4382 case PassByRefKind::ErrorOnCell
: e
.FPassCE(paramId
); break;
4383 default: assert(false);
4386 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4387 case StackSym::CN
: e
.FPassN(paramId
); break;
4388 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4389 case StackSym::CG
: e
.FPassG(paramId
); break;
4390 case StackSym::LS
: e
.CGetL2(m_evalStack
.getLoc(i
)); // fall through
4391 case StackSym::CS
: e
.FPassS(paramId
); break;
4392 case StackSym::V
: e
.FPassV(paramId
); break;
4393 case StackSym::R
: e
.FPassR(paramId
); break;
4395 unexpectedStackSym(sym
, "emitFPass");
4400 std::vector
<uchar
> vectorImm
;
4401 buildVectorImm(vectorImm
, i
, iLast
, true, e
);
4402 e
.FPassM(paramId
, vectorImm
);
4406 void EmitterVisitor::emitIsset(Emitter
& e
) {
4407 if (checkIfStackEmpty("Isset*")) return;
4409 emitClsIfSPropBase(e
);
4410 int iLast
= m_evalStack
.size()-1;
4411 int i
= scanStackForLocation(iLast
);
4414 char sym
= m_evalStack
.get(i
);
4415 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
4417 case StackSym::L
: e
.IssetL(m_evalStack
.getLoc(i
)); break;
4418 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4419 case StackSym::CN
: e
.IssetN(); break;
4420 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4421 case StackSym::CG
: e
.IssetG(); break;
4422 case StackSym::LS
: e
.CGetL2(m_evalStack
.getLoc(i
)); // fall through
4423 case StackSym::CS
: e
.IssetS(); break;
4424 //XXX: Zend does not allow isset() on the result
4425 // of a function call. We allow it here so that emitted
4426 // code is valid. Once the parser handles this correctly,
4427 // the R and C cases can go.
4428 case StackSym::R
: e
.UnboxR(); // fall through
4429 case StackSym::C
: e
.IsNullC(); e
.Not(); break;
4431 unexpectedStackSym(sym
, "emitIsset");
4436 std::vector
<uchar
> vectorImm
;
4437 buildVectorImm(vectorImm
, i
, iLast
, false, e
);
4438 e
.IssetM(vectorImm
);
4442 #define EMIT_TYPE_CHECK_INSTR(What) \
4443 void EmitterVisitor::emitIs ## What(Emitter& e) { \
4444 if (checkIfStackEmpty("Is"#What)) return; \
4446 emitConvertToCellOrLoc(e); \
4447 switch (char sym = m_evalStack.top()) { \
4449 e.Is ## What ## L( \
4450 m_evalStack.getLoc(m_evalStack.size() - 1)); \
4453 e.Is ## What ## C(); \
4456 unexpectedStackSym(sym, "emitIs" #What); \
4460 EMIT_TYPE_CHECK_INSTR(Null
);
4461 EMIT_TYPE_CHECK_INSTR(Bool
);
4462 EMIT_TYPE_CHECK_INSTR(Int
);
4463 EMIT_TYPE_CHECK_INSTR(Double
);
4464 EMIT_TYPE_CHECK_INSTR(String
);
4465 EMIT_TYPE_CHECK_INSTR(Array
);
4466 EMIT_TYPE_CHECK_INSTR(Object
);
4467 #undef EMIT_TYPE_CHECK_INSTR
4469 void EmitterVisitor::emitEmpty(Emitter
& e
) {
4470 if (checkIfStackEmpty("Empty*")) return;
4472 emitClsIfSPropBase(e
);
4473 int iLast
= m_evalStack
.size()-1;
4474 int i
= scanStackForLocation(iLast
);
4477 char sym
= m_evalStack
.get(i
);
4478 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
4480 case StackSym::L
: e
.EmptyL(m_evalStack
.getLoc(i
)); break;
4481 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4482 case StackSym::CN
: e
.EmptyN(); break;
4483 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4484 case StackSym::CG
: e
.EmptyG(); break;
4485 case StackSym::LS
: e
.CGetL2(m_evalStack
.getLoc(i
)); // fall through
4486 case StackSym::CS
: e
.EmptyS(); break;
4487 //XXX: Zend does not allow empty() on the result
4488 // of a function call. We allow it here so that emitted
4489 // code is valid. Once the parser handles this correctly,
4490 // the R and C cases can go.
4491 case StackSym::R
: e
.UnboxR(); // fall through
4492 case StackSym::C
: e
.Not(); break;
4494 unexpectedStackSym(sym
, "emitEmpty");
4499 std::vector
<uchar
> vectorImm
;
4500 buildVectorImm(vectorImm
, i
, iLast
, false, e
);
4501 e
.EmptyM(vectorImm
);
4505 void EmitterVisitor::emitUnset(Emitter
& e
,
4506 ExpressionPtr exp
/* = ExpressionPtr() */) {
4507 if (checkIfStackEmpty("Unset*")) return;
4509 emitClsIfSPropBase(e
);
4510 int iLast
= m_evalStack
.size()-1;
4511 int i
= scanStackForLocation(iLast
);
4514 char sym
= m_evalStack
.get(i
);
4515 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
4517 case StackSym::L
: e
.UnsetL(m_evalStack
.getLoc(i
)); break;
4518 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4519 case StackSym::CN
: e
.UnsetN(); break;
4520 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4521 case StackSym::CG
: e
.UnsetG(); break;
4522 case StackSym::LS
: // fall through
4526 StringData::GetStaticString("Attempt to unset static property " +
4533 unexpectedStackSym(sym
, "emitUnset");
4538 std::vector
<uchar
> vectorImm
;
4539 buildVectorImm(vectorImm
, i
, iLast
, false, e
);
4540 e
.UnsetM(vectorImm
);
4544 void EmitterVisitor::emitVisitAndUnset(Emitter
& e
, ExpressionPtr exp
) {
4549 void EmitterVisitor::emitSet(Emitter
& e
) {
4550 if (checkIfStackEmpty("Set*")) return;
4552 int iLast
= m_evalStack
.size()-2;
4553 int i
= scanStackForLocation(iLast
);
4556 char sym
= m_evalStack
.get(i
);
4557 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
4559 case StackSym::L
: e
.SetL(m_evalStack
.getLoc(i
)); break;
4560 case StackSym::LN
: emitCGetL2(e
); // fall through
4561 case StackSym::CN
: e
.SetN(); break;
4562 case StackSym::LG
: emitCGetL2(e
); // fall through
4563 case StackSym::CG
: e
.SetG(); break;
4564 case StackSym::LS
: emitCGetL3(e
); // fall through
4565 case StackSym::CS
: e
.SetS(); break;
4567 unexpectedStackSym(sym
, "emitSet");
4572 std::vector
<uchar
> vectorImm
;
4573 buildVectorImm(vectorImm
, i
, iLast
, true, e
);
4578 void EmitterVisitor::emitSetOp(Emitter
& e
, int op
) {
4579 if (checkIfStackEmpty("SetOp*")) return;
4581 unsigned char cop
= SetOp_invalid
;
4583 case T_PLUS_EQUAL
: cop
= SetOpPlusEqual
; break;
4584 case T_MINUS_EQUAL
: cop
= SetOpMinusEqual
; break;
4585 case T_MUL_EQUAL
: cop
= SetOpMulEqual
; break;
4586 case T_DIV_EQUAL
: cop
= SetOpDivEqual
; break;
4587 case T_CONCAT_EQUAL
: cop
= SetOpConcatEqual
; break;
4588 case T_MOD_EQUAL
: cop
= SetOpModEqual
; break;
4589 case T_AND_EQUAL
: cop
= SetOpAndEqual
; break;
4590 case T_OR_EQUAL
: cop
= SetOpOrEqual
; break;
4591 case T_XOR_EQUAL
: cop
= SetOpXorEqual
; break;
4592 case T_SL_EQUAL
: cop
= SetOpSlEqual
; break;
4593 case T_SR_EQUAL
: cop
= SetOpSrEqual
; break;
4594 default: assert(false);
4596 int iLast
= m_evalStack
.size()-2;
4597 int i
= scanStackForLocation(iLast
);
4600 char sym
= m_evalStack
.get(i
);
4601 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
4603 case StackSym::L
: e
.SetOpL(m_evalStack
.getLoc(i
), cop
); break;
4604 case StackSym::LN
: emitCGetL2(e
); // fall through
4605 case StackSym::CN
: e
.SetOpN(cop
); break;
4606 case StackSym::LG
: emitCGetL2(e
); // fall through
4607 case StackSym::CG
: e
.SetOpG(cop
); break;
4608 case StackSym::LS
: emitCGetL3(e
); // fall through
4609 case StackSym::CS
: e
.SetOpS(cop
); break;
4611 unexpectedStackSym(sym
, "emitSetOp");
4616 std::vector
<uchar
> vectorImm
;
4617 buildVectorImm(vectorImm
, i
, iLast
, true, e
);
4618 e
.SetOpM(cop
, vectorImm
);
4622 void EmitterVisitor::emitBind(Emitter
& e
) {
4623 if (checkIfStackEmpty("Bind*")) return;
4625 int iLast
= m_evalStack
.size()-2;
4626 int i
= scanStackForLocation(iLast
);
4629 char sym
= m_evalStack
.get(i
);
4630 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
4632 case StackSym::L
: e
.BindL(m_evalStack
.getLoc(i
)); break;
4633 case StackSym::LN
: emitCGetL2(e
); // fall through
4634 case StackSym::CN
: e
.BindN(); break;
4635 case StackSym::LG
: emitCGetL2(e
); // fall through
4636 case StackSym::CG
: e
.BindG(); break;
4637 case StackSym::LS
: emitCGetL3(e
); // fall through
4638 case StackSym::CS
: e
.BindS(); break;
4640 unexpectedStackSym(sym
, "emitBind");
4645 std::vector
<uchar
> vectorImm
;
4646 buildVectorImm(vectorImm
, i
, iLast
, true, e
);
4651 void EmitterVisitor::emitIncDec(Emitter
& e
, unsigned char cop
) {
4652 if (checkIfStackEmpty("IncDec*")) return;
4654 emitClsIfSPropBase(e
);
4655 int iLast
= m_evalStack
.size()-1;
4656 int i
= scanStackForLocation(iLast
);
4659 char sym
= m_evalStack
.get(i
);
4660 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
4662 case StackSym::L
: e
.IncDecL(m_evalStack
.getLoc(i
), cop
); break;
4663 case StackSym::LN
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4664 case StackSym::CN
: e
.IncDecN(cop
); break;
4665 case StackSym::LG
: e
.CGetL(m_evalStack
.getLoc(i
)); // fall through
4666 case StackSym::CG
: e
.IncDecG(cop
); break;
4667 case StackSym::LS
: e
.CGetL2(m_evalStack
.getLoc(i
)); // fall through
4668 case StackSym::CS
: e
.IncDecS(cop
); break;
4670 unexpectedStackSym(sym
, "emitIncDec");
4675 std::vector
<uchar
> vectorImm
;
4676 buildVectorImm(vectorImm
, i
, iLast
, true, e
);
4677 e
.IncDecM(cop
, vectorImm
);
4681 void EmitterVisitor::emitConvertToCell(Emitter
& e
) {
4685 void EmitterVisitor::emitFreePendingIters(Emitter
& e
) {
4686 for (unsigned i
= 0; i
< m_pendingIters
.size(); ++i
) {
4687 auto pendingIter
= m_pendingIters
[i
];
4688 if (pendingIter
.second
== KindOfMIter
) {
4689 e
.MIterFree(pendingIter
.first
);
4691 assert(pendingIter
.second
== KindOfIter
);
4692 e
.IterFree(pendingIter
.first
);
4697 void EmitterVisitor::emitConvertSecondToCell(Emitter
& e
) {
4698 if (m_evalStack
.size() <= 1) {
4700 "Emitter encounted an empty evaluation stack when inside "
4701 "the emitConvertSecondToCell() function (at offset %d)",
4705 char sym
= m_evalStack
.get(m_evalStack
.size() - 2);
4706 char symFlavor
= StackSym::GetSymFlavor(sym
);
4707 if (symFlavor
== StackSym::C
) {
4709 } else if (symFlavor
== StackSym::L
) {
4712 // emitConvertSecondToCell() should never be used for symbolic flavors
4713 // other than C or L
4715 "Emitter encountered an unsupported StackSym \"%s\" on "
4716 "the evaluation stack inside the emitConvertSecondToCell()"
4717 " function (at offset %d)",
4718 StackSym::ToString(sym
).c_str(),
4723 void EmitterVisitor::emitConvertToCellIfVar(Emitter
& e
) {
4724 if (!m_evalStack
.empty()) {
4725 char sym
= m_evalStack
.top();
4726 if (sym
== StackSym::V
) {
4727 emitConvertToCell(e
);
4732 void EmitterVisitor::emitConvertToCellOrLoc(Emitter
& e
) {
4733 if (m_evalStack
.empty()) {
4735 "Emitter encounted an empty evaluation stack when inside "
4736 "the emitConvertToCellOrLoc() function (at offset %d)",
4740 char sym
= m_evalStack
.top();
4741 if (sym
== StackSym::L
) {
4742 // If the top of stack is a loc that is not marked, do nothing
4744 // Otherwise, call emitCGet to convert the top of stack to cell
4749 void EmitterVisitor::emitConvertToVar(Emitter
& e
) {
4754 * Class bases are stored on the symbolic stack in a "virtual" way so
4755 * we can resolve them later (here) in order to properly handle php
4761 * $cls::$x[0][f()] = g();
4763 * We need to evaluate f(), then resolve $cls to an A (possibly
4764 * invoking an autoload handler), then evaluate g(), then do the set.
4766 * Complex cases involve unnamed local temporaries. For example, in:
4768 * ${func()}::${f()} = g();
4770 * We'll emit code which calls func() and stashes the result in a
4771 * unnamed local. Then we call f(), then we turn the unnamed local
4772 * into an 'A' so that autoload handlers will run after f(). Then g()
4773 * is evaluated and then the set happens.
4775 void EmitterVisitor::emitResolveClsBase(Emitter
& e
, int pos
) {
4776 switch (m_evalStack
.getClsBaseType(pos
)) {
4777 case SymbolicStack::CLS_STRING_NAME
:
4778 e
.String(m_evalStack
.getName(pos
));
4781 case SymbolicStack::CLS_LATE_BOUND
:
4784 case SymbolicStack::CLS_SELF
:
4787 case SymbolicStack::CLS_PARENT
:
4790 case SymbolicStack::CLS_NAMED_LOCAL
: {
4791 int loc
= m_evalStack
.getLoc(pos
);
4792 emitVirtualLocal(loc
);
4796 case SymbolicStack::CLS_UNNAMED_LOCAL
: {
4797 int loc
= m_evalStack
.getLoc(pos
);
4798 emitVirtualLocal(loc
);
4800 emitVirtualLocal(loc
);
4802 newFaultRegion(m_evalStack
.getUnnamedLocStart(pos
),
4804 new UnsetUnnamedLocalThunklet(loc
));
4805 m_curFunc
->freeUnnamedLocal(loc
);
4808 case SymbolicStack::CLS_INVALID
:
4813 m_evalStack
.consumeBelowTop(m_evalStack
.size() - pos
- 1);
4816 void EmitterVisitor::emitClsIfSPropBase(Emitter
& e
) {
4817 // If the eval stack is empty, then there is no work to do
4818 if (m_evalStack
.empty()) return;
4820 // Scan past any values marked with the Elem, NewElem, or Prop markers
4821 int pos
= m_evalStack
.size() - 1;
4823 char marker
= StackSym::GetMarker(m_evalStack
.get(pos
));
4824 if (marker
!= StackSym::E
&& marker
!= StackSym::W
&&
4825 marker
!= StackSym::P
) {
4830 InvariantViolation("Emitter expected a location on the stack but none "
4831 "was found (at offset %d)",
4836 // After scanning, if we did not find a value marked with the SProp
4837 // marker then there is no work to do
4838 if (StackSym::GetMarker(m_evalStack
.get(pos
)) != StackSym::S
) {
4845 "Emitter emitted an instruction that tries to consume "
4846 "a value from the stack when the stack is empty "
4847 "(expected symbolic flavor \"C\" or \"L\" at offset %d)",
4851 emitResolveClsBase(e
, pos
);
4852 m_evalStack
.set(m_evalStack
.size() - 1,
4853 m_evalStack
.get(m_evalStack
.size() - 1) | StackSym::M
);
4856 void EmitterVisitor::emitContinuationSwitch(Emitter
& e
, int ncase
) {
4857 // There's an implicit fall-through "label 0" case in the switch
4858 // statement generated by the parser, so ncase is equal to the
4859 // number of yields in the body of the php function, which is one
4860 // less than the number of __yield__ labels.
4862 // fall-through to the label 0
4868 // make sure the labels are available
4869 m_yieldLabels
.resize(ncase
+ 1);
4872 // Don't bother with the jump table when there are only two targets
4874 e
.JmpNZ(m_yieldLabels
[1]);
4878 std::vector
<Label
*> targets(ncase
+ 1);
4879 for (int i
= 0; i
<= ncase
; ++i
) {
4880 targets
[i
] = &m_yieldLabels
[i
];
4883 e
.Switch(targets
, 0, 0);
4884 m_yieldLabels
[0].set(e
);
4887 DataType
EmitterVisitor::analyzeSwitch(SwitchStatementPtr sw
,
4888 SwitchState
& state
) {
4889 auto& caseMap
= state
.cases
;
4890 DataType t
= KindOfUninit
;
4891 StatementListPtr
cases(sw
->getCases());
4892 const int ncase
= cases
->getCount();
4894 // Bail if the cases aren't homogeneous
4895 for (int i
= 0; i
< ncase
; ++i
) {
4896 CaseStatementPtr
c(static_pointer_cast
<CaseStatement
>((*cases
)[i
]));
4897 ExpressionPtr condition
= c
->getCondition();
4901 if (condition
->getScalarValue(cval
)) {
4902 caseType
= cval
.getType();
4903 if (caseType
== KindOfStaticString
) caseType
= KindOfString
;
4904 if ((caseType
!= KindOfInt64
&& caseType
!= KindOfString
) ||
4905 !IMPLIES(t
!= KindOfUninit
, caseType
== t
)) {
4906 return KindOfInvalid
;
4910 return KindOfInvalid
;
4914 if (t
== KindOfInt64
) {
4915 n
= cval
.asInt64Val();
4918 always_assert(t
== KindOfString
);
4919 n
= m_ue
.mergeLitstr(cval
.asStrRef().get());
4920 isNonZero
= false; // not used for string switches
4922 if (!mapContains(caseMap
, n
)) {
4923 // If 'case n:' appears multiple times, only the first will
4926 if (t
== KindOfString
) {
4927 // We have to preserve the original order of the cases for string
4928 // switches because of insane things like 0 being equal to any string
4929 // that is not a nonzero numeric string.
4930 state
.caseOrder
.push_back(StrCase(safe_cast
<Id
>(n
), i
));
4933 if (state
.nonZeroI
== -1 && isNonZero
) {
4934 // true is equal to any non-zero integer, so to preserve php's
4935 // switch semantics we have to remember the first non-zero
4936 // case to appear in the source text
4940 // Last 'default:' wins
4945 if (t
== KindOfInt64
) {
4946 int64_t base
= caseMap
.begin()->first
;
4947 int64_t nTargets
= caseMap
.rbegin()->first
- base
+ 1;
4948 // Fail if the cases are too sparse
4949 if ((float)caseMap
.size() / nTargets
< 0.5) {
4950 return KindOfInvalid
;
4952 } else if (t
== KindOfString
) {
4953 if (caseMap
.size() < kMinStringSwitchCases
) {
4954 return KindOfInvalid
;
4961 void EmitterVisitor::emitIntegerSwitch(Emitter
& e
, SwitchStatementPtr sw
,
4962 std::vector
<Label
>& caseLabels
,
4963 Label
& done
, const SwitchState
& state
) {
4964 auto& caseMap
= state
.cases
;
4965 int64_t base
= caseMap
.begin()->first
;
4966 int64_t nTargets
= caseMap
.rbegin()->first
- base
+ 1;
4968 // It's on. Map case values to Labels, filling in the blanks as
4970 Label
* defLabel
= state
.defI
== -1 ? &done
: &caseLabels
[state
.defI
];
4971 std::vector
<Label
*> labels(nTargets
+ 2);
4972 for (int i
= 0; i
< nTargets
; ++i
) {
4974 if (mapGet(caseMap
, base
+ i
, &caseIdx
)) {
4975 labels
[i
] = &caseLabels
[caseIdx
];
4977 labels
[i
] = defLabel
;
4981 // Fill in offsets for the first non-zero case and default
4982 labels
[labels
.size() - 2] =
4983 state
.nonZeroI
== -1 ? defLabel
: &caseLabels
[state
.nonZeroI
];
4984 labels
[labels
.size() - 1] = defLabel
;
4986 visit(sw
->getExp());
4987 emitConvertToCell(e
);
4988 e
.Switch(labels
, base
, 1);
4991 void EmitterVisitor::emitStringSwitch(Emitter
& e
, SwitchStatementPtr sw
,
4992 std::vector
<Label
>& caseLabels
,
4993 Label
& done
, const SwitchState
& state
) {
4994 std::vector
<Emitter::StrOff
> labels
;
4995 for (auto& pair
: state
.caseOrder
) {
4996 labels
.push_back(Emitter::StrOff(pair
.first
, &caseLabels
[pair
.second
]));
4999 // Default case comes last
5000 Label
* defLabel
= state
.defI
== -1 ? &done
: &caseLabels
[state
.defI
];
5001 labels
.push_back(Emitter::StrOff(-1, defLabel
));
5003 visit(sw
->getExp());
5004 emitConvertToCell(e
);
5008 void EmitterVisitor::markElem(Emitter
& e
) {
5009 if (m_evalStack
.empty()) {
5010 InvariantViolation("Emitter encountered an empty evaluation stack inside"
5011 " the markElem function (at offset %d)",
5015 char sym
= m_evalStack
.top();
5016 if (sym
== StackSym::C
|| sym
== StackSym::L
|| sym
== StackSym::T
||
5017 sym
== StackSym::I
) {
5018 m_evalStack
.set(m_evalStack
.size()-1, (sym
| StackSym::E
));
5021 "Emitter encountered an unsupported StackSym \"%s\" on "
5022 "the evaluation stack inside the markElem function (at "
5024 StackSym::ToString(sym
).c_str(),
5029 void EmitterVisitor::markNewElem(Emitter
& e
) {
5030 m_evalStack
.push(StackSym::W
);
5033 void EmitterVisitor::markProp(Emitter
& e
) {
5034 if (m_evalStack
.empty()) {
5036 "Emitter encountered an empty evaluation stack inside "
5037 "the markProp function (at offset %d)",
5041 char sym
= m_evalStack
.top();
5042 if (sym
== StackSym::C
|| sym
== StackSym::L
|| sym
== StackSym::T
) {
5043 m_evalStack
.set(m_evalStack
.size()-1, (sym
| StackSym::P
));
5046 "Emitter encountered an unsupported StackSym \"%s\" on "
5047 "the evaluation stack inside the markProp function (at "
5049 StackSym::ToString(sym
).c_str(),
5054 void EmitterVisitor::markSProp(Emitter
& e
) {
5055 if (m_evalStack
.empty()) {
5057 "Emitter encountered an empty evaluation stack inside "
5058 "the markSProp function (at offset %d)",
5062 char sym
= m_evalStack
.top();
5063 if (sym
== StackSym::C
|| sym
== StackSym::L
) {
5064 m_evalStack
.set(m_evalStack
.size()-1, (sym
| StackSym::S
));
5067 "Emitter encountered an unsupported StackSym \"%s\" on "
5068 "the evaluation stack inside the markSProp function "
5070 StackSym::ToString(sym
).c_str(),
5075 #define MARK_NAME_BODY(index, requiredStackSize) \
5076 if (m_evalStack.size() < requiredStackSize) { \
5077 InvariantViolation( \
5078 "Emitter encountered an evaluation stack with %d " \
5079 "elements inside the %s function (at offset %d)", \
5080 m_evalStack.size(), __FUNCTION__, m_ue.bcPos()); \
5083 char sym = m_evalStack.get(index); \
5084 if (sym == StackSym::C || sym == StackSym::L) { \
5085 m_evalStack.set(index, (sym | StackSym::N)); \
5087 InvariantViolation( \
5088 "Emitter encountered an unsupported StackSym \"%s\" " \
5089 "on the evaluation stack inside the %s function (at " \
5091 StackSym::ToString(sym).c_str(), __FUNCTION__, \
5095 void EmitterVisitor::markName(Emitter
& e
) {
5096 int index
= m_evalStack
.size() - 1;
5097 MARK_NAME_BODY(index
, 1);
5100 void EmitterVisitor::markNameSecond(Emitter
& e
) {
5101 int index
= m_evalStack
.size() - 2;
5102 MARK_NAME_BODY(index
, 2);
5105 #undef MARK_NAME_BODY
5107 void EmitterVisitor::markGlobalName(Emitter
& e
) {
5108 if (m_evalStack
.empty()) {
5110 "Emitter encountered an empty evaluation stack inside "
5111 "the markGlobalName function (at offset %d)",
5115 char sym
= m_evalStack
.top();
5116 if (sym
== StackSym::C
|| sym
== StackSym::L
) {
5117 m_evalStack
.set(m_evalStack
.size()-1, (sym
| StackSym::G
));
5120 "Emitter encountered an unsupported StackSym \"%s\" on "
5121 "the evaluation stack inside the markGlobalName function "
5123 StackSym::ToString(sym
).c_str(),
5128 void EmitterVisitor::emitNameString(Emitter
& e
, ExpressionPtr n
,
5129 bool allowLiteral
) {
5131 if (n
->getScalarValue(v
) && v
.isString()) {
5132 StringData
* nLiteral
= StringData::GetStaticString(v
.toCStrRef().get());
5134 m_evalStack
.push(StackSym::T
);
5138 m_evalStack
.setString(nLiteral
);
5141 emitConvertToCellOrLoc(e
);
5145 void EmitterVisitor::postponeMeth(MethodStatementPtr m
, FuncEmitter
* fe
,
5147 ClosureUseVarVec
* useVars
/* = NULL */) {
5148 m_postponedMeths
.push_back(PostponedMeth(m
, fe
, top
, useVars
));
5151 void EmitterVisitor::postponeCtor(InterfaceStatementPtr is
, FuncEmitter
* fe
) {
5152 m_postponedCtors
.push_back(PostponedCtor(is
, fe
));
5155 void EmitterVisitor::postponePinit(InterfaceStatementPtr is
, FuncEmitter
* fe
,
5157 m_postponedPinits
.push_back(PostponedNonScalars(is
, fe
, v
));
5160 void EmitterVisitor::postponeSinit(InterfaceStatementPtr is
, FuncEmitter
* fe
,
5162 m_postponedSinits
.push_back(PostponedNonScalars(is
, fe
, v
));
5165 void EmitterVisitor::postponeCinit(InterfaceStatementPtr is
, FuncEmitter
* fe
,
5167 m_postponedCinits
.push_back(PostponedNonScalars(is
, fe
, v
));
5170 static Attr
buildAttrs(ModifierExpressionPtr mod
, bool isRef
= false) {
5171 int attrs
= AttrNone
;
5173 attrs
|= AttrReference
;
5176 attrs
|= mod
->isPublic() ? AttrPublic
:
5177 mod
->isPrivate() ? AttrPrivate
:
5178 mod
->isProtected() ? AttrProtected
: AttrNone
;
5179 if (mod
->isStatic()) {
5180 attrs
|= AttrStatic
;
5182 if (mod
->isAbstract()) {
5183 attrs
|= AttrAbstract
;
5185 if (mod
->isFinal()) {
5192 void EmitterVisitor::emitPostponedMeths() {
5193 vector
<FuncEmitter
*> top_fes
;
5194 while (!m_postponedMeths
.empty()) {
5195 assert(m_actualStackHighWater
== 0);
5196 assert(m_fdescHighWater
== 0);
5197 PostponedMeth
& p
= m_postponedMeths
.front();
5198 FunctionScopePtr funcScope
= p
.m_meth
->getFunctionScope();
5199 FuncEmitter
* fe
= p
.m_fe
;
5202 const StringData
* methName
=
5203 StringData::GetStaticString(p
.m_meth
->getOriginalName());
5204 fe
= new FuncEmitter(m_ue
, -1, -1, methName
);
5205 fe
->setIsGenerator(funcScope
->isGenerator());
5206 fe
->setIsGeneratorFromClosure(funcScope
->isGeneratorFromClosure());
5207 fe
->setHasGeneratorAsBody(!!p
.m_meth
->getGeneratorFunc());
5209 top_fes
.push_back(fe
);
5211 const FunctionScope::UserAttributeMap
& userAttrs
=
5212 funcScope
->userAttributes();
5213 for (FunctionScope::UserAttributeMap::const_iterator it
= userAttrs
.begin();
5214 it
!= userAttrs
.end(); ++it
) {
5215 const StringData
* uaName
= StringData::GetStaticString(it
->first
);
5216 ExpressionPtr uaValue
= it
->second
;
5218 assert(uaValue
->isScalar());
5220 initScalar(tv
, uaValue
);
5221 fe
->addUserAttribute(uaName
, tv
);
5223 Emitter
e(p
.m_meth
, m_ue
, *this);
5225 StringData
* methName
=
5226 StringData::GetStaticString(p
.m_meth
->getOriginalName());
5227 auto entry
= m_methLabels
.find(methName
);
5228 if (entry
!= m_methLabels
.end() && entry
->second
->isSet()) {
5229 // According to Zend, this is include-time; Zend doesn't appear
5230 // to execute the pseudomain that redeclares.
5231 throw IncludeTimeFatalException(p
.m_meth
,
5232 (string("Function already defined: ") +
5233 string(methName
->data())).c_str());
5236 m_methLabels
[methName
]->set(e
);
5239 typedef std::pair
<Id
, ConstructPtr
> DVInitializer
;
5240 std::vector
<DVInitializer
> dvInitializers
;
5241 ExpressionListPtr params
= p
.m_meth
->getParams();
5242 int numParam
= params
? params
->getCount() : 0;
5243 for (int i
= 0; i
< numParam
; i
++) {
5244 ParameterExpressionPtr
par(
5245 static_pointer_cast
<ParameterExpression
>((*params
)[i
]));
5246 StringData
* parName
= StringData::GetStaticString(par
->getName());
5247 if (par
->isOptional()) {
5248 dvInitializers
.push_back(DVInitializer(i
, par
->defaultValue()));
5250 // Will be fixed up later, when the DV initializers are emitted.
5251 FuncEmitter::ParamInfo pi
;
5252 if (par
->hasTypeHint()) {
5253 ConstantExpressionPtr ce
=
5254 dynamic_pointer_cast
<ConstantExpression
>(par
->defaultValue());
5255 bool nullable
= ce
&& ce
->isNull();
5258 StringData::GetStaticString(par
->getOriginalTypeHint()),
5261 pi
.setTypeConstraint(tc
);
5262 TRACE(1, "Added constraint to %s\n", fe
->name()->data());
5264 if (par
->hasUserType()) {
5265 pi
.setUserType(StringData::GetStaticString(par
->getUserTypeHint()));
5267 // Store info about the default value if there is one.
5268 if (par
->isOptional()) {
5269 const StringData
* phpCode
;
5270 ExpressionPtr vNode
= par
->defaultValue();
5271 if (vNode
->isScalar()) {
5273 initScalar(dv
, vNode
);
5274 pi
.setDefaultValue(dv
);
5276 std::string orig
= vNode
->getComment();
5278 // Simple case: it's a scalar value so we just serialize it
5279 VariableSerializer
vs(VariableSerializer::PHPOutput
);
5280 String result
= vs
.serialize(tvAsCVarRef(&dv
), true);
5281 phpCode
= StringData::GetStaticString(result
.get());
5283 // This was optimized from a Constant, or ClassConstant
5284 // use the original string
5285 phpCode
= StringData::GetStaticString(orig
);
5288 // Non-scalar, so we have to output PHP from the AST node
5289 std::ostringstream os
;
5290 CodeGenerator
cg(&os
, CodeGenerator::PickledPHP
);
5291 AnalysisResultPtr
ar(new AnalysisResult());
5292 vNode
->outputPHP(cg
, ar
);
5293 phpCode
= StringData::GetStaticString(os
.str());
5295 pi
.setPhpCode(phpCode
);
5297 ExpressionListPtr paramUserAttrs
=
5298 dynamic_pointer_cast
<ExpressionList
>(par
->userAttributeList());
5299 if (paramUserAttrs
) {
5300 for (int j
= 0; j
< paramUserAttrs
->getCount(); ++j
) {
5301 UserAttributePtr a
= dynamic_pointer_cast
<UserAttribute
>(
5302 (*paramUserAttrs
)[j
]);
5303 StringData
* uaName
= StringData::GetStaticString(a
->getName());
5304 ExpressionPtr uaValue
= a
->getExp();
5306 assert(uaValue
->isScalar());
5308 initScalar(tv
, uaValue
);
5309 pi
.addUserAttribute(uaName
, tv
);
5313 pi
.setRef(par
->isRef());
5314 fe
->appendParam(parName
, pi
);
5316 // add return type hint
5317 fe
->setReturnTypeConstraint(
5318 StringData::GetStaticString(p
.m_meth
->getReturnTypeConstraint()));
5322 if (fe
->isClosureBody() || fe
->isGeneratorFromClosure()) {
5323 // We are going to keep the closure as the first local
5324 fe
->allocVarId(StringData::GetStaticString("0Closure"));
5326 if (fe
->isClosureBody()) {
5327 ClosureUseVarVec
* useVars
= p
.m_closureUseVars
;
5328 for (auto& useVar
: *useVars
) {
5329 // These are all locals. I want them right after the params so I don't
5330 // have to keep track of which one goes where at runtime.
5331 fe
->allocVarId(useVar
.first
);
5336 // Assign ids to all of the local variables eagerly. This gives us the
5337 // nice property that all named local variables will be assigned ids
5338 // 0 through k-1, while any unnamed local variable will have an id >= k.
5339 // Note that the logic above already assigned ids to the parameters, so
5340 // we will still uphold the invariant that the n parameters will have
5341 // ids 0 through n-1 respectively.
5342 assignLocalVariableIds(funcScope
);
5344 // set all the params and metadata etc on fe
5345 StringData
* methDoc
=
5346 StringData::GetStaticString(p
.m_meth
->getDocComment());
5347 ModifierExpressionPtr
mod(p
.m_meth
->getModifiers());
5348 Attr attrs
= buildAttrs(mod
, p
.m_meth
->isRef());
5350 if (funcScope
->mayUseVV()) {
5351 attrs
= attrs
| AttrMayUseVV
;
5354 auto fullName
= p
.m_meth
->getOriginalFullName();
5355 auto it
= Option::FunctionSections
.find(fullName
);
5356 if ((it
!= Option::FunctionSections
.end() && it
->second
== "hot") ||
5357 (RuntimeOption::EvalRandomHotFuncs
&&
5358 (hash_string_i(fullName
.c_str()) & 8))) {
5359 attrs
= attrs
| AttrHot
;
5362 if (Option::WholeProgram
) {
5363 if (!funcScope
->isRedeclaring()) {
5364 attrs
= attrs
| AttrUnique
;
5366 (!funcScope
->isVolatile() ||
5367 funcScope
->isPersistent() ||
5368 funcScope
->isGenerator())) {
5369 attrs
= attrs
| AttrPersistent
;
5372 if (ClassScopePtr cls
= p
.m_meth
->getClassScope()) {
5373 if (p
.m_meth
->getName() == cls
->getName() &&
5374 !cls
->classNameCtor()) {
5376 In WholeProgram mode, we inline the traits into their
5377 classes. If a trait method name matches the class name
5378 it is NOT a constructor.
5379 We mark the method with AttrTrait so that we can avoid
5380 treating it as a constructor even though it looks like
5383 attrs
= attrs
| AttrTrait
;
5385 if (!funcScope
->hasOverride()) {
5386 attrs
= attrs
| AttrNoOverride
;
5389 } else if (!SystemLib::s_inited
) {
5390 // we're building systemlib. everything is unique
5391 attrs
= attrs
| AttrUnique
| AttrPersistent
;
5394 // For closures, the MethodStatement didn't have real attributes; enforce
5395 // that the __invoke method is public here
5396 if (fe
->isClosureBody()) {
5397 assert(!(attrs
& (AttrProtected
| AttrPrivate
)));
5398 attrs
= attrs
| AttrPublic
;
5402 const Location
* sLoc
= p
.m_meth
->getLocation().get();
5403 fe
->init(sLoc
->line0
, sLoc
->line1
, m_ue
.bcPos(), attrs
, p
.m_top
, methDoc
);
5404 // --Method emission begins--
5406 if (funcScope
->needsLocalThis() &&
5407 !funcScope
->isStatic() &&
5408 !funcScope
->isGenerator()) {
5410 static const StringData
* thisStr
= StringData::GetStaticString("this");
5411 Id thisId
= fe
->lookupVarId(thisStr
);
5412 e
.InitThisLoc(thisId
);
5414 for (uint i
= 0; i
< fe
->params().size(); i
++) {
5415 const TypeConstraint
& tc
= fe
->params()[i
].typeConstraint();
5416 if (!tc
.exists()) continue;
5417 TRACE(2, "permanent home for tc %s, param %d of func %s: %p\n",
5418 tc
.typeName()->data(), i
, fe
->name()->data(), &tc
);
5419 assert(tc
.typeName()->data() != (const char*)0xdeadba5eba11f00d);
5420 e
.VerifyParamType(i
);
5423 if (funcScope
->isAbstract()) {
5425 StringData::GetStaticString("Cannot call abstract method ");
5426 const StringData
* methName
=
5427 StringData::GetStaticString(p
.m_meth
->getOriginalFullName());
5428 msg
= NEW(StringData
)(msg
, methName
->slice());
5429 msg
= NEW(StringData
)(msg
, "()");
5434 Label ctFatal
, ctFatalWithPop
;
5436 CONTROL_BODY(ctFatal
, ctFatal
, ctFatalWithPop
, ctFatalWithPop
);
5438 visit(p
.m_meth
->getStmts());
5440 // If the break/continue label was used, we need to emit some extra code to
5441 // handle stray breaks
5443 if (ctFatal
.isUsed() || ctFatalWithPop
.isUsed()) {
5445 if (ctFatalWithPop
.isUsed()) {
5446 ctFatalWithPop
.set(e
);
5449 if (ctFatal
.isUsed()) {
5452 static const StringData
* msg
=
5453 StringData::GetStaticString("Cannot break/continue");
5457 if (exit
.isUsed()) {
5460 // If the current position in the bytecode is reachable, emit code to
5462 if (currentPositionIsReachable()) {
5464 if (p
.m_meth
->getFunctionScope()->isGenerator()) {
5465 m_metaInfo
.addKnownDataType(
5466 KindOfObject
, false, m_ue
.bcPos(), false, 1);
5467 assert(m_evalStack
.size() == 1);
5470 if ((p
.m_meth
->getStmts() && p
.m_meth
->getStmts()->isGuarded())) {
5471 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::GuardedThis
,
5476 } // -- Method emission ends --
5478 FuncFinisher
ff(this, e
, p
.m_fe
);
5480 // Default value initializers
5481 for (uint i
= 0; i
< dvInitializers
.size(); ++i
) {
5482 Label
entryPoint(e
);
5483 Id paramId
= dvInitializers
[i
].first
;
5484 ConstructPtr node
= dvInitializers
[i
].second
;
5485 emitVirtualLocal(paramId
, KindOfUninit
);
5490 p
.m_fe
->setParamFuncletOff(paramId
, entryPoint
.getAbsoluteOffset());
5492 if (!dvInitializers
.empty()) {
5493 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::NoSurprise
, false, 0, 0);
5496 delete p
.m_closureUseVars
;
5497 m_postponedMeths
.pop_front();
5500 for (size_t i
= 0; i
< top_fes
.size(); i
++) {
5501 m_ue
.appendTopEmitter(top_fes
[i
]);
5505 void EmitterVisitor::emitPostponedCtors() {
5506 while (!m_postponedCtors
.empty()) {
5507 PostponedCtor
& p
= m_postponedCtors
.front();
5509 Attr attrs
= AttrPublic
;
5510 StringData
* methDoc
= empty_string
.get();
5511 const Location
* sLoc
= p
.m_is
->getLocation().get();
5512 p
.m_fe
->init(sLoc
->line0
, sLoc
->line1
, m_ue
.bcPos(), attrs
, false, methDoc
);
5513 Emitter
e(p
.m_is
, m_ue
, *this);
5514 FuncFinisher
ff(this, e
, p
.m_fe
);
5518 m_postponedCtors
.pop_front();
5522 void EmitterVisitor::emitPostponedPSinit(PostponedNonScalars
& p
, bool pinit
) {
5523 Attr attrs
= (Attr
)(AttrPrivate
| AttrStatic
);
5524 StringData
* methDoc
= empty_string
.get();
5525 const Location
* sLoc
= p
.m_is
->getLocation().get();
5526 p
.m_fe
->init(sLoc
->line0
, sLoc
->line1
, m_ue
.bcPos(), attrs
, false, methDoc
);
5528 FuncEmitter::ParamInfo pi
;
5530 static const StringData
* s_props
= StringData::GetStaticString("props");
5531 p
.m_fe
->appendParam(s_props
, pi
);
5534 static const StringData
* s_sentinel
=
5535 StringData::GetStaticString("sentinel");
5536 p
.m_fe
->appendParam(s_sentinel
,
5537 FuncEmitter::ParamInfo());
5540 Emitter
e(p
.m_is
, m_ue
, *this);
5541 FuncFinisher
ff(this, e
, p
.m_fe
);
5543 // Generate HHBC of the structure:
5545 // private static function 86pinit(&$props, $sentinel) {
5546 // # Private instance properties.
5547 // props["\0C\0p0"] = <non-scalar initialization>;
5548 // props["\0C\0p1"] = <non-scalar initialization>;
5551 // if (props["q0"]) === $sentinel) {
5552 // props["q0"] = <non-scalar initialization>;
5554 // if (props["q1"] === $sentinel) {
5555 // props["q1"] = <non-scalar initialization>;
5560 // private static function 86sinit(&$props) {
5561 // props["p0"] = <non-scalar initialization>;
5562 // props["p1"] = <non-scalar initialization>;
5565 size_t nProps
= p
.m_vec
->size();
5567 for (size_t i
= 0; i
< nProps
; ++i
) {
5568 const StringData
* propName
=
5569 StringData::GetStaticString(((*p
.m_vec
)[i
]).first
);
5574 const PreClassEmitter::Prop
& preProp
=
5575 p
.m_fe
->pce()->lookupProp(propName
);
5576 if ((preProp
.attrs() & (AttrPrivate
|AttrStatic
)) == AttrPrivate
) {
5577 conditional
= false;
5578 propName
= preProp
.mangledName();
5583 conditional
= false;
5587 emitVirtualLocal(0);
5588 e
.String((StringData
*)propName
);
5591 emitVirtualLocal(1);
5597 emitVirtualLocal(0);
5598 e
.String((StringData
*)propName
);
5600 visit((*p
.m_vec
)[i
].second
);
5610 void EmitterVisitor::emitPostponedPinits() {
5611 while (!m_postponedPinits
.empty()) {
5612 PostponedNonScalars
& p
= m_postponedPinits
.front();
5613 emitPostponedPSinit(p
, true);
5614 p
.release(); // Manually trigger memory cleanup.
5615 m_postponedPinits
.pop_front();
5619 void EmitterVisitor::emitPostponedSinits() {
5620 while (!m_postponedSinits
.empty()) {
5621 PostponedNonScalars
& p
= m_postponedSinits
.front();
5622 emitPostponedPSinit(p
, false);
5623 p
.release(); // Manually trigger memory cleanup.
5624 m_postponedSinits
.pop_front();
5628 void EmitterVisitor::emitPostponedCinits() {
5629 while (!m_postponedCinits
.empty()) {
5630 PostponedNonScalars
& p
= m_postponedCinits
.front();
5632 Attr attrs
= (Attr
)(AttrPrivate
| AttrStatic
);
5633 StringData
* methDoc
= empty_string
.get();
5634 const Location
* sLoc
= p
.m_is
->getLocation().get();
5635 p
.m_fe
->init(sLoc
->line0
, sLoc
->line1
, m_ue
.bcPos(), attrs
, false, methDoc
);
5636 static const StringData
* s_constName
=
5637 StringData::GetStaticString("constName");
5638 p
.m_fe
->appendParam(s_constName
,
5639 FuncEmitter::ParamInfo());
5641 Emitter
e(p
.m_is
, m_ue
, *this);
5642 FuncFinisher
ff(this, e
, p
.m_fe
);
5644 // Generate HHBC of the structure:
5646 // private static function 86cinit(constName) {
5647 // if (constName == "FOO") {
5648 // return <expr for FOO>;
5649 // } else if (constName == "BAR") {
5650 // return <expr for BAR>;
5651 // } else { # (constName == "BAZ")
5652 // return <expr for BAZ>;
5655 size_t nConsts
= p
.m_vec
->size();
5656 assert(nConsts
> 0);
5658 for (size_t i
= 0; i
< nConsts
- 1; ++i
) {
5661 emitVirtualLocal(0);
5663 e
.String((StringData
*)StringData::GetStaticString(((*p
.m_vec
)[i
]).first
));
5667 visit((*p
.m_vec
)[i
].second
);
5672 visit((*p
.m_vec
)[nConsts
-1].second
);
5676 p
.release(); // Manually trigger memory cleanup.
5677 m_postponedCinits
.pop_front();
5681 void EmitterVisitor::emitVirtualLocal(int localId
,
5682 DataType dt
/* = KindOfUnknown */) {
5685 m_evalStack
.push(StackSym::L
);
5686 m_evalStack
.setInt(localId
);
5687 if (dt
!= KindOfUnknown
) {
5688 m_evalStack
.setKnownType(dt
);
5689 m_evalStack
.setNotRef();
5693 template<class Expr
>
5694 void EmitterVisitor::emitVirtualClassBase(Emitter
& e
, Expr
* node
) {
5697 m_evalStack
.push(StackSym::K
);
5699 if (node
->isStatic()) {
5700 m_evalStack
.setClsBaseType(SymbolicStack::CLS_LATE_BOUND
);
5701 } else if (node
->getClass()) {
5702 const ExpressionPtr
& expr
= node
->getClass();
5703 SimpleVariable
* sv
= static_cast<SimpleVariable
*>(expr
.get());
5704 if (expr
->is(Expression::KindOfSimpleVariable
) &&
5705 !sv
->isSuperGlobal() &&
5707 StringData
* name
= StringData::GetStaticString(sv
->getName());
5708 Id locId
= m_curFunc
->lookupVarId(name
);
5709 m_evalStack
.setClsBaseType(SymbolicStack::CLS_NAMED_LOCAL
);
5710 m_evalStack
.setInt(locId
);
5713 * More complex expressions get stashed into an unnamed local so
5714 * we can evaluate them at the proper time.
5716 * See emitResolveClsBase() for examples.
5718 int unnamedLoc
= m_curFunc
->allocUnnamedLocal();
5719 int clsBaseIdx
= m_evalStack
.size() - 1;
5720 m_evalStack
.setClsBaseType(SymbolicStack::CLS_UNNAMED_LOCAL
);
5721 emitVirtualLocal(unnamedLoc
);
5722 visit(node
->getClass());
5723 emitConvertToCell(e
);
5725 m_evalStack
.setUnnamedLocal(clsBaseIdx
, unnamedLoc
, m_ue
.bcPos());
5728 } else if (!node
->getOriginalClass() ||
5729 node
->getOriginalClass()->isTrait()) {
5730 // In a trait or psuedo-main, we can't resolve self:: or parent::
5731 // yet, so we emit special instructions that do those lookups.
5732 if (node
->isParent()) {
5733 m_evalStack
.setClsBaseType(SymbolicStack::CLS_PARENT
);
5734 } else if (node
->isSelf()) {
5735 m_evalStack
.setClsBaseType(SymbolicStack::CLS_SELF
);
5737 m_evalStack
.setClsBaseType(SymbolicStack::CLS_STRING_NAME
);
5738 m_evalStack
.setString(
5739 StringData::GetStaticString(node
->getOriginalClassName()));
5741 } else if (node
->isParent() &&
5742 node
->getOriginalClass()->getOriginalParent().empty()) {
5743 // parent:: in a class without a parent. We'll emit a Parent
5744 // opcode because it can handle this error case.
5745 m_evalStack
.setClsBaseType(SymbolicStack::CLS_PARENT
);
5747 m_evalStack
.setClsBaseType(SymbolicStack::CLS_STRING_NAME
);
5748 m_evalStack
.setString(
5749 StringData::GetStaticString(node
->getOriginalClassName()));
5753 bool EmitterVisitor::emitCallUserFunc(Emitter
& e
, SimpleFunctionCallPtr func
) {
5756 int minParams
, maxParams
;
5757 CallUserFuncFlags flags
;
5759 { "call_user_func", 1, INT_MAX
, CallUserFuncPlain
},
5760 { "call_user_func_array", 2, 2, CallUserFuncArray
},
5761 { "forward_static_call", 1, INT_MAX
, CallUserFuncForward
},
5762 { "forward_static_call_array", 2, 2, CallUserFuncForwardArray
},
5763 { "fb_call_user_func_safe", 1, INT_MAX
, CallUserFuncSafe
},
5764 { "fb_call_user_func_array_safe", 2, 2, CallUserFuncSafeArray
},
5765 { "fb_call_user_func_safe_return", 2, INT_MAX
, CallUserFuncSafeReturn
},
5768 ExpressionListPtr params
= func
->getParams();
5769 if (!params
) return false;
5770 int nParams
= params
->getCount();
5771 if (!nParams
) return false;
5772 CallUserFuncFlags flags
= CallUserFuncNone
;
5773 for (unsigned i
= 0; i
< sizeof(cufTab
) / sizeof(cufTab
[0]); i
++) {
5774 if (func
->isCallToFunction(cufTab
[i
].name
) &&
5775 nParams
>= cufTab
[i
].minParams
&&
5776 nParams
<= cufTab
[i
].maxParams
) {
5777 flags
= cufTab
[i
].flags
;
5781 if (flags
== CallUserFuncNone
) return false;
5784 ExpressionPtr callable
= (*params
)[0];
5786 emitConvertToCell(e
);
5787 Offset fpiStart
= m_ue
.bcPos();
5788 if (flags
& CallUserFuncForward
) {
5789 e
.FPushCufF(nParams
- param
);
5790 } else if (flags
& CallUserFuncSafe
) {
5791 if (flags
& CallUserFuncReturn
) {
5792 assert(nParams
>= 2);
5793 visit((*params
)[param
++]);
5794 emitConvertToCell(e
);
5798 fpiStart
= m_ue
.bcPos();
5799 e
.FPushCufSafe(nParams
- param
);
5801 e
.FPushCuf(nParams
- param
);
5805 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
5806 for (int i
= param
; i
< nParams
; i
++) {
5807 visit((*params
)[i
]);
5808 emitConvertToCell(e
);
5809 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::NopOut
, false, 0, 0);
5810 e
.FPassC(i
- param
);
5814 if (flags
& CallUserFuncArray
) {
5817 e
.FCall(nParams
- param
);
5819 if (flags
& CallUserFuncSafe
) {
5820 if (flags
& CallUserFuncReturn
) {
5829 bool EmitterVisitor::canEmitBuiltinCall(FunctionCallPtr fn
,
5830 const std::string
& name
,
5832 if (Option::JitEnableRenameFunction
) {
5835 if (Option::DynamicInvokeFunctions
.size()) {
5836 if (Option::DynamicInvokeFunctions
.find(name
) !=
5837 Option::DynamicInvokeFunctions
.end()) {
5841 FunctionScopePtr func
= fn
->getFuncScope();
5842 if (!func
|| func
->getMaxParamCount() > kMaxBuiltinArgs
) {
5845 if (!func
->isUserFunction() && !func
->needsActRec()
5846 && numParams
>= func
->getMinParamCount()
5847 && numParams
<= func
->getMaxParamCount()) {
5848 if (func
->isVariableArgument() || func
->isReferenceVariableArgument()
5849 || func
->isMixedVariableArgument()) {
5852 TypePtr t
= func
->getReturnType();
5853 if (!t
|| t
->getHhvmDataType() == KindOfDouble
) {
5856 for (int i
= 0; i
< func
->getMaxParamCount(); i
++) {
5857 t
= func
->getParamType(i
);
5858 if (!t
|| t
->getHhvmDataType() == KindOfDouble
) {
5861 // unserializable default values such as TimeStamp::Current()
5862 // are serialized as kUnserializableString ("\x01")
5863 if (i
>= numParams
&& func
->getParamDefault(i
) == kUnserializableString
) {
5867 // Special handling for func_get_args and friends inside a generator.
5868 if (m_curFunc
->isGenerator() &&
5869 (name
== "func_get_args" || name
== "func_num_args"
5870 || name
== "func_get_arg")) {
5873 // in sandbox mode, don't emit FCallBuiltin for redefinable functions
5874 if (func
->allowOverride() && !Option::WholeProgram
) {
5882 void EmitterVisitor::emitFuncCall(Emitter
& e
, FunctionCallPtr node
) {
5883 ExpressionPtr nameExp
= node
->getNameExp();
5884 const std::string
& nameStr
= node
->getOriginalName();
5885 ExpressionListPtr
params(node
->getParams());
5886 int numParams
= params
? params
->getCount() : 0;
5887 bool useFCallBuiltin
= false;
5888 StringData
* nLiteral
= nullptr;
5890 if (node
->getClass() || !node
->getClassName().empty()) {
5891 bool isSelfOrParent
= node
->isSelf() || node
->isParent();
5892 if (!node
->isStatic() && !isSelfOrParent
&&
5893 !node
->getOriginalClassName().empty() && !nameStr
.empty()) {
5895 StringData
* cLiteral
=
5896 StringData::GetStaticString(node
->getOriginalClassName());
5897 StringData
* nLiteral
= StringData::GetStaticString(nameStr
);
5898 fpiStart
= m_ue
.bcPos();
5899 e
.FPushClsMethodD(numParams
, nLiteral
, cLiteral
);
5900 if (node
->forcedPresent()) {
5901 m_metaInfo
.add(fpiStart
, Unit::MetaInfo::GuardedCls
, false, 0, 0);
5904 emitVirtualClassBase(e
, node
.get());
5905 if (!nameStr
.empty()) {
5907 StringData
* nLiteral
= StringData::GetStaticString(nameStr
);
5912 emitConvertToCell(e
);
5914 emitResolveClsBase(e
, m_evalStack
.size() - 2);
5915 fpiStart
= m_ue
.bcPos();
5916 if (isSelfOrParent
) {
5917 // self and parent are "forwarding" calls, so we need to
5918 // use FPushClsMethodF instead
5919 e
.FPushClsMethodF(numParams
);
5921 e
.FPushClsMethod(numParams
);
5924 } else if (!nameStr
.empty()) {
5926 nLiteral
= StringData::GetStaticString(nameStr
);
5927 useFCallBuiltin
= canEmitBuiltinCall(node
, nameStr
, numParams
);
5929 StringData
* nsName
= nullptr;
5930 if (!node
->hadBackslash()) {
5931 const std::string
& nonNSName
= node
->getNonNSOriginalName();
5932 if (nonNSName
!= nameStr
) {
5934 nLiteral
= StringData::GetStaticString(nonNSName
);
5935 useFCallBuiltin
= false;
5939 if (useFCallBuiltin
) {
5940 } else if (!m_curFunc
->isGenerator()) {
5941 fpiStart
= m_ue
.bcPos();
5943 if (nsName
== nullptr) {
5944 e
.FPushFuncD(numParams
, nLiteral
);
5946 e
.FPushFuncU(numParams
, nsName
, nLiteral
);
5949 // Special handling for func_get_args and friends inside a generator.
5950 const StringData
* specialMethodName
= nullptr;
5951 static const StringData
* contName
=
5952 StringData::GetStaticString(CONTINUATION_OBJECT_NAME
);
5953 Id contId
= m_curFunc
->lookupVarId(contName
);
5954 static const StringData
* s_get_args
=
5955 StringData::GetStaticString("get_args");
5956 static const StringData
* s_num_args
=
5957 StringData::GetStaticString("num_args");
5958 static const StringData
* s_get_arg
=
5959 StringData::GetStaticString("get_arg");
5960 if (nameStr
== "func_get_args") {
5961 specialMethodName
= s_get_args
;
5962 } else if (nameStr
== "func_num_args") {
5963 specialMethodName
= s_num_args
;
5964 } else if (nameStr
== "func_get_arg") {
5965 specialMethodName
= s_get_arg
;
5968 if (nsName
!= nullptr) {
5969 e
.FPushFuncU(numParams
, nsName
, nLiteral
);
5970 } else if (specialMethodName
!= nullptr) {
5971 emitVirtualLocal(contId
);
5973 fpiStart
= m_ue
.bcPos();
5974 e
.FPushObjMethodD(numParams
, specialMethodName
);
5976 fpiStart
= m_ue
.bcPos();
5977 e
.FPushFuncD(numParams
, nLiteral
);
5983 emitConvertToCell(e
);
5984 // FPushFunc consumes method name from stack
5985 fpiStart
= m_ue
.bcPos();
5986 e
.FPushFunc(numParams
);
5988 if (useFCallBuiltin
) {
5989 FunctionScopePtr func
= node
->getFuncScope();
5991 assert(numParams
<= func
->getMaxParamCount()
5992 && numParams
>= func
->getMinParamCount());
5994 for (; i
< numParams
; i
++) {
5995 // for builtin calls, since we don't push the ActRec, we
5996 // must determine the reffiness statically
5997 bool byRef
= func
->isRefParam(i
);
5998 emitBuiltinCallArg(e
, (*params
)[i
], i
, byRef
);
6000 for (; i
< func
->getMaxParamCount(); i
++) {
6001 Variant v
= unserialize_from_string(func
->getParamDefault(i
));
6002 TypePtr t
= func
->getParamType(i
);
6003 emitBuiltinDefaultArg(e
, v
, t
->getDataType(), i
);
6005 e
.FCallBuiltin(func
->getMaxParamCount(), numParams
, nLiteral
);
6008 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
6009 for (int i
= 0; i
< numParams
; i
++) {
6010 emitFuncCallArg(e
, (*params
)[i
], i
);
6015 if (Option::WholeProgram
) {
6016 fixReturnType(e
, node
, useFCallBuiltin
);
6020 void EmitterVisitor::emitClassTraitPrecRule(PreClassEmitter
* pce
,
6021 TraitPrecStatementPtr stmt
) {
6022 StringData
* traitName
= StringData::GetStaticString(stmt
->getTraitName());
6023 StringData
* methodName
= StringData::GetStaticString(stmt
->getMethodName());
6025 PreClass::TraitPrecRule
rule(traitName
, methodName
);
6027 std::set
<std::string
> otherTraitNames
;
6028 stmt
->getOtherTraitNames(otherTraitNames
);
6029 for (std::set
<std::string
>::iterator it
= otherTraitNames
.begin();
6030 it
!= otherTraitNames
.end(); it
++) {
6031 rule
.addOtherTraitName(StringData::GetStaticString(*it
));
6034 pce
->addTraitPrecRule(rule
);
6037 void EmitterVisitor::emitClassTraitAliasRule(PreClassEmitter
* pce
,
6038 TraitAliasStatementPtr stmt
) {
6039 StringData
* traitName
= StringData::GetStaticString(stmt
->getTraitName());
6040 StringData
* origMethName
= StringData::GetStaticString(stmt
->getMethodName());
6041 StringData
* newMethName
=
6042 StringData::GetStaticString(stmt
->getNewMethodName());
6043 // If there are no modifiers, buildAttrs() defaults to AttrPublic.
6044 // Here we don't want that. Instead, set AttrNone so that the modifiers of the
6045 // original method are preserved.
6046 Attr attr
= (stmt
->getModifiers()->getCount() == 0 ? AttrNone
:
6047 buildAttrs(stmt
->getModifiers()));
6049 PreClass::TraitAliasRule
rule(traitName
, origMethName
, newMethName
, attr
);
6051 pce
->addTraitAliasRule(rule
);
6054 void EmitterVisitor::emitClassUseTrait(PreClassEmitter
* pce
,
6055 UseTraitStatementPtr useStmt
) {
6056 StatementListPtr rules
= useStmt
->getStmts();
6057 for (int r
= 0; r
< rules
->getCount(); r
++) {
6058 StatementPtr rule
= (*rules
)[r
];
6059 TraitPrecStatementPtr precStmt
=
6060 dynamic_pointer_cast
<TraitPrecStatement
>(rule
);
6062 emitClassTraitPrecRule(pce
, precStmt
);
6064 TraitAliasStatementPtr aliasStmt
=
6065 dynamic_pointer_cast
<TraitAliasStatement
>(rule
);
6067 emitClassTraitAliasRule(pce
, aliasStmt
);
6072 void EmitterVisitor::emitTypedef(Emitter
& e
, TypedefStatementPtr td
) {
6073 auto kind
= !strcasecmp(td
->value
.c_str(), "array") ? KindOfArray
:
6074 !strcasecmp(td
->value
.c_str(), "int") ? KindOfInt64
:
6075 !strcasecmp(td
->value
.c_str(), "integer") ? KindOfInt64
:
6076 !strcasecmp(td
->value
.c_str(), "bool") ? KindOfBoolean
:
6077 !strcasecmp(td
->value
.c_str(), "boolean") ? KindOfBoolean
:
6078 !strcasecmp(td
->value
.c_str(), "string") ? KindOfString
:
6079 !strcasecmp(td
->value
.c_str(), "real") ? KindOfDouble
:
6080 !strcasecmp(td
->value
.c_str(), "float") ? KindOfDouble
:
6081 !strcasecmp(td
->value
.c_str(), "double") ? KindOfDouble
:
6084 // We have to merge the strings as litstrs to ensure namedentity
6086 auto const name
= StringData::GetStaticString(td
->name
);
6087 auto const value
= StringData::GetStaticString(td
->value
);
6088 m_ue
.mergeLitstr(name
);
6089 m_ue
.mergeLitstr(value
);
6092 record
.m_name
= name
;
6093 record
.m_value
= value
;
6094 record
.m_kind
= kind
;
6095 Id id
= m_ue
.addTypedef(record
);
6099 PreClass::Hoistable
EmitterVisitor::emitClass(Emitter
& e
, ClassScopePtr cNode
,
6101 InterfaceStatementPtr
is(
6102 static_pointer_cast
<InterfaceStatement
>(cNode
->getStmt()));
6103 StringData
* className
= StringData::GetStaticString(cNode
->getOriginalName());
6104 StringData
* parentName
=
6105 StringData::GetStaticString(cNode
->getOriginalParent());
6106 StringData
* classDoc
= StringData::GetStaticString(cNode
->getDocComment());
6107 Attr attr
= cNode
->isInterface() ? AttrInterface
:
6108 cNode
->isTrait() ? AttrTrait
:
6109 cNode
->isAbstract() ? AttrAbstract
:
6110 cNode
->isFinal() ? AttrFinal
:
6112 if (Option::WholeProgram
) {
6113 if (!cNode
->isRedeclaring() &&
6114 !cNode
->derivesFromRedeclaring()) {
6115 attr
= attr
| AttrUnique
;
6116 if (!cNode
->isVolatile()) {
6117 attr
= attr
| AttrPersistent
;
6120 if (!cNode
->getAttribute(ClassScope::NotFinal
)) {
6121 attr
= attr
| AttrNoOverride
;
6123 if (cNode
->getUsedTraitNames().size()) {
6124 attr
= attr
| AttrNoExpandTrait
;
6126 } else if (!SystemLib::s_inited
) {
6127 // we're building systemlib. everything is unique
6128 attr
= attr
| AttrUnique
| AttrPersistent
;
6131 const Location
* sLoc
= is
->getLocation().get();
6132 const std::vector
<std::string
>& bases(cNode
->getBases());
6133 int firstInterface
= cNode
->getOriginalParent().empty() ? 0 : 1;
6134 int nInterfaces
= bases
.size();
6135 PreClass::Hoistable hoistable
= PreClass::NotHoistable
;
6137 if (SystemLib::s_inited
) {
6138 if (nInterfaces
> firstInterface
|| cNode
->getUsedTraitNames().size()) {
6139 hoistable
= PreClass::Mergeable
;
6140 } else if (firstInterface
&&
6141 !m_hoistables
.count(cNode
->getOriginalParent())) {
6142 hoistable
= PreClass::MaybeHoistable
;
6145 if (hoistable
== PreClass::NotHoistable
) {
6146 hoistable
= attr
& AttrUnique
?
6147 PreClass::AlwaysHoistable
: PreClass::MaybeHoistable
;
6148 m_hoistables
.insert(cNode
->getOriginalName());
6151 PreClassEmitter
* pce
= m_ue
.newPreClassEmitter(className
, hoistable
);
6152 pce
->init(sLoc
->line0
, sLoc
->line1
, m_ue
.bcPos(), attr
, parentName
,
6154 LocationPtr
loc(new Location(*sLoc
));
6155 loc
->line1
= loc
->line0
;
6156 loc
->char1
= loc
->char0
;
6157 e
.setTempLocation(loc
);
6158 if (hoistable
!= PreClass::AlwaysHoistable
) {
6159 e
.DefCls(pce
->id());
6161 // To atatch the line number to for error reporting...
6164 e
.setTempLocation(LocationPtr());
6165 for (int i
= firstInterface
; i
< nInterfaces
; ++i
) {
6166 pce
->addInterface(StringData::GetStaticString(bases
[i
]));
6169 const std::vector
<std::string
>& usedTraits
= cNode
->getUsedTraitNames();
6170 for (size_t i
= 0; i
< usedTraits
.size(); i
++) {
6171 pce
->addUsedTrait(StringData::GetStaticString(usedTraits
[i
]));
6173 const ClassScope::UserAttributeMap
& userAttrs
= cNode
->userAttributes();
6174 for (ClassScope::UserAttributeMap::const_iterator it
= userAttrs
.begin();
6175 it
!= userAttrs
.end(); ++it
) {
6176 const StringData
* uaName
= StringData::GetStaticString(it
->first
);
6177 ExpressionPtr uaValue
= it
->second
;
6179 assert(uaValue
->isScalar());
6181 initScalar(tv
, uaValue
);
6182 pce
->addUserAttribute(uaName
, tv
);
6185 NonScalarVec
* nonScalarPinitVec
= nullptr;
6186 NonScalarVec
* nonScalarSinitVec
= nullptr;
6187 NonScalarVec
* nonScalarConstVec
= nullptr;
6188 if (StatementListPtr stmts
= is
->getStmts()) {
6189 int i
, n
= stmts
->getCount();
6190 for (i
= 0; i
< n
; i
++) {
6191 if (MethodStatementPtr meth
=
6192 dynamic_pointer_cast
<MethodStatement
>((*stmts
)[i
])) {
6193 StringData
* methName
=
6194 StringData::GetStaticString(meth
->getOriginalName());
6195 FuncEmitter
* fe
= m_ue
.newMethodEmitter(methName
, pce
);
6196 bool isGenerator
= meth
->getFunctionScope()->isGenerator();
6197 fe
->setIsGenerator(isGenerator
);
6198 fe
->setIsGeneratorFromClosure(
6199 meth
->getFunctionScope()->isGeneratorFromClosure());
6200 bool added UNUSED
= pce
->addMethod(fe
);
6202 postponeMeth(meth
, fe
, false);
6203 } else if (ClassVariablePtr cv
=
6204 dynamic_pointer_cast
<ClassVariable
>((*stmts
)[i
])) {
6205 ModifierExpressionPtr
mod(cv
->getModifiers());
6206 ExpressionListPtr
el(cv
->getVarList());
6207 Attr attrs
= buildAttrs(mod
);
6208 StringData
* typeConstraint
= StringData::GetStaticString(
6209 cv
->getTypeConstraint());
6210 int nVars
= el
->getCount();
6211 for (int ii
= 0; ii
< nVars
; ii
++) {
6212 ExpressionPtr
exp((*el
)[ii
]);
6213 ExpressionPtr vNode
;
6214 SimpleVariablePtr var
;
6215 if (exp
->is(Expression::KindOfAssignmentExpression
)) {
6216 AssignmentExpressionPtr
ae(
6217 static_pointer_cast
<AssignmentExpression
>(exp
));
6218 var
= static_pointer_cast
<SimpleVariable
>(ae
->getVariable());
6219 vNode
= ae
->getValue();
6221 var
= static_pointer_cast
<SimpleVariable
>(exp
);
6224 // A non-invalid HPHPC type for a property implies the
6225 // property's type is !KindOfUninit, and always
6226 // hphpcType|KindOfNull.
6227 const auto hphpcType
= var
->getSymbol()
6228 ? var
->getSymbol()->getFinalType()->getDataType()
6231 StringData
* propName
= StringData::GetStaticString(var
->getName());
6232 StringData
* propDoc
= empty_string
.get();
6235 if (vNode
->isScalar()) {
6236 initScalar(tvVal
, vNode
);
6238 tvWriteUninit(&tvVal
);
6239 if (!(attrs
& AttrStatic
)) {
6240 if (requiresDeepInit(vNode
)) {
6241 attrs
= (Attr
)(attrs
| AttrDeepInit
);
6243 if (nonScalarPinitVec
== nullptr) {
6244 nonScalarPinitVec
= new NonScalarVec();
6246 nonScalarPinitVec
->push_back(NonScalarPair(propName
, vNode
));
6248 if (nonScalarSinitVec
== nullptr) {
6249 nonScalarSinitVec
= new NonScalarVec();
6251 nonScalarSinitVec
->push_back(NonScalarPair(propName
, vNode
));
6255 tvWriteNull(&tvVal
);
6258 pce
->addProperty(propName
, attrs
, typeConstraint
, propDoc
, &tvVal
,
6262 } else if (ClassConstantPtr cc
=
6263 dynamic_pointer_cast
<ClassConstant
>((*stmts
)[i
])) {
6264 ExpressionListPtr
el(cc
->getConList());
6265 StringData
* typeConstraint
= StringData::GetStaticString(
6266 cc
->getTypeConstraint());
6267 int nCons
= el
->getCount();
6268 for (int ii
= 0; ii
< nCons
; ii
++) {
6269 AssignmentExpressionPtr
ae(
6270 static_pointer_cast
<AssignmentExpression
>((*el
)[ii
]));
6271 ConstantExpressionPtr
con(
6272 static_pointer_cast
<ConstantExpression
>(ae
->getVariable()));
6273 ExpressionPtr
vNode(ae
->getValue());
6274 StringData
* constName
= StringData::GetStaticString(con
->getName());
6277 if (vNode
->isArray()) {
6278 throw IncludeTimeFatalException(
6279 cc
, "Arrays are not allowed in class constants");
6280 } else if (vNode
->isScalar()) {
6281 initScalar(tvVal
, vNode
);
6283 tvWriteUninit(&tvVal
);
6284 if (nonScalarConstVec
== nullptr) {
6285 nonScalarConstVec
= new NonScalarVec();
6287 nonScalarConstVec
->push_back(NonScalarPair(constName
, vNode
));
6289 // Store PHP source code for constant initializer.
6290 std::ostringstream os
;
6291 CodeGenerator
cg(&os
, CodeGenerator::PickledPHP
);
6292 AnalysisResultPtr
ar(new AnalysisResult());
6293 vNode
->outputPHP(cg
, ar
);
6294 bool added UNUSED
= pce
->addConstant(
6295 constName
, typeConstraint
, &tvVal
,
6296 StringData::GetStaticString(os
.str()));
6299 } else if (UseTraitStatementPtr useStmt
=
6300 dynamic_pointer_cast
<UseTraitStatement
>((*stmts
)[i
])) {
6301 emitClassUseTrait(pce
, useStmt
);
6306 if (!cNode
->getAttribute(ClassScope::HasConstructor
) &&
6307 !cNode
->getAttribute(ClassScope::ClassNameConstructor
)) {
6308 // cNode does not have a constructor; synthesize 86ctor() so that the class
6309 // will always have a method that can be called during construction.
6310 static const StringData
* methName
= StringData::GetStaticString("86ctor");
6311 FuncEmitter
* fe
= m_ue
.newMethodEmitter(methName
, pce
);
6312 bool added UNUSED
= pce
->addMethod(fe
);
6314 postponeCtor(is
, fe
);
6317 if (nonScalarPinitVec
!= nullptr) {
6318 // Non-scalar property initializers require 86pinit() for run-time
6319 // initialization support.
6320 static const StringData
* methName
= StringData::GetStaticString("86pinit");
6321 FuncEmitter
* fe
= m_ue
.newMethodEmitter(methName
, pce
);
6323 postponePinit(is
, fe
, nonScalarPinitVec
);
6326 if (nonScalarSinitVec
!= nullptr) {
6327 // Non-scalar property initializers require 86sinit() for run-time
6328 // initialization support.
6329 static const StringData
* methName
= StringData::GetStaticString("86sinit");
6330 FuncEmitter
* fe
= m_ue
.newMethodEmitter(methName
, pce
);
6332 postponeSinit(is
, fe
, nonScalarSinitVec
);
6335 if (nonScalarConstVec
!= nullptr) {
6336 // Non-scalar constant initializers require 86cinit() for run-time
6337 // initialization support.
6338 static const StringData
* methName
= StringData::GetStaticString("86cinit");
6339 FuncEmitter
* fe
= m_ue
.newMethodEmitter(methName
, pce
);
6340 assert(!(attr
& AttrTrait
));
6341 bool added UNUSED
= pce
->addMethod(fe
);
6343 postponeCinit(is
, fe
, nonScalarConstVec
);
6350 EmitterVisitor::emitBreakHandler(Emitter
& e
,
6356 IterKind itKind
/* = KindOfIter */) {
6357 // Handle dynamic break
6358 if (brkHand
.isUsed()) {
6360 // Whatever happens, we have left this loop
6362 if (itKind
== KindOfMIter
) {
6365 assert(itKind
== KindOfIter
);
6374 e
.JmpZ(topBreakHandler());
6375 e
.PopC(); // Pop break num
6379 // Handle dynamic continue
6380 if (cntHand
.isUsed()) {
6388 // Freeing the iterator means another jump
6394 // Leaving this loop
6395 if (itKind
== KindOfMIter
) {
6398 assert(itKind
== KindOfIter
);
6401 e
.Jmp(topContHandler());
6403 e
.JmpZ(topContHandler());
6410 class ForeachIterGuard
{
6411 EmitterVisitor
& m_ev
;
6413 ForeachIterGuard(EmitterVisitor
& ev
, Id iterId
, bool itRef
) : m_ev(ev
) {
6414 m_ev
.pushIterScope(iterId
, itRef
);
6416 ~ForeachIterGuard() {
6417 m_ev
.popIterScope();
6421 void EmitterVisitor::emitForeach(Emitter
& e
, ForEachStatementPtr fe
) {
6422 ExpressionPtr
ae(fe
->getArrayExp());
6423 ExpressionPtr
val(fe
->getValueExp());
6424 ExpressionPtr
key(fe
->getNameExp());
6425 StatementPtr
body(fe
->getBody());
6428 bool strong
= fe
->isStrong();
6435 Id itId
= m_curFunc
->allocIterator();
6436 ForeachIterGuard
fig(*this, itId
, strong
);
6437 bool simpleCase
= (!key
|| key
->is(Expression::KindOfSimpleVariable
)) &&
6438 val
->is(Expression::KindOfSimpleVariable
);
6441 SimpleVariablePtr
svVal(static_pointer_cast
<SimpleVariable
>(val
));
6442 StringData
* name
= StringData::GetStaticString(svVal
->getName());
6443 valTempLocal
= m_curFunc
->lookupVarId(name
);
6445 SimpleVariablePtr
svKey(static_pointer_cast
<SimpleVariable
>(key
));
6446 name
= StringData::GetStaticString(svKey
->getName());
6447 keyTempLocal
= m_curFunc
->lookupVarId(name
);
6449 // Meta info on the key local will confuse the translator (and
6450 // wouldn't be useful anyway)
6451 m_evalStack
.cleanTopMeta();
6457 // Meta info on the value local will confuse the translator (and
6458 // wouldn't be useful anyway)
6459 m_evalStack
.cleanTopMeta();
6462 emitConvertToVar(e
);
6464 e
.MIterInitK(itId
, exit
, valTempLocal
, keyTempLocal
);
6466 e
.MIterInit(itId
, exit
, valTempLocal
);
6469 emitConvertToCell(e
);
6471 e
.IterInitK(itId
, exit
, valTempLocal
, keyTempLocal
);
6473 e
.IterInit(itId
, exit
, valTempLocal
);
6478 bIterStart
= m_ue
.bcPos();
6480 keyTempLocal
= key
? m_curFunc
->allocUnnamedLocal() : -1;
6481 valTempLocal
= m_curFunc
->allocUnnamedLocal();
6483 emitVirtualLocal(keyTempLocal
);
6485 emitVirtualLocal(valTempLocal
);
6489 emitConvertToVar(e
);
6491 emitConvertToCell(e
);
6496 e
.MIterInitK(itId
, exit
, valTempLocal
, keyTempLocal
);
6498 e
.MIterInit(itId
, exit
, valTempLocal
);
6502 e
.IterInitK(itId
, exit
, valTempLocal
, keyTempLocal
);
6504 e
.IterInit(itId
, exit
, valTempLocal
);
6508 // At this point, valTempLocal and keyTempLocal if applicable, contain the
6509 // key and value for the iterator.
6511 bIterStart
= m_ue
.bcPos();
6516 emitVirtualLocal(valTempLocal
);
6525 emitVirtualLocal(valTempLocal
);
6527 newFaultRegion(bIterStart
, m_ue
.bcPos(),
6528 new UnsetUnnamedLocalThunklet(valTempLocal
));
6530 assert(keyTempLocal
!= -1);
6531 emitVirtualLocal(keyTempLocal
);
6535 emitVirtualLocal(keyTempLocal
);
6537 newFaultRegion(bIterStart
, m_ue
.bcPos(),
6538 new UnsetUnnamedLocalThunklet(keyTempLocal
));
6543 FOREACH_BODY(itId
, strong
, exit
, next
, brkHand
, cntHand
);
6544 if (body
) visit(body
);
6546 bool needBreakHandler
= (brkHand
.isUsed() || cntHand
.isUsed());
6547 if (next
.isUsed() || needBreakHandler
) {
6551 emitVirtualLocal(keyTempLocal
);
6552 // Meta info on the key local will confuse the translator (and
6553 // wouldn't be useful anyway)
6554 m_evalStack
.cleanTopMeta();
6556 emitVirtualLocal(valTempLocal
);
6557 // Meta info on the value local will confuse the translator (and
6558 // wouldn't be useful anyway)
6559 m_evalStack
.cleanTopMeta();
6562 e
.MIterNextK(itId
, start
, valTempLocal
, keyTempLocal
);
6564 e
.MIterNext(itId
, start
, valTempLocal
);
6568 e
.IterNextK(itId
, start
, valTempLocal
, keyTempLocal
);
6570 e
.IterNext(itId
, start
, valTempLocal
);
6573 newFaultRegion(bIterStart
, m_ue
.bcPos(), new IterFreeThunklet(itId
, strong
),
6575 if (needBreakHandler
) {
6577 IterKind itKind
= strong
? KindOfMIter
: KindOfIter
;
6578 emitBreakHandler(e
, exit
, next
, brkHand
, cntHand
, itId
, itKind
);
6581 m_curFunc
->freeUnnamedLocal(valTempLocal
);
6583 m_curFunc
->freeUnnamedLocal(keyTempLocal
);
6587 m_curFunc
->freeIterator(itId
);
6591 * Emits bytecode that restores the previous error reporting level after
6592 * evaluating a silenced (@) expression, or in the fault funclet protecting such
6593 * an expression. Requires a local variable id containing the previous error
6594 * reporting level. The whole silenced expression looks like this:
6595 * oldvalue = error_reporting(0)
6596 * ...evaluate silenced expression...
6597 * oldvalue = error_reporting(oldvalue)
6599 * error_reporting(oldvalue)
6601 void EmitterVisitor::emitRestoreErrorReporting(Emitter
& e
, Id oldLevelLoc
) {
6602 static const StringData
* funcName
=
6603 StringData::GetStaticString("error_reporting");
6605 // Optimistically call with the old value. If this returns nonzero, call it
6606 // again with that return value.
6607 emitVirtualLocal(oldLevelLoc
);
6608 Offset fpiStart
= m_ue
.bcPos();
6609 e
.FPushFuncD(1, funcName
);
6611 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
6612 emitVirtualLocal(oldLevelLoc
);
6614 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::NopOut
, false, 0, 0);
6619 // stack is now: ...[Loc oldLevelLoc][return value]
6620 // save the return value in local, and leave it on the stack
6624 e
.JmpNZ(dontRollback
);
6625 fpiStart
= m_ue
.bcPos();
6626 e
.FPushFuncD(1, funcName
);
6628 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
6629 emitVirtualLocal(oldLevelLoc
);
6631 m_metaInfo
.add(m_ue
.bcPos(), Unit::MetaInfo::NopOut
, false, 0, 0);
6636 dontRollback
.set(e
);
6639 void EmitterVisitor::emitMakeUnitFatal(Emitter
& e
, const std::string
& msg
) {
6640 StringData
* sd
= StringData::GetStaticString(msg
);
6645 void EmitterVisitor::addFunclet(Thunklet
* body
, Label
* entry
) {
6646 m_funclets
.push_back(Funclet(body
, entry
));
6648 void EmitterVisitor::emitFunclets(Emitter
& e
) {
6649 while (!m_funclets
.empty()) {
6650 Funclet
& f
= m_funclets
.front();
6654 m_funclets
.pop_front();
6659 void EmitterVisitor::newFaultRegion(Offset start
, Offset end
, Thunklet
* t
,
6661 FaultRegion
* r
= new FaultRegion(start
, end
, iterId
);
6662 m_faultRegions
.push_back(r
);
6663 addFunclet(t
, &r
->m_func
);
6666 void EmitterVisitor::newFPIRegion(Offset start
, Offset end
, Offset fpOff
) {
6667 FPIRegion
* r
= new FPIRegion(start
, end
, fpOff
);
6668 m_fpiRegions
.push_back(r
);
6671 void EmitterVisitor::copyOverExnHandlers(FuncEmitter
* fe
) {
6672 for (std::deque
<ExnHandlerRegion
*>::const_iterator it
= m_exnHandlers
.begin();
6673 it
!= m_exnHandlers
.end(); ++it
) {
6674 EHEnt
& e
= fe
->addEHEnt();
6675 e
.m_ehtype
= EHEnt::EHType_Catch
;
6676 e
.m_base
= (*it
)->m_start
;
6677 e
.m_past
= (*it
)->m_end
;
6679 for (std::vector
<std::pair
<StringData
*, Label
*> >::const_iterator it2
6680 = (*it
)->m_catchLabels
.begin();
6681 it2
!= (*it
)->m_catchLabels
.end(); ++it2
) {
6682 Id id
= m_ue
.mergeLitstr(it2
->first
);
6683 Offset off
= it2
->second
->getAbsoluteOffset();
6684 e
.m_catches
.push_back(std::pair
<Id
, Offset
>(id
, off
));
6688 m_exnHandlers
.clear();
6689 for (std::deque
<FaultRegion
*>::iterator it
= m_faultRegions
.begin();
6690 it
!= m_faultRegions
.end(); ++it
) {
6691 EHEnt
& e
= fe
->addEHEnt();
6692 e
.m_ehtype
= EHEnt::EHType_Fault
;
6693 e
.m_base
= (*it
)->m_start
;
6694 e
.m_past
= (*it
)->m_end
;
6695 e
.m_iterId
= (*it
)->m_iterId
;
6696 e
.m_fault
= (*it
)->m_func
.getAbsoluteOffset();
6699 m_faultRegions
.clear();
6702 void EmitterVisitor::copyOverFPIRegions(FuncEmitter
* fe
) {
6703 for (std::deque
<FPIRegion
*>::iterator it
= m_fpiRegions
.begin();
6704 it
!= m_fpiRegions
.end(); ++it
) {
6705 FPIEnt
& e
= fe
->addFPIEnt();
6706 e
.m_fpushOff
= (*it
)->m_start
;
6707 e
.m_fcallOff
= (*it
)->m_end
;
6708 e
.m_fpOff
= (*it
)->m_fpOff
;
6711 m_fpiRegions
.clear();
6714 void EmitterVisitor::saveMaxStackCells(FuncEmitter
* fe
) {
6715 // Max stack cells is used for stack overflow checks. We need to
6716 // count all the locals, and all cells due to ActRecs. We don't
6717 // need to count this function's own ActRec because whoever called
6718 // it already included it in its "max stack cells".
6719 fe
->setMaxStackCells(m_actualStackHighWater
+
6722 m_actualStackHighWater
= 0;
6723 m_fdescHighWater
= 0;
6726 // Are you sure you mean to be calling this directly? Would FuncFinisher
6727 // be more appropriate?
6728 void EmitterVisitor::finishFunc(Emitter
& e
, FuncEmitter
* fe
) {
6730 saveMaxStackCells(fe
);
6731 copyOverExnHandlers(fe
);
6732 copyOverFPIRegions(fe
);
6733 m_gotoLabels
.clear();
6734 m_yieldLabels
.clear();
6735 Offset past
= e
.getUnitEmitter().bcPos();
6736 fe
->finish(past
, false);
6737 e
.getUnitEmitter().recordFunction(fe
);
6740 StringData
* EmitterVisitor::newClosureName() {
6741 std::ostringstream str
;
6742 str
<< "Closure" << '$';
6743 if (m_curFunc
->pce() != nullptr) {
6744 str
<< m_curFunc
->pce()->name()->data();
6747 if (m_curFunc
->isPseudoMain()) {
6748 str
<< "__pseudoMain";
6750 str
<< m_curFunc
->name()->data();
6757 << m_curFunc
->ue().md5().q
[1] << m_curFunc
->ue().md5().q
[0]
6759 << '$' << m_closureCounter
++;
6761 return StringData::GetStaticString(str
.str());
6764 void EmitterVisitor::initScalar(TypedValue
& tvVal
, ExpressionPtr val
) {
6765 assert(val
->isScalar());
6766 tvVal
.m_type
= KindOfUninit
;
6767 switch (val
->getKindOf()) {
6768 case Expression::KindOfConstantExpression
: {
6769 ConstantExpressionPtr
ce(static_pointer_cast
<ConstantExpression
>(val
));
6771 tvVal
.m_data
.num
= 0;
6772 tvVal
.m_type
= KindOfNull
;
6773 } else if (ce
->isBoolean()) {
6774 tvVal
.m_data
.num
= ce
->getBooleanValue() ? 1 : 0;
6775 tvVal
.m_type
= KindOfBoolean
;
6776 } else if (ce
->isScalar()) {
6777 ce
->getScalarValue(tvAsVariant(&tvVal
));
6783 case Expression::KindOfScalarExpression
: {
6784 ScalarExpressionPtr sval
= static_pointer_cast
<ScalarExpression
>(val
);
6785 const std::string
* s
;
6786 if (sval
->getString(s
)) {
6787 StringData
* sd
= StringData::GetStaticString(*s
);
6788 tvVal
.m_data
.pstr
= sd
;
6789 tvVal
.m_type
= KindOfString
;
6793 if (sval
->getInt(i
)) {
6794 tvVal
.m_data
.num
= i
;
6795 tvVal
.m_type
= KindOfInt64
;
6799 if (sval
->getDouble(d
)) {
6800 tvVal
.m_data
.dbl
= d
;
6801 tvVal
.m_type
= KindOfDouble
;
6807 case Expression::KindOfUnaryOpExpression
: {
6808 UnaryOpExpressionPtr
u(static_pointer_cast
<UnaryOpExpression
>(val
));
6809 if (u
->getOp() == T_ARRAY
) {
6810 auto a
= ArrayData::Make(0);
6812 m_staticArrays
.push_back(a
);
6814 visit(u
->getExpression());
6816 HphpArray
* va
= m_staticArrays
.back();
6817 m_staticArrays
.pop_back();
6819 auto sa
= ArrayData::GetScalarArray(va
);
6820 tvVal
.m_data
.parr
= sa
;
6821 tvVal
.m_type
= KindOfArray
;
6829 if (val
->getScalarValue(tvAsVariant(&tvVal
))) {
6830 if (tvAsVariant(&tvVal
).isArray()) {
6840 bool EmitterVisitor::requiresDeepInit(ExpressionPtr initExpr
) const {
6841 switch (initExpr
->getKindOf()) {
6842 case Expression::KindOfScalarExpression
:
6843 case Expression::KindOfConstantExpression
:
6844 case Expression::KindOfClassConstantExpression
:
6846 case Expression::KindOfUnaryOpExpression
: {
6847 UnaryOpExpressionPtr
u(
6848 static_pointer_cast
<UnaryOpExpression
>(initExpr
));
6849 if (u
->getOp() == T_ARRAY
) {
6850 ExpressionListPtr el
=
6851 static_pointer_cast
<ExpressionList
>(u
->getExpression());
6853 int n
= el
->getCount();
6854 for (int i
= 0; i
< n
; i
++) {
6855 ArrayPairExpressionPtr ap
=
6856 static_pointer_cast
<ArrayPairExpression
>((*el
)[i
]);
6857 ExpressionPtr key
= ap
->getName();
6858 if (requiresDeepInit(ap
->getValue()) ||
6859 (key
&& requiresDeepInit(key
))) {
6865 } else if (u
->getOp() == '+' || u
->getOp() == '-') {
6866 return requiresDeepInit(u
->getExpression());
6875 Thunklet::~Thunklet() {}
6877 using HPHP::Eval::PhpFile
;
6879 static ConstructPtr
doOptimize(ConstructPtr c
, AnalysisResultConstPtr ar
) {
6880 for (int i
= 0, n
= c
->getKidCount(); i
< n
; i
++) {
6881 if (ConstructPtr k
= c
->getNthKid(i
)) {
6882 if (ConstructPtr rep
= doOptimize(k
, ar
)) {
6883 c
->setNthKid(i
, rep
);
6887 if (ExpressionPtr e
= dynamic_pointer_cast
<Expression
>(c
)) {
6888 switch (e
->getKindOf()) {
6889 case Expression::KindOfBinaryOpExpression
:
6890 case Expression::KindOfUnaryOpExpression
:
6891 case Expression::KindOfIncludeExpression
:
6892 case Expression::KindOfSimpleFunctionCall
:
6893 return e
->preOptimize(ar
);
6898 return ConstructPtr();
6901 static UnitEmitter
* emitHHBCUnitEmitter(AnalysisResultPtr ar
, FileScopePtr fsp
,
6903 if (fsp
->getPseudoMain() && !Option::WholeProgram
) {
6904 ar
->setPhase(AnalysisResult::FirstPreOptimize
);
6905 doOptimize(fsp
->getPseudoMain()->getStmt(), ar
);
6908 if (RuntimeOption::EvalDumpAst
) {
6909 if (fsp
->getPseudoMain()) {
6910 fsp
->getPseudoMain()->getStmt()->dump(0, ar
);
6914 MethodStatementPtr
msp(dynamic_pointer_cast
<MethodStatement
>(
6915 fsp
->getPseudoMain()->getStmt()));
6916 UnitEmitter
* ue
= new UnitEmitter(md5
);
6917 const Location
* sLoc
= msp
->getLocation().get();
6918 ue
->initMain(sLoc
->line0
, sLoc
->line1
);
6919 EmitterVisitor
ev(*ue
);
6922 } catch (EmitterVisitor::IncludeTimeFatalException
& ex
) {
6923 // Replace the unit with an empty one, but preserve its file path.
6924 UnitEmitter
* nue
= new UnitEmitter(md5
);
6925 nue
->initMain(sLoc
->line0
, sLoc
->line1
);
6926 nue
->setFilepath(ue
->getFilepath());
6930 EmitterVisitor
fev(*ue
);
6931 Emitter
emitter(ex
.m_node
, *ue
, fev
);
6932 FuncFinisher
ff(&fev
, emitter
, ue
->getMain());
6933 fev
.emitMakeUnitFatal(emitter
, ex
.getMessage());
6940 const HhbcExtClassInfo
* info
;
6941 const ClassInfo
* ci
;
6944 static Unit
* emitHHBCNativeFuncUnit(const HhbcExtFuncInfo
* builtinFuncs
,
6945 ssize_t numBuiltinFuncs
) {
6946 MD5
md5("11111111111111111111111111111111");
6947 UnitEmitter
* ue
= new UnitEmitter(md5
);
6948 ue
->setFilepath(StringData::GetStaticString(""));
6950 FuncEmitter
* mfe
= ue
->getMain();
6954 mfe
->setMaxStackCells(1);
6955 mfe
->finish(ue
->bcPos(), false);
6956 ue
->recordFunction(mfe
);
6958 TypedValue mainReturn
;
6959 mainReturn
.m_data
.num
= 1;
6960 mainReturn
.m_type
= KindOfInt64
;
6961 ue
->setMainReturn(&mainReturn
);
6962 ue
->setMergeOnly(true);
6965 Special function used by FPushCuf* when its argument
6968 StringData
* name
= StringData::GetStaticString("86null");
6969 FuncEmitter
* fe
= ue
->newFuncEmitter(name
, /*top*/ true);
6970 fe
->init(0, 0, ue
->bcPos(), AttrUnique
| AttrPersistent
,
6971 true, empty_string
.get());
6974 fe
->setMaxStackCells(1);
6975 fe
->finish(ue
->bcPos(), false);
6976 ue
->recordFunction(fe
);
6978 for (ssize_t i
= 0; i
< numBuiltinFuncs
; ++i
) {
6979 const HhbcExtFuncInfo
* info
= &builtinFuncs
[i
];
6980 StringData
* name
= StringData::GetStaticString(info
->m_name
);
6981 BuiltinFunction bif
= (BuiltinFunction
)info
->m_builtinFunc
;
6982 BuiltinFunction nif
= (BuiltinFunction
)info
->m_nativeFunc
;
6983 const ClassInfo::MethodInfo
* mi
= ClassInfo::FindFunction(name
);
6985 "MethodInfo not found; may be a problem with the .idl.json files");
6986 FuncEmitter
* fe
= ue
->newFuncEmitter(name
, /*top*/ true);
6987 Offset base
= ue
->bcPos();
6988 fe
->setBuiltinFunc(mi
, bif
, nif
, base
);
6989 ue
->emitOp(OpNativeImpl
);
6990 fe
->setMaxStackCells(kNumActRecCells
+ 1);
6991 fe
->setAttrs(fe
->attrs() | AttrUnique
| AttrPersistent
);
6992 fe
->finish(ue
->bcPos(), false);
6993 ue
->recordFunction(fe
);
6996 Unit
* unit
= ue
->create();
7001 enum ContinuationMethod
{
7008 typedef hphp_hash_map
<const StringData
*, ContinuationMethod
,
7009 string_data_hash
, string_data_same
> ContMethMap
;
7011 static void emitContinuationMethod(UnitEmitter
& ue
, FuncEmitter
* fe
,
7012 ContinuationMethod m
,
7013 MetaInfoBuilder
& metaInfo
) {
7014 static const StringData
* valStr
= StringData::GetStaticString("value");
7015 static const StringData
* exnStr
= StringData::GetStaticString("exception");
7017 Attr attrs
= (Attr
)(AttrPublic
| AttrMayUseVV
);
7018 fe
->init(0, 0, ue
.bcPos(), attrs
, false, empty_string
.get());
7022 fe
->appendParam(valStr
, FuncEmitter::ParamInfo());
7024 // We always want these methods to be cloned with new funcids in
7025 // subclasses so we can burn Class*s and Func*s into the
7027 fe
->setAttrs(Attr(fe
->attrs() | AttrClone
));
7029 static Op mOps
[] = {
7035 const Offset ehStart
= ue
.bcPos();
7036 ue
.emitOp(OpContEnter
);
7037 ue
.emitOp(OpContStopped
);
7041 EHEnt
& eh
= fe
->addEHEnt();
7042 eh
.m_ehtype
= EHEnt::EHType_Catch
;
7043 eh
.m_base
= ehStart
;
7044 eh
.m_past
= ue
.bcPos();
7045 eh
.m_catches
.push_back(
7046 std::pair
<Id
, Offset
>(ue
.mergeLitstr(exnStr
), ue
.bcPos()));
7048 ue
.emitOp(OpContHandle
);
7052 ue
.emitOp(OpContValid
);
7056 case METH_CURRENT
: {
7057 ue
.emitOp(OpContCurrent
);
7067 static Unit
* emitHHBCNativeClassUnit(const HhbcExtClassInfo
* builtinClasses
,
7068 ssize_t numBuiltinClasses
) {
7069 MD5
md5("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
7070 UnitEmitter
* ue
= new UnitEmitter(md5
);
7071 ue
->setFilepath(StringData::GetStaticString(""));
7073 FuncEmitter
* mfe
= ue
->getMain();
7077 Offset past
= ue
->bcPos();
7078 mfe
->setMaxStackCells(1);
7079 mfe
->finish(past
, false);
7080 ue
->recordFunction(mfe
);
7082 TypedValue mainReturn
;
7083 mainReturn
.m_data
.num
= 1;
7084 mainReturn
.m_type
= KindOfInt64
;
7085 ue
->setMainReturn(&mainReturn
);
7086 ue
->setMergeOnly(true);
7088 MetaInfoBuilder metaInfo
;
7090 ContMethMap contMethods
;
7091 contMethods
[StringData::GetStaticString("next")] = METH_NEXT
;
7092 contMethods
[StringData::GetStaticString("send")] = METH_SEND
;
7093 contMethods
[StringData::GetStaticString("raise")] = METH_RAISE
;
7094 contMethods
[StringData::GetStaticString("valid")] = METH_VALID
;
7095 contMethods
[StringData::GetStaticString("current")] = METH_CURRENT
;
7097 // Build up extClassHash, a hashtable that maps class names to structures
7098 // containing C++ function pointers for the class's methods and constructors
7099 assert(Class::s_extClassHash
.size() == 0);
7100 for (long long i
= 0; i
< numBuiltinClasses
; ++i
) {
7101 const HhbcExtClassInfo
* info
= builtinClasses
+ i
;
7102 StringData
*s
= StringData::GetStaticString(info
->m_name
);
7103 Class::s_extClassHash
[s
] = info
;
7105 // If a given class has a base class, then we can't load that class
7106 // before we load the base class. Build up some structures so that
7107 // we can load the C++ builtin classes in the right order.
7108 std::vector
<Entry
> classEntries
;
7110 typedef hphp_hash_map
<StringData
*, std::vector
<Entry
>,
7111 string_data_hash
, string_data_isame
> PendingMap
;
7113 hphp_hash_map
<const StringData
*, const HhbcExtClassInfo
*,
7114 string_data_hash
, string_data_isame
>::iterator it
;
7115 for (it
= Class::s_extClassHash
.begin();
7116 it
!= Class::s_extClassHash
.end(); ++it
) {
7118 e
.name
= const_cast<StringData
*>(it
->first
);
7119 e
.info
= it
->second
;
7120 e
.ci
= ClassInfo::FindSystemClassInterfaceOrTrait(e
.name
);
7122 StringData
* parentName
7123 = StringData::GetStaticString(e
.ci
->getParentClass().get());
7124 if (parentName
->empty()) {
7125 // If this class doesn't have a base class, it's eligible to be
7127 classEntries
.push_back(e
);
7129 // If this class has a base class, we can't load it until its
7130 // base class has been loaded
7131 pending
[parentName
].push_back(e
);
7134 for (unsigned k
= 0; k
< classEntries
.size(); ++k
) {
7135 Entry
& e
= classEntries
[k
];
7136 // Any classes that derive from this class are now eligible to be
7138 PendingMap::iterator pendingIt
= pending
.find(e
.name
);
7139 if (pendingIt
!= pending
.end()) {
7140 for (unsigned i
= 0; i
< pendingIt
->second
.size(); ++i
) {
7141 classEntries
.push_back(pendingIt
->second
[i
]);
7143 pending
.erase(pendingIt
);
7146 assert(pending
.empty());
7149 for (unsigned int i
= 0; i
< classEntries
.size(); ++i
) {
7150 Entry
& e
= classEntries
[i
];
7151 StringData
* parentName
=
7152 StringData::GetStaticString(e
.ci
->getParentClass().get());
7153 PreClassEmitter
* pce
= ue
->newPreClassEmitter(e
.name
,
7154 PreClass::AlwaysHoistable
);
7155 pce
->init(0, 0, ue
->bcPos(), AttrUnique
|AttrPersistent
, parentName
,
7157 pce
->setBuiltinClassInfo(e
.ci
, e
.info
->m_InstanceCtor
, e
.info
->m_sizeof
);
7159 ClassInfo::InterfaceVec intfVec
= e
.ci
->getInterfacesVec();
7160 for (unsigned i
= 0; i
< intfVec
.size(); ++i
) {
7161 const StringData
* intf
= StringData::GetStaticString(intfVec
[i
].get());
7162 pce
->addInterface(intf
);
7165 for (ssize_t j
= 0; j
< e
.info
->m_methodCount
; ++j
) {
7166 const HhbcExtMethodInfo
* methodInfo
= &(e
.info
->m_methods
[j
]);
7167 static const StringData
* continuationCls
=
7168 StringData::GetStaticString("continuation");
7169 StringData
* methName
=
7170 StringData::GetStaticString(methodInfo
->m_name
);
7171 ContinuationMethod cmeth
;
7173 FuncEmitter
* fe
= ue
->newMethodEmitter(methName
, pce
);
7175 if (e
.name
->isame(continuationCls
) &&
7176 mapGet(contMethods
, methName
, &cmeth
)) {
7177 emitContinuationMethod(*ue
, fe
, cmeth
, metaInfo
);
7179 // Build the function
7180 BuiltinFunction bcf
=
7181 (BuiltinFunction
)methodInfo
->m_pGenericMethod
;
7182 const ClassInfo::MethodInfo
* mi
=
7183 e
.ci
->getMethodInfo(std::string(methodInfo
->m_name
));
7184 Offset base
= ue
->bcPos();
7185 fe
->setBuiltinFunc(mi
, bcf
, nullptr, base
);
7186 ue
->emitOp(OpNativeImpl
);
7188 Offset past
= ue
->bcPos();
7189 fe
->setMaxStackCells(kNumActRecCells
+ 1);
7190 fe
->finish(past
, false);
7191 ue
->recordFunction(fe
);
7194 ClassInfo::ConstantVec cnsVec
= e
.ci
->getConstantsVec();
7195 for (unsigned i
= 0; i
< cnsVec
.size(); ++i
) {
7196 const ClassInfo::ConstantInfo
* cnsInfo
= cnsVec
[i
];
7200 val
= cnsInfo
->getValue();
7201 } catch (Exception
& e
) {
7205 cnsInfo
->name
.get(),
7207 (TypedValue
*)(&val
),
7208 empty_string
.get());
7212 ClassInfo::PropertyVec propVec
= e
.ci
->getPropertiesVec();
7213 for (unsigned i
= 0; i
< propVec
.size(); ++i
) {
7214 const ClassInfo::PropertyInfo
* propInfo
= propVec
[i
];
7216 int attr
= AttrNone
;
7217 if (propInfo
->attribute
& ClassInfo::IsProtected
) attr
|= AttrProtected
;
7218 else if (propInfo
->attribute
& ClassInfo::IsPrivate
) attr
|= AttrPrivate
;
7219 else attr
|= AttrPublic
;
7220 if (propInfo
->attribute
& ClassInfo::IsStatic
) attr
|= AttrStatic
;
7223 tvWriteNull(&tvNull
);
7225 propInfo
->name
.get(),
7228 propInfo
->docComment
? StringData::GetStaticString(propInfo
->docComment
) : nullptr,
7236 Peephole
peephole(*ue
, metaInfo
);
7237 metaInfo
.setForUnit(*ue
);
7239 Unit
* unit
= ue
->create();
7244 static UnitEmitter
* emitHHBCVisitor(AnalysisResultPtr ar
, FileScopeRawPtr fsp
) {
7245 MD5 md5
= fsp
->getMd5();
7247 if (!Option::WholeProgram
) {
7248 // The passed-in ar is only useful in whole-program mode, so create a
7249 // distinct ar to be used only for emission of this unit, and perform
7250 // unit-level (non-global) optimization.
7251 ar
= AnalysisResultPtr(new AnalysisResult());
7252 fsp
->setOuterScope(ar
);
7255 ar
->setPhase(AnalysisResult::AnalyzeAll
);
7256 fsp
->analyzeProgram(ar
);
7259 UnitEmitter
* ue
= emitHHBCUnitEmitter(ar
, fsp
, md5
);
7260 assert(ue
!= nullptr);
7262 if (Option::GenerateTextHHBC
) {
7263 std::unique_ptr
<Unit
> unit(ue
->create());
7264 std::string fullPath
= AnalysisResult::prepareFile(
7265 ar
->getOutputPath().c_str(), Option::UserFilePrefix
+ fsp
->getName(),
7266 true, false) + ".hhbc.txt";
7268 std::ofstream
f(fullPath
.c_str());
7270 Logger::Error("Unable to open %s for write", fullPath
.c_str());
7272 CodeGenerator
cg(&f
, CodeGenerator::TextHHBC
);
7273 cg
.printf("Hash: %" PRIx64
"%016" PRIx64
"\n", md5
.q
[0], md5
.q
[1]);
7274 cg
.printRaw(unit
->toString().c_str());
7282 class UEQ
: public Synchronizable
{
7284 void push(UnitEmitter
* ue
) {
7285 assert(ue
!= nullptr);
7287 m_ues
.push_back(ue
);
7290 UnitEmitter
* tryPop(long sec
, long long nsec
) {
7292 if (m_ues
.empty()) {
7293 // Check for empty() after wait(), in case of spurious wakeup.
7294 if (!wait(sec
, nsec
) || m_ues
.empty()) {
7298 assert(m_ues
.size() > 0);
7299 UnitEmitter
* ue
= m_ues
.front();
7300 assert(ue
!= nullptr);
7305 std::deque
<UnitEmitter
*> m_ues
;
7309 class EmitterWorker
: public JobQueueWorker
<FileScopeRawPtr
, true, true> {
7311 EmitterWorker() : m_ret(true) {}
7312 virtual void doJob(JobType job
) {
7314 AnalysisResultPtr ar
= ((AnalysisResult
*)m_opaque
)->shared_from_this();
7315 UnitEmitter
* ue
= emitHHBCVisitor(ar
, job
);
7316 if (Option::GenerateBinaryHHBC
) {
7321 } catch (Exception
&e
) {
7322 Logger::Error("%s", e
.getMessage().c_str());
7325 Logger::Error("Fatal: An unexpected exception was thrown");
7333 static void addEmitterWorker(AnalysisResultPtr ar
, StatementPtr sp
,
7335 ((JobQueueDispatcher
<EmitterWorker::JobType
,
7336 EmitterWorker
>*)data
)->enqueue(sp
->getFileScope());
7339 static void batchCommit(std::vector
<UnitEmitter
*>& ues
) {
7340 assert(Option::GenerateBinaryHHBC
);
7341 Repo
& repo
= Repo::get();
7343 // Attempt batch commit. This can legitimately fail due to multiple input
7344 // files having identical contents.
7349 for (std::vector
<UnitEmitter
*>::const_iterator it
= ues
.begin();
7350 it
!= ues
.end(); ++it
) {
7351 UnitEmitter
* ue
= *it
;
7352 if (repo
.insertUnit(ue
, UnitOriginFile
, txn
)) {
7363 for (std::vector
<UnitEmitter
*>::const_iterator it
= ues
.begin();
7364 it
!= ues
.end(); ++it
) {
7365 UnitEmitter
* ue
= *it
;
7366 // Commit units individually if an error occurred during batch commit.
7368 repo
.commitUnit(ue
, UnitOriginFile
);
7375 static void emitSystemLib() {
7376 if (!Option::WholeProgram
) return;
7378 string slib
= get_systemlib();
7379 if (slib
.empty()) return;
7381 Option::WholeProgram
= false;
7382 SystemLib::s_inited
= false;
7385 SystemLib::s_inited
= true;
7386 Option::WholeProgram
= true;
7389 AnalysisResultPtr
ar(new AnalysisResult());
7390 Scanner
scanner(slib
.c_str(), slib
.size(),
7391 RuntimeOption::GetScannerType(), "/:systemlib.php");
7392 Parser
parser(scanner
, "/:systemlib.php", ar
, slib
.size());
7394 FileScopePtr fsp
= parser
.getFileScope();
7395 fsp
->setOuterScope(ar
);
7398 ar
->setPhase(AnalysisResult::AnalyzeAll
);
7399 fsp
->analyzeProgram(ar
);
7402 char* md5str
= string_md5(slib
.c_str(), slib
.size(), false, md5len
);
7406 UnitEmitter
* ue
= emitHHBCUnitEmitter(ar
, fsp
, md5
);
7407 Repo::get().commitUnit(ue
, UnitOriginFile
);
7411 * This is the entry point for offline bytecode generation.
7413 void emitAllHHBC(AnalysisResultPtr ar
) {
7414 unsigned int threadCount
= Option::ParserThreadCount
;
7415 unsigned int nFiles
= ar
->getAllFilesVector().size();
7416 if (threadCount
> nFiles
) {
7417 threadCount
= nFiles
;
7419 if (!threadCount
) threadCount
= 1;
7421 /* there is a race condition in the first call to
7422 GetStaticString. Make sure we dont hit it */
7423 StringData::GetStaticString("");
7424 /* same for TypeConstraint */
7427 JobQueueDispatcher
<EmitterWorker::JobType
, EmitterWorker
>
7428 dispatcher(threadCount
, true, 0, false, ar
.get());
7431 ar
->visitFiles(addEmitterWorker
, &dispatcher
);
7433 if (Option::GenerateBinaryHHBC
) {
7434 // kBatchSize needs to strike a balance between reducing transaction commit
7435 // overhead (bigger batches are better), and limiting the cost incurred by
7436 // failed commits due to identical units that require rollback and retry
7437 // (smaller batches have less to lose). Empirical results indicate that a
7438 // value in the 2-10 range is reasonable.
7439 static const unsigned kBatchSize
= 8;
7440 std::vector
<UnitEmitter
*> ues
;
7442 // Gather up units created by the worker threads and commit them in
7445 bool inShutdown
= false;
7447 // Poll, but with a 100ms timeout so that this thread doesn't spin wildly
7448 // if it gets ahead of the workers.
7449 UnitEmitter
* ue
= s_ueq
.tryPop(0, 100 * 1000 * 1000);
7450 if ((didPop
= (ue
!= nullptr))) {
7453 if (ues
.size() == kBatchSize
7454 || (!didPop
&& inShutdown
&& ues
.size() > 0)) {
7458 inShutdown
= dispatcher
.pollEmpty();
7459 } else if (!didPop
) {
7460 assert(ues
.size() == 0);
7467 dispatcher
.waitEmpty();
7472 * This is the entry point from the runtime; i.e. online bytecode generation.
7473 * The 'filename' parameter may be NULL if there is no file associated with
7476 * Before being actually used, hphp_compiler_parse must be called with
7477 * a NULL `code' parameter to do initialization.
7482 Unit
* hphp_compiler_parse(const char* code
, int codeLen
, const MD5
& md5
,
7483 const char* filename
) {
7484 if (UNLIKELY(!code
)) {
7485 // Do initialization when code is null; see above.
7486 Option::EnableHipHopSyntax
= RuntimeOption::EnableHipHopSyntax
;
7487 Option::JitEnableRenameFunction
=
7488 RuntimeOption::EvalJitEnableRenameFunction
;
7489 for (auto& i
: RuntimeOption::DynamicInvokeFunctions
) {
7490 Option::DynamicInvokeFunctions
.insert(i
);
7492 Option::RecordErrors
= false;
7493 Option::ParseTimeOpts
= false;
7494 Option::WholeProgram
= false;
7495 Type::InitTypeHintMap();
7496 BuiltinSymbols::LoadSuperGlobals();
7497 AnalysisResultPtr
ar(new AnalysisResult());
7498 BuiltinSymbols::Load(ar
, true);
7499 BuiltinSymbols::NoSuperGlobals
= false;
7505 UnitOrigin unitOrigin
= UnitOriginFile
;
7508 unitOrigin
= UnitOriginEval
;
7510 SCOPE_EXIT
{ SymbolTable::Purge(); };
7512 // Check if this file contains raw hip hop bytecode instead of php.
7513 // For now this is just dictated by file extension, and doesn't ever
7514 // commit to the repo.
7515 if (RuntimeOption::EvalAllowHhas
) {
7516 if (const char* dot
= strrchr(filename
, '.')) {
7517 const char hhbc_ext
[] = "hhas";
7518 if (!strcmp(dot
+ 1, hhbc_ext
)) {
7519 return assemble_file(filename
, md5
);
7524 AnalysisResultPtr
ar(new AnalysisResult());
7525 Scanner
scanner(code
, codeLen
, RuntimeOption::GetScannerType(), filename
);
7526 Parser
parser(scanner
, filename
, ar
, codeLen
);
7528 FileScopePtr fsp
= parser
.getFileScope();
7529 fsp
->setOuterScope(ar
);
7532 ar
->setPhase(AnalysisResult::AnalyzeAll
);
7533 fsp
->analyzeProgram(ar
);
7535 UnitEmitter
* ue
= emitHHBCUnitEmitter(ar
, fsp
, md5
);
7536 Repo::get().commitUnit(ue
, unitOrigin
);
7537 Unit
* unit
= ue
->create();
7540 } catch (const std::exception
&) {
7541 // extern "C" function should not be throwing exceptions...
7546 Unit
* hphp_build_native_func_unit(const HhbcExtFuncInfo
* builtinFuncs
,
7547 ssize_t numBuiltinFuncs
) {
7548 return emitHHBCNativeFuncUnit(builtinFuncs
, numBuiltinFuncs
);
7551 Unit
* hphp_build_native_class_unit(const HhbcExtClassInfo
* builtinClasses
,
7552 ssize_t numBuiltinClasses
) {
7553 return emitHHBCNativeClassUnit(builtinClasses
, numBuiltinClasses
);
7558 ///////////////////////////////////////////////////////////////////////////////