Fewer varEnvs in generators
[hiphop-php.git] / hphp / compiler / analysis / emitter.cpp
blobbd36c9620dad7ea1930274eb97294610b84bcbe8
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
98 #include <iostream>
99 #include <iomanip>
100 #include <vector>
101 #include <algorithm>
103 namespace HPHP {
104 namespace Compiler {
105 ///////////////////////////////////////////////////////////////////////////////
107 TRACE_SET_MOD(emitter)
109 using boost::dynamic_pointer_cast;
110 using boost::static_pointer_cast;
112 namespace StackSym {
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);
150 std::string res;
151 switch (symFlavor) {
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;
161 default: break;
163 char marker = StackSym::GetMarker(sym);
164 switch (marker) {
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;
172 default: break;
174 if (res == "") {
175 if (sym == StackSym::None) {
176 res = "None";
177 } else {
178 res = "?";
181 return res;
185 //=============================================================================
186 // Emitter.
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()); \
192 assert(false); \
193 } while (0)
195 // RAII guard for function creation.
196 class FuncFinisher {
197 EmitterVisitor* m_ev;
198 Emitter& m_e;
199 FuncEmitter* m_fe;
201 public:
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);
207 ~FuncFinisher() {
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 {
215 Emitter& m_e;
216 LocationPtr m_loc;
218 public:
219 LocationGuard(Emitter& e, LocationPtr newLoc)
220 : m_e(e), m_loc(e.getTempLocation()) {
221 if (newLoc) m_e.setTempLocation(newLoc);
223 ~LocationGuard() {
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());
232 int count = 0;
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
238 // immediates.
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) {
254 ++count;
257 assert(vec - &immVec[0] == int(immVec.size()));
258 return count;
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(); \
266 POP_##pop; \
267 const int nIn UNUSED = COUNT_##pop; \
268 POP_HA_##imm; \
269 PUSH_##push; \
270 getUnitEmitter().emitOp(Op##name); \
271 IMPL_##imm; \
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); \
279 #define COUNT_NOV 0
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
290 #define ONE(t) \
291 DEC_##t a1
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
298 #define NA
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&
310 #define DEC_OA uchar
312 #define POP_NOV
313 #define POP_ONE(t) \
314 POP_##t(0)
315 #define POP_TWO(t1, t2) \
316 POP_##t1(0); \
317 POP_##t2(1)
318 #define POP_THREE(t1, t2, t3) \
319 POP_##t1(0); \
320 POP_##t2(1); \
321 POP_##t3(2)
322 #define POP_FOUR(t1, t2, t3, t4) \
323 POP_##t1(0); \
324 POP_##t2(1); \
325 POP_##t3(2); \
326 POP_##t4(3)
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()
335 #define POP_FMANY \
336 getEmitterVisitor().popEvalStackMany(a1, StackSym::F)
337 #define POP_CMANY \
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) \
348 POP_HA_##t(nIn)
349 #define POP_HA_TWO(t1, t2) \
350 POP_HA_##t1(nIn); \
351 POP_HA_##t2(nIn)
352 #define POP_HA_THREE(t1, t2, t3) \
353 POP_HA_##t1(nIn); \
354 POP_HA_##t2(nIn); \
355 POP_HA_##t3(nIn)
356 #define POP_HA_FOUR(t1, t2, t3, t4) \
357 POP_HA_##t1(nIn); \
358 POP_HA_##t2(nIn); \
359 POP_HA_##t3(nIn); \
360 POP_HA_##t4(nIn)
362 #define POP_HA_NA
363 #define POP_HA_MA(i)
364 #define POP_HA_BLA(i)
365 #define POP_HA_SLA(i)
366 #define POP_HA_IVA(i)
367 #define POP_HA_IA(i)
368 #define POP_HA_I64A(i)
369 #define POP_HA_DA(i)
370 #define POP_HA_SA(i)
371 #define POP_HA_AA(i)
372 #define POP_HA_BA(i)
373 #define POP_HA_OA(i)
375 #define POP_HA_HA(i) \
376 getEmitterVisitor().popSymbolicLocal(opcode, i, curPos)
378 #define PUSH_NOV
379 #define PUSH_ONE(t) \
380 PUSH_##t
381 #define PUSH_TWO(t1, t2) \
382 PUSH_##t2; \
383 PUSH_##t1
384 #define PUSH_THREE(t1, t2, t3) \
385 PUSH_##t3; \
386 PUSH_##t2; \
387 PUSH_##t1
388 #define PUSH_FOUR(t1, t2, t3, t4) \
389 PUSH_##t4; \
390 PUSH_##t3; \
391 PUSH_##t2; \
392 PUSH_##t1
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);
410 #define IMPL_NA
411 #define IMPL_ONE(t) \
412 IMPL1_##t
413 #define IMPL_TWO(t1, t2) \
414 IMPL1_##t1; \
415 IMPL2_##t2
416 #define IMPL_THREE(t1, t2, t3) \
417 IMPL1_##t1; \
418 IMPL2_##t2; \
419 IMPL3_##t3
420 #define IMPL_FOUR(t1, t2, t3, t4) \
421 IMPL1_##t1; \
422 IMPL2_##t2; \
423 IMPL3_##t3; \
424 IMPL4_##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]); \
432 } while (0)
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) { \
441 IMPL_BA(*var[i]); \
443 } while (0)
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); \
454 IMPL_BA(*i.dest); \
456 } while (0)
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); \
463 } while (0)
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()); \
512 } else { \
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)
527 OPCODES
528 #undef O
529 #undef ONE
530 #undef TWO
531 #undef THREE
532 #undef FOUR
533 #undef NA
534 #undef DEC_MA
535 #undef DEC_IVA
536 #undef DEC_HA
537 #undef DEC_IA
538 #undef DEC_I64A
539 #undef DEC_DA
540 #undef DEC_SA
541 #undef DEC_AA
542 #undef DEC_BA
543 #undef DEC_OA
544 #undef POP_NOV
545 #undef POP_ONE
546 #undef POP_TWO
547 #undef POP_THREE
548 #undef POP_FOUR
549 #undef POP_LMANY
550 #undef POP_C_LMANY
551 #undef POP_V_LMANY
552 #undef POP_CV
553 #undef POP_VV
554 #undef POP_HV
555 #undef POP_AV
556 #undef POP_RV
557 #undef POP_FV
558 #undef POP_LREST
559 #undef POP_FMANY
560 #undef POP_CMANY
561 #undef POP_HA_ONE
562 #undef POP_HA_TWO
563 #undef POP_HA_THREE
564 #undef POP_HA_FOUR
565 #undef POP_HA_NA
566 #undef POP_HA_MA
567 #undef POP_HA_IVA
568 #undef POP_HA_IA
569 #undef POP_HA_I64A
570 #undef POP_HA_DA
571 #undef POP_HA_SA
572 #undef POP_HA_AA
573 #undef POP_HA_BA
574 #undef POP_HA_OA
575 #undef POP_HA_HA
576 #undef PUSH_NOV
577 #undef PUSH_ONE
578 #undef PUSH_TWO
579 #undef PUSH_THREE
580 #undef PUSH_FOUR
581 #undef PUSH_CV
582 #undef PUSH_VV
583 #undef PUSH_HV
584 #undef PUSH_AV
585 #undef PUSH_RV
586 #undef PUSH_FV
587 #undef IMPL_ONE
588 #undef IMPL_TWO
589 #undef IMPL_THREE
590 #undef IMPL_FOUR
591 #undef IMPL_NA
592 #undef IMPL_MA
593 #undef IMPL1_MA
594 #undef IMPL2_MA
595 #undef IMPL3_MA
596 #undef IMPL4_MA
597 #undef IMPL_BLA
598 #undef IMPL1_BLA
599 #undef IMPL2_BLA
600 #undef IMPL3_BLA
601 #undef IMPL4_BLA
602 #undef IMPL_SLA
603 #undef IMPL1_SLA
604 #undef IMPL2_SLA
605 #undef IMPL3_SLA
606 #undef IMPL4_SLA
607 #undef IMPL_IVA
608 #undef IMPL1_IVA
609 #undef IMPL2_IVA
610 #undef IMPL3_IVA
611 #undef IMPL4_IVA
612 #undef IMPL1_HA
613 #undef IMPL2_HA
614 #undef IMPL3_HA
615 #undef IMPL4_HA
616 #undef IMPL1_IA
617 #undef IMPL2_IA
618 #undef IMPL3_IA
619 #undef IMPL4_IA
620 #undef IMPL_I64A
621 #undef IMPL1_I64A
622 #undef IMPL2_I64A
623 #undef IMPL3_I64A
624 #undef IMPL4_I64A
625 #undef IMPL_DA
626 #undef IMPL1_DA
627 #undef IMPL2_DA
628 #undef IMPL3_DA
629 #undef IMPL4_DA
630 #undef IMPL_SA
631 #undef IMPL1_SA
632 #undef IMPL2_SA
633 #undef IMPL3_SA
634 #undef IMPL4_SA
635 #undef IMPL_AA
636 #undef IMPL1_AA
637 #undef IMPL2_AA
638 #undef IMPL3_AA
639 #undef IMPL4_AA
640 #undef IMPL_BA
641 #undef IMPL1_BA
642 #undef IMPL2_BA
643 #undef IMPL3_BA
644 #undef IMPL4_BA
645 #undef IMPL_OA
646 #undef IMPL1_OA
647 #undef IMPL2_OA
648 #undef IMPL3_OA
649 #undef IMPL4_OA
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",
657 source.size(),
658 dest.size());
659 Logger::Warning("src stack : %s", source.pretty().c_str());
660 Logger::Warning("dest stack: %s", dest.pretty().c_str());
661 assert(false);
662 return;
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));
671 if (!matches) {
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());
677 assert(false);
678 return;
683 std::string SymbolicStack::pretty() const {
684 std::ostringstream out;
685 out << " [" << std::hex;
686 size_t j = 0;
687 for (size_t i = 0; i < m_symStack.size(); ++i) {
688 while (j < m_actualStack.size() && m_actualStack[j] < int(i)) {
689 ++j;
691 if (j < m_actualStack.size() && m_actualStack[j] == int(i)) {
692 out << "*";
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();
701 out << ' ';
703 return out.str();
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;
745 return nullptr;
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);
763 } else {
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);
776 nonNull = true;
778 se.className = s;
779 se.notNull = se.notNull || nonNull;
782 void SymbolicStack::setNotRef() {
783 assert(m_symStack.size());
784 SymEntry& se = m_symStack.back();
785 se.notRef = true;
788 bool SymbolicStack::getNotRef() const {
789 assert(m_symStack.size());
790 const SymEntry& se = m_symStack.back();
791 return se.notRef;
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();
802 if (se.className) {
803 assert(dt == KindOfObject);
804 se.notNull = true;
805 } else {
806 assert(se.metaType == META_NONE);
807 se.metaType = META_DATA_TYPE;
808 se.metaData.dt = dt;
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) {
819 return KindOfObject;
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;
837 se.notRef = false;
840 void SymbolicStack::setClsBaseType(ClassBaseType type) {
841 assert(!m_symStack.empty());
842 m_symStack.back().clsBaseType = type;
845 void SymbolicStack::setUnnamedLocal(int index,
846 int localId,
847 Offset startOff) {
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() {
871 m_symStack.clear();
872 m_actualStack.clear();
873 m_fdescCount = 0;
876 void SymbolicStack::consumeBelowTop(int depth) {
877 if (int(m_symStack.size()) < depth + 1) {
878 Logger::Warning(
879 "Emitter tried to consumeBelowTop() when the symbolic "
880 "stack did not have enough elements in it.");
881 assert(false);
882 return;
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
890 * this element.
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) {
897 --m_actualStack[i];
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) {
907 return j;
910 NOT_REACHED();
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
954 // actual stack.
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) {
963 ++m_actualStack[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) {
981 if (isSet()) {
982 InvariantViolation(
983 "Label::set was called more than once on the same "
984 "Label; originally set to %d; now %d",
985 m_off,
986 e.getUnitEmitter().bcPos());
987 return;
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());
1002 } else {
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());
1008 } else {
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 "
1023 "been set to %d",
1024 m_off);
1025 return;
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());
1031 } else {
1032 m_evalStack = ev.getEvalStack();
1036 struct FPIRegionRecorder {
1037 FPIRegionRecorder(EmitterVisitor* ev, UnitEmitter& ue, SymbolicStack& stack,
1038 Offset start)
1039 : m_ev(ev), m_ue(ue), m_stack(stack), m_fpOff(m_stack.sizeActual()),
1040 m_start(start) {
1041 m_stack.pushFDesc();
1043 ~FPIRegionRecorder() {
1044 m_stack.popFDesc();
1045 m_ev->newFPIRegion(m_start, m_ue.bcPos(), m_fpOff);
1047 private:
1048 EmitterVisitor* m_ev;
1049 UnitEmitter& m_ue;
1050 SymbolicStack& m_stack;
1051 int m_fpOff;
1052 Offset m_start;
1055 //=============================================================================
1056 // EmitterVisitor.
1058 void MetaInfoBuilder::add(int pos, Unit::MetaInfo::Kind kind,
1059 bool mVector, int arg, Id data) {
1060 assert(arg >= 0);
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) {
1066 info.clear();
1067 } else if (i == 1 && info[0].m_kind == Unit::MetaInfo::NopOut) {
1068 return;
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
1074 i = 0;
1076 info.insert(info.begin() + i, Unit::MetaInfo(kind, arg, data));
1079 void MetaInfoBuilder::addKnownDataType(DataType dt,
1080 bool dtPredicted,
1081 int pos,
1082 bool mVector,
1083 int arg) {
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();
1108 it != end; ++it) {
1109 index1.push_back(it->first);
1110 index2.push_back(sz1 + sz2 + data.size());
1112 const Vec& v = it->second;
1113 assert(v.size());
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);
1120 } else {
1121 union {
1122 uint32_t val;
1123 uint8_t bytes[4];
1124 } u;
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);
1141 free(meta);
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++) {
1155 delete it->second;
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) {
1160 delete *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)",
1168 forInstruction,
1169 m_ue.bcPos());
1170 return true;
1172 return false;
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(),
1179 where,
1180 m_ue.bcPos());
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(),
1191 m_ue.bcPos());
1192 return;
1195 if (arg >= 0 && pos >= 0 &&
1196 (expected == StackSym::C || expected == StackSym::R)) {
1197 m_metaInfo.addKnownDataType(m_evalStack.getKnownType(),
1198 m_evalStack.isTypePredicted(),
1199 pos, false, arg);
1202 char sym = m_evalStack.top();
1203 char actual = StackSym::GetSymFlavor(sym);
1204 m_evalStack.pop();
1205 if (actual != expected) {
1206 InvariantViolation(
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\" "
1211 "at offset %d)",
1212 StackSym::ToString(expected).c_str(),
1213 StackSym::ToString(actual).c_str(),
1214 m_ue.bcPos());
1218 void EmitterVisitor::popSymbolicLocal(Opcode op, int arg, int pos) {
1219 // Special case for instructions that consume the loc below the
1220 // top.
1221 int belowTop = -1;
1222 if (op == OpCGetL3) {
1223 belowTop = 3;
1224 } else if (op == OpCGetL2) {
1225 belowTop = 2;
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);
1237 } else {
1238 if (arg >= 0 && pos >= 0) {
1239 m_metaInfo.addKnownDataType(m_evalStack.getKnownType(),
1240 m_evalStack.isTypePredicted(),
1241 pos, false, arg);
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) {
1255 InvariantViolation(
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(),
1262 m_ue.bcPos());
1264 } else if (marker == StackSym::W) {
1265 if (symFlavor != StackSym::None) {
1266 InvariantViolation(
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(),
1273 m_ue.bcPos());
1275 } else if (marker == StackSym::M) {
1276 assert(symFlavor == StackSym::A);
1277 } else {
1278 break;
1280 m_evalStack.pop();
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 "
1286 "(at offset %d)",
1287 m_ue.bcPos());
1288 return;
1290 char sym = m_evalStack.top();
1291 char symFlavor = StackSym::GetSymFlavor(sym);
1292 m_evalStack.pop();
1293 if (symFlavor != StackSym::C && symFlavor != StackSym::L &&
1294 symFlavor != StackSym::R && symFlavor != StackSym::H) {
1295 InvariantViolation(
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(),
1302 m_ue.bcPos());
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
1314 // symbolic flavor
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) {
1324 InvariantViolation(
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 "
1329 "offset %d)",
1330 StackSym::ToString(expected).c_str(),
1331 StackSym::ToString(actual).c_str(),
1332 m_ue.bcPos());
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.");
1365 return;
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) {
1376 InvariantViolation(
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;
1383 return;
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;
1394 return;
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 {
1419 public:
1420 IterFreeThunklet(Id iterId, bool itRef) : m_id(iterId), m_itRef(itRef) {}
1421 virtual void emit(Emitter& e) {
1422 if (m_itRef) {
1423 e.MIterFree(m_id);
1424 } else {
1425 e.IterFree(m_id);
1427 e.Unwind();
1429 private:
1430 Id m_id;
1431 bool m_itRef;
1435 * A thunklet for the fault region protecting a silenced (@) expression.
1437 class RestoreErrorReportingThunklet : public Thunklet {
1438 public:
1439 explicit RestoreErrorReportingThunklet(Id loc) : m_oldLevelLoc(loc) {}
1440 virtual void emit(Emitter& e) {
1441 e.getEmitterVisitor().emitRestoreErrorReporting(e, m_oldLevelLoc);
1442 e.Unwind();
1444 private:
1445 Id m_oldLevelLoc;
1448 class UnsetUnnamedLocalThunklet : public Thunklet {
1449 public:
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);
1454 e.Unwind();
1456 private:
1457 Id m_loc;
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) {
1471 if (!exp) {
1472 // Empty slot
1473 return;
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();
1486 } else {
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));
1490 visit(exp);
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
1501 * expression.
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));
1510 if (binOpNode) {
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);
1516 Label localLabel;
1517 ExpressionPtr lhs = binOpNode->getExp1();
1518 if (isOr) {
1519 Emitter lhsEmitter(lhs, m_ue, *this);
1520 visitIfCondition(lhs, lhsEmitter, tru, localLabel, false);
1521 // falls through if that condition was false
1522 } else {
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()) {
1528 localLabel.set(e);
1530 if (currentPositionIsReachable()) {
1531 ExpressionPtr rhs = binOpNode->getExp2();
1532 Emitter rhsEmitter(rhs, m_ue, *this);
1533 visitIfCondition(rhs, rhsEmitter, tru, fals, truFallthrough);
1535 return;
1539 UnaryOpExpressionPtr unOpNode(dynamic_pointer_cast<UnaryOpExpression>(cond));
1540 if (unOpNode) {
1541 int op = unOpNode->getOp();
1542 // Logical not
1543 if (op == '!') {
1544 ExpressionPtr val = unOpNode->getExpression();
1545 Emitter valEmitter(val, m_ue, *this);
1546 visitIfCondition(val, valEmitter, fals, tru, !truFallthrough);
1547 return;
1551 Variant val;
1552 if (cond->getScalarValue(val)) {
1553 if (truFallthrough) {
1554 if (!val.toBoolean()) e.Jmp(fals);
1555 } else {
1556 if (val.toBoolean()) e.Jmp(tru);
1558 return;
1561 visit(cond);
1562 emitConvertToCell(e);
1563 if (truFallthrough) {
1564 e.JmpZ(fals);
1565 } else {
1566 e.JmpNZ(tru);
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());
1585 if (!func) return;
1587 m_file = file;
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());
1594 assert(ar);
1595 MethodStatementPtr m(dynamic_pointer_cast<MethodStatement>(func->getStmt()));
1596 if (!m) return;
1597 StatementListPtr stmts(m->getStmts());
1598 if (!stmts) return;
1600 Emitter e(m, m_ue, *this);
1602 Label ctFatal;
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();
1613 // Emit afterwards
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:
1634 break;
1635 case Statement::KindOfInterfaceStatement:
1636 case Statement::KindOfClassStatement: {
1637 // Handle classes directly here, since only top-level classes are
1638 // hoistable.
1639 ClassScopePtr cNode = s->getClassScope();
1640 PreClass::Hoistable h = emitClass(e, cNode, true);
1641 if (h < allHoistable) allHoistable = h;
1642 break;
1644 case Statement::KindOfTypedefStatement:
1645 emitTypedef(e, static_pointer_cast<TypedefStatement>(s));
1646 notMergeOnly = true; // TODO(#2103206): typedefs should be mergable
1647 break;
1648 case Statement::KindOfReturnStatement:
1649 if (mainReturn.m_type != KindOfInvalid) break;
1650 if (notMergeOnly) {
1651 tvWriteUninit(&mainReturn);
1652 m_ue.returnSeen();
1653 goto fail;
1654 } else {
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);
1660 goto fail;
1662 if (v.isString()) {
1663 v = String(StringData::GetStaticString(v.asCStrRef().get()));
1664 } else if (v.isArray()) {
1665 v = Array(ArrayData::GetScalarArray(v.asCArrRef().get()));
1666 } else {
1667 assert(!IS_REFCOUNTED_TYPE(v.getType()));
1669 mainReturn = *v.getTypedAccessor();
1670 m_ue.returnSeen();
1672 break;
1673 case Statement::KindOfExpStatement:
1674 if (mainReturn.m_type == KindOfInvalid) {
1675 ExpressionPtr e =
1676 static_pointer_cast<ExpStatement>(s)->getExpression();
1677 switch (e->getKindOf()) {
1678 case Expression::KindOfSimpleFunctionCall: {
1679 SimpleFunctionCallPtr func =
1680 static_pointer_cast<SimpleFunctionCall>(e);
1681 StringData *name;
1682 TypedValue tv;
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);
1690 visit(s);
1691 continue;
1693 break;
1695 case Expression::KindOfAssignmentExpression: {
1696 AssignmentExpressionPtr ae(
1697 static_pointer_cast<AssignmentExpression>(e));
1698 StringData *name;
1699 TypedValue tv;
1700 if (ae->isSimpleGlobalAssign(&name, &tv)) {
1701 m_ue.pushMergeableDef(UnitMergeKindGlobal, name, tv);
1702 visit(s);
1703 continue;
1705 break;
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(
1718 kind,
1719 StringData::GetStaticString(inc->includePath()));
1720 visit(s);
1721 continue;
1725 break;
1727 default:
1728 break;
1730 } // fall through
1731 default:
1732 if (mainReturn.m_type != KindOfInvalid) break;
1733 // fall through
1734 fail:
1735 notMergeOnly = true;
1736 visit(s);
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
1747 Label exit;
1748 if (ctFatal.isUsed() || ctFatalWithPop.isUsed()) {
1749 e.Jmp(exit);
1750 if (ctFatalWithPop.isUsed()) {
1751 ctFatalWithPop.set(e);
1752 e.PopC(); // the number of levels to jump up
1754 if (ctFatal.isUsed()) {
1755 ctFatal.set(e);
1757 static const StringData* msg =
1758 StringData::GetStaticString("Cannot break/continue");
1759 e.String(msg);
1760 e.Fatal(0);
1762 if (exit.isUsed()) {
1763 exit.set(e);
1765 // Pseudo-main returns the integer value 1 by default. If the
1766 // current position in the bytecode is reachable, emit code to
1767 // return 1.
1768 if (currentPositionIsReachable()) {
1769 e.Int(1);
1770 e.RetC();
1774 if (!m_evalStack.empty()) {
1775 InvariantViolation("Eval stack was not empty as expected before "
1776 "emitPostponed* phase");
1779 // Method bodies
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;
1791 if (e->isThis()) {
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)) {
1798 cls = c2;
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());
1810 return nullptr;
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();
1820 if (!act) {
1821 return KindOfUnknown;
1823 return act->getDataType();
1826 void EmitterVisitor::fixReturnType(Emitter& e, FunctionCallPtr fn,
1827 bool useFCallBuiltin) {
1828 int ref = -1;
1829 if (fn->hasAnyContext(Expression::RefValue |
1830 Expression::DeepReference |
1831 Expression::LValue |
1832 Expression::OprLValue |
1833 Expression::UnsetContext)) {
1834 return;
1836 bool voidReturn = false;
1837 if (fn->isValid() && fn->getFuncScope()) {
1838 ref = fn->getFuncScope()->isRefReturn();
1839 if (!(fn->getFuncScope()->getReturnType())) {
1840 voidReturn = true;
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() &&
1849 ref >= 0 &&
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
1853 case */
1854 assert(m_evalStack.get(m_evalStack.size() - 1) == StackSym::R);
1855 Offset cur = m_ue.bcPos();
1856 if (ref) {
1857 e.BoxR();
1858 } else {
1859 e.UnboxR();
1861 m_metaInfo.add(cur, Unit::MetaInfo::NopOut, false, 0, 0);
1864 if (voidReturn) {
1865 m_evalStack.setKnownType(KindOfNull, false /* inferred */);
1866 m_evalStack.setNotRef();
1867 } else if (!ref) {
1868 DataType dt = getPredictedDataType(fn);
1869 if (dt != KindOfUnknown) {
1870 if (useFCallBuiltin) {
1871 switch (dt) {
1872 case KindOfBoolean:
1873 case KindOfInt64:
1874 case KindOfDouble: /* inferred */
1875 m_evalStack.setKnownType(dt, false);
1876 break;
1877 default: /* predicted */
1878 m_evalStack.setKnownType(dt, true);
1879 break;
1881 } else {
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));
1892 visit(kid);
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;
1914 *cap = n;
1915 return true;
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()) {
1925 dt = KindOfUninit;
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;
1931 } else {
1932 return ret;
1934 char sym = m_evalStack.top();
1935 if (StackSym::GetMarker(sym)) return ret;
1936 switch (StackSym::GetSymFlavor(sym)) {
1937 case StackSym::C:
1938 m_evalStack.setNotRef();
1939 m_evalStack.setKnownType(dt);
1940 break;
1941 case StackSym::L:
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);
1949 break;
1952 return ret;
1955 void EmitterVisitor::emitFatal(Emitter& e, const char* message) {
1956 const StringData* msg = StringData::GetStaticString(message);
1957 e.String(msg);
1958 e.Fatal(0);
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:
1970 visitKids(node);
1971 return false;
1973 case Statement::KindOfTypedefStatement: {
1974 emitFatal(e, "Type statements are currently only allowed at "
1975 "the top-level");
1976 return false;
1979 case Statement::KindOfContinueStatement:
1980 case Statement::KindOfBreakStatement: {
1981 BreakStatementPtr bs(static_pointer_cast<BreakStatement>(s));
1982 int64_t n = bs->getDepth();
1983 if (n == 1) {
1984 // Plain old "break;" or "continue;"
1985 if (m_controlTargets.empty()) {
1986 static const StringData* msg =
1987 StringData::GetStaticString("Cannot break/continue 1 level");
1988 e.String(msg);
1989 e.Fatal(0);
1990 return false;
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);
1996 } else {
1997 e.IterFree(m_controlTargets.front().m_itId);
2000 e.Jmp(m_controlTargets.front().m_brkTarg);
2001 } else {
2002 e.Jmp(m_controlTargets.front().m_cntTarg);
2004 return false;
2007 // Dynamic break/continue.
2008 if (n == 0) {
2009 // Depth can't be statically determined.
2010 visit(bs->getExp());
2011 emitConvertToCell(e);
2012 } else {
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()));
2018 e.Fatal(0);
2019 return false;
2021 e.Int(n);
2023 if (bs->is(Statement::KindOfBreakStatement)) {
2024 e.Jmp(m_controlTargets.front().m_brkHand);
2025 } else {
2026 e.Jmp(m_controlTargets.front().m_cntHand);
2028 return false;
2031 case Statement::KindOfDoStatement: {
2032 DoStatementPtr ds(static_pointer_cast<DoStatement>(s));
2033 Label top(e);
2034 Label condition;
2035 Label exit;
2036 Label brkHand;
2037 Label cntHand;
2039 CONTROL_BODY(exit, condition, brkHand, cntHand);
2040 visit(ds->getBody());
2042 condition.set(e);
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()) {
2050 e.Jmp(exit);
2051 emitBreakHandler(e, exit, condition, brkHand, cntHand);
2053 if (exit.isUsed()) exit.set(e);
2054 return false;
2057 case Statement::KindOfCaseStatement: {
2058 // Should never be called. Handled in visitSwitch.
2059 not_reached();
2062 case Statement::KindOfCatchStatement: {
2063 // Store the current exception object in the appropriate local variable
2064 CatchStatementPtr cs(static_pointer_cast<CatchStatement>(node));
2065 StringData* vName =
2066 StringData::GetStaticString(cs->getVariable()->getName());
2067 Id i = m_curFunc->lookupVarId(vName);
2068 emitVirtualLocal(i);
2069 e.Catch();
2070 emitSet(e);
2071 emitPop(e);
2072 visit(cs->getStmt());
2073 return false;
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++) {
2081 visit((*exps)[i]);
2082 emitConvertToCell(e);
2083 e.Print();
2084 e.PopC();
2086 return false;
2089 case Statement::KindOfExpStatement: {
2090 ExpStatementPtr es(static_pointer_cast<ExpStatement>(s));
2091 if (visit(es->getExpression())) {
2092 emitPop(e);
2094 return false;
2097 case Statement::KindOfForStatement: {
2098 ForStatementPtr fs(static_pointer_cast<ForStatement>(s));
2099 if (visit(fs->getInitExp())) {
2100 emitPop(e);
2102 Label preCond(e);
2103 Label preInc;
2104 Label fail;
2105 Label brkHand;
2106 Label cntHand;
2107 if (ExpressionPtr condExp = fs->getCondExp()) {
2108 Label tru;
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());
2117 preInc.set(e);
2118 if (visit(fs->getIncExp())) {
2119 emitPop(e);
2121 e.Jmp(preCond);
2122 emitBreakHandler(e, fail, preInc, brkHand, cntHand);
2123 if (fail.isUsed()) fail.set(e);
2124 return false;
2127 case Statement::KindOfForEachStatement: {
2128 ForEachStatementPtr fe(static_pointer_cast<ForEachStatement>(node));
2129 emitForeach(e, fe);
2130 return false;
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()) {
2141 continue;
2143 StringData* nLiteral = StringData::GetStaticString(sv->getName());
2144 Id i = m_curFunc->lookupVarId(nLiteral);
2145 emitVirtualLocal(i);
2146 e.String(nLiteral);
2147 markGlobalName(e);
2148 e.VGetG();
2149 emitBind(e);
2150 e.PopV();
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
2158 e.Dup();
2159 markNameSecond(e);
2160 markGlobalName(e);
2161 e.VGetG();
2162 e.BindN();
2163 e.PopV();
2164 } else {
2165 not_implemented();
2168 return false;
2171 case Statement::KindOfIfStatement: {
2172 IfStatementPtr ifp(static_pointer_cast<IfStatement>(node));
2173 StatementListPtr branches(ifp->getIfBranches());
2174 int nb = branches->getCount();
2175 Label done;
2176 for (int i = 0; i < nb; i++) {
2177 IfBranchStatementPtr branch(
2178 static_pointer_cast<IfBranchStatement>((*branches)[i]));
2179 Label fals;
2180 if (branch->getCondition()) {
2181 Label tru;
2182 Emitter condEmitter(branch->getCondition(), m_ue, *this);
2183 visitIfCondition(branch->getCondition(), condEmitter,
2184 tru, fals, true);
2185 if (tru.isUsed()) {
2186 tru.set(e);
2189 visit(branch->getStmt());
2190 if (currentPositionIsReachable() && i + 1 < nb) {
2191 e.Jmp(done);
2193 if (fals.isUsed()) {
2194 fals.set(e);
2197 if (done.isUsed()) {
2198 done.set(e);
2200 return false;
2203 case Statement::KindOfIfBranchStatement:
2204 not_reached(); // handled by KindOfIfStatement
2206 case Statement::KindOfReturnStatement: {
2207 ReturnStatementPtr r(static_pointer_cast<ReturnStatement>(node));
2208 bool retV = false;
2209 if (visit(r->getRetExp())) {
2210 if (r->getRetExp()->getContext() & Expression::RefValue) {
2211 emitConvertToVar(e);
2212 emitFreePendingIters(e);
2213 retV = true;
2214 } else {
2215 emitConvertToCell(e);
2216 emitFreePendingIters(e);
2218 } else {
2219 emitFreePendingIters(e);
2220 e.Null();
2223 if (m_curFunc->isGenerator()) {
2224 assert(!retV);
2225 m_metaInfo.addKnownDataType(
2226 KindOfObject, false, m_ue.bcPos(), false, 1);
2227 assert(m_evalStack.size() == 1);
2228 e.ContRetC();
2229 return false;
2232 if (r->isGuarded()) {
2233 m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::GuardedThis,
2234 false, 0, 0);
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,
2241 false, 0, v);
2243 if (retV) {
2244 e.RetV();
2245 } else {
2246 e.RetC();
2248 return false;
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;
2267 svInfo.name = name;
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()) {
2276 visit(value);
2277 emitConvertToCell(e);
2278 e.StaticLocInit(local, name);
2279 } else {
2280 Label done;
2281 e.StaticLoc(local, name);
2282 e.JmpNZ(done);
2284 emitVirtualLocal(local);
2285 visit(value);
2286 emitConvertToCell(e);
2287 emitSet(e);
2288 emitPop(e);
2290 done.set(e);
2293 return false;
2296 case Statement::KindOfSwitchStatement: {
2297 SwitchStatementPtr sw(static_pointer_cast<SwitchStatement>(node));
2298 StatementListPtr cases(sw->getCases());
2299 if (!cases) {
2300 visit(sw->getExp());
2301 emitPop(e);
2302 return false;
2304 uint ncase = cases->getCount();
2305 std::vector<Label> caseLabels(ncase);
2306 Label done;
2307 Label brkHand;
2308 Label cntHand;
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();
2322 Id tempLocal = -1;
2323 Offset start = InvalidAbsoluteOffset;
2325 bool enabled = RuntimeOption::EnableEmitSwitch;
2326 SimpleFunctionCallPtr
2327 call(dynamic_pointer_cast<SimpleFunctionCall>(subject));
2329 SwitchState state;
2330 bool didSwitch = false;
2331 if (enabled) {
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());
2342 emitPop(e);
2343 } else if (stype == KindOfInt64) {
2344 emitIntegerSwitch(e, sw, caseLabels, done, state);
2345 } else {
2346 assert(IS_STRING_TYPE(stype));
2347 emitStringSwitch(e, sw, caseLabels, done, state);
2349 didSwitch = true;
2352 if (!didSwitch) {
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);
2358 visit(subject);
2359 emitConvertToCell(e);
2360 emitSet(e);
2361 emitPop(e);
2362 start = m_ue.bcPos();
2365 int defI = -1;
2366 for (uint i = 0; i < ncase; i++) {
2367 CaseStatementPtr c(static_pointer_cast<CaseStatement>((*cases)[i]));
2368 ExpressionPtr condition = c->getCondition();
2369 if (condition) {
2370 if (simpleSubject) {
2371 // Evaluate the subject every time.
2372 visit(subject);
2373 emitConvertToCellOrLoc(e);
2374 visit(condition);
2375 emitConvertToCell(e);
2376 emitConvertSecondToCell(e);
2377 } else {
2378 emitVirtualLocal(tempLocal);
2379 emitCGet(e);
2380 visit(condition);
2381 emitConvertToCell(e);
2383 e.Eq();
2384 e.JmpNZ(caseLabels[i]);
2385 } else {
2386 // Default clause. The last one wins.
2387 defI = i;
2390 if (defI != -1) {
2391 e.Jmp(caseLabels[defI]);
2392 } else {
2393 e.Jmp(done);
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()) {
2403 e.Jmp(done);
2404 emitBreakHandler(e, done, done, brkHand, cntHand);
2406 done.set(e);
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);
2414 emitUnset(e);
2415 m_curFunc->freeUnnamedLocal(tempLocal);
2417 return false;
2420 case Statement::KindOfThrowStatement: {
2421 visitKids(node);
2422 emitConvertToCell(e);
2423 e.Throw();
2424 return false;
2427 case Statement::KindOfFinallyStatement: {
2428 return false;
2431 case Statement::KindOfTryStatement: {
2432 if (!m_evalStack.empty()) {
2433 InvariantViolation(
2434 "Emitter detected that the evaluation stack is not empty "
2435 "at the beginning of a try region: %d", m_ue.bcPos());
2437 Label after;
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
2444 e.Jmp(after);
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",
2450 end);
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>
2461 ((*catches)[i]));
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,
2467 // fall into here.
2468 if (!firstHandler) {
2469 e.Jmp(after);
2470 } else {
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,
2477 label));
2478 visit(c);
2482 after.set(e);
2483 return false;
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]);
2492 return false;
2495 case Statement::KindOfWhileStatement: {
2496 WhileStatementPtr ws(static_pointer_cast<WhileStatement>(s));
2497 Label preCond(e);
2498 Label fail;
2499 Label brkHand;
2500 Label cntHand;
2502 Label tru;
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());
2512 e.Jmp(preCond);
2513 emitBreakHandler(e, fail, preCond, brkHand, cntHand);
2514 if (fail.isUsed()) fail.set(e);
2515 return false;
2518 case Statement::KindOfInterfaceStatement:
2519 case Statement::KindOfClassStatement: {
2520 emitClass(e, node->getClassScope(), false);
2521 return false;
2524 case Statement::KindOfClassVariable:
2525 case Statement::KindOfClassConstant:
2526 case Statement::KindOfMethodStatement:
2527 // handled by emitClass
2528 not_reached();
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.
2540 return false;
2542 Label*& label = m_methLabels[nName];
2543 if (!label) {
2544 // its possible to see the same generator more than once
2545 label = new Label();
2546 } else {
2547 return false;
2550 postponeMeth(m, nullptr, true);
2551 } else {
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);
2557 return 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]);
2564 return false;
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];
2571 lab.set(e);
2572 return false;
2574 case Statement::KindOfUseTraitStatement:
2575 case Statement::KindOfTraitPrecStatement:
2576 case Statement::KindOfTraitAliasStatement: {
2577 not_implemented();
2580 } else {
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]);
2598 e.Null();
2599 return true;
2602 emitVisitAndUnset(e, exp);
2603 e.Null();
2604 return true;
2606 if (op == T_ARRAY) {
2607 int tuple_cap;
2608 if (u->isScalar()) {
2609 TypedValue tv;
2610 tvWriteUninit(&tv);
2611 initScalar(tv, u);
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);
2626 } else {
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();
2632 if (capacity > 0) {
2633 m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::ArrayCapacity,
2634 false, 0, capacity);
2637 e.NewArray();
2638 visit(ex);
2640 return true;
2641 } else if (op == T_ISSET) {
2642 ExpressionListPtr list =
2643 dynamic_pointer_cast<ExpressionList>(u->getExpression());
2644 if (list) {
2645 // isset($a, $b, ...) ==> isset($a) && isset($b) && ...
2646 Label done;
2647 int n = list->getCount();
2648 for (int i = 0; i < n - 1; ++i) {
2649 visit((*list)[i]);
2650 emitIsset(e);
2651 e.Dup();
2652 e.JmpZ(done);
2653 emitPop(e);
2655 // Treat the last one specially; let it fall through
2656 visit((*list)[n - 1]);
2657 emitIsset(e);
2658 done.set(e);
2659 } else {
2660 // Simple case
2661 visit(u->getExpression());
2662 emitIsset(e);
2664 return true;
2665 } else if (op == '+' || op == '-') {
2666 e.Int(0);
2669 Id oldErrorLevelLoc = -1;
2670 Offset start = InvalidAbsoluteOffset;
2671 if (op == '@') {
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);
2683 e.Int(0);
2684 m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::NopOut, false, 0, 0);
2685 e.FPassC(0);
2687 e.FCall(1);
2688 e.UnboxR();
2689 emitSet(e); // set $oldErrorLevelLoc
2690 e.PopC();
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)
2701 e.Int(0);
2702 } else {
2703 // __FILE__ and __DIR__ are special unary ops that don't
2704 // have expressions
2705 assert(op == T_FILE || op == T_DIR);
2707 switch (op) {
2708 case T_INC:
2709 case T_DEC: {
2710 int cop = IncDec_invalid;
2711 if (op == T_INC) {
2712 if (u->getFront()) {
2713 cop = PreInc;
2714 } else {
2715 cop = PostInc;
2717 } else {
2718 if (u->getFront()) {
2719 cop = PreDec;
2720 } else {
2721 cop = PostDec;
2724 emitIncDec(e, cop);
2725 break;
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;
2733 case '(': 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;
2742 case '@': {
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);
2749 break;
2751 case T_PRINT: e.Print(); break;
2752 case T_EVAL: e.Eval(); break;
2753 case T_FILE: {
2754 e.File();
2755 break;
2757 case T_DIR: {
2758 e.Dir();
2759 break;
2761 default:
2762 assert(false);
2764 return true;
2767 case Expression::KindOfAssignmentExpression: {
2768 AssignmentExpressionPtr ae(
2769 static_pointer_cast<AssignmentExpression>(node));
2770 ExpressionPtr rhs = ae->getValue();
2771 Id tempLocal = -1;
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);
2785 } else {
2786 visit(rhs);
2789 if (rhs->hasContext(Expression::RefValue)) {
2790 emitConvertToVar(e);
2791 emitBind(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);
2801 } else {
2802 emitConvertToCell(e);
2803 emitSet(e);
2805 return true;
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);
2816 emitSetOp(e, op);
2817 return true;
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()) {
2825 e.False();
2826 e.Jmp(done);
2828 if (tru.isUsed()) tru.set(e);
2829 if (currentPositionIsReachable()) {
2830 e.True();
2832 done.set(e);
2833 return true;
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);
2854 e.FCall(0);
2855 e.UnboxR();
2856 e.InstanceOf();
2857 } else if (s != "") {
2858 ClassScopeRawPtr cls = second->getOriginalClass();
2859 if (cls) {
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);
2868 } else {
2869 visit(b->getExp2());
2870 emitConvertToCell(e);
2871 e.InstanceOf();
2873 } else {
2874 visit(b->getExp2());
2875 emitConvertToCell(e);
2876 e.InstanceOf();
2878 return true;
2881 if (op == T_COLLECTION) {
2882 ScalarExpressionPtr cls =
2883 static_pointer_cast<ScalarExpression>(b->getExp1());
2884 int nElms = 0;
2885 ExpressionListPtr el;
2886 if (b->getExp2()) {
2887 el = static_pointer_cast<ExpressionList>(b->getExp2());
2888 nElms = el->getCount();
2890 const std::string* clsName = nullptr;
2891 cls->getString(clsName);
2892 int cType = 0;
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;
2903 if (nElms != 2) {
2904 throw IncludeTimeFatalException(b,
2905 "Pair objects must have exactly 2 elements");
2907 } else {
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);
2914 if (kvPairs) {
2915 for (int i = 0; i < nElms; i++) {
2916 ArrayPairExpressionPtr ap(
2917 static_pointer_cast<ArrayPairExpression>((*el)[i]));
2918 ExpressionPtr key = ap->getName();
2919 if (!key) {
2920 throw IncludeTimeFatalException(ap,
2921 "Keys must be specified for Map and StableMap "
2922 "initialization");
2924 visit(key);
2925 emitConvertToCell(e);
2926 visit(ap->getValue());
2927 emitConvertToCell(e);
2928 e.ColAddElemC();
2930 } else {
2931 for (int i = 0; i < nElms; i++) {
2932 ArrayPairExpressionPtr ap(
2933 static_pointer_cast<ArrayPairExpression>((*el)[i]));
2934 ExpressionPtr key = ap->getName();
2935 if ((bool)key) {
2936 throw IncludeTimeFatalException(ap,
2937 "Keys may not be specified for Vector, Set, or Pair "
2938 "initialization");
2940 visit(ap->getValue());
2941 emitConvertToCell(e);
2942 e.ColAddNewElemC();
2945 return true;
2948 visit(b->getExp1());
2949 emitConvertToCellOrLoc(e);
2950 visit(b->getExp2());
2951 emitConvertToCell(e);
2952 emitConvertSecondToCell(e);
2953 switch (op) {
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);
2976 return true;
2979 case Expression::KindOfClassConstantExpression: {
2980 ClassConstantExpressionPtr cc(
2981 static_pointer_cast<ClassConstantExpression>(node));
2982 StringData* nName = StringData::GetStaticString(cc->getConName());
2983 if (cc->isStatic()) {
2984 // static::Constant
2985 e.LateBoundCls();
2986 e.ClsCns(nName);
2987 } else if (cc->getClass()) {
2988 // $x::Constant
2989 ExpressionPtr cls(cc->getClass());
2990 visit(cls);
2991 emitAGet(e);
2992 e.ClsCns(nName);
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
3001 e.Self();
3002 e.ClsCns(nName);
3003 } else if (cc->isParent()) {
3004 // parent::Constant inside trait or pseudomain
3005 e.Parent();
3006 e.ClsCns(nName);
3007 } else {
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);
3018 return true;
3021 case Expression::KindOfConstantExpression: {
3022 ConstantExpressionPtr c(static_pointer_cast<ConstantExpression>(node));
3023 if (c->isNull()) {
3024 e.Null();
3025 } else if (c->isBoolean()) {
3026 if (c->getBooleanValue()) {
3027 e.True();
3028 } else {
3029 e.False();
3031 return true;
3032 } else {
3033 std::string nameStr = c->getOriginalName();
3034 StringData* nName = StringData::GetStaticString(nameStr);
3035 if (c->hadBackslash()) {
3036 e.CnsE(nName);
3037 } else {
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);
3043 } else {
3044 e.Cns(StringData::GetStaticString(c->getName()));
3048 return true;
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;
3056 int i = 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);
3066 if (n) {
3067 visit((*args)[i++]);
3068 emitConvertToCellOrLoc(e);
3069 if (i == n) {
3070 emitConvertToCell(e);
3071 e.CastString();
3072 } else {
3073 while (i < n) {
3074 visit((*args)[i++]);
3075 emitConvertToCell(e);
3076 emitConvertSecondToCell(e);
3077 e.Concat();
3080 } else {
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);
3087 e.FPassC(0);
3088 delete fpi;
3089 e.FCall(1);
3091 return true;
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();
3105 Variant v;
3106 if (!ae->isSuperGlobal() && offset &&
3107 offset->getScalarValue(v) && (v.isInteger() || v.isString())) {
3108 if (v.isString()) {
3109 m_evalStack.push(StackSym::T);
3110 m_evalStack.setString(
3111 StringData::GetStaticString(v.toCStrRef().get()));
3112 } else {
3113 m_evalStack.push(StackSym::I);
3114 m_evalStack.setInt(v.asInt64Val());
3116 markElem(e);
3117 } else if (visit(offset)) {
3118 emitConvertToCellOrLoc(e);
3119 if (ae->isSuperGlobal()) {
3120 markGlobalName(e);
3121 } else {
3122 markElem(e);
3124 } else {
3125 markNewElem(e);
3127 if (!ae->hasAnyContext(Expression::AccessContext|
3128 Expression::ObjectContext)) {
3129 m_tempLoc = ae->getLocation();
3131 return true;
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];
3146 Variant v;
3147 if (p->getScalarValue(v)) {
3148 assert(v.isString());
3149 StringData* msg = StringData::GetStaticString(v.toString());
3150 throw IncludeTimeFatalException(call, msg->data());
3152 not_reached();
3154 } else if (emitCallUserFunc(e, call)) {
3155 return true;
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);
3162 e.AKExists();
3163 return true;
3165 } else if (call->isCallToFunction("strlen")) {
3166 if (params && params->getCount() == 1) {
3167 visit((*params)[0]);
3168 emitConvertToCell(e);
3169 e.Strlen();
3170 return true;
3172 } else if (call->isCallToFunction("define")) {
3173 if (params && params->getCount() == 2) {
3174 ExpressionPtr p0 = (*params)[0];
3175 Variant v0;
3176 if (p0->getScalarValue(v0) && v0.isString()) {
3177 const StringData* cname =
3178 StringData::GetStaticString(v0.toString());
3179 ExpressionPtr p1 = (*params)[1];
3180 Variant v1;
3181 if (p1->getScalarValue(v1) && v1.isAllowedAsConstantValue()) {
3182 m_ue.addPreConst(cname, *v1.getTypedAccessor());
3183 } else {
3184 m_ue.addPreConst(cname, *null_variant.getTypedAccessor());
3187 visit(p1);
3188 emitConvertToCell(e);
3189 e.DefCns(cname);
3190 return true;
3193 } else if (call->isCompilerCallToFunction("hphp_unpack_continuation")) {
3194 assert(!params || params->getCount() == 0);
3195 int yieldLabelCount = call->getFunctionScope()->getYieldLabelCount();
3196 inputIsAnObject(0);
3197 emitContinuationSwitch(e, yieldLabelCount);
3198 return false;
3199 } else if (call->isCompilerCallToFunction("hphp_create_continuation")) {
3200 assert(params && (params->getCount() == 3 ||
3201 params->getCount() == 4));
3202 ExpressionPtr name = (*params)[1];
3203 Variant nameVar;
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);
3210 return true;
3211 } else if (call->isCompilerCallToFunction("hphp_continuation_done")) {
3212 assert(params && params->getCount() == 1);
3213 visit((*params)[0]);
3214 emitConvertToCell(e);
3215 inputIsAnObject(1);
3216 assert(m_evalStack.size() == 1);
3217 e.ContRetC();
3218 e.Null();
3219 return true;
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)) {
3224 // Push name
3225 emitNameString(e, (*params)[0]);
3226 emitConvertToCell(e);
3228 // Push autoload, defaulting to true
3229 if (params->getCount() == 1) {
3230 e.True();
3231 } else {
3232 visit((*params)[1]);
3233 emitConvertToCell(e);
3235 if (call->isCallToFunction("class_exists")) {
3236 e.ClassExists();
3237 } else if (call->isCallToFunction("interface_exists")) {
3238 e.InterfaceExists();
3239 } else {
3240 assert(call->isCallToFunction("trait_exists"));
3241 e.TraitExists();
3243 return true;
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); \
3250 return true; \
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
3264 // fall through
3266 case Expression::KindOfDynamicFunctionCall: {
3267 emitFuncCall(e, static_pointer_cast<FunctionCall>(node));
3268 return true;
3271 case Expression::KindOfIncludeExpression: {
3272 IncludeExpressionPtr ie(static_pointer_cast<IncludeExpression>(node));
3273 if (ie->isReqLit()) {
3274 StringData* nValue = StringData::GetStaticString(ie->includePath());
3275 e.String(nValue);
3276 } else {
3277 visit(ie->getExpression());
3278 emitConvertToCell(e);
3280 switch (ie->getOp()) {
3281 case T_INCLUDE:
3282 e.Incl();
3283 break;
3284 case T_INCLUDE_ONCE:
3285 e.InclOnce();
3286 break;
3287 case T_REQUIRE:
3288 e.Req();
3289 break;
3290 case T_REQUIRE_ONCE:
3291 if (ie->isDocumentRoot()) {
3292 e.ReqDoc();
3293 } else {
3294 e.ReqOnce();
3296 break;
3298 return true;
3301 case Expression::KindOfListAssignment: {
3302 ListAssignmentPtr la(static_pointer_cast<ListAssignment>(node));
3303 ExpressionPtr rhs = la->getArray();
3305 // visitListAssignmentLHS should have handled this
3306 assert(rhs);
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();
3312 Id tempLocal = -1;
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()) {
3338 continue;
3341 if (nullRHS) {
3342 e.Null();
3343 } else {
3344 if (simpleRHS) {
3345 visit(rhs);
3346 } else {
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]);
3352 markElem(e);
3354 emitCGet(e);
3356 emitSet(e);
3357 emitPop(e);
3359 delete currIndexChain;
3362 // Leave the RHS on the stack
3363 if (simpleRHS) {
3364 visit(rhs);
3365 } else {
3366 emitPushAndFreeUnnamedL(e, tempLocal, start);
3369 return true;
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();
3379 Offset fpiStart;
3380 if (ne->isStatic()) {
3381 // new static()
3382 e.LateBoundCls();
3383 fpiStart = m_ue.bcPos();
3384 e.FPushCtor(numParams);
3385 } else if (ne->getOriginalName().empty()) {
3386 // new $x()
3387 visit(ne->getNameExp());
3388 emitAGet(e);
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()))) {
3394 if (ne->isSelf()) {
3395 // new self() inside a trait or code statically not inside any class
3396 e.Self();
3397 } else {
3398 // new parent() inside a trait, code statically not inside any
3399 // class, or a class with no parent
3400 e.Parent();
3402 fpiStart = m_ue.bcPos();
3403 e.FPushCtor(numParams);
3404 } else {
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);
3418 e.FCall(numParams);
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();
3426 e.PopR();
3427 return true;
3430 case Expression::KindOfObjectMethodExpression: {
3431 ObjectMethodExpressionPtr om(
3432 static_pointer_cast<ObjectMethodExpression>(node));
3433 // $obj->name(...)
3434 // ^^^^
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()) {
3450 // $obj->name(...)
3451 // ^^^^
3452 // Use getOriginalLiteralString(), which hasn't been
3453 // case-normalized, since __call() needs to preserve
3454 // the case.
3455 StringData* nameLiteral = StringData::GetStaticString(methStr);
3456 fpiStart = m_ue.bcPos();
3457 e.FPushObjMethodD(numParams, nameLiteral);
3458 useDirectForm = true;
3461 if (!useDirectForm) {
3462 // $obj->{...}(...)
3463 // ^^^^^
3464 visit(methName);
3465 emitConvertToCell(e);
3466 fpiStart = m_ue.bcPos();
3467 e.FPushObjMethod(numParams);
3469 if (clsName) {
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);
3476 // $obj->name(...)
3477 // ^^^^^
3478 for (int i = 0; i < numParams; i++) {
3479 emitFuncCallArg(e, (*params)[i], i);
3482 e.FCall(numParams);
3483 if (Option::WholeProgram) {
3484 fixReturnType(e, om);
3486 return true;
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,
3497 false, 0, 0);
3499 e.CheckThis();
3500 m_evalStack.push(StackSym::H);
3501 } else {
3502 visit(obj);
3504 StringData* clsName = getClassName(op->getObject());
3505 if (clsName) {
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();
3513 markProp(e);
3514 return true;
3517 case Expression::KindOfQOpExpression: {
3518 QOpExpressionPtr q(static_pointer_cast<QOpExpression>(node));
3519 if (q->getYes()) {
3520 // <expr> ? <expr> : <expr>
3521 Label tru, fals, done;
3523 Emitter condEmitter(q->getCondition(), m_ue, *this);
3524 visitIfCondition(q->getCondition(), condEmitter,
3525 tru, fals, true);
3527 if (tru.isUsed()) {
3528 tru.set(e);
3530 if (currentPositionIsReachable()) {
3531 visit(q->getYes());
3532 emitConvertToCell(e);
3533 e.Jmp(done);
3535 if (fals.isUsed()) fals.set(e);
3536 if (currentPositionIsReachable()) {
3537 visit(q->getNo());
3538 emitConvertToCell(e);
3540 if (done.isUsed()) {
3541 done.set(e);
3542 m_evalStack.cleanTopMeta();
3544 } else {
3545 // <expr> ?: <expr>
3546 Label done;
3547 visit(q->getCondition());
3548 emitConvertToCell(e);
3549 e.Dup();
3550 e.JmpNZ(done);
3551 e.PopC();
3552 visit(q->getNo());
3553 emitConvertToCell(e);
3554 done.set(e);
3555 m_evalStack.cleanTopMeta();
3557 return true;
3560 case Expression::KindOfScalarExpression: {
3561 Variant v;
3562 ex->getScalarValue(v);
3563 switch (v.getType()) {
3564 case KindOfString:
3565 case KindOfStaticString:
3567 ScalarExpressionPtr
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);
3581 e.FCall(0);
3582 e.UnboxR();
3583 } else {
3584 StringData* nValue =
3585 StringData::GetStaticString(v.getStringData());
3586 e.String(nValue);
3588 break;
3590 case KindOfInt64:
3591 e.Int(v.getInt64());
3592 break;
3593 case KindOfDouble:
3594 e.Double(v.getDouble()); break;
3595 default:
3596 assert(false);
3598 return true;
3601 case Expression::KindOfSimpleVariable: {
3602 SimpleVariablePtr sv(static_pointer_cast<SimpleVariable>(node));
3603 if (sv->isThis()) {
3604 if (sv->hasContext(Expression::ObjectContext)) {
3605 if (sv->isGuarded()) {
3606 m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::GuardedThis,
3607 false, 0, 0);
3609 e.This();
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);
3615 } else {
3616 if (sv->isGuarded()) {
3617 m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::GuardedThis,
3618 false, 0, 0);
3619 e.This();
3620 } else {
3621 e.BareThis(!sv->hasContext(Expression::ExistContext));
3624 } else {
3625 StringData* nLiteral = StringData::GetStaticString(sv->getName());
3626 if (sv->isSuperGlobal()) {
3627 e.String(nLiteral);
3628 markGlobalName(e);
3629 return true;
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);
3645 return true;
3648 case Expression::KindOfDynamicVariable: {
3649 DynamicVariablePtr dv(static_pointer_cast<DynamicVariable>(node));
3650 visit(dv->getSubExpression());
3651 emitConvertToCellOrLoc(e);
3652 markName(e);
3653 return true;
3656 case Expression::KindOfStaticMemberExpression: {
3657 StaticMemberExpressionPtr sm(
3658 static_pointer_cast<StaticMemberExpression>(node));
3659 emitVirtualClassBase(e, sm.get());
3660 emitNameString(e, sm->getExp());
3661 markSProp(e);
3662 return true;
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();
3674 // Value.
3675 TypedValue tvVal;
3676 initScalar(tvVal, val);
3678 if (key != nullptr) {
3679 // Key.
3680 assert(key->isScalar());
3681 TypedValue tvKey;
3682 if (key->is(Expression::KindOfConstantExpression)) {
3683 ConstantExpressionPtr c(
3684 static_pointer_cast<ConstantExpression>(key));
3685 if (c->isNull()) {
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;
3693 } else {
3694 // Handle INF and NAN
3695 assert(c->isDouble());
3696 Variant v;
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;
3705 int64_t i;
3706 double d;
3707 if (sval->getString(s)) {
3708 StringData* sd = StringData::GetStaticString(*s);
3709 int64_t n = 0;
3710 if (sd->isStrictlyInteger(n)) {
3711 tvKey.m_data.num = n;
3712 tvKey.m_type = KindOfInt64;
3713 } else {
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;
3723 } else {
3724 not_implemented();
3726 } else {
3727 not_implemented();
3729 a->set(tvAsCVarRef(&tvKey), tvAsVariant(&tvVal), false);
3730 } else {
3731 a->append(tvAsCVarRef(&tvVal), false);
3733 } else {
3734 // Assume new array is on top of stack
3735 bool hasKey = (bool)key;
3736 if (hasKey) {
3737 visit(key);
3738 emitConvertToCellOrLoc(e);
3740 visit(ap->getValue());
3741 if (ap->isRef()) {
3742 emitConvertToVar(e);
3743 if (hasKey) {
3744 emitConvertSecondToCell(e);
3745 e.AddElemV();
3746 } else {
3747 e.AddNewElemV();
3749 } else {
3750 emitConvertToCell(e);
3751 if (hasKey) {
3752 emitConvertSecondToCell(e);
3753 e.AddElemC();
3754 } else {
3755 e.AddNewElemC();
3759 return true;
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 ?
3766 0 : nelem - 1;
3767 int cnt = 0;
3768 for (i = 0; i < nelem; i++) {
3769 ExpressionPtr p((*el)[i]);
3770 if (visit(p)) {
3771 if (pop && i != keep) {
3772 emitPop(e);
3773 } else {
3774 cnt++;
3778 return cnt != 0;
3780 case Expression::KindOfParameterExpression: {
3781 not_implemented();
3783 case Expression::KindOfModifierExpression: {
3784 not_implemented();
3786 case Expression::KindOfUserAttribute: {
3787 not_implemented();
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);
3803 if (useList) {
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.
3832 TypedValue uninit;
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));
3851 return true;
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());
3867 // transfer control
3868 assert(m_evalStack.size() == 0);
3869 e.ContExit();
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);
3878 e.ContReceive();
3880 assert(m_evalStack.size() == 1);
3881 return true;
3886 not_reached();
3889 int EmitterVisitor::scanStackForLocation(int iLast) {
3890 assert(iLast >= 0);
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) {
3896 return i;
3899 InvariantViolation("Emitter expected a location on the stack but none "
3900 "was found (at offset %d)",
3901 m_ue.bcPos());
3902 return 0;
3905 void EmitterVisitor::buildVectorImm(std::vector<uchar>& vectorImm,
3906 int iFirst, int iLast, bool allowW,
3907 Emitter& e) {
3908 assert(iFirst >= 0);
3909 assert(iFirst <= iLast);
3910 assert(iLast < (int)m_evalStack.size());
3911 vectorImm.clear();
3912 vectorImm.reserve(iLast - iFirst + 1);
3914 int metaI = 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);
3933 switch (marker) {
3934 case StackSym::N: {
3935 if (symFlavor == StackSym::C) {
3936 vectorImm.push_back(LNC);
3937 } else if (symFlavor == StackSym::L) {
3938 vectorImm.push_back(LNL);
3939 } else {
3940 assert(false);
3942 } break;
3943 case StackSym::G: {
3944 if (symFlavor == StackSym::C) {
3945 vectorImm.push_back(LGC);
3946 } else if (symFlavor == StackSym::L) {
3947 vectorImm.push_back(LGL);
3948 } else {
3949 assert(false);
3951 } break;
3952 case StackSym::S: {
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);
3961 } break;
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);
3971 } else {
3972 not_reached();
3974 } break;
3975 default: {
3976 not_reached();
3979 if (symFlavor == StackSym::L) {
3980 encodeIvaToVector(vectorImm, m_evalStack.getLoc(iFirst));
3984 int i = iFirst + 1;
3985 while (i <= iLast) {
3986 char sym = m_evalStack.get(i);
3987 char symFlavor = StackSym::GetSymFlavor(sym);
3988 char marker = StackSym::GetMarker(sym);
3989 Id strid = -1;
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
3995 // metadata table.
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);
4010 switch (marker) {
4011 case StackSym::M: {
4012 assert(symFlavor == StackSym::A);
4013 break;
4015 case StackSym::E: {
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);
4022 } else {
4023 vectorImm.push_back(MEC);
4025 } break;
4026 case StackSym::W: {
4027 if (allowW) {
4028 vectorImm.push_back(MW);
4029 } else {
4030 throw IncludeTimeFatalException(e.getNode(),
4031 "Cannot use [] for reading");
4033 } break;
4034 case StackSym::P: {
4035 if (symFlavor == StackSym::L) {
4036 vectorImm.push_back(MPL);
4037 } else if (symFlavor == StackSym::T) {
4038 vectorImm.push_back(MPT);
4039 } else {
4040 vectorImm.push_back(MPC);
4042 } break;
4043 case StackSym::S: {
4044 assert(false);
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));
4058 ++i;
4059 if (marker != StackSym::W) ++metaI;
4063 void EmitterVisitor::emitPop(Emitter& e) {
4064 if (checkIfStackEmpty("Pop*")) return;
4065 LocationGuard loc(e, m_tempLoc);
4066 m_tempLoc.reset();
4068 emitClsIfSPropBase(e);
4069 int iLast = m_evalStack.size()-1;
4070 int i = scanStackForLocation(iLast);
4071 int sz = iLast - i;
4072 assert(sz >= 0);
4073 char sym = m_evalStack.get(i);
4074 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
4075 switch (sym) {
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;
4086 default: {
4087 unexpectedStackSym(sym, "emitPop");
4088 break;
4091 } else {
4092 std::vector<uchar> vectorImm;
4093 buildVectorImm(vectorImm, i, iLast, false, e);
4094 e.CGetM(vectorImm);
4095 e.PopC();
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))
4103 == StackSym::L);
4104 int localIdx = m_evalStack.getLoc(m_evalStack.size() - 2);
4105 e.CGetL2(localIdx);
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))
4112 == StackSym::L);
4113 int localIdx = m_evalStack.getLoc(m_evalStack.size() - 3);
4114 e.CGetL3(localIdx);
4117 void EmitterVisitor::emitAGet(Emitter& e) {
4118 if (checkIfStackEmpty("AGet*")) return;
4120 emitConvertToCellOrLoc(e);
4121 switch (char sym = m_evalStack.top()) {
4122 case StackSym::L:
4123 e.AGetL(m_evalStack.getLoc(m_evalStack.size() - 1));
4124 break;
4125 case StackSym::C:
4126 e.AGetC();
4127 break;
4128 default:
4129 unexpectedStackSym(sym, "emitAGet");
4133 void EmitterVisitor::emitCGet(Emitter& e) {
4134 if (checkIfStackEmpty("CGet*")) return;
4135 LocationGuard loc(e, m_tempLoc);
4136 m_tempLoc.reset();
4138 emitClsIfSPropBase(e);
4139 int iLast = m_evalStack.size()-1;
4140 int i = scanStackForLocation(iLast);
4141 int sz = iLast - i;
4142 assert(sz >= 0);
4143 char sym = m_evalStack.get(i);
4144 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
4145 switch (sym) {
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;
4156 default: {
4157 unexpectedStackSym(sym, "emitCGet");
4158 break;
4161 } else {
4162 std::vector<uchar> vectorImm;
4163 buildVectorImm(vectorImm, i, iLast, false, e);
4164 e.CGetM(vectorImm);
4168 void EmitterVisitor::emitVGet(Emitter& e) {
4169 if (checkIfStackEmpty("VGet*")) return;
4170 LocationGuard loc(e, m_tempLoc);
4171 m_tempLoc.reset();
4173 emitClsIfSPropBase(e);
4174 int iLast = m_evalStack.size()-1;
4175 int i = scanStackForLocation(iLast);
4176 int sz = iLast - i;
4177 assert(sz >= 0);
4178 char sym = m_evalStack.get(i);
4179 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
4180 switch (sym) {
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;
4191 default: {
4192 unexpectedStackSym(sym, "emitVGet");
4193 break;
4196 } else {
4197 std::vector<uchar> vectorImm;
4198 buildVectorImm(vectorImm, i, iLast, true, e);
4199 e.VGetM(vectorImm);
4203 Id EmitterVisitor::emitVisitAndSetUnnamedL(Emitter& e, ExpressionPtr exp) {
4204 visit(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);
4213 emitPop(e);
4214 return tempLocal;
4217 void EmitterVisitor::emitPushAndFreeUnnamedL(Emitter& e, Id tempLocal, Offset start) {
4218 assert(tempLocal >= 0);
4219 assert(start != InvalidAbsoluteOffset);
4220 emitVirtualLocal(tempLocal);
4221 emitCGet(e);
4222 newFaultRegion(start, m_ue.bcPos(), new UnsetUnnamedLocalThunklet(tempLocal));
4223 emitVirtualLocal(tempLocal);
4224 emitUnset(e);
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;
4249 } break;
4250 case Expression::KindOfUnaryOpExpression: {
4251 UnaryOpExpressionPtr u(static_pointer_cast<UnaryOpExpression>(exp));
4252 int op = u->getOp();
4253 if (op == T_CLONE) {
4254 // 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;
4261 } break;
4262 case Expression::KindOfExpressionList: {
4263 ExpressionListPtr el(static_pointer_cast<ExpressionList>(exp));
4264 if (el->getListKind() != ExpressionList::ListKindParam) {
4265 return PassByRefKind::WarnOnCell;
4267 } break;
4268 default:
4269 break;
4271 // All other cases
4272 return PassByRefKind::ErrorOnCell;
4275 void EmitterVisitor::emitBuiltinCallArg(Emitter& e,
4276 ExpressionPtr exp,
4277 int paramId,
4278 bool byRef) {
4279 visit(exp);
4280 if (checkIfStackEmpty("BPass*")) return;
4281 if (byRef) {
4282 emitVGet(e);
4283 m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::NopOut, false, 0, 0);
4284 e.BPassV(paramId);
4285 } else {
4286 emitCGet(e);
4287 m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::NopOut, false, 0, 0);
4288 e.BPassC(paramId);
4290 return;
4293 void EmitterVisitor::emitBuiltinDefaultArg(Emitter& e, Variant& v,
4294 DataType t, int paramId) {
4295 switch (v.getType()) {
4296 case KindOfString:
4297 case KindOfStaticString: {
4298 StringData *nValue = StringData::GetStaticString(v.getStringData());
4299 e.String(nValue);
4300 break;
4302 case KindOfInt64:
4303 e.Int(v.getInt64());
4304 break;
4305 case KindOfBoolean:
4306 if (v.getBoolean()) {
4307 e.True();
4308 } else {
4309 e.False();
4311 break;
4312 case KindOfNull:
4313 switch (t) {
4314 case KindOfString:
4315 case KindOfStaticString:
4316 case KindOfObject:
4317 case KindOfArray:
4318 e.Int(0);
4319 break;
4320 case KindOfUnknown:
4321 e.NullUninit();
4322 break;
4323 default:
4324 not_reached();
4326 break;
4327 case KindOfArray:
4328 e.Array(v.getArrayData());
4329 break;
4330 default:
4331 not_reached();
4333 m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::NopOut, false, 0, 0);
4334 e.BPassC(paramId);
4337 void EmitterVisitor::emitFuncCallArg(Emitter& e,
4338 ExpressionPtr exp,
4339 int paramId) {
4340 visit(exp);
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) {
4348 emitVGet(e);
4349 m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::NopOut, false, 0, 0);
4350 e.FPassV(paramId);
4351 return;
4353 } else {
4354 emitCGet(e);
4355 m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::NopOut, false, 0, 0);
4356 e.FPassC(paramId);
4357 return;
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);
4367 m_tempLoc.reset();
4369 emitClsIfSPropBase(e);
4370 int iLast = m_evalStack.size()-1;
4371 int i = scanStackForLocation(iLast);
4372 int sz = iLast - i;
4373 assert(sz >= 0);
4374 char sym = m_evalStack.get(i);
4375 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
4376 switch (sym) {
4377 case StackSym::L: e.FPassL(paramId, m_evalStack.getLoc(i)); break;
4378 case StackSym::C:
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);
4385 break;
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;
4394 default: {
4395 unexpectedStackSym(sym, "emitFPass");
4396 break;
4399 } else {
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);
4412 int sz = iLast - i;
4413 assert(sz >= 0);
4414 char sym = m_evalStack.get(i);
4415 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
4416 switch (sym) {
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;
4430 default: {
4431 unexpectedStackSym(sym, "emitIsset");
4432 break;
4435 } else {
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()) { \
4448 case StackSym::L: \
4449 e.Is ## What ## L( \
4450 m_evalStack.getLoc(m_evalStack.size() - 1)); \
4451 break; \
4452 case StackSym::C: \
4453 e.Is ## What ## C(); \
4454 break; \
4455 default: \
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);
4475 int sz = iLast - i;
4476 assert(sz >= 0);
4477 char sym = m_evalStack.get(i);
4478 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
4479 switch (sym) {
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;
4493 default: {
4494 unexpectedStackSym(sym, "emitEmpty");
4495 break;
4498 } else {
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);
4512 int sz = iLast - i;
4513 assert(sz >= 0);
4514 char sym = m_evalStack.get(i);
4515 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
4516 switch (sym) {
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
4523 case StackSym::CS:
4524 assert(exp);
4525 e.String(
4526 StringData::GetStaticString("Attempt to unset static property " +
4527 exp->getText())
4529 e.Fatal(0);
4530 break;
4532 default: {
4533 unexpectedStackSym(sym, "emitUnset");
4534 break;
4537 } else {
4538 std::vector<uchar> vectorImm;
4539 buildVectorImm(vectorImm, i, iLast, false, e);
4540 e.UnsetM(vectorImm);
4544 void EmitterVisitor::emitVisitAndUnset(Emitter& e, ExpressionPtr exp) {
4545 visit(exp);
4546 emitUnset(e, exp);
4549 void EmitterVisitor::emitSet(Emitter& e) {
4550 if (checkIfStackEmpty("Set*")) return;
4552 int iLast = m_evalStack.size()-2;
4553 int i = scanStackForLocation(iLast);
4554 int sz = iLast - i;
4555 assert(sz >= 0);
4556 char sym = m_evalStack.get(i);
4557 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
4558 switch (sym) {
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;
4566 default: {
4567 unexpectedStackSym(sym, "emitSet");
4568 break;
4571 } else {
4572 std::vector<uchar> vectorImm;
4573 buildVectorImm(vectorImm, i, iLast, true, e);
4574 e.SetM(vectorImm);
4578 void EmitterVisitor::emitSetOp(Emitter& e, int op) {
4579 if (checkIfStackEmpty("SetOp*")) return;
4581 unsigned char cop = SetOp_invalid;
4582 switch (op) {
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);
4598 int sz = iLast - i;
4599 assert(sz >= 0);
4600 char sym = m_evalStack.get(i);
4601 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
4602 switch (sym) {
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;
4610 default: {
4611 unexpectedStackSym(sym, "emitSetOp");
4612 break;
4615 } else {
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);
4627 int sz = iLast - i;
4628 assert(sz >= 0);
4629 char sym = m_evalStack.get(i);
4630 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
4631 switch (sym) {
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;
4639 default: {
4640 unexpectedStackSym(sym, "emitBind");
4641 break;
4644 } else {
4645 std::vector<uchar> vectorImm;
4646 buildVectorImm(vectorImm, i, iLast, true, e);
4647 e.BindM(vectorImm);
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);
4657 int sz = iLast - i;
4658 assert(sz >= 0);
4659 char sym = m_evalStack.get(i);
4660 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
4661 switch (sym) {
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;
4669 default: {
4670 unexpectedStackSym(sym, "emitIncDec");
4671 break;
4674 } else {
4675 std::vector<uchar> vectorImm;
4676 buildVectorImm(vectorImm, i, iLast, true, e);
4677 e.IncDecM(cop, vectorImm);
4681 void EmitterVisitor::emitConvertToCell(Emitter& e) {
4682 emitCGet(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);
4690 } else {
4691 assert(pendingIter.second == KindOfIter);
4692 e.IterFree(pendingIter.first);
4697 void EmitterVisitor::emitConvertSecondToCell(Emitter& e) {
4698 if (m_evalStack.size() <= 1) {
4699 InvariantViolation(
4700 "Emitter encounted an empty evaluation stack when inside "
4701 "the emitConvertSecondToCell() function (at offset %d)",
4702 m_ue.bcPos());
4703 return;
4705 char sym = m_evalStack.get(m_evalStack.size() - 2);
4706 char symFlavor = StackSym::GetSymFlavor(sym);
4707 if (symFlavor == StackSym::C) {
4708 // do nothing
4709 } else if (symFlavor == StackSym::L) {
4710 emitCGetL2(e);
4711 } else {
4712 // emitConvertSecondToCell() should never be used for symbolic flavors
4713 // other than C or L
4714 InvariantViolation(
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(),
4719 m_ue.bcPos());
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()) {
4734 InvariantViolation(
4735 "Emitter encounted an empty evaluation stack when inside "
4736 "the emitConvertToCellOrLoc() function (at offset %d)",
4737 m_ue.bcPos());
4738 return;
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
4743 } else {
4744 // Otherwise, call emitCGet to convert the top of stack to cell
4745 emitCGet(e);
4749 void EmitterVisitor::emitConvertToVar(Emitter& e) {
4750 emitVGet(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
4756 * evaluation order.
4758 * For example, in:
4760 * $cls = 'cls';
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));
4779 emitAGet(e);
4780 break;
4781 case SymbolicStack::CLS_LATE_BOUND:
4782 e.LateBoundCls();
4783 break;
4784 case SymbolicStack::CLS_SELF:
4785 e.Self();
4786 break;
4787 case SymbolicStack::CLS_PARENT:
4788 e.Parent();
4789 break;
4790 case SymbolicStack::CLS_NAMED_LOCAL: {
4791 int loc = m_evalStack.getLoc(pos);
4792 emitVirtualLocal(loc);
4793 emitAGet(e);
4794 break;
4796 case SymbolicStack::CLS_UNNAMED_LOCAL: {
4797 int loc = m_evalStack.getLoc(pos);
4798 emitVirtualLocal(loc);
4799 emitAGet(e);
4800 emitVirtualLocal(loc);
4801 emitUnset(e);
4802 newFaultRegion(m_evalStack.getUnnamedLocStart(pos),
4803 m_ue.bcPos(),
4804 new UnsetUnnamedLocalThunklet(loc));
4805 m_curFunc->freeUnnamedLocal(loc);
4806 break;
4808 case SymbolicStack::CLS_INVALID:
4809 default:
4810 assert(false);
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;
4822 for (;;) {
4823 char marker = StackSym::GetMarker(m_evalStack.get(pos));
4824 if (marker != StackSym::E && marker != StackSym::W &&
4825 marker != StackSym::P) {
4826 break;
4828 --pos;
4829 if (pos < 0) {
4830 InvariantViolation("Emitter expected a location on the stack but none "
4831 "was found (at offset %d)",
4832 m_ue.bcPos());
4833 return;
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) {
4839 return;
4842 --pos;
4843 if (pos < 0) {
4844 InvariantViolation(
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)",
4848 m_ue.bcPos());
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.
4861 if (ncase == 0) {
4862 // fall-through to the label 0
4863 e.UnpackCont();
4864 e.PopC();
4865 return;
4868 // make sure the labels are available
4869 m_yieldLabels.resize(ncase + 1);
4871 if (ncase == 1) {
4872 // Don't bother with the jump table when there are only two targets
4873 e.UnpackCont();
4874 e.JmpNZ(m_yieldLabels[1]);
4875 return;
4878 std::vector<Label*> targets(ncase + 1);
4879 for (int i = 0; i <= ncase; ++i) {
4880 targets[i] = &m_yieldLabels[i];
4882 e.UnpackCont();
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();
4898 if (condition) {
4899 Variant cval;
4900 DataType caseType;
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;
4908 t = caseType;
4909 } else {
4910 return KindOfInvalid;
4912 int64_t n;
4913 bool isNonZero;
4914 if (t == KindOfInt64) {
4915 n = cval.asInt64Val();
4916 isNonZero = n;
4917 } else {
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
4924 // ever match
4925 caseMap[n] = i;
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
4937 state.nonZeroI = i;
4939 } else {
4940 // Last 'default:' wins
4941 state.defI = i;
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;
4958 return t;
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
4969 // appropriate.
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) {
4973 int caseIdx;
4974 if (mapGet(caseMap, base + i, &caseIdx)) {
4975 labels[i] = &caseLabels[caseIdx];
4976 } else {
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);
5005 e.SSwitch(labels);
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)",
5012 m_ue.bcPos());
5013 return;
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));
5019 } else {
5020 InvariantViolation(
5021 "Emitter encountered an unsupported StackSym \"%s\" on "
5022 "the evaluation stack inside the markElem function (at "
5023 "offset %d)",
5024 StackSym::ToString(sym).c_str(),
5025 m_ue.bcPos());
5029 void EmitterVisitor::markNewElem(Emitter& e) {
5030 m_evalStack.push(StackSym::W);
5033 void EmitterVisitor::markProp(Emitter& e) {
5034 if (m_evalStack.empty()) {
5035 InvariantViolation(
5036 "Emitter encountered an empty evaluation stack inside "
5037 "the markProp function (at offset %d)",
5038 m_ue.bcPos());
5039 return;
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));
5044 } else {
5045 InvariantViolation(
5046 "Emitter encountered an unsupported StackSym \"%s\" on "
5047 "the evaluation stack inside the markProp function (at "
5048 "offset %d)",
5049 StackSym::ToString(sym).c_str(),
5050 m_ue.bcPos());
5054 void EmitterVisitor::markSProp(Emitter& e) {
5055 if (m_evalStack.empty()) {
5056 InvariantViolation(
5057 "Emitter encountered an empty evaluation stack inside "
5058 "the markSProp function (at offset %d)",
5059 m_ue.bcPos());
5060 return;
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));
5065 } else {
5066 InvariantViolation(
5067 "Emitter encountered an unsupported StackSym \"%s\" on "
5068 "the evaluation stack inside the markSProp function "
5069 "(at offset %d)",
5070 StackSym::ToString(sym).c_str(),
5071 m_ue.bcPos());
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()); \
5081 return; \
5083 char sym = m_evalStack.get(index); \
5084 if (sym == StackSym::C || sym == StackSym::L) { \
5085 m_evalStack.set(index, (sym | StackSym::N)); \
5086 } else { \
5087 InvariantViolation( \
5088 "Emitter encountered an unsupported StackSym \"%s\" " \
5089 "on the evaluation stack inside the %s function (at " \
5090 "offset %d)", \
5091 StackSym::ToString(sym).c_str(), __FUNCTION__, \
5092 m_ue.bcPos()); \
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()) {
5109 InvariantViolation(
5110 "Emitter encountered an empty evaluation stack inside "
5111 "the markGlobalName function (at offset %d)",
5112 m_ue.bcPos());
5113 return;
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));
5118 } else {
5119 InvariantViolation(
5120 "Emitter encountered an unsupported StackSym \"%s\" on "
5121 "the evaluation stack inside the markGlobalName function "
5122 "(at offset %d)",
5123 StackSym::ToString(sym).c_str(),
5124 m_ue.bcPos());
5128 void EmitterVisitor::emitNameString(Emitter& e, ExpressionPtr n,
5129 bool allowLiteral) {
5130 Variant v;
5131 if (n->getScalarValue(v) && v.isString()) {
5132 StringData* nLiteral = StringData::GetStaticString(v.toCStrRef().get());
5133 if (allowLiteral) {
5134 m_evalStack.push(StackSym::T);
5135 } else {
5136 e.String(nLiteral);
5138 m_evalStack.setString(nLiteral);
5139 } else {
5140 visit(n);
5141 emitConvertToCellOrLoc(e);
5145 void EmitterVisitor::postponeMeth(MethodStatementPtr m, FuncEmitter* fe,
5146 bool top,
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,
5156 NonScalarVec* v) {
5157 m_postponedPinits.push_back(PostponedNonScalars(is, fe, v));
5160 void EmitterVisitor::postponeSinit(InterfaceStatementPtr is, FuncEmitter* fe,
5161 NonScalarVec* v) {
5162 m_postponedSinits.push_back(PostponedNonScalars(is, fe, v));
5165 void EmitterVisitor::postponeCinit(InterfaceStatementPtr is, FuncEmitter* fe,
5166 NonScalarVec* v) {
5167 m_postponedCinits.push_back(PostponedNonScalars(is, fe, v));
5170 static Attr buildAttrs(ModifierExpressionPtr mod, bool isRef = false) {
5171 int attrs = AttrNone;
5172 if (isRef) {
5173 attrs |= AttrReference;
5175 if (mod) {
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()) {
5186 attrs |= AttrFinal;
5189 return Attr(attrs);
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;
5200 if (!fe) {
5201 assert(p.m_top);
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());
5208 p.m_fe = fe;
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;
5217 assert(uaValue);
5218 assert(uaValue->isScalar());
5219 TypedValue tv;
5220 initScalar(tv, uaValue);
5221 fe->addUserAttribute(uaName, tv);
5223 Emitter e(p.m_meth, m_ue, *this);
5224 if (p.m_top) {
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());
5234 } else {
5235 // Set label
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();
5256 TypeConstraint tc =
5257 TypeConstraint(
5258 StringData::GetStaticString(par->getOriginalTypeHint()),
5259 nullable,
5260 par->hhType());
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()) {
5272 TypedValue dv;
5273 initScalar(dv, vNode);
5274 pi.setDefaultValue(dv);
5276 std::string orig = vNode->getComment();
5277 if (orig.empty()) {
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());
5282 } else {
5283 // This was optimized from a Constant, or ClassConstant
5284 // use the original string
5285 phpCode = StringData::GetStaticString(orig);
5287 } else {
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();
5305 assert(uaValue);
5306 assert(uaValue->isScalar());
5307 TypedValue tv;
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()));
5320 m_curFunc = fe;
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;
5365 if (p.m_top &&
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
5381 one.
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;
5401 Label topOfBody(e);
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()) {
5409 assert(!p.m_top);
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()) {
5424 StringData* msg =
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, "()");
5430 e.String(msg);
5431 e.Fatal(1);
5434 Label ctFatal, ctFatalWithPop;
5436 CONTROL_BODY(ctFatal, ctFatal, ctFatalWithPop, ctFatalWithPop);
5437 // emit body
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
5442 Label exit;
5443 if (ctFatal.isUsed() || ctFatalWithPop.isUsed()) {
5444 e.Jmp(exit);
5445 if (ctFatalWithPop.isUsed()) {
5446 ctFatalWithPop.set(e);
5447 e.PopC();
5449 if (ctFatal.isUsed()) {
5450 ctFatal.set(e);
5452 static const StringData* msg =
5453 StringData::GetStaticString("Cannot break/continue");
5454 e.String(msg);
5455 e.Fatal(0);
5457 if (exit.isUsed()) {
5458 exit.set(e);
5460 // If the current position in the bytecode is reachable, emit code to
5461 // return null
5462 if (currentPositionIsReachable()) {
5463 e.Null();
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);
5468 e.ContRetC();
5469 } else {
5470 if ((p.m_meth->getStmts() && p.m_meth->getStmts()->isGuarded())) {
5471 m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::GuardedThis,
5472 false, 0, 0);
5474 e.RetC();
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);
5486 visit(node);
5487 emitCGet(e);
5488 emitSet(e);
5489 e.PopC();
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);
5494 e.Jmp(topOfBody);
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);
5515 e.Null();
5516 e.RetC();
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;
5529 pi.setRef(true);
5530 static const StringData* s_props = StringData::GetStaticString("props");
5531 p.m_fe->appendParam(s_props, pi);
5533 if (pinit) {
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>;
5549 // # ...
5551 // if (props["q0"]) === $sentinel) {
5552 // props["q0"] = <non-scalar initialization>;
5553 // }
5554 // if (props["q1"] === $sentinel) {
5555 // props["q1"] = <non-scalar initialization>;
5556 // }
5557 // # ...
5558 // }
5560 // private static function 86sinit(&$props) {
5561 // props["p0"] = <non-scalar initialization>;
5562 // props["p1"] = <non-scalar initialization>;
5563 // # ...
5564 // }
5565 size_t nProps = p.m_vec->size();
5566 assert(nProps > 0);
5567 for (size_t i = 0; i < nProps; ++i) {
5568 const StringData* propName =
5569 StringData::GetStaticString(((*p.m_vec)[i]).first);
5570 Label isset;
5572 bool conditional;
5573 if (pinit) {
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();
5579 } else {
5580 conditional = true;
5582 } else {
5583 conditional = false;
5586 if (conditional) {
5587 emitVirtualLocal(0);
5588 e.String((StringData*)propName);
5589 markElem(e);
5590 emitCGet(e);
5591 emitVirtualLocal(1);
5592 emitCGet(e);
5593 e.Same();
5594 e.JmpZ(isset);
5597 emitVirtualLocal(0);
5598 e.String((StringData*)propName);
5599 markElem(e);
5600 visit((*p.m_vec)[i].second);
5601 emitSet(e);
5602 e.PopC();
5604 isset.set(e);
5606 e.Null();
5607 e.RetC();
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>;
5653 // }
5654 // }
5655 size_t nConsts = p.m_vec->size();
5656 assert(nConsts > 0);
5657 Label retC;
5658 for (size_t i = 0; i < nConsts - 1; ++i) {
5659 Label mismatch;
5661 emitVirtualLocal(0);
5662 emitCGet(e);
5663 e.String((StringData*)StringData::GetStaticString(((*p.m_vec)[i]).first));
5664 e.Eq();
5665 e.JmpZ(mismatch);
5667 visit((*p.m_vec)[i].second);
5669 e.Jmp(retC);
5670 mismatch.set(e);
5672 visit((*p.m_vec)[nConsts-1].second);
5673 retC.set(e);
5674 e.RetC();
5676 p.release(); // Manually trigger memory cleanup.
5677 m_postponedCinits.pop_front();
5681 void EmitterVisitor::emitVirtualLocal(int localId,
5682 DataType dt /* = KindOfUnknown */) {
5683 prepareEvalStack();
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) {
5695 prepareEvalStack();
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() &&
5706 !sv->isThis()) {
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);
5711 } else {
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);
5724 emitSet(e);
5725 m_evalStack.setUnnamedLocal(clsBaseIdx, unnamedLoc, m_ue.bcPos());
5726 emitPop(e);
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);
5736 } else {
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);
5746 } else {
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) {
5754 static struct {
5755 const char* name;
5756 int minParams, maxParams;
5757 CallUserFuncFlags flags;
5758 } cufTab[] = {
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;
5778 break;
5781 if (flags == CallUserFuncNone) return false;
5783 int param = 1;
5784 ExpressionPtr callable = (*params)[0];
5785 visit(callable);
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);
5795 } else {
5796 e.Null();
5798 fpiStart = m_ue.bcPos();
5799 e.FPushCufSafe(nParams - param);
5800 } else {
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) {
5815 e.FCallArray();
5816 } else {
5817 e.FCall(nParams - param);
5819 if (flags & CallUserFuncSafe) {
5820 if (flags & CallUserFuncReturn) {
5821 e.CufSafeReturn();
5822 } else {
5823 e.CufSafeArray();
5826 return true;
5829 bool EmitterVisitor::canEmitBuiltinCall(FunctionCallPtr fn,
5830 const std::string& name,
5831 int numParams) {
5832 if (Option::JitEnableRenameFunction) {
5833 return false;
5835 if (Option::DynamicInvokeFunctions.size()) {
5836 if (Option::DynamicInvokeFunctions.find(name) !=
5837 Option::DynamicInvokeFunctions.end()) {
5838 return false;
5841 FunctionScopePtr func = fn->getFuncScope();
5842 if (!func || func->getMaxParamCount() > kMaxBuiltinArgs) {
5843 return false;
5845 if (!func->isUserFunction() && !func->needsActRec()
5846 && numParams >= func->getMinParamCount()
5847 && numParams <= func->getMaxParamCount()) {
5848 if (func->isVariableArgument() || func->isReferenceVariableArgument()
5849 || func->isMixedVariableArgument()) {
5850 return false;
5852 TypePtr t = func->getReturnType();
5853 if (!t || t->getHhvmDataType() == KindOfDouble) {
5854 return false;
5856 for (int i = 0; i < func->getMaxParamCount(); i++) {
5857 t = func->getParamType(i);
5858 if (!t || t->getHhvmDataType() == KindOfDouble) {
5859 return false;
5861 // unserializable default values such as TimeStamp::Current()
5862 // are serialized as kUnserializableString ("\x01")
5863 if (i >= numParams && func->getParamDefault(i) == kUnserializableString) {
5864 return false;
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")) {
5871 return false;
5873 // in sandbox mode, don't emit FCallBuiltin for redefinable functions
5874 if (func->allowOverride() && !Option::WholeProgram) {
5875 return false;
5877 return true;
5879 return false;
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;
5889 Offset fpiStart;
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()) {
5894 // cls::foo()
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);
5903 } else {
5904 emitVirtualClassBase(e, node.get());
5905 if (!nameStr.empty()) {
5906 // ...::foo()
5907 StringData* nLiteral = StringData::GetStaticString(nameStr);
5908 e.String(nLiteral);
5909 } else {
5910 // ...::$foo()
5911 visit(nameExp);
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);
5920 } else {
5921 e.FPushClsMethod(numParams);
5924 } else if (!nameStr.empty()) {
5925 // foo()
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) {
5933 nsName = nLiteral;
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);
5945 } else {
5946 e.FPushFuncU(numParams, nsName, nLiteral);
5948 } else {
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);
5972 emitCGet(e);
5973 fpiStart = m_ue.bcPos();
5974 e.FPushObjMethodD(numParams, specialMethodName);
5975 } else {
5976 fpiStart = m_ue.bcPos();
5977 e.FPushFuncD(numParams, nLiteral);
5980 } else {
5981 // $foo()
5982 visit(nameExp);
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();
5990 assert(func);
5991 assert(numParams <= func->getMaxParamCount()
5992 && numParams >= func->getMinParamCount());
5993 int i = 0;
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);
6006 } else {
6008 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
6009 for (int i = 0; i < numParams; i++) {
6010 emitFuncCallArg(e, (*params)[i], i);
6013 e.FCall(numParams);
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);
6061 if (precStmt) {
6062 emitClassTraitPrecRule(pce, precStmt);
6063 } else {
6064 TraitAliasStatementPtr aliasStmt =
6065 dynamic_pointer_cast<TraitAliasStatement>(rule);
6066 assert(aliasStmt);
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 :
6082 KindOfObject;
6084 // We have to merge the strings as litstrs to ensure namedentity
6085 // creation.
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);
6091 Typedef record;
6092 record.m_name = name;
6093 record.m_value = value;
6094 record.m_kind = kind;
6095 Id id = m_ue.addTypedef(record);
6096 e.DefTypedef(id);
6099 PreClass::Hoistable EmitterVisitor::emitClass(Emitter& e, ClassScopePtr cNode,
6100 bool toplevel) {
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 :
6111 AttrNone;
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;
6136 if (toplevel) {
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,
6153 classDoc);
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());
6160 } else {
6161 // To atatch the line number to for error reporting...
6162 e.Nop();
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;
6178 assert(uaValue);
6179 assert(uaValue->isScalar());
6180 TypedValue tv;
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);
6201 assert(added);
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();
6220 } else {
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()
6229 : KindOfInvalid;
6231 StringData* propName = StringData::GetStaticString(var->getName());
6232 StringData* propDoc = empty_string.get();
6233 TypedValue tvVal;
6234 if (vNode) {
6235 if (vNode->isScalar()) {
6236 initScalar(tvVal, vNode);
6237 } else {
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));
6247 } else {
6248 if (nonScalarSinitVec == nullptr) {
6249 nonScalarSinitVec = new NonScalarVec();
6251 nonScalarSinitVec->push_back(NonScalarPair(propName, vNode));
6254 } else {
6255 tvWriteNull(&tvVal);
6257 bool added UNUSED =
6258 pce->addProperty(propName, attrs, typeConstraint, propDoc, &tvVal,
6259 hphpcType);
6260 assert(added);
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());
6275 assert(vNode);
6276 TypedValue tvVal;
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);
6282 } else {
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()));
6297 assert(added);
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);
6313 assert(added);
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);
6322 pce->addMethod(fe);
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);
6331 pce->addMethod(fe);
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);
6342 assert(added);
6343 postponeCinit(is, fe, nonScalarConstVec);
6346 return hoistable;
6349 void
6350 EmitterVisitor::emitBreakHandler(Emitter& e,
6351 Label& brkTarg,
6352 Label& cntTarg,
6353 Label& brkHand,
6354 Label& cntHand,
6355 Id iter /* = -1 */,
6356 IterKind itKind /* = KindOfIter */) {
6357 // Handle dynamic break
6358 if (brkHand.isUsed()) {
6359 brkHand.set(e);
6360 // Whatever happens, we have left this loop
6361 if (iter != -1) {
6362 if (itKind == KindOfMIter) {
6363 e.MIterFree(iter);
6364 } else {
6365 assert(itKind == KindOfIter);
6366 e.IterFree(iter);
6369 e.Int(1);
6370 e.Sub();
6371 e.Dup();
6372 e.Int(1);
6373 e.Lt();
6374 e.JmpZ(topBreakHandler());
6375 e.PopC(); // Pop break num
6376 e.Jmp(brkTarg);
6379 // Handle dynamic continue
6380 if (cntHand.isUsed()) {
6381 cntHand.set(e);
6382 e.Int(1);
6383 e.Sub();
6384 e.Dup();
6385 e.Int(1);
6386 e.Lt();
6387 if (iter != -1) {
6388 // Freeing the iterator means another jump
6389 Label leaving;
6390 e.JmpZ(leaving);
6391 e.PopC();
6392 e.Jmp(cntTarg);
6393 leaving.set(e);
6394 // Leaving this loop
6395 if (itKind == KindOfMIter) {
6396 e.MIterFree(iter);
6397 } else {
6398 assert(itKind == KindOfIter);
6399 e.IterFree(iter);
6401 e.Jmp(topContHandler());
6402 } else {
6403 e.JmpZ(topContHandler());
6404 e.PopC();
6405 e.Jmp(cntTarg);
6410 class ForeachIterGuard {
6411 EmitterVisitor& m_ev;
6412 public:
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());
6426 int keyTempLocal;
6427 int valTempLocal;
6428 bool strong = fe->isStrong();
6429 Label exit;
6430 Label next;
6431 Label brkHand;
6432 Label cntHand;
6433 Label start;
6434 Offset bIterStart;
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);
6440 if (simpleCase) {
6441 SimpleVariablePtr svVal(static_pointer_cast<SimpleVariable>(val));
6442 StringData* name = StringData::GetStaticString(svVal->getName());
6443 valTempLocal = m_curFunc->lookupVarId(name);
6444 if (key) {
6445 SimpleVariablePtr svKey(static_pointer_cast<SimpleVariable>(key));
6446 name = StringData::GetStaticString(svKey->getName());
6447 keyTempLocal = m_curFunc->lookupVarId(name);
6448 visit(key);
6449 // Meta info on the key local will confuse the translator (and
6450 // wouldn't be useful anyway)
6451 m_evalStack.cleanTopMeta();
6452 } else {
6453 // Make gcc happy
6454 keyTempLocal = -1;
6456 visit(val);
6457 // Meta info on the value local will confuse the translator (and
6458 // wouldn't be useful anyway)
6459 m_evalStack.cleanTopMeta();
6460 visit(ae);
6461 if (strong) {
6462 emitConvertToVar(e);
6463 if (key) {
6464 e.MIterInitK(itId, exit, valTempLocal, keyTempLocal);
6465 } else {
6466 e.MIterInit(itId, exit, valTempLocal);
6468 } else {
6469 emitConvertToCell(e);
6470 if (key) {
6471 e.IterInitK(itId, exit, valTempLocal, keyTempLocal);
6472 } else {
6473 e.IterInit(itId, exit, valTempLocal);
6477 start.set(e);
6478 bIterStart = m_ue.bcPos();
6479 } else {
6480 keyTempLocal = key ? m_curFunc->allocUnnamedLocal() : -1;
6481 valTempLocal = m_curFunc->allocUnnamedLocal();
6482 if (key) {
6483 emitVirtualLocal(keyTempLocal);
6485 emitVirtualLocal(valTempLocal);
6487 visit(ae);
6488 if (strong) {
6489 emitConvertToVar(e);
6490 } else {
6491 emitConvertToCell(e);
6494 if (strong) {
6495 if (key) {
6496 e.MIterInitK(itId, exit, valTempLocal, keyTempLocal);
6497 } else {
6498 e.MIterInit(itId, exit, valTempLocal);
6500 } else {
6501 if (key) {
6502 e.IterInitK(itId, exit, valTempLocal, keyTempLocal);
6503 } else {
6504 e.IterInit(itId, exit, valTempLocal);
6508 // At this point, valTempLocal and keyTempLocal if applicable, contain the
6509 // key and value for the iterator.
6510 start.set(e);
6511 bIterStart = m_ue.bcPos();
6512 if (key) {
6513 visit(key);
6515 visit(val);
6516 emitVirtualLocal(valTempLocal);
6517 if (strong) {
6518 emitVGet(e);
6519 emitBind(e);
6520 } else {
6521 emitCGet(e);
6522 emitSet(e);
6524 emitPop(e);
6525 emitVirtualLocal(valTempLocal);
6526 emitUnset(e);
6527 newFaultRegion(bIterStart, m_ue.bcPos(),
6528 new UnsetUnnamedLocalThunklet(valTempLocal));
6529 if (key) {
6530 assert(keyTempLocal != -1);
6531 emitVirtualLocal(keyTempLocal);
6532 emitCGet(e);
6533 emitSet(e);
6534 emitPop(e);
6535 emitVirtualLocal(keyTempLocal);
6536 emitUnset(e);
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) {
6548 next.set(e);
6550 if (key) {
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();
6560 if (strong) {
6561 if (key) {
6562 e.MIterNextK(itId, start, valTempLocal, keyTempLocal);
6563 } else {
6564 e.MIterNext(itId, start, valTempLocal);
6566 } else {
6567 if (key) {
6568 e.IterNextK(itId, start, valTempLocal, keyTempLocal);
6569 } else {
6570 e.IterNext(itId, start, valTempLocal);
6573 newFaultRegion(bIterStart, m_ue.bcPos(), new IterFreeThunklet(itId, strong),
6574 itId);
6575 if (needBreakHandler) {
6576 e.Jmp(exit);
6577 IterKind itKind = strong ? KindOfMIter : KindOfIter;
6578 emitBreakHandler(e, exit, next, brkHand, cntHand, itId, itKind);
6580 if (!simpleCase) {
6581 m_curFunc->freeUnnamedLocal(valTempLocal);
6582 if (key) {
6583 m_curFunc->freeUnnamedLocal(keyTempLocal);
6586 exit.set(e);
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)
6598 * if oldvalue != 0:
6599 * error_reporting(oldvalue)
6601 void EmitterVisitor::emitRestoreErrorReporting(Emitter& e, Id oldLevelLoc) {
6602 static const StringData* funcName =
6603 StringData::GetStaticString("error_reporting");
6604 Label dontRollback;
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);
6613 emitCGet(e);
6614 m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::NopOut, false, 0, 0);
6615 e.FPassC(0);
6617 e.FCall(1);
6618 e.UnboxR();
6619 // stack is now: ...[Loc oldLevelLoc][return value]
6620 // save the return value in local, and leave it on the stack
6621 emitSet(e);
6622 e.Int(0);
6623 e.Eq();
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);
6630 emitCGet(e);
6631 m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::NopOut, false, 0, 0);
6632 e.FPassC(0);
6634 e.FCall(1);
6635 e.PopR();
6636 dontRollback.set(e);
6639 void EmitterVisitor::emitMakeUnitFatal(Emitter& e, const std::string& msg) {
6640 StringData* sd = StringData::GetStaticString(msg);
6641 e.String(sd);
6642 e.Fatal(0);
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();
6651 f.m_entry->set(e);
6652 f.m_body->emit(e);
6653 delete f.m_body;
6654 m_funclets.pop_front();
6656 m_funclets.clear();
6659 void EmitterVisitor::newFaultRegion(Offset start, Offset end, Thunklet* t,
6660 Id iterId) {
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;
6678 e.m_iterId = -1;
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));
6686 delete *it;
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();
6697 delete *it;
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;
6709 delete *it;
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 +
6720 fe->numLocals() +
6721 m_fdescHighWater);
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) {
6729 emitFunclets(e);
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();
6746 str << '$';
6747 if (m_curFunc->isPseudoMain()) {
6748 str << "__pseudoMain";
6749 } else {
6750 str << m_curFunc->name()->data();
6753 * Uniquify the name
6755 str << '$'
6756 << std::hex
6757 << m_curFunc->ue().md5().q[1] << m_curFunc->ue().md5().q[0]
6758 << std::dec
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));
6770 if (ce->isNull()) {
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));
6778 } else {
6779 not_implemented();
6781 break;
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;
6790 break;
6792 int64_t i;
6793 if (sval->getInt(i)) {
6794 tvVal.m_data.num = i;
6795 tvVal.m_type = KindOfInt64;
6796 break;
6798 double d;
6799 if (sval->getDouble(d)) {
6800 tvVal.m_data.dbl = d;
6801 tvVal.m_type = KindOfDouble;
6802 break;
6804 assert(false);
6805 break;
6807 case Expression::KindOfUnaryOpExpression: {
6808 UnaryOpExpressionPtr u(static_pointer_cast<UnaryOpExpression>(val));
6809 if (u->getOp() == T_ARRAY) {
6810 auto a = ArrayData::Make(0);
6811 a->incRefCount();
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;
6823 decRefArr(a);
6824 break;
6826 // Fall through
6828 default: {
6829 if (val->getScalarValue(tvAsVariant(&tvVal))) {
6830 if (tvAsVariant(&tvVal).isArray()) {
6831 not_implemented();
6833 break;
6835 not_reached();
6840 bool EmitterVisitor::requiresDeepInit(ExpressionPtr initExpr) const {
6841 switch (initExpr->getKindOf()) {
6842 case Expression::KindOfScalarExpression:
6843 case Expression::KindOfConstantExpression:
6844 case Expression::KindOfClassConstantExpression:
6845 return false;
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());
6852 if (el) {
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))) {
6860 return true;
6864 return false;
6865 } else if (u->getOp() == '+' || u->getOp() == '-') {
6866 return requiresDeepInit(u->getExpression());
6868 // fall through
6870 default:
6871 return true;
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);
6895 default: break;
6898 return ConstructPtr();
6901 static UnitEmitter* emitHHBCUnitEmitter(AnalysisResultPtr ar, FileScopePtr fsp,
6902 const MD5& md5) {
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);
6920 try {
6921 ev.visit(fsp);
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());
6927 delete ue;
6928 ue = nue;
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());
6935 return ue;
6938 struct Entry {
6939 StringData* name;
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(""));
6949 ue->initMain(0, 0);
6950 FuncEmitter* mfe = ue->getMain();
6951 ue->emitOp(OpInt);
6952 ue->emitInt64(1);
6953 ue->emitOp(OpRetC);
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
6966 is not callable.
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());
6972 ue->emitOp(OpNull);
6973 ue->emitOp(OpRetC);
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);
6984 assert(mi &&
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();
6997 delete ue;
6998 return unit;
7001 enum ContinuationMethod {
7002 METH_NEXT,
7003 METH_SEND,
7004 METH_RAISE,
7005 METH_VALID,
7006 METH_CURRENT,
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());
7019 switch (m) {
7020 case METH_SEND:
7021 case METH_RAISE:
7022 fe->appendParam(valStr, FuncEmitter::ParamInfo());
7023 case METH_NEXT: {
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
7026 // translations
7027 fe->setAttrs(Attr(fe->attrs() | AttrClone));
7029 static Op mOps[] = {
7030 OpContNext,
7031 OpContSend,
7032 OpContRaise,
7034 ue.emitOp(mOps[m]);
7035 const Offset ehStart = ue.bcPos();
7036 ue.emitOp(OpContEnter);
7037 ue.emitOp(OpContStopped);
7038 ue.emitOp(OpNull);
7039 ue.emitOp(OpRetC);
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()));
7047 ue.emitOp(OpCatch);
7048 ue.emitOp(OpContHandle);
7049 break;
7051 case METH_VALID: {
7052 ue.emitOp(OpContValid);
7053 ue.emitOp(OpRetC);
7054 break;
7056 case METH_CURRENT: {
7057 ue.emitOp(OpContCurrent);
7058 ue.emitOp(OpRetC);
7059 break;
7062 default:
7063 not_reached();
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(""));
7072 ue->initMain(0, 0);
7073 FuncEmitter* mfe = ue->getMain();
7074 ue->emitOp(OpInt);
7075 ue->emitInt64(1);
7076 ue->emitOp(OpRetC);
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;
7112 PendingMap pending;
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) {
7117 Entry e;
7118 e.name = const_cast<StringData*>(it->first);
7119 e.info = it->second;
7120 e.ci = ClassInfo::FindSystemClassInterfaceOrTrait(e.name);
7121 assert(e.ci);
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
7126 // loaded now
7127 classEntries.push_back(e);
7128 } else {
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
7137 // loaded
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,
7156 nullptr);
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);
7174 pce->addMethod(fe);
7175 if (e.name->isame(continuationCls) &&
7176 mapGet(contMethods, methName, &cmeth)) {
7177 emitContinuationMethod(*ue, fe, cmeth, metaInfo);
7178 } else {
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];
7197 assert(cnsInfo);
7198 Variant val;
7199 try {
7200 val = cnsInfo->getValue();
7201 } catch (Exception& e) {
7202 assert(false);
7204 pce->addConstant(
7205 cnsInfo->name.get(),
7206 nullptr,
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];
7215 assert(propInfo);
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;
7222 TypedValue tvNull;
7223 tvWriteNull(&tvNull);
7224 pce->addProperty(
7225 propInfo->name.get(),
7226 Attr(attr),
7227 nullptr,
7228 propInfo->docComment ? StringData::GetStaticString(propInfo->docComment) : nullptr,
7229 &tvNull,
7230 KindOfInvalid
7236 Peephole peephole(*ue, metaInfo);
7237 metaInfo.setForUnit(*ue);
7239 Unit* unit = ue->create();
7240 delete ue;
7241 return unit;
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);
7254 ar->loadBuiltins();
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());
7269 if (!f) {
7270 Logger::Error("Unable to open %s for write", fullPath.c_str());
7271 } else {
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());
7275 f.close();
7279 return ue;
7282 class UEQ : public Synchronizable {
7283 public:
7284 void push(UnitEmitter* ue) {
7285 assert(ue != nullptr);
7286 Lock lock(this);
7287 m_ues.push_back(ue);
7288 notify();
7290 UnitEmitter* tryPop(long sec, long long nsec) {
7291 Lock lock(this);
7292 if (m_ues.empty()) {
7293 // Check for empty() after wait(), in case of spurious wakeup.
7294 if (!wait(sec, nsec) || m_ues.empty()) {
7295 return nullptr;
7298 assert(m_ues.size() > 0);
7299 UnitEmitter* ue = m_ues.front();
7300 assert(ue != nullptr);
7301 m_ues.pop_front();
7302 return ue;
7304 private:
7305 std::deque<UnitEmitter*> m_ues;
7307 static UEQ s_ueq;
7309 class EmitterWorker : public JobQueueWorker<FileScopeRawPtr, true, true> {
7310 public:
7311 EmitterWorker() : m_ret(true) {}
7312 virtual void doJob(JobType job) {
7313 try {
7314 AnalysisResultPtr ar = ((AnalysisResult*)m_opaque)->shared_from_this();
7315 UnitEmitter* ue = emitHHBCVisitor(ar, job);
7316 if (Option::GenerateBinaryHHBC) {
7317 s_ueq.push(ue);
7318 } else {
7319 delete ue;
7321 } catch (Exception &e) {
7322 Logger::Error("%s", e.getMessage().c_str());
7323 m_ret = false;
7324 } catch (...) {
7325 Logger::Error("Fatal: An unexpected exception was thrown");
7326 m_ret = false;
7329 private:
7330 bool m_ret;
7333 static void addEmitterWorker(AnalysisResultPtr ar, StatementPtr sp,
7334 void *data) {
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.
7345 bool err = false;
7347 RepoTxn txn(repo);
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)) {
7353 err = true;
7354 break;
7357 if (!err) {
7358 txn.commit();
7362 // Clean up.
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.
7367 if (err) {
7368 repo.commitUnit(ue, UnitOriginFile);
7370 delete ue;
7372 ues.clear();
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;
7384 SCOPE_EXIT {
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());
7393 parser.parse();
7394 FileScopePtr fsp = parser.getFileScope();
7395 fsp->setOuterScope(ar);
7397 ar->loadBuiltins();
7398 ar->setPhase(AnalysisResult::AnalyzeAll);
7399 fsp->analyzeProgram(ar);
7401 int md5len;
7402 char* md5str = string_md5(slib.c_str(), slib.size(), false, md5len);
7403 MD5 md5(md5str);
7404 free(md5str);
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 */
7425 TypeConstraint tc;
7427 JobQueueDispatcher<EmitterWorker::JobType, EmitterWorker>
7428 dispatcher(threadCount, true, 0, false, ar.get());
7430 dispatcher.start();
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
7443 // batches.
7444 bool didPop;
7445 bool inShutdown = false;
7446 while (true) {
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))) {
7451 ues.push_back(ue);
7453 if (ues.size() == kBatchSize
7454 || (!didPop && inShutdown && ues.size() > 0)) {
7455 batchCommit(ues);
7457 if (!inShutdown) {
7458 inShutdown = dispatcher.pollEmpty();
7459 } else if (!didPop) {
7460 assert(ues.size() == 0);
7461 break;
7465 emitSystemLib();
7466 } else {
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
7474 * the source code.
7476 * Before being actually used, hphp_compiler_parse must be called with
7477 * a NULL `code' parameter to do initialization.
7480 extern "C" {
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;
7500 TypeConstraint tc;
7501 return nullptr;
7504 try {
7505 UnitOrigin unitOrigin = UnitOriginFile;
7506 if (!filename) {
7507 filename = "";
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);
7527 parser.parse();
7528 FileScopePtr fsp = parser.getFileScope();
7529 fsp->setOuterScope(ar);
7531 ar->loadBuiltins();
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();
7538 delete ue;
7539 return unit;
7540 } catch (const std::exception&) {
7541 // extern "C" function should not be throwing exceptions...
7542 return nullptr;
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);
7556 } // extern "C"
7558 ///////////////////////////////////////////////////////////////////////////////