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