Delete StructArray and Shape
[hiphop-php.git] / hphp / compiler / analysis / emitter.cpp
blob4287445a48e0c1a90f039f2330e707f84da8cefc
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2016 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/compiler/analysis/emitter.h"
19 #include <algorithm>
20 #include <deque>
21 #include <exception>
22 #include <fstream>
23 #include <iomanip>
24 #include <iostream>
25 #include <memory>
26 #include <set>
27 #include <utility>
28 #include <vector>
30 #include <boost/algorithm/string/predicate.hpp>
32 #include <folly/MapUtil.h>
33 #include <folly/Memory.h>
34 #include <folly/ScopeGuard.h>
35 #ifndef _MSC_VER
36 #include <folly/Subprocess.h>
37 #endif
38 #include <folly/String.h>
40 #include "hphp/compiler/builtin_symbols.h"
41 #include "hphp/compiler/analysis/class_scope.h"
42 #include "hphp/compiler/analysis/code_error.h"
43 #include "hphp/compiler/analysis/file_scope.h"
44 #include "hphp/compiler/analysis/function_scope.h"
45 #include "hphp/compiler/expression/array_element_expression.h"
46 #include "hphp/compiler/expression/array_pair_expression.h"
47 #include "hphp/compiler/expression/assignment_expression.h"
48 #include "hphp/compiler/expression/binary_op_expression.h"
49 #include "hphp/compiler/expression/class_constant_expression.h"
50 #include "hphp/compiler/expression/class_expression.h"
51 #include "hphp/compiler/expression/closure_expression.h"
52 #include "hphp/compiler/expression/constant_expression.h"
53 #include "hphp/compiler/expression/dynamic_variable.h"
54 #include "hphp/compiler/expression/encaps_list_expression.h"
55 #include "hphp/compiler/expression/expression_list.h"
56 #include "hphp/compiler/expression/include_expression.h"
57 #include "hphp/compiler/expression/list_assignment.h"
58 #include "hphp/compiler/expression/modifier_expression.h"
59 #include "hphp/compiler/expression/new_object_expression.h"
60 #include "hphp/compiler/expression/object_method_expression.h"
61 #include "hphp/compiler/expression/parameter_expression.h"
62 #include "hphp/compiler/expression/qop_expression.h"
63 #include "hphp/compiler/expression/null_coalesce_expression.h"
64 #include "hphp/compiler/expression/scalar_expression.h"
65 #include "hphp/compiler/expression/simple_variable.h"
66 #include "hphp/compiler/expression/simple_function_call.h"
67 #include "hphp/compiler/expression/static_member_expression.h"
68 #include "hphp/compiler/expression/unary_op_expression.h"
69 #include "hphp/compiler/expression/yield_expression.h"
70 #include "hphp/compiler/expression/yield_from_expression.h"
71 #include "hphp/compiler/expression/await_expression.h"
72 #include "hphp/compiler/statement/block_statement.h"
73 #include "hphp/compiler/statement/break_statement.h"
74 #include "hphp/compiler/statement/case_statement.h"
75 #include "hphp/compiler/statement/catch_statement.h"
76 #include "hphp/compiler/statement/class_constant.h"
77 #include "hphp/compiler/statement/class_variable.h"
78 #include "hphp/compiler/statement/do_statement.h"
79 #include "hphp/compiler/statement/echo_statement.h"
80 #include "hphp/compiler/statement/exp_statement.h"
81 #include "hphp/compiler/statement/for_statement.h"
82 #include "hphp/compiler/statement/foreach_statement.h"
83 #include "hphp/compiler/statement/finally_statement.h"
84 #include "hphp/compiler/statement/function_statement.h"
85 #include "hphp/compiler/statement/global_statement.h"
86 #include "hphp/compiler/statement/goto_statement.h"
87 #include "hphp/compiler/statement/if_branch_statement.h"
88 #include "hphp/compiler/statement/if_statement.h"
89 #include "hphp/compiler/statement/label_statement.h"
90 #include "hphp/compiler/statement/method_statement.h"
91 #include "hphp/compiler/statement/return_statement.h"
92 #include "hphp/compiler/statement/statement_list.h"
93 #include "hphp/compiler/statement/static_statement.h"
94 #include "hphp/compiler/statement/switch_statement.h"
95 #include "hphp/compiler/statement/try_statement.h"
96 #include "hphp/compiler/statement/unset_statement.h"
97 #include "hphp/compiler/statement/while_statement.h"
98 #include "hphp/compiler/statement/use_trait_statement.h"
99 #include "hphp/compiler/statement/class_require_statement.h"
100 #include "hphp/compiler/statement/trait_prec_statement.h"
101 #include "hphp/compiler/statement/trait_alias_statement.h"
102 #include "hphp/compiler/statement/typedef_statement.h"
103 #include "hphp/compiler/statement/declare_statement.h"
104 #include "hphp/compiler/parser/parser.h"
105 #include "hphp/hhbbc/hhbbc.h"
106 #include "hphp/hhbbc/parallel.h"
108 #include "hphp/util/trace.h"
109 #include "hphp/util/safe-cast.h"
110 #include "hphp/util/logger.h"
111 #include "hphp/util/job-queue.h"
112 #include "hphp/parser/hphp.tab.hpp"
113 #include "hphp/runtime/base/repo-auth-type.h"
114 #include "hphp/runtime/vm/bytecode.h"
115 #include "hphp/runtime/vm/native.h"
116 #include "hphp/runtime/vm/repo.h"
117 #include "hphp/runtime/vm/repo-global-data.h"
118 #include "hphp/runtime/vm/as.h"
119 #include "hphp/runtime/base/packed-array.h"
120 #include "hphp/runtime/base/stats.h"
121 #include "hphp/runtime/base/static-string-table.h"
122 #include "hphp/runtime/base/runtime-option.h"
123 #include "hphp/runtime/base/zend-string.h"
124 #include "hphp/runtime/base/zend-functions.h"
125 #include "hphp/runtime/base/type-conversions.h"
126 #include "hphp/runtime/base/builtin-functions.h"
127 #include "hphp/runtime/base/variable-serializer.h"
128 #include "hphp/runtime/base/program-functions.h"
129 #include "hphp/runtime/base/unit-cache.h"
130 #include "hphp/runtime/base/user-attributes.h"
131 #include "hphp/runtime/base/collections.h"
132 #include "hphp/runtime/vm/preclass-emitter.h"
133 #include "hphp/runtime/vm/runtime.h"
135 #include "hphp/system/systemlib.h"
137 namespace HPHP {
139 DECLARE_BOOST_TYPES(AwaitExpression);
140 DECLARE_BOOST_TYPES(ClosureExpression);
141 DECLARE_BOOST_TYPES(FileScope);
142 DECLARE_BOOST_TYPES(ForEachStatement);
143 DECLARE_BOOST_TYPES(FunctionCall);
144 DECLARE_BOOST_TYPES(FunctionScope);
145 DECLARE_BOOST_TYPES(InterfaceStatement);
146 DECLARE_BOOST_TYPES(ListAssignment);
147 DECLARE_BOOST_TYPES(MethodStatement);
148 DECLARE_BOOST_TYPES(SimpleFunctionCall);
149 DECLARE_BOOST_TYPES(SwitchStatement);
151 namespace Compiler {
152 ///////////////////////////////////////////////////////////////////////////////
154 TRACE_SET_MOD(emitter);
156 const StaticString
157 s_ini_get("ini_get"),
158 s_is_deprecated("deprecated function"),
159 s_trigger_error("trigger_error"),
160 s_trigger_sampled_error("trigger_sampled_error"),
161 s_zend_assertions("zend.assertions");
163 struct Label;
164 struct EmitterVisitor;
166 using OptLocation = folly::Optional<Location::Range>;
168 namespace StackSym {
169 static const char None = 0x00;
172 * We don't actually track the U flavor (we treat it as a C),
173 * because there's nothing important to do with it for emission.
174 * The verifier will check they are only created at the appropriate
175 * times.
177 static const char C = 0x01; // Cell symbolic flavor
178 static const char V = 0x02; // Ref symbolic flavor
179 static const char A = 0x03; // Classref symbolic flavor
180 static const char R = 0x04; // Return value symbolic flavor
181 static const char F = 0x05; // Function argument symbolic flavor
182 static const char L = 0x06; // Local symbolic flavor
183 static const char T = 0x07; // String literal symbolic flavor
184 static const char I = 0x08; // int literal symbolic flavor
185 static const char H = 0x09; // $this symbolic flavor
187 static const char N = 0x10; // Name marker
188 static const char G = 0x20; // Global name marker
189 static const char E = 0x30; // Element marker
190 static const char W = 0x40; // New element marker
191 static const char P = 0x50; // Property marker
192 static const char S = 0x60; // Static property marker
193 static const char M = 0x70; // Non elem/prop/W part of M-vector
194 static const char K = (char)0x80u; // Class base marker
195 static const char Q = (char)0x90u; // NullSafe Property marker
197 static const char CN = C | N;
198 static const char CG = C | G;
199 static const char CS = C | S;
200 static const char LN = L | N;
201 static const char LG = L | G;
202 static const char LS = L | S;
203 static const char AM = A | M;
205 char GetSymFlavor(char sym) { return (sym & 0x0F); }
206 char GetMarker(char sym) { return (sym & 0xF0); }
209 * Return whether or not sym represents a symbolic stack element, rather than
210 * an actual stack element. Symbolic stack elements do not have corresponding
211 * values on the real eval stack at runtime, and represent things like local
212 * variable ids or literal ints and strings.
214 bool IsSymbolic(char sym) {
215 auto const flavor = GetSymFlavor(sym);
216 if (flavor == L || flavor == T || flavor == I || flavor == H) return true;
218 auto const marker = GetMarker(sym);
219 if (marker == W || marker == K) return true;
221 return false;
224 std::string ToString(char sym) {
225 char symFlavor = StackSym::GetSymFlavor(sym);
226 std::string res;
227 switch (symFlavor) {
228 case StackSym::C: res = "C"; break;
229 case StackSym::V: res = "V"; break;
230 case StackSym::A: res = "A"; break;
231 case StackSym::R: res = "R"; break;
232 case StackSym::F: res = "F"; break;
233 case StackSym::L: res = "L"; break;
234 case StackSym::T: res = "T"; break;
235 case StackSym::I: res = "I"; break;
236 case StackSym::H: res = "H"; break;
237 default: break;
239 char marker = StackSym::GetMarker(sym);
240 switch (marker) {
241 case StackSym::N: res += "N"; break;
242 case StackSym::G: res += "G"; break;
243 case StackSym::E: res += "E"; break;
244 case StackSym::W: res += "W"; break;
245 case StackSym::P: res += "P"; break;
246 case StackSym::Q: res += "Q"; break;
247 case StackSym::S: res += "S"; break;
248 case StackSym::K: res += "K"; break;
249 default: break;
251 if (res == "") {
252 if (sym == StackSym::None) {
253 res = "None";
254 } else {
255 res = "?";
258 return res;
262 struct Emitter {
263 Emitter(ConstructPtr node, UnitEmitter& ue, EmitterVisitor& ev)
264 : m_node(node), m_ue(ue), m_ev(ev) {}
265 UnitEmitter& getUnitEmitter() { return m_ue; }
266 ConstructPtr getNode() { return m_node; }
267 EmitterVisitor& getEmitterVisitor() { return m_ev; }
268 void setTempLocation(const OptLocation& r) {
269 m_tempLoc = r;
271 const OptLocation& getTempLocation() { return m_tempLoc; }
272 void incStat(int counter, int value) {
273 if (RuntimeOption::EnableEmitterStats) {
274 IncStat(counter, value);
278 struct StrOff {
279 StrOff(Id s, Label* d) : str(s), dest(d) {}
280 Id str;
281 Label* dest;
284 struct IterPair {
285 IterPair(IterKind k, Id i) : kind(k), id(i) {}
286 IterKind kind;
287 Id id;
290 #define NA
291 #define ONE(typ) \
292 IMM_##typ
293 #define TWO(typ1, typ2) \
294 IMM_##typ1, IMM_##typ2
295 #define THREE(typ1, typ2, typ3) \
296 IMM_##typ1, IMM_##typ2, IMM_##typ3
297 #define FOUR(typ1, typ2, typ3, typ4) \
298 IMM_##typ1, IMM_##typ2, IMM_##typ3, IMM_##typ4
299 #define IMM_BLA std::vector<Label*>&
300 #define IMM_SLA std::vector<StrOff>&
301 #define IMM_ILA std::vector<IterPair>&
302 #define IMM_IVA int32_t
303 #define IMM_LA int32_t
304 #define IMM_IA int32_t
305 #define IMM_I64A int64_t
306 #define IMM_DA double
307 #define IMM_SA const StringData*
308 #define IMM_RATA RepoAuthType
309 #define IMM_AA ArrayData*
310 #define IMM_BA Label&
311 #define IMM_OA(type) type
312 #define IMM_VSA std::vector<std::string>&
313 #define IMM_KA MemberKey
314 #define O(name, imm, pop, push, flags) void name(imm);
315 OPCODES
316 #undef O
317 #undef NA
318 #undef ONE
319 #undef TWO
320 #undef THREE
321 #undef FOUR
322 #undef IMM_MA
323 #undef IMM_BLA
324 #undef IMM_SLA
325 #undef IMM_ILA
326 #undef IMM_IVA
327 #undef IMM_LA
328 #undef IMM_IA
329 #undef IMM_I64A
330 #undef IMM_DA
331 #undef IMM_SA
332 #undef IMM_RATA
333 #undef IMM_AA
334 #undef IMM_BA
335 #undef IMM_OA
336 #undef IMM_VSA
337 #undef IMM_KA
339 private:
340 ConstructPtr m_node;
341 UnitEmitter& m_ue;
342 EmitterVisitor& m_ev;
343 OptLocation m_tempLoc;
346 struct SymbolicStack {
347 enum ClassBaseType {
348 CLS_INVALID,
349 CLS_LATE_BOUND,
350 CLS_UNNAMED_LOCAL, // loc is an unnamed local
351 CLS_NAMED_LOCAL, // loc is a normal program local
352 CLS_STRING_NAME, // name is the string to use
353 CLS_SELF,
354 CLS_PARENT
357 private:
359 * Symbolic stack (m_symStack)
361 * The symbolic stack is used to keep track of the flavor descriptors
362 * of values along with other contextual information. Each position in
363 * the symbolic stack can encode a "symbolic flavor" and a "marker".
364 * Most symbolic flavors correspond with flavor descriptors in the HHBC
365 * spec, but some symbolic flavors used in the symbolic stack (ex. "L")
366 * do not correspond with a flavor descriptor from the spec. Markers
367 * provide contextual information and are used by the emitter in various
368 * situations to determine the appropriate bytecode instruction to use.
370 * Note that not all positions on the symbolic stack correspond to a
371 * value on the actual evaluation stack as described in the HHBC spec.
373 struct SymEntry {
374 explicit SymEntry(char s = 0)
375 : sym(s)
376 , name(nullptr)
377 , className(nullptr)
378 , intval(-1)
379 , unnamedLocalStart(InvalidAbsoluteOffset)
380 , clsBaseType(CLS_INVALID)
382 char sym;
383 const StringData* name;
384 const StringData* className;
385 int64_t intval; // used for L and I symbolic flavors
387 // If intval is an unnamed local temporary, this offset is the start
388 // of the region we are using it (which we will need to have a
389 // fault funclet for).
390 Offset unnamedLocalStart;
392 // When class bases are emitted, we need to delay class lookup for
393 // evaluation order reasons, but may have to do some evaluation
394 // early. How this works depends on the type of class base---see
395 // emitResolveClsBase for details.
396 ClassBaseType clsBaseType;
398 std::string pretty() const;
400 std::vector<SymEntry> m_symStack;
403 * Actual stack (m_actualStack)
405 * The actual stack represents the evaluation stack as described in the
406 * HHBC spec. Each position in m_actualStack contains the index of the
407 * corresponding symbolic value in m_symStack.
409 std::vector<int> m_actualStack;
411 // The number of Func descriptors (in HHVM terms, ActRecs) currently on the
412 // stack.
413 int m_fdescCount;
415 public:
416 int* m_actualStackHighWaterPtr;
418 SymbolicStack() : m_fdescCount(0) {}
420 std::string pretty() const;
422 void updateHighWater();
423 void push(char sym);
424 void setInt(int64_t v);
425 void setString(const StringData* s);
426 void setKnownCls(const StringData* s, bool nonNull);
427 void cleanTopMeta();
428 void setClsBaseType(ClassBaseType);
429 void setUnnamedLocal(int index, int localId, Offset startOffset);
430 void pop();
431 char top() const;
432 char get(int index) const;
433 const StringData* getName(int index) const;
434 const StringData* getClsName(int index) const;
435 bool isCls(int index) const;
436 bool isTypePredicted(int index = -1 /* stack top */) const;
437 void set(int index, char sym);
438 size_t size() const;
439 size_t actualSize() const;
440 size_t fdescSize() const { return m_fdescCount; }
441 bool empty() const;
442 void clear();
445 * Erase a stack element depth below the top. This is used for some
446 * instructions that pull elements out of the middle, and for our
447 * ClassBaseType virtual elements.
449 void consumeBelowTop(int depth);
451 int getActualPos(int vpos) const;
452 char getActual(int index) const;
453 void setActual(int index, char sym);
454 void insertAt(int depth, char sym);
455 int sizeActual() const;
457 ClassBaseType getClsBaseType(int index) const;
458 int getLoc(int index) const;
459 int64_t getInt(int index) const;
460 Offset getUnnamedLocStart(int index) const;
462 void pushFDesc();
463 void popFDesc();
466 struct Label {
467 Label() : m_off(InvalidAbsoluteOffset) {}
468 explicit Label(Emitter& e) : m_off(InvalidAbsoluteOffset) {
469 set(e);
471 Offset getAbsoluteOffset() const { return m_off; }
472 // Sets the Label to the bytecode offset of given by e,
473 // fixes up any instructions that have already been
474 // emitted that reference this Label, and fixes up the
475 // EmitterVisitor's jump target info
476 void set(Emitter& e);
477 // If a Label is has not been set, it is the Emitter's
478 // resposibility to call bind on the Label each time it
479 // prepares to emit an instruction that uses the Label
480 void bind(EmitterVisitor& ev, Offset instrAddr, Offset offAddr);
481 bool isSet() { return m_off != InvalidAbsoluteOffset; }
482 bool isUsed();
483 private:
484 Offset m_off;
485 std::vector<std::pair<Offset, Offset> > m_emittedOffs;
486 // m_evalStack is used to store the eval stack of the
487 // first forward jump we see that references this label
488 SymbolicStack m_evalStack;
491 struct Thunklet {
492 virtual ~Thunklet();
493 virtual void emit(Emitter& e) = 0;
496 struct Funclet {
497 explicit Funclet(Thunklet* body)
498 : m_body(body) {
500 Thunklet* m_body;
501 Label m_entry;
504 DECLARE_BOOST_TYPES(ControlTarget);
506 * The structure represents a code path that potentially requires
507 * running finally blocks. A code path has an assigned state ID that
508 * is used inside switch statements emitted at the end of finally
509 * blocks. It also has an optional label (the destination to jump
510 * to after all the required finally blocks are run).
512 struct ControlTarget {
513 static const int k_unsetState;
514 explicit ControlTarget(EmitterVisitor* router);
515 ~ControlTarget();
516 // Manage state ID reuse.
517 bool isRegistered();
518 EmitterVisitor* m_visitor;
519 // The target to jump to once all the necessary finally blocks are run.
520 Label m_label;
521 // The state ID that identifies this control target inside finally
522 // epilogues. This ID assigned to the "state" unnamed local variable.
523 int m_state;
526 struct ControlTargetInfo {
527 ControlTargetInfo() : used(false) {}
528 ControlTargetInfo(ControlTargetPtr t, bool b) : target(t), used(b) {}
529 ControlTargetPtr target;
530 bool used;
533 DECLARE_BOOST_TYPES(Region);
536 * Region represents a single level of the unified stack
537 * of constructs that are meaningful from the point of view of finally
538 * implementation. The levels are used to keep track of the information
539 * such as the control targets that can be taken inside a block.
541 struct Region {
542 enum Kind {
543 // Top-level (global) context.
544 Global,
545 // Function body / method body entry.
546 FuncBody,
547 // Entry for finally fault funclets emitted after the body of
548 // a function
549 FaultFunclet,
550 // Region by a finally clause
551 TryFinally,
552 // Finally block entry (begins after catches ends after finally)
553 Finally,
554 // Loop or switch statement.
555 LoopOrSwitch,
558 typedef Emitter::IterPair IterPair;
559 typedef std::vector<IterPair> IterVec;
561 Region(Region::Kind kind, RegionPtr parent);
563 // Helper for establishing the maximal depth of break / continue
564 // control targets that are allocated.
565 int getBreakContinueDepth();
567 // Returns the maximal break / continue depth admissable (aka the
568 // number of nested loops).
569 int getMaxBreakContinueDepth();
571 int getMaxState();
573 // The number of cases to be emitted. This is a helper used in
574 // establishing whether one of the optimized cases can be used.
575 int getCaseCount();
577 bool isForeach() { return m_iterId != -1; }
578 bool isTryFinally() { return m_kind == Region::Kind::TryFinally; }
579 bool isFinally() { return m_kind == Region::Kind::Finally; }
581 bool isBreakUsed(int i) {
582 auto it = m_breakTargets.find(i);
583 if (it == m_breakTargets.end()) return false;
584 return it->second.used;
587 bool isContinueUsed(int i) {
588 auto it = m_continueTargets.find(i);
589 if (it == m_continueTargets.end()) return false;
590 return it->second.used;
593 Region::Kind m_kind;
594 // Only used for loop / break kind of entries.
595 Id m_iterId;
596 IterKind m_iterKind;
597 // Because of a bug in code emission, functions sometimes have
598 // inconsistent return flavors. Therefore instead of a single
599 // return control target, there need to be one return control
600 // target per flavor used. Once the bug is removed, this code
601 // can be simplified.
602 std::map<char, ControlTargetInfo> m_returnTargets;
603 // Break and continue control targets identified by their depth.
604 std::map<int, ControlTargetInfo> m_breakTargets;
605 std::map<int, ControlTargetInfo> m_continueTargets;
606 // Goto control targets. Each goto control target is identified
607 // by the name of the destination label.
608 std::map<StringData*, ControlTargetInfo, string_data_lt> m_gotoTargets;
609 // A set of goto labels occurrning inside the statement represented
610 // by this entry. This value is used for establishing whether
611 // a finally block needs to be executed when performing gotos.
612 std::set<StringData*, string_data_lt> m_gotoLabels;
613 // The label denoting the beginning of a finally block inside the
614 // current try. Only used when the entry kind is a try statement.
615 Label m_finallyLabel;
616 // The parent entry.
617 RegionPtr m_parent;
620 struct EmitterVisitor {
621 friend struct UnsetUnnamedLocalThunklet;
622 friend struct FuncFinisher;
623 public:
624 typedef std::vector<int> IndexChain;
625 typedef std::pair<ExpressionPtr, IndexChain> IndexPair;
626 typedef Emitter::IterPair IterPair;
627 typedef std::vector<IterPair> IterVec;
629 explicit EmitterVisitor(UnitEmitter& ue);
630 ~EmitterVisitor();
632 bool visit(ConstructPtr c);
633 void visitKids(ConstructPtr c);
634 void visit(FileScopePtr file);
635 void assignLocalVariableIds(FunctionScopePtr fs);
636 void assignFinallyVariableIds();
637 void fixReturnType(Emitter& e, FunctionCallPtr fn,
638 Func* builtinFunc = nullptr);
640 void listAssignmentVisitLHS(Emitter& e, ExpressionPtr exp,
641 IndexChain& indexChain,
642 std::vector<IndexPair>& chainList);
643 void listAssignmentAssignElements(Emitter& e,
644 std::vector<IndexPair>& indexChains,
645 std::function<void()> emitSrc);
647 void visitIfCondition(ExpressionPtr cond, Emitter& e, Label& tru, Label& fals,
648 bool truFallthrough);
649 const SymbolicStack& getEvalStack() const { return m_evalStack; }
650 SymbolicStack& getEvalStack() { return m_evalStack; }
651 void setEvalStack(const SymbolicStack& es) {
652 m_evalStack = es;
653 m_evalStackIsUnknown = false;
655 bool evalStackIsUnknown() { return m_evalStackIsUnknown; }
656 void popEvalStack(char symFlavor);
657 void popSymbolicLocal(Op opcode);
658 void popEvalStackMMany();
659 void popEvalStackMany(int len, char symFlavor);
660 void popEvalStackCVMany(int len);
661 void pushEvalStack(char symFlavor);
662 void peekEvalStack(char symFlavor, int depthActual);
663 void pokeEvalStack(char symFlavor, int depthActual);
664 void prepareEvalStack();
665 void recordJumpTarget(Offset target, const SymbolicStack& evalStack);
666 void recordJumpTarget(Offset target) {
667 recordJumpTarget(target, m_evalStack);
669 void restoreJumpTargetEvalStack();
670 void recordCall();
671 bool isJumpTarget(Offset target);
672 void setPrevOpcode(Op op) { m_prevOpcode = op; }
673 Op getPrevOpcode() const { return m_prevOpcode; }
674 bool currentPositionIsReachable() {
675 return (m_ue.bcPos() == m_curFunc->base
676 || isJumpTarget(m_ue.bcPos())
677 || (instrFlags(getPrevOpcode()) & TF) == 0);
679 FuncEmitter* getFuncEmitter() { return m_curFunc; }
680 Id getStateLocal() {
681 assert(m_stateLocal >= 0);
682 return m_stateLocal;
684 Id getRetLocal() {
685 assert(m_retLocal >= 0);
686 return m_retLocal;
689 struct IncludeTimeFatalException : Exception {
690 ConstructPtr m_node;
691 bool m_parseFatal;
692 IncludeTimeFatalException(ConstructPtr node, const char* fmt, ...)
693 : Exception(), m_node(node), m_parseFatal(false) {
694 va_list ap; va_start(ap, fmt); format(fmt, ap); va_end(ap);
696 EXCEPTION_COMMON_IMPL(IncludeTimeFatalException);
697 void setParseFatal(bool b = true) { m_parseFatal = b; }
700 void pushIterScope(Id id, IterKind kind) {
701 m_pendingIters.emplace_back(id, kind);
703 void popIterScope() { m_pendingIters.pop_back(); }
705 private:
706 typedef std::pair<StringData*, bool> ClosureUseVar; // (name, byRef)
707 typedef std::vector<ClosureUseVar> ClosureUseVarVec;
708 typedef std::vector<std::pair<Id,IterKind> > PendingIterVec;
709 typedef std::pair<StringData*, ExpressionPtr> NonScalarPair;
710 typedef std::vector<NonScalarPair> NonScalarVec;
711 typedef std::pair<Id, int> StrCase;
713 struct PostponedMeth {
714 PostponedMeth(MethodStatementPtr m, FuncEmitter* fe, bool top,
715 ClosureUseVarVec* useVars)
716 : m_meth(m), m_fe(fe), m_top(top), m_closureUseVars(useVars) {}
717 MethodStatementPtr m_meth;
718 FuncEmitter* m_fe;
719 bool m_top;
720 ClosureUseVarVec* m_closureUseVars;
723 struct PostponedCtor {
724 PostponedCtor(InterfaceStatementPtr is, FuncEmitter* fe)
725 : m_is(is), m_fe(fe) {}
726 InterfaceStatementPtr m_is;
727 FuncEmitter* m_fe;
730 struct PostponedNonScalars {
731 PostponedNonScalars(InterfaceStatementPtr is, FuncEmitter* fe,
732 NonScalarVec* v)
733 : m_is(is), m_fe(fe), m_vec(v) {}
734 void release() {
735 delete m_vec;
737 InterfaceStatementPtr m_is;
738 FuncEmitter* m_fe;
739 NonScalarVec* m_vec;
742 struct PostponedClosureCtor {
743 PostponedClosureCtor(ClosureUseVarVec& v, ClosureExpressionPtr e,
744 FuncEmitter* fe)
745 : m_useVars(v), m_expr(e), m_fe(fe) {}
746 ClosureUseVarVec m_useVars;
747 ClosureExpressionPtr m_expr;
748 FuncEmitter* m_fe;
751 struct CatchRegion {
752 CatchRegion(Offset start, Offset end) : m_start(start),
753 m_end(end) {}
754 ~CatchRegion() {
755 for (std::vector<std::pair<StringData*, Label*> >::const_iterator it =
756 m_catchLabels.begin(); it != m_catchLabels.end(); it++) {
757 delete it->second;
760 Offset m_start;
761 Offset m_end;
762 std::set<StringData*, string_data_lt> m_names;
763 std::vector<std::pair<StringData*, Label*> > m_catchLabels;
766 struct FaultRegion {
767 FaultRegion(Offset start,
768 Offset end,
769 Label* func,
770 Id iterId,
771 IterKind kind)
772 : m_start(start)
773 , m_end(end)
774 , m_func(func)
775 , m_iterId(iterId)
776 , m_iterKind(kind) {}
778 Offset m_start;
779 Offset m_end;
780 Label* m_func;
781 Id m_iterId;
782 IterKind m_iterKind;
785 struct FPIRegion {
786 FPIRegion(Offset start, Offset end, Offset fpOff)
787 : m_start(start), m_end(end), m_fpOff(fpOff) {}
788 Offset m_start;
789 Offset m_end;
790 Offset m_fpOff;
793 struct SwitchState {
794 SwitchState() : nonZeroI(-1), defI(-1) {}
796 SwitchState(const SwitchState&) = delete;
797 SwitchState& operator=(const SwitchState&) = delete;
799 std::map<int64_t, int> cases; // a map from int (or litstr id) to case index
800 std::vector<StrCase> caseOrder; // for string switches, a list of the
801 // <litstr id, case index> in the order
802 // they appear in the source
803 int nonZeroI;
804 int defI;
807 void allocPipeLocal(Id pipeVar) { m_pipeVars.emplace(pipeVar); }
808 void releasePipeLocal(Id pipeVar) {
809 assert(!m_pipeVars.empty() && m_pipeVars.top() == pipeVar);
810 m_pipeVars.pop();
812 folly::Optional<Id> getPipeLocal() {
813 if (m_pipeVars.empty()) return folly::none;
814 return m_pipeVars.top();
817 private:
818 static constexpr size_t kMinStringSwitchCases = 8;
820 UnitEmitter& m_ue;
821 FuncEmitter* m_curFunc;
822 FileScopePtr m_file;
824 Op m_prevOpcode;
826 std::deque<PostponedMeth> m_postponedMeths;
827 std::deque<PostponedCtor> m_postponedCtors;
828 std::deque<PostponedNonScalars> m_postponedPinits;
829 std::deque<PostponedNonScalars> m_postponedSinits;
830 std::deque<PostponedNonScalars> m_postponedCinits;
831 std::deque<PostponedClosureCtor> m_postponedClosureCtors;
832 PendingIterVec m_pendingIters;
833 hphp_hash_map<std::string, FuncEmitter*> m_topMethodEmitted;
834 SymbolicStack m_evalStack;
835 bool m_evalStackIsUnknown;
836 hphp_hash_map<Offset, SymbolicStack> m_jumpTargetEvalStacks;
837 typedef tbb::concurrent_hash_map<const StringData*, int,
838 StringDataHashICompare> EmittedClosures;
839 static EmittedClosures s_emittedClosures;
840 std::deque<Funclet*> m_funclets;
841 std::map<StatementPtr, Funclet*> m_memoizedFunclets;
842 std::deque<CatchRegion*> m_catchRegions;
843 std::deque<FaultRegion*> m_faultRegions;
844 std::deque<FPIRegion*> m_fpiRegions;
845 std::vector<Array> m_staticArrays;
846 std::vector<folly::Optional<HeaderKind>> m_staticColType;
847 std::set<std::string,stdltistr> m_hoistables;
848 OptLocation m_tempLoc;
849 std::unordered_set<std::string> m_staticEmitted;
851 // The stack of all Regions that this EmitterVisitor is currently inside
852 std::vector<RegionPtr> m_regions;
853 // The state IDs currently allocated for the "finally router" logic.
854 // See FIXME above the registerControlTarget() method.
855 std::set<int> m_states;
856 // Unnamed local variables used by the "finally router" logic
857 Id m_stateLocal;
858 Id m_retLocal;
859 // stack of nested unnamed pipe variables
860 std::stack<Id> m_pipeVars;
862 public:
863 bool checkIfStackEmpty(const char* forInstruction) const;
864 void unexpectedStackSym(char sym, const char* where) const;
866 int scanStackForLocation(int iLast);
869 * Emit bytecodes for the base and intermediate dims, returning the number of
870 * eval stack slots containing member keys that should be consumed by the
871 * final operation.
873 struct MInstrOpts {
874 explicit MInstrOpts(MOpFlags flags)
875 : allowW{flags & MOpFlags::Define}
876 , flags{flags}
879 explicit MInstrOpts(int32_t paramId)
880 : allowW{true}
881 , fpass{true}
882 , paramId{paramId}
885 MInstrOpts& rhs() {
886 rhsVal = true;
887 return *this;
890 bool allowW{false};
891 bool rhsVal{false};
892 bool fpass{false};
893 union {
894 MOpFlags flags;
895 int32_t paramId;
899 MemberKey symToMemberKey(Emitter& e, int i, bool allowW);
900 size_t emitMOp(int iFirst, int& iLast, Emitter& e, MInstrOpts opts);
901 void emitQueryMOp(int iFirst, int iLast, Emitter& e, QueryMOp op);
903 enum class PassByRefKind {
904 AllowCell,
905 WarnOnCell,
906 ErrorOnCell,
908 PassByRefKind getPassByRefKind(ExpressionPtr exp);
909 void emitCall(Emitter& e, FunctionCallPtr func,
910 ExpressionListPtr params, Offset fpiStart);
911 void emitAGet(Emitter& e);
912 void emitCGetL2(Emitter& e);
913 void emitCGetL3(Emitter& e);
914 void emitPushL(Emitter& e);
915 void emitCGet(Emitter& e);
916 void emitCGetQuiet(Emitter& e);
917 bool emitVGet(Emitter& e, bool skipCells = false);
918 void emitIsset(Emitter& e);
919 void emitIsType(Emitter& e, IsTypeOp op);
920 void emitEmpty(Emitter& e);
921 void emitUnset(Emitter& e, ExpressionPtr exp = ExpressionPtr());
922 void emitVisitAndUnset(Emitter& e, ExpressionPtr exp);
923 void emitSet(Emitter& e);
924 void emitSetOp(Emitter& e, int op);
925 void emitBind(Emitter& e);
926 void emitIncDec(Emitter& e, IncDecOp cop);
927 void emitPop(Emitter& e);
928 void emitConvertToCell(Emitter& e);
929 void emitConvertToCellIfVar(Emitter& e);
930 void emitConvertToCellOrLoc(Emitter& e);
931 void emitConvertSecondToCell(Emitter& e);
932 void emitConvertToVar(Emitter& e);
933 void emitFPass(Emitter& e, int paramID, PassByRefKind passByRefKind);
934 void emitVirtualLocal(int localId);
935 template<class Expr> void emitVirtualClassBase(Emitter&, Expr* node);
936 void emitResolveClsBase(Emitter& e, int pos);
937 void emitClsIfSPropBase(Emitter& e);
938 Id emitVisitAndSetUnnamedL(Emitter& e, ExpressionPtr exp);
939 Id emitSetUnnamedL(Emitter& e);
940 void emitPushAndFreeUnnamedL(Emitter& e, Id tempLocal, Offset start);
941 MaybeDataType analyzeSwitch(SwitchStatementPtr s, SwitchState& state);
942 void emitIntegerSwitch(Emitter& e, SwitchStatementPtr s,
943 std::vector<Label>& caseLabels, Label& done,
944 const SwitchState& state);
945 void emitStringSwitch(Emitter& e, SwitchStatementPtr s,
946 std::vector<Label>& caseLabels, Label& done,
947 const SwitchState& state);
948 void emitArrayInit(Emitter& e, ExpressionListPtr el,
949 folly::Optional<HeaderKind> ct = folly::none);
950 void emitPairInit(Emitter&e, ExpressionListPtr el);
951 void emitVectorInit(Emitter&e, CollectionType ct, ExpressionListPtr el);
952 void emitMapInit(Emitter&e, CollectionType ct, ExpressionListPtr el);
953 void emitSetInit(Emitter&e, CollectionType ct, ExpressionListPtr el);
954 void emitCollectionInit(Emitter& e, BinaryOpExpressionPtr exp);
955 void markElem(Emitter& e);
956 void markNewElem(Emitter& e);
957 void markProp(Emitter& e, PropAccessType propAccessType);
958 void markSProp(Emitter& e);
959 void markName(Emitter& e);
960 void markNameSecond(Emitter& e);
961 void markGlobalName(Emitter& e);
963 void emitNameString(Emitter& e, ExpressionPtr n, bool allowLiteral = false);
964 void emitAssignment(Emitter& e, ExpressionPtr c, int op, bool bind);
965 void emitListAssignment(Emitter& e, ListAssignmentPtr lst);
966 void postponeMeth(MethodStatementPtr m, FuncEmitter* fe, bool top,
967 ClosureUseVarVec* useVars = nullptr);
968 void postponeCtor(InterfaceStatementPtr m, FuncEmitter* fe);
969 void postponePinit(InterfaceStatementPtr m, FuncEmitter* fe, NonScalarVec* v);
970 void postponeSinit(InterfaceStatementPtr m, FuncEmitter* fe, NonScalarVec* v);
971 void postponeCinit(InterfaceStatementPtr m, FuncEmitter* fe, NonScalarVec* v);
972 void emitPostponedMeths();
973 void bindUserAttributes(MethodStatementPtr meth,
974 FuncEmitter *fe,
975 bool &allowOverride);
976 void bindNativeFunc(MethodStatementPtr meth, FuncEmitter *fe);
977 int32_t emitNativeOpCodeImpl(MethodStatementPtr meth,
978 const char* funcName,
979 const char* className,
980 FuncEmitter* fe);
981 void emitMethodMetadata(MethodStatementPtr meth,
982 ClosureUseVarVec* useVars,
983 bool top);
984 void fillFuncEmitterParams(FuncEmitter* fe,
985 ExpressionListPtr params,
986 bool coerce_params = false);
987 void emitMethodPrologue(Emitter& e, MethodStatementPtr meth);
988 void emitDeprecationWarning(Emitter& e, MethodStatementPtr meth);
989 void emitMethod(MethodStatementPtr meth);
990 void emitMemoizeProp(Emitter& e, MethodStatementPtr meth, Id localID,
991 const std::vector<Id>& paramIDs, uint32_t numParams);
992 void addMemoizeProp(MethodStatementPtr meth);
993 void emitMemoizeMethod(MethodStatementPtr meth, const StringData* methName);
994 void emitConstMethodCallNoParams(Emitter& e, const std::string& name);
995 bool emitInlineGen(Emitter& e, const ExpressionPtr&);
996 bool emitInlineGena(Emitter& e, const SimpleFunctionCallPtr& call);
997 bool emitInlineGenva(Emitter& e, const SimpleFunctionCallPtr& call);
998 bool emitInlineHHAS(Emitter& e, SimpleFunctionCallPtr);
999 bool emitHHInvariant(Emitter& e, SimpleFunctionCallPtr);
1000 void emitMethodDVInitializers(Emitter& e,
1001 MethodStatementPtr& meth,
1002 Label& topOfBody);
1003 void emitPostponedCtors();
1004 void emitPostponedPSinit(PostponedNonScalars& p, bool pinit);
1005 void emitPostponedPinits();
1006 void emitPostponedSinits();
1007 void emitPostponedCinits();
1008 void emitPostponedClosureCtors();
1009 enum CallUserFuncFlags {
1010 CallUserFuncNone = -1,
1011 CallUserFuncPlain = 0,
1012 CallUserFuncArray = 1,
1013 CallUserFuncSafe = 2,
1014 CallUserFuncReturn = 4,
1015 CallUserFuncForward = 8,
1016 CallUserFuncSafeArray = CallUserFuncSafe | CallUserFuncArray,
1017 CallUserFuncSafeReturn = CallUserFuncSafe | CallUserFuncReturn,
1018 CallUserFuncForwardArray = CallUserFuncForward | CallUserFuncArray
1021 bool emitSystemLibVarEnvFunc(Emitter& e, SimpleFunctionCallPtr node);
1022 bool emitCallUserFunc(Emitter& e, SimpleFunctionCallPtr node);
1023 Func* canEmitBuiltinCall(const std::string& name, int numParams);
1024 void emitFuncCall(Emitter& e, FunctionCallPtr node,
1025 const char* nameOverride = nullptr,
1026 ExpressionListPtr paramsOverride = nullptr);
1027 bool emitConstantFuncCall(Emitter& e, SimpleFunctionCallPtr call);
1028 void emitFuncCallArg(Emitter& e, ExpressionPtr exp, int paramId,
1029 bool isUnpack);
1030 bool emitBuiltinCallArg(Emitter& e, ExpressionPtr exp, int paramId,
1031 bool byRef, bool mustBeRef);
1032 bool emitScalarValue(Emitter& e, const Variant& value);
1033 void emitLambdaCaptureArg(Emitter& e, ExpressionPtr exp);
1034 void emitBuiltinDefaultArg(Emitter& e, Variant& v,
1035 MaybeDataType t, int paramId);
1036 void emitClass(Emitter& e, ClassScopePtr cNode, bool topLevel);
1037 Id emitTypedef(Emitter& e, TypedefStatementPtr);
1038 void emitForeachListAssignment(Emitter& e,
1039 ListAssignmentPtr la,
1040 std::function<void()> emitSrc);
1041 void emitForeach(Emitter& e, ForEachStatementPtr fe);
1042 void emitForeachAwaitAs(Emitter& e, ForEachStatementPtr fe);
1043 void emitRestoreErrorReporting(Emitter& e, Id oldLevelLoc);
1044 void emitMakeUnitFatal(Emitter& e,
1045 const char* msg,
1046 FatalOp k = FatalOp::Runtime);
1048 // Emits a Jmp or IterBreak instruction to the specified target, freeing
1049 // the specified iterator variables. emitJump() cannot be used to leave a
1050 // try region, except if it jumps to the m_finallyLabel of the try region.
1051 void emitJump(Emitter& e, IterVec& iters, Label& target);
1053 // These methods handle the return, break, continue, and goto operations.
1054 // These methods are aware of try/finally blocks and foreach blocks and
1055 // will free iterators and jump to finally epilogues as appropriate.
1056 void emitReturn(Emitter& e, char sym, StatementPtr s);
1057 void emitBreak(Emitter& e, int depth, StatementPtr s);
1058 void emitContinue(Emitter& e, int depth, StatementPtr s);
1059 void emitGoto(Emitter& e, StringData* name, StatementPtr s);
1061 void emitYieldFrom(Emitter& e, ExpressionPtr exp);
1063 // Helper methods for emitting IterFree instructions
1064 void emitIterFree(Emitter& e, IterVec& iters);
1065 void emitIterFreeForReturn(Emitter& e);
1067 // A "finally epilogue" is a blob of bytecode that comes after an inline
1068 // copy of a "finally" clause body. Finally epilogues are used to ensure
1069 // that that the bodies of finally clauses are executed whenever a return,
1070 // break, continue, or goto operation jumps out of their corresponding
1071 // "try" blocks.
1072 void emitFinallyEpilogue(Emitter& e, Region* entry);
1073 void emitReturnTrampoline(Emitter& e, Region* entry,
1074 std::vector<Label*>& cases, char sym);
1075 void emitBreakTrampoline(Emitter& e, Region* entry,
1076 std::vector<Label*>& cases, int depth);
1077 void emitContinueTrampoline(Emitter& e, Region* entry,
1078 std::vector<Label*>& cases, int depth);
1079 void emitGotoTrampoline(Emitter& e, Region* entry,
1080 std::vector<Label*>& cases, StringData* name);
1082 // Returns true if VerifyRetType should be emitted before Ret for
1083 // the current function.
1084 bool shouldEmitVerifyRetType();
1086 Funclet* addFunclet(Thunklet* body);
1087 Funclet* addFunclet(StatementPtr stmt,
1088 Thunklet* body);
1089 Funclet* getFunclet(StatementPtr stmt);
1090 void emitFunclets(Emitter& e);
1092 struct FaultIterInfo {
1093 Id iterId;
1094 IterKind kind;
1097 void newFaultRegion(Offset start,
1098 Offset end,
1099 Label* entry,
1100 FaultIterInfo = FaultIterInfo { -1, KindOfIter });
1101 void newFaultRegion(StatementPtr stmt,
1102 Offset start,
1103 Offset end,
1104 Label* entry,
1105 FaultIterInfo = FaultIterInfo { -1, KindOfIter });
1106 void
1107 newFaultRegionAndFunclet(Offset start,
1108 Offset end,
1109 Thunklet* t,
1110 FaultIterInfo = FaultIterInfo { -1, KindOfIter });
1111 void
1112 newFaultRegionAndFunclet(StatementPtr stmt,
1113 Offset start,
1114 Offset end,
1115 Thunklet* t,
1116 FaultIterInfo = FaultIterInfo { -1, KindOfIter });
1118 void newFPIRegion(Offset start, Offset end, Offset fpOff);
1119 void copyOverCatchAndFaultRegions(FuncEmitter* fe);
1120 void copyOverFPIRegions(FuncEmitter* fe);
1121 void saveMaxStackCells(FuncEmitter* fe, int32_t stackPad);
1122 void finishFunc(Emitter& e, FuncEmitter* fe, int32_t stackPad);
1123 void initScalar(TypedValue& tvVal, ExpressionPtr val,
1124 folly::Optional<HeaderKind> ct = folly::none);
1125 bool requiresDeepInit(ExpressionPtr initExpr) const;
1127 void emitClassTraitPrecRule(PreClassEmitter* pce, TraitPrecStatementPtr rule);
1128 void emitClassTraitAliasRule(PreClassEmitter* pce,
1129 TraitAliasStatementPtr rule);
1130 void emitClassUseTrait(PreClassEmitter* pce, UseTraitStatementPtr useStmt);
1132 // Helper function for creating entries.
1133 RegionPtr createRegion(StatementPtr s, Region::Kind kind);
1134 // Enter/leave the passed in entry. Note that entries sometimes need be
1135 // to be constructed before they are entered, or need to be accessed
1136 // after they are left. This especially applies to constructs such
1137 // as loops and try blocks.
1138 void enterRegion(RegionPtr);
1139 void leaveRegion(RegionPtr);
1141 // Functions used for handling state IDs allocation.
1142 // FIXME (#3275259): This should be moved into global / func
1143 // body / fault funclet entries in order to optimize state
1144 // allocation. See the task description for more details.
1145 void registerControlTarget(ControlTarget* t);
1146 void unregisterControlTarget(ControlTarget* t);
1148 void registerReturn(StatementPtr s, Region* entry, char sym);
1149 void registerYieldAwait(ExpressionPtr e);
1150 ControlTargetPtr registerBreak(StatementPtr s, Region* entry, int depth,
1151 bool alloc);
1152 ControlTargetPtr registerContinue(StatementPtr s, Region* entry, int depth,
1153 bool alloc);
1154 ControlTargetPtr registerGoto(StatementPtr s, Region* entry,
1155 StringData* name, bool alloc);
1158 //=============================================================================
1159 // Emitter.
1161 #define InvariantViolation(...) do { \
1162 Logger::Warning(__VA_ARGS__); \
1163 Logger::Warning("Eval stack at the time of error: %s", \
1164 m_evalStack.pretty().c_str()); \
1165 assertx(false); \
1166 } while (0)
1169 * RAII guard for function creation.
1171 * This ensures that the eval stack's high water pointer is pointing
1172 * to the current function's maxStackCells (needed before we start
1173 * emitting bytecodes that manipulate the stack), and also that we
1174 * properly finish the function at the end of the scope.
1176 struct FuncFinisher {
1177 FuncFinisher(EmitterVisitor* ev, Emitter& e, FuncEmitter* fe,
1178 int32_t stackPad = 0)
1179 : m_ev(ev), m_e(e), m_fe(fe), m_stackPad(stackPad)
1181 assert(!ev->m_evalStack.m_actualStackHighWaterPtr ||
1182 ev->m_evalStack.m_actualStackHighWaterPtr == &fe->maxStackCells);
1183 ev->m_evalStack.m_actualStackHighWaterPtr = &fe->maxStackCells;
1186 ~FuncFinisher() {
1187 m_ev->finishFunc(m_e, m_fe, m_stackPad);
1190 void setStackPad(int32_t stackPad) {
1191 m_stackPad = stackPad;
1193 private:
1194 EmitterVisitor* m_ev;
1195 Emitter& m_e;
1196 FuncEmitter* m_fe;
1197 int32_t m_stackPad;
1200 // RAII guard for temporarily overriding an Emitter's location
1201 struct LocationGuard {
1202 LocationGuard(Emitter& e, const OptLocation& newLoc)
1203 : m_e(e), m_loc(e.getTempLocation()) {
1204 if (newLoc) m_e.setTempLocation(newLoc);
1206 ~LocationGuard() {
1207 m_e.setTempLocation(m_loc);
1210 private:
1211 Emitter& m_e;
1212 OptLocation m_loc;
1215 #define O(name, imm, pop, push, flags) \
1216 void Emitter::name(imm) { \
1217 auto const opcode = Op::name; \
1218 ITRACE(2, "{}\n", #name); \
1219 Trace::Indent indent; \
1220 ITRACE(3, "before: {}\n", m_ev.getEvalStack().pretty()); \
1221 /* Process opcode's effects on the EvalStack and emit it */ \
1222 Offset curPos UNUSED = getUnitEmitter().bcPos(); \
1224 Trace::Indent indent; \
1225 getEmitterVisitor().prepareEvalStack(); \
1226 char idxAPop UNUSED; \
1227 POP_##pop; \
1228 const int nIn UNUSED = COUNT_##pop; \
1229 POP_LA_##imm; \
1230 PUSH_##push; \
1231 getUnitEmitter().emitOp(Op##name); \
1232 IMPL_##imm; \
1234 ITRACE(3, "after: {}\n", m_ev.getEvalStack().pretty()); \
1235 auto& loc = m_tempLoc ? *m_tempLoc : m_node->getRange(); \
1236 auto UNUSED pc = m_ue.bc() + curPos; \
1237 ITRACE(2, "lines [{},{}] chars [{},{}]\n", \
1238 loc.line0, loc.line1, loc.char0, loc.char1); \
1239 /* Update various other metadata */ \
1240 getUnitEmitter().recordSourceLocation(loc, curPos); \
1241 if (flags & TF) { \
1242 getEmitterVisitor().restoreJumpTargetEvalStack(); \
1243 ITRACE(3, " jmp: {}\n", m_ev.getEvalStack().pretty()); \
1245 if (opcode == Op::FCall) getEmitterVisitor().recordCall(); \
1246 getEmitterVisitor().setPrevOpcode(opcode); \
1249 #define COUNT_NOV 0
1250 #define COUNT_ONE(t) 1
1251 #define COUNT_TWO(t1,t2) 2
1252 #define COUNT_THREE(t1,t2,t3) 3
1253 #define COUNT_FOUR(t1,t2,t3,t4) 4
1254 #define COUNT_MFINAL 0
1255 #define COUNT_F_MFINAL 0
1256 #define COUNT_C_MFINAL 0
1257 #define COUNT_V_MFINAL 0
1258 #define COUNT_FMANY 0
1259 #define COUNT_CVUMANY 0
1260 #define COUNT_CMANY 0
1261 #define COUNT_SMANY 0
1262 #define COUNT_IDX_A 0
1264 #define ONE(t) \
1265 DEC_##t a1
1266 #define TWO(t1, t2) \
1267 DEC_##t1 a1, DEC_##t2 a2
1268 #define THREE(t1, t2, t3) \
1269 DEC_##t1 a1, DEC_##t2 a2, DEC_##t3 a3
1270 #define FOUR(t1, t2, t3, t4) \
1271 DEC_##t1 a1, DEC_##t2 a2, DEC_##t3 a3, DEC_##t4 a4
1272 #define NA
1273 #define DEC_BLA std::vector<Label*>&
1274 #define DEC_SLA std::vector<StrOff>&
1275 #define DEC_ILA std::vector<IterPair>&
1276 #define DEC_IVA int32_t
1277 #define DEC_LA int32_t
1278 #define DEC_IA int32_t
1279 #define DEC_I64A int64_t
1280 #define DEC_DA double
1281 #define DEC_SA const StringData*
1282 #define DEC_RATA RepoAuthType
1283 #define DEC_AA ArrayData*
1284 #define DEC_BA Label&
1285 #define DEC_OA(type) type
1286 #define DEC_VSA std::vector<std::string>&
1287 #define DEC_KA MemberKey
1289 #define POP_NOV
1290 #define POP_ONE(t) \
1291 POP_##t(0)
1292 #define POP_TWO(t1, t2) \
1293 POP_##t1(0); \
1294 POP_##t2(1)
1295 #define POP_THREE(t1, t2, t3) \
1296 POP_##t1(0); \
1297 POP_##t2(1); \
1298 POP_##t3(2)
1299 #define POP_FOUR(t1, t2, t3, t4) \
1300 POP_##t1(0); \
1301 POP_##t2(1); \
1302 POP_##t3(2); \
1303 POP_##t4(3)
1304 #define POP_MFINAL \
1305 getEmitterVisitor().popEvalStackMMany()
1306 #define POP_F_MFINAL POP_MFINAL
1307 #define POP_C_MFINAL \
1308 getEmitterVisitor().popEvalStack(StackSym::C); \
1309 getEmitterVisitor().popEvalStackMMany()
1310 #define POP_V_MFINAL \
1311 getEmitterVisitor().popEvalStack(StackSym::V); \
1312 getEmitterVisitor().popEvalStackMMany()
1313 #define POP_FMANY \
1314 getEmitterVisitor().popEvalStackMany(a1, StackSym::F)
1315 #define POP_CVUMANY \
1316 getEmitterVisitor().popEvalStackCVMany(a1)
1317 #define POP_CMANY \
1318 getEmitterVisitor().popEvalStackMany(a1, StackSym::C)
1319 #define POP_SMANY \
1320 getEmitterVisitor().popEvalStackMany(a1.size(), StackSym::C)
1321 #define POP_IDX_A \
1322 idxAPop = getEmitterVisitor().getEvalStack().top(); \
1323 if (a2 == 1) getEmitterVisitor().popEvalStackCVMany(1); \
1324 getEmitterVisitor().popEvalStack(StackSym::A)
1326 #define POP_CV(i) getEmitterVisitor().popEvalStack(StackSym::C)
1327 #define POP_VV(i) getEmitterVisitor().popEvalStack(StackSym::V)
1328 #define POP_AV(i) getEmitterVisitor().popEvalStack(StackSym::A)
1329 #define POP_RV(i) getEmitterVisitor().popEvalStack(StackSym::R)
1330 #define POP_FV(i) getEmitterVisitor().popEvalStack(StackSym::F)
1332 // Pop of virtual "locs" on the stack that turn into immediates.
1333 #define POP_LA_ONE(t) \
1334 POP_LA_##t(nIn)
1335 #define POP_LA_TWO(t1, t2) \
1336 POP_LA_##t1(nIn); \
1337 POP_LA_##t2(nIn)
1338 #define POP_LA_THREE(t1, t2, t3) \
1339 POP_LA_##t1(nIn); \
1340 POP_LA_##t2(nIn); \
1341 POP_LA_##t3(nIn)
1342 #define POP_LA_FOUR(t1, t2, t3, t4) \
1343 POP_LA_##t1(nIn); \
1344 POP_LA_##t2(nIn); \
1345 POP_LA_##t3(nIn); \
1346 POP_LA_##t4(nIn)
1348 #define POP_LA_NA
1349 #define POP_LA_BLA(i)
1350 #define POP_LA_SLA(i)
1351 #define POP_LA_ILA(i)
1352 #define POP_LA_IVA(i)
1353 #define POP_LA_IA(i)
1354 #define POP_LA_I64A(i)
1355 #define POP_LA_DA(i)
1356 #define POP_LA_SA(i)
1357 #define POP_LA_RATA(i)
1358 #define POP_LA_AA(i)
1359 #define POP_LA_BA(i)
1360 #define POP_LA_IMPL(x)
1361 #define POP_LA_OA(i) POP_LA_IMPL
1362 #define POP_LA_VSA(i)
1363 #define POP_LA_KA(i)
1365 #define POP_LA_LA(i) \
1366 getEmitterVisitor().popSymbolicLocal(opcode)
1368 #define PUSH_NOV
1369 #define PUSH_ONE(t) \
1370 PUSH_##t
1371 #define PUSH_TWO(t1, t2) \
1372 PUSH_##t2; \
1373 PUSH_##t1
1374 #define PUSH_THREE(t1, t2, t3) \
1375 PUSH_##t3; \
1376 PUSH_##t2; \
1377 PUSH_##t1
1378 #define PUSH_FOUR(t1, t2, t3, t4) \
1379 PUSH_##t4; \
1380 PUSH_##t3; \
1381 PUSH_##t2; \
1382 PUSH_##t1
1383 #define PUSH_INS_1(t) PUSH_INS_1_##t
1384 #define PUSH_INS_2(t) PUSH_INS_2_##t
1386 #define PUSH_CV getEmitterVisitor().pushEvalStack(StackSym::C)
1387 #define PUSH_UV PUSH_CV
1388 #define PUSH_CUV PUSH_CV
1389 #define PUSH_VV getEmitterVisitor().pushEvalStack(StackSym::V)
1390 #define PUSH_AV getEmitterVisitor().pushEvalStack(StackSym::A)
1391 #define PUSH_RV getEmitterVisitor().pushEvalStack(StackSym::R)
1392 #define PUSH_FV getEmitterVisitor().pushEvalStack(StackSym::F)
1394 #define PUSH_INS_1_CV \
1395 getEmitterVisitor().getEvalStack().insertAt(1, StackSym::C);
1396 #define PUSH_INS_1_AV \
1397 getEmitterVisitor().getEvalStack().insertAt(1, StackSym::A);
1399 #define PUSH_INS_2_CV \
1400 getEmitterVisitor().getEvalStack().insertAt(2, StackSym::C);
1402 #define PUSH_IDX_A \
1403 if (a2 == 1) getEmitterVisitor().pushEvalStack(idxAPop);
1405 #define IMPL_NA
1406 #define IMPL_ONE(t) \
1407 IMPL1_##t
1408 #define IMPL_TWO(t1, t2) \
1409 IMPL1_##t1; \
1410 IMPL2_##t2
1411 #define IMPL_THREE(t1, t2, t3) \
1412 IMPL1_##t1; \
1413 IMPL2_##t2; \
1414 IMPL3_##t3
1415 #define IMPL_FOUR(t1, t2, t3, t4) \
1416 IMPL1_##t1; \
1417 IMPL2_##t2; \
1418 IMPL3_##t3; \
1419 IMPL4_##t4
1421 #define IMPL_BLA(var) do { \
1422 getUnitEmitter().emitInt32(var.size()); \
1423 for (unsigned int i = 0; i < var.size(); ++i) { \
1424 IMPL_BA(*var[i]); \
1426 } while (0)
1427 #define IMPL1_BLA IMPL_BLA(a1)
1428 #define IMPL2_BLA IMPL_BLA(a2)
1429 #define IMPL3_BLA IMPL_BLA(a3)
1430 #define IMPL4_BLA IMPL_BLA(a4)
1432 #define IMPL_ILA(var) do { \
1433 auto& ue = getUnitEmitter(); \
1434 ue.emitInt32(var.size()); \
1435 for (auto& i : var) { \
1436 ue.emitInt32(i.kind); \
1437 ue.emitInt32(i.id); \
1439 } while(0)
1440 #define IMPL1_ILA IMPL_ILA(a1)
1441 #define IMPL2_ILA IMPL_ILA(a2)
1442 #define IMPL3_ILA IMPL_ILA(a3)
1443 #define IMPL4_ILA IMPL_ILA(a4)
1445 #define IMPL_SLA(var) do { \
1446 auto& ue = getUnitEmitter(); \
1447 ue.emitInt32(var.size()); \
1448 for (auto& i : var) { \
1449 ue.emitInt32(i.str); \
1450 IMPL_BA(*i.dest); \
1452 } while (0)
1453 #define IMPL1_SLA IMPL_SLA(a1)
1454 #define IMPL2_SLA IMPL_SLA(a2)
1455 #define IMPL3_SLA IMPL_SLA(a3)
1457 #define IMPL_VSA(var) do { \
1458 auto n = var.size(); \
1459 getUnitEmitter().emitInt32(n); \
1460 for (size_t i = 0; i < n; ++i) { \
1461 IMPL_SA((HPHP::String(var[i])).get()); \
1463 } while (0)
1464 #define IMPL1_VSA IMPL_VSA(a1)
1465 #define IMPL2_VSA IMPL_VSA(a2)
1466 #define IMPL3_VSA IMPL_VSA(a3)
1467 #define IMPL4_VSA IMPL_VSA(a4)
1469 #define IMPL_IVA(var) do { \
1470 getUnitEmitter().emitIVA(var); \
1471 } while (0)
1472 #define IMPL1_IVA IMPL_IVA(a1)
1473 #define IMPL2_IVA IMPL_IVA(a2)
1474 #define IMPL3_IVA IMPL_IVA(a3)
1475 #define IMPL4_IVA IMPL_IVA(a4)
1477 #define IMPL1_LA IMPL_IVA(a1)
1478 #define IMPL2_LA IMPL_IVA(a2)
1479 #define IMPL3_LA IMPL_IVA(a3)
1480 #define IMPL4_LA IMPL_IVA(a4)
1482 #define IMPL1_IA IMPL_IVA(a1)
1483 #define IMPL2_IA IMPL_IVA(a2)
1484 #define IMPL3_IA IMPL_IVA(a3)
1485 #define IMPL4_IA IMPL_IVA(a4)
1487 #define IMPL_I64A(var) getUnitEmitter().emitInt64(var)
1488 #define IMPL1_I64A IMPL_I64A(a1)
1489 #define IMPL2_I64A IMPL_I64A(a2)
1490 #define IMPL3_I64A IMPL_I64A(a3)
1491 #define IMPL4_I64A IMPL_I64A(a4)
1493 #define IMPL_SA(var) \
1494 getUnitEmitter().emitInt32(getUnitEmitter().mergeLitstr(var))
1495 #define IMPL1_SA IMPL_SA(a1)
1496 #define IMPL2_SA IMPL_SA(a2)
1497 #define IMPL3_SA IMPL_SA(a3)
1498 #define IMPL4_SA IMPL_SA(a4)
1500 // Emitting RATAs isn't supported here right now. (They're only
1501 // created in hhbbc.)
1502 #define IMPL_RATA(var) not_reached()
1503 #define IMPL1_RATA IMPL_RATA(a1)
1504 #define IMPL2_RATA IMPL_RATA(a2)
1505 #define IMPL3_RATA IMPL_RATA(a3)
1506 #define IMPL4_RATA IMPL_RATA(a4)
1508 #define IMPL_AA(var) \
1509 getUnitEmitter().emitInt32(getUnitEmitter().mergeArray(var))
1510 #define IMPL1_AA IMPL_AA(a1)
1511 #define IMPL2_AA IMPL_AA(a2)
1512 #define IMPL3_AA IMPL_AA(a3)
1513 #define IMPL4_AA IMPL_AA(a4)
1515 #define IMPL_DA(var) getUnitEmitter().emitDouble(var)
1516 #define IMPL1_DA IMPL_DA(a1)
1517 #define IMPL2_DA IMPL_DA(a2)
1518 #define IMPL3_DA IMPL_DA(a3)
1519 #define IMPL4_DA IMPL_DA(a4)
1521 #define IMPL_BA(var) \
1522 if ((var).getAbsoluteOffset() == InvalidAbsoluteOffset) { \
1523 /* For forward jumps, we store information about the */ \
1524 /* current instruction in the Label. When the Label is */ \
1525 /* set, it will fix up any instructions that reference */ \
1526 /* it, and then it will call recordJumpTarget */ \
1527 (var).bind(getEmitterVisitor(), curPos, getUnitEmitter().bcPos()); \
1528 } else { \
1529 /* For backward jumps, we simply call recordJumpTarget */ \
1530 getEmitterVisitor().recordJumpTarget((var).getAbsoluteOffset()); \
1532 getUnitEmitter().emitInt32((var).getAbsoluteOffset() - curPos);
1533 #define IMPL1_BA IMPL_BA(a1)
1534 #define IMPL2_BA IMPL_BA(a2)
1535 #define IMPL3_BA IMPL_BA(a3)
1536 #define IMPL4_BA IMPL_BA(a4)
1538 #define IMPL_OA(var) getUnitEmitter().emitByte(static_cast<uint8_t>(var))
1539 #define IMPL1_OA(type) IMPL_OA(a1)
1540 #define IMPL2_OA(type) IMPL_OA(a2)
1541 #define IMPL3_OA(type) IMPL_OA(a3)
1542 #define IMPL4_OA(type) IMPL_OA(a4)
1544 #define IMPL_KA(var) encode_member_key(var, getUnitEmitter())
1545 #define IMPL1_KA IMPL_KA(a1)
1546 #define IMPL2_KA IMPL_KA(a2)
1547 #define IMPL3_KA IMPL_KA(a3)
1548 #define IMPL4_KA IMPL_KA(a4)
1550 OPCODES
1552 #undef O
1553 #undef ONE
1554 #undef TWO
1555 #undef THREE
1556 #undef FOUR
1557 #undef NA
1558 #undef DEC_IVA
1559 #undef DEC_LA
1560 #undef DEC_IA
1561 #undef DEC_I64A
1562 #undef DEC_DA
1563 #undef DEC_SA
1564 #undef DEC_RATA
1565 #undef DEC_AA
1566 #undef DEC_BA
1567 #undef DEC_OA
1568 #undef DEC_KA
1569 #undef POP_NOV
1570 #undef POP_ONE
1571 #undef POP_TWO
1572 #undef POP_THREE
1573 #undef POP_FOUR
1574 #undef POP_MFINAL
1575 #undef POP_F_MFINAL
1576 #undef POP_C_MFINAL
1577 #undef POP_V_MFINAL
1578 #undef POP_CV
1579 #undef POP_VV
1580 #undef POP_HV
1581 #undef POP_AV
1582 #undef POP_RV
1583 #undef POP_FV
1584 #undef POP_LREST
1585 #undef POP_FMANY
1586 #undef POP_CVUMANY
1587 #undef POP_CMANY
1588 #undef POP_SMANY
1589 #undef POP_IDX_A
1590 #undef POP_LA_ONE
1591 #undef POP_LA_TWO
1592 #undef POP_LA_THREE
1593 #undef POP_LA_FOUR
1594 #undef POP_LA_NA
1595 #undef POP_LA_IVA
1596 #undef POP_LA_IA
1597 #undef POP_LA_I64A
1598 #undef POP_LA_DA
1599 #undef POP_LA_SA
1600 #undef POP_LA_RATA
1601 #undef POP_LA_AA
1602 #undef POP_LA_BA
1603 #undef POP_LA_IMPL
1604 #undef POP_LA_OA
1605 #undef POP_LA_LA
1606 #undef POP_LA_KA
1607 #undef PUSH_NOV
1608 #undef PUSH_ONE
1609 #undef PUSH_TWO
1610 #undef PUSH_THREE
1611 #undef PUSH_FOUR
1612 #undef PUSH_CV
1613 #undef PUSH_UV
1614 #undef PUSH_CUV
1615 #undef PUSH_VV
1616 #undef PUSH_HV
1617 #undef PUSH_AV
1618 #undef PUSH_RV
1619 #undef PUSH_FV
1620 #undef PUSH_IDX_A
1621 #undef IMPL_ONE
1622 #undef IMPL_TWO
1623 #undef IMPL_THREE
1624 #undef IMPL_FOUR
1625 #undef IMPL_NA
1626 #undef IMPL_BLA
1627 #undef IMPL1_BLA
1628 #undef IMPL2_BLA
1629 #undef IMPL3_BLA
1630 #undef IMPL4_BLA
1631 #undef IMPL_SLA
1632 #undef IMPL1_SLA
1633 #undef IMPL2_SLA
1634 #undef IMPL3_SLA
1635 #undef IMPL4_SLA
1636 #undef IMPL_ILA
1637 #undef IMPL1_ILA
1638 #undef IMPL2_ILA
1639 #undef IMPL3_ILA
1640 #undef IMPL4_ILA
1641 #undef IMPL_IVA
1642 #undef IMPL1_IVA
1643 #undef IMPL2_IVA
1644 #undef IMPL3_IVA
1645 #undef IMPL4_IVA
1646 #undef IMPL1_LA
1647 #undef IMPL2_LA
1648 #undef IMPL3_LA
1649 #undef IMPL4_LA
1650 #undef IMPL1_IA
1651 #undef IMPL2_IA
1652 #undef IMPL3_IA
1653 #undef IMPL4_IA
1654 #undef IMPL_I64A
1655 #undef IMPL1_I64A
1656 #undef IMPL2_I64A
1657 #undef IMPL3_I64A
1658 #undef IMPL4_I64A
1659 #undef IMPL_DA
1660 #undef IMPL1_DA
1661 #undef IMPL2_DA
1662 #undef IMPL3_DA
1663 #undef IMPL4_DA
1664 #undef IMPL_SA
1665 #undef IMPL1_SA
1666 #undef IMPL2_SA
1667 #undef IMPL3_SA
1668 #undef IMPL4_SA
1669 #undef IMPL_RATA
1670 #undef IMPL1_RATA
1671 #undef IMPL2_RATA
1672 #undef IMPL3_RATA
1673 #undef IMPL4_RATA
1674 #undef IMPL_AA
1675 #undef IMPL1_AA
1676 #undef IMPL2_AA
1677 #undef IMPL3_AA
1678 #undef IMPL4_AA
1679 #undef IMPL_BA
1680 #undef IMPL1_BA
1681 #undef IMPL2_BA
1682 #undef IMPL3_BA
1683 #undef IMPL4_BA
1684 #undef IMPL_OA
1685 #undef IMPL1_OA
1686 #undef IMPL2_OA
1687 #undef IMPL3_OA
1688 #undef IMPL4_OA
1689 #undef IMPL_KA
1690 #undef IMPL1_KA
1691 #undef IMPL2_KA
1692 #undef IMPL3_KA
1693 #undef IMPL4_KA
1695 static void checkJmpTargetEvalStack(const SymbolicStack& source,
1696 const SymbolicStack& dest) {
1697 if (source.size() != dest.size()) {
1698 Logger::FWarning("Emitter detected a point in the bytecode where the "
1699 "depth of the stack is not the same for all possible "
1700 "control flow paths. source size: {}. dest size: {}",
1701 source.size(),
1702 dest.size());
1703 Logger::Warning("src stack : %s", source.pretty().c_str());
1704 Logger::Warning("dest stack: %s", dest.pretty().c_str());
1705 assertx(false);
1706 return;
1709 for (unsigned int i = 0; i < source.size(); ++i) {
1710 char flavor = StackSym::GetSymFlavor(source.get(i));
1711 bool matches = source.get(i) == dest.get(i) &&
1712 (flavor != StackSym::L || source.getLoc(i) == dest.getLoc(i)) &&
1713 (flavor != StackSym::T || source.getName(i) == dest.getName(i)) &&
1714 (flavor != StackSym::I || source.getInt(i) == dest.getInt(i));
1715 if (!matches) {
1716 Logger::Warning("Emitter detected a point in the bytecode where the "
1717 "symbolic flavor of a slot on the stack is not the same "
1718 "for all possible control flow paths");
1719 Logger::Warning("src stack : %s", source.pretty().c_str());
1720 Logger::Warning("dest stack: %s", dest.pretty().c_str());
1721 assert(false);
1722 return;
1727 std::string SymbolicStack::SymEntry::pretty() const {
1728 std::string ret;
1729 ret += StackSym::ToString(sym);
1730 char flavor = StackSym::GetSymFlavor(sym);
1731 if (flavor == StackSym::L || flavor == StackSym::I) {
1732 folly::toAppend(':', intval, &ret);
1733 } else if (flavor == StackSym::T && name) {
1734 folly::toAppend(':', name->data(), &ret);
1736 return ret;
1739 std::string SymbolicStack::pretty() const {
1740 std::ostringstream out;
1741 out << "[" << std::hex;
1742 size_t j = 0;
1743 auto sep = "";
1744 for (size_t i = 0; i < m_symStack.size(); ++i) {
1745 out << sep;
1746 sep = " ";
1747 while (j < m_actualStack.size() && m_actualStack[j] < int(i)) {
1748 ++j;
1750 if (j < m_actualStack.size() && m_actualStack[j] == int(i)) {
1751 out << "*";
1753 out << m_symStack[i].pretty();
1755 out << ']';
1756 return out.str();
1759 void SymbolicStack::updateHighWater() {
1760 *m_actualStackHighWaterPtr =
1761 std::max(*m_actualStackHighWaterPtr,
1762 static_cast<int>(m_actualStack.size() + m_fdescCount));
1765 void SymbolicStack::push(char sym) {
1766 if (!StackSym::IsSymbolic(sym)) {
1767 m_actualStack.push_back(m_symStack.size());
1768 updateHighWater();
1770 m_symStack.push_back(SymEntry(sym));
1771 ITRACE(4, "push: {}\n", m_symStack.back().pretty());
1774 void SymbolicStack::pop() {
1775 // TODO(drew): assert eval stack unknown is false?
1776 assert(!m_symStack.empty());
1777 char sym = m_symStack.back().sym;
1778 char flavor = StackSym::GetSymFlavor(sym);
1779 if (StackSym::GetMarker(sym) != StackSym::W &&
1780 flavor != StackSym::L && flavor != StackSym::T && flavor != StackSym::I &&
1781 flavor != StackSym::H) {
1782 assert(!m_actualStack.empty());
1783 m_actualStack.pop_back();
1785 ITRACE(4, "pop: {}\n", m_symStack.back().pretty());
1786 m_symStack.pop_back();
1789 char SymbolicStack::top() const {
1790 assert(!m_symStack.empty());
1791 return m_symStack.back().sym;
1794 char SymbolicStack::get(int index) const {
1795 assert(index >= 0 && index < (int)m_symStack.size());
1796 return m_symStack[index].sym;
1799 const StringData* SymbolicStack::getName(int index) const {
1800 assert(index >= 0 && index < (int)m_symStack.size());
1801 return m_symStack[index].name;
1804 const StringData* SymbolicStack::getClsName(int index) const {
1805 assert(index >= 0 && index < (int)m_symStack.size());
1806 return m_symStack[index].className;
1809 bool SymbolicStack::isCls(int index) const {
1810 assert(index >= 0 && index < (int)m_symStack.size());
1811 return m_symStack[index].className != nullptr;
1814 void SymbolicStack::setString(const StringData* s) {
1815 assert(m_symStack.size());
1816 SymEntry& se = m_symStack.back();
1817 assert(!se.name || se.name == s);
1818 se.name = s;
1821 void SymbolicStack::setKnownCls(const StringData* s, bool nonNull) {
1822 assert(m_symStack.size());
1823 SymEntry& se = m_symStack.back();
1824 assert(!se.className || se.className == s);
1825 se.className = s;
1828 void SymbolicStack::setInt(int64_t v) {
1829 assert(m_symStack.size());
1830 m_symStack.back().intval = v;
1833 void SymbolicStack::cleanTopMeta() {
1834 SymEntry& se = m_symStack.back();
1835 se.clsBaseType = CLS_INVALID;
1836 se.name = nullptr;
1839 void SymbolicStack::setClsBaseType(ClassBaseType type) {
1840 assert(!m_symStack.empty());
1841 m_symStack.back().clsBaseType = type;
1844 void SymbolicStack::setUnnamedLocal(int index,
1845 int localId,
1846 Offset startOff) {
1847 assert(size_t(index) < m_symStack.size());
1848 assert(m_symStack[index].sym == StackSym::K);
1849 assert(m_symStack[index].clsBaseType == CLS_UNNAMED_LOCAL);
1850 m_symStack[index].intval = localId;
1851 m_symStack[index].unnamedLocalStart = startOff;
1854 void SymbolicStack::set(int index, char sym) {
1855 assert(index >= 0 && index < (int)m_symStack.size());
1856 // XXX Add assert in debug build to make sure W is not getting
1857 // written or overwritten by something else
1858 m_symStack[index].sym = sym;
1859 ITRACE(4, " set: {} -> {}\n", index, m_symStack[index].pretty());
1862 size_t SymbolicStack::size() const {
1863 return m_symStack.size();
1866 size_t SymbolicStack::actualSize() const {
1867 return m_actualStack.size();
1870 bool SymbolicStack::empty() const {
1871 return m_symStack.empty();
1874 void SymbolicStack::clear() {
1875 m_symStack.clear();
1876 m_actualStack.clear();
1877 m_fdescCount = 0;
1880 void SymbolicStack::consumeBelowTop(int depth) {
1881 if (int(m_symStack.size()) < depth + 1) {
1882 Logger::Warning(
1883 "Emitter tried to consumeBelowTop() when the symbolic "
1884 "stack did not have enough elements in it.");
1885 assert(false);
1886 return;
1888 assert(int(m_symStack.size()) >= depth + 1);
1889 int index = m_symStack.size() - depth - 1;
1890 m_symStack.erase(m_symStack.begin() + index);
1893 * Update any indexes into the actual stack that pointed to or past
1894 * this element.
1896 * (In practice they should all be past---we don't currently ever
1897 * remove below the top for actual stack elements.)
1899 for (size_t i = 0; i < m_actualStack.size(); ++i) {
1900 if (m_actualStack[i] >= index) {
1901 --m_actualStack[i];
1906 int SymbolicStack::getActualPos(int vpos) const {
1907 assert(vpos >= 0 && vpos < int(m_symStack.size()));
1908 assert(!m_actualStack.empty());
1909 for (int j = int(m_actualStack.size()) - 1; j >= 0; --j) {
1910 if (m_actualStack[j] == vpos) {
1911 return j;
1914 not_reached();
1917 char SymbolicStack::getActual(int index) const {
1918 assert(index >= 0 && index < (int)m_actualStack.size());
1919 return get(m_actualStack[index]);
1922 void SymbolicStack::setActual(int index, char sym) {
1923 assert(index >= 0 && index < (int)m_actualStack.size());
1924 set(m_actualStack[index], sym);
1927 SymbolicStack::ClassBaseType
1928 SymbolicStack::getClsBaseType(int index) const {
1929 assert(m_symStack.size() > size_t(index));
1930 assert(m_symStack[index].sym == StackSym::K);
1931 assert(m_symStack[index].clsBaseType != CLS_INVALID);
1932 return m_symStack[index].clsBaseType;
1935 int SymbolicStack::getLoc(int index) const {
1936 assert(m_symStack.size() > size_t(index));
1937 assert(StackSym::GetSymFlavor(m_symStack[index].sym) == StackSym::L ||
1938 m_symStack[index].clsBaseType == CLS_NAMED_LOCAL ||
1939 m_symStack[index].clsBaseType == CLS_UNNAMED_LOCAL);
1940 assert(m_symStack[index].intval != -1);
1941 return m_symStack[index].intval;
1944 int64_t SymbolicStack::getInt(int index) const {
1945 assert(m_symStack.size() > size_t(index));
1946 assert(StackSym::GetSymFlavor(m_symStack[index].sym) == StackSym::I);
1947 return m_symStack[index].intval;
1950 Offset SymbolicStack::getUnnamedLocStart(int index) const {
1951 assert(m_symStack.size() > size_t(index));
1952 assert(m_symStack[index].sym == StackSym::K);
1953 assert(m_symStack[index].clsBaseType == CLS_UNNAMED_LOCAL);
1954 return m_symStack[index].unnamedLocalStart;
1957 // Insert an element in the actual stack at the specified depth of the
1958 // actual stack.
1959 void SymbolicStack::insertAt(int depth, char sym) {
1960 assert(depth <= sizeActual() && depth > 0);
1961 int virtIdx = m_actualStack[sizeActual() - depth];
1963 m_symStack.insert(m_symStack.begin() + virtIdx, SymEntry(sym));
1964 m_actualStack.insert(m_actualStack.end() - depth, virtIdx);
1966 for (size_t i = sizeActual() - depth + 1; i < m_actualStack.size(); ++i) {
1967 ++m_actualStack[i];
1971 int SymbolicStack::sizeActual() const {
1972 return m_actualStack.size();
1975 void SymbolicStack::pushFDesc() {
1976 m_fdescCount += kNumActRecCells;
1977 updateHighWater();
1980 void SymbolicStack::popFDesc() {
1981 m_fdescCount -= kNumActRecCells;
1984 void Label::set(Emitter& e) {
1985 if (isSet()) {
1986 InvariantViolation(
1987 "Label::set was called more than once on the same "
1988 "Label; originally set to %d; now %d",
1989 m_off,
1990 e.getUnitEmitter().bcPos());
1991 return;
1993 m_off = e.getUnitEmitter().bcPos();
1994 // Fix up any forward jumps that reference to this Label
1995 for (std::vector<std::pair<Offset, Offset> >::const_iterator it =
1996 m_emittedOffs.begin(); it != m_emittedOffs.end(); ++it) {
1997 e.getUnitEmitter().emitInt32(m_off - it->first, it->second);
1999 EmitterVisitor& ev = e.getEmitterVisitor();
2000 if (!m_emittedOffs.empty()) {
2001 // If there were forward jumps that referenced this Label,
2002 // compare the the eval stack from the first foward jump we
2003 // saw with the current eval stack
2004 if (!ev.evalStackIsUnknown()) {
2005 checkJmpTargetEvalStack(m_evalStack, ev.getEvalStack());
2006 } else {
2007 // Assume the current eval stack matches that of the forward branch
2008 ITRACE(3, "bind: {}\n", m_evalStack.pretty());
2009 ev.setEvalStack(m_evalStack);
2011 // Fix up the EmitterVisitor's table of jump targets
2012 ev.recordJumpTarget(m_off, ev.getEvalStack());
2013 } else {
2014 // There were no forward jumps that referenced this label
2015 ev.prepareEvalStack();
2016 // Fix up the EmitterVisitor's table of jump targets
2017 ev.recordJumpTarget(m_off, ev.getEvalStack());
2021 bool Label::isUsed() {
2022 return (m_off != InvalidAbsoluteOffset || !m_emittedOffs.empty());
2025 void Label::bind(EmitterVisitor& ev, Offset instrAddr, Offset offAddr) {
2026 if (m_off != InvalidAbsoluteOffset) {
2027 InvariantViolation("Label::bind was called on a Label that has already "
2028 "been set to %d",
2029 m_off);
2030 return;
2032 bool labelHasEvalStack = !m_emittedOffs.empty();
2033 m_emittedOffs.push_back(std::pair<Offset, Offset>(instrAddr, offAddr));
2034 if (labelHasEvalStack) {
2035 checkJmpTargetEvalStack(m_evalStack, ev.getEvalStack());
2036 } else {
2037 m_evalStack = ev.getEvalStack();
2041 struct FPIRegionRecorder {
2042 FPIRegionRecorder(EmitterVisitor* ev, UnitEmitter& ue, SymbolicStack& stack,
2043 Offset start)
2044 : m_ev(ev), m_ue(ue), m_stack(stack), m_fpOff(m_stack.sizeActual()),
2045 m_start(start) {
2046 m_stack.pushFDesc();
2048 ~FPIRegionRecorder() {
2049 m_stack.popFDesc();
2050 m_ev->newFPIRegion(m_start, m_ue.bcPos(), m_fpOff);
2052 private:
2053 EmitterVisitor* m_ev;
2054 UnitEmitter& m_ue;
2055 SymbolicStack& m_stack;
2056 int m_fpOff;
2057 Offset m_start;
2060 //=============================================================================
2061 // ControlTarget.
2063 const int ControlTarget::k_unsetState = -1;
2065 ControlTarget::ControlTarget(EmitterVisitor* router)
2066 : m_visitor(router), m_label(), m_state(k_unsetState) {
2067 assert(m_visitor != nullptr);
2070 ControlTarget::~ControlTarget() {
2071 // The scope of states used in finally router is controled
2072 // using shared pointer refcounting. State numbers can be reused once
2073 // all the references are released.
2074 if (isRegistered()) {
2075 m_visitor->unregisterControlTarget(this);
2076 m_state = k_unsetState;
2080 bool ControlTarget::isRegistered() {
2081 return m_state != k_unsetState;
2084 //=============================================================================
2085 // Region.
2087 Region::Region(Region::Kind kind, RegionPtr parent)
2088 : m_kind(kind),
2089 m_iterId(-1),
2090 m_iterKind(KindOfIter),
2091 m_parent(parent) {
2094 void
2095 EmitterVisitor::registerReturn(StatementPtr s, Region* region, char sym) {
2096 ControlTargetPtr t;
2097 Region* r;
2098 for (r = region; true; r = r->m_parent.get()) {
2099 assert(r);
2100 if (r->isFinally()) {
2101 throw EmitterVisitor::IncludeTimeFatalException(s,
2102 "Return inside a finally block is not supported");
2104 if (r->m_returnTargets.count(sym)) {
2105 // We registered the control target before. Just return the existing one.
2106 t = r->m_returnTargets[sym].target;
2107 break;
2109 // Haven't registered the control target with region r yet.
2110 if (r->m_parent.get() == nullptr) {
2111 // Top of the region hierarchy - allocate a fresh control target.
2112 t = std::make_shared<ControlTarget>(this);
2113 r = r->m_parent.get();
2114 break;
2117 assert(t != nullptr);
2118 if (!t->isRegistered()) {
2119 registerControlTarget(t.get());
2121 // For all entries we visited that did not have this control target in
2122 // m_returnTargets, add this control target to these entries' m_returnTargets
2123 // fields as appropriate.
2124 Region* end = r;
2125 for (r = region; r != end; r = r->m_parent.get()) {
2126 r->m_returnTargets[sym] = ControlTargetInfo(t, r->isTryFinally());
2130 ControlTargetPtr
2131 EmitterVisitor::registerGoto(StatementPtr s, Region* region, StringData* name,
2132 bool alloc) {
2133 ControlTargetPtr t;
2134 Region* r;
2135 for (r = region; true; r = r->m_parent.get()) {
2136 assert(r);
2137 if (r->m_gotoTargets.count(name)) {
2138 // We registered the control target before. Just return the existing one.
2139 t = r->m_gotoTargets[name].target;
2140 if (alloc && r->isTryFinally()) {
2141 r->m_gotoTargets[name].used = true;
2143 break;
2145 // Haven't registered the control target in this region yet.
2146 if (r->m_parent.get() == nullptr) {
2147 // Top of the region hierarchy - allocate a fresh control target.
2148 t = std::make_shared<ControlTarget>(this);
2149 r = r->m_parent.get();
2150 break;
2153 assert(t != nullptr);
2154 if (alloc && !t->isRegistered()) {
2155 registerControlTarget(t.get());
2157 // For all entries we visited that did not have this control target in
2158 // m_gotoTargets, add this control target to these entries' m_gotoTargets
2159 // fields as appropriate.
2160 Region* end = r;
2161 for (r = region; r != end; r = r->m_parent.get()) {
2162 r->m_gotoTargets[name] = ControlTargetInfo(t, alloc && r->isTryFinally());
2164 return t;
2167 void EmitterVisitor::registerYieldAwait(ExpressionPtr e) {
2168 Region* region = m_regions.back().get();
2169 for (; region; region = region->m_parent.get()) {
2170 if (region->isFinally()) {
2171 throw EmitterVisitor::IncludeTimeFatalException(e,
2172 "Yield expression inside a finally block is not supported");
2177 ControlTargetPtr
2178 EmitterVisitor::registerBreak(StatementPtr s, Region* region, int depth,
2179 bool alloc) {
2180 ControlTargetPtr t;
2181 assert(depth >= 1);
2182 int d = depth;
2183 Region* r;
2184 for (r = region; true; r = r->m_parent.get()) {
2185 assert(r);
2186 if (r->isFinally()) {
2187 throw EmitterVisitor::IncludeTimeFatalException(s,
2188 "Break jump is not allowed to leave a finally block");
2190 if (r->m_breakTargets.count(d)) {
2191 // We registered the control target before. Just return the existing one.
2192 t = r->m_breakTargets[d].target;
2193 if (alloc && r->isTryFinally()) {
2194 r->m_breakTargets[d].used = true;
2196 break;
2198 if (r->m_kind != Region::Kind::LoopOrSwitch) {
2199 continue;
2201 if (d == 1) {
2202 // We should never reach this case if alloc == true, since the loop or
2203 // switch should have registered its break target in advance
2204 assert(!alloc);
2205 // If this is a loop, and depth is one, just allocate a fresh
2206 // control target, since there are no more entries to delegate to.
2207 t = std::make_shared<ControlTarget>(this);
2208 r = r->m_parent.get();
2209 break;
2211 // Otherwise, delegate to the parent. One break level has been
2212 // taken care of by this region.
2213 --d;
2215 assert(t != nullptr);
2216 if (alloc) {
2217 if (!t->isRegistered()) {
2218 registerControlTarget(t.get());
2221 // For all of the entries that did not have this control target in
2222 // m_breakTargets, add this control target to these entries' m_breakTargets
2223 // fields as appropriate.
2224 Region* end = r;
2225 for (r = region; r != end; r = r->m_parent.get()) {
2226 r->m_breakTargets[depth] =
2227 ControlTargetInfo(t, alloc && r->isTryFinally());
2228 if (r->m_kind == Region::Kind::LoopOrSwitch) {
2229 --depth;
2232 return t;
2235 ControlTargetPtr
2236 EmitterVisitor::registerContinue(StatementPtr s, Region* region, int depth,
2237 bool alloc) {
2238 ControlTargetPtr t;
2239 assert(depth >= 1);
2240 int d = depth;
2241 Region* r;
2242 for (r = region; true; r = r->m_parent.get()) {
2243 assert(r);
2244 if (r->isFinally()) {
2245 throw EmitterVisitor::IncludeTimeFatalException(s,
2246 "Continue jump is not allowed to leave a finally block");
2248 if (r->m_continueTargets.count(d)) {
2249 // We registered the control target before. Just return the existing one.
2250 t = r->m_continueTargets[d].target;
2251 if (alloc && r->isTryFinally()) {
2252 r->m_continueTargets[d].used = true;
2254 break;
2256 if (r->m_kind != Region::Kind::LoopOrSwitch) {
2257 continue;
2259 if (d == 1) {
2260 // We should never reach this case if alloc == true, since the loop or
2261 // switch should have registered its continue target in advance
2262 assert(!alloc);
2263 t = std::make_shared<ControlTarget>(this);
2264 r = r->m_parent.get();
2265 break;
2267 // Otherwise, delegate to the parent. One continue level has been
2268 // taken care of by this region.
2269 --d;
2271 assert(t != nullptr);
2272 if (alloc && !t->isRegistered()) {
2273 registerControlTarget(t.get());
2275 // For all of the entries that did not have this control target in
2276 // m_continueTargets, add this control target to these entries'
2277 // m_continueTargets fields as appropriate.
2278 Region* end = r;
2279 for (r = region; r != end; r = r->m_parent.get()) {
2280 r->m_continueTargets[depth] =
2281 ControlTargetInfo(t, alloc && r->isTryFinally());
2282 if (r->m_kind == Region::Kind::LoopOrSwitch) {
2283 --depth;
2286 return t;
2289 int Region::getCaseCount() {
2290 int count = 1; // The fall-through case.
2291 for (auto& t : m_returnTargets) {
2292 if (t.second.target->isRegistered()) ++count;
2294 for (auto& t : m_breakTargets) {
2295 if (t.second.target->isRegistered()) ++count;
2297 for (auto& t : m_continueTargets) {
2298 if (t.second.target->isRegistered()) ++count;
2300 for (auto& t : m_gotoTargets) {
2301 if (t.second.target->isRegistered()) ++count;
2303 return count;
2306 void EmitterVisitor::emitIterFree(Emitter& e, IterVec& iters) {
2307 for (auto& iter : iters) {
2308 assert(iter.id != -1);
2309 if (iter.kind == KindOfMIter) {
2310 e.MIterFree(iter.id);
2311 } else {
2312 assert(iter.kind == KindOfIter);
2313 e.IterFree(iter.id);
2318 void EmitterVisitor::emitJump(Emitter& e, IterVec& iters, Label& target) {
2319 if (!iters.empty()) {
2320 e.IterBreak(target, iters);
2321 iters.clear();
2322 } else {
2323 e.Jmp(target);
2327 void EmitterVisitor::emitReturn(Emitter& e, char sym, StatementPtr s) {
2328 Region* region = m_regions.back().get();
2329 registerReturn(s, region, sym);
2330 assert(getEvalStack().size() == 1);
2331 assert(region->m_returnTargets.count(sym));
2332 IterVec iters;
2333 for (Region* r = region; true; r = r->m_parent.get()) {
2334 auto& t = r->m_returnTargets[sym].target;
2335 if (r->m_parent == nullptr) {
2336 // At the top of the hierarchy, no more finally blocks to run.
2337 // Check return type, free pending iterators and actually return.
2338 if (sym == StackSym::C) {
2339 if (shouldEmitVerifyRetType()) {
2340 e.VerifyRetTypeC();
2342 // IterFree must come after VerifyRetType, because VerifyRetType may
2343 // throw, in which case any Iters will be freed by the fault funclet.
2344 emitIterFree(e, iters);
2345 e.RetC();
2346 } else {
2347 assert(sym == StackSym::V);
2348 if (shouldEmitVerifyRetType()) {
2349 e.VerifyRetTypeV();
2351 emitIterFree(e, iters);
2352 e.RetV();
2354 return;
2357 if (r->isTryFinally()) {
2358 // We encountered a try block - a finally needs to be run
2359 // before returning.
2360 Id stateLocal = getStateLocal();
2361 Id retLocal = getRetLocal();
2362 // Set the unnamed "state" local to the appropriate identifier
2363 emitVirtualLocal(retLocal);
2364 assert(t->isRegistered());
2365 e.Int(t->m_state);
2366 e.SetL(stateLocal);
2367 e.PopC();
2368 // Emit code stashing the current return value in the "ret" unnamed
2369 // local
2370 if (sym == StackSym::C) {
2371 // For legacy purposes, SetL expects its immediate argument to
2372 // be present on the symbolic stack. In reality, retLocal is
2373 // an immediate argument. The following pop and push instructions
2374 // ensure that the arguments are place on the symbolic stack
2375 // in a correct order. In reality the following three calls are
2376 // a no-op.
2377 popEvalStack(StackSym::C);
2378 emitVirtualLocal(retLocal);
2379 pushEvalStack(StackSym::C);
2380 e.SetL(retLocal);
2381 e.PopC();
2382 } else {
2383 assert(sym == StackSym::V);
2384 popEvalStack(StackSym::V);
2385 emitVirtualLocal(retLocal);
2386 pushEvalStack(StackSym::V);
2387 e.BindL(retLocal);
2388 e.PopV();
2390 emitJump(e, iters, r->m_finallyLabel);
2391 return;
2393 if (r->isForeach()) {
2394 iters.push_back(IterPair(r->m_iterKind, r->m_iterId));
2399 void EmitterVisitor::emitGoto(Emitter& e, StringData* name, StatementPtr s) {
2400 Region* region = m_regions.back().get();
2401 registerGoto(s, region, name, true);
2402 assert(region->m_gotoTargets.count(name));
2403 IterVec iters;
2404 for (Region* r = region; true; r = r->m_parent.get()) {
2405 auto t = r->m_gotoTargets[name].target;
2406 if (r->m_gotoLabels.count(name)) {
2407 // If only the destination label is within the statement
2408 // associated with the current statement, just perform a
2409 // direct jump. Free the pending iterators on the way.
2410 emitJump(e, iters, t->m_label);
2411 return;
2413 if (r->isFinally()) {
2414 throw EmitterVisitor::IncludeTimeFatalException(s,
2415 "Goto to a label outside a finally block is not supported");
2417 if (r->isTryFinally()) {
2418 // We came across a try region, need to run a finally block.
2419 // Store appropriate value inside the state local.
2420 Id stateLocal = getStateLocal();
2421 emitVirtualLocal(stateLocal);
2422 assert(t->isRegistered());
2423 e.Int(t->m_state);
2424 e.SetL(stateLocal);
2425 e.PopC();
2426 // Jump to the finally block and free any pending iterators on the
2427 // way.
2428 emitJump(e, iters, r->m_finallyLabel);
2429 return;
2431 if (r->isForeach()) {
2432 iters.push_back(IterPair(r->m_iterKind, r->m_iterId));
2437 void EmitterVisitor::emitBreak(Emitter& e, int depth, StatementPtr s) {
2438 Region* region = m_regions.back().get();
2439 registerBreak(s, region, depth, true);
2440 assert(depth >= 1);
2441 assert(!region->isFinally());
2442 assert(region->m_parent != nullptr);
2443 assert(region->m_breakTargets.count(depth));
2444 IterVec iters;
2446 for (Region* r = region; true; r = r->m_parent.get()) {
2447 auto t = r->m_breakTargets[depth].target;
2448 if (r->isTryFinally()) {
2449 // Encountered a try block, need to run finally.
2450 assert(r->m_breakTargets.count(depth));
2451 assert(t->isRegistered());
2452 Id stateLocal = getStateLocal();
2453 emitVirtualLocal(stateLocal);
2454 e.Int(t->m_state);
2455 e.SetL(stateLocal);
2456 e.PopC();
2457 emitJump(e, iters, r->m_finallyLabel);
2458 return;
2460 if (r->m_kind != Region::Kind::LoopOrSwitch) {
2461 continue;
2463 // Free iterator for the current loop whether or not
2464 // this is the last loop that we jump out of.
2465 if (r->isForeach()) {
2466 iters.push_back(IterPair(r->m_iterKind, r->m_iterId));
2468 if (depth == 1) {
2469 // Last loop to jumpt out of. Performa direct jump to the
2470 // break lable and free any pending iterators left.
2471 emitJump(e, iters, t->m_label);
2472 return;
2474 --depth;
2478 void EmitterVisitor::emitContinue(Emitter& e, int depth, StatementPtr s) {
2479 Region* region = m_regions.back().get();
2480 registerContinue(s, region, depth, true);
2481 assert(depth >= 1);
2482 assert(!region->isFinally());
2483 assert(region->m_parent != nullptr);
2484 assert(region->m_continueTargets.count(depth));
2485 IterVec iters;
2487 for (Region* r = region; true; r = r->m_parent.get()) {
2488 auto t = r->m_continueTargets[depth].target;
2489 if (r->isTryFinally()) {
2490 // Encountered a try block, need to run finally.
2491 assert(r->m_continueTargets.count(depth));
2492 Id stateLocal = getStateLocal();
2493 emitVirtualLocal(stateLocal);
2494 assert(t->isRegistered());
2495 e.Int(t->m_state);
2496 e.SetL(stateLocal);
2497 e.PopC();
2498 emitJump(e, iters, r->m_finallyLabel);
2499 return;
2501 if (r->m_kind != Region::Kind::LoopOrSwitch) {
2502 continue;
2504 if (depth == 1) {
2505 // Last level. Don't free the iterator for the current loop
2506 // however free any earlier pending iterators.
2507 emitJump(e, iters, t->m_label);
2508 return;
2510 // Only free the iterator for the current loop if this is
2511 // NOT the last level to continue out of.
2512 if (r->isForeach()) {
2513 iters.push_back(IterPair(r->m_iterKind, r->m_iterId));
2515 --depth;
2519 void EmitterVisitor::emitFinallyEpilogue(Emitter& e, Region* region) {
2520 assert(region != nullptr);
2521 assert(region->isTryFinally());
2522 assert(region->m_finallyLabel.isSet());
2523 int count = region->getCaseCount();
2524 assert(count >= 1);
2525 Label after;
2526 if (count == 1) {
2527 // If there is only one case (the fall-through case) then we're done
2528 after.set(e);
2529 return;
2531 // Otherwise, we need to emit some conditional jumps/switches to handle
2532 // the different cases. We start by builing up a vector of Label* that
2533 // we'll use for the Switch instruction and/or for conditional branches.
2534 int maxState = region->getMaxState();
2535 std::vector<Label*> cases;
2536 while (cases.size() <= maxState) {
2537 cases.push_back(new Label());
2539 // Now that we have our vector of Label*'s ready, we can emit a
2540 // Switch instruction and/or conditional branches, and we can
2541 // emit the body of each case.
2542 Id stateLocal = getStateLocal();
2543 emitVirtualLocal(stateLocal);
2544 e.IssetL(stateLocal);
2545 e.JmpZ(after);
2546 if (count >= 3) {
2547 // A switch is needed since there are more than two cases.
2548 emitVirtualLocal(stateLocal);
2549 e.CGetL(stateLocal);
2550 e.Switch(SwitchKind::Unbounded, 0, cases);
2552 for (auto& p : region->m_returnTargets) {
2553 if (p.second.used) emitReturnTrampoline(e, region, cases, p.first);
2555 assert(region->isTryFinally());
2556 int max_depth = region->getBreakContinueDepth();
2557 for (int i = 1; i <= max_depth; ++i) {
2558 if (region->isBreakUsed(i)) emitBreakTrampoline(e, region, cases, i);
2559 if (region->isContinueUsed(i)) emitContinueTrampoline(e, region, cases, i);
2561 for (auto& p : region->m_gotoTargets) {
2562 if (p.second.used) emitGotoTrampoline(e, region, cases, p.first);
2564 for (auto c : cases) {
2565 // Some cases might get assigned state numbers but not actually
2566 // occur in the try block. We need to set /some/ target for them,
2567 // so point them here.
2568 if (!c->isSet()) c->set(e);
2569 delete c;
2571 after.set(e);
2574 void EmitterVisitor::emitReturnTrampoline(Emitter& e,
2575 Region* region,
2576 std::vector<Label*>& cases,
2577 char sym) {
2578 assert(region->isTryFinally());
2579 assert(region->m_parent != nullptr);
2580 assert(region->m_returnTargets.count(sym));
2581 auto& t = region->m_returnTargets[sym].target;
2582 cases[t->m_state]->set(e);
2584 IterVec iters;
2585 // We are emitting a case in a finally epilogue, therefore skip
2586 // the current try region and start from its parent
2587 for (region = region->m_parent.get(); true; region = region->m_parent.get()) {
2588 assert(region->m_returnTargets.count(sym));
2589 assert(region->m_returnTargets[sym].target->isRegistered());
2590 // Add pending iterator if applicable
2591 if (region->isForeach()) {
2592 iters.push_back(IterPair(region->m_iterKind, region->m_iterId));
2594 if (region->m_parent == nullptr) {
2595 // At the bottom of the hierarchy. Restore the return value
2596 // and perform the actual return.
2597 Id retLocal = getRetLocal();
2598 emitVirtualLocal(retLocal);
2599 if (sym == StackSym::C) {
2600 e.CGetL(retLocal);
2601 if (shouldEmitVerifyRetType()) {
2602 e.VerifyRetTypeC();
2604 e.RetC();
2605 } else {
2606 assert(sym == StackSym::V);
2607 e.VGetL(retLocal);
2608 if (shouldEmitVerifyRetType()) {
2609 e.VerifyRetTypeV();
2611 e.RetV();
2613 return;
2615 if (region->isTryFinally()) {
2616 // Encountered another try block, jump to its finally and free
2617 // iterators on the way.
2618 emitJump(e, iters, region->m_finallyLabel);
2619 return;
2624 void EmitterVisitor::emitGotoTrampoline(Emitter& e,
2625 Region* region,
2626 std::vector<Label*>& cases,
2627 StringData* name) {
2628 assert(region->m_gotoTargets.count(name));
2629 auto t = region->m_gotoTargets[name].target;
2630 cases[t->m_state]->set(e);
2631 assert(region->m_parent != nullptr);
2632 IterVec iters;
2633 for (region = region->m_parent.get(); true; region = region->m_parent.get()) {
2634 assert(region->m_gotoTargets.count(name));
2635 auto t = region->m_gotoTargets[name].target;
2636 if (region->m_gotoLabels.count(name)) {
2637 // If only there is the appropriate label inside the current region
2638 // perform a jump.
2639 Id stateLocal = getStateLocal();
2640 emitVirtualLocal(stateLocal);
2641 // We need to unset the state unnamed local in order to correctly
2642 // fall through any future finally blocks.
2643 e.UnsetL(stateLocal);
2644 // Jump to the label and free any pending iterators.
2645 emitJump(e, iters, t->m_label);
2646 return;
2648 if (region->isTryFinally()) {
2649 // Encountered a finally block, jump and free any pending iterators
2650 emitJump(e, iters, region->m_finallyLabel);
2651 return;
2653 // Otherwise we will be jumping out of the current context,
2654 // therefore if we are in a loop, we need to free the iterator.
2655 if (region->isForeach()) {
2656 iters.push_back(IterPair(region->m_iterKind, region->m_iterId));
2658 // Error, because the label is crossing a finally
2659 if (region->isFinally()) {
2660 throw EmitterVisitor::IncludeTimeFatalException(e.getNode(),
2661 "jump out of a finally block is disallowed");
2663 // We should never break out of a function, therefore there
2664 // should always be a parent
2665 assert(region->m_parent != nullptr);
2669 void EmitterVisitor::emitBreakTrampoline(Emitter& e, Region* region,
2670 std::vector<Label*>& cases,
2671 int depth) {
2672 assert(depth >= 1);
2673 assert(region->isTryFinally());
2674 assert(region->m_breakTargets.count(depth));
2675 auto t = region->m_breakTargets[depth].target;
2676 cases[t->m_state]->set(e);
2677 assert(region->m_parent != nullptr);
2678 IterVec iters;
2679 for (region = region->m_parent.get(); true; region = region->m_parent.get()) {
2680 assert(depth >= 1);
2681 assert(!region->isFinally());
2682 assert(region->m_parent != nullptr);
2683 assert(region->m_breakTargets.count(depth));
2684 auto t = region->m_breakTargets[depth].target;
2685 if (region->isTryFinally()) {
2686 // We encountered another try block, jump to the corresponding
2687 // finally, freeing any iterators on the way.
2688 emitJump(e, iters, region->m_finallyLabel);
2689 return;
2691 if (region->m_kind != Region::Kind::LoopOrSwitch) {
2692 continue;
2694 // Whether or not this is the last loop to break out of, we
2695 // will be freeing the current iterator
2696 if (region->isForeach()) {
2697 iters.push_back(IterPair(region->m_iterKind, region->m_iterId));
2699 if (depth == 1) {
2700 // This is the last loop to break out of. Unset the state local in
2701 // order to correctly fall through any future finally blocks
2702 Id stateLocal = getStateLocal();
2703 emitVirtualLocal(stateLocal);
2704 e.UnsetL(stateLocal);
2705 // Jump to the break label and free any pending iterators on the
2706 // way.
2707 emitJump(e, iters, t->m_label);
2708 return;
2710 // Otherwise just delegate to the parent. One loop level has been
2711 // taken care of.
2712 --depth;
2716 void EmitterVisitor::emitContinueTrampoline(Emitter& e, Region* region,
2717 std::vector<Label*>& cases,
2718 int depth) {
2719 assert(depth >= 1);
2720 assert(region->isTryFinally());
2721 assert(region->m_continueTargets.count(depth));
2722 auto t = region->m_continueTargets[depth].target;
2723 cases[t->m_state]->set(e);
2724 assert(region->m_parent != nullptr);
2725 IterVec iters;
2726 for (region = region->m_parent.get(); true; region = region->m_parent.get()) {
2727 assert(depth >= 1);
2728 assert(region->m_parent != nullptr);
2729 assert(!region->isFinally());
2730 auto t = region->m_continueTargets[depth].target;
2731 if (region->isTryFinally()) {
2732 emitJump(e, iters, region->m_finallyLabel);
2733 return;
2735 if (region->m_kind != Region::Kind::LoopOrSwitch) {
2736 continue;
2738 if (depth == 1) {
2739 // This is the last loop level to continue out of. Don't free the
2740 // iterator for the current loop. We need to free the state unnamed
2741 // local in order to fall through any future finallies correctly
2742 Id stateLocal = getStateLocal();
2743 emitVirtualLocal(stateLocal);
2744 e.UnsetL(stateLocal);
2745 // Jump to the continue label and free any pending iterators
2746 emitJump(e, iters, t->m_label);
2747 return;
2749 // This is not the last loop level, therefore the current
2750 // iterator should be freed.
2751 if (region->isForeach()) {
2752 iters.push_back(IterPair(region->m_iterKind, region->m_iterId));
2754 --depth;
2758 bool EmitterVisitor::shouldEmitVerifyRetType() {
2759 return (m_curFunc->retTypeConstraint.hasConstraint() &&
2760 !m_curFunc->isGenerator);
2763 int Region::getMaxBreakContinueDepth() {
2764 if (m_parent == nullptr || isFinally()) {
2765 return 0;
2766 } else if (m_kind == Region::Kind::LoopOrSwitch) {
2767 return m_parent->getMaxBreakContinueDepth() + 1;
2768 } else {
2769 return m_parent->getMaxBreakContinueDepth();
2773 int Region::getBreakContinueDepth() {
2774 int depth = 0;
2775 for (auto& p : m_breakTargets) {
2776 depth = std::max(depth, p.first);
2778 for (auto& p : m_continueTargets) {
2779 depth = std::max(depth, p.first);
2781 return depth;
2784 int Region::getMaxState() {
2785 int maxState = -1;
2786 for (auto& p : m_returnTargets) {
2787 if (p.second.used) {
2788 maxState = std::max(maxState, p.second.target->m_state);
2791 int max_depth = getBreakContinueDepth();
2792 for (int i = 1; i <= max_depth; ++i) {
2793 if (isBreakUsed(i)) {
2794 maxState = std::max(maxState, m_breakTargets[i].target->m_state);
2796 if (isContinueUsed(i)) {
2797 maxState = std::max(maxState, m_continueTargets[i].target->m_state);
2800 for (auto& p : m_gotoTargets) {
2801 if (p.second.used) {
2802 maxState = std::max(maxState, p.second.target->m_state);
2805 return maxState;
2808 RegionPtr
2809 EmitterVisitor::createRegion(StatementPtr s, Region::Kind kind) {
2810 RegionPtr parent = nullptr;
2811 if (kind != Region::Kind::FuncBody && kind != Region::Kind::FaultFunclet &&
2812 kind != Region::Kind::Global && !m_regions.empty()) {
2813 parent = m_regions.back();
2815 auto region = std::make_shared<Region>(kind, parent);
2816 // We preregister all the labels occurring in the provided statement
2817 // ahead of the time. Therefore at the time of emitting the actual
2818 // goto instructions we can reliably tell which finally blocks to
2819 // run.
2820 for (auto& label : s->getLabelScope()->getLabels()) {
2821 StringData* nName = makeStaticString(label.getName().c_str());
2822 if (!region->m_gotoLabels.count(nName)) {
2823 region->m_gotoLabels.insert(nName);
2826 return region;
2829 void EmitterVisitor::enterRegion(RegionPtr region) {
2830 assert(region != nullptr);
2831 m_regions.push_back(region);
2834 void EmitterVisitor::leaveRegion(RegionPtr region) {
2835 assert(region != nullptr);
2836 assert(m_regions.size() > 0);
2837 assert(m_regions.back() == region);
2838 m_regions.pop_back();
2841 void EmitterVisitor::registerControlTarget(ControlTarget* t) {
2842 assert(!t->isRegistered());
2843 int state = 0;
2844 while (m_states.count(state)) {
2845 ++state;
2847 m_states.insert(state);
2848 t->m_state = state;
2851 void EmitterVisitor::unregisterControlTarget(ControlTarget* t) {
2852 assert(t->isRegistered());
2853 int state = t->m_state;
2854 assert(m_states.count(state));
2855 m_states.erase(state);
2856 t->m_state = ControlTarget::k_unsetState;
2859 //=============================================================================
2860 // EmitterVisitor.
2862 EmitterVisitor::EmittedClosures EmitterVisitor::s_emittedClosures;
2864 EmitterVisitor::EmitterVisitor(UnitEmitter& ue)
2865 : m_ue(ue),
2866 m_curFunc(ue.getMain()),
2867 m_evalStackIsUnknown(false),
2868 m_stateLocal(-1),
2869 m_retLocal(-1) {
2870 m_prevOpcode = OpLowInvalid;
2871 m_evalStack.m_actualStackHighWaterPtr = &m_curFunc->maxStackCells;
2874 EmitterVisitor::~EmitterVisitor() {
2875 // If a fatal occurs during emission, some extra cleanup is necessary.
2876 for (std::deque<CatchRegion*>::const_iterator it = m_catchRegions.begin();
2877 it != m_catchRegions.end(); ++it) {
2878 delete *it;
2882 bool EmitterVisitor::checkIfStackEmpty(const char* forInstruction) const {
2883 if (m_evalStack.empty()) {
2884 InvariantViolation("Emitter tried to emit a %s instruction when the "
2885 "evaluation stack is empty (at offset %d)",
2886 forInstruction,
2887 m_ue.bcPos());
2888 return true;
2890 return false;
2893 void EmitterVisitor::unexpectedStackSym(char sym, const char* where) const {
2894 InvariantViolation("Emitter encountered an unexpected StackSym \"%s\""
2895 " in %s() (at offset %d)",
2896 StackSym::ToString(sym).c_str(),
2897 where,
2898 m_ue.bcPos());
2901 void EmitterVisitor::popEvalStack(char expected) {
2902 // Pop a value off of the evaluation stack, and verify that it
2903 // matches the specified symbolic flavor
2904 if (m_evalStack.size() == 0) {
2905 InvariantViolation("Emitter emitted an instruction that tries to consume "
2906 "a value from the stack when the stack is empty "
2907 "(expected symbolic flavor \"%s\" at offset %d)",
2908 StackSym::ToString(expected).c_str(),
2909 m_ue.bcPos());
2910 return;
2913 char sym = m_evalStack.top();
2914 char actual = StackSym::GetSymFlavor(sym);
2915 m_evalStack.pop();
2916 if (actual != expected) {
2917 InvariantViolation(
2918 "Emitter emitted an instruction that tries to consume a "
2919 "value from the stack when the top of the stack does not "
2920 "match the symbolic flavor that the instruction expects "
2921 "(expected symbolic flavor \"%s\", actual symbolic flavor \"%s\" "
2922 "at offset %d)",
2923 StackSym::ToString(expected).c_str(),
2924 StackSym::ToString(actual).c_str(),
2925 m_ue.bcPos());
2929 void EmitterVisitor::popSymbolicLocal(Op op) {
2930 // A number of member instructions read locals without consuming an L from
2931 // the symbolic stack through the normal path.
2932 if (isMemberBaseOp(op) || isMemberDimOp(op) || isMemberFinalOp(op)) {
2933 return;
2936 int belowTop = -1;
2937 if (op == OpCGetL3) {
2938 belowTop = 3;
2939 } else if (op == OpCGetL2) {
2940 belowTop = 2;
2943 if (belowTop != -1) {
2944 char symFlavor = StackSym::GetSymFlavor(
2945 m_evalStack.get(m_evalStack.size() - belowTop));
2946 if (symFlavor != StackSym::L) {
2947 InvariantViolation("Operation tried to remove a local below the top of"
2948 " the symbolic stack but instead found \"%s\"",
2949 StackSym::ToString(symFlavor).c_str());
2951 m_evalStack.consumeBelowTop(belowTop - 1);
2952 } else {
2953 popEvalStack(StackSym::L);
2957 void EmitterVisitor::popEvalStackMMany() {
2958 ITRACE(3, "popEvalStackMMany()\n");
2959 Trace::Indent i;
2961 ITRACE(3, "popping member codes\n");
2962 while (!m_evalStack.empty()) {
2963 char sym = m_evalStack.top();
2964 char symFlavor = StackSym::GetSymFlavor(sym);
2965 char marker = StackSym::GetMarker(sym);
2966 if (marker == StackSym::E || marker == StackSym::P ||
2967 marker == StackSym::Q) {
2968 if (symFlavor != StackSym::C && symFlavor != StackSym::L &&
2969 symFlavor != StackSym::T && symFlavor != StackSym::I) {
2970 InvariantViolation(
2971 "Emitter emitted an instruction that tries to consume "
2972 "a value from the stack when the top of the stack "
2973 "does not match the symbolic flavor that the instruction "
2974 "expects (expected symbolic flavor \"C\", \"L\", \"T\", or \"I\", "
2975 "actual symbolic flavor \"%s\" at offset %d)",
2976 StackSym::ToString(symFlavor).c_str(),
2977 m_ue.bcPos());
2979 } else if (marker == StackSym::W) {
2980 if (symFlavor != StackSym::None) {
2981 InvariantViolation(
2982 "Emitter emitted an instruction that tries to consume "
2983 "a value from the stack when the top of the stack "
2984 "does not match the symbolic flavor that the instruction "
2985 "expects (expected symbolic flavor \"None\", actual "
2986 "symbolic flavor \"%s\" at offset %d)",
2987 StackSym::ToString(symFlavor).c_str(),
2988 m_ue.bcPos());
2990 } else if (marker == StackSym::M) {
2991 assert(symFlavor == StackSym::A);
2992 } else {
2993 break;
2995 m_evalStack.pop();
2998 if (m_evalStack.empty()) {
2999 InvariantViolation("Emitter emitted an instruction that tries to consume "
3000 "a value from the stack when the stack is empty "
3001 "(at offset %d)",
3002 m_ue.bcPos());
3003 return;
3006 ITRACE(3, "popping location\n");
3007 char sym = m_evalStack.top();
3008 char symFlavor = StackSym::GetSymFlavor(sym);
3009 m_evalStack.pop();
3010 if (symFlavor != StackSym::C && symFlavor != StackSym::L &&
3011 symFlavor != StackSym::R && symFlavor != StackSym::H) {
3012 InvariantViolation(
3013 "Emitter emitted an instruction that tries to consume a "
3014 "value from the stack when the top of the stack does not "
3015 "match the symbolic flavor that the instruction expects "
3016 "(expected symbolic flavor \"C\", \"L\", \"R\", or \"H\", actual "
3017 "symbolic flavor \"%s\" at offset %d)",
3018 StackSym::ToString(symFlavor).c_str(),
3019 m_ue.bcPos());
3023 void EmitterVisitor::popEvalStackMany(int len, char symFlavor) {
3024 for (int i = 0; i < len; ++i) {
3025 popEvalStack(symFlavor);
3029 void EmitterVisitor::popEvalStackCVMany(int len) {
3030 for (int i = 0; i < len; i++) {
3031 if (m_evalStack.size() == 0) {
3032 InvariantViolation("Emitter emitted an instruction that tries to consume "
3033 "a value from the stack when the stack is empty "
3034 "(expected symbolic flavor C or V at offset %d)",
3035 m_ue.bcPos());
3036 return;
3039 char sym = m_evalStack.top();
3040 char actual = StackSym::GetSymFlavor(sym);
3041 m_evalStack.pop();
3042 if (actual != StackSym::C && actual != StackSym::V) {
3043 InvariantViolation(
3044 "Emitter emitted an instruction that tries to consume a "
3045 "value from the stack when the top of the stack does not "
3046 "match the symbolic flavor that the instruction expects "
3047 "(expected symbolic flavor C or V, actual symbolic flavor \"%s\" "
3048 "at offset %d)",
3049 StackSym::ToString(actual).c_str(),
3050 m_ue.bcPos());
3055 void EmitterVisitor::pushEvalStack(char symFlavor) {
3056 // Push a value from the evaluation stack with the specified
3057 // symbolic flavor
3058 m_evalStack.push(symFlavor);
3061 void EmitterVisitor::peekEvalStack(char expected, int depthActual) {
3062 int posActual = (m_evalStack.sizeActual() - depthActual - 1);
3063 if (posActual >= 0 && posActual < (int)m_evalStack.sizeActual()) {
3064 char sym = m_evalStack.getActual(posActual);
3065 char actual = StackSym::GetSymFlavor(sym);
3066 if (actual != expected) {
3067 InvariantViolation(
3068 "Emitter emitted an instruction that tries to consume a "
3069 "value from the stack whose symbolic flavor does not match "
3070 "the symbolic flavor that the instruction expects (expected "
3071 "symbolic flavor \"%s\", actual symbolic flavor \"%s\" at "
3072 "offset %d)",
3073 StackSym::ToString(expected).c_str(),
3074 StackSym::ToString(actual).c_str(),
3075 m_ue.bcPos());
3080 void EmitterVisitor::pokeEvalStack(char symFlavor, int depthActual) {
3081 int sizeActual = m_evalStack.sizeActual();
3082 int posActual = sizeActual - depthActual - 1;
3083 if (posActual >= 0 && posActual < sizeActual) {
3084 m_evalStack.setActual(posActual, symFlavor);
3089 * Prior to making any changes to the evaluation stack in between
3090 * instructions, this function should be called.
3092 * What this handles is recording the evaluation stack state at
3093 * instruction boundaries so that jumps know what their stack state
3094 * will be at the destination.
3096 * When m_evalStackIsUnknown, it means we have hit a place in the
3097 * bytecode where the offset cannot be reached via fallthrough, but no
3098 * forward jumps targeted it either. In this case, the stack must be
3099 * empty, and we need to record that this is the case so that later
3100 * backward jumps can check this is the case at their jump site.
3102 void EmitterVisitor::prepareEvalStack() {
3103 if (m_evalStackIsUnknown) {
3104 if (!m_evalStack.empty()) {
3105 InvariantViolation("Emitter expected to have an empty evaluation "
3106 "stack because the eval stack was unknown, but "
3107 "it was non-empty.");
3108 return;
3110 // Record that we are assuming that the eval stack is empty
3111 recordJumpTarget(m_ue.bcPos(), m_evalStack);
3112 m_evalStackIsUnknown = false;
3116 void EmitterVisitor::recordJumpTarget(Offset target,
3117 const SymbolicStack& evalStack) {
3118 if (target == InvalidAbsoluteOffset) {
3119 InvariantViolation(
3120 "Offset passed to EmitterVisitor::recordJumpTarget was invalid");
3122 auto it = m_jumpTargetEvalStacks.find(target);
3123 if (it == m_jumpTargetEvalStacks.end()) {
3124 m_jumpTargetEvalStacks[target] = evalStack;
3125 return;
3127 checkJmpTargetEvalStack(evalStack, it->second);
3130 void EmitterVisitor::restoreJumpTargetEvalStack() {
3131 m_evalStack.clear();
3132 auto it = m_jumpTargetEvalStacks.find(m_ue.bcPos());
3133 if (it == m_jumpTargetEvalStacks.end()) {
3134 m_evalStackIsUnknown = true;
3135 return;
3137 m_evalStack = it->second;
3140 void EmitterVisitor::recordCall() {
3141 m_curFunc->containsCalls = true;
3144 bool EmitterVisitor::isJumpTarget(Offset target) {
3145 // Returns true iff one of the following conditions is true:
3146 // 1) We have seen an instruction that jumps to the specified offset
3147 // 2) We know of a Label that has been set to the specified offset
3148 // 3) We have seen a try region that ends at the specified offset
3149 auto it = m_jumpTargetEvalStacks.find(target);
3150 return (it != m_jumpTargetEvalStacks.end());
3153 struct IterFreeThunklet final : Thunklet {
3154 IterFreeThunklet(Id iterId, bool itRef)
3155 : m_id(iterId), m_itRef(itRef) {}
3156 void emit(Emitter& e) override {
3157 if (m_itRef) {
3158 e.MIterFree(m_id);
3159 } else {
3160 e.IterFree(m_id);
3162 e.Unwind();
3164 private:
3165 Id m_id;
3166 bool m_itRef;
3170 * A thunklet for the fault region protecting a silenced (@) expression.
3172 struct RestoreErrorReportingThunklet final : Thunklet {
3173 explicit RestoreErrorReportingThunklet(Id loc)
3174 : m_oldLevelLoc(loc) {}
3175 void emit(Emitter& e) override {
3176 e.getEmitterVisitor().emitRestoreErrorReporting(e, m_oldLevelLoc);
3177 e.Unwind();
3179 private:
3180 Id m_oldLevelLoc;
3183 struct UnsetUnnamedLocalThunklet final : Thunklet {
3184 explicit UnsetUnnamedLocalThunklet(Id loc)
3185 : m_loc(loc) {}
3186 void emit(Emitter& e) override {
3187 e.getEmitterVisitor().emitVirtualLocal(m_loc);
3188 e.getEmitterVisitor().emitUnset(e);
3189 e.Unwind();
3191 private:
3192 Id m_loc;
3195 struct UnsetUnnamedLocalsThunklet final : Thunklet {
3196 explicit UnsetUnnamedLocalsThunklet(std::vector<Id>&& locs)
3197 : m_locs(std::move(locs)) {}
3198 void emit(Emitter& e) override {
3199 auto& visitor = e.getEmitterVisitor();
3200 for (auto loc : m_locs) {
3201 visitor.emitVirtualLocal(loc);
3202 visitor.emitUnset(e);
3204 e.Unwind();
3206 private:
3207 const std::vector<Id> m_locs;
3210 struct UnsetGeneratorDelegateThunklet final : Thunklet {
3211 explicit UnsetGeneratorDelegateThunklet(Id iterId)
3212 : m_id(iterId) {}
3213 void emit(Emitter& e) override {
3214 e.ContUnsetDelegate(true, m_id);
3215 e.Unwind();
3217 private:
3218 Id m_id;
3221 struct FinallyThunklet final : Thunklet {
3222 explicit FinallyThunklet(FinallyStatementPtr finallyStatement,
3223 int numLiveIters)
3224 : m_finallyStatement(finallyStatement), m_numLiveIters(numLiveIters) {}
3225 void emit(Emitter& e) override {
3226 auto& visitor = e.getEmitterVisitor();
3227 auto region =
3228 visitor.createRegion(m_finallyStatement, Region::Kind::FaultFunclet);
3229 visitor.enterRegion(region);
3230 SCOPE_EXIT { visitor.leaveRegion(region); };
3231 Id stateLocal = visitor.getStateLocal();
3232 visitor.emitVirtualLocal(stateLocal);
3233 e.UnsetL(stateLocal);
3234 Id retLocal = visitor.getStateLocal();
3235 visitor.emitVirtualLocal(retLocal);
3236 e.UnsetL(retLocal);
3237 auto* func = visitor.getFuncEmitter();
3238 int oldNumLiveIters = func->numLiveIterators();
3239 func->setNumLiveIterators(m_numLiveIters);
3240 SCOPE_EXIT { func->setNumLiveIterators(oldNumLiveIters); };
3241 visitor.visit(m_finallyStatement);
3242 e.Unwind();
3244 private:
3245 FinallyStatementPtr m_finallyStatement;
3246 int m_numLiveIters;
3250 * Helper to deal with emitting list assignment and keeping track of some
3251 * associated info. A list assignment can be thought of as a list of "index
3252 * chains"; that is, sequences of indices that should be accessed for each
3253 * bottom-level expression in the list assignment. We recursively walk down the
3254 * LHS, building up index chains and copying them into the top-level list as we
3255 * reach the leaves of the tree.
3257 void EmitterVisitor::listAssignmentVisitLHS(Emitter& e, ExpressionPtr exp,
3258 IndexChain& indexChain,
3259 std::vector<IndexPair>& all) {
3260 if (!exp) {
3261 // Empty slot
3262 return;
3265 if (exp->is(Expression::KindOfListAssignment)) {
3266 // Nested assignment
3267 auto la = static_pointer_cast<ListAssignment>(exp);
3268 auto lhs = la->getVariables();
3269 int n = lhs->getCount();
3270 for (int i = 0; i < n; ++i) {
3271 indexChain.push_back(i);
3272 listAssignmentVisitLHS(e, (*lhs)[i], indexChain, all);
3273 indexChain.pop_back();
3275 } else {
3276 // Reached a "leaf". Lock in this index chain and deal with this exp.
3277 assert(!indexChain.empty());
3278 all.emplace_back(exp, IndexChain(indexChain));
3280 // First: the order we visit the LHS elements matters, as does whether we
3281 // do the RHS or LHS first, for things like:
3282 // list($a[$n++], $b[$n++]) = $c[$n++]
3284 // In PHP5 mode, we visit the LHS elements of the list() now. This does
3285 // two things: it causes their side effects to happen in LTR order, but
3286 // since they are pushed onto the m_evalStack they are actually asigned to
3287 // in LIFO order, e.g., RTL, in listAssignmentAssignElements below.
3289 // In PHP7 mode, we need to visit the elements in LTR order so their side
3290 // effects take place in that order, but we *also* need to assign them in
3291 // LTR order, so we can't push them onto the m_evalStack right now. Since
3292 // visit() does both of these things, we need to delay calling visit()
3293 // until listAssignmentAssignElements below. This also has the side effect
3294 // of making isRhsFirst() effectively always on in PHP7 mode when doing
3295 // list() assignment (since we delay the visit() until after the check
3296 // anyways), which turns out to be the right behavior.
3297 if (!RuntimeOption::PHP7_LTR_assign) {
3298 visit(exp);
3299 emitClsIfSPropBase(e);
3304 void EmitterVisitor::listAssignmentAssignElements(
3305 Emitter& e,
3306 std::vector<IndexPair>& indexPairs,
3307 std::function<void()> emitSrc
3310 // PHP5 does list() assignments RTL, PHP7 does them LTR, so this loop can go
3311 // either way and looks a little ugly. The assignment order normally isn't
3312 // visible, but it is if you do something like:
3313 // list($a[], $a[]) = $foo
3314 auto const ltr = RuntimeOption::PHP7_LTR_assign;
3315 for (int i = ltr ? 0 : (int)indexPairs.size() - 1;
3316 i >= 0 && i < (int)indexPairs.size();
3317 ltr ? i++ : i--) {
3318 if (ltr) {
3319 // Visit now, so we can both eval LTR and assign LTR. See comment in
3320 // listAssignmentVisitLHS.
3321 visit(indexPairs[i].first);
3322 emitClsIfSPropBase(e);
3325 IndexChain& currIndexChain = indexPairs[i].second;
3326 if (currIndexChain.empty()) {
3327 continue;
3330 if (emitSrc == nullptr) {
3331 e.Null();
3332 } else {
3333 emitSrc();
3334 for (int j = 0; j < (int)currIndexChain.size(); ++j) {
3335 m_evalStack.push(StackSym::I);
3336 m_evalStack.setInt(currIndexChain[j]);
3337 markElem(e);
3339 emitCGet(e);
3342 emitSet(e);
3343 emitPop(e);
3348 * visitIfCondition() serves as a helper method for visiting the condition
3349 * of an "if" statement. This function recursively visits each node in an
3350 * expression, keeping track of where to jump if an "and" or "or" expression
3351 * short circuits. If an expression other than "and", "or", or "not" is
3352 * encountered, this method calls EmitterVisitor::visit() to handle the
3353 * expression.
3355 void EmitterVisitor::visitIfCondition(
3356 ExpressionPtr cond, Emitter& e, Label& tru, Label& fals,
3357 bool truFallthrough) {
3359 auto binOpNode = dynamic_pointer_cast<BinaryOpExpression>(cond);
3361 if (binOpNode) {
3362 int op = binOpNode->getOp();
3363 // Short circuit && and ||
3364 if (op == T_LOGICAL_OR || op == T_LOGICAL_AND ||
3365 op == T_BOOLEAN_OR || op == T_BOOLEAN_AND) {
3366 bool isOr = (op == T_LOGICAL_OR || op == T_BOOLEAN_OR);
3367 Label localLabel;
3368 ExpressionPtr lhs = binOpNode->getExp1();
3369 if (isOr) {
3370 Emitter lhsEmitter(lhs, m_ue, *this);
3371 visitIfCondition(lhs, lhsEmitter, tru, localLabel, false);
3372 // falls through if that condition was false
3373 } else {
3374 Emitter lhsEmitter(lhs, m_ue, *this);
3375 visitIfCondition(lhs, lhsEmitter, localLabel, fals, true);
3376 // falls through if that condition was true
3378 if (localLabel.isUsed()) {
3379 localLabel.set(e);
3381 if (currentPositionIsReachable()) {
3382 ExpressionPtr rhs = binOpNode->getExp2();
3383 Emitter rhsEmitter(rhs, m_ue, *this);
3384 visitIfCondition(rhs, rhsEmitter, tru, fals, truFallthrough);
3386 return;
3390 auto unOpNode = dynamic_pointer_cast<UnaryOpExpression>(cond);
3391 if (unOpNode) {
3392 int op = unOpNode->getOp();
3393 // Logical not
3394 if (op == '!') {
3395 ExpressionPtr val = unOpNode->getExpression();
3396 Emitter valEmitter(val, m_ue, *this);
3397 visitIfCondition(val, valEmitter, fals, tru, !truFallthrough);
3398 return;
3402 Variant val;
3403 if (cond->getScalarValue(val)) {
3404 if (truFallthrough) {
3405 if (!val.toBoolean()) e.Jmp(fals);
3406 } else {
3407 if (val.toBoolean()) e.Jmp(tru);
3409 return;
3412 visit(cond);
3413 emitConvertToCell(e);
3414 if (truFallthrough) {
3415 e.JmpZ(fals);
3416 } else {
3417 e.JmpNZ(tru);
3421 // Assigns ids to all of the local variables eagerly. This gives us the
3422 // nice property that all named local variables will be assigned ids
3423 // 0 through k-1, while any unnamed local variable will have an id >= k.
3424 void EmitterVisitor::assignLocalVariableIds(FunctionScopePtr fs) {
3425 VariableTablePtr variables = fs->getVariables();
3426 std::vector<std::string> localNames;
3427 variables->getLocalVariableNames(localNames);
3428 for (int i = 0; i < (int)localNames.size(); ++i) {
3429 StringData* nLiteral = makeStaticString(localNames[i].c_str());
3430 m_curFunc->allocVarId(nLiteral);
3434 void EmitterVisitor::assignFinallyVariableIds() {
3435 assert(m_stateLocal < 0);
3436 m_stateLocal = m_curFunc->allocUnnamedLocal();
3437 assert(m_retLocal < 0);
3438 m_retLocal = m_curFunc->allocUnnamedLocal();
3441 void EmitterVisitor::visit(FileScopePtr file) {
3442 const std::string& filename = file->getName();
3443 m_ue.m_filepath = makeStaticString(filename);
3444 m_ue.m_isHHFile = file->isHHFile();
3445 m_ue.m_useStrictTypes = file->useStrictTypes();
3447 FunctionScopePtr func(file->getPseudoMain());
3448 if (!func) return;
3450 SCOPE_ASSERT_DETAIL("visit FileScope") { return m_evalStack.pretty(); };
3451 ITRACE(1, "Emitting file {}\n", file->getName());
3452 Trace::Indent indent;
3454 m_file = file;
3455 assignLocalVariableIds(func);
3457 AnalysisResultPtr ar(file->getContainingProgram());
3458 assert(ar);
3459 auto m = dynamic_pointer_cast<MethodStatement>(func->getStmt());
3460 if (!m) return;
3461 StatementListPtr stmts(m->getStmts());
3462 if (!stmts) return;
3464 Emitter e(m, m_ue, *this);
3466 int i, nk = stmts->getCount();
3467 for (i = 0; i < nk; i++) {
3468 StatementPtr s = (*stmts)[i];
3469 if (auto meth = dynamic_pointer_cast<MethodStatement>(s)) {
3470 // Emit afterwards
3471 postponeMeth(meth, nullptr, true);
3475 FunctionScopePtr fsp = m->getFunctionScope();
3476 if (fsp->needsLocalThis()) {
3477 static const StringData* thisStr = makeStaticString("this");
3478 Id thisId = m_curFunc->lookupVarId(thisStr);
3479 emitVirtualLocal(thisId);
3480 e.InitThisLoc(thisId);
3482 if (fsp->needsFinallyLocals()) {
3483 assignFinallyVariableIds();
3485 FuncFinisher ff(this, e, m_curFunc);
3486 TypedValue mainReturn;
3487 mainReturn.m_type = kInvalidDataType;
3488 bool notMergeOnly = false;
3490 if (Option::UseHHBBC && SystemLib::s_inited) notMergeOnly = true;
3492 auto region = createRegion(stmts, Region::Kind::Global);
3493 enterRegion(region);
3494 SCOPE_EXIT { leaveRegion(region); };
3496 for (auto cls : m_file->getAnonClasses()) {
3497 emitClass(e, cls->getClassScope(), true);
3500 for (i = 0; i < nk; i++) {
3501 StatementPtr s = (*stmts)[i];
3502 e.setTempLocation(s->getRange());
3503 switch (s->getKindOf()) {
3504 case Statement::KindOfMethodStatement:
3505 case Statement::KindOfFunctionStatement:
3506 break;
3507 case Statement::KindOfInterfaceStatement:
3508 case Statement::KindOfClassStatement: {
3509 // Handle classes directly here, since only top-level classes are
3510 // hoistable.
3511 ClassScopePtr cNode = s->getClassScope();
3512 emitClass(e, cNode, true);
3513 if (cNode->getFatalMessage()) {
3514 notMergeOnly = true;
3516 break;
3518 case Construct::KindOfDeclareStatement: {
3519 auto ds = static_pointer_cast<DeclareStatement>(s);
3520 for (auto& decl : ds->getDeclareMap()) {
3521 if (decl.first == "strict_types") {
3522 if (ds->getBlock()->getStmts()->getCount()) {
3523 emitMakeUnitFatal(e, "strict_types declaration must not use "
3524 "block mode");
3525 break;
3527 if (!RuntimeOption::PHP7_ScalarTypes) {
3528 emitMakeUnitFatal(e, "strict_types can only be used when "
3529 "hhvm.php7.scalar_types = true");
3530 break;
3535 visit(ds->getBlock());
3536 break;
3538 case Statement::KindOfTypedefStatement: {
3539 auto const id =
3540 emitTypedef(e, static_pointer_cast<TypedefStatement>(s));
3541 m_ue.pushMergeableTypeAlias(Unit::MergeKind::TypeAlias, id);
3542 break;
3544 case Statement::KindOfReturnStatement:
3545 if (mainReturn.m_type != kInvalidDataType) break;
3547 visit(s);
3548 if (notMergeOnly) {
3549 tvWriteUninit(&mainReturn);
3550 m_ue.m_returnSeen = true;
3551 continue;
3555 auto r = static_pointer_cast<ReturnStatement>(s);
3556 Variant v((Variant::NullInit()));
3557 if (r->getRetExp() &&
3558 !r->getRetExp()->getScalarValue(v)) {
3559 tvWriteUninit(&mainReturn);
3560 notMergeOnly = true;
3561 continue;
3563 if (v.isString()) {
3564 v = String(makeStaticString(v.asCStrRef().get()));
3565 } else if (v.isArray()) {
3566 v = Array(ArrayData::GetScalarArray(v.asCArrRef().get()));
3567 } else {
3568 assert(v.isInitialized());
3569 assert(!isRefcountedType(v.getType()));
3571 mainReturn = *v.asCell();
3572 m_ue.m_returnSeen = true;
3574 break;
3575 case Statement::KindOfExpStatement:
3576 if (mainReturn.m_type == kInvalidDataType) {
3577 auto e = static_pointer_cast<ExpStatement>(s)->getExpression();
3578 switch (e->getKindOf()) {
3579 case Expression::KindOfSimpleFunctionCall: {
3580 auto func = static_pointer_cast<SimpleFunctionCall>(e);
3581 StringData *name;
3582 TypedValue tv;
3583 if (func->isSimpleDefine(&name, &tv)) {
3584 auto k = func->isDefineWithoutImpl(ar)
3585 ? Unit::MergeKind::PersistentDefine
3586 : Unit::MergeKind::Define;
3587 if (tv.m_type == KindOfUninit) {
3588 tv.m_type = KindOfNull;
3590 m_ue.pushMergeableDef(k, name, tv);
3591 visit(s);
3592 continue;
3594 break;
3596 case Expression::KindOfAssignmentExpression: {
3597 auto ae = static_pointer_cast<AssignmentExpression>(e);
3598 StringData *name;
3599 TypedValue tv;
3600 if (ae->isSimpleGlobalAssign(&name, &tv)) {
3601 m_ue.pushMergeableDef(Unit::MergeKind::Global, name, tv);
3602 visit(s);
3603 continue;
3605 break;
3607 case Expression::KindOfIncludeExpression: {
3608 auto inc = static_pointer_cast<IncludeExpression>(e);
3609 if (inc->isReqLit()) {
3610 if (FileScopeRawPtr f = inc->getIncludedFile(ar)) {
3611 if (StatementListPtr sl = f->getStmt()) {
3612 FunctionScopeRawPtr ps DEBUG_ONLY =
3613 sl->getFunctionScope();
3614 assert(ps && ps->inPseudoMain());
3615 m_ue.pushMergeableInclude(
3616 Unit::MergeKind::ReqDoc,
3617 makeStaticString(inc->includePath()));
3618 visit(s);
3619 continue;
3623 break;
3625 default:
3626 break;
3628 } // fall through
3629 default:
3630 if (mainReturn.m_type != kInvalidDataType) break;
3631 notMergeOnly = true;
3632 visit(s);
3636 if (!notMergeOnly) {
3637 m_ue.m_mergeOnly = true;
3638 if (mainReturn.m_type == kInvalidDataType) {
3639 tvWriteUninit(&mainReturn);
3640 if (boost::algorithm::ends_with(filename, EVAL_FILENAME_SUFFIX)) {
3641 tvAsVariant(&mainReturn) = init_null();
3642 } else {
3643 tvAsVariant(&mainReturn) = 1;
3646 m_ue.m_mainReturn = mainReturn;
3649 // Pseudo-main returns the integer value 1 by default. If the
3650 // current position in the bytecode is reachable, emit code to
3651 // return 1.
3652 if (currentPositionIsReachable()) {
3653 Location::Range loc;
3654 if (m_ue.bcPos() > 0) loc.line0 = -1;
3655 e.setTempLocation(loc);
3656 if (boost::algorithm::ends_with(filename, EVAL_FILENAME_SUFFIX)) {
3657 e.Null();
3658 } else {
3659 e.Int(1);
3661 e.RetC();
3662 e.setTempLocation(OptLocation());
3666 if (!m_evalStack.empty()) {
3667 InvariantViolation("Eval stack was not empty as expected before "
3668 "emitPostponed* phase");
3671 // Method bodies
3672 emitPostponedMeths();
3673 emitPostponedCtors();
3674 emitPostponedPinits();
3675 emitPostponedSinits();
3676 emitPostponedCinits();
3679 static StringData* getClassName(ExpressionPtr e) {
3680 ClassScopeRawPtr cls;
3681 if (e->isThis()) {
3682 cls = e->getClassScope();
3684 if (cls && !cls->isTrait()) {
3685 return makeStaticString(cls->getScopeName());
3687 return nullptr;
3690 void EmitterVisitor::fixReturnType(Emitter& e, FunctionCallPtr fn,
3691 Func* builtinFunc) {
3692 int ref = -1;
3693 if (fn->hasAnyContext(Expression::RefValue |
3694 Expression::DeepReference |
3695 Expression::LValue |
3696 Expression::OprLValue |
3697 Expression::UnsetContext)) {
3698 return;
3700 if (builtinFunc) {
3701 ref = (builtinFunc->attrs() & AttrReference) != 0;
3702 } else if (fn->isValid() && fn->getFuncScope()) {
3703 ref = fn->getFuncScope()->isRefReturn();
3704 } else if (!fn->getOriginalName().empty()) {
3705 FunctionScope::FunctionInfoPtr fi =
3706 FunctionScope::GetFunctionInfo(fn->getOriginalName());
3707 if (!fi || !fi->getMaybeRefReturn()) ref = false;
3710 if (!fn->isUnused() &&
3711 ref >= 0 &&
3712 (!ref || !fn->hasAnyContext(Expression::AccessContext |
3713 Expression::ObjectContext))) {
3714 /* we dont support V in M-vectors, so leave it as an R in that
3715 case */
3716 assert(m_evalStack.get(m_evalStack.size() - 1) == StackSym::R);
3717 if (ref) {
3718 e.BoxRNop();
3719 } else {
3720 e.UnboxRNop();
3725 void EmitterVisitor::visitKids(ConstructPtr c) {
3726 for (int i = 0, nk = c->getKidCount(); i < nk; i++) {
3727 ConstructPtr kid(c->getNthKid(i));
3728 visit(kid);
3732 template<uint32_t MaxMakeSize, class Fun>
3733 bool checkKeys(ExpressionPtr init_expr, bool check_size, Fun fun) {
3734 if (init_expr->getKindOf() != Expression::KindOfExpressionList) {
3735 return false;
3738 auto el = static_pointer_cast<ExpressionList>(init_expr);
3739 int n = el->getCount();
3740 if (n < 1 || (check_size && n > MaxMakeSize)) {
3741 return false;
3744 for (int i = 0, n = el->getCount(); i < n; ++i) {
3745 ExpressionPtr ex = (*el)[i];
3746 if (ex->getKindOf() != Expression::KindOfArrayPairExpression) {
3747 return false;
3749 auto ap = static_pointer_cast<ArrayPairExpression>(ex);
3750 if (ap->isRef()) return false;
3751 if (!fun(ap)) return false;
3753 return true;
3757 * isPackedInit() returns true if this expression list looks like an
3758 * array with no keys and no ref values; e.g. array(x,y,z).
3760 * In this case we can NewPackedArray to create the array. The elements are
3761 * pushed on the stack, so we arbitrarily limit this to a small multiple of
3762 * MixedArray::SmallSize (12).
3764 bool isPackedInit(ExpressionPtr init_expr, int* size,
3765 bool check_size = true) {
3766 *size = 0;
3767 return checkKeys<MixedArray::MaxMakeSize>(init_expr, check_size,
3768 [&](ArrayPairExpressionPtr ap) {
3769 Variant key;
3771 // If we have a key...
3772 if (ap->getName() != nullptr) {
3773 // ...and it has no scalar value, bail.
3774 if (!ap->getName()->getScalarValue(key)) return false;
3776 if (key.isInteger()) {
3777 // If it's an integer key, check if it's the next packed index.
3778 if (key.asInt64Val() != *size) return false;
3779 } else if (key.isBoolean()) {
3780 // Bool to Int conversion
3781 if (static_cast<int>(key.asBooleanVal()) != *size) return false;
3782 } else {
3783 // Give up if it's not a string.
3784 if (!key.isString()) return false;
3786 int64_t i; double d;
3787 auto numtype = key.getStringData()->isNumericWithVal(i, d, false);
3789 // If it's a string of the next packed index,
3790 if (numtype != KindOfInt64 || i != *size) return false;
3794 (*size)++;
3795 return true;
3800 * isStructInit() is like isPackedInit(), but returns true if the keys are
3801 * all static strings with no duplicates.
3803 bool isStructInit(ExpressionPtr init_expr, std::vector<std::string>& keys) {
3804 return checkKeys<MixedArray::MaxStructMakeSize>(init_expr, true,
3805 [&](ArrayPairExpressionPtr ap) {
3806 auto key = ap->getName();
3807 if (key == nullptr || !key->isLiteralString()) return false;
3808 auto name = key->getLiteralString();
3809 int64_t ival;
3810 double dval;
3811 auto kind = is_numeric_string(name.data(), name.size(), &ival, &dval, 0);
3812 if (kind != KindOfNull) return false; // don't allow numeric keys
3813 if (std::find(keys.begin(), keys.end(), name) != keys.end()) return false;
3814 keys.push_back(name);
3815 return true;
3819 void EmitterVisitor::emitCall(Emitter& e,
3820 FunctionCallPtr func,
3821 ExpressionListPtr params,
3822 Offset fpiStart) {
3823 auto const numParams = params ? params->getCount() : 0;
3824 auto const unpack = func->hasUnpack();
3825 if (!func->checkUnpackParams()) {
3826 throw IncludeTimeFatalException(
3827 func, "Only the last parameter in a function call is allowed to use ...");
3830 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
3831 for (int i = 0; i < numParams; i++) {
3832 auto param = (*params)[i];
3833 emitFuncCallArg(e, param, i, param->isUnpack());
3837 if (unpack) {
3838 e.FCallUnpack(numParams);
3839 } else {
3840 e.FCall(numParams);
3844 bool EmitterVisitor::visit(ConstructPtr node) {
3845 if (!node) return false;
3847 SCOPE_ASSERT_DETAIL("visit Construct") { return node->getText(); };
3849 Emitter e(node, m_ue, *this);
3851 switch (node->getKindOf()) {
3852 case Construct::KindOfBlockStatement:
3853 case Construct::KindOfStatementList:
3854 visitKids(node);
3855 return false;
3857 case Construct::KindOfTypedefStatement: {
3858 emitMakeUnitFatal(e, "Type statements are currently only allowed at "
3859 "the top-level");
3860 return false;
3863 case Construct::KindOfDeclareStatement: {
3864 auto ds = static_pointer_cast<DeclareStatement>(node);
3865 for (auto& decl : ds->getDeclareMap()) {
3866 if (decl.first == "strict_types") {
3867 emitMakeUnitFatal(e, "strict_types declaration must not use "
3868 "block mode");
3872 visit(ds->getBlock());
3873 return false;
3876 case Construct::KindOfContinueStatement:
3877 case Construct::KindOfBreakStatement: {
3878 auto s = static_pointer_cast<Statement>(node);
3879 auto bs = static_pointer_cast<BreakStatement>(s);
3880 uint64_t destLevel = bs->getDepth();
3882 if (destLevel > m_regions.back()->getMaxBreakContinueDepth()) {
3883 std::ostringstream msg;
3884 msg << "Cannot break/continue " << destLevel << " level";
3885 if (destLevel > 1) {
3886 msg << "s";
3888 emitMakeUnitFatal(e, msg.str().c_str());
3889 return false;
3892 if (bs->is(Construct::KindOfBreakStatement)) {
3893 emitBreak(e, destLevel, bs);
3894 } else {
3895 emitContinue(e, destLevel, bs);
3898 return false;
3901 case Construct::KindOfDoStatement: {
3902 auto s = static_pointer_cast<Statement>(node);
3903 auto region = createRegion(s, Region::Kind::LoopOrSwitch);
3904 auto ds = static_pointer_cast<DoStatement>(s);
3906 Label top(e);
3907 Label& condition =
3908 registerContinue(ds, region.get(), 1, false)->m_label;
3909 Label& exit =
3910 registerBreak(ds, region.get(), 1, false)->m_label;
3912 enterRegion(region);
3913 SCOPE_EXIT { leaveRegion(region); };
3914 visit(ds->getBody());
3916 condition.set(e);
3918 ExpressionPtr c = ds->getCondExp();
3919 Emitter condEmitter(c, m_ue, *this);
3920 visitIfCondition(c, condEmitter, top, exit, false);
3923 if (exit.isUsed()) exit.set(e);
3924 return false;
3927 case Construct::KindOfCaseStatement: {
3928 // Should never be called. Handled in visitSwitch.
3929 not_reached();
3932 case Construct::KindOfCatchStatement: {
3933 // Store the current exception object in the appropriate local variable
3934 auto cs = static_pointer_cast<CatchStatement>(node);
3935 StringData* vName = makeStaticString(cs->getVariable()->getName());
3936 Id i = m_curFunc->lookupVarId(vName);
3937 emitVirtualLocal(i);
3938 e.Catch();
3939 emitSet(e);
3940 emitPop(e);
3941 visit(cs->getStmt());
3942 return false;
3945 case Construct::KindOfEchoStatement: {
3946 auto es = static_pointer_cast<EchoStatement>(node);
3947 auto exps = es->getExpressionList();
3948 int count = exps->getCount();
3949 for (int i = 0; i < count; i++) {
3950 visit((*exps)[i]);
3951 emitConvertToCell(e);
3952 e.Print();
3953 e.PopC();
3955 return false;
3958 case Construct::KindOfExpStatement: {
3959 auto s = static_pointer_cast<Statement>(node);
3960 auto es = static_pointer_cast<ExpStatement>(s);
3961 if (visit(es->getExpression())) {
3962 // reachability tracking isn't very sophisticated; emitting a pop
3963 // when we're unreachable will make the emitter think the next
3964 // position is reachable.
3965 // In that case, it will spit out Null;RetC at the end of the
3966 // function, which can cause issues if an asm expression has
3967 // already output fault funclets.
3968 if (currentPositionIsReachable()) {
3969 emitPop(e);
3970 } else {
3971 popEvalStack(StackSym::C);
3974 return false;
3977 case Construct::KindOfForStatement: {
3978 auto s = static_pointer_cast<Statement>(node);
3979 auto region = createRegion(s, Region::Kind::LoopOrSwitch);
3980 auto fs = static_pointer_cast<ForStatement>(s);
3982 if (visit(fs->getInitExp())) {
3983 emitPop(e);
3985 Label preCond(e);
3986 Label& preInc = registerContinue(fs, region.get(), 1, false)->m_label;
3987 Label& fail = registerBreak(fs, region.get(), 1, false)->m_label;
3988 ExpressionPtr condExp = fs->getCondExp();
3989 auto emit_cond = [&] (Label& tru, bool truFallthrough) {
3990 if (!condExp) return;
3991 Emitter condEmitter(condExp, m_ue, *this);
3992 visitIfCondition(condExp, condEmitter, tru, fail, truFallthrough);
3994 Label top;
3995 emit_cond(top, true);
3996 top.set(e);
3998 enterRegion(region);
3999 SCOPE_EXIT { leaveRegion(region); };
4000 visit(fs->getBody());
4002 preInc.set(e);
4003 if (visit(fs->getIncExp())) {
4004 emitPop(e);
4006 if (!condExp) {
4007 e.Jmp(top);
4008 } else {
4009 emit_cond(top, false);
4011 if (fail.isUsed()) fail.set(e);
4012 return false;
4015 case Construct::KindOfForEachStatement: {
4016 auto fe = static_pointer_cast<ForEachStatement>(node);
4017 if (fe->isAwaitAs()) {
4018 emitForeachAwaitAs(e, fe);
4019 } else {
4020 emitForeach(e, fe);
4022 return false;
4025 case Construct::KindOfGlobalStatement: {
4026 auto vars = static_pointer_cast<GlobalStatement>(node)->getVars();
4027 for (int i = 0, n = vars->getCount(); i < n; i++) {
4028 ExpressionPtr var((*vars)[i]);
4029 if (var->is(Construct::KindOfSimpleVariable)) {
4030 auto sv = static_pointer_cast<SimpleVariable>(var);
4031 if (sv->isSuperGlobal()) {
4032 continue;
4034 StringData* nLiteral = makeStaticString(sv->getName());
4035 Id i = m_curFunc->lookupVarId(nLiteral);
4036 emitVirtualLocal(i);
4037 e.String(nLiteral);
4038 markGlobalName(e);
4039 e.VGetG();
4040 emitBind(e);
4041 e.PopV();
4042 } else if (var->is(Construct::KindOfDynamicVariable)) {
4043 // global $<exp> =& $GLOBALS[<exp>]
4044 auto dv = static_pointer_cast<DynamicVariable>(var);
4045 // Get the variable name as a cell, for the LHS
4046 visit(dv->getSubExpression());
4047 emitConvertToCell(e);
4048 // Copy the variable name, for indexing into $GLOBALS
4049 e.Dup();
4050 markNameSecond(e);
4051 markGlobalName(e);
4052 e.VGetG();
4053 e.BindN();
4054 e.PopV();
4055 } else {
4056 not_implemented();
4059 return false;
4062 case Construct::KindOfIfStatement: {
4063 auto ifp = static_pointer_cast<IfStatement>(node);
4064 StatementListPtr branches(ifp->getIfBranches());
4065 int nb = branches->getCount();
4066 Label done;
4067 for (int i = 0; i < nb; i++) {
4068 auto branch = static_pointer_cast<IfBranchStatement>((*branches)[i]);
4069 Label fals;
4070 if (branch->getCondition()) {
4071 Label tru;
4072 Emitter condEmitter(branch->getCondition(), m_ue, *this);
4073 visitIfCondition(branch->getCondition(), condEmitter,
4074 tru, fals, true);
4075 if (tru.isUsed()) {
4076 tru.set(e);
4079 visit(branch->getStmt());
4080 if (currentPositionIsReachable() && i + 1 < nb) {
4081 e.Jmp(done);
4083 if (fals.isUsed()) {
4084 fals.set(e);
4087 if (done.isUsed()) {
4088 done.set(e);
4090 return false;
4093 case Construct::KindOfIfBranchStatement:
4094 not_reached(); // handled by KindOfIfStatement
4096 case Construct::KindOfReturnStatement: {
4097 auto r = static_pointer_cast<ReturnStatement>(node);
4099 char retSym = StackSym::C;
4100 if (visit(r->getRetExp())) {
4101 if (r->getRetExp()->getContext() & Expression::RefValue &&
4102 // Generators don't support returning by references
4103 !m_curFunc->isGenerator) {
4104 emitConvertToVar(e);
4105 retSym = StackSym::V;
4106 } else {
4107 emitConvertToCell(e);
4109 } else {
4110 e.Null();
4112 assert(m_evalStack.size() == 1);
4113 assert(IMPLIES(m_curFunc->isAsync || m_curFunc->isGenerator,
4114 retSym == StackSym::C));
4115 emitReturn(e, retSym, r);
4116 return false;
4119 case Construct::KindOfStaticStatement: {
4120 auto vars = static_pointer_cast<StaticStatement>(node)->getVars();
4121 for (int i = 0, n = vars->getCount(); i < n; i++) {
4122 ExpressionPtr se((*vars)[i]);
4123 assert(se->is(Construct::KindOfAssignmentExpression));
4124 auto ae = static_pointer_cast<AssignmentExpression>(se);
4125 ExpressionPtr var(ae->getVariable());
4126 ExpressionPtr value(ae->getValue());
4127 assert(var->is(Construct::KindOfSimpleVariable));
4128 auto sv = static_pointer_cast<SimpleVariable>(var);
4129 StringData* name = makeStaticString(sv->getName());
4130 Id local = m_curFunc->lookupVarId(name);
4132 if (m_staticEmitted.insert(sv->getName()).second) {
4133 Func::SVInfo svInfo;
4134 svInfo.name = name;
4135 std::ostringstream os;
4136 CodeGenerator cg(&os, CodeGenerator::PickledPHP);
4137 auto ar = std::make_shared<AnalysisResult>();
4138 value->outputPHP(cg, ar);
4139 svInfo.phpCode = makeStaticString(os.str());
4140 m_curFunc->staticVars.push_back(svInfo);
4143 if (value->isScalar()) {
4144 emitVirtualLocal(local);
4145 visit(value);
4146 emitConvertToCell(e);
4147 e.StaticLocInit(local, name);
4148 } else {
4149 Label done;
4150 emitVirtualLocal(local);
4151 e.StaticLoc(local, name);
4152 e.JmpNZ(done);
4154 emitVirtualLocal(local);
4155 visit(value);
4156 emitConvertToCell(e);
4157 emitSet(e);
4158 emitPop(e);
4160 done.set(e);
4163 return false;
4166 case Construct::KindOfSwitchStatement: {
4167 auto s = static_pointer_cast<Statement>(node);
4168 auto region = createRegion(s, Region::Kind::LoopOrSwitch);
4169 auto sw = static_pointer_cast<SwitchStatement>(node);
4171 auto cases = sw->getCases();
4172 if (!cases) {
4173 visit(sw->getExp());
4174 emitPop(e);
4175 return false;
4177 uint32_t ncase = cases->getCount();
4178 std::vector<Label> caseLabels(ncase);
4179 Label& brkTarget = registerBreak(sw, region.get(), 1, false)->m_label;
4180 Label& contTarget =
4181 registerContinue(sw, region.get(), 1, false)->m_label;
4182 // There are two different ways this can go. If the subject is a simple
4183 // variable, then we have to evaluate it every time we compare against a
4184 // case condition. Otherwise, we evaluate it once and store it in an
4185 // unnamed local. This is because (a) switch statements are equivalent
4186 // to a series of if-elses, and (b) Zend has some weird evaluation order
4187 // rules. For example, "$a == ++$a" is true but "$a[0] == ++$a[0]" is
4188 // false. In particular, if a case condition modifies the switch
4189 // subject, things behave differently depending on whether the subject
4190 // is a simple variable.
4191 auto subject = sw->getExp();
4192 bool simpleSubject = subject->is(Construct::KindOfSimpleVariable)
4193 && !static_pointer_cast<SimpleVariable>(subject)->getAlwaysStash();
4194 Id tempLocal = -1;
4195 Offset start = InvalidAbsoluteOffset;
4197 bool enabled = RuntimeOption::EvalEmitSwitch;
4198 auto call = dynamic_pointer_cast<SimpleFunctionCall>(subject);
4200 SwitchState state;
4201 bool didSwitch = false;
4202 if (enabled) {
4203 MaybeDataType stype = analyzeSwitch(sw, state);
4204 if (stype) {
4205 e.incStat(stype == KindOfInt64 ? Stats::Switch_Integer
4206 : Stats::Switch_String,
4208 if (state.cases.empty()) {
4209 // If there are no non-default cases, evaluate the subject for
4210 // side effects and fall through. If there's a default case it
4211 // will be emitted immediately after this.
4212 visit(sw->getExp());
4213 emitPop(e);
4214 } else if (stype == KindOfInt64) {
4215 emitIntegerSwitch(e, sw, caseLabels, brkTarget, state);
4216 } else {
4217 assert(isStringType(*stype));
4218 emitStringSwitch(e, sw, caseLabels, brkTarget, state);
4220 didSwitch = true;
4223 if (!didSwitch) {
4224 e.incStat(Stats::Switch_Generic, 1);
4225 if (!simpleSubject) {
4226 // Evaluate the subject once and stash it in a local
4227 tempLocal = m_curFunc->allocUnnamedLocal();
4228 emitVirtualLocal(tempLocal);
4229 visit(subject);
4230 emitConvertToCell(e);
4231 emitSet(e);
4232 emitPop(e);
4233 start = m_ue.bcPos();
4236 int defI = -1;
4237 for (uint32_t i = 0; i < ncase; i++) {
4238 auto c = static_pointer_cast<CaseStatement>((*cases)[i]);
4239 ExpressionPtr condition = c->getCondition();
4240 if (condition) {
4241 if (simpleSubject) {
4242 // Evaluate the subject every time.
4243 visit(subject);
4244 emitConvertToCellOrLoc(e);
4245 visit(condition);
4246 emitConvertToCell(e);
4247 emitConvertSecondToCell(e);
4248 } else {
4249 emitVirtualLocal(tempLocal);
4250 emitCGet(e);
4251 visit(condition);
4252 emitConvertToCell(e);
4254 e.Eq();
4255 e.JmpNZ(caseLabels[i]);
4256 } else if (LIKELY(defI == -1)) {
4257 // Default clause.
4258 defI = i;
4259 } else {
4260 throw IncludeTimeFatalException(
4261 c, "Switch statements may only contain one default: clause");
4264 if (defI != -1) {
4265 e.Jmp(caseLabels[defI]);
4266 } else {
4267 e.Jmp(brkTarget);
4270 for (uint32_t i = 0; i < ncase; i++) {
4271 caseLabels[i].set(e);
4272 auto c = static_pointer_cast<CaseStatement>((*cases)[i]);
4273 enterRegion(region);
4274 SCOPE_EXIT { leaveRegion(region); };
4275 visit(c->getStatement());
4277 if (brkTarget.isUsed()) brkTarget.set(e);
4278 if (contTarget.isUsed()) contTarget.set(e);
4279 if (!didSwitch && !simpleSubject) {
4280 // Null out temp local, to invoke any needed refcounting
4281 assert(tempLocal >= 0);
4282 assert(start != InvalidAbsoluteOffset);
4283 newFaultRegionAndFunclet(start, m_ue.bcPos(),
4284 new UnsetUnnamedLocalThunklet(tempLocal));
4285 emitVirtualLocal(tempLocal);
4286 emitUnset(e);
4287 m_curFunc->freeUnnamedLocal(tempLocal);
4289 return false;
4292 case Construct::KindOfThrowStatement: {
4293 visitKids(node);
4294 emitConvertToCell(e);
4295 e.Throw();
4296 return false;
4299 case Construct::KindOfFinallyStatement: {
4300 auto s = static_pointer_cast<Statement>(node);
4301 auto region = createRegion(s, Region::Kind::Finally);
4302 enterRegion(region);
4303 SCOPE_EXIT { leaveRegion(region); };
4305 auto fs = static_pointer_cast<FinallyStatement>(node);
4306 visit(fs->getBody());
4307 return false;
4310 case Construct::KindOfTryStatement: {
4311 auto s = static_pointer_cast<Statement>(node);
4312 auto region = createRegion(s, Region::Kind::TryFinally);
4313 if (!m_evalStack.empty()) {
4314 InvariantViolation(
4315 "Emitter detected that the evaluation stack is not empty "
4316 "at the beginning of a try region: %d", m_ue.bcPos());
4319 auto ts = static_pointer_cast<TryStatement>(node);
4320 auto f = static_pointer_cast<FinallyStatement>(ts->getFinally());
4322 Offset start = m_ue.bcPos();
4323 Offset end;
4324 Label after;
4327 if (f) {
4328 enterRegion(region);
4330 SCOPE_EXIT {
4331 if (f) {
4332 leaveRegion(region);
4336 visit(ts->getBody());
4338 StatementListPtr catches = ts->getCatches();
4339 int catch_count = catches->getCount();
4340 if (catch_count > 0) {
4341 // include the jump out of the try-catch block in the
4342 // exception handler address range
4343 e.Jmp(after);
4345 end = m_ue.bcPos();
4346 if (!m_evalStack.empty()) {
4347 InvariantViolation("Emitter detected that the evaluation stack "
4348 "is not empty at the end of a try region: %d",
4349 end);
4352 if (catch_count > 0) {
4353 CatchRegion* r = new CatchRegion(start, end);
4354 m_catchRegions.push_back(r);
4356 bool firstHandler = true;
4357 for (int i = 0; i < catch_count; i++) {
4358 auto c = static_pointer_cast<CatchStatement>((*catches)[i]);
4359 StringData* eName = makeStaticString(c->getOriginalClassName());
4361 // If there's already a catch of this class, skip;
4362 // the first one wins
4363 if (r->m_names.find(eName) == r->m_names.end()) {
4364 // Don't let execution of the try body, or the
4365 // previous catch body,
4366 // fall into here.
4367 if (!firstHandler) {
4368 e.Jmp(after);
4369 } else {
4370 firstHandler = false;
4373 Label* label = new Label(e);
4374 r->m_names.insert(eName);
4375 r->m_catchLabels.push_back(std::pair<StringData*, Label*>(eName,
4376 label));
4377 visit(c);
4383 Offset end_catches = m_ue.bcPos();
4384 if (after.isUsed()) after.set(e);
4386 if (f) {
4387 region->m_finallyLabel.set(e);
4388 visit(f);
4389 emitFinallyEpilogue(e, region.get());
4390 auto func = getFunclet(f);
4391 if (func == nullptr) {
4392 auto thunklet =
4393 new FinallyThunklet(f, m_curFunc->numLiveIterators());
4394 func = addFunclet(f, thunklet);
4396 newFaultRegion(start, end_catches, &func->m_entry);
4399 return false;
4402 case Construct::KindOfUnsetStatement: {
4403 auto exps = static_pointer_cast<UnsetStatement>(node)->getExps();
4404 for (int i = 0, n = exps->getCount(); i < n; i++) {
4405 emitVisitAndUnset(e, (*exps)[i]);
4407 return false;
4410 case Construct::KindOfWhileStatement: {
4411 auto s = static_pointer_cast<Statement>(node);
4412 auto region = createRegion(s, Region::Kind::LoopOrSwitch);
4413 auto ws = static_pointer_cast<WhileStatement>(s);
4414 ExpressionPtr condExp(ws->getCondExp());
4415 Label& lcontinue = registerContinue(ws, region.get(), 1,
4416 false)->m_label;
4417 Label& fail = registerBreak(ws, region.get(), 1, false)->m_label;
4418 Label top;
4419 auto emit_cond = [&] (Label& tru, bool truFallthrough) {
4420 Emitter condEmitter(condExp, m_ue, *this);
4421 visitIfCondition(condExp, condEmitter, tru, fail, truFallthrough);
4423 emit_cond(top, true);
4424 top.set(e);
4426 enterRegion(region);
4427 SCOPE_EXIT { leaveRegion(region); };
4428 visit(ws->getBody());
4430 if (lcontinue.isUsed()) lcontinue.set(e);
4431 emit_cond(top, false);
4432 if (fail.isUsed()) fail.set(e);
4433 return false;
4436 case Construct::KindOfInterfaceStatement:
4437 case Construct::KindOfClassStatement: {
4438 emitClass(e, node->getClassScope(), false);
4439 return false;
4442 case Construct::KindOfClassVariable:
4443 case Construct::KindOfClassConstant:
4444 case Construct::KindOfMethodStatement:
4445 // handled by emitClass
4446 not_reached();
4448 case Construct::KindOfFunctionStatement: {
4449 auto m = static_pointer_cast<MethodStatement>(node);
4450 // Only called for fn defs not on the top level
4451 assert(!node->getClassScope()); // Handled directly by emitClass().
4452 StringData* nName = makeStaticString(m->getOriginalName());
4453 FuncEmitter* fe = m_ue.newFuncEmitter(nName);
4454 e.DefFunc(fe->id());
4455 postponeMeth(m, fe, false);
4456 return false;
4459 case Construct::KindOfGotoStatement: {
4460 auto g = static_pointer_cast<GotoStatement>(node);
4461 StringData* nName = makeStaticString(g->label());
4462 emitGoto(e, nName, g);
4463 return false;
4466 case Construct::KindOfLabelStatement: {
4467 auto l = static_pointer_cast<LabelStatement>(node);
4468 StringData* nName = makeStaticString(l->label());
4469 registerGoto(l, m_regions.back().get(), nName, false)
4470 ->m_label.set(e);
4471 return false;
4473 case Construct::KindOfStatement:
4474 case Construct::KindOfUseTraitStatement:
4475 case Construct::KindOfClassRequireStatement:
4476 case Construct::KindOfTraitPrecStatement:
4477 case Construct::KindOfTraitAliasStatement: {
4478 not_implemented();
4480 case Construct::KindOfUnaryOpExpression: {
4481 auto u = static_pointer_cast<UnaryOpExpression>(node);
4482 int op = u->getOp();
4484 if (op == T_UNSET) {
4485 // php doesnt have an unset expression, but hphp's optimizations
4486 // sometimes introduce them
4487 auto exp = u->getExpression();
4488 if (exp->is(Construct::KindOfExpressionList)) {
4489 auto exps = static_pointer_cast<ExpressionList>(exp);
4490 if (exps->getListKind() == ExpressionList::ListKindParam) {
4491 for (int i = 0, n = exps->getCount(); i < n; i++) {
4492 emitVisitAndUnset(e, (*exps)[i]);
4494 e.Null();
4495 return true;
4498 emitVisitAndUnset(e, exp);
4499 e.Null();
4500 return true;
4503 if (op == T_ARRAY) {
4504 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
4505 emitArrayInit(e, el);
4506 return true;
4509 if (op == T_DICT) {
4510 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
4511 emitArrayInit(e, el, HeaderKind::Dict);
4512 return true;
4515 if (op == T_VEC) {
4516 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
4517 emitArrayInit(e, el, HeaderKind::VecArray);
4518 return true;
4521 if (op == T_KEYSET) {
4522 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
4523 emitArrayInit(e, el, HeaderKind::Keyset);
4524 return true;
4527 if (op == T_ISSET) {
4528 auto list = dynamic_pointer_cast<ExpressionList>(u->getExpression());
4529 if (list) {
4530 // isset($a, $b, ...) ==> isset($a) && isset($b) && ...
4531 Label done;
4532 int n = list->getCount();
4533 for (int i = 0; i < n - 1; ++i) {
4534 visit((*list)[i]);
4535 emitIsset(e);
4536 e.Dup();
4537 e.JmpZ(done);
4538 emitPop(e);
4540 // Treat the last one specially; let it fall through
4541 visit((*list)[n - 1]);
4542 emitIsset(e);
4543 done.set(e);
4544 } else {
4545 // Simple case
4546 visit(u->getExpression());
4547 emitIsset(e);
4549 return true;
4550 } else if (op == '+' || op == '-') {
4551 e.Int(0);
4554 Id oldErrorLevelLoc = -1;
4555 Offset start = InvalidAbsoluteOffset;
4556 if (op == '@') {
4557 oldErrorLevelLoc = m_curFunc->allocUnnamedLocal();
4558 emitVirtualLocal(oldErrorLevelLoc);
4559 auto idx = m_evalStack.size() - 1;
4560 e.Silence(m_evalStack.getLoc(idx), SilenceOp::Start);
4561 start = m_ue.bcPos();
4564 ExpressionPtr exp = u->getExpression();
4565 if (exp && visit(exp)) {
4566 if (op != T_EMPTY && op != T_INC && op != T_DEC) {
4567 emitConvertToCell(e);
4569 } else if (op == T_EXIT) {
4570 // exit without an expression is treated as exit(0)
4571 e.Int(0);
4572 } else {
4573 // __FILE__ and __DIR__ are special unary ops that don't
4574 // have expressions
4575 assert(op == T_FILE || op == T_DIR);
4577 switch (op) {
4578 case T_INC:
4579 case T_DEC: {
4580 // $this++ is a no-op
4581 if (auto var = dynamic_pointer_cast<SimpleVariable>(exp)) {
4582 if (var->isThis()) break;
4585 auto const cop = [&] {
4586 if (op == T_INC) {
4587 if (RuntimeOption::IntsOverflowToInts) {
4588 return u->getFront() ? IncDecOp::PreInc : IncDecOp::PostInc;
4590 return u->getFront() ? IncDecOp::PreIncO : IncDecOp::PostIncO;
4592 if (RuntimeOption::IntsOverflowToInts) {
4593 return u->getFront() ? IncDecOp::PreDec : IncDecOp::PostDec;
4595 return u->getFront() ? IncDecOp::PreDecO : IncDecOp::PostDecO;
4596 }();
4597 emitIncDec(e, cop);
4598 break;
4600 case T_EMPTY: emitEmpty(e); break;
4601 case T_CLONE: e.Clone(); break;
4602 case '+':
4603 RuntimeOption::IntsOverflowToInts ? e.Add() : e.AddO();
4604 break;
4605 case '-':
4606 RuntimeOption::IntsOverflowToInts ? e.Sub() : e.SubO();
4607 break;
4608 case '!': e.Not(); break;
4609 case '~': e.BitNot(); break;
4610 case '(': break;
4611 case T_INT_CAST: e.CastInt(); break;
4612 case T_DOUBLE_CAST: e.CastDouble(); break;
4613 case T_STRING_CAST: e.CastString(); break;
4614 case T_ARRAY_CAST: e.CastArray(); break;
4615 case T_OBJECT_CAST: e.CastObject(); break;
4616 case T_BOOL_CAST: e.CastBool(); break;
4617 case T_UNSET_CAST: emitPop(e); e.Null(); break;
4618 case T_EXIT: e.Exit(); break;
4619 case '@': {
4620 assert(oldErrorLevelLoc >= 0);
4621 assert(start != InvalidAbsoluteOffset);
4622 newFaultRegionAndFunclet(start, m_ue.bcPos(),
4623 new RestoreErrorReportingThunklet(oldErrorLevelLoc));
4624 emitRestoreErrorReporting(e, oldErrorLevelLoc);
4625 m_curFunc->freeUnnamedLocal(oldErrorLevelLoc);
4626 break;
4628 case T_PRINT: e.Print(); break;
4629 case T_EVAL: e.Eval(); break;
4630 case T_FILE: {
4631 e.File();
4632 break;
4634 case T_DIR: {
4635 e.Dir();
4636 break;
4638 default:
4639 assert(false);
4641 return true;
4644 case Construct::KindOfAssignmentExpression: {
4645 auto ae = static_pointer_cast<AssignmentExpression>(node);
4646 ExpressionPtr rhs = ae->getValue();
4647 Id tempLocal = -1;
4648 Offset start = InvalidAbsoluteOffset;
4650 if (ae->isRhsFirst()) {
4651 assert(!rhs->hasContext(Expression::RefValue));
4652 tempLocal = emitVisitAndSetUnnamedL(e, rhs);
4653 start = m_ue.bcPos();
4656 visit(ae->getVariable());
4657 emitClsIfSPropBase(e);
4659 if (ae->isRhsFirst()) {
4660 emitPushAndFreeUnnamedL(e, tempLocal, start);
4661 } else {
4662 visit(rhs);
4665 if (rhs->hasContext(Expression::RefValue)) {
4666 emitConvertToVar(e);
4667 emitBind(e);
4668 if (ae->hasAnyContext(Expression::AccessContext|
4669 Expression::ObjectContext|
4670 Expression::ExistContext)) {
4672 * hphpc optimizations can result in
4673 * ($x =& $y)->foo or ($x =& $y)['foo'] or empty($x =& $y)
4675 emitConvertToCellIfVar(e);
4677 } else {
4678 emitConvertToCell(e);
4679 emitSet(e);
4681 return true;
4684 case Construct::KindOfBinaryOpExpression: {
4685 auto b = static_pointer_cast<BinaryOpExpression>(node);
4686 int op = b->getOp();
4687 if (b->isAssignmentOp()) {
4688 visit(b->getExp1());
4689 emitClsIfSPropBase(e);
4690 visit(b->getExp2());
4691 emitConvertToCell(e);
4692 emitSetOp(e, op);
4693 return true;
4696 if (b->isShortCircuitOperator()) {
4697 Label tru, fls, done;
4698 visitIfCondition(b, e, tru, fls, false);
4699 if (fls.isUsed()) fls.set(e);
4700 if (currentPositionIsReachable()) {
4701 e.False();
4702 e.Jmp(done);
4704 if (tru.isUsed()) tru.set(e);
4705 if (currentPositionIsReachable()) {
4706 e.True();
4708 done.set(e);
4709 return true;
4712 if (op == T_INSTANCEOF) {
4713 visit(b->getExp1());
4714 emitConvertToCell(e);
4715 ExpressionPtr second = b->getExp2();
4716 if (second->isScalar()) {
4717 auto scalar = dynamic_pointer_cast<ScalarExpression>(second);
4718 bool notQuoted = scalar && !scalar->isQuoted();
4719 std::string s = second->getLiteralString();
4721 const auto isame =
4722 [](const std::string& a, const std::string& b) {
4723 return (a.size() == b.size()) &&
4724 !strncasecmp(a.c_str(), b.c_str(), a.size());
4727 if (notQuoted && isame(s, "static")) {
4728 // Can't resolve this to a literal name at emission time
4729 static const StringData* fname
4730 = makeStaticString("get_called_class");
4731 Offset fpiStart = m_ue.bcPos();
4732 e.FPushFuncD(0, fname);
4734 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
4736 e.FCall(0);
4737 e.UnboxR();
4738 e.InstanceOf();
4739 } else if (s != "") {
4740 ClassScopeRawPtr cls = second->getClassScope();
4741 bool isTrait = cls && cls->isTrait();
4742 bool isSelf = notQuoted && isame(s, "self");
4743 bool isParent = notQuoted && isame(s, "parent");
4745 if (isTrait && (isSelf || isParent)) {
4746 emitConvertToCell(e);
4747 if (isSelf) {
4748 e.Self();
4749 } else if (isParent) {
4750 e.Parent();
4753 e.NameA();
4754 e.InstanceOf();
4755 } else {
4756 if (cls) {
4757 if (isSelf) {
4758 s = cls->getScopeName();
4759 } else if (isParent) {
4760 s = cls->getOriginalParent();
4764 StringData* nLiteral = makeStaticString(s);
4765 e.InstanceOfD(nLiteral);
4767 } else {
4768 visit(b->getExp2());
4769 emitConvertToCell(e);
4770 e.InstanceOf();
4772 } else {
4773 visit(b->getExp2());
4774 emitConvertToCell(e);
4775 e.InstanceOf();
4777 return true;
4780 if (op == T_COLLECTION) {
4781 emitCollectionInit(e, b);
4782 return true;
4785 if (op == T_PIPE) {
4786 Id pipeVar = emitVisitAndSetUnnamedL(e, b->getExp1());
4787 allocPipeLocal(pipeVar);
4788 visit(b->getExp2());
4789 emitConvertToCell(e);
4790 releasePipeLocal(pipeVar);
4791 emitPushAndFreeUnnamedL(e, pipeVar, m_ue.bcPos());
4792 e.PopC();
4793 return true;
4796 visit(b->getExp1());
4797 emitConvertToCellOrLoc(e);
4798 visit(b->getExp2());
4799 emitConvertToCell(e);
4800 emitConvertSecondToCell(e);
4801 switch (op) {
4802 case T_LOGICAL_XOR: e.Xor(); break;
4803 case '|': e.BitOr(); break;
4804 case '&': e.BitAnd(); break;
4805 case '^': e.BitXor(); break;
4806 case '.': e.Concat(); break;
4807 case '+':
4808 RuntimeOption::IntsOverflowToInts ? e.Add() : e.AddO();
4809 break;
4810 case '-':
4811 RuntimeOption::IntsOverflowToInts ? e.Sub() : e.SubO();
4812 break;
4813 case '*':
4814 RuntimeOption::IntsOverflowToInts ? e.Mul() : e.MulO();
4815 break;
4816 case '/': e.Div(); break;
4817 case '%': e.Mod(); break;
4818 case T_SL: e.Shl(); break;
4819 case T_SR: e.Shr(); break;
4820 case T_IS_IDENTICAL: e.Same(); break;
4821 case T_IS_NOT_IDENTICAL: e.NSame(); break;
4822 case T_IS_EQUAL: e.Eq(); break;
4823 case T_IS_NOT_EQUAL: e.Neq(); break;
4824 case '<': e.Lt(); break;
4825 case T_IS_SMALLER_OR_EQUAL: e.Lte(); break;
4826 case '>': e.Gt(); break;
4827 case T_IS_GREATER_OR_EQUAL: e.Gte(); break;
4828 case T_SPACESHIP: e.Cmp(); break;
4829 case T_POW: e.Pow(); break;
4830 default: assert(false);
4832 return true;
4835 case Construct::KindOfClassConstantExpression: {
4836 auto cc = static_pointer_cast<ClassConstantExpression>(node);
4837 auto const nName = makeStaticString(cc->getConName());
4838 auto const getOriginalClassName = [&] {
4839 const std::string& clsName = cc->getOriginalClassName();
4840 return makeStaticString(clsName);
4843 // We treat ::class as a class constant in the AST and the
4844 // parser, but at the bytecode and runtime level it isn't
4845 // one.
4846 auto const emitClsCns = [&] {
4847 if (cc->isColonColonClass()) {
4848 e.NameA();
4849 return;
4851 e.ClsCns(nName);
4853 auto const noClassAllowed = [&] {
4854 auto const nCls = getOriginalClassName();
4855 std::ostringstream s;
4856 s << "Cannot access " << nCls->data() << "::" << nName->data() <<
4857 " when no class scope is active";
4858 throw IncludeTimeFatalException(cc, s.str().c_str());
4861 if (cc->isStatic()) {
4862 // static::Constant
4863 e.LateBoundCls();
4864 emitClsCns();
4865 } else if (cc->getClass()) {
4866 // $x::Constant
4867 ExpressionPtr cls(cc->getClass());
4868 visit(cls);
4869 emitAGet(e);
4870 emitClsCns();
4871 } else if (cc->getOriginalClassScope() &&
4872 !cc->getOriginalClassScope()->isTrait()) {
4873 // C::Constant inside a class
4874 auto nCls = getOriginalClassName();
4875 if (cc->isColonColonClass()) {
4876 e.String(nCls);
4877 } else {
4878 e.ClsCnsD(nName, nCls);
4880 } else if (cc->isSelf()) {
4881 // self::Constant inside trait or pseudomain
4882 e.Self();
4883 if (cc->isColonColonClass() &&
4884 cc->getFunctionScope()->inPseudoMain()) {
4885 noClassAllowed();
4887 emitClsCns();
4888 } else if (cc->isParent()) {
4889 // parent::Constant inside trait or pseudomain
4890 e.Parent();
4891 if (cc->isColonColonClass() &&
4892 cc->getFunctionScope()->inPseudoMain()) {
4893 noClassAllowed();
4895 emitClsCns();
4896 } else {
4897 // C::Constant inside a trait or pseudomain
4898 // Be careful to keep this case here after the isSelf and
4899 // isParent cases because StaticClassName::resolveClass()
4900 // will set cc->originalClassName to the trait's name for
4901 // the isSelf and isParent cases, but self and parent must
4902 // be resolved dynamically when used inside of traits.
4903 auto nCls = getOriginalClassName();
4904 if (cc->isColonColonClass()) noClassAllowed();
4905 e.ClsCnsD(nName, nCls);
4907 return true;
4910 case Construct::KindOfConstantExpression: {
4911 auto c = static_pointer_cast<ConstantExpression>(node);
4912 if (c->isNull()) {
4913 e.Null();
4914 } else if (c->isBoolean()) {
4915 if (c->getBooleanValue()) {
4916 e.True();
4917 } else {
4918 e.False();
4920 return true;
4921 } else {
4922 std::string nameStr = c->getOriginalName();
4923 StringData* nName = makeStaticString(nameStr);
4924 if (c->hadBackslash()) {
4925 e.CnsE(nName);
4926 } else {
4927 const std::string& nonNSName = c->getNonNSOriginalName();
4928 if (nonNSName != nameStr) {
4929 StringData* nsName = nName;
4930 nName = makeStaticString(nonNSName);
4931 e.CnsU(nsName, nName);
4932 } else {
4933 e.Cns(makeStaticString(c->getName()));
4937 return true;
4940 case Construct::KindOfEncapsListExpression: {
4941 auto el = static_pointer_cast<EncapsListExpression>(node);
4942 auto args = el->getExpressions();
4943 int n = args ? args->getCount() : 0;
4944 int i = 0;
4945 FPIRegionRecorder* fpi = nullptr;
4946 if (el->getType() == '`') {
4947 const static StringData* s_shell_exec =
4948 makeStaticString("shell_exec");
4949 Offset fpiStart = m_ue.bcPos();
4950 e.FPushFuncD(1, s_shell_exec);
4951 fpi = new FPIRegionRecorder(this, m_ue, m_evalStack, fpiStart);
4954 if (n) {
4955 visit((*args)[i++]);
4956 emitConvertToCellOrLoc(e);
4957 if (i == n) {
4958 emitConvertToCell(e);
4959 e.CastString();
4960 } else {
4961 while (i < n) {
4962 visit((*args)[i++]);
4963 emitConvertToCell(e);
4964 emitConvertSecondToCell(e);
4965 e.Concat();
4968 } else {
4969 e.String(staticEmptyString());
4972 if (el->getType() == '`') {
4973 emitConvertToCell(e);
4974 e.FPassC(0);
4975 delete fpi;
4976 e.FCall(1);
4978 return true;
4981 case Construct::KindOfArrayElementExpression: {
4982 auto ae = static_pointer_cast<ArrayElementExpression>(node);
4983 if (!ae->isSuperGlobal() || !ae->getOffset()) {
4984 visit(ae->getVariable());
4985 // XHP syntax allows for expressions like "($a =& $b)[0]". We
4986 // handle this by unboxing the var produced by "($a =& $b)".
4987 emitConvertToCellIfVar(e);
4990 ExpressionPtr offset = ae->getOffset();
4991 Variant v;
4992 if (!ae->isSuperGlobal() && offset &&
4993 offset->getScalarValue(v) && (v.isInteger() || v.isString())) {
4994 if (v.isString()) {
4995 m_evalStack.push(StackSym::T);
4996 m_evalStack.setString(
4997 makeStaticString(v.toCStrRef().get()));
4998 } else {
4999 m_evalStack.push(StackSym::I);
5000 m_evalStack.setInt(v.asInt64Val());
5002 markElem(e);
5003 } else if (visit(offset)) {
5004 emitConvertToCellOrLoc(e);
5005 if (ae->isSuperGlobal()) {
5006 markGlobalName(e);
5007 } else {
5008 markElem(e);
5010 } else {
5011 markNewElem(e);
5013 if (!ae->hasAnyContext(Expression::AccessContext|
5014 Expression::ObjectContext)) {
5015 m_tempLoc = ae->getRange();
5017 return true;
5020 case Construct::KindOfSimpleFunctionCall: {
5021 auto call = static_pointer_cast<SimpleFunctionCall>(node);
5022 auto params = call->getParams();
5024 if (call->isFatalFunction()) {
5025 if (params && params->getCount() == 1) {
5026 ExpressionPtr p = (*params)[0];
5027 Variant v;
5028 if (p->getScalarValue(v)) {
5029 assert(v.isString());
5030 StringData* msg = makeStaticString(v.toString());
5031 auto exn = IncludeTimeFatalException(call, "%s", msg->data());
5032 exn.setParseFatal(call->isParseFatalFunction());
5033 throw exn;
5035 not_reached();
5037 } else if (emitCallUserFunc(e, call)) {
5038 return true;
5039 } else if (call->isCallToFunction("array_key_exists")) {
5040 if (params && params->getCount() == 2) {
5041 visit((*params)[0]);
5042 emitConvertToCell(e);
5043 visit((*params)[1]);
5044 emitConvertToCell(e);
5045 call->changeToBytecode();
5046 e.AKExists();
5047 return true;
5049 } else if (call->isCallToFunction("hh\\asm")) {
5050 if (emitInlineHHAS(e, call)) return true;
5051 } else if (call->isCallToFunction("hh\\invariant")) {
5052 if (emitHHInvariant(e, call)) return true;
5053 } else if (call->isCallToFunction("hh\\idx") &&
5054 !Option::JitEnableRenameFunction) {
5055 if (params && (params->getCount() == 2 || params->getCount() == 3)) {
5056 visit((*params)[0]);
5057 emitConvertToCell(e);
5058 visit((*params)[1]);
5059 emitConvertToCell(e);
5060 if (params->getCount() == 2) {
5061 e.Null();
5062 } else {
5063 visit((*params)[2]);
5064 emitConvertToCell(e);
5066 call->changeToBytecode();
5067 e.Idx();
5068 return true;
5070 } else if (call->isCallToFunction("hphp_array_idx")) {
5071 if (params && params->getCount() == 3) {
5072 visit((*params)[0]);
5073 emitConvertToCell(e);
5074 visit((*params)[1]);
5075 emitConvertToCell(e);
5076 visit((*params)[2]);
5077 emitConvertToCell(e);
5078 call->changeToBytecode();
5079 e.ArrayIdx();
5080 return true;
5082 } else if (call->isCallToFunction("max")) {
5083 if (params && params->getCount() == 2) {
5084 emitFuncCall(e, call, "__SystemLib\\max2", params);
5085 return true;
5087 } else if (call->isCallToFunction("min")) {
5088 if (params && params->getCount() == 2) {
5089 emitFuncCall(e, call, "__SystemLib\\min2", params);
5090 return true;
5092 } else if (call->isCallToFunction("define")) {
5093 if (params && params->getCount() == 2) {
5094 ExpressionPtr p0 = (*params)[0];
5095 Variant v0;
5096 if (p0->getScalarValue(v0) && v0.isString()) {
5097 const StringData* cname =
5098 makeStaticString(v0.toString());
5099 visit((*params)[1]);
5100 emitConvertToCell(e);
5101 e.DefCns(cname);
5102 return true;
5105 } else if (call->isCallToFunction("assert")) {
5106 // Special-case some logic around emitting assert(), or jumping around
5107 // it. This all applies only for direct calls to assert() -- dynamic
5108 // calls don't get this special logic, and don't in PHP7 either.
5110 if (!RuntimeOption::AssertEmitted) {
5111 e.True();
5112 return true;
5115 // We need to emit an ini_get around all asserts to check if the
5116 // zend.assertions option is enabled -- you can switch between 0 and 1
5117 // at runtime, and having it set to 0 disables the assert from running,
5118 // including side effects of function arguments, so we need to jump
5119 // around it if so. (The -1 value of zend.assertions corresponds to
5120 // AssertEmitted being set to 0 above, and is not changeable at
5121 // runtime.)
5122 Label disabled, after;
5123 e.String(s_zend_assertions.get());
5124 e.FCallBuiltin(1, 1, s_ini_get.get());
5125 e.UnboxRNop();
5126 e.Int(0);
5127 e.Gt();
5128 e.JmpZ(disabled);
5130 emitFuncCall(e, call, "__SystemLib\\assert", call->getParams());
5131 emitConvertToCell(e);
5132 e.Jmp(after);
5134 disabled.set(e);
5135 e.True();
5137 after.set(e);
5138 return true;
5139 } else if (emitSystemLibVarEnvFunc(e, call)) {
5140 return true;
5141 } else if (call->isCallToFunction("array_slice") &&
5142 params && params->getCount() == 2 &&
5143 !Option::JitEnableRenameFunction) {
5144 ExpressionPtr p0 = (*params)[0];
5145 ExpressionPtr p1 = (*params)[1];
5146 Variant v1;
5147 if (p0->getKindOf() == Construct::KindOfSimpleFunctionCall &&
5148 p1->getScalarValue(v1) && v1.isInteger()) {
5149 auto innerCall = static_pointer_cast<SimpleFunctionCall>(p0);
5150 auto innerParams = innerCall->getParams();
5151 if (innerCall->isCallToFunction("func_get_args") &&
5152 (!innerParams || innerParams->getCount() == 0)) {
5153 params->removeElement(0);
5154 emitFuncCall(e, innerCall,
5155 "__SystemLib\\func_slice_args", params);
5156 return true;
5159 // fall through
5160 } else if ((call->isCallToFunction("class_exists") ||
5161 call->isCallToFunction("interface_exists") ||
5162 call->isCallToFunction("trait_exists"))
5163 && params
5164 && (params->getCount() == 1 || params->getCount() == 2)) {
5165 // Push name
5166 emitNameString(e, (*params)[0]);
5167 emitConvertToCell(e);
5168 e.CastString();
5170 // Push autoload, defaulting to true
5171 if (params->getCount() == 1) {
5172 e.True();
5173 } else {
5174 visit((*params)[1]);
5175 emitConvertToCell(e);
5176 e.CastBool();
5178 if (call->isCallToFunction("class_exists")) {
5179 e.OODeclExists(OODeclExistsOp::Class);
5180 } else if (call->isCallToFunction("interface_exists")) {
5181 e.OODeclExists(OODeclExistsOp::Interface);
5182 } else {
5183 assert(call->isCallToFunction("trait_exists"));
5184 e.OODeclExists(OODeclExistsOp::Trait);
5186 return true;
5187 } else if (call->isCallToFunction("get_class") &&
5188 !params &&
5189 call->getClassScope() &&
5190 !call->getClassScope()->isTrait()) {
5191 StringData* name =
5192 makeStaticString(call->getClassScope()->getScopeName());
5193 e.String(name);
5194 return true;
5195 } else if (call->isCallToFunction("dict") &&
5196 (m_ue.m_isHHFile || Option::EnableHipHopSyntax)) {
5197 emitFuncCall(e, call, "HH\\dict", params);
5198 return true;
5199 } else if (call->isCallToFunction("vec") &&
5200 (m_ue.m_isHHFile || Option::EnableHipHopSyntax)) {
5201 emitFuncCall(e, call, "HH\\vec", params);
5202 return true;
5203 } else if (call->isCallToFunction("is_vec") &&
5204 (m_ue.m_isHHFile || Option::EnableHipHopSyntax)) {
5205 emitFuncCall(e, call, "HH\\is_vec", params);
5206 return true;
5207 } else if (emitConstantFuncCall(e, call)) {
5208 return true;
5210 #define TYPE_CONVERT_INSTR(what, What) \
5211 else if (call->isCallToFunction(#what"val") && \
5212 params && params->getCount() == 1) { \
5213 visit((*params)[0]); \
5214 emitConvertToCell(e); \
5215 e.Cast ## What(); \
5216 return true; \
5218 TYPE_CONVERT_INSTR(bool, Bool)
5219 TYPE_CONVERT_INSTR(int, Int)
5220 TYPE_CONVERT_INSTR(double, Double)
5221 TYPE_CONVERT_INSTR(float, Double)
5222 TYPE_CONVERT_INSTR(str, String)
5223 #undef TYPE_CONVERT_INSTR
5225 #define TYPE_CHECK_INSTR(what, What) \
5226 else if (call->isCallToFunction("is_"#what) && \
5227 params && params->getCount() == 1) { \
5228 visit((*call->getParams())[0]); \
5229 emitIsType(e, IsTypeOp::What); \
5230 return true; \
5233 TYPE_CHECK_INSTR(null, Null)
5234 TYPE_CHECK_INSTR(object, Obj)
5235 TYPE_CHECK_INSTR(array, Arr)
5236 TYPE_CHECK_INSTR(string, Str)
5237 TYPE_CHECK_INSTR(int, Int)
5238 TYPE_CHECK_INSTR(integer, Int)
5239 TYPE_CHECK_INSTR(long, Int)
5240 TYPE_CHECK_INSTR(bool, Bool)
5241 TYPE_CHECK_INSTR(double, Dbl)
5242 TYPE_CHECK_INSTR(real, Dbl)
5243 TYPE_CHECK_INSTR(float, Dbl)
5244 TYPE_CHECK_INSTR(scalar, Scalar)
5245 #undef TYPE_CHECK_INSTR
5246 // fall through
5248 case Construct::KindOfDynamicFunctionCall: {
5249 emitFuncCall(e, static_pointer_cast<FunctionCall>(node));
5250 return true;
5253 case Construct::KindOfIncludeExpression: {
5254 auto ie = static_pointer_cast<IncludeExpression>(node);
5255 if (ie->isReqLit()) {
5256 StringData* nValue = makeStaticString(ie->includePath());
5257 e.String(nValue);
5258 } else {
5259 visit(ie->getExpression());
5260 emitConvertToCell(e);
5262 switch (ie->getOp()) {
5263 case T_INCLUDE:
5264 e.Incl();
5265 break;
5266 case T_INCLUDE_ONCE:
5267 e.InclOnce();
5268 break;
5269 case T_REQUIRE:
5270 e.Req();
5271 break;
5272 case T_REQUIRE_ONCE:
5273 if (ie->isDocumentRoot()) {
5274 e.ReqDoc();
5275 } else {
5276 e.ReqOnce();
5278 break;
5280 return true;
5283 case Construct::KindOfListAssignment: {
5284 auto la = static_pointer_cast<ListAssignment>(node);
5285 auto rhs = la->getArray();
5287 // listAssignmentVisitLHS should have handled this
5288 assert(rhs);
5290 bool nullRHS = la->getRHSKind() == ListAssignment::Null;
5291 // If the RHS is not a simple variable, we need to evaluate it and assign
5292 // it to a temp local. If it is, whether or not we directly use it or copy
5293 // it into a temp local is visible in perverse statements like:
5294 // list($a, $b) = $a
5295 // The behavior of that changed between PHP5 and PHP7; in PHP5 we directly
5296 // use the temp local, in PHP7 we need to copy it.
5297 bool simpleRHS = rhs->is(Construct::KindOfSimpleVariable)
5298 && !static_pointer_cast<SimpleVariable>(rhs)->getAlwaysStash()
5299 && !RuntimeOption::PHP7_LTR_assign;
5300 Id tempLocal = -1;
5301 Offset start = InvalidAbsoluteOffset;
5303 if (!simpleRHS && la->isRhsFirst()) {
5304 tempLocal = emitVisitAndSetUnnamedL(e, rhs);
5305 start = m_ue.bcPos();
5308 // We use "index chains" to deal with nested list assignment. We will
5309 // end up with one index chain per expression we need to assign to.
5310 // The helper function will populate indexChains.
5312 // In PHP5 mode, this will also evaluate the LHS; in PHP7 mode, that is
5313 // always delayed until listAssignmentAssignElements below. This means
5314 // that isRhsFirst() has no effect in PHP7 mode. See comments in
5315 // listAssignmentVisitLHS and listAssignmentAssignElements for more
5316 // explanation.
5317 std::vector<IndexPair> indexPairs;
5318 IndexChain workingChain;
5319 listAssignmentVisitLHS(e, la, workingChain, indexPairs);
5321 if (!simpleRHS && !la->isRhsFirst()) {
5322 assert(tempLocal == -1);
5323 assert(start == InvalidAbsoluteOffset);
5324 tempLocal = emitVisitAndSetUnnamedL(e, rhs);
5325 start = m_ue.bcPos();
5328 // Assign elements.
5329 if (nullRHS) {
5330 listAssignmentAssignElements(e, indexPairs, nullptr);
5331 } else if (simpleRHS) {
5332 listAssignmentAssignElements(e, indexPairs, [&] { visit(rhs); });
5333 } else {
5334 listAssignmentAssignElements(
5335 e, indexPairs,
5336 [&] { emitVirtualLocal(tempLocal); }
5340 // Leave the RHS on the stack
5341 if (simpleRHS) {
5342 visit(rhs);
5343 } else {
5344 emitPushAndFreeUnnamedL(e, tempLocal, start);
5347 return true;
5350 case Construct::KindOfNewObjectExpression: {
5351 auto ne = static_pointer_cast<NewObjectExpression>(node);
5352 auto params = ne->getParams();
5353 int numParams = params ? params->getCount() : 0;
5354 ClassScopeRawPtr cls = ne->getClassScope();
5356 Offset fpiStart;
5357 if (ne->isStatic()) {
5358 // new static()
5359 e.LateBoundCls();
5360 fpiStart = m_ue.bcPos();
5361 e.FPushCtor(numParams);
5362 } else if (ne->getOriginalName().empty()) {
5363 // new $x()
5364 visit(ne->getNameExp());
5365 emitAGet(e);
5366 fpiStart = m_ue.bcPos();
5367 e.FPushCtor(numParams);
5368 } else if ((ne->isSelf() || ne->isParent()) &&
5369 (!cls || cls->isTrait() ||
5370 (ne->isParent() && cls->getOriginalParent().empty()))) {
5371 if (ne->isSelf()) {
5372 // new self() inside a trait or code statically not inside any class
5373 e.Self();
5374 } else {
5375 // new parent() inside a trait, code statically not inside any
5376 // class, or a class with no parent
5377 e.Parent();
5379 fpiStart = m_ue.bcPos();
5380 e.FPushCtor(numParams);
5381 } else {
5382 // new C() inside trait or pseudomain
5383 fpiStart = m_ue.bcPos();
5384 e.FPushCtorD(numParams,
5385 makeStaticString(ne->getOriginalClassName()));
5388 emitCall(e, ne, params, fpiStart);
5389 e.PopR();
5390 return true;
5393 case Construct::KindOfObjectMethodExpression: {
5394 auto om = static_pointer_cast<ObjectMethodExpression>(node);
5395 // $obj->name(...)
5396 // ^^^^
5397 visit(om->getObject());
5398 m_tempLoc = om->getRange();
5399 emitConvertToCell(e);
5400 ExpressionListPtr params(om->getParams());
5401 int numParams = params ? params->getCount() : 0;
5403 Offset fpiStart = 0;
5404 ExpressionPtr methName = om->getNameExp();
5405 bool useDirectForm = false;
5406 if (methName->is(Construct::KindOfScalarExpression)) {
5407 auto sval = static_pointer_cast<ScalarExpression>(methName);
5408 const std::string& methStr = sval->getOriginalLiteralString();
5409 if (!methStr.empty()) {
5410 // $obj->name(...)
5411 // ^^^^
5412 // Use getOriginalLiteralString(), which hasn't been
5413 // case-normalized, since __call() needs to preserve
5414 // the case.
5415 StringData* nameLiteral = makeStaticString(methStr);
5416 fpiStart = m_ue.bcPos();
5417 e.FPushObjMethodD(
5418 numParams,
5419 nameLiteral,
5420 om->isNullSafe() ? ObjMethodOp::NullSafe : ObjMethodOp::NullThrows
5422 useDirectForm = true;
5425 if (!useDirectForm) {
5426 // $obj->{...}(...)
5427 // ^^^^^
5428 visit(methName);
5429 emitConvertToCell(e);
5430 fpiStart = m_ue.bcPos();
5431 e.FPushObjMethod(
5432 numParams,
5433 om->isNullSafe() ? ObjMethodOp::NullSafe : ObjMethodOp::NullThrows
5436 // $obj->name(...)
5437 // ^^^^^
5438 emitCall(e, om, params, fpiStart);
5439 return true;
5442 case Construct::KindOfObjectPropertyExpression: {
5443 auto op = static_pointer_cast<ObjectPropertyExpression>(node);
5444 if (op->isNullSafe() &&
5445 op->hasAnyContext(
5446 Expression::RefValue
5447 | Expression::LValue
5448 | Expression::DeepReference
5449 ) && !op->hasContext(Expression::InvokeArgument)
5451 throw IncludeTimeFatalException(op,
5452 Strings::NULLSAFE_PROP_WRITE_ERROR);
5454 ExpressionPtr obj = op->getObject();
5455 auto sv = dynamic_pointer_cast<SimpleVariable>(obj);
5456 if (sv && sv->isThis() && sv->hasContext(Expression::ObjectContext)) {
5457 e.CheckThis();
5458 m_evalStack.push(StackSym::H);
5459 } else {
5460 visit(obj);
5462 StringData* clsName = getClassName(op->getObject());
5463 if (clsName) {
5464 m_evalStack.setKnownCls(clsName, false);
5466 emitNameString(e, op->getProperty(), true);
5467 if (!op->hasAnyContext(Expression::AccessContext|
5468 Expression::ObjectContext)) {
5469 m_tempLoc = op->getRange();
5471 markProp(
5473 op->isNullSafe()
5474 ? PropAccessType::NullSafe
5475 : PropAccessType::Normal
5477 return true;
5480 case Construct::KindOfQOpExpression: {
5481 auto q = static_pointer_cast<QOpExpression>(node);
5482 if (q->getYes()) {
5483 // <expr> ? <expr> : <expr>
5484 Label tru, fals, done;
5486 Emitter condEmitter(q->getCondition(), m_ue, *this);
5487 visitIfCondition(q->getCondition(), condEmitter,
5488 tru, fals, true);
5490 if (tru.isUsed()) {
5491 tru.set(e);
5493 if (currentPositionIsReachable()) {
5494 visit(q->getYes());
5495 emitConvertToCell(e);
5496 e.Jmp(done);
5498 if (fals.isUsed()) fals.set(e);
5499 if (currentPositionIsReachable()) {
5500 visit(q->getNo());
5501 emitConvertToCell(e);
5503 if (done.isUsed()) {
5504 done.set(e);
5505 m_evalStack.cleanTopMeta();
5507 } else {
5508 // <expr> ?: <expr>
5509 Label done;
5510 visit(q->getCondition());
5511 emitConvertToCell(e);
5512 e.Dup();
5513 e.JmpNZ(done);
5514 e.PopC();
5515 visit(q->getNo());
5516 emitConvertToCell(e);
5517 done.set(e);
5518 m_evalStack.cleanTopMeta();
5520 return true;
5523 case Construct::KindOfNullCoalesceExpression: {
5524 auto q = static_pointer_cast<NullCoalesceExpression>(node);
5526 Label done;
5527 visit(q->getFirst());
5528 emitCGetQuiet(e);
5529 e.Dup();
5530 emitIsset(e);
5531 e.JmpNZ(done);
5532 e.PopC();
5533 visit(q->getSecond());
5534 emitConvertToCell(e);
5535 done.set(e);
5536 m_evalStack.cleanTopMeta();
5538 return true;
5541 case Construct::KindOfScalarExpression: {
5542 auto ex = static_pointer_cast<Expression>(node);
5543 Variant v;
5544 ex->getScalarValue(v);
5545 auto const emitted = emitScalarValue(e, v);
5546 always_assert(emitted);
5547 return true;
5550 case Construct::KindOfPipeVariable: {
5551 if (auto pipeVar = getPipeLocal()) {
5552 emitVirtualLocal(*pipeVar);
5553 return true;
5556 throw IncludeTimeFatalException(
5557 node, "Pipe variables must occur only in the RHS of pipe expressions");
5560 case Construct::KindOfSimpleVariable: {
5561 auto sv = static_pointer_cast<SimpleVariable>(node);
5562 if (sv->isThis()) {
5563 if (sv->hasContext(Expression::ObjectContext)) {
5564 e.This();
5565 } else if (sv->getFunctionScope()->needsLocalThis()) {
5566 static const StringData* thisStr = makeStaticString("this");
5567 Id thisId = m_curFunc->lookupVarId(thisStr);
5568 emitVirtualLocal(thisId);
5569 } else {
5570 auto const subop = sv->hasContext(Expression::ExistContext)
5571 ? BareThisOp::NoNotice
5572 : BareThisOp::Notice;
5573 e.BareThis(subop);
5575 } else {
5576 StringData* nLiteral = makeStaticString(sv->getName());
5577 if (sv->isSuperGlobal()) {
5578 e.String(nLiteral);
5579 markGlobalName(e);
5580 return true;
5582 Id i = m_curFunc->lookupVarId(nLiteral);
5583 emitVirtualLocal(i);
5584 if (sv->getAlwaysStash() &&
5585 !sv->hasAnyContext(Expression::ExistContext |
5586 Expression::RefValue |
5587 Expression::LValue |
5588 Expression::RefParameter)) {
5589 emitConvertToCell(e);
5593 return true;
5596 case Construct::KindOfDynamicVariable: {
5597 auto dv = static_pointer_cast<DynamicVariable>(node);
5598 visit(dv->getSubExpression());
5599 emitConvertToCellOrLoc(e);
5600 markName(e);
5601 return true;
5604 case Construct::KindOfStaticMemberExpression: {
5605 auto sm = static_pointer_cast<StaticMemberExpression>(node);
5606 emitVirtualClassBase(e, sm.get());
5607 emitNameString(e, sm->getExp());
5608 markSProp(e);
5609 return true;
5612 case Construct::KindOfArrayPairExpression: {
5613 auto ap = static_pointer_cast<ArrayPairExpression>(node);
5615 auto key = ap->getName();
5616 if (!m_staticArrays.empty()) {
5617 auto val = ap->getValue();
5619 TypedValue tvVal;
5620 initScalar(tvVal, val);
5622 if (key != nullptr) {
5623 assert(key->isScalar());
5624 TypedValue tvKey = make_tv<KindOfNull>();
5625 if (!key->getScalarValue(tvAsVariant(&tvKey))) {
5626 InvariantViolation("Expected scalar value for array key\n");
5627 always_assert(0);
5629 m_staticArrays.back().set(tvAsCVarRef(&tvKey),
5630 tvAsVariant(&tvVal));
5631 } else {
5632 // Set/ImmSet, val is the key
5633 if (m_staticColType.back() == HeaderKind::Set ||
5634 m_staticColType.back() == HeaderKind::ImmSet) {
5635 m_staticArrays.back().set(tvAsVariant(&tvVal),
5636 tvAsVariant(&tvVal));
5637 } else {
5638 m_staticArrays.back().append(tvAsCVarRef(&tvVal));
5641 } else {
5642 // Assume new array is on top of stack
5643 bool hasKey = (bool)key;
5644 if (hasKey) {
5645 visit(key);
5646 emitConvertToCellOrLoc(e);
5648 visit(ap->getValue());
5649 if (ap->isRef()) {
5650 emitConvertToVar(e);
5651 if (hasKey) {
5652 emitConvertSecondToCell(e);
5653 e.AddElemV();
5654 } else {
5655 e.AddNewElemV();
5657 } else {
5658 emitConvertToCell(e);
5659 if (hasKey) {
5660 emitConvertSecondToCell(e);
5661 e.AddElemC();
5662 } else {
5663 e.AddNewElemC();
5667 return true;
5669 case Construct::KindOfExpressionList: {
5670 auto el = static_pointer_cast<ExpressionList>(node);
5671 if (!m_staticArrays.empty() &&
5672 (m_staticColType.back() == HeaderKind::VecArray ||
5673 m_staticColType.back() == HeaderKind::Keyset)) {
5674 auto const nelem = el->getCount();
5675 for (int i = 0; i < nelem; ++i) {
5676 auto const expr = (*el)[i];
5677 assert(expr->isScalar());
5678 TypedValue tvVal;
5679 initScalar(tvVal, expr);
5680 m_staticArrays.back().append(tvAsCVarRef(&tvVal));
5682 return true;
5683 } else {
5684 int nelem = el->getCount(), i;
5685 bool pop = el->getListKind() != ExpressionList::ListKindParam;
5686 int keep = el->getListKind() == ExpressionList::ListKindLeft ?
5687 0 : nelem - 1;
5688 int cnt = 0;
5689 for (i = 0; i < nelem; i++) {
5690 ExpressionPtr p((*el)[i]);
5691 if (visit(p)) {
5692 if (pop && i != keep) {
5693 emitPop(e);
5694 } else {
5695 cnt++;
5699 return cnt != 0;
5702 case Construct::KindOfParameterExpression: {
5703 not_implemented();
5705 case Construct::KindOfModifierExpression: {
5706 not_implemented();
5708 case Construct::KindOfUserAttribute: {
5709 not_implemented();
5711 case Construct::KindOfClosureExpression: {
5712 // Closures are implemented by anonymous classes that extend Closure.
5713 // There is one anonymous class per closure body.
5714 auto ce = static_pointer_cast<ClosureExpression>(node);
5716 // Build a convenient list of use-variables. Each one corresponds to:
5717 // (a) an instance variable, to store the value until call time
5718 // (b) a parameter of the generated constructor
5719 // (c) an argument to the constructor at the definition site
5720 // (d) a line of code in the generated constructor;
5721 // (e) a line of code in the generated prologue to the closure body
5722 auto useList = ce->getClosureVariables();
5723 ClosureUseVarVec useVars;
5724 int useCount = (useList ? useList->getCount() : 0);
5725 if (useList) {
5726 for (int i = 0; i < useCount; ++i) {
5727 auto var = static_pointer_cast<ParameterExpression>((*useList)[i]);
5728 StringData* varName = makeStaticString(var->getName());
5729 useVars.push_back(ClosureUseVar(varName, var->isRef()));
5733 // We're still at the closure definition site. Emit code to instantiate
5734 // the new anonymous class, with the use variables as arguments.
5735 ExpressionListPtr valuesList(ce->getClosureValues());
5736 for (int i = 0; i < useCount; ++i) {
5737 ce->type() == ClosureType::Short
5738 ? emitLambdaCaptureArg(e, (*valuesList)[i])
5739 : (void)emitBuiltinCallArg(e, (*valuesList)[i], i,
5740 useVars[i].second, false);
5743 // The parser generated a unique name for the function,
5744 // use that for the class
5745 std::string clsName = ce->getClosureFunction()->getOriginalName();
5747 if (m_curFunc->isPseudoMain()) {
5748 std::ostringstream oss;
5749 oss << clsName << '$' << std::hex <<
5750 m_curFunc->ue().md5().q[1] << m_curFunc->ue().md5().q[0] << '$';
5751 clsName = oss.str();
5754 if (Option::WholeProgram) {
5755 int my_id;
5757 EmittedClosures::accessor acc;
5758 s_emittedClosures.insert(acc, makeStaticString(clsName));
5759 my_id = ++acc->second;
5761 if (my_id > 1) {
5762 // The closure was from a trait, so we need a unique name in the
5763 // implementing class. _ is different from the #, which is used for
5764 // many closures in the same func in ParserBase::newClosureName
5765 folly::toAppend('_', my_id, &clsName);
5769 auto ssClsName = makeStaticString(clsName);
5770 e.CreateCl(useCount, ssClsName);
5772 // From here on out, we're creating a new class to hold the closure.
5773 const static StringData* parentName = makeStaticString("Closure");
5774 PreClassEmitter* pce = m_ue.newPreClassEmitter(
5775 ssClsName, PreClass::ClosureHoistable);
5777 auto const attrs = AttrNoOverride | AttrUnique | AttrPersistent;
5779 pce->init(ce->line0(), ce->line1(), m_ue.bcPos(),
5780 attrs, parentName, nullptr);
5782 // Instance properties---one for each use var, and one for
5783 // each static local.
5784 TypedValue uninit;
5785 tvWriteUninit(&uninit);
5786 for (auto& useVar : useVars) {
5787 pce->addProperty(useVar.first, AttrPrivate, nullptr, nullptr,
5788 &uninit, RepoAuthType{});
5791 // The __invoke method. This is the body of the closure, preceded by
5792 // code that pulls the object's instance variables into locals.
5793 static const StringData* invokeName = makeStaticString("__invoke");
5794 FuncEmitter* invoke = m_ue.newMethodEmitter(invokeName, pce);
5795 invoke->isClosureBody = true;
5796 pce->addMethod(invoke);
5797 auto body = static_pointer_cast<MethodStatement>(ce->getClosureFunction());
5798 postponeMeth(body, invoke, false, new ClosureUseVarVec(useVars));
5800 return true;
5802 case Construct::KindOfClassExpression: {
5803 auto ce = static_pointer_cast<ClassExpression>(node);
5804 // The parser generated a unique name for the class, use that
5805 std::string clsName = ce->getClass()->getOriginalName();
5806 auto ssClsName = makeStaticString(clsName);
5807 auto fpiStart = m_ue.bcPos();
5808 auto params = ce->getParams();
5809 int numParams = params ? params->getCount() : 0;
5810 e.FPushCtorD(numParams, ssClsName);
5811 emitCall(e, ce, ce->getParams(), fpiStart);
5812 e.PopR();
5813 return true;
5815 case Construct::KindOfYieldExpression: {
5816 auto y = static_pointer_cast<YieldExpression>(node);
5818 registerYieldAwait(y);
5819 assert(m_evalStack.size() == 0);
5821 // evaluate key passed to yield, if applicable
5822 ExpressionPtr keyExp = y->getKeyExpression();
5823 if (keyExp) {
5824 m_curFunc->isPairGenerator = true;
5825 visit(keyExp);
5826 emitConvertToCell(e);
5829 // evaluate value expression passed to yield
5830 visit(y->getValueExpression());
5831 emitConvertToCell(e);
5833 // suspend generator
5834 if (keyExp) {
5835 assert(m_evalStack.size() == 2);
5836 e.YieldK();
5837 } else {
5838 assert(m_evalStack.size() == 1);
5839 e.Yield();
5842 // continue with the received result on the stack
5843 assert(m_evalStack.size() == 1);
5844 return true;
5846 case Construct::KindOfYieldFromExpression: {
5847 auto yf = static_pointer_cast<YieldFromExpression>(node);
5849 registerYieldAwait(yf);
5850 assert(m_evalStack.size() == 0);
5852 emitYieldFrom(e, yf->getExpression());
5854 return true;
5856 case Construct::KindOfAwaitExpression: {
5857 auto await = static_pointer_cast<AwaitExpression>(node);
5859 registerYieldAwait(await);
5860 assert(m_evalStack.size() == 0);
5862 auto expression = await->getExpression();
5863 if (emitInlineGen(e, expression)) return true;
5865 Label resume;
5867 // evaluate expression passed to await
5868 visit(expression);
5869 emitConvertToCell(e);
5871 // if expr is null, just continue
5872 e.Dup();
5873 emitIsType(e, IsTypeOp::Null);
5874 e.JmpNZ(resume);
5876 assert(m_evalStack.size() == 1);
5878 e.Await();
5880 resume.set(e);
5881 return true;
5883 case Construct::KindOfUseDeclarationStatementFragment:
5884 case Construct::KindOfExpression: {
5885 not_reached();
5889 not_reached();
5892 bool EmitterVisitor::emitScalarValue(Emitter& e, const Variant& v) {
5893 switch (v.getRawType()) {
5894 case KindOfUninit:
5895 e.NullUninit();
5896 return true;
5898 case KindOfNull:
5899 e.Null();
5900 return true;
5902 case KindOfBoolean:
5903 v.asBooleanVal() ? e.True() : e.False();
5904 return true;
5906 case KindOfInt64:
5907 e.Int(v.getInt64());
5908 return true;
5910 case KindOfDouble:
5911 e.Double(v.getDouble());
5912 return true;
5914 case KindOfPersistentString:
5915 case KindOfString:
5916 e.String(makeStaticString(v.getStringData()));
5917 return true;
5919 case KindOfPersistentArray:
5920 case KindOfArray:
5921 e.Array(ArrayData::GetScalarArray(v.getArrayData()));
5922 return true;
5924 case KindOfObject:
5925 case KindOfResource:
5926 case KindOfRef:
5927 case KindOfClass:
5928 return false;
5930 not_reached();
5933 void EmitterVisitor::emitConstMethodCallNoParams(Emitter& e,
5934 const std::string& name) {
5935 auto const nameLit = makeStaticString(name);
5936 auto const fpiStart = m_ue.bcPos();
5937 e.FPushObjMethodD(0, nameLit, ObjMethodOp::NullThrows);
5939 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
5941 e.FCall(0);
5942 emitConvertToCell(e);
5945 namespace {
5946 const StaticString
5947 s_hh_invariant_violation("hh\\invariant_violation"),
5948 s_invariant_violation("invariant_violation"),
5949 s_gennull("HH\\Asio\\null"),
5950 s_fromArray("fromArray"),
5951 s_AwaitAllWaitHandle("HH\\AwaitAllWaitHandle")
5955 bool EmitterVisitor::emitInlineGenva(
5956 Emitter& e,
5957 const SimpleFunctionCallPtr& call
5959 assert(call->isCallToFunction("genva"));
5960 const auto params = call->getParams();
5961 if (!params) {
5962 e.Array(staticEmptyArray());
5963 return true;
5965 if (params->containsUnpack()) {
5966 throw IncludeTimeFatalException(params, "do not use ...$args with genva()");
5968 const auto num_params = params->getCount();
5969 assertx(num_params > 0);
5971 for (auto i = int{0}; i < num_params; i++) {
5972 Label gwh;
5974 visit((*params)[i]);
5975 emitConvertToCell(e);
5977 // ($_ !== null ? HH\Asio\null() : $_)->getWaitHandle()
5978 e.Dup();
5979 emitIsType(e, IsTypeOp::Null);
5980 e.JmpZ(gwh);
5981 emitPop(e);
5983 Offset fpiStart = m_ue.bcPos();
5984 e.FPushFuncD(0, s_gennull.get());
5986 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
5988 e.FCall(0);
5989 e.UnboxR();
5990 gwh.set(e);
5991 emitConstMethodCallNoParams(e, "getWaitHandle");
5994 std::vector<Id> waithandles(num_params);
5995 for (auto i = int{num_params - 1}; i >= 0; --i) {
5996 waithandles[i] = emitSetUnnamedL(e);
5998 assertx(waithandles.size() == num_params);
6000 // AwaitAllWaitHandle::fromArray() always returns a WaitHandle.
6001 Offset fpiStart = m_ue.bcPos();
6002 e.FPushClsMethodD(1, s_fromArray.get(), s_AwaitAllWaitHandle.get());
6004 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
6005 // create a packed array of the waithandles
6006 for (const auto wh : waithandles) {
6007 emitVirtualLocal(wh);
6008 emitCGet(e);
6010 e.NewPackedArray(num_params);
6011 emitFPass(e, 0, PassByRefKind::ErrorOnCell);
6013 e.FCall(1);
6014 e.UnboxR();
6016 e.Await();
6017 // result of AwaitAllWaitHandle does not matter
6018 emitPop(e);
6020 for (const auto wh : waithandles) {
6021 emitVirtualLocal(wh);
6022 emitPushL(e);
6023 e.WHResult();
6025 e.NewPackedArray(num_params);
6027 for (auto wh : waithandles) {
6028 m_curFunc->freeUnnamedLocal(wh);
6031 newFaultRegionAndFunclet(
6032 fpiStart, m_ue.bcPos(),
6033 new UnsetUnnamedLocalsThunklet(std::move(waithandles)));
6035 return true;
6038 bool EmitterVisitor::emitInlineGena(
6039 Emitter& e,
6040 const SimpleFunctionCallPtr& call
6042 assert(call->isCallToFunction("gena"));
6043 const auto params = call->getParams();
6045 if (params->getCount() != 1) return false;
6048 // Convert input into an array of WH (inline this?)
6049 // Two elements is the most common size.
6051 e.NewArray(2);
6052 const auto array = emitSetUnnamedL(e);
6053 const auto arrayStart = m_ue.bcPos();
6056 // Iterate over input and store wait handles for all elements in
6057 // a new array.
6059 const auto key = m_curFunc->allocUnnamedLocal();
6060 const auto item = m_curFunc->allocUnnamedLocal();
6062 emitVirtualLocal(key);
6063 emitVirtualLocal(item);
6065 visit((*params)[0]);
6066 emitConvertToCell(e);
6068 Label endloop;
6069 const auto initItId = m_curFunc->allocIterator();
6070 e.IterInitK(initItId, endloop, item, key);
6071 const auto iterStart = m_ue.bcPos();
6073 Label loop(e);
6075 emitVirtualLocal(array); // for Set below
6076 emitVirtualLocal(key); // for Set below
6077 markElem(e);
6079 emitVirtualLocal(item);
6080 emitCGet(e);
6081 emitConstMethodCallNoParams(e, "getWaitHandle");
6083 emitSet(e); // array[$key] = $item->getWaitHandle();
6084 emitPop(e);
6086 emitVirtualLocal(key);
6087 emitVirtualLocal(item);
6088 e.IterNextK(initItId, loop, item, key);
6089 endloop.set(e);
6091 // Clear item and key. Free iterator.
6092 emitVirtualLocal(item);
6093 emitUnset(e);
6094 emitVirtualLocal(key);
6095 emitUnset(e);
6096 m_curFunc->freeIterator(initItId);
6098 newFaultRegionAndFunclet(iterStart, m_ue.bcPos(),
6099 new UnsetUnnamedLocalsThunklet({item, key}));
6100 newFaultRegionAndFunclet(iterStart, m_ue.bcPos(),
6101 new IterFreeThunklet(initItId, false),
6102 { initItId, KindOfIter });
6106 // Construct an AAWH from the array.
6108 const auto fromArrayStart = m_ue.bcPos();
6109 e.FPushClsMethodD(1, s_fromArray.get(), s_AwaitAllWaitHandle.get());
6111 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fromArrayStart);
6112 emitVirtualLocal(array);
6113 e.FPassL(0, array);
6115 e.FCall(1);
6116 e.UnboxR();
6119 // Await on the AAWH. Note: the result of Await does not matter.
6121 e.Await();
6122 emitPop(e);
6125 // Iterate over results and store in array. Reuse same temporary array.
6128 emitVirtualLocal(key);
6129 emitVirtualLocal(item);
6131 emitVirtualLocal(array);
6132 emitCGet(e);
6134 Label endloop2;
6135 const auto resultItId = m_curFunc->allocIterator();
6136 e.IterInitK(resultItId, endloop2, item, key);
6137 const auto iterStart2 = m_ue.bcPos();
6139 Label loop2(e);
6141 emitVirtualLocal(array); // for Set below
6142 emitVirtualLocal(key); // for Set below
6143 markElem(e);
6145 emitVirtualLocal(item);
6146 emitCGet(e);
6147 e.WHResult();
6149 emitSet(e); // array[$key] = WHResult($item);
6150 emitPop(e);
6152 emitVirtualLocal(key);
6153 emitVirtualLocal(item);
6154 e.IterNextK(resultItId, loop2, item, key);
6155 endloop2.set(e);
6157 // Clear item and key. Free iterator.
6158 emitVirtualLocal(item);
6159 emitUnset(e);
6160 emitVirtualLocal(key);
6161 emitUnset(e);
6162 m_curFunc->freeIterator(resultItId);
6164 newFaultRegionAndFunclet(iterStart2, m_ue.bcPos(),
6165 new UnsetUnnamedLocalsThunklet({item, key}));
6167 newFaultRegionAndFunclet(iterStart2, m_ue.bcPos(),
6168 new IterFreeThunklet(resultItId, false),
6169 { resultItId, KindOfIter });
6172 // clean up locals
6173 m_curFunc->freeUnnamedLocal(item);
6174 m_curFunc->freeUnnamedLocal(key);
6176 // Leave result array on the stack.
6177 emitPushAndFreeUnnamedL(e, array, arrayStart);
6179 return true;
6182 bool EmitterVisitor::emitInlineGen(
6183 Emitter& e,
6184 const ExpressionPtr& expression
6186 if (!m_ue.m_isHHFile || !Option::EnableHipHopSyntax ||
6187 !expression->is(Expression::KindOfSimpleFunctionCall) ||
6188 Option::JitEnableRenameFunction) {
6189 return false;
6192 const auto call = static_pointer_cast<SimpleFunctionCall>(expression);
6193 if (call->isCallToFunction("genva")) {
6194 return emitInlineGenva(e, call);
6195 } else if (call->isCallToFunction("gena")) {
6196 return emitInlineGena(e, call);
6198 return false;
6201 // Compile a static string as HHAS
6203 // The hhas bytecodes should either leave the stack untouched, in
6204 // which case the result of the hh\asm() expression will be null; or
6205 // they should push exactly one cell, which will be the result of the
6206 // hh\asm() expression.
6207 bool EmitterVisitor::emitInlineHHAS(Emitter& e, SimpleFunctionCallPtr func) {
6208 if (SystemLib::s_inited &&
6209 !func->getFunctionScope()->isSystem() &&
6210 !RuntimeOption::EvalAllowHhas) {
6211 throw IncludeTimeFatalException(func,
6212 "Inline hhas only allowed in systemlib");
6214 auto const params = func->getParams();
6215 if (!params || params->getCount() != 1) {
6216 throw IncludeTimeFatalException(func,
6217 "Inline hhas expects exactly one argument");
6219 Variant v;
6220 if (!((*params)[0]->getScalarValue(v)) || !v.isString()) {
6221 throw IncludeTimeFatalException(func,
6222 "Inline hhas must be string literal");
6225 try {
6226 auto result =
6227 assemble_expression(m_ue, m_curFunc,
6228 m_evalStack.size() + m_evalStack.fdescSize(),
6229 v.toString().toCppString());
6230 switch (result) {
6231 case AsmResult::NoResult:
6232 e.Null();
6233 break;
6234 case AsmResult::ValuePushed:
6235 pushEvalStack(StackSym::C);
6236 break;
6237 case AsmResult::Unreachable:
6238 // PrevOpcode is only used to determine whether the current position
6239 // is reachable. Arbitrarily set it to Jmp to ensure that the emitter
6240 // knows the current position is not reachable
6241 setPrevOpcode(Op::Jmp);
6242 pushEvalStack(StackSym::C);
6243 break;
6245 } catch (const std::exception& ex) {
6246 throw IncludeTimeFatalException(func, ex.what());
6249 return true;
6252 bool EmitterVisitor::emitHHInvariant(Emitter& e, SimpleFunctionCallPtr call) {
6253 if (!m_ue.m_isHHFile && !RuntimeOption::EnableHipHopSyntax) return false;
6255 auto const params = call->getParams();
6256 if (!params || params->getCount() < 1) return false;
6258 Label ok;
6260 visit((*params)[0]);
6261 emitCGet(e);
6262 e.JmpNZ(ok);
6264 auto const fpiStart = m_ue.bcPos();
6265 e.FPushFuncD(params->getCount() - 1, s_hh_invariant_violation.get());
6267 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
6268 for (auto i = uint32_t{1}; i < params->getCount(); ++i) {
6269 emitFuncCallArg(e, (*params)[i], i - 1, false);
6272 e.FCall(params->getCount() - 1);
6273 emitPop(e);
6274 // The invariant_violation can't return; but bytecode invariants mandate an
6275 // opcode that can't fall through:
6276 e.String(s_invariant_violation.get());
6277 e.Fatal(FatalOp::Runtime);
6279 ok.set(e);
6280 e.Null(); // invariant returns null if used in an expression, void according
6281 // to the typechecker.
6282 return true;
6285 int EmitterVisitor::scanStackForLocation(int iLast) {
6286 assertx(iLast >= 0);
6287 assertx(iLast < (int)m_evalStack.size());
6288 for (int i = iLast; i >= 0; --i) {
6289 char marker = StackSym::GetMarker(m_evalStack.get(i));
6290 if (marker != StackSym::E && marker != StackSym::W &&
6291 marker != StackSym::P && marker != StackSym::M &&
6292 marker != StackSym::Q) {
6293 return i;
6296 InvariantViolation("Emitter expected a location on the stack but none "
6297 "was found (at offset %d)",
6298 m_ue.bcPos());
6299 return 0;
6302 static MOpFlags makeBaseFlags(MOpFlags f) {
6303 auto constexpr mask = uint8_t(MOpFlags::Warn) | uint8_t(MOpFlags::Define);
6304 return MOpFlags(uint8_t(f) & mask);
6307 size_t EmitterVisitor::emitMOp(
6308 int iFirst,
6309 int& iLast,
6310 Emitter& e,
6311 MInstrOpts opts
6313 auto stackIdx = [&](int i) {
6314 return m_evalStack.actualSize() - 1 - m_evalStack.getActualPos(i);
6317 auto const baseFlags = opts.fpass ? MOpFlags::None
6318 : makeBaseFlags(opts.flags);
6320 // Emit the base location operation.
6321 auto sym = m_evalStack.get(iFirst);
6322 auto flavor = StackSym::GetSymFlavor(sym);
6323 switch (StackSym::GetMarker(sym)) {
6324 case StackSym::N:
6325 switch (flavor) {
6326 case StackSym::C:
6327 if (opts.fpass) {
6328 e.FPassBaseNC(opts.paramId, stackIdx(iFirst));
6329 } else {
6330 e.BaseNC(stackIdx(iFirst), baseFlags);
6332 break;
6333 case StackSym::L:
6334 if (opts.fpass) {
6335 e.FPassBaseNL(opts.paramId, m_evalStack.getLoc(iFirst));
6336 } else {
6337 e.BaseNL(m_evalStack.getLoc(iFirst), baseFlags);
6339 break;
6340 default:
6341 always_assert(false);
6343 break;
6345 case StackSym::G:
6346 switch (flavor) {
6347 case StackSym::C:
6348 if (opts.fpass) {
6349 e.FPassBaseGC(opts.paramId, stackIdx(iFirst));
6350 } else {
6351 e.BaseGC(stackIdx(iFirst), baseFlags);
6353 break;
6354 case StackSym::L:
6355 if (opts.fpass) {
6356 e.FPassBaseGL(opts.paramId, m_evalStack.getLoc(iFirst));
6357 } else {
6358 e.BaseGL(m_evalStack.getLoc(iFirst), baseFlags);
6360 break;
6361 default:
6362 always_assert(false);
6364 break;
6366 case StackSym::S: {
6367 if (m_evalStack.get(iLast) != StackSym::AM) {
6368 unexpectedStackSym(sym, "S-vector base, class ref");
6371 auto const clsIdx = opts.rhsVal ? 1 : 0;
6372 switch (flavor) {
6373 case StackSym::C:
6374 e.BaseSC(stackIdx(iFirst), clsIdx);
6375 break;
6376 case StackSym::L:
6377 e.BaseSL(m_evalStack.getLoc(iFirst), clsIdx);
6378 break;
6379 default:
6380 unexpectedStackSym(sym, "S-vector base, prop name");
6381 break;
6383 // The BaseS* bytecodes consume the Class from the eval stack so the
6384 // final operations don't have to expect an A-flavored input. Adjust
6385 // iLast accordingly.
6386 --iLast;
6387 break;
6390 case StackSym::None:
6391 switch (flavor) {
6392 case StackSym::L:
6393 if (opts.fpass) {
6394 e.FPassBaseL(opts.paramId, m_evalStack.getLoc(iFirst));
6395 } else {
6396 e.BaseL(m_evalStack.getLoc(iFirst), baseFlags);
6398 break;
6399 case StackSym::C:
6400 e.BaseC(stackIdx(iFirst));
6401 break;
6402 case StackSym::R:
6403 e.BaseR(stackIdx(iFirst));
6404 break;
6405 case StackSym::H:
6406 e.BaseH();
6407 break;
6408 default:
6409 always_assert(false);
6411 break;
6413 default:
6414 always_assert(false && "Bad base marker");
6417 assert(StackSym::GetMarker(m_evalStack.get(iLast)) != StackSym::M);
6419 // Emit all intermediate operations, leaving the final operation up to our
6420 // caller.
6421 for (auto i = iFirst + 1; i < iLast; ++i) {
6422 if (opts.fpass) {
6423 e.FPassDim(opts.paramId, symToMemberKey(e, i, opts.allowW));
6424 } else {
6425 e.Dim(opts.flags, symToMemberKey(e, i, opts.allowW));
6429 size_t stackCount = 0;
6430 for (int i = iFirst; i <= iLast; ++i) {
6431 if (!StackSym::IsSymbolic(m_evalStack.get(i))) ++stackCount;
6433 return stackCount;
6436 MemberKey EmitterVisitor::symToMemberKey(Emitter& e, int i, bool allowW) {
6437 auto const sym = m_evalStack.get(i);
6438 auto const marker = StackSym::GetMarker(sym);
6439 if (marker == StackSym::W) {
6440 if (allowW) return MemberKey{};
6442 throw EmitterVisitor::IncludeTimeFatalException(
6443 e.getNode(), "Cannot use [] for reading"
6447 switch (StackSym::GetSymFlavor(sym)) {
6448 case StackSym::L: {
6449 auto const local = m_evalStack.getLoc(i);
6450 switch (marker) {
6451 case StackSym::E: return MemberKey{MEL, local};
6452 case StackSym::P: return MemberKey{MPL, local};
6453 default: always_assert(false);
6456 case StackSym::C: {
6457 auto const idx =
6458 int32_t(m_evalStack.actualSize() - 1 - m_evalStack.getActualPos(i));
6459 switch (marker) {
6460 case StackSym::E: return MemberKey{MEC, idx};
6461 case StackSym::P: return MemberKey{MPC, idx};
6462 default: always_assert(false);
6465 case StackSym::I: {
6466 auto const int64 = m_evalStack.getInt(i);
6467 switch (marker) {
6468 case StackSym::E: return MemberKey{MEI, int64};
6469 default: always_assert(false);
6472 case StackSym::T: {
6473 auto const str = m_evalStack.getName(i);
6474 switch (marker) {
6475 case StackSym::E: return MemberKey{MET, str};
6476 case StackSym::P: return MemberKey{MPT, str};
6477 case StackSym::Q: return MemberKey{MQT, str};
6478 default: always_assert(false);
6481 default:
6482 always_assert(false);
6486 void EmitterVisitor::emitPop(Emitter& e) {
6487 if (checkIfStackEmpty("Pop*")) return;
6488 LocationGuard loc(e, m_tempLoc);
6489 m_tempLoc.clear();
6491 emitClsIfSPropBase(e);
6492 int iLast = m_evalStack.size()-1;
6493 int i = scanStackForLocation(iLast);
6494 int sz = iLast - i;
6495 assert(sz >= 0);
6496 char sym = m_evalStack.get(i);
6497 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
6498 switch (sym) {
6499 case StackSym::L: e.CGetL(m_evalStack.getLoc(i)); // fall through
6500 case StackSym::C: e.PopC(); break;
6501 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
6502 case StackSym::CN: e.CGetN(); e.PopC(); break;
6503 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
6504 case StackSym::CG: e.CGetG(); e.PopC(); break;
6505 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
6506 case StackSym::CS: e.CGetS(); e.PopC(); break;
6507 case StackSym::V: e.PopV(); break;
6508 case StackSym::R: e.PopR(); break;
6509 default: {
6510 unexpectedStackSym(sym, "emitPop");
6511 break;
6514 } else {
6515 emitQueryMOp(i, iLast, e, QueryMOp::CGet);
6516 e.PopC();
6520 void EmitterVisitor::emitCGetL2(Emitter& e) {
6521 assert(m_evalStack.size() >= 2);
6522 assert(m_evalStack.sizeActual() >= 1);
6523 assert(StackSym::GetSymFlavor(m_evalStack.get(m_evalStack.size() - 2))
6524 == StackSym::L);
6525 int localIdx = m_evalStack.getLoc(m_evalStack.size() - 2);
6526 e.CGetL2(localIdx);
6529 void EmitterVisitor::emitCGetL3(Emitter& e) {
6530 assert(m_evalStack.size() >= 3);
6531 assert(m_evalStack.sizeActual() >= 2);
6532 assert(StackSym::GetSymFlavor(m_evalStack.get(m_evalStack.size() - 3))
6533 == StackSym::L);
6534 int localIdx = m_evalStack.getLoc(m_evalStack.size() - 3);
6535 e.CGetL3(localIdx);
6538 void EmitterVisitor::emitPushL(Emitter& e) {
6539 assert(m_evalStack.size() >= 1);
6540 auto const idx = m_evalStack.size() - 1;
6541 assert(StackSym::GetSymFlavor(m_evalStack.get(idx)) == StackSym::L);
6542 e.PushL(m_evalStack.getLoc(idx));
6545 void EmitterVisitor::emitAGet(Emitter& e) {
6546 if (checkIfStackEmpty("AGet*")) return;
6548 emitConvertToCellOrLoc(e);
6549 switch (char sym = m_evalStack.top()) {
6550 case StackSym::L:
6551 e.AGetL(m_evalStack.getLoc(m_evalStack.size() - 1));
6552 break;
6553 case StackSym::C:
6554 e.AGetC();
6555 break;
6556 default:
6557 unexpectedStackSym(sym, "emitAGet");
6561 void EmitterVisitor::emitQueryMOp(int iFirst, int iLast, Emitter& e,
6562 QueryMOp op) {
6563 auto const flags = getQueryMOpFlags(op);
6564 auto const stackCount = emitMOp(iFirst, iLast, e, MInstrOpts{flags});
6565 e.QueryM(stackCount, op, symToMemberKey(e, iLast, false /* allowW */));
6568 void EmitterVisitor::emitCGet(Emitter& e) {
6569 if (checkIfStackEmpty("CGet*")) return;
6570 LocationGuard loc(e, m_tempLoc);
6571 m_tempLoc.clear();
6573 emitClsIfSPropBase(e);
6574 int iLast = m_evalStack.size()-1;
6575 int i = scanStackForLocation(iLast);
6576 int sz = iLast - i;
6577 assert(sz >= 0);
6578 char sym = m_evalStack.get(i);
6579 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
6580 switch (sym) {
6581 case StackSym::L: e.CGetL(m_evalStack.getLoc(i)); break;
6582 case StackSym::C: /* nop */ break;
6583 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
6584 case StackSym::CN: e.CGetN(); break;
6585 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
6586 case StackSym::CG: e.CGetG(); break;
6587 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
6588 case StackSym::CS: e.CGetS(); break;
6589 case StackSym::V: e.Unbox(); break;
6590 case StackSym::R: e.UnboxR(); break;
6591 default: {
6592 unexpectedStackSym(sym, "emitCGet");
6593 break;
6596 } else {
6597 emitQueryMOp(i, iLast, e, QueryMOp::CGet);
6601 void EmitterVisitor::emitCGetQuiet(Emitter& e) {
6602 if (checkIfStackEmpty("CGetQuiet*")) return;
6603 LocationGuard loc(e, m_tempLoc);
6604 m_tempLoc.clear();
6606 emitClsIfSPropBase(e);
6607 int iLast = m_evalStack.size()-1;
6608 int i = scanStackForLocation(iLast);
6609 int sz = iLast - i;
6610 assert(sz >= 0);
6611 char sym = m_evalStack.get(i);
6612 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
6613 switch (sym) {
6614 case StackSym::L: e.CGetQuietL(m_evalStack.getLoc(i)); break;
6615 case StackSym::C: /* nop */ break;
6616 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
6617 case StackSym::CN: e.CGetQuietN(); break;
6618 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
6619 case StackSym::CG: e.CGetQuietG(); break;
6620 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
6621 case StackSym::CS: e.CGetS(); break;
6622 case StackSym::V: e.Unbox(); break;
6623 case StackSym::R: e.UnboxR(); break;
6624 default: {
6625 unexpectedStackSym(sym, "emitCGetQuiet");
6626 break;
6630 } else {
6631 emitQueryMOp(i, iLast, e, QueryMOp::CGetQuiet);
6635 bool EmitterVisitor::emitVGet(Emitter& e, bool skipCells) {
6636 if (checkIfStackEmpty("VGet*")) return false;
6637 LocationGuard loc(e, m_tempLoc);
6638 m_tempLoc.clear();
6640 emitClsIfSPropBase(e);
6641 int iLast = m_evalStack.size()-1;
6642 int i = scanStackForLocation(iLast);
6643 int sz = iLast - i;
6644 assert(sz >= 0);
6645 char sym = m_evalStack.get(i);
6646 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
6647 switch (sym) {
6648 case StackSym::L: e.VGetL(m_evalStack.getLoc(i)); break;
6649 case StackSym::C: if (skipCells) return true; e.Box(); break;
6650 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
6651 case StackSym::CN: e.VGetN(); break;
6652 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
6653 case StackSym::CG: e.VGetG(); break;
6654 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
6655 case StackSym::CS: e.VGetS(); break;
6656 case StackSym::V: /* nop */ break;
6657 case StackSym::R: e.BoxR(); break;
6658 default: {
6659 unexpectedStackSym(sym, "emitVGet");
6660 break;
6663 } else {
6664 auto const stackCount =
6665 emitMOp(i, iLast, e, MInstrOpts{MOpFlags::DefineReffy});
6666 e.VGetM(stackCount, symToMemberKey(e, iLast, true /* allowW */));
6668 return false;
6671 Id EmitterVisitor::emitVisitAndSetUnnamedL(Emitter& e, ExpressionPtr exp) {
6672 visit(exp);
6673 emitConvertToCell(e);
6675 return emitSetUnnamedL(e);
6678 Id EmitterVisitor::emitSetUnnamedL(Emitter& e) {
6679 // HACK: emitVirtualLocal would pollute m_evalStack before visiting exp,
6680 // YieldExpression won't be happy
6681 Id tempLocal = m_curFunc->allocUnnamedLocal();
6682 auto& ue = e.getUnitEmitter();
6683 ue.emitOp(OpSetL);
6684 ue.emitIVA(tempLocal);
6686 emitPop(e);
6687 return tempLocal;
6690 void EmitterVisitor::emitPushAndFreeUnnamedL(Emitter& e, Id tempLocal,
6691 Offset start) {
6692 assert(tempLocal >= 0);
6693 assert(start != InvalidAbsoluteOffset);
6694 newFaultRegionAndFunclet(start, m_ue.bcPos(),
6695 new UnsetUnnamedLocalThunklet(tempLocal));
6696 emitVirtualLocal(tempLocal);
6697 emitPushL(e);
6698 m_curFunc->freeUnnamedLocal(tempLocal);
6701 EmitterVisitor::PassByRefKind
6702 EmitterVisitor::getPassByRefKind(ExpressionPtr exp) {
6703 auto permissiveKind = PassByRefKind::AllowCell;
6705 // The PassByRefKind of a list assignment expression is determined
6706 // by the PassByRefKind of the RHS. This loop will repeatedly recurse
6707 // on the RHS until it encounters an expression other than a list
6708 // assignment expression.
6709 while (exp->is(Expression::KindOfListAssignment)) {
6710 exp = static_pointer_cast<ListAssignment>(exp)->getArray();
6711 permissiveKind = PassByRefKind::WarnOnCell;
6714 switch (exp->getKindOf()) {
6715 case Expression::KindOfSimpleFunctionCall: {
6716 auto sfc = static_pointer_cast<SimpleFunctionCall>(exp);
6717 // this only happens for calls that have been morphed into bytecode
6718 // e.g. idx(), abs(), strlen(), etc..
6719 // It is to allow the following code to work
6720 // function f(&$arg) {...}
6721 // f(idx($array, 'key')); <- this fails otherwise
6722 if (sfc->hasBeenChangedToBytecode()) {
6723 return PassByRefKind::AllowCell;
6725 } break;
6726 case Expression::KindOfNewObjectExpression:
6727 case Expression::KindOfIncludeExpression:
6728 case Expression::KindOfSimpleVariable:
6729 // New and include/require
6730 return PassByRefKind::AllowCell;
6731 case Expression::KindOfArrayElementExpression:
6732 // Allow if bare; warn if inside list assignment
6733 return permissiveKind;
6734 case Expression::KindOfAssignmentExpression:
6735 // Assignment (=) and binding assignment (=&)
6736 return PassByRefKind::WarnOnCell;
6737 case Expression::KindOfBinaryOpExpression: {
6738 auto b = static_pointer_cast<BinaryOpExpression>(exp);
6739 // Assignment op (+=, -=, *=, etc)
6740 if (b->isAssignmentOp()) return PassByRefKind::WarnOnCell;
6741 } break;
6742 case Expression::KindOfUnaryOpExpression: {
6743 auto u = static_pointer_cast<UnaryOpExpression>(exp);
6744 int op = u->getOp();
6745 if (op == T_CLONE) {
6746 // clone
6747 return PassByRefKind::AllowCell;
6748 } else if (op == '@' || op == T_EVAL ||
6749 ((op == T_INC || op == T_DEC) && u->getFront())) {
6750 // Silence operator, eval, preincrement, and predecrement
6751 return PassByRefKind::WarnOnCell;
6753 } break;
6754 case Expression::KindOfExpressionList: {
6755 auto el = static_pointer_cast<ExpressionList>(exp);
6756 if (el->getListKind() != ExpressionList::ListKindParam) {
6757 return PassByRefKind::WarnOnCell;
6759 } break;
6760 default:
6761 break;
6763 // All other cases
6764 return PassByRefKind::ErrorOnCell;
6767 bool EmitterVisitor::emitBuiltinCallArg(Emitter& e,
6768 ExpressionPtr exp,
6769 int paramId,
6770 bool byRef,
6771 bool mustBeRef) {
6772 visit(exp);
6773 if (checkIfStackEmpty("Builtin arg*")) return true;
6774 if (byRef) {
6775 auto wasCell = emitVGet(e, true);
6776 if (wasCell && mustBeRef) {
6777 auto kind = getPassByRefKind(exp);
6778 switch (kind) {
6779 case PassByRefKind::AllowCell:
6780 // nop
6781 break;
6782 case PassByRefKind::WarnOnCell:
6783 e.String(
6784 makeStaticString("Only variables should be passed by reference"));
6785 e.Int(k_E_STRICT);
6786 e.FCallBuiltin(2, 2, s_trigger_error.get());
6787 emitPop(e);
6788 break;
6789 case PassByRefKind::ErrorOnCell:
6790 auto save = m_evalStack;
6791 e.String(
6792 makeStaticString("Only variables can be passed by reference"));
6793 e.Fatal(FatalOp::Runtime);
6794 m_evalStackIsUnknown = false;
6795 m_evalStack = save;
6796 return false;
6799 } else {
6800 emitCGet(e);
6802 return true;
6805 static bool isNormalLocalVariable(const ExpressionPtr& expr) {
6806 SimpleVariable* sv = static_cast<SimpleVariable*>(expr.get());
6807 return (expr->is(Expression::KindOfSimpleVariable) &&
6808 !sv->isSuperGlobal() &&
6809 !sv->isThis());
6812 void EmitterVisitor::emitLambdaCaptureArg(Emitter& e, ExpressionPtr exp) {
6813 // Constant folding may lead this to be not a var anymore,
6814 // so we should not be emitting *GetL in this case.
6815 if (!isNormalLocalVariable(exp)) {
6816 visit(exp);
6817 return;
6819 auto const sv = static_cast<SimpleVariable*>(exp.get());
6820 Id locId = m_curFunc->lookupVarId(makeStaticString(sv->getName()));
6821 emitVirtualLocal(locId);
6822 e.CUGetL(locId);
6825 void EmitterVisitor::emitBuiltinDefaultArg(Emitter& e, Variant& v,
6826 MaybeDataType t, int paramId) {
6827 switch (v.getType()) {
6828 case KindOfNull:
6829 if (t) {
6830 [&] {
6831 switch (*t) {
6832 case KindOfPersistentString:
6833 case KindOfString:
6834 case KindOfPersistentArray:
6835 case KindOfArray:
6836 case KindOfObject:
6837 case KindOfResource:
6838 e.Int(0);
6839 return;
6840 case KindOfUninit:
6841 case KindOfNull:
6842 case KindOfBoolean:
6843 case KindOfInt64:
6844 case KindOfDouble:
6845 case KindOfRef:
6846 case KindOfClass:
6847 break;
6849 not_reached();
6850 }();
6851 } else {
6852 e.NullUninit();
6854 return;
6856 case KindOfBoolean:
6857 if (v.getBoolean()) {
6858 e.True();
6859 } else {
6860 e.False();
6862 return;
6864 case KindOfInt64:
6865 e.Int(v.getInt64());
6866 return;
6868 case KindOfDouble:
6869 e.Double(v.toDouble());
6870 return;
6872 case KindOfPersistentString:
6873 case KindOfString: {
6874 StringData *nValue = makeStaticString(v.getStringData());
6875 e.String(nValue);
6876 return;
6879 case KindOfPersistentArray:
6880 case KindOfArray:
6881 e.Array(v.getArrayData());
6882 return;
6884 case KindOfUninit:
6885 case KindOfObject:
6886 case KindOfResource:
6887 case KindOfRef:
6888 case KindOfClass:
6889 break;
6891 not_reached();
6894 void EmitterVisitor::emitFuncCallArg(Emitter& e,
6895 ExpressionPtr exp,
6896 int paramId,
6897 bool isUnpack) {
6898 visit(exp);
6899 if (checkIfStackEmpty("FPass*")) return;
6901 // TODO(4599379): if dealing with an unpack, here is where we'd want to
6902 // emit a bytecode to traverse any containers;
6904 auto kind = getPassByRefKind(exp);
6905 if (isUnpack) {
6906 // This deals with the case where the called function has a
6907 // by ref param at the index of the unpack (because we don't
6908 // want to box the unpack itself).
6909 // But note that unless the user created the array manually,
6910 // and added reference params at the correct places, we'll
6911 // still get warnings, and the array elements will not be
6912 // passed by reference.
6913 emitConvertToCell(e);
6914 kind = PassByRefKind::AllowCell;
6916 emitFPass(e, paramId, kind);
6919 void EmitterVisitor::emitFPass(Emitter& e, int paramId,
6920 PassByRefKind passByRefKind) {
6921 if (checkIfStackEmpty("FPass*")) return;
6922 LocationGuard locGuard(e, m_tempLoc);
6923 m_tempLoc.clear();
6925 emitClsIfSPropBase(e);
6926 int iLast = m_evalStack.size()-1;
6927 int i = scanStackForLocation(iLast);
6928 int sz = iLast - i;
6929 assert(sz >= 0);
6930 char sym = m_evalStack.get(i);
6931 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
6932 switch (sym) {
6933 case StackSym::L: e.FPassL(paramId, m_evalStack.getLoc(i)); break;
6934 case StackSym::C:
6935 switch (passByRefKind) {
6936 case PassByRefKind::AllowCell: e.FPassC(paramId); break;
6937 case PassByRefKind::WarnOnCell: e.FPassCW(paramId); break;
6938 case PassByRefKind::ErrorOnCell: e.FPassCE(paramId); break;
6939 default: assert(false);
6941 break;
6942 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
6943 case StackSym::CN: e.FPassN(paramId); break;
6944 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
6945 case StackSym::CG: e.FPassG(paramId); break;
6946 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
6947 case StackSym::CS: e.FPassS(paramId); break;
6948 case StackSym::V: e.FPassV(paramId); break;
6949 case StackSym::R: e.FPassR(paramId); break;
6950 default: {
6951 unexpectedStackSym(sym, "emitFPass");
6952 break;
6955 } else {
6956 auto const stackCount = emitMOp(i, iLast, e, MInstrOpts{paramId});
6957 e.FPassM(paramId, stackCount, symToMemberKey(e, iLast, true /* allowW */));
6961 void EmitterVisitor::emitIsset(Emitter& e) {
6962 if (checkIfStackEmpty("Isset*")) return;
6964 emitClsIfSPropBase(e);
6965 int iLast = m_evalStack.size()-1;
6966 int i = scanStackForLocation(iLast);
6967 int sz = iLast - i;
6968 assert(sz >= 0);
6969 char sym = m_evalStack.get(i);
6970 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
6971 switch (sym) {
6972 case StackSym::L: e.IssetL(m_evalStack.getLoc(i)); break;
6973 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
6974 case StackSym::CN: e.IssetN(); break;
6975 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
6976 case StackSym::CG: e.IssetG(); break;
6977 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
6978 case StackSym::CS: e.IssetS(); break;
6979 //XXX: Zend does not allow isset() on the result
6980 // of a function call. We allow it here so that emitted
6981 // code is valid. Once the parser handles this correctly,
6982 // the R and C cases can go.
6983 case StackSym::R: e.UnboxR(); // fall through
6984 case StackSym::C:
6985 e.IsTypeC(IsTypeOp::Null);
6986 e.Not();
6987 break;
6988 default: {
6989 unexpectedStackSym(sym, "emitIsset");
6990 break;
6993 } else {
6994 emitQueryMOp(i, iLast, e, QueryMOp::Isset);
6998 void EmitterVisitor::emitIsType(Emitter& e, IsTypeOp op) {
6999 if (checkIfStackEmpty("IsType")) return;
7001 emitConvertToCellOrLoc(e);
7002 switch (char sym = m_evalStack.top()) {
7003 case StackSym::L:
7004 e.IsTypeL(m_evalStack.getLoc(m_evalStack.size() - 1), op);
7005 break;
7006 case StackSym::C:
7007 e.IsTypeC(op);
7008 break;
7009 default:
7010 unexpectedStackSym(sym, "emitIsType");
7014 void EmitterVisitor::emitEmpty(Emitter& e) {
7015 if (checkIfStackEmpty("Empty*")) return;
7017 emitClsIfSPropBase(e);
7018 int iLast = m_evalStack.size()-1;
7019 int i = scanStackForLocation(iLast);
7020 int sz = iLast - i;
7021 assert(sz >= 0);
7022 char sym = m_evalStack.get(i);
7023 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7024 switch (sym) {
7025 case StackSym::L: e.EmptyL(m_evalStack.getLoc(i)); break;
7026 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
7027 case StackSym::CN: e.EmptyN(); break;
7028 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
7029 case StackSym::CG: e.EmptyG(); break;
7030 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
7031 case StackSym::CS: e.EmptyS(); break;
7032 case StackSym::R: e.UnboxR(); // fall through
7033 case StackSym::C: e.Not(); break;
7034 default: {
7035 unexpectedStackSym(sym, "emitEmpty");
7036 break;
7039 } else {
7040 emitQueryMOp(i, iLast, e, QueryMOp::Empty);
7044 void EmitterVisitor::emitUnset(Emitter& e,
7045 ExpressionPtr exp /* = ExpressionPtr() */) {
7046 if (checkIfStackEmpty("Unset*")) return;
7048 emitClsIfSPropBase(e);
7049 int iLast = m_evalStack.size()-1;
7050 int i = scanStackForLocation(iLast);
7051 int sz = iLast - i;
7052 assert(sz >= 0);
7053 char sym = m_evalStack.get(i);
7054 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7055 switch (sym) {
7056 case StackSym::L: e.UnsetL(m_evalStack.getLoc(i)); break;
7057 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
7058 case StackSym::CN: e.UnsetN(); break;
7059 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
7060 case StackSym::CG: e.UnsetG(); break;
7061 case StackSym::LS: // fall through
7062 case StackSym::CS: {
7063 assert(exp);
7065 std::ostringstream s;
7066 s << "Attempt to unset static property " << exp->getText();
7067 emitMakeUnitFatal(e, s.str().c_str());
7068 break;
7070 default: {
7071 unexpectedStackSym(sym, "emitUnset");
7072 break;
7075 } else {
7076 auto const stackCount = emitMOp(i, iLast, e, MInstrOpts{MOpFlags::Unset});
7077 e.UnsetM(stackCount, symToMemberKey(e, iLast, false /* allowW */));
7081 void EmitterVisitor::emitVisitAndUnset(Emitter& e, ExpressionPtr exp) {
7082 visit(exp);
7083 emitUnset(e, exp);
7086 void EmitterVisitor::emitSet(Emitter& e) {
7087 if (checkIfStackEmpty("Set*")) return;
7089 int iLast = m_evalStack.size()-2;
7090 int i = scanStackForLocation(iLast);
7091 int sz = iLast - i;
7092 assert(sz >= 0);
7093 char sym = m_evalStack.get(i);
7094 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7095 switch (sym) {
7096 case StackSym::L: e.SetL(m_evalStack.getLoc(i)); break;
7097 case StackSym::LN: emitCGetL2(e); // fall through
7098 case StackSym::CN: e.SetN(); break;
7099 case StackSym::LG: emitCGetL2(e); // fall through
7100 case StackSym::CG: e.SetG(); break;
7101 case StackSym::LS: emitCGetL3(e); // fall through
7102 case StackSym::CS: e.SetS(); break;
7103 default: {
7104 unexpectedStackSym(sym, "emitSet");
7105 break;
7108 } else {
7109 auto const stackCount =
7110 emitMOp(i, iLast, e, MInstrOpts{MOpFlags::Define}.rhs());
7111 return e.SetM(stackCount, symToMemberKey(e, iLast, true /* allowW */));
7115 void EmitterVisitor::emitSetOp(Emitter& e, int tokenOp) {
7116 if (checkIfStackEmpty("SetOp*")) return;
7118 auto ifIntOverflow = [](SetOpOp trueVal, SetOpOp falseVal) {
7119 return RuntimeOption::IntsOverflowToInts ? trueVal : falseVal;
7122 auto const op = [&] {
7123 switch (tokenOp) {
7124 case T_PLUS_EQUAL:
7125 return ifIntOverflow(SetOpOp::PlusEqual, SetOpOp::PlusEqualO);
7126 case T_MINUS_EQUAL:
7127 return ifIntOverflow(SetOpOp::MinusEqual, SetOpOp::MinusEqualO);
7128 case T_MUL_EQUAL:
7129 return ifIntOverflow(SetOpOp::MulEqual, SetOpOp::MulEqualO);
7130 case T_POW_EQUAL: return SetOpOp::PowEqual;
7131 case T_DIV_EQUAL: return SetOpOp::DivEqual;
7132 case T_CONCAT_EQUAL: return SetOpOp::ConcatEqual;
7133 case T_MOD_EQUAL: return SetOpOp::ModEqual;
7134 case T_AND_EQUAL: return SetOpOp::AndEqual;
7135 case T_OR_EQUAL: return SetOpOp::OrEqual;
7136 case T_XOR_EQUAL: return SetOpOp::XorEqual;
7137 case T_SL_EQUAL: return SetOpOp::SlEqual;
7138 case T_SR_EQUAL: return SetOpOp::SrEqual;
7139 default: break;
7141 not_reached();
7142 }();
7144 int iLast = m_evalStack.size()-2;
7145 int i = scanStackForLocation(iLast);
7146 int sz = iLast - i;
7147 assert(sz >= 0);
7148 char sym = m_evalStack.get(i);
7149 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7150 switch (sym) {
7151 case StackSym::L: e.SetOpL(m_evalStack.getLoc(i), op); break;
7152 case StackSym::LN: emitCGetL2(e); // fall through
7153 case StackSym::CN: e.SetOpN(op); break;
7154 case StackSym::LG: emitCGetL2(e); // fall through
7155 case StackSym::CG: e.SetOpG(op); break;
7156 case StackSym::LS: emitCGetL3(e); // fall through
7157 case StackSym::CS: e.SetOpS(op); break;
7158 default: {
7159 unexpectedStackSym(sym, "emitSetOp");
7160 break;
7163 } else {
7164 auto const stackCount =
7165 emitMOp(i, iLast, e, MInstrOpts{MOpFlags::Define}.rhs());
7166 e.SetOpM(stackCount, op, symToMemberKey(e, iLast, true /* allowW */));
7170 void EmitterVisitor::emitBind(Emitter& e) {
7171 if (checkIfStackEmpty("Bind*")) return;
7173 int iLast = m_evalStack.size()-2;
7174 int i = scanStackForLocation(iLast);
7175 int sz = iLast - i;
7176 assert(sz >= 0);
7177 char sym = m_evalStack.get(i);
7178 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7179 switch (sym) {
7180 case StackSym::L: e.BindL(m_evalStack.getLoc(i)); break;
7181 case StackSym::LN: emitCGetL2(e); // fall through
7182 case StackSym::CN: e.BindN(); break;
7183 case StackSym::LG: emitCGetL2(e); // fall through
7184 case StackSym::CG: e.BindG(); break;
7185 case StackSym::LS: emitCGetL3(e); // fall through
7186 case StackSym::CS: e.BindS(); break;
7187 default: {
7188 unexpectedStackSym(sym, "emitBind");
7189 break;
7192 } else {
7193 auto const stackCount =
7194 emitMOp(i, iLast, e, MInstrOpts{MOpFlags::DefineReffy}.rhs());
7195 e.BindM(stackCount, symToMemberKey(e, iLast, true /* allowW */));
7199 void EmitterVisitor::emitIncDec(Emitter& e, IncDecOp op) {
7200 if (checkIfStackEmpty("IncDec*")) return;
7202 emitClsIfSPropBase(e);
7203 int iLast = m_evalStack.size()-1;
7204 int i = scanStackForLocation(iLast);
7205 int sz = iLast - i;
7206 assert(sz >= 0);
7207 char sym = m_evalStack.get(i);
7208 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7209 switch (sym) {
7210 case StackSym::L: e.IncDecL(m_evalStack.getLoc(i), op); break;
7211 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
7212 case StackSym::CN: e.IncDecN(op); break;
7213 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
7214 case StackSym::CG: e.IncDecG(op); break;
7215 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
7216 case StackSym::CS: e.IncDecS(op); break;
7217 default: {
7218 unexpectedStackSym(sym, "emitIncDec");
7219 break;
7222 } else {
7223 auto const stackCount =
7224 emitMOp(i, iLast, e, MInstrOpts{MOpFlags::Define});
7225 e.IncDecM(stackCount, op, symToMemberKey(e, iLast, true /* allowW */));
7229 void EmitterVisitor::emitConvertToCell(Emitter& e) {
7230 emitCGet(e);
7233 void EmitterVisitor::emitConvertSecondToCell(Emitter& e) {
7234 if (m_evalStack.size() <= 1) {
7235 InvariantViolation(
7236 "Emitter encounted an empty evaluation stack when inside "
7237 "the emitConvertSecondToCell() function (at offset %d)",
7238 m_ue.bcPos());
7239 return;
7241 char sym = m_evalStack.get(m_evalStack.size() - 2);
7242 char symFlavor = StackSym::GetSymFlavor(sym);
7243 if (symFlavor == StackSym::C) {
7244 // do nothing
7245 } else if (symFlavor == StackSym::L) {
7246 emitCGetL2(e);
7247 } else {
7248 // emitConvertSecondToCell() should never be used for symbolic flavors
7249 // other than C or L
7250 InvariantViolation(
7251 "Emitter encountered an unsupported StackSym \"%s\" on "
7252 "the evaluation stack inside the emitConvertSecondToCell()"
7253 " function (at offset %d)",
7254 StackSym::ToString(sym).c_str(),
7255 m_ue.bcPos());
7259 void EmitterVisitor::emitConvertToCellIfVar(Emitter& e) {
7260 if (!m_evalStack.empty()) {
7261 char sym = m_evalStack.top();
7262 if (sym == StackSym::V) {
7263 emitConvertToCell(e);
7268 void EmitterVisitor::emitConvertToCellOrLoc(Emitter& e) {
7269 if (m_evalStack.empty()) {
7270 InvariantViolation(
7271 "Emitter encounted an empty evaluation stack when inside "
7272 "the emitConvertToCellOrLoc() function (at offset %d)",
7273 m_ue.bcPos());
7274 return;
7276 char sym = m_evalStack.top();
7277 if (sym == StackSym::L) {
7278 // If the top of stack is a loc that is not marked, do nothing
7279 } else {
7280 // Otherwise, call emitCGet to convert the top of stack to cell
7281 emitCGet(e);
7285 void EmitterVisitor::emitConvertToVar(Emitter& e) {
7286 emitVGet(e);
7290 * Class bases are stored on the symbolic stack in a "virtual" way so
7291 * we can resolve them later (here) in order to properly handle php
7292 * evaluation order.
7294 * For example, in:
7296 * $cls = 'cls';
7297 * $cls::$x[0][f()] = g();
7299 * We need to evaluate f(), then resolve $cls to an A (possibly
7300 * invoking an autoload handler), then evaluate g(), then do the set.
7302 * Complex cases involve unnamed local temporaries. For example, in:
7304 * ${func()}::${f()} = g();
7306 * We'll emit code which calls func() and stashes the result in a
7307 * unnamed local. Then we call f(), then we turn the unnamed local
7308 * into an 'A' so that autoload handlers will run after f(). Then g()
7309 * is evaluated and then the set happens.
7311 void EmitterVisitor::emitResolveClsBase(Emitter& e, int pos) {
7312 switch (m_evalStack.getClsBaseType(pos)) {
7313 case SymbolicStack::CLS_STRING_NAME:
7314 e.String(m_evalStack.getName(pos));
7315 emitAGet(e);
7316 break;
7317 case SymbolicStack::CLS_LATE_BOUND:
7318 e.LateBoundCls();
7319 break;
7320 case SymbolicStack::CLS_SELF:
7321 e.Self();
7322 break;
7323 case SymbolicStack::CLS_PARENT:
7324 e.Parent();
7325 break;
7326 case SymbolicStack::CLS_NAMED_LOCAL: {
7327 int loc = m_evalStack.getLoc(pos);
7328 emitVirtualLocal(loc);
7329 emitAGet(e);
7330 break;
7332 case SymbolicStack::CLS_UNNAMED_LOCAL: {
7333 int loc = m_evalStack.getLoc(pos);
7334 emitVirtualLocal(loc);
7335 emitAGet(e);
7336 emitVirtualLocal(loc);
7337 emitUnset(e);
7338 newFaultRegionAndFunclet(m_evalStack.getUnnamedLocStart(pos),
7339 m_ue.bcPos(),
7340 new UnsetUnnamedLocalThunklet(loc));
7341 m_curFunc->freeUnnamedLocal(loc);
7342 break;
7344 case SymbolicStack::CLS_INVALID:
7345 default:
7346 assert(false);
7349 m_evalStack.consumeBelowTop(m_evalStack.size() - pos - 1);
7352 void EmitterVisitor::emitClsIfSPropBase(Emitter& e) {
7353 // If the eval stack is empty, then there is no work to do
7354 if (m_evalStack.empty()) return;
7356 // Scan past any values marked with the Elem, NewElem, or Prop markers
7357 int pos = m_evalStack.size() - 1;
7358 for (;;) {
7359 char marker = StackSym::GetMarker(m_evalStack.get(pos));
7360 if (marker != StackSym::E && marker != StackSym::W &&
7361 marker != StackSym::P && marker != StackSym::Q) {
7362 break;
7364 --pos;
7365 if (pos < 0) {
7366 InvariantViolation("Emitter expected a location on the stack but none "
7367 "was found (at offset %d)",
7368 m_ue.bcPos());
7369 return;
7372 // After scanning, if we did not find a value marked with the SProp
7373 // marker then there is no work to do
7374 if (StackSym::GetMarker(m_evalStack.get(pos)) != StackSym::S) {
7375 return;
7378 --pos;
7379 if (pos < 0) {
7380 InvariantViolation(
7381 "Emitter emitted an instruction that tries to consume "
7382 "a value from the stack when the stack is empty "
7383 "(expected symbolic flavor \"C\" or \"L\" at offset %d)",
7384 m_ue.bcPos());
7387 emitResolveClsBase(e, pos);
7388 m_evalStack.set(m_evalStack.size() - 1,
7389 m_evalStack.get(m_evalStack.size() - 1) | StackSym::M);
7392 MaybeDataType EmitterVisitor::analyzeSwitch(SwitchStatementPtr sw,
7393 SwitchState& state) {
7394 auto& caseMap = state.cases;
7395 DataType t = KindOfUninit;
7396 StatementListPtr cases(sw->getCases());
7397 const int ncase = cases->getCount();
7399 // Bail if the cases aren't homogeneous
7400 for (int i = 0; i < ncase; ++i) {
7401 auto c = static_pointer_cast<CaseStatement>((*cases)[i]);
7402 auto condition = c->getCondition();
7403 if (condition) {
7404 Variant cval;
7405 DataType caseType;
7406 if (condition->getScalarValue(cval)) {
7407 caseType = cval.getType();
7408 if (caseType == KindOfPersistentString) caseType = KindOfString;
7409 if ((caseType != KindOfInt64 && caseType != KindOfString) ||
7410 !IMPLIES(t != KindOfUninit, caseType == t)) {
7411 return folly::none;
7413 t = caseType;
7414 } else {
7415 return folly::none;
7417 int64_t n;
7418 bool isNonZero;
7419 if (t == KindOfInt64) {
7420 n = cval.asInt64Val();
7421 isNonZero = n;
7422 } else {
7423 always_assert(t == KindOfString);
7424 n = m_ue.mergeLitstr(cval.asStrRef().get());
7425 isNonZero = false; // not used for string switches
7427 if (!caseMap.count(n)) {
7428 // If 'case n:' appears multiple times, only the first will
7429 // ever match
7430 caseMap[n] = i;
7431 if (t == KindOfString) {
7432 // We have to preserve the original order of the cases for string
7433 // switches because of insane things like 0 being equal to any string
7434 // that is not a nonzero numeric string.
7435 state.caseOrder.push_back(StrCase(safe_cast<Id>(n), i));
7438 if (state.nonZeroI == -1 && isNonZero) {
7439 // true is equal to any non-zero integer, so to preserve php's
7440 // switch semantics we have to remember the first non-zero
7441 // case to appear in the source text
7442 state.nonZeroI = i;
7444 } else if (LIKELY(state.defI == -1)) {
7445 state.defI = i;
7446 } else {
7447 // Multiple defaults are not allowed
7448 throw IncludeTimeFatalException(
7449 c, "Switch statements may only contain one default: clause");
7453 if (t == KindOfInt64) {
7454 int64_t base = caseMap.begin()->first;
7455 int64_t nTargets = caseMap.rbegin()->first - base + 1;
7456 // Fail if the cases are too sparse. We emit Switch even for absurdly small
7457 // cases to allow the jit to decide when to lower back to comparisons.
7458 if ((float)caseMap.size() / nTargets < 0.5) {
7459 return folly::none;
7461 } else if (t == KindOfString) {
7462 if (caseMap.size() < kMinStringSwitchCases) {
7463 return folly::none;
7467 return t;
7470 void EmitterVisitor::emitIntegerSwitch(Emitter& e, SwitchStatementPtr sw,
7471 std::vector<Label>& caseLabels,
7472 Label& done, const SwitchState& state) {
7473 auto& caseMap = state.cases;
7474 int64_t base = caseMap.begin()->first;
7475 int64_t nTargets = caseMap.rbegin()->first - base + 1;
7477 // It's on. Map case values to Labels, filling in the blanks as
7478 // appropriate.
7479 Label* defLabel = state.defI == -1 ? &done : &caseLabels[state.defI];
7480 std::vector<Label*> labels(nTargets + 2);
7481 for (int i = 0; i < nTargets; ++i) {
7482 if (auto const caseIdx = folly::get_ptr(caseMap, base + i)) {
7483 labels[i] = &caseLabels[*caseIdx];
7484 } else {
7485 labels[i] = defLabel;
7489 // Fill in offsets for the first non-zero case and default
7490 labels[labels.size() - 2] =
7491 state.nonZeroI == -1 ? defLabel : &caseLabels[state.nonZeroI];
7492 labels[labels.size() - 1] = defLabel;
7494 visit(sw->getExp());
7495 emitConvertToCell(e);
7496 e.Switch(SwitchKind::Bounded, base, labels);
7499 void EmitterVisitor::emitStringSwitch(Emitter& e, SwitchStatementPtr sw,
7500 std::vector<Label>& caseLabels,
7501 Label& done, const SwitchState& state) {
7502 std::vector<Emitter::StrOff> labels;
7503 for (auto& pair : state.caseOrder) {
7504 labels.push_back(Emitter::StrOff(pair.first, &caseLabels[pair.second]));
7507 // Default case comes last
7508 Label* defLabel = state.defI == -1 ? &done : &caseLabels[state.defI];
7509 labels.push_back(Emitter::StrOff(-1, defLabel));
7511 visit(sw->getExp());
7512 emitConvertToCell(e);
7513 e.SSwitch(labels);
7516 void EmitterVisitor::markElem(Emitter& e) {
7517 if (m_evalStack.empty()) {
7518 InvariantViolation("Emitter encountered an empty evaluation stack inside"
7519 " the markElem function (at offset %d)",
7520 m_ue.bcPos());
7521 return;
7523 char sym = m_evalStack.top();
7524 if (sym == StackSym::C || sym == StackSym::L || sym == StackSym::T ||
7525 sym == StackSym::I) {
7526 m_evalStack.set(m_evalStack.size()-1, (sym | StackSym::E));
7527 } else {
7528 InvariantViolation(
7529 "Emitter encountered an unsupported StackSym \"%s\" on "
7530 "the evaluation stack inside the markElem function (at "
7531 "offset %d)",
7532 StackSym::ToString(sym).c_str(),
7533 m_ue.bcPos());
7537 void EmitterVisitor::markNewElem(Emitter& e) {
7538 m_evalStack.push(StackSym::W);
7541 void EmitterVisitor::markProp(Emitter& e, PropAccessType propAccessType) {
7542 if (m_evalStack.empty()) {
7543 InvariantViolation(
7544 "Emitter encountered an empty evaluation stack inside "
7545 "the markProp function (at offset %d)",
7546 m_ue.bcPos());
7547 return;
7549 char sym = m_evalStack.top();
7550 if (sym == StackSym::C || sym == StackSym::L || sym == StackSym::T) {
7551 m_evalStack.set(
7552 m_evalStack.size()-1,
7553 (sym | (propAccessType == PropAccessType::NullSafe
7554 ? StackSym::Q
7555 : StackSym::P
7558 } else {
7559 InvariantViolation(
7560 "Emitter encountered an unsupported StackSym \"%s\" on "
7561 "the evaluation stack inside the markProp function (at "
7562 "offset %d)",
7563 StackSym::ToString(sym).c_str(),
7564 m_ue.bcPos());
7568 void EmitterVisitor::markSProp(Emitter& e) {
7569 if (m_evalStack.empty()) {
7570 InvariantViolation(
7571 "Emitter encountered an empty evaluation stack inside "
7572 "the markSProp function (at offset %d)",
7573 m_ue.bcPos());
7574 return;
7576 char sym = m_evalStack.top();
7577 if (sym == StackSym::C || sym == StackSym::L) {
7578 m_evalStack.set(m_evalStack.size()-1, (sym | StackSym::S));
7579 } else {
7580 InvariantViolation(
7581 "Emitter encountered an unsupported StackSym \"%s\" on "
7582 "the evaluation stack inside the markSProp function "
7583 "(at offset %d)",
7584 StackSym::ToString(sym).c_str(),
7585 m_ue.bcPos());
7589 #define MARK_NAME_BODY(index, requiredStackSize) \
7590 if (m_evalStack.size() < requiredStackSize) { \
7591 InvariantViolation( \
7592 "Emitter encountered an evaluation stack with %lu" \
7593 " elements inside the %s function (at offset %d)", \
7594 (unsigned long)m_evalStack.size(), \
7595 __FUNCTION__, m_ue.bcPos()); \
7596 return; \
7598 char sym = m_evalStack.get(index); \
7599 if (sym == StackSym::C || sym == StackSym::L) { \
7600 m_evalStack.set(index, (sym | StackSym::N)); \
7601 } else { \
7602 InvariantViolation( \
7603 "Emitter encountered an unsupported StackSym \"%s\" " \
7604 "on the evaluation stack inside the %s function (at " \
7605 "offset %d)", \
7606 StackSym::ToString(sym).c_str(), __FUNCTION__, \
7607 m_ue.bcPos()); \
7610 void EmitterVisitor::markName(Emitter& e) {
7611 int index = m_evalStack.size() - 1;
7612 MARK_NAME_BODY(index, 1);
7615 void EmitterVisitor::markNameSecond(Emitter& e) {
7616 int index = m_evalStack.size() - 2;
7617 MARK_NAME_BODY(index, 2);
7620 #undef MARK_NAME_BODY
7622 void EmitterVisitor::markGlobalName(Emitter& e) {
7623 if (m_evalStack.empty()) {
7624 InvariantViolation(
7625 "Emitter encountered an empty evaluation stack inside "
7626 "the markGlobalName function (at offset %d)",
7627 m_ue.bcPos());
7628 return;
7630 char sym = m_evalStack.top();
7631 if (sym == StackSym::C || sym == StackSym::L) {
7632 m_evalStack.set(m_evalStack.size()-1, (sym | StackSym::G));
7633 } else {
7634 InvariantViolation(
7635 "Emitter encountered an unsupported StackSym \"%s\" on "
7636 "the evaluation stack inside the markGlobalName function "
7637 "(at offset %d)",
7638 StackSym::ToString(sym).c_str(),
7639 m_ue.bcPos());
7643 void EmitterVisitor::emitNameString(Emitter& e, ExpressionPtr n,
7644 bool allowLiteral) {
7645 Variant v;
7646 if (n->getScalarValue(v) && v.isString()) {
7647 StringData* nLiteral = makeStaticString(v.toCStrRef().get());
7648 if (allowLiteral) {
7649 m_evalStack.push(StackSym::T);
7650 } else {
7651 e.String(nLiteral);
7653 m_evalStack.setString(nLiteral);
7654 } else {
7655 visit(n);
7656 emitConvertToCellOrLoc(e);
7660 void EmitterVisitor::postponeMeth(MethodStatementPtr m, FuncEmitter* fe,
7661 bool top,
7662 ClosureUseVarVec* useVars /* = NULL */) {
7663 m_postponedMeths.push_back(PostponedMeth(m, fe, top, useVars));
7666 void EmitterVisitor::postponeCtor(InterfaceStatementPtr is, FuncEmitter* fe) {
7667 m_postponedCtors.push_back(PostponedCtor(is, fe));
7670 void EmitterVisitor::postponePinit(InterfaceStatementPtr is, FuncEmitter* fe,
7671 NonScalarVec* v) {
7672 m_postponedPinits.push_back(PostponedNonScalars(is, fe, v));
7675 void EmitterVisitor::postponeSinit(InterfaceStatementPtr is, FuncEmitter* fe,
7676 NonScalarVec* v) {
7677 m_postponedSinits.push_back(PostponedNonScalars(is, fe, v));
7680 void EmitterVisitor::postponeCinit(InterfaceStatementPtr is, FuncEmitter* fe,
7681 NonScalarVec* v) {
7682 m_postponedCinits.push_back(PostponedNonScalars(is, fe, v));
7685 static Attr buildAttrs(ModifierExpressionPtr mod, bool isRef = false) {
7686 int attrs = AttrNone;
7687 if (isRef) {
7688 attrs |= AttrReference;
7690 if (mod) {
7691 attrs |= mod->isPublic() ? AttrPublic :
7692 mod->isPrivate() ? AttrPrivate :
7693 mod->isProtected() ? AttrProtected : AttrNone;
7694 if (mod->isStatic()) {
7695 attrs |= AttrStatic;
7697 if (mod->isAbstract()) {
7698 attrs |= AttrAbstract;
7700 if (mod->isFinal()) {
7701 attrs |= AttrFinal;
7704 return Attr(attrs);
7708 * <<__HipHopSpecific>> user attribute marks funcs/methods as HipHop specific
7709 * for reflection.
7710 * <<__IsFoldable>> Function has no side-effects and may be called at
7711 * compile time with constant input to get deterministic output.
7713 const StaticString
7714 s_IsFoldable("__IsFoldable"),
7715 s_ParamCoerceModeNull("__ParamCoerceModeNull"),
7716 s_ParamCoerceModeFalse("__ParamCoerceModeFalse");
7718 static void parseUserAttributes(FuncEmitter* fe, Attr& attrs) {
7719 if (fe->userAttributes.count(s_IsFoldable.get())) {
7720 attrs = attrs | AttrIsFoldable;
7722 if (fe->userAttributes.count(s_ParamCoerceModeNull.get())) {
7723 attrs = attrs | AttrParamCoerceModeNull;
7724 } else if (fe->userAttributes.count(s_ParamCoerceModeFalse.get())) {
7725 attrs = attrs | AttrParamCoerceModeFalse;
7729 static Attr buildMethodAttrs(MethodStatementPtr meth, FuncEmitter* fe,
7730 bool top, bool allowOverride) {
7731 FunctionScopePtr funcScope = meth->getFunctionScope();
7732 ModifierExpressionPtr mod(meth->getModifiers());
7733 Attr attrs = buildAttrs(mod, meth->isRef());
7735 if (allowOverride) {
7736 attrs = attrs | AttrAllowOverride;
7739 // if hasCallToGetArgs() or if mayUseVV
7740 if (meth->hasCallToGetArgs() || funcScope->mayUseVV()) {
7741 attrs = attrs | AttrMayUseVV;
7744 auto fullName = meth->getOriginalFullName();
7745 auto it = Option::FunctionSections.find(fullName);
7746 if ((it != Option::FunctionSections.end() && it->second == "hot") ||
7747 (RuntimeOption::EvalRandomHotFuncs &&
7748 (hash_string_i_unsafe(fullName.c_str(), fullName.size()) & 8))) {
7749 attrs = attrs | AttrHot;
7752 if (!SystemLib::s_inited) {
7753 // we're building systemlib. everything is unique
7754 attrs = attrs | AttrBuiltin | AttrUnique | AttrPersistent;
7755 } else if (Option::WholeProgram) {
7756 if (!funcScope->isRedeclaring()) {
7757 attrs = attrs | AttrUnique;
7758 if (top &&
7759 (!funcScope->isVolatile() ||
7760 funcScope->isPersistent())) {
7761 attrs = attrs | AttrPersistent;
7764 if (meth->getClassScope() && !funcScope->hasOverride()) {
7765 attrs = attrs | AttrNoOverride;
7767 if (funcScope->isSystem()) {
7768 assert((attrs & AttrPersistent) || meth->getClassScope());
7769 attrs = attrs | AttrBuiltin;
7773 // For closures, the MethodStatement didn't have real attributes; enforce
7774 // that the __invoke method is public here
7775 if (fe->isClosureBody) {
7776 assert(!(attrs & (AttrProtected | AttrPrivate)));
7777 attrs = attrs | AttrPublic;
7780 // Coerce memoized methods to private. This is needed for code that uses
7781 // parent:: to call through to the correct underlying function
7782 if (meth->is(Statement::KindOfMethodStatement) && fe->isMemoizeImpl) {
7783 attrs = static_cast<Attr>(attrs & ~(AttrPublic | AttrProtected));
7784 attrs = attrs | AttrPrivate;
7787 parseUserAttributes(fe, attrs);
7788 // Not supported except in __Native functions
7789 attrs = static_cast<Attr>(
7790 attrs & ~(AttrParamCoerceModeNull | AttrParamCoerceModeFalse));
7792 return attrs;
7796 * The code below is used for both, function/method parameter type as well as
7797 * for function/method return type.
7799 static TypeConstraint
7800 determine_type_constraint_from_annot(const TypeAnnotationPtr annot,
7801 bool is_return) {
7802 if (annot) {
7803 auto flags = TypeConstraint::ExtendedHint | TypeConstraint::HHType;
7805 // We only care about a subset of extended type constaints:
7806 // typevar, nullable, soft, return types.
7808 // For everything else, we return {}. We also return {} for annotations
7809 // we don't know how to handle.
7810 if (annot->isFunction() || annot->isMixed()) {
7811 return {};
7813 if (annot->isTypeAccess()) {
7814 flags = flags | TypeConstraint::TypeConstant;
7816 if (annot->isTypeVar()) {
7817 flags = flags | TypeConstraint::TypeVar;
7819 if (annot->isNullable()) {
7820 flags = flags | TypeConstraint::Nullable;
7822 if (annot->isSoft()) {
7823 flags = flags | TypeConstraint::Soft;
7825 if (!is_return &&
7826 (flags == (TypeConstraint::ExtendedHint | TypeConstraint::HHType))) {
7827 return {};
7830 auto strippedName = annot->stripNullable().stripSoft().vanillaName();
7832 return TypeConstraint{
7833 makeStaticString(strippedName),
7834 flags
7838 return {};
7841 static TypeConstraint
7842 determine_type_constraint(const ParameterExpressionPtr& par) {
7843 if (par->hasTypeHint()) {
7844 auto ce = dynamic_pointer_cast<ConstantExpression>(par->defaultValue());
7845 auto flags = TypeConstraint::NoFlags;
7846 if (ce && ce->isNull()) {
7847 flags = flags|TypeConstraint::Nullable;
7849 if (par->hhType()) {
7850 flags = flags|TypeConstraint::HHType;
7852 return TypeConstraint{
7853 makeStaticString(par->getOriginalTypeHint()),
7854 flags
7858 return determine_type_constraint_from_annot(par->annotation(), false);
7861 void EmitterVisitor::emitPostponedMeths() {
7862 std::vector<FuncEmitter*> top_fes;
7863 while (!m_postponedMeths.empty()) {
7864 assert(m_evalStack.m_actualStackHighWaterPtr == nullptr);
7865 PostponedMeth& p = m_postponedMeths.front();
7866 MethodStatementPtr meth = p.m_meth;
7867 FuncEmitter* fe = p.m_fe;
7869 ITRACE(1, "Emitting postponed method {}\n", meth->getOriginalFullName());
7870 Trace::Indent indent;
7872 if (!fe) {
7873 assert(p.m_top);
7874 const StringData* methName = makeStaticString(meth->getOriginalName());
7875 fe = new FuncEmitter(m_ue, -1, -1, methName);
7876 auto oldFunc = m_topMethodEmitted.find(meth->getOriginalName());
7877 if (oldFunc != m_topMethodEmitted.end()) {
7878 throw IncludeTimeFatalException(
7879 meth,
7880 "Cannot redeclare %s() (previously declared in %s:%d)",
7881 meth->getOriginalName().c_str(),
7882 oldFunc->second->ue().m_filepath->data(),
7883 oldFunc->second->getLocation().second);
7885 m_topMethodEmitted.emplace(meth->getOriginalName(), fe);
7887 p.m_fe = fe;
7888 top_fes.push_back(fe);
7891 auto funcScope = meth->getFunctionScope();
7892 m_curFunc = fe;
7893 fe->isAsync = funcScope->isAsync();
7894 fe->isGenerator = funcScope->isGenerator();
7896 if (fe->isAsync && !fe->isGenerator && meth->retTypeAnnotation()) {
7897 auto rta = meth->retTypeAnnotation();
7898 auto nTypeArgs = rta->numTypeArgs();
7899 if (!rta->isAwaitable() && !rta->isWaitHandle()) {
7900 if (fe->isClosureBody) {
7901 throw IncludeTimeFatalException(
7902 meth,
7903 "Return type hint for async closure must be awaitable"
7905 } else {
7906 throw IncludeTimeFatalException(
7907 meth,
7908 "Return type hint for async %s %s() must be awaitable",
7909 meth->getClassScope() ? "method" : "function",
7910 meth->getOriginalFullName().c_str()
7914 if (nTypeArgs >= 2) {
7915 throw IncludeTimeFatalException(
7916 meth,
7917 "Awaitable interface expects 1 type argument, %d given",
7918 nTypeArgs);
7922 if (funcScope->userAttributes().count("__Memoize") &&
7923 !funcScope->isAbstract()) {
7924 auto const originalName = fe->name;
7925 auto const rewrittenName = makeStaticString(
7926 folly::sformat("{}$memoize_impl", fe->name->data()));
7928 FuncEmitter* memoizeFe = nullptr;
7929 if (meth->is(Statement::KindOfFunctionStatement)) {
7930 if (!p.m_top) {
7931 throw IncludeTimeFatalException(meth,
7932 "<<__Memoize>> cannot be applied to closures and inline functions");
7935 memoizeFe = new FuncEmitter(m_ue, -1, -1, originalName);
7936 fe->name = rewrittenName;
7937 top_fes.push_back(memoizeFe);
7938 } else {
7939 // Rename the method and create a new method with the original name
7940 fe->pce()->renameMethod(originalName, rewrittenName);
7941 memoizeFe = m_ue.newMethodEmitter(originalName, fe->pce());
7942 bool added UNUSED = fe->pce()->addMethod(memoizeFe);
7943 assert(added);
7946 // Emit the new method that handles the memoization
7947 m_curFunc = memoizeFe;
7948 m_curFunc->isMemoizeWrapper = true;
7949 addMemoizeProp(meth);
7950 emitMethodMetadata(meth, p.m_closureUseVars, p.m_top);
7951 emitMemoizeMethod(meth, rewrittenName);
7953 // Switch back to the original method and mark it as a memoize
7954 // implementation
7955 m_curFunc = fe;
7956 m_curFunc->isMemoizeImpl = true;
7959 if (funcScope->isNative()) {
7960 bindNativeFunc(meth, fe);
7961 } else {
7962 emitMethodMetadata(meth, p.m_closureUseVars, p.m_top);
7963 emitMethod(meth);
7966 if (fe->isClosureBody) {
7967 TypedValue uninit;
7968 tvWriteUninit(&uninit);
7969 for (auto& sv : m_curFunc->staticVars) {
7970 auto const str = makeStaticString(
7971 folly::format("86static_{}", sv.name->data()).str());
7972 fe->pce()->addProperty(str, AttrPrivate, nullptr, nullptr,
7973 &uninit, RepoAuthType{});
7977 delete p.m_closureUseVars;
7978 m_postponedMeths.pop_front();
7981 for (size_t i = 0; i < top_fes.size(); i++) {
7982 m_ue.appendTopEmitter(top_fes[i]);
7986 void EmitterVisitor::bindUserAttributes(MethodStatementPtr meth,
7987 FuncEmitter *fe,
7988 bool &allowOverride) {
7989 auto const& userAttrs = meth->getFunctionScope()->userAttributes();
7990 for (auto& attr : userAttrs) {
7991 if (attr.first == "__Overridable") {
7992 allowOverride = true;
7993 continue;
7995 const StringData* uaName = makeStaticString(attr.first);
7996 ExpressionPtr uaValue = attr.second;
7997 assert(uaValue);
7998 assert(uaValue->isScalar());
7999 TypedValue tv;
8000 initScalar(tv, uaValue);
8001 fe->userAttributes[uaName] = tv;
8005 const StaticString s_Void("HH\\void");
8006 const char* attr_Deprecated = "__Deprecated";
8007 const StaticString s_attr_Deprecated(attr_Deprecated);
8009 void EmitterVisitor::bindNativeFunc(MethodStatementPtr meth,
8010 FuncEmitter *fe) {
8011 if (SystemLib::s_inited &&
8012 !(Option::WholeProgram && meth->isSystem())) {
8013 throw IncludeTimeFatalException(meth,
8014 "Native functions/methods may only be defined in systemlib");
8017 auto modifiers = meth->getModifiers();
8018 bool allowOverride = false;
8019 bindUserAttributes(meth, fe, allowOverride);
8021 Attr attributes = AttrBuiltin | AttrNative | AttrUnique | AttrPersistent;
8022 if (meth->isRef()) {
8023 attributes = attributes | AttrReference;
8025 auto pce = fe->pce();
8026 if (pce) {
8027 if (modifiers->isStatic()) {
8028 attributes = attributes | AttrStatic;
8030 if (modifiers->isFinal()) {
8031 attributes = attributes | AttrFinal;
8033 if (modifiers->isAbstract()) {
8034 attributes = attributes | AttrAbstract;
8036 if (modifiers->isPrivate()) {
8037 attributes = attributes | AttrPrivate;
8038 } else {
8039 attributes = attributes | (modifiers->isProtected()
8040 ? AttrProtected : AttrPublic);
8042 } else {
8043 if (allowOverride) {
8044 attributes = attributes | AttrAllowOverride;
8047 parseUserAttributes(fe, attributes);
8048 if (!(attributes & (AttrParamCoerceModeFalse | AttrParamCoerceModeNull))) {
8049 attributes = attributes | AttrParamCoerceModeNull;
8052 fe->setLocation(meth->line0(), meth->line1());
8053 fe->docComment = makeStaticString(
8054 Option::GenerateDocComments ? meth->getDocComment().c_str() : ""
8056 auto retType = meth->retTypeAnnotation();
8057 assert(retType ||
8058 meth->isNamed("__construct") ||
8059 meth->isNamed("__destruct"));
8060 fe->returnType = retType ? retType->dataType() : KindOfNull;
8061 fe->retUserType = makeStaticString(meth->getReturnTypeConstraint());
8063 FunctionScopePtr funcScope = meth->getFunctionScope();
8064 const char *funcname = funcScope->getScopeName().c_str();
8065 const char *classname = pce ? pce->name()->data() : nullptr;
8066 auto const& info = Native::GetBuiltinFunction(funcname, classname,
8067 modifiers->isStatic());
8069 if (!classname && (
8070 !strcasecmp(funcname, "fb_call_user_func_safe") ||
8071 !strcasecmp(funcname, "fb_call_user_func_safe_return") ||
8072 !strcasecmp(funcname, "fb_call_user_func_array_safe"))) {
8073 // Legacy optimization functions
8074 funcScope->setOptFunction(hphp_opt_fb_call_user_func);
8077 int nativeAttrs = fe->parseNativeAttributes(attributes);
8078 BuiltinFunction bif = nullptr, nif = nullptr;
8079 Native::getFunctionPointers(info, nativeAttrs, bif, nif);
8080 if (nif && !(nativeAttrs & Native::AttrZendCompat)) {
8081 if (retType) {
8082 fe->retTypeConstraint =
8083 determine_type_constraint_from_annot(retType, true);
8084 } else {
8085 fe->retTypeConstraint = TypeConstraint {
8086 s_Void.get(),
8087 TypeConstraint::ExtendedHint | TypeConstraint::HHType
8092 Emitter e(meth, m_ue, *this);
8093 FuncFinisher ff(this, e, fe, 0);
8094 Label topOfBody(e);
8096 Offset base = m_ue.bcPos();
8098 if (meth->getFunctionScope()->userAttributes().count(attr_Deprecated)) {
8099 emitDeprecationWarning(e, meth);
8102 fe->setBuiltinFunc(bif, nif, attributes, base);
8103 fillFuncEmitterParams(fe, meth->getParams(), true);
8104 if (nativeAttrs & Native::AttrOpCodeImpl) {
8105 ff.setStackPad(emitNativeOpCodeImpl(meth, funcname, classname, fe));
8106 } else {
8107 e.NativeImpl();
8109 emitMethodDVInitializers(e, meth, topOfBody);
8112 void EmitterVisitor::emitMethodMetadata(MethodStatementPtr meth,
8113 ClosureUseVarVec* useVars,
8114 bool top) {
8115 FuncEmitter* fe = m_curFunc;
8116 bool allowOverride = false;
8117 bindUserAttributes(meth, fe, allowOverride);
8119 // assign ids to parameters (all methods)
8120 int numParam = meth->getParams() ? meth->getParams()->getCount() : 0;
8121 for (int i = 0; i < numParam; i++) {
8122 auto par =
8123 static_pointer_cast<ParameterExpression>((*meth->getParams())[i]);
8124 fe->allocVarId(makeStaticString(par->getName()));
8127 // assign ids to 0Closure and use parameters (closures)
8128 if (fe->isClosureBody) {
8129 fe->allocVarId(makeStaticString("0Closure"));
8131 for (auto& useVar : *useVars) {
8132 fe->allocVarId(useVar.first);
8136 // assign id to 86metadata local representing frame metadata
8137 if (meth->mayCallSetFrameMetadata()) {
8138 fe->allocVarId(makeStaticString("86metadata"));
8141 // assign ids to local variables
8142 if (!fe->isMemoizeWrapper) {
8143 assignLocalVariableIds(meth->getFunctionScope());
8146 // add parameter info
8147 fillFuncEmitterParams(fe, meth->getParams(),
8148 meth->getFunctionScope()->isParamCoerceMode());
8150 // copy declared return type (hack)
8151 fe->retUserType = makeStaticString(meth->getReturnTypeConstraint());
8153 auto annot = meth->retTypeAnnotation();
8154 // For a non-generator async function with a return annotation of the form
8155 // "Awaitable<T>", we set m_retTypeConstraint to T. For all other async
8156 // functions, we leave m_retTypeConstraint empty.
8157 if (annot && fe->isAsync && !fe->isGenerator) {
8158 // Semantic checks ensure that the return annotation is "Awaitable" or
8159 // "WaitHandle" and that it has at most one type parameter
8160 assert(annot->isAwaitable() || annot->isWaitHandle());
8161 assert(annot->numTypeArgs() <= 1);
8162 bool isSoft = annot->isSoft();
8163 // If annot was "Awaitable" with no type args, getTypeArg() will return an
8164 // empty annotation
8165 annot = annot->getTypeArg(0);
8166 // If the original annotation was soft, make sure we preserve the softness
8167 if (annot && isSoft) annot->setSoft();
8169 // Ideally we should handle the void case in TypeConstraint::check. This
8170 // should however get done in a different diff, since it could impact
8171 // perf in a negative way (#3145038)
8172 if (annot && !annot->isVoid() && !annot->isThis()) {
8173 fe->retTypeConstraint = determine_type_constraint_from_annot(annot, true);
8176 // add the original filename for flattened traits
8177 auto const originalFilename = meth->getOriginalFilename();
8178 if (!originalFilename.empty()) {
8179 fe->originalFilename = makeStaticString(originalFilename);
8182 StringData* methDoc = Option::GenerateDocComments ?
8183 makeStaticString(meth->getDocComment()) : staticEmptyString();
8185 fe->init(meth->line0(),
8186 meth->line1(),
8187 m_ue.bcPos(),
8188 buildMethodAttrs(meth, fe, top, allowOverride),
8189 top,
8190 methDoc);
8192 if (meth->getFunctionScope()->needsFinallyLocals()) {
8193 assignFinallyVariableIds();
8197 void EmitterVisitor::fillFuncEmitterParams(FuncEmitter* fe,
8198 ExpressionListPtr params,
8199 bool coerce_params /*= false */) {
8200 int numParam = params ? params->getCount() : 0;
8201 for (int i = 0; i < numParam; i++) {
8202 auto par = static_pointer_cast<ParameterExpression>((*params)[i]);
8203 StringData* parName = makeStaticString(par->getName());
8205 FuncEmitter::ParamInfo pi;
8206 auto const typeConstraint = determine_type_constraint(par);
8207 if (typeConstraint.hasConstraint()) {
8208 pi.typeConstraint = typeConstraint;
8210 if (coerce_params) {
8211 if (auto const typeAnnotation = par->annotation()) {
8212 pi.builtinType = typeAnnotation->dataType();
8216 if (par->hasUserType()) {
8217 pi.userType = makeStaticString(par->getUserTypeHint());
8220 // Store info about the default value if there is one.
8221 if (par->isOptional()) {
8222 const StringData* phpCode;
8223 ExpressionPtr vNode = par->defaultValue();
8224 if (vNode->isScalar()) {
8225 TypedValue dv;
8226 initScalar(dv, vNode);
8227 pi.defaultValue = dv;
8229 std::string orig = vNode->getComment();
8230 if (orig.empty()) {
8231 // Simple case: it's a scalar value so we just serialize it
8232 VariableSerializer vs(VariableSerializer::Type::PHPOutput);
8233 String result = vs.serialize(tvAsCVarRef(&dv), true);
8234 phpCode = makeStaticString(result.get());
8235 } else {
8236 // This was optimized from a Constant, or ClassConstant
8237 // use the original string
8238 phpCode = makeStaticString(orig);
8240 } else {
8241 // Non-scalar, so we have to output PHP from the AST node
8242 std::ostringstream os;
8243 CodeGenerator cg(&os, CodeGenerator::PickledPHP);
8244 auto ar = std::make_shared<AnalysisResult>();
8245 vNode->outputPHP(cg, ar);
8246 phpCode = makeStaticString(os.str());
8248 pi.phpCode = phpCode;
8251 auto paramUserAttrs =
8252 dynamic_pointer_cast<ExpressionList>(par->userAttributeList());
8253 if (paramUserAttrs) {
8254 for (int j = 0; j < paramUserAttrs->getCount(); ++j) {
8255 auto a = dynamic_pointer_cast<UserAttribute>((*paramUserAttrs)[j]);
8256 StringData* uaName = makeStaticString(a->getName());
8257 ExpressionPtr uaValue = a->getExp();
8258 assert(uaValue);
8259 assert(uaValue->isScalar());
8260 TypedValue tv;
8261 initScalar(tv, uaValue);
8262 pi.userAttributes[uaName] = tv;
8266 pi.byRef = par->isRef();
8267 pi.variadic = par->isVariadic();
8268 fe->appendParam(parName, pi);
8272 void EmitterVisitor::emitMethodPrologue(Emitter& e, MethodStatementPtr meth) {
8273 FunctionScopePtr funcScope = meth->getFunctionScope();
8275 if (!m_curFunc->isMemoizeWrapper &&
8276 funcScope->needsLocalThis() && !funcScope->isStatic()) {
8277 assert(!m_curFunc->top);
8278 static const StringData* thisStr = makeStaticString("this");
8279 Id thisId = m_curFunc->lookupVarId(thisStr);
8280 emitVirtualLocal(thisId);
8281 e.InitThisLoc(thisId);
8284 if (!m_curFunc->isMemoizeImpl) {
8285 for (uint32_t i = 0; i < m_curFunc->params.size(); i++) {
8286 const TypeConstraint& tc = m_curFunc->params[i].typeConstraint;
8287 if (!tc.hasConstraint()) continue;
8288 emitVirtualLocal(i);
8289 e.VerifyParamType(i);
8293 if (funcScope->isAbstract()) {
8294 std::ostringstream s;
8295 s << "Cannot call abstract method " << meth->getOriginalFullName() << "()";
8296 emitMakeUnitFatal(e, s.str().c_str(), FatalOp::RuntimeOmitFrame);
8300 void EmitterVisitor::emitDeprecationWarning(Emitter& e,
8301 MethodStatementPtr meth) {
8302 auto funcScope = meth->getFunctionScope();
8304 auto userAttributes DEBUG_ONLY = funcScope->userAttributes();
8305 assert(userAttributes.find(attr_Deprecated) != userAttributes.end());
8307 // Include the message from <<__Deprecated('<message>')>> in the warning
8308 auto deprArgs = funcScope->getUserAttributeParams(attr_Deprecated);
8309 auto deprMessage = deprArgs.empty()
8310 ? s_is_deprecated.data()
8311 : deprArgs.front()->getString();
8313 // how often to display the warning (1 / rate)
8314 auto rate = deprArgs.size() > 1 ? deprArgs[1]->getLiteralInteger() : 1;
8315 if (rate <= 0) {
8316 // deprecation warnings disabled
8317 return;
8320 { // preface the message with the name of the offending function
8321 auto funcName = funcScope->getScopeName();
8322 BlockScopeRawPtr b = funcScope->getOuterScope();
8323 if (b && b->is(BlockScope::ClassScope)) {
8324 auto clsScope = dynamic_pointer_cast<ClassScope>(b);
8325 if (clsScope->isTrait()) {
8326 e.Self();
8327 e.NameA();
8328 e.String(makeStaticString("::" + funcName + ": " + deprMessage));
8329 e.Concat();
8330 } else {
8331 e.String(makeStaticString(
8332 clsScope->getScopeName() + "::" + funcName
8333 + ": " + deprMessage));
8335 } else {
8336 e.String(makeStaticString(funcName + ": " + deprMessage));
8340 e.Int(rate);
8341 e.Int((funcScope->isSystem() || funcScope->isNative())
8342 ? k_E_DEPRECATED : k_E_USER_DEPRECATED);
8343 e.FCallBuiltin(3, 3, s_trigger_sampled_error.get());
8344 emitPop(e);
8347 void EmitterVisitor::emitMethod(MethodStatementPtr meth) {
8348 auto region = createRegion(meth, Region::Kind::FuncBody);
8349 enterRegion(region);
8350 SCOPE_EXIT { leaveRegion(region); };
8352 Emitter e(meth, m_ue, *this);
8353 FuncFinisher ff(this, e, m_curFunc);
8354 Label topOfBody(e);
8355 emitMethodPrologue(e, meth);
8357 if (meth->getFunctionScope()->userAttributes().count(attr_Deprecated)) {
8358 emitDeprecationWarning(e, meth);
8361 // emit code to create generator object
8362 if (m_curFunc->isGenerator) {
8363 e.CreateCont();
8364 e.PopC();
8367 // emit method body
8368 visit(meth->getStmts());
8369 assert(m_evalStack.size() == 0);
8371 // if the current position is reachable, emit code to return null
8372 if (currentPositionIsReachable()) {
8373 auto r = meth->getRange();
8374 r.line0 = r.line1;
8375 r.char0 = r.char1 - 1;
8376 e.setTempLocation(r);
8377 e.Null();
8378 if (shouldEmitVerifyRetType()) {
8379 e.VerifyRetTypeC();
8381 e.RetC();
8382 e.setTempLocation(OptLocation());
8385 if (!m_curFunc->isMemoizeImpl) {
8386 emitMethodDVInitializers(e, meth, topOfBody);
8390 void EmitterVisitor::emitMethodDVInitializers(Emitter& e,
8391 MethodStatementPtr& meth,
8392 Label& topOfBody) {
8393 bool hasOptional = false;
8394 ExpressionListPtr params = meth->getParams();
8395 int numParam = params ? params->getCount() : 0;
8396 for (int i = 0; i < numParam; i++) {
8397 auto par = static_pointer_cast<ParameterExpression>((*params)[i]);
8398 if (par->isOptional()) {
8399 hasOptional = true;
8400 Label entryPoint(e);
8401 emitVirtualLocal(i);
8402 visit(par->defaultValue());
8403 emitCGet(e);
8404 emitSet(e);
8405 e.PopC();
8406 m_curFunc->params[i].funcletOff = entryPoint.getAbsoluteOffset();
8409 if (hasOptional) e.JmpNS(topOfBody);
8412 void EmitterVisitor::addMemoizeProp(MethodStatementPtr meth) {
8413 assert(m_curFunc->isMemoizeWrapper);
8415 if (meth->is(Statement::KindOfFunctionStatement)) {
8416 // Functions use statics within themselves. So all we need to do here is
8417 // set the name
8418 m_curFunc->memoizePropName = makeStaticString("static$memoize_cache");
8419 return;
8422 auto pce = m_curFunc->pce();
8423 auto classScope = meth->getClassScope();
8424 auto funcScope = meth->getFunctionScope();
8425 bool useSharedProp = !funcScope->isStatic();
8427 std::string propNameBase;
8428 if (useSharedProp) {
8429 propNameBase = "$shared";
8430 m_curFunc->hasMemoizeSharedProp = true;
8431 m_curFunc->memoizeSharedPropIndex = pce->getNextMemoizeCacheKey();
8432 } else {
8433 propNameBase = toLower(funcScope->getScopeName());
8436 // The prop definition in traits conflicts with the definition in a class
8437 // so make a different prop for each trait
8438 std::string traitNamePart;
8439 if (classScope && classScope->isTrait()) {
8440 traitNamePart = toLower(classScope->getScopeName());
8441 // the backslash comes from namespaces. @jan thought that would cause
8442 // issues, so use $ instead
8443 for (char &c: traitNamePart) {
8444 c = (c == '\\' ? '$' : c);
8446 traitNamePart += "$";
8449 m_curFunc->memoizePropName = makeStaticString(
8450 folly::sformat("{}${}memoize_cache", propNameBase, traitNamePart));
8452 TypedValue tvProp;
8453 if (useSharedProp ||
8454 (meth->getParams() && meth->getParams()->getCount() > 0)) {
8455 tvProp = make_tv<KindOfPersistentArray>(staticEmptyArray());
8456 } else {
8457 tvWriteNull(&tvProp);
8460 Attr attrs = AttrPrivate | AttrNoSerialize;
8461 attrs = attrs | (funcScope->isStatic() ? AttrStatic : AttrNone);
8462 pce->addProperty(m_curFunc->memoizePropName, attrs, nullptr, nullptr, &tvProp,
8463 RepoAuthType{});
8466 void EmitterVisitor::emitMemoizeProp(Emitter& e,
8467 MethodStatementPtr meth,
8468 Id localID,
8469 const std::vector<Id>& paramIDs,
8470 uint32_t numParams) {
8471 assert(m_curFunc->isMemoizeWrapper);
8473 if (meth->is(Statement::KindOfFunctionStatement)) {
8474 emitVirtualLocal(localID);
8475 } else if (meth->getFunctionScope()->isStatic()) {
8476 m_evalStack.push(StackSym::K);
8477 m_evalStack.setClsBaseType(SymbolicStack::CLS_SELF);
8478 e.String(m_curFunc->memoizePropName);
8479 markSProp(e);
8480 } else {
8481 m_evalStack.push(StackSym::H);
8482 m_evalStack.setKnownCls(m_curFunc->pce()->name(), false);
8483 m_evalStack.push(StackSym::T);
8484 m_evalStack.setString(m_curFunc->memoizePropName);
8485 markProp(e, PropAccessType::Normal);
8488 assert(numParams <= paramIDs.size());
8489 for (uint32_t i = 0; i < numParams; i++) {
8490 if (i == 0 && m_curFunc->hasMemoizeSharedProp) {
8491 e.Int(m_curFunc->memoizeSharedPropIndex);
8492 } else {
8493 emitVirtualLocal(paramIDs[i]);
8495 markElem(e);
8499 void EmitterVisitor::emitMemoizeMethod(MethodStatementPtr meth,
8500 const StringData* methName) {
8501 assert(m_curFunc->isMemoizeWrapper);
8503 if (meth->getFunctionScope()->isRefReturn()) {
8504 throw IncludeTimeFatalException(meth,
8505 "<<__Memoize>> cannot be used on functions that return by reference");
8507 if (meth->getFunctionScope()->allowsVariableArguments()) {
8508 throw IncludeTimeFatalException(meth,
8509 "<<__Memoize>> cannot be used on functions with variable arguments");
8512 auto classScope = meth->getClassScope();
8513 if (classScope && classScope->isInterface()) {
8514 throw IncludeTimeFatalException(meth,
8515 "<<__Memoize>> cannot be used in interfaces");
8518 bool isFunc = meth->is(Statement::KindOfFunctionStatement);
8519 int numParams = m_curFunc->params.size();
8520 std::vector<Id> cacheLookup;
8522 auto region = createRegion(meth, Region::Kind::FuncBody);
8523 enterRegion(region);
8524 SCOPE_EXIT { leaveRegion(region); };
8526 Emitter e(meth, m_ue, *this);
8527 FuncFinisher ff(this, e, m_curFunc);
8529 Label topOfBody(e);
8530 Label cacheMiss;
8532 emitMethodPrologue(e, meth);
8534 // Function start
8535 int staticLocalID = 0;
8536 if (isFunc) {
8537 // static ${propName} = {numParams > 0 ? array() : null};
8538 staticLocalID = m_curFunc->allocUnnamedLocal();
8539 emitVirtualLocal(staticLocalID);
8540 if (numParams == 0) {
8541 e.Null();
8542 } else {
8543 e.Array(staticEmptyArray());
8545 e.StaticLocInit(staticLocalID, m_curFunc->memoizePropName);
8546 } else if (!meth->getFunctionScope()->isStatic()) {
8547 e.CheckThis();
8550 if (m_curFunc->hasMemoizeSharedProp) {
8551 // The code below depends on cacheLookup having the right number of elements
8552 // Push a dummy value even though we'll use the cacheID as an int instead
8553 // instead of emitting a local
8554 cacheLookup.push_back(0);
8557 if (numParams == 0 && cacheLookup.size() == 0) {
8558 // if (${propName} !== null)
8559 emitMemoizeProp(e, meth, staticLocalID, cacheLookup, 0);
8560 emitIsType(e, IsTypeOp::Null);
8561 e.JmpNZ(cacheMiss);
8562 } else {
8563 // Serialize all the params into something we can use for the key
8564 for (int i = 0; i < numParams; i++) {
8565 if (m_curFunc->params[i].byRef) {
8566 throw IncludeTimeFatalException(meth,
8567 "<<__Memoize>> cannot be used on functions with args passed by "
8568 "reference");
8571 // Translate the arg to a memoize key
8572 int serResultLocal = m_curFunc->allocUnnamedLocal();
8573 cacheLookup.push_back(serResultLocal);
8575 emitVirtualLocal(serResultLocal);
8576 emitVirtualLocal(i);
8577 emitCGet(e);
8578 e.GetMemoKey();
8579 emitSet(e);
8580 emitPop(e);
8583 // isset returns false for null values. Given that:
8584 // - If we know that we can't return null, we can do a single isset check
8585 // - If we could return null, but there's only one arg, we can do a single
8586 // array_key_exists() check
8587 // - Otherwise we need an isset check to make sure we can dereference the
8588 // first N - 1 args, and then an array_key_exists() check
8589 int cacheLookupLen = cacheLookup.size();
8590 bool noRetNull =
8591 meth->getFunctionScope()->isAsync() ||
8592 (m_curFunc->retTypeConstraint.hasConstraint() &&
8593 !m_curFunc->retTypeConstraint.isSoft() &&
8594 !m_curFunc->retTypeConstraint.isNullable());
8596 if (cacheLookupLen > 1 || noRetNull) {
8597 // if (isset(${propName}[$param1]...[noRetNull ? $paramN : $paramN-1]))
8598 emitMemoizeProp(e, meth, staticLocalID, cacheLookup,
8599 noRetNull ? cacheLookupLen : cacheLookupLen - 1);
8600 emitIsset(e);
8601 e.JmpZ(cacheMiss);
8604 if (!noRetNull) {
8605 // if (array_key_exists($paramN, ${propName}[$param1][...][$paramN-1]))
8606 if (cacheLookupLen == 1 && m_curFunc->hasMemoizeSharedProp) {
8607 e.Int(m_curFunc->memoizeSharedPropIndex);
8608 } else {
8609 emitVirtualLocal(cacheLookup[cacheLookupLen - 1]);
8610 emitCGet(e);
8612 emitMemoizeProp(e, meth, staticLocalID, cacheLookup, cacheLookupLen - 1);
8613 emitCGet(e);
8614 e.AKExists();
8615 e.JmpZ(cacheMiss);
8619 // return $<propName>[$param1][...][$paramN]
8620 int cacheLookupLen = cacheLookup.size();
8621 emitMemoizeProp(e, meth, staticLocalID, cacheLookup, cacheLookupLen);
8622 emitCGet(e);
8623 e.RetC();
8625 // Otherwise, call the memoized func, store the result, and return it
8626 cacheMiss.set(e);
8627 emitMemoizeProp(e, meth, staticLocalID, cacheLookup, cacheLookupLen);
8628 auto fpiStart = m_ue.bcPos();
8629 if (isFunc) {
8630 e.FPushFuncD(numParams, methName);
8631 } else if (meth->getFunctionScope()->isStatic()) {
8632 emitClsIfSPropBase(e);
8634 if (classScope && classScope->isTrait()) {
8635 e.String(methName);
8636 e.Self();
8637 fpiStart = m_ue.bcPos();
8638 e.FPushClsMethodF(numParams);
8639 } else {
8640 fpiStart = m_ue.bcPos();
8641 e.FPushClsMethodD(numParams, methName, m_curFunc->pce()->name());
8643 } else {
8644 e.This();
8645 fpiStart = m_ue.bcPos();
8646 e.FPushObjMethodD(numParams, methName, ObjMethodOp::NullThrows);
8649 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
8650 for (uint32_t i = 0; i < numParams; i++) {
8651 emitVirtualLocal(i);
8652 emitFPass(e, i, PassByRefKind::ErrorOnCell);
8655 e.FCall(numParams);
8656 emitConvertToCell(e);
8658 emitSet(e);
8659 e.RetC();
8661 assert(m_evalStack.size() == 0);
8663 emitMethodDVInitializers(e, meth, topOfBody);
8666 void EmitterVisitor::emitPostponedCtors() {
8667 while (!m_postponedCtors.empty()) {
8668 PostponedCtor& p = m_postponedCtors.front();
8670 Attr attrs = AttrPublic;
8671 if (!SystemLib::s_inited || p.m_is->getClassScope()->isSystem()) {
8672 attrs = attrs | AttrBuiltin;
8674 StringData* methDoc = staticEmptyString();
8675 p.m_fe->init(p.m_is->line0(), p.m_is->line1(),
8676 m_ue.bcPos(), attrs, false, methDoc);
8677 Emitter e(p.m_is, m_ue, *this);
8678 FuncFinisher ff(this, e, p.m_fe);
8679 e.Null();
8680 e.RetC();
8682 m_postponedCtors.pop_front();
8686 void EmitterVisitor::emitPostponedPSinit(PostponedNonScalars& p, bool pinit) {
8687 Attr attrs = (Attr)(AttrPrivate | AttrStatic);
8688 if (!SystemLib::s_inited || p.m_is->getClassScope()->isSystem()) {
8689 attrs = attrs | AttrBuiltin;
8691 StringData* methDoc = staticEmptyString();
8692 p.m_fe->init(p.m_is->line0(), p.m_is->line1(),
8693 m_ue.bcPos(), attrs, false, methDoc);
8695 Emitter e(p.m_is, m_ue, *this);
8696 FuncFinisher ff(this, e, p.m_fe);
8698 // Private instance and static properties are initialized using
8699 // InitProp.
8700 size_t nProps = p.m_vec->size();
8701 assert(nProps > 0);
8702 for (size_t i = 0; i < nProps; ++i) {
8703 const StringData* propName =
8704 makeStaticString(((*p.m_vec)[i]).first);
8706 Label isset;
8707 InitPropOp op = InitPropOp::NonStatic;
8708 const PreClassEmitter::Prop& preProp =
8709 p.m_fe->pce()->lookupProp(propName);
8710 if ((preProp.attrs() & AttrStatic) == AttrStatic) {
8711 op = InitPropOp::Static;
8712 } else if ((preProp.attrs() & (AttrPrivate|AttrStatic)) != AttrPrivate) {
8713 e.CheckProp(const_cast<StringData*>(propName));
8714 e.JmpNZ(isset);
8716 visit((*p.m_vec)[i].second);
8717 e.InitProp(const_cast<StringData*>(propName), op);
8718 isset.set(e);
8720 e.Null();
8721 e.RetC();
8724 void EmitterVisitor::emitPostponedPinits() {
8725 while (!m_postponedPinits.empty()) {
8726 PostponedNonScalars& p = m_postponedPinits.front();
8727 emitPostponedPSinit(p, true);
8728 p.release(); // Manually trigger memory cleanup.
8729 m_postponedPinits.pop_front();
8733 void EmitterVisitor::emitPostponedSinits() {
8734 while (!m_postponedSinits.empty()) {
8735 PostponedNonScalars& p = m_postponedSinits.front();
8736 emitPostponedPSinit(p, false);
8737 p.release(); // Manually trigger memory cleanup.
8738 m_postponedSinits.pop_front();
8742 void EmitterVisitor::emitPostponedCinits() {
8743 while (!m_postponedCinits.empty()) {
8744 PostponedNonScalars& p = m_postponedCinits.front();
8746 Attr attrs = (Attr)(AttrPrivate | AttrStatic);
8747 if (!SystemLib::s_inited || p.m_is->getClassScope()->isSystem()) {
8748 attrs = attrs | AttrBuiltin;
8750 StringData* methDoc = staticEmptyString();
8751 p.m_fe->init(p.m_is->line0(), p.m_is->line1(),
8752 m_ue.bcPos(), attrs, false, methDoc);
8753 static const StringData* s_constName = makeStaticString("constName");
8754 p.m_fe->appendParam(s_constName, FuncEmitter::ParamInfo());
8756 Emitter e(p.m_is, m_ue, *this);
8757 FuncFinisher ff(this, e, p.m_fe);
8759 // Generate HHBC of the structure:
8761 // private static function 86cinit(constName) {
8762 // if (constName == "FOO") {
8763 // return <expr for FOO>;
8764 // } else if (constName == "BAR") {
8765 // return <expr for BAR>;
8766 // } else { # (constName == "BAZ")
8767 // return <expr for BAZ>;
8768 // }
8769 // }
8770 size_t nConsts = p.m_vec->size();
8771 assert(nConsts > 0);
8772 Label retC;
8773 for (size_t i = 0; i < nConsts - 1; ++i) {
8774 Label mismatch;
8776 emitVirtualLocal(0);
8777 emitCGet(e);
8778 e.String((StringData*)makeStaticString(((*p.m_vec)[i]).first));
8779 e.Eq();
8780 e.JmpZ(mismatch);
8782 visit((*p.m_vec)[i].second);
8784 e.Jmp(retC);
8785 mismatch.set(e);
8787 visit((*p.m_vec)[nConsts-1].second);
8788 retC.set(e);
8789 e.RetC();
8791 p.release(); // Manually trigger memory cleanup.
8792 m_postponedCinits.pop_front();
8796 void EmitterVisitor::emitVirtualLocal(int localId) {
8797 prepareEvalStack();
8799 m_evalStack.push(StackSym::L);
8800 m_evalStack.setInt(localId);
8803 template<class Expr>
8804 void EmitterVisitor::emitVirtualClassBase(Emitter& e, Expr* node) {
8805 prepareEvalStack();
8807 m_evalStack.push(StackSym::K);
8808 auto const func = node->getFunctionScope();
8810 if (node->isStatic()) {
8811 m_evalStack.setClsBaseType(SymbolicStack::CLS_LATE_BOUND);
8812 } else if (node->getClass()) {
8813 const ExpressionPtr& expr = node->getClass();
8814 if (isNormalLocalVariable(expr)) {
8815 SimpleVariable* sv = static_cast<SimpleVariable*>(expr.get());
8816 StringData* name = makeStaticString(sv->getName());
8817 Id locId = m_curFunc->lookupVarId(name);
8818 m_evalStack.setClsBaseType(SymbolicStack::CLS_NAMED_LOCAL);
8819 m_evalStack.setInt(locId);
8820 } else {
8822 * More complex expressions get stashed into an unnamed local so
8823 * we can evaluate them at the proper time.
8825 * See emitResolveClsBase() for examples.
8827 int unnamedLoc = m_curFunc->allocUnnamedLocal();
8828 int clsBaseIdx = m_evalStack.size() - 1;
8829 m_evalStack.setClsBaseType(SymbolicStack::CLS_UNNAMED_LOCAL);
8830 emitVirtualLocal(unnamedLoc);
8831 visit(node->getClass());
8832 emitConvertToCell(e);
8833 emitSet(e);
8834 m_evalStack.setUnnamedLocal(clsBaseIdx, unnamedLoc, m_ue.bcPos());
8835 emitPop(e);
8837 } else if (!node->getClassScope() ||
8838 node->getClassScope()->isTrait() ||
8839 (func && func->isClosure())) {
8840 // In a trait, a potentially rebound closure or psuedo-main, we can't
8841 // resolve self:: or parent:: yet, so we emit special instructions that do
8842 // those lookups.
8843 if (node->isParent()) {
8844 m_evalStack.setClsBaseType(SymbolicStack::CLS_PARENT);
8845 } else if (node->isSelf()) {
8846 m_evalStack.setClsBaseType(SymbolicStack::CLS_SELF);
8847 } else {
8848 m_evalStack.setClsBaseType(SymbolicStack::CLS_STRING_NAME);
8849 m_evalStack.setString(
8850 makeStaticString(node->getOriginalClassName()));
8852 } else if (node->isParent() &&
8853 node->getClassScope()->getOriginalParent().empty()) {
8854 // parent:: in a class without a parent. We'll emit a Parent
8855 // opcode because it can handle this error case.
8856 m_evalStack.setClsBaseType(SymbolicStack::CLS_PARENT);
8857 } else {
8858 m_evalStack.setClsBaseType(SymbolicStack::CLS_STRING_NAME);
8859 m_evalStack.setString(
8860 makeStaticString(node->getOriginalClassName()));
8864 bool EmitterVisitor::emitSystemLibVarEnvFunc(Emitter& e,
8865 SimpleFunctionCallPtr call) {
8866 if (call->isCallToFunction("extract")) {
8867 emitFuncCall(e, call,
8868 "__SystemLib\\extract", call->getParams());
8869 return true;
8870 } else if (call->isCallToFunction("parse_str")) {
8871 emitFuncCall(e, call, "__SystemLib\\parse_str", call->getParams());
8872 return true;
8873 } else if (call->isCallToFunction("compact")) {
8874 emitFuncCall(e, call,
8875 "__SystemLib\\compact_sl", call->getParams());
8876 return true;
8877 } else if (call->isCallToFunction("get_defined_vars")) {
8878 emitFuncCall(e, call,
8879 "__SystemLib\\get_defined_vars", call->getParams());
8880 return true;
8881 } else if (call->isCallToFunction("func_get_args")) {
8882 emitFuncCall(e, call,
8883 "__SystemLib\\func_get_args_sl", call->getParams());
8884 return true;
8885 } else if (call->isCallToFunction("func_get_arg")) {
8886 emitFuncCall(e, call,
8887 "__SystemLib\\func_get_arg_sl", call->getParams());
8888 return true;
8889 } else if (call->isCallToFunction("func_num_args")) {
8890 emitFuncCall(e, call,
8891 "__SystemLib\\func_num_arg_", call->getParams());
8892 return true;
8894 return false;
8897 bool EmitterVisitor::emitCallUserFunc(Emitter& e, SimpleFunctionCallPtr func) {
8898 static struct {
8899 const char* name;
8900 int minParams, maxParams;
8901 CallUserFuncFlags flags;
8902 } cufTab[] = {
8903 { "call_user_func", 1, INT_MAX, CallUserFuncPlain },
8904 { "call_user_func_array", 2, 2, CallUserFuncArray },
8905 { "forward_static_call", 1, INT_MAX, CallUserFuncForward },
8906 { "forward_static_call_array", 2, 2, CallUserFuncForwardArray },
8907 { "fb_call_user_func_safe", 1, INT_MAX, CallUserFuncSafe },
8908 { "fb_call_user_func_array_safe", 2, 2, CallUserFuncSafeArray },
8909 { "fb_call_user_func_safe_return", 2, INT_MAX, CallUserFuncSafeReturn },
8912 ExpressionListPtr params = func->getParams();
8913 if (!params) return false;
8914 int nParams = params->getCount();
8915 if (!nParams) return false;
8916 CallUserFuncFlags flags = CallUserFuncNone;
8917 for (unsigned i = 0; i < sizeof(cufTab) / sizeof(cufTab[0]); i++) {
8918 if (func->isCallToFunction(cufTab[i].name) &&
8919 nParams >= cufTab[i].minParams &&
8920 nParams <= cufTab[i].maxParams) {
8921 flags = cufTab[i].flags;
8922 break;
8925 if (flags == CallUserFuncNone) return false;
8926 if (func->hasUnpack()) {
8927 throw EmitterVisitor::IncludeTimeFatalException(
8928 func,
8929 "Using argument unpacking for a call_user_func is not supported"
8932 int param = 1;
8933 ExpressionPtr callable = (*params)[0];
8934 visit(callable);
8935 emitConvertToCell(e);
8936 Offset fpiStart = m_ue.bcPos();
8937 if (flags & CallUserFuncForward) {
8938 e.FPushCufF(nParams - param);
8939 } else if (flags & CallUserFuncSafe) {
8940 if (flags & CallUserFuncReturn) {
8941 assert(nParams >= 2);
8942 visit((*params)[param++]);
8943 emitConvertToCell(e);
8944 } else {
8945 e.Null();
8947 fpiStart = m_ue.bcPos();
8948 e.FPushCufSafe(nParams - param);
8949 } else {
8950 e.FPushCuf(nParams - param);
8954 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
8955 for (int i = param; i < nParams; i++) {
8956 visit((*params)[i]);
8957 emitConvertToCell(e);
8958 e.FPassC(i - param);
8962 if (flags & CallUserFuncArray) {
8963 e.FCallArray();
8964 } else {
8965 e.FCall(nParams - param);
8967 if (flags & CallUserFuncSafe) {
8968 if (flags & CallUserFuncReturn) {
8969 e.CufSafeReturn();
8970 } else {
8971 e.CufSafeArray();
8974 return true;
8977 Func* EmitterVisitor::canEmitBuiltinCall(const std::string& name,
8978 int numParams) {
8979 if (Option::JitEnableRenameFunction ||
8980 !RuntimeOption::EvalEnableCallBuiltin) {
8981 return nullptr;
8983 if (Option::DynamicInvokeFunctions.size()) {
8984 if (Option::DynamicInvokeFunctions.find(name) !=
8985 Option::DynamicInvokeFunctions.end()) {
8986 return nullptr;
8989 Func* f = Unit::lookupFunc(makeStaticString(name));
8990 if (!f ||
8991 (f->attrs() & AttrNoFCallBuiltin) ||
8992 !f->nativeFuncPtr() ||
8993 f->isMethod() ||
8994 (f->numParams() > Native::maxFCallBuiltinArgs()) ||
8995 (f->userAttributes().count(
8996 LowStringPtr(s_attr_Deprecated.get())))) return nullptr;
8998 auto variadic = f->hasVariadicCaptureParam();
9000 // Only allowed to overrun the signature if we have somewhere to put it
9001 if ((numParams > f->numParams()) && !variadic) return nullptr;
9003 if ((f->returnType() == KindOfDouble) &&
9004 !Native::allowFCallBuiltinDoubles()) return nullptr;
9006 if (!(f->attrs() & AttrNative)) {
9007 // HNI only enables Variable args via ActRec which in turn
9008 // is captured by the f->nativeFuncPtr() == nullptr,
9009 // so there's nothing additional to check in the HNI case
9010 return nullptr;
9013 bool allowDoubleArgs = Native::allowFCallBuiltinDoubles();
9014 auto concrete_params = f->numParams();
9015 if (variadic) {
9016 assertx(concrete_params > 0);
9017 --concrete_params;
9019 for (int i = 0; i < concrete_params; i++) {
9020 if ((!allowDoubleArgs) &&
9021 (f->params()[i].builtinType == KindOfDouble)) {
9022 return nullptr;
9024 if (i >= numParams) {
9025 auto &pi = f->params()[i];
9026 if (pi.isVariadic()) continue;
9027 if (!pi.hasDefaultValue()) {
9028 return nullptr;
9030 if (pi.defaultValue.m_type == KindOfUninit) {
9031 // TODO: Resolve persistent constants
9032 return nullptr;
9037 return f;
9040 void EmitterVisitor::emitFuncCall(Emitter& e, FunctionCallPtr node,
9041 const char* nameOverride,
9042 ExpressionListPtr paramsOverride) {
9043 ExpressionPtr nameExp = node->getNameExp();
9044 const std::string& nameStr = nameOverride ? nameOverride :
9045 node->getOriginalName();
9046 ExpressionListPtr params(paramsOverride ? paramsOverride :
9047 node->getParams());
9048 int numParams = params ? params->getCount() : 0;
9049 auto const unpack = node->hasUnpack();
9050 assert(!paramsOverride || !unpack);
9052 Func* fcallBuiltin = nullptr;
9053 StringData* nLiteral = nullptr;
9054 Offset fpiStart = 0;
9055 if (node->getClass() || node->hasStaticClass()) {
9056 bool isSelfOrParent = node->isSelf() || node->isParent();
9057 if (!node->isStatic() && !isSelfOrParent &&
9058 !node->getOriginalClassName().empty() && !nameStr.empty()) {
9059 // cls::foo()
9060 StringData* cLiteral =
9061 makeStaticString(node->getOriginalClassName());
9062 StringData* nLiteral = makeStaticString(nameStr);
9063 fpiStart = m_ue.bcPos();
9064 e.FPushClsMethodD(numParams, nLiteral, cLiteral);
9065 } else {
9066 emitVirtualClassBase(e, node.get());
9067 if (!nameStr.empty()) {
9068 // ...::foo()
9069 StringData* nLiteral = makeStaticString(nameStr);
9070 e.String(nLiteral);
9071 } else {
9072 // ...::$foo()
9073 visit(nameExp);
9074 emitConvertToCell(e);
9076 emitResolveClsBase(e, m_evalStack.size() - 2);
9077 fpiStart = m_ue.bcPos();
9078 if (isSelfOrParent) {
9079 // self and parent are "forwarding" calls, so we need to
9080 // use FPushClsMethodF instead
9081 e.FPushClsMethodF(numParams);
9082 } else {
9083 e.FPushClsMethod(numParams);
9086 } else if (!nameStr.empty()) {
9087 // foo()
9088 nLiteral = makeStaticString(nameStr);
9089 fcallBuiltin = canEmitBuiltinCall(nameStr, numParams);
9090 if (unpack &&
9091 fcallBuiltin &&
9092 (!fcallBuiltin->hasVariadicCaptureParam() ||
9093 numParams != fcallBuiltin->numParams())) {
9094 fcallBuiltin = nullptr;
9096 if (fcallBuiltin && (fcallBuiltin->attrs() & AttrAllowOverride)) {
9097 if (!Option::WholeProgram ||
9098 (node->getFuncScope() && node->getFuncScope()->isUserFunction())) {
9099 // In non-WholeProgram mode, we can't tell whether the function
9100 // will be overridden, so never use FCallBuiltin.
9101 // In WholeProgram mode, don't use FCallBuiltin if it *has* been
9102 // overridden.
9103 fcallBuiltin = nullptr;
9106 StringData* nsName = nullptr;
9107 if (!node->hadBackslash() && !nameOverride) {
9108 // nameOverride is to be used only when there's an exact function
9109 // to be called ... supporting a fallback doesn't make sense
9110 const std::string& nonNSName = node->getNonNSOriginalName();
9111 if (nonNSName != nameStr) {
9112 nsName = nLiteral;
9113 nLiteral = makeStaticString(nonNSName);
9114 fcallBuiltin = nullptr;
9118 if (!fcallBuiltin) {
9119 fpiStart = m_ue.bcPos();
9120 if (nsName == nullptr) {
9121 e.FPushFuncD(numParams, nLiteral);
9122 } else {
9123 assert(!nameOverride);
9124 e.FPushFuncU(numParams, nsName, nLiteral);
9127 } else {
9128 // $foo()
9129 visit(nameExp);
9130 emitConvertToCell(e);
9131 // FPushFunc consumes method name from stack
9132 fpiStart = m_ue.bcPos();
9133 e.FPushFunc(numParams);
9135 if (fcallBuiltin) {
9136 auto variadic = !unpack && fcallBuiltin->hasVariadicCaptureParam();
9137 assertx((numParams <= fcallBuiltin->numParams()) || variadic);
9139 auto concreteParams = fcallBuiltin->numParams();
9140 if (variadic) {
9141 assertx(concreteParams > 0);
9142 --concreteParams;
9145 int i = 0;
9146 for (; i < numParams; i++) {
9147 // for builtin calls, since we don't push the ActRec, we
9148 // must determine the reffiness statically
9149 bool byRef = fcallBuiltin->byRef(i);
9150 bool mustBeRef = fcallBuiltin->mustBeRef(i);
9151 if (!emitBuiltinCallArg(e, (*params)[i], i, byRef, mustBeRef)) {
9152 while (i--) emitPop(e);
9153 return;
9157 for (; i < concreteParams; i++) {
9158 auto &pi = fcallBuiltin->params()[i];
9159 assert(pi.hasDefaultValue());
9160 auto &def = pi.defaultValue;
9161 emitBuiltinDefaultArg(e, tvAsVariant(const_cast<TypedValue*>(&def)),
9162 pi.builtinType, i);
9165 if (variadic) {
9166 if (numParams <= concreteParams) {
9167 e.Array(staticEmptyArray());
9168 } else {
9169 e.NewPackedArray(numParams - concreteParams);
9172 e.FCallBuiltin(fcallBuiltin->numParams(),
9173 std::min<int32_t>(numParams, fcallBuiltin->numParams()),
9174 nLiteral);
9175 } else {
9176 emitCall(e, node, params, fpiStart);
9178 if (fcallBuiltin) {
9179 fixReturnType(e, node, fcallBuiltin);
9183 bool EmitterVisitor::emitConstantFuncCall(Emitter& e,
9184 SimpleFunctionCallPtr call) {
9185 if (!Option::WholeProgram || Option::ConstantFunctions.empty()) return false;
9187 if (call->getClass()) {
9188 // The class expression was either non-scalar or static, neither of which
9189 // we want to optimize.
9190 return false;
9193 auto const name = call->getFullName();
9194 auto const it = Option::ConstantFunctions.find(name);
9195 if (it == Option::ConstantFunctions.end()) return false;
9197 VariableUnserializer uns{
9198 it->second.data(), it->second.size(), VariableUnserializer::Type::Serialize,
9199 false, empty_array()
9202 try {
9203 return emitScalarValue(e, uns.unserialize());
9204 } catch (const Exception& e) {
9205 throw IncludeTimeFatalException(call,
9206 "Bad ConstantValue for %s: '%s'",
9207 name.c_str(), it->second.c_str());
9211 void EmitterVisitor::emitClassTraitPrecRule(PreClassEmitter* pce,
9212 TraitPrecStatementPtr stmt) {
9213 StringData* traitName = makeStaticString(stmt->getTraitName());
9214 StringData* methodName = makeStaticString(stmt->getMethodName());
9216 PreClass::TraitPrecRule rule(traitName, methodName);
9218 hphp_string_iset otherTraitNames;
9219 stmt->getOtherTraitNames(otherTraitNames);
9220 for (auto const& name : otherTraitNames) {
9221 rule.addOtherTraitName(makeStaticString(name));
9224 pce->addTraitPrecRule(rule);
9227 void EmitterVisitor::emitClassTraitAliasRule(PreClassEmitter* pce,
9228 TraitAliasStatementPtr stmt) {
9229 StringData* traitName = makeStaticString(stmt->getTraitName());
9230 StringData* origMethName = makeStaticString(stmt->getMethodName());
9231 StringData* newMethName = makeStaticString(stmt->getNewMethodName());
9232 // If there are no modifiers, buildAttrs() defaults to AttrPublic.
9233 // Here we don't want that. Instead, set AttrNone so that the modifiers of the
9234 // original method are preserved.
9235 Attr attr = (stmt->getModifiers()->getCount() == 0 ? AttrNone :
9236 buildAttrs(stmt->getModifiers()));
9238 PreClass::TraitAliasRule rule(traitName, origMethName, newMethName, attr);
9240 pce->addTraitAliasRule(rule);
9243 void EmitterVisitor::emitClassUseTrait(PreClassEmitter* pce,
9244 UseTraitStatementPtr useStmt) {
9245 auto rules = useStmt->getStmts();
9246 for (int r = 0; r < rules->getCount(); r++) {
9247 auto rule = (*rules)[r];
9248 auto precStmt = dynamic_pointer_cast<TraitPrecStatement>(rule);
9249 if (precStmt) {
9250 emitClassTraitPrecRule(pce, precStmt);
9251 } else {
9252 auto aliasStmt = dynamic_pointer_cast<TraitAliasStatement>(rule);
9253 assert(aliasStmt);
9254 emitClassTraitAliasRule(pce, aliasStmt);
9259 Id EmitterVisitor::emitTypedef(Emitter& e, TypedefStatementPtr td) {
9260 auto const nullable = td->annot->isNullable();
9261 auto const annot = td->annot->stripNullable();
9262 auto const valueStr = annot.vanillaName();
9264 // We have to merge the strings as litstrs to ensure namedentity
9265 // creation.
9266 auto const name = makeStaticString(td->name);
9267 auto const value = makeStaticString(valueStr);
9268 m_ue.mergeLitstr(name);
9269 m_ue.mergeLitstr(value);
9271 AnnotType type;
9272 if (annot.isFunction() || annot.isMixed()) {
9273 type = AnnotType::Mixed;
9274 } else {
9275 auto const at = nameToAnnotType(value);
9276 type = at ? *at : AnnotType::Object;
9277 // Type aliases are always defined at top-level scope, so
9278 // they're not allowed to reference "self" or "parent" (and
9279 // "static" is already disallowed by the parser, so we don't
9280 // need to worry about it here).
9281 if (UNLIKELY(type == AnnotType::Self || type == AnnotType::Parent)) {
9282 throw IncludeTimeFatalException(
9284 "Cannot access %s when no class scope is active",
9285 type == AnnotType::Self ? "self" : "parent");
9289 UserAttributeMap userAttrs;
9290 ExpressionListPtr attrList = td->attrList;
9291 if (attrList) {
9292 for (int i = 0; i < attrList->getCount(); ++i) {
9293 auto attr = dynamic_pointer_cast<UserAttribute>((*attrList)[i]);
9294 auto const uaName = makeStaticString(attr->getName());
9295 auto uaValue = attr->getExp();
9296 assert(uaValue);
9297 assert(uaValue->isScalar());
9298 TypedValue tv;
9299 initScalar(tv, uaValue);
9300 userAttrs[uaName] = tv;
9304 TypeAlias record;
9305 record.typeStructure = Array(td->annot->getScalarArrayRep());
9306 record.name = name;
9307 record.value = value;
9308 record.type = type;
9309 record.nullable = nullable;
9310 record.userAttrs = userAttrs;
9311 record.attrs = !SystemLib::s_inited ? AttrPersistent : AttrNone;
9312 Id id = m_ue.addTypeAlias(record);
9313 e.DefTypeAlias(id);
9315 return id;
9318 void EmitterVisitor::emitClass(Emitter& e,
9319 ClassScopePtr cNode,
9320 bool toplevel) {
9322 const StringData* fatal_msg = cNode->getFatalMessage();
9323 if (fatal_msg != nullptr) {
9324 e.String(fatal_msg);
9325 e.Fatal(FatalOp::Runtime);
9326 return;
9329 auto is = static_pointer_cast<InterfaceStatement>(cNode->getStmt());
9330 StringData* className = makeStaticString(cNode->getOriginalName());
9331 StringData* parentName = makeStaticString(cNode->getOriginalParent());
9332 StringData* classDoc = Option::GenerateDocComments ?
9333 makeStaticString(cNode->getDocComment()) : staticEmptyString();
9334 Attr attr = cNode->isInterface() ? AttrInterface :
9335 cNode->isTrait() ? AttrTrait :
9336 cNode->isAbstract() ? AttrAbstract :
9337 cNode->isEnum() ? (AttrEnum | AttrFinal) :
9338 AttrNone;
9339 if (cNode->isFinal()) {
9340 attr = attr | AttrFinal;
9342 if (Option::WholeProgram) {
9343 if (!cNode->isRedeclaring() &&
9344 cNode->derivesFromRedeclaring() == Derivation::Normal) {
9345 attr = attr | AttrUnique;
9346 if (!cNode->isVolatile()) {
9347 attr = attr | AttrPersistent;
9350 if (cNode->isSystem()) {
9351 assert(attr & AttrPersistent);
9352 attr = attr | AttrBuiltin;
9354 if (!cNode->getAttribute(ClassScope::NotFinal)) {
9355 attr = attr | AttrNoOverride;
9357 if (cNode->getUsedTraitNames().size()) {
9358 attr = attr | AttrNoExpandTrait;
9360 } else if (!SystemLib::s_inited) {
9361 // we're building systemlib. everything is unique
9362 attr = attr | AttrBuiltin | AttrUnique | AttrPersistent;
9365 const std::vector<std::string>& bases(cNode->getBases());
9366 int firstInterface = cNode->getOriginalParent().empty() ? 0 : 1;
9367 int nInterfaces = bases.size();
9368 PreClass::Hoistable hoistable = PreClass::NotHoistable;
9369 if (toplevel) {
9370 if (SystemLib::s_inited && !cNode->isSystem()) {
9371 if (nInterfaces > firstInterface
9372 || cNode->getUsedTraitNames().size()
9373 || cNode->getClassRequiredExtends().size()
9374 || cNode->getClassRequiredImplements().size()
9375 || cNode->isEnum()
9377 hoistable = PreClass::Mergeable;
9378 } else if (firstInterface &&
9379 !m_hoistables.count(cNode->getOriginalParent())) {
9380 hoistable = PreClass::MaybeHoistable;
9383 if (hoistable == PreClass::NotHoistable) {
9384 hoistable = attr & AttrUnique ?
9385 PreClass::AlwaysHoistable : PreClass::MaybeHoistable;
9386 m_hoistables.insert(cNode->getOriginalName());
9389 PreClassEmitter* pce = m_ue.newPreClassEmitter(className, hoistable);
9390 pce->init(is->line0(), is->line1(), m_ue.bcPos(), attr, parentName,
9391 classDoc);
9392 auto r = is->getRange();
9393 r.line1 = r.line0;
9394 r.char1 = r.char0;
9395 e.setTempLocation(r);
9396 if (hoistable != PreClass::AlwaysHoistable) {
9397 e.DefCls(pce->id());
9398 } else {
9399 // To attach the line number to for error reporting.
9400 e.DefClsNop(pce->id());
9402 e.setTempLocation(OptLocation());
9403 for (int i = firstInterface; i < nInterfaces; ++i) {
9404 pce->addInterface(makeStaticString(bases[i]));
9407 const std::vector<std::string>& usedTraits = cNode->getUsedTraitNames();
9408 for (size_t i = 0; i < usedTraits.size(); i++) {
9409 pce->addUsedTrait(makeStaticString(usedTraits[i]));
9411 pce->setNumDeclMethods(cNode->getNumDeclMethods());
9412 if (cNode->isTrait() || cNode->isInterface() || Option::WholeProgram) {
9413 for (auto& reqExtends : cNode->getClassRequiredExtends()) {
9414 pce->addClassRequirement(
9415 PreClass::ClassRequirement(makeStaticString(reqExtends), true));
9417 for (auto& reqImplements : cNode->getClassRequiredImplements()) {
9418 pce->addClassRequirement(
9419 PreClass::ClassRequirement(makeStaticString(reqImplements), false));
9422 auto const& userAttrs = cNode->userAttributes();
9423 for (auto it = userAttrs.begin(); it != userAttrs.end(); ++it) {
9424 const StringData* uaName = makeStaticString(it->first);
9425 ExpressionPtr uaValue = it->second;
9426 assert(uaValue);
9427 assert(uaValue->isScalar());
9428 TypedValue tv;
9429 initScalar(tv, uaValue);
9430 pce->addUserAttribute(uaName, tv);
9433 NonScalarVec* nonScalarPinitVec = nullptr;
9434 NonScalarVec* nonScalarSinitVec = nullptr;
9435 NonScalarVec* nonScalarConstVec = nullptr;
9436 if (StatementListPtr stmts = is->getStmts()) {
9437 int i, n = stmts->getCount();
9438 for (i = 0; i < n; i++) {
9439 if (auto meth = dynamic_pointer_cast<MethodStatement>((*stmts)[i])) {
9440 StringData* methName = makeStaticString(meth->getOriginalName());
9441 FuncEmitter* fe = m_ue.newMethodEmitter(methName, pce);
9442 bool added UNUSED = pce->addMethod(fe);
9443 assert(added);
9444 postponeMeth(meth, fe, false);
9445 } else if (auto cv = dynamic_pointer_cast<ClassVariable>((*stmts)[i])) {
9446 ModifierExpressionPtr mod(cv->getModifiers());
9447 ExpressionListPtr el(cv->getVarList());
9448 Attr declAttrs = buildAttrs(mod);
9449 StringData* typeConstraint = makeStaticString(
9450 cv->getTypeConstraint());
9451 int nVars = el->getCount();
9452 for (int ii = 0; ii < nVars; ii++) {
9453 ExpressionPtr exp((*el)[ii]);
9454 ExpressionPtr vNode;
9455 SimpleVariablePtr var;
9456 if (exp->is(Expression::KindOfAssignmentExpression)) {
9457 auto ae = static_pointer_cast<AssignmentExpression>(exp);
9458 var = static_pointer_cast<SimpleVariable>(ae->getVariable());
9459 vNode = ae->getValue();
9460 } else {
9461 var = static_pointer_cast<SimpleVariable>(exp);
9464 auto const propName = makeStaticString(var->getName());
9465 auto const propDoc = Option::GenerateDocComments ?
9466 makeStaticString(var->getDocComment()) : staticEmptyString();
9467 TypedValue tvVal;
9468 // Some properties may need to be marked with the AttrDeepInit
9469 // attribute, while other properties should not be marked with
9470 // this attrbiute. We copy declAttrs into propAttrs for each loop
9471 // iteration so that we can safely add AttrDeepInit to propAttrs
9472 // without mutating the original declAttrs.
9473 Attr propAttrs = declAttrs;
9474 if (vNode) {
9475 if (vNode->isScalar()) {
9476 initScalar(tvVal, vNode);
9477 } else {
9478 tvWriteUninit(&tvVal);
9479 if (!(declAttrs & AttrStatic)) {
9480 if (requiresDeepInit(vNode)) {
9481 propAttrs = propAttrs | AttrDeepInit;
9483 if (nonScalarPinitVec == nullptr) {
9484 nonScalarPinitVec = new NonScalarVec();
9486 nonScalarPinitVec->push_back(NonScalarPair(propName, vNode));
9487 } else {
9488 if (nonScalarSinitVec == nullptr) {
9489 nonScalarSinitVec = new NonScalarVec();
9491 nonScalarSinitVec->push_back(NonScalarPair(propName, vNode));
9494 } else {
9495 tvWriteNull(&tvVal);
9497 bool added UNUSED =
9498 pce->addProperty(propName, propAttrs, typeConstraint,
9499 propDoc, &tvVal, RepoAuthType{});
9500 assert(added);
9502 } else if (auto cc = dynamic_pointer_cast<ClassConstant>((*stmts)[i])) {
9504 ExpressionListPtr el(cc->getConList());
9505 StringData* typeConstraint =
9506 makeStaticString(cc->getTypeConstraint());
9507 int nCons = el->getCount();
9509 if (cc->isAbstract()) {
9510 for (int ii = 0; ii < nCons; ii++) {
9511 auto con = static_pointer_cast<ConstantExpression>((*el)[ii]);
9512 StringData* constName = makeStaticString(con->getName());
9513 bool added UNUSED =
9514 pce->addAbstractConstant(constName, typeConstraint,
9515 cc->isTypeconst());
9516 assert(added);
9518 } else {
9519 for (int ii = 0; ii < nCons; ii++) {
9520 auto ae = static_pointer_cast<AssignmentExpression>((*el)[ii]);
9521 auto con =
9522 static_pointer_cast<ConstantExpression>(ae->getVariable());
9523 auto vNode = ae->getValue();
9524 StringData* constName = makeStaticString(con->getName());
9525 assert(vNode);
9526 TypedValue tvVal;
9527 if (vNode->isArray()) {
9528 throw IncludeTimeFatalException(
9529 cc, "Arrays are not allowed in class constants");
9530 } else if (vNode->isCollection()) {
9531 throw IncludeTimeFatalException(
9532 cc, "Collections are not allowed in class constants");
9533 } else if (vNode->isScalar()) {
9534 initScalar(tvVal, vNode);
9535 } else {
9536 tvWriteUninit(&tvVal);
9537 if (nonScalarConstVec == nullptr) {
9538 nonScalarConstVec = new NonScalarVec();
9540 nonScalarConstVec->push_back(NonScalarPair(constName, vNode));
9542 // Store PHP source code for constant initializer.
9543 std::ostringstream os;
9544 CodeGenerator cg(&os, CodeGenerator::PickledPHP);
9545 auto ar = std::make_shared<AnalysisResult>();
9546 vNode->outputPHP(cg, ar);
9547 bool added UNUSED = pce->addConstant(
9548 constName, typeConstraint, &tvVal,
9549 makeStaticString(os.str()),
9550 cc->isTypeconst(),
9551 cc->getTypeStructure());
9552 assert(added);
9555 } else if (auto useStmt =
9556 dynamic_pointer_cast<UseTraitStatement>((*stmts)[i])) {
9557 emitClassUseTrait(pce, useStmt);
9562 if (!cNode->getAttribute(ClassScope::HasConstructor) &&
9563 !cNode->getAttribute(ClassScope::ClassNameConstructor)) {
9564 // cNode does not have a constructor; synthesize 86ctor() so that the class
9565 // will always have a method that can be called during construction.
9566 static const StringData* methName = makeStaticString("86ctor");
9567 FuncEmitter* fe = m_ue.newMethodEmitter(methName, pce);
9568 bool added UNUSED = pce->addMethod(fe);
9569 assert(added);
9570 postponeCtor(is, fe);
9573 if (nonScalarPinitVec != nullptr) {
9574 // Non-scalar property initializers require 86pinit() for run-time
9575 // initialization support.
9576 static const StringData* methName = makeStaticString("86pinit");
9577 FuncEmitter* fe = m_ue.newMethodEmitter(methName, pce);
9578 pce->addMethod(fe);
9579 postponePinit(is, fe, nonScalarPinitVec);
9582 if (nonScalarSinitVec != nullptr) {
9583 // Non-scalar property initializers require 86sinit() for run-time
9584 // initialization support.
9585 static const StringData* methName = makeStaticString("86sinit");
9586 FuncEmitter* fe = m_ue.newMethodEmitter(methName, pce);
9587 pce->addMethod(fe);
9588 postponeSinit(is, fe, nonScalarSinitVec);
9591 if (nonScalarConstVec != nullptr) {
9592 // Non-scalar constant initializers require 86cinit() for run-time
9593 // initialization support.
9594 static const StringData* methName = makeStaticString("86cinit");
9595 FuncEmitter* fe = m_ue.newMethodEmitter(methName, pce);
9596 assert(!(attr & AttrTrait));
9597 bool added UNUSED = pce->addMethod(fe);
9598 assert(added);
9599 postponeCinit(is, fe, nonScalarConstVec);
9602 // If this is an enum, get its type constraint.
9603 if (cNode->isEnum()) {
9604 auto cs = static_pointer_cast<ClassStatement>(is);
9605 auto const typeConstraint =
9606 determine_type_constraint_from_annot(cs->getEnumBaseTy(), true);
9607 pce->setEnumBaseTy(typeConstraint);
9611 namespace {
9613 struct ForeachIterGuard {
9614 ForeachIterGuard(EmitterVisitor& ev,
9615 Id iterId,
9616 IterKind kind)
9617 : m_ev(ev)
9619 m_ev.pushIterScope(iterId, kind);
9621 ~ForeachIterGuard() {
9622 m_ev.popIterScope();
9625 private:
9626 EmitterVisitor& m_ev;
9631 void EmitterVisitor::emitForeachListAssignment(Emitter& e,
9632 ListAssignmentPtr la,
9633 std::function<void()> emitSrc) {
9634 std::vector<IndexPair> indexPairs;
9635 IndexChain workingChain;
9636 listAssignmentVisitLHS(e, la, workingChain, indexPairs);
9638 if (indexPairs.size() == 0) {
9639 throw IncludeTimeFatalException(la, "Cannot use empty list");
9642 listAssignmentAssignElements(e, indexPairs, emitSrc);
9645 void EmitterVisitor::emitForeach(Emitter& e,
9646 ForEachStatementPtr fe) {
9647 auto region = createRegion(fe, Region::Kind::LoopOrSwitch);
9648 ExpressionPtr ae(fe->getArrayExp());
9649 ExpressionPtr val(fe->getValueExp());
9650 ExpressionPtr key(fe->getNameExp());
9651 StatementPtr body(fe->getBody());
9652 int keyTempLocal;
9653 int valTempLocal;
9654 bool strong = fe->isStrong();
9655 Label& exit = registerBreak(fe, region.get(), 1, false)->m_label;
9656 Label& next = registerContinue(fe, region.get(), 1, false)->m_label;
9657 Label start;
9658 Offset bIterStart;
9659 Id itId = m_curFunc->allocIterator();
9660 ForeachIterGuard fig(*this, itId, strong ? KindOfMIter : KindOfIter);
9661 bool simpleCase = (!key || isNormalLocalVariable(key)) &&
9662 isNormalLocalVariable(val);
9663 bool listKey = key ? key->is(Expression::KindOfListAssignment) : false;
9664 bool listVal = val->is(Expression::KindOfListAssignment);
9666 if (simpleCase) {
9667 auto svVal = static_pointer_cast<SimpleVariable>(val);
9668 StringData* name = makeStaticString(svVal->getName());
9669 valTempLocal = m_curFunc->lookupVarId(name);
9670 if (key) {
9671 auto svKey = static_pointer_cast<SimpleVariable>(key);
9672 name = makeStaticString(svKey->getName());
9673 keyTempLocal = m_curFunc->lookupVarId(name);
9674 visit(key);
9675 // Meta info on the key local will confuse the translator (and
9676 // wouldn't be useful anyway)
9677 m_evalStack.cleanTopMeta();
9678 } else {
9679 // Make gcc happy
9680 keyTempLocal = -1;
9682 visit(val);
9683 // Meta info on the value local will confuse the translator (and
9684 // wouldn't be useful anyway)
9685 m_evalStack.cleanTopMeta();
9686 visit(ae);
9687 if (strong) {
9688 emitConvertToVar(e);
9689 if (key) {
9690 e.MIterInitK(itId, exit, valTempLocal, keyTempLocal);
9691 } else {
9692 e.MIterInit(itId, exit, valTempLocal);
9694 } else {
9695 emitConvertToCell(e);
9696 if (key) {
9697 e.IterInitK(itId, exit, valTempLocal, keyTempLocal);
9698 } else {
9699 e.IterInit(itId, exit, valTempLocal);
9703 start.set(e);
9704 bIterStart = m_ue.bcPos();
9705 } else {
9706 keyTempLocal = key ? m_curFunc->allocUnnamedLocal() : -1;
9707 valTempLocal = m_curFunc->allocUnnamedLocal();
9708 if (key) {
9709 emitVirtualLocal(keyTempLocal);
9711 emitVirtualLocal(valTempLocal);
9713 visit(ae);
9714 if (strong) {
9715 emitConvertToVar(e);
9716 } else {
9717 emitConvertToCell(e);
9720 if (strong) {
9721 if (key) {
9722 e.MIterInitK(itId, exit, valTempLocal, keyTempLocal);
9723 } else {
9724 e.MIterInit(itId, exit, valTempLocal);
9726 } else {
9727 if (key) {
9728 e.IterInitK(itId, exit, valTempLocal, keyTempLocal);
9729 } else {
9730 e.IterInit(itId, exit, valTempLocal);
9734 // At this point, valTempLocal and keyTempLocal if applicable, contain the
9735 // key and value for the iterator.
9736 start.set(e);
9737 bIterStart = m_ue.bcPos();
9738 if (key && !listKey) {
9739 visit(key);
9740 emitClsIfSPropBase(e);
9742 if (listVal) {
9743 emitForeachListAssignment(
9745 ListAssignmentPtr(static_pointer_cast<ListAssignment>(val)),
9746 [&] { emitVirtualLocal(valTempLocal); }
9748 } else {
9749 visit(val);
9750 emitClsIfSPropBase(e);
9751 emitVirtualLocal(valTempLocal);
9752 if (strong) {
9753 emitVGet(e);
9754 emitBind(e);
9755 } else {
9756 emitCGet(e);
9757 emitSet(e);
9759 emitPop(e);
9761 emitVirtualLocal(valTempLocal);
9762 emitUnset(e);
9763 newFaultRegionAndFunclet(bIterStart, m_ue.bcPos(),
9764 new UnsetUnnamedLocalThunklet(valTempLocal));
9765 if (key) {
9766 assert(keyTempLocal != -1);
9767 if (listKey) {
9768 emitForeachListAssignment(
9770 ListAssignmentPtr(static_pointer_cast<ListAssignment>(key)),
9771 [&] { emitVirtualLocal(keyTempLocal); }
9773 } else {
9774 emitVirtualLocal(keyTempLocal);
9775 emitCGet(e);
9776 emitSet(e);
9777 emitPop(e);
9779 emitVirtualLocal(keyTempLocal);
9780 emitUnset(e);
9781 newFaultRegionAndFunclet(bIterStart, m_ue.bcPos(),
9782 new UnsetUnnamedLocalThunklet(keyTempLocal));
9787 region->m_iterId = itId;
9788 region->m_iterKind = strong ? KindOfMIter : KindOfIter;
9789 enterRegion(region);
9790 SCOPE_EXIT { leaveRegion(region); };
9791 if (body) visit(body);
9793 if (next.isUsed()) {
9794 next.set(e);
9796 if (key) {
9797 emitVirtualLocal(keyTempLocal);
9798 // Meta info on the key local will confuse the translator (and
9799 // wouldn't be useful anyway)
9800 m_evalStack.cleanTopMeta();
9802 emitVirtualLocal(valTempLocal);
9803 // Meta info on the value local will confuse the translator (and
9804 // wouldn't be useful anyway)
9805 m_evalStack.cleanTopMeta();
9806 if (strong) {
9807 if (key) {
9808 e.MIterNextK(itId, start, valTempLocal, keyTempLocal);
9809 } else {
9810 e.MIterNext(itId, start, valTempLocal);
9812 } else {
9813 if (key) {
9814 e.IterNextK(itId, start, valTempLocal, keyTempLocal);
9815 } else {
9816 e.IterNext(itId, start, valTempLocal);
9819 newFaultRegionAndFunclet(bIterStart, m_ue.bcPos(),
9820 new IterFreeThunklet(itId, strong),
9821 { itId, strong ? KindOfMIter : KindOfIter });
9822 if (!simpleCase) {
9823 m_curFunc->freeUnnamedLocal(valTempLocal);
9824 if (key) {
9825 m_curFunc->freeUnnamedLocal(keyTempLocal);
9828 exit.set(e);
9829 m_curFunc->freeIterator(itId);
9832 void EmitterVisitor::emitForeachAwaitAs(Emitter& e,
9833 ForEachStatementPtr fe) {
9834 assert(!fe->isStrong());
9835 auto region = createRegion(fe, Region::Kind::LoopOrSwitch);
9836 Label& exit = registerBreak(fe, region.get(), 1, false)->m_label;
9837 Label& next = registerContinue(fe, region.get(), 1, false)->m_label;
9839 // Evaluate the AsyncIterator object and store it into unnamed local.
9840 auto const iterTempLocal = m_curFunc->allocUnnamedLocal();
9841 emitVirtualLocal(iterTempLocal);
9842 visit(fe->getArrayExp());
9843 emitConvertToCell(e);
9844 emitSet(e);
9845 auto const iterTempStartUse = m_ue.bcPos();
9847 // Make sure it actually is an AsyncIterator.
9848 e.InstanceOfD(makeStaticString("HH\\AsyncIterator"));
9849 e.JmpNZ(next);
9850 e.String(makeStaticString(
9851 "Unable to iterate non-AsyncIterator asynchronously"));
9852 e.Fatal(FatalOp::Runtime);
9854 // Start of the next iteration.
9855 next.set(e);
9857 // Await the next value.
9858 emitVirtualLocal(iterTempLocal);
9859 emitCGet(e);
9860 emitConstMethodCallNoParams(e, "next");
9861 e.Await();
9862 auto const resultTempLocal = emitSetUnnamedL(e);
9864 // Did we finish yet?
9865 emitVirtualLocal(resultTempLocal);
9866 emitIsType(e, IsTypeOp::Null);
9867 e.JmpNZ(exit);
9869 auto const populate = [&](ExpressionPtr target, int index) {
9870 auto const emitSrc = [&] {
9871 emitVirtualLocal(resultTempLocal);
9872 m_evalStack.push(StackSym::I);
9873 m_evalStack.setInt(index);
9874 markElem(e);
9877 if (target->is(Expression::KindOfListAssignment)) {
9878 emitForeachListAssignment(
9880 ListAssignmentPtr(static_pointer_cast<ListAssignment>(target)),
9881 emitSrc
9883 } else {
9884 // Obtain target to be set.
9885 visit(target);
9887 // Put $result[index] on the stack.
9888 emitSrc();
9889 emitCGet(e);
9891 // Set target.
9892 emitSet(e);
9893 emitPop(e);
9897 auto const resultTempStartUse = m_ue.bcPos();
9899 // Set the key.
9900 if (fe->getNameExp()) {
9901 populate(fe->getNameExp(), 0);
9904 // Set the value.
9905 populate(fe->getValueExp(), 1);
9907 newFaultRegionAndFunclet(resultTempStartUse, m_ue.bcPos(),
9908 new UnsetUnnamedLocalThunklet(resultTempLocal));
9909 emitVirtualLocal(resultTempLocal);
9910 emitUnset(e);
9912 // Run body.
9914 enterRegion(region);
9915 SCOPE_EXIT { leaveRegion(region); };
9916 if (fe->getBody()) visit(fe->getBody());
9919 // Continue iteration.
9920 e.Jmp(next);
9922 // Exit cleanup.
9923 exit.set(e);
9925 emitVirtualLocal(resultTempLocal);
9926 emitUnset(e);
9927 m_curFunc->freeUnnamedLocal(resultTempLocal);
9929 newFaultRegionAndFunclet(iterTempStartUse, m_ue.bcPos(),
9930 new UnsetUnnamedLocalThunklet(iterTempLocal));
9931 emitVirtualLocal(iterTempLocal);
9932 emitUnset(e);
9933 m_curFunc->freeUnnamedLocal(iterTempLocal);
9936 void EmitterVisitor::emitYieldFrom(Emitter& e, ExpressionPtr exp) {
9937 Id itId = m_curFunc->allocIterator();
9939 // Set the delegate to the result of visiting our expression
9940 visit(exp);
9941 emitConvertToCell(e);
9942 e.ContAssignDelegate(itId);
9944 Offset bDelegateAssigned = m_ue.bcPos();
9946 // Pass null to ContEnterDelegate initially.
9947 e.Null();
9949 Label loopBeginning(e);
9950 e.ContEnterDelegate();
9951 e.YieldFromDelegate(itId, loopBeginning);
9952 newFaultRegionAndFunclet(bDelegateAssigned, m_ue.bcPos(),
9953 new UnsetGeneratorDelegateThunklet(itId));
9955 // Now that we're done with it, remove the delegate. This lets us enforce
9956 // the invariant that if we have a delegate set, we should be using it.
9957 e.ContUnsetDelegate(false, itId);
9961 * Emits bytecode that restores the previous error reporting level after
9962 * evaluating a silenced (@) expression, or in the fault funclet protecting such
9963 * an expression. Requires a local variable id containing the previous error
9964 * reporting level. The whole silenced expression looks like this:
9965 * oldvalue = error_reporting(0)
9966 * ...evaluate silenced expression...
9967 * oldvalue = error_reporting(oldvalue)
9968 * if oldvalue != 0:
9969 * error_reporting(oldvalue)
9971 void EmitterVisitor::emitRestoreErrorReporting(Emitter& e, Id oldLevelLoc) {
9972 emitVirtualLocal(oldLevelLoc);
9973 auto idx = m_evalStack.size() - 1;
9974 e.Silence(m_evalStack.getLoc(idx), SilenceOp::End);
9977 void EmitterVisitor::emitMakeUnitFatal(Emitter& e,
9978 const char* msg,
9979 FatalOp k) {
9980 const StringData* sd = makeStaticString(msg);
9981 e.String(sd);
9982 e.Fatal(k);
9985 Funclet* EmitterVisitor::addFunclet(StatementPtr stmt, Thunklet* body) {
9986 Funclet* f = addFunclet(body);
9987 m_memoizedFunclets.insert(std::make_pair(stmt, f));
9988 return f;
9991 Funclet* EmitterVisitor::addFunclet(Thunklet* body) {
9992 m_funclets.push_back(new Funclet(body));
9993 return m_funclets.back();
9996 Funclet* EmitterVisitor::getFunclet(StatementPtr stmt) {
9997 if (m_memoizedFunclets.count(stmt)) {
9998 return m_memoizedFunclets[stmt];
9999 } else {
10000 return nullptr;
10004 void EmitterVisitor::emitFunclets(Emitter& e) {
10005 // TODO (#3271358): New fault funclets might appear while emitting
10006 // finally fault funclets. This is because we currently don't memoize
10007 // fault funclets other than finally fault fuclets. See task
10008 // description for more details.
10009 for (int i = 0; i < m_funclets.size(); ++i) {
10010 Funclet* f = m_funclets[i];
10011 f->m_entry.set(e);
10012 f->m_body->emit(e);
10013 delete f->m_body;
10014 f->m_body = nullptr;
10018 void EmitterVisitor::newFaultRegion(Offset start,
10019 Offset end,
10020 Label* entry,
10021 FaultIterInfo iter) {
10022 auto r = new FaultRegion(start, end, entry, iter.iterId, iter.kind);
10023 m_faultRegions.push_back(r);
10026 void EmitterVisitor::newFaultRegionAndFunclet(Offset start,
10027 Offset end,
10028 Thunklet* t,
10029 FaultIterInfo iter) {
10030 Funclet* f = addFunclet(t);
10031 newFaultRegion(start, end, &f->m_entry, iter);
10034 void EmitterVisitor::newFaultRegionAndFunclet(StatementPtr stmt,
10035 Offset start,
10036 Offset end,
10037 Thunklet* t,
10038 FaultIterInfo iter) {
10039 Funclet* f = addFunclet(stmt, t);
10040 newFaultRegion(start, end, &f->m_entry, iter);
10043 void EmitterVisitor::newFPIRegion(Offset start, Offset end, Offset fpOff) {
10044 FPIRegion* r = new FPIRegion(start, end, fpOff);
10045 m_fpiRegions.push_back(r);
10048 void EmitterVisitor::copyOverCatchAndFaultRegions(FuncEmitter* fe) {
10049 for (auto& eh : m_catchRegions) {
10050 auto& e = fe->addEHEnt();
10051 e.m_type = EHEnt::Type::Catch;
10052 e.m_base = eh->m_start;
10053 e.m_past = eh->m_end;
10054 assert(e.m_base != kInvalidOffset);
10055 assert(e.m_past != kInvalidOffset);
10056 e.m_iterId = -1;
10057 for (auto& c : eh->m_catchLabels) {
10058 Id id = m_ue.mergeLitstr(c.first);
10059 Offset off = c.second->getAbsoluteOffset();
10060 e.m_catches.push_back(std::pair<Id, Offset>(id, off));
10062 delete eh;
10064 m_catchRegions.clear();
10065 for (auto& fr : m_faultRegions) {
10066 auto& e = fe->addEHEnt();
10067 e.m_type = EHEnt::Type::Fault;
10068 e.m_base = fr->m_start;
10069 e.m_past = fr->m_end;
10070 assert(e.m_base != kInvalidOffset);
10071 assert(e.m_past != kInvalidOffset);
10072 e.m_iterId = fr->m_iterId;
10073 e.m_itRef = fr->m_iterKind == KindOfMIter;
10074 e.m_fault = fr->m_func->getAbsoluteOffset();
10075 assert(e.m_fault != kInvalidOffset);
10076 delete fr;
10078 m_faultRegions.clear();
10079 for (auto f : m_funclets) {
10080 delete f;
10082 m_funclets.clear();
10083 m_memoizedFunclets.clear();
10086 void EmitterVisitor::copyOverFPIRegions(FuncEmitter* fe) {
10087 for (std::deque<FPIRegion*>::iterator it = m_fpiRegions.begin();
10088 it != m_fpiRegions.end(); ++it) {
10089 FPIEnt& e = fe->addFPIEnt();
10090 e.m_fpushOff = (*it)->m_start;
10091 e.m_fcallOff = (*it)->m_end;
10092 e.m_fpOff = (*it)->m_fpOff;
10093 delete *it;
10095 m_fpiRegions.clear();
10098 void EmitterVisitor::saveMaxStackCells(FuncEmitter* fe, int32_t stackPad) {
10099 fe->maxStackCells +=
10100 fe->numIterators() * kNumIterCells +
10101 fe->numLocals() +
10102 stackPad;
10104 m_evalStack.m_actualStackHighWaterPtr = nullptr;
10107 // Are you sure you mean to be calling this directly? Would FuncFinisher
10108 // be more appropriate?
10109 void EmitterVisitor::finishFunc(Emitter& e, FuncEmitter* fe, int32_t stackPad) {
10110 emitFunclets(e);
10111 saveMaxStackCells(fe, stackPad);
10112 copyOverCatchAndFaultRegions(fe);
10113 copyOverFPIRegions(fe);
10114 m_staticEmitted.clear();
10115 Offset past = e.getUnitEmitter().bcPos();
10116 fe->finish(past, false);
10117 e.getUnitEmitter().recordFunction(fe);
10118 if (m_stateLocal >= 0) {
10119 m_stateLocal = -1;
10121 if (m_retLocal >= 0) {
10122 m_retLocal = -1;
10126 void EmitterVisitor::initScalar(TypedValue& tvVal, ExpressionPtr val,
10127 folly::Optional<HeaderKind> kind) {
10128 assert(val->isScalar());
10129 tvVal.m_type = KindOfUninit;
10130 // static array initilization
10131 auto initArray = [&](ExpressionPtr el, folly::Optional<HeaderKind> k) {
10132 if (k == HeaderKind::Dict) {
10133 m_staticArrays.push_back(Array::attach(MixedArray::MakeReserveDict(0)));
10134 } else if (k == HeaderKind::VecArray) {
10135 m_staticArrays.push_back(Array::attach(PackedArray::MakeReserveVec(0)));
10136 } else if (k == HeaderKind::Keyset) {
10137 m_staticArrays.push_back(Array::attach(MixedArray::MakeReserveKeyset(0)));
10138 } else {
10139 m_staticArrays.push_back(Array::attach(PackedArray::MakeReserve(0)));
10141 m_staticColType.push_back(k);
10142 visit(el);
10143 tvVal = make_tv<KindOfPersistentArray>(
10144 ArrayData::GetScalarArray(m_staticArrays.back().get())
10146 m_staticArrays.pop_back();
10147 m_staticColType.pop_back();
10149 switch (val->getKindOf()) {
10150 case Expression::KindOfConstantExpression: {
10151 auto ce = static_pointer_cast<ConstantExpression>(val);
10152 if (ce->isNull()) {
10153 tvVal.m_data.num = 0;
10154 tvVal.m_type = KindOfNull;
10155 } else if (ce->isBoolean()) {
10156 tvVal = make_tv<KindOfBoolean>(ce->getBooleanValue());
10157 } else if (ce->isScalar()) {
10158 ce->getScalarValue(tvAsVariant(&tvVal));
10159 } else {
10160 not_implemented();
10162 break;
10164 case Expression::KindOfScalarExpression: {
10165 auto sval = static_pointer_cast<ScalarExpression>(val);
10166 const std::string* s;
10167 if (sval->getString(s)) {
10168 StringData* sd = makeStaticString(*s);
10169 tvVal = make_tv<KindOfString>(sd);
10170 break;
10172 int64_t i;
10173 if (sval->getInt(i)) {
10174 tvVal = make_tv<KindOfInt64>(i);
10175 break;
10177 double d;
10178 if (sval->getDouble(d)) {
10179 tvVal = make_tv<KindOfDouble>(d);
10180 break;
10182 assert(false);
10183 break;
10185 case Expression::KindOfExpressionList: {
10186 // Array, possibly for collection initialization.
10187 initArray(val, kind);
10188 break;
10190 case Expression::KindOfUnaryOpExpression: {
10191 auto u = static_pointer_cast<UnaryOpExpression>(val);
10192 if (u->getOp() == T_ARRAY) {
10193 initArray(u->getExpression(), folly::none);
10194 break;
10196 if (u->getOp() == T_DICT) {
10197 initArray(u->getExpression(), HeaderKind::Dict);
10198 break;
10200 if (u->getOp() == T_VEC) {
10201 initArray(u->getExpression(), HeaderKind::VecArray);
10202 break;
10204 if (u->getOp() == T_KEYSET) {
10205 initArray(u->getExpression(), HeaderKind::Keyset);
10206 break;
10208 // Fall through
10210 default: {
10211 if (val->getScalarValue(tvAsVariant(&tvVal))) {
10212 if (tvAsVariant(&tvVal).isArray()) {
10213 not_implemented();
10215 break;
10217 not_reached();
10222 void EmitterVisitor::emitArrayInit(Emitter& e, ExpressionListPtr el,
10223 folly::Optional<HeaderKind> kind) {
10224 assert(m_staticArrays.empty());
10225 auto const isDict = kind == HeaderKind::Dict;
10226 auto const isVec = kind == HeaderKind::VecArray;
10227 auto const isKeyset = kind == HeaderKind::Keyset;
10229 if (el == nullptr) {
10230 if (isDict) {
10231 e.Array(staticEmptyDictArray());
10232 return;
10234 if (isVec) {
10235 e.Array(staticEmptyVecArray());
10236 return;
10238 if (isKeyset) {
10239 e.Array(staticEmptyKeysetArray());
10240 return;
10242 e.Array(staticEmptyArray());
10243 return;
10246 auto const scalar =
10247 isDict ? isDictScalar(el) :
10248 isKeyset ? isKeysetScalar(el) :
10249 el->isScalar();
10250 if (scalar) {
10251 TypedValue tv;
10252 tvWriteUninit(&tv);
10253 initScalar(tv, el, kind);
10254 e.Array(tv.m_data.parr);
10255 return;
10258 if (isVec || isKeyset) {
10259 auto const count = el->getCount();
10260 for (int i = 0; i < count; i++) {
10261 auto expr = static_pointer_cast<Expression>((*el)[i]);
10262 visit(expr);
10263 emitConvertToCell(e);
10265 if (isVec) {
10266 e.NewVecArray(count);
10267 } else {
10268 e.NewKeysetArray(count);
10270 return;
10273 auto const allowPacked = !kind || isVectorCollection((CollectionType)*kind);
10275 int nElms;
10276 if (allowPacked && isPackedInit(el, &nElms)) {
10277 for (int i = 0; i < nElms; ++i) {
10278 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10279 visit(ap->getValue());
10280 emitConvertToCell(e);
10282 e.NewPackedArray(nElms);
10283 return;
10286 auto const allowStruct = !kind;
10287 std::vector<std::string> keys;
10288 if (allowStruct && isStructInit(el, keys)) {
10289 for (int i = 0, n = keys.size(); i < n; i++) {
10290 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10291 visit(ap->getValue());
10292 emitConvertToCell(e);
10294 e.NewStructArray(keys);
10295 return;
10298 auto capacityHint = MixedArray::SmallSize;
10299 auto const capacity = el->getCount();
10300 if (capacity > 0) capacityHint = capacity;
10301 if (allowPacked && isPackedInit(el, &nElms, false /* ignore size */)) {
10302 e.NewArray(capacityHint);
10303 } else if (isDict) {
10304 e.NewDictArray(capacityHint);
10305 } else {
10306 e.NewMixedArray(capacityHint);
10308 visit(el);
10311 void EmitterVisitor::emitPairInit(Emitter& e, ExpressionListPtr el) {
10312 if (el->getCount() != 2) {
10313 throw IncludeTimeFatalException(el,
10314 "Pair objects must have exactly 2 elements");
10316 e.NewCol(static_cast<int>(CollectionType::Pair));
10317 for (int i = 0; i < 2; i++) {
10318 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10319 if (ap->getName() != nullptr) {
10320 throw IncludeTimeFatalException(ap,
10321 "Keys may not be specified for Pair initialization");
10323 visit(ap->getValue());
10324 emitConvertToCell(e);
10325 e.ColAddNewElemC();
10329 void EmitterVisitor::emitVectorInit(Emitter&e, CollectionType ct,
10330 ExpressionListPtr el) {
10331 // Do not allow specification of keys even if the resulting array is
10332 // packed. It doesn't make sense to specify keys for Vectors.
10333 for (int i = 0; i < el->getCount(); i++) {
10334 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10335 if (ap->getName() != nullptr) {
10336 throw IncludeTimeFatalException(ap,
10337 "Keys may not be specified for Vector initialization");
10340 emitArrayInit(e, el, (HeaderKind)ct);
10341 e.ColFromArray(static_cast<int>(ct));
10342 return;
10345 void EmitterVisitor::emitSetInit(Emitter&e, CollectionType ct,
10346 ExpressionListPtr el) {
10348 * Use an array to initialize the Set only if all the following conditional
10349 * are met:
10350 * 1. non-empty initializer;
10351 * 2. no integer-like string values (keys are the same as values for Set);
10352 * 3. !arr->isVectorData() to guarantee that we have a MixedArray.
10354 * Effectively, we use array for Set initialization only when it is a static
10355 * array for now.
10357 auto const nElms = el->getCount();
10358 auto useArray = !!nElms;
10359 auto hasVectorData = true;
10360 for (int i = 0; i < nElms; i++) {
10361 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10362 auto key = ap->getName();
10363 if ((bool)key) {
10364 throw IncludeTimeFatalException(ap,
10365 "Keys may not be specified for Set initialization");
10367 if (!useArray) continue;
10368 auto val = ap->getValue();
10369 Variant v;
10370 if (val->getScalarValue(v)) {
10371 if (v.isString()) {
10372 hasVectorData = false;
10373 int64_t intVal;
10374 if (v.getStringData()->isStrictlyInteger(intVal)) {
10375 useArray = false;
10377 } else if (v.isInteger()) {
10378 if (v.asInt64Val() != i) hasVectorData = false;
10379 } else {
10380 useArray = false;
10382 } else {
10383 useArray = false;
10386 if (hasVectorData) useArray = false;
10388 if (useArray) {
10389 emitArrayInit(e, el, (HeaderKind)ct);
10390 e.ColFromArray(static_cast<int>(ct));
10391 } else {
10392 if (nElms == 0) {
10393 // Will use the static empty mixed array to avoid allocation.
10394 e.NewCol(static_cast<int>(ct));
10395 return;
10397 e.NewMixedArray(nElms);
10398 e.ColFromArray(static_cast<int>(ct));
10399 for (int i = 0; i < nElms; i++) {
10400 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10401 visit(ap->getValue());
10402 emitConvertToCell(e);
10403 e.ColAddNewElemC();
10408 void EmitterVisitor::emitMapInit(Emitter&e, CollectionType ct,
10409 ExpressionListPtr el) {
10411 * Use an array to initialize the Map only when all the following conditional
10412 * are met:
10413 * 1. non-empty initializer;
10414 * 2. no integer-like string keys;
10415 * 3. !arr->isVectorData() to guarantee that we have a MixedArray.
10417 auto nElms = el->getCount();
10418 auto useArray = !!nElms;
10419 auto hasVectorData = true;
10420 int64_t max = 0;
10421 for (int i = 0; i < nElms; i++) {
10422 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10423 auto key = ap->getName();
10424 if (key == nullptr) {
10425 throw IncludeTimeFatalException(ap,
10426 "Keys must be specified for Map initialization");
10428 if (!useArray) continue;
10429 Variant vkey;
10430 if (key->getScalarValue(vkey)) {
10431 if (vkey.isString()) {
10432 hasVectorData = false;
10433 int64_t intKey;
10434 if (vkey.getStringData()->isStrictlyInteger(intKey)) {
10435 useArray = false;
10437 } else if (vkey.isInteger()) {
10438 auto val = vkey.asInt64Val();
10439 if (val > max || val < 0) {
10440 hasVectorData = false;
10441 } else if (val == max) {
10442 ++max;
10444 } else {
10445 useArray = false;
10447 } else {
10448 useArray = false;
10451 if (hasVectorData) useArray = false;
10453 if (useArray) {
10454 emitArrayInit(e, el, (HeaderKind)ct);
10455 e.ColFromArray(static_cast<int>(ct));
10456 } else {
10457 if (nElms == 0) {
10458 e.NewCol(static_cast<int>(ct));
10459 return;
10461 e.NewMixedArray(nElms);
10462 e.ColFromArray(static_cast<int>(ct));
10463 for (int i = 0; i < nElms; i++) {
10464 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10465 visit(ap->getName());
10466 emitConvertToCell(e);
10467 visit(ap->getValue());
10468 emitConvertToCell(e);
10469 e.MapAddElemC();
10474 void EmitterVisitor::emitCollectionInit(Emitter& e, BinaryOpExpressionPtr b) {
10475 auto cls = static_pointer_cast<ScalarExpression>(b->getExp1());
10476 const std::string* clsName = nullptr;
10477 cls->getString(clsName);
10478 auto ct = collections::stringToType(*clsName);
10479 if (!ct) {
10480 throw IncludeTimeFatalException(b,
10481 "Cannot use collection initialization for non-collection class");
10484 ExpressionListPtr el = nullptr;
10485 if (b->getExp2()) {
10486 el = static_pointer_cast<ExpressionList>(b->getExp2());
10487 } else {
10488 if (ct == CollectionType::Pair) {
10489 throw IncludeTimeFatalException(b, "Initializer needed for Pair object");
10491 e.NewCol(static_cast<int>(*ct));
10492 return;
10495 if (ct == CollectionType::Pair) {
10496 return emitPairInit(e, el);
10499 if (ct == CollectionType::Vector || ct == CollectionType::ImmVector) {
10500 return emitVectorInit(e, *ct, el);
10503 if (ct == CollectionType::Map || ct == CollectionType::ImmMap) {
10504 return emitMapInit(e, *ct, el);
10507 if (ct == CollectionType::Set || ct == CollectionType::ImmSet) {
10508 return emitSetInit(e, *ct, el);
10511 not_reached();
10514 bool EmitterVisitor::requiresDeepInit(ExpressionPtr initExpr) const {
10515 switch (initExpr->getKindOf()) {
10516 case Expression::KindOfScalarExpression:
10517 return false;
10518 case Expression::KindOfClassConstantExpression:
10519 case Expression::KindOfConstantExpression:
10520 return !initExpr->isScalar();
10521 case Expression::KindOfUnaryOpExpression: {
10522 auto u = static_pointer_cast<UnaryOpExpression>(initExpr);
10523 if (u->getOp() == T_ARRAY || u->getOp() == T_DICT) {
10524 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
10525 if (el) {
10526 int n = el->getCount();
10527 for (int i = 0; i < n; i++) {
10528 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10529 ExpressionPtr key = ap->getName();
10530 if (requiresDeepInit(ap->getValue()) ||
10531 (key && requiresDeepInit(key))) {
10532 return true;
10536 return false;
10537 } else if (u->getOp() == T_VEC || u->getOp() == T_KEYSET) {
10538 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
10539 if (el) {
10540 int n = el->getCount();
10541 for (int i = 0; i < n; i++) {
10542 auto expr = static_pointer_cast<Expression>((*el)[i]);
10543 if (requiresDeepInit(expr)) return true;
10546 return false;
10547 } else if (u->getOp() == '+' || u->getOp() == '-') {
10548 return requiresDeepInit(u->getExpression());
10549 } else if (u->getOp() == T_FILE || u->getOp() == T_DIR) {
10550 return false;
10552 return true;
10554 case Expression::KindOfBinaryOpExpression: {
10555 auto b = static_pointer_cast<BinaryOpExpression>(initExpr);
10556 return requiresDeepInit(b->getExp1()) || requiresDeepInit(b->getExp2());
10558 default:
10559 return true;
10563 Thunklet::~Thunklet() {}
10565 static ConstructPtr doOptimize(ConstructPtr c, AnalysisResultConstPtr ar) {
10566 for (int i = 0, n = c->getKidCount(); i < n; i++) {
10567 if (ConstructPtr k = c->getNthKid(i)) {
10568 if (ConstructPtr rep = doOptimize(k, ar)) {
10569 c->setNthKid(i, rep);
10573 if (auto e = dynamic_pointer_cast<Expression>(c)) {
10574 switch (e->getKindOf()) {
10575 case Expression::KindOfBinaryOpExpression:
10576 case Expression::KindOfUnaryOpExpression:
10577 case Expression::KindOfIncludeExpression:
10578 case Expression::KindOfSimpleFunctionCall:
10579 return e->preOptimize(ar);
10580 case Expression::KindOfClosureExpression: {
10581 auto cl = static_pointer_cast<ClosureExpression>(e);
10582 auto UNUSED exp = doOptimize(cl->getClosureFunction(), ar);
10583 assert(!exp);
10584 break;
10586 default: break;
10589 return ConstructPtr();
10592 static UnitEmitter* emitHHBCUnitEmitter(AnalysisResultPtr ar, FileScopePtr fsp,
10593 const MD5& md5) {
10594 if (fsp->getPseudoMain() && !Option::WholeProgram) {
10595 ar->setPhase(AnalysisResult::FirstPreOptimize);
10596 doOptimize(fsp->getPseudoMain()->getStmt(), ar);
10599 if (RuntimeOption::EvalDumpAst) {
10600 if (fsp->getPseudoMain()) {
10601 fsp->getPseudoMain()->getStmt()->dump(0, ar);
10605 auto msp =
10606 dynamic_pointer_cast<MethodStatement>(fsp->getPseudoMain()->getStmt());
10607 UnitEmitter* ue = new UnitEmitter(md5);
10608 ue->m_preloadPriority = fsp->preloadPriority();
10609 ue->initMain(msp->line0(), msp->line1());
10610 EmitterVisitor ev(*ue);
10611 try {
10612 ev.visit(fsp);
10613 } catch (EmitterVisitor::IncludeTimeFatalException& ex) {
10614 // Replace the unit with an empty one, but preserve its file path.
10615 UnitEmitter* nue = new UnitEmitter(md5);
10616 nue->initMain(msp->line0(), msp->line1());
10617 nue->m_filepath = ue->m_filepath;
10618 delete ue;
10619 ue = nue;
10621 EmitterVisitor fev(*ue);
10622 Emitter emitter(ex.m_node, *ue, fev);
10623 FuncFinisher ff(&fev, emitter, ue->getMain());
10624 auto kind = ex.m_parseFatal ? FatalOp::Parse : FatalOp::Runtime;
10625 fev.emitMakeUnitFatal(emitter, ex.getMessage().c_str(), kind);
10627 return ue;
10630 enum GeneratorMethod {
10631 METH_NEXT,
10632 METH_SEND,
10633 METH_RAISE,
10634 METH_VALID,
10635 METH_CURRENT,
10636 METH_KEY,
10637 METH_REWIND,
10638 METH_GETRETURN,
10641 typedef hphp_hash_map<const StringData*, GeneratorMethod,
10642 string_data_hash, string_data_same> ContMethMap;
10643 typedef std::map<StaticString, GeneratorMethod> ContMethMapT;
10645 namespace {
10646 StaticString s_next("next");
10647 StaticString s_send("send");
10648 StaticString s_raise("raise");
10649 StaticString s_valid("valid");
10650 StaticString s_current("current");
10651 StaticString s_key("key");
10652 StaticString s_throw("throw");
10653 StaticString s_rewind("rewind");
10654 StaticString s_getReturn("getReturn");
10656 StaticString genCls("Generator");
10657 StaticString asyncGenCls("HH\\AsyncGenerator");
10659 ContMethMapT s_asyncGenMethods = {
10660 {s_next, GeneratorMethod::METH_NEXT},
10661 {s_send, GeneratorMethod::METH_SEND},
10662 {s_raise, GeneratorMethod::METH_RAISE}
10664 ContMethMapT s_genMethods = {
10665 {s_next, GeneratorMethod::METH_NEXT},
10666 {s_send, GeneratorMethod::METH_SEND},
10667 {s_raise, GeneratorMethod::METH_RAISE},
10668 {s_valid, GeneratorMethod::METH_VALID},
10669 {s_current, GeneratorMethod::METH_CURRENT},
10670 {s_key, GeneratorMethod::METH_KEY},
10671 {s_throw, GeneratorMethod::METH_RAISE},
10672 {s_rewind, GeneratorMethod::METH_REWIND},
10673 {s_getReturn, GeneratorMethod::METH_GETRETURN}
10677 static int32_t emitGeneratorMethod(UnitEmitter& ue,
10678 FuncEmitter* fe,
10679 GeneratorMethod m,
10680 bool isAsync) {
10681 Attr attrs = (Attr)(AttrBuiltin | AttrPublic);
10682 fe->init(0, 0, ue.bcPos(), attrs, false, staticEmptyString());
10684 if (!isAsync && RuntimeOption::AutoprimeGenerators) {
10685 // Create a dummy Emitter, so it's possible to emit jump instructions
10686 EmitterVisitor ev(ue);
10687 Emitter e(ConstructPtr(), ue, ev);
10688 Location::Range loc;
10689 if (ue.bcPos() > 0) loc.line0 = -1;
10690 e.setTempLocation(loc);
10692 // Check if the generator has started yet
10693 Label started;
10694 e.ContStarted();
10695 e.JmpNZ(started);
10697 // If it hasn't started, perform one "next" operation before
10698 // the actual operation (auto-priming)
10699 e.ContCheck(false);
10700 e.Null();
10701 e.ContEnter();
10702 e.PopC();
10703 started.set(e);
10706 if (!RuntimeOption::AutoprimeGenerators && m == METH_REWIND) {
10707 // In non-autopriming mode the rewind function will always call next, when
10708 // autopriming is enabled, rewind matches PHP behavior and will only advance
10709 // the generator when it has not yet been started.
10710 m = METH_NEXT;
10713 switch (m) {
10714 case METH_SEND:
10715 case METH_RAISE:
10716 case METH_NEXT: {
10717 // We always want these methods to be cloned with new funcids in
10718 // subclasses so we can burn Class*s and Func*s into the
10719 // translations
10720 fe->attrs |= AttrClone;
10722 // check generator status; send()/raise() also checks started
10723 ue.emitOp(OpContCheck);
10724 ue.emitIVA(m == METH_SEND || m == METH_RAISE);
10726 switch (m) {
10727 case METH_NEXT:
10728 ue.emitOp(OpNull);
10729 ue.emitOp(OpContEnter);
10730 break;
10731 case METH_SEND:
10732 ue.emitOp(OpPushL); ue.emitIVA(0);
10733 ue.emitOp(OpContEnter);
10734 break;
10735 case METH_RAISE:
10736 ue.emitOp(OpPushL); ue.emitIVA(0);
10737 ue.emitOp(OpContRaise);
10738 break;
10739 default:
10740 not_reached();
10743 // Backtrace has off-by-one bug when determining whether we are
10744 // in returning opcode; add Nop to avoid it
10745 ue.emitOp(OpNop);
10746 ue.emitOp(OpRetC);
10747 break;
10749 case METH_VALID: {
10750 ue.emitOp(OpContValid);
10751 ue.emitOp(OpRetC);
10752 break;
10754 case METH_CURRENT: {
10755 ue.emitOp(OpContCurrent);
10756 ue.emitOp(OpRetC);
10757 break;
10759 case METH_KEY: {
10760 ue.emitOp(OpContKey);
10761 ue.emitOp(OpRetC);
10762 break;
10764 case METH_REWIND: {
10765 ue.emitOp(OpNull);
10766 ue.emitOp(OpRetC);
10767 break;
10769 case METH_GETRETURN: {
10770 ue.emitOp(OpContGetReturn);
10771 ue.emitOp(OpRetC);
10772 break;
10774 default:
10775 not_reached();
10778 return 1; // Above cases push at most one stack cell.
10781 // Emit byte codes to implement methods. Return the maximum stack cell count.
10782 int32_t EmitterVisitor::emitNativeOpCodeImpl(MethodStatementPtr meth,
10783 const char* funcName,
10784 const char* className,
10785 FuncEmitter* fe) {
10786 GeneratorMethod* cmeth;
10787 StaticString s_func(funcName);
10788 StaticString s_class(className);
10790 if (genCls.same(s_class) &&
10791 (cmeth = folly::get_ptr(s_genMethods, s_func))) {
10792 return emitGeneratorMethod(m_ue, fe, *cmeth, false);
10793 } else if (asyncGenCls.same(s_class) &&
10794 (cmeth = folly::get_ptr(s_asyncGenMethods, s_func))) {
10795 return emitGeneratorMethod(m_ue, fe, *cmeth, true);
10798 throw IncludeTimeFatalException(meth,
10799 "OpCodeImpl attribute is not applicable to %s", funcName);
10802 static UnitEmitter* emitHHBCVisitor(AnalysisResultPtr ar, FileScopeRawPtr fsp) {
10803 auto md5 = fsp->getMd5();
10805 if (!Option::WholeProgram) {
10806 // The passed-in ar is only useful in whole-program mode, so create a
10807 // distinct ar to be used only for emission of this unit, and perform
10808 // unit-level (non-global) optimization.
10809 ar = std::make_shared<AnalysisResult>();
10810 fsp->setOuterScope(ar);
10812 ar->setPhase(AnalysisResult::AnalyzeAll);
10813 fsp->analyzeProgram(ar);
10816 auto ue = emitHHBCUnitEmitter(ar, fsp, md5);
10817 assert(ue != nullptr);
10819 if (Option::GenerateTextHHBC) {
10820 // TODO(#2973538): Move HHBC text generation to after all the
10821 // units are created, and get rid of the LitstrTable locking,
10822 // since it won't be needed in that case.
10823 LitstrTable::get().mutex().lock();
10824 LitstrTable::get().setReading();
10825 std::unique_ptr<Unit> unit(ue->create());
10826 std::string fullPath = AnalysisResult::prepareFile(
10827 ar->getOutputPath().c_str(), Option::UserFilePrefix + fsp->getName(),
10828 true, false) + ".hhbc.txt";
10830 std::ofstream f(fullPath.c_str());
10831 if (!f) {
10832 Logger::Error("Unable to open %s for write", fullPath.c_str());
10833 } else {
10834 CodeGenerator cg(&f, CodeGenerator::TextHHBC);
10835 cg.printf("Hash: %" PRIx64 "%016" PRIx64 "\n", md5.q[0], md5.q[1]);
10836 cg.printRaw(unit->toString().c_str());
10837 f.close();
10839 LitstrTable::get().setWriting();
10840 LitstrTable::get().mutex().unlock();
10843 return ue;
10846 struct UEQ : Synchronizable {
10847 void push(UnitEmitter* ue) {
10848 assert(ue != nullptr);
10849 Lock lock(this);
10850 m_ues.push_back(ue);
10851 notify();
10853 UnitEmitter* tryPop(long sec, long long nsec) {
10854 Lock lock(this);
10855 if (m_ues.empty()) {
10856 // Check for empty() after wait(), in case of spurious wakeup.
10857 if (!wait(sec, nsec) || m_ues.empty()) {
10858 return nullptr;
10861 assert(m_ues.size() > 0);
10862 UnitEmitter* ue = m_ues.front();
10863 assert(ue != nullptr);
10864 m_ues.pop_front();
10865 return ue;
10867 private:
10868 std::deque<UnitEmitter*> m_ues;
10870 static UEQ s_ueq;
10872 class EmitterWorker
10873 : public JobQueueWorker<FileScopeRawPtr, void*, true, true> {
10874 public:
10875 EmitterWorker() : m_ret(true) {}
10876 void doJob(JobType job) override {
10877 try {
10878 AnalysisResultPtr ar = ((AnalysisResult*)m_context)->shared_from_this();
10879 UnitEmitter* ue = emitHHBCVisitor(ar, job);
10880 if (Option::GenerateBinaryHHBC) {
10881 s_ueq.push(ue);
10882 } else {
10883 delete ue;
10885 } catch (Exception &e) {
10886 Logger::Error("%s", e.getMessage().c_str());
10887 m_ret = false;
10888 } catch (...) {
10889 Logger::Error("Fatal: An unexpected exception was thrown");
10890 m_ret = false;
10893 void onThreadEnter() override {
10894 g_context.getCheck();
10896 void onThreadExit() override {
10897 hphp_memory_cleanup();
10899 private:
10900 bool m_ret;
10903 static void addEmitterWorker(AnalysisResultPtr ar, StatementPtr sp,
10904 void *data) {
10905 ((JobQueueDispatcher<EmitterWorker>*)data)->enqueue(sp->getFileScope());
10908 static void
10909 commitGlobalData(std::unique_ptr<ArrayTypeTable::Builder> arrTable) {
10910 auto gd = Repo::GlobalData{};
10911 gd.HardTypeHints = HHBBC::options.HardTypeHints;
10912 gd.HardReturnTypeHints = HHBBC::options.HardReturnTypeHints;
10913 gd.UsedHHBBC = Option::UseHHBBC;
10914 gd.PHP7_IntSemantics = RuntimeOption::PHP7_IntSemantics;
10915 gd.PHP7_ScalarTypes = RuntimeOption::PHP7_ScalarTypes;
10916 gd.PHP7_Substr = RuntimeOption::PHP7_Substr;
10917 gd.AutoprimeGenerators = RuntimeOption::AutoprimeGenerators;
10918 gd.HardPrivatePropInference = true;
10919 for (auto a : Option::APCProfile) {
10920 gd.APCProfile.emplace_back(StringData::MakeStatic(folly::StringPiece(a)));
10922 if (arrTable) gd.arrayTypeTable.repopulate(*arrTable);
10923 Repo::get().saveGlobalData(gd);
10927 * This is the entry point for offline bytecode generation.
10929 void emitAllHHBC(AnalysisResultPtr&& ar) {
10930 unsigned int threadCount = Option::ParserThreadCount;
10931 unsigned int nFiles = ar->getAllFilesVector().size();
10932 if (threadCount > nFiles) {
10933 threadCount = nFiles;
10935 if (!threadCount) threadCount = 1;
10937 LitstrTable::get().setWriting();
10939 /* there is a race condition in the first call to
10940 makeStaticString. Make sure we dont hit it */
10942 makeStaticString("");
10943 /* same for TypeConstraint */
10944 TypeConstraint tc;
10947 Compiler::ClearErrors();
10949 JobQueueDispatcher<EmitterWorker>
10950 dispatcher(threadCount, true, 0, false, ar.get());
10952 auto setPreloadPriority = [&ar](const std::string& f, int p) {
10953 auto fs = ar->findFileScope(f);
10954 if (fs) fs->setPreloadPriority(p);
10958 * Mark files that are referenced from the autoload map
10959 * so they get preloaded via preloadRepo.
10960 * Higher priorities are preloaded first.
10961 * Classes, then functions, then constants mimics
10962 * the order of our existing warmup scripts
10964 for (const auto& ent : Option::AutoloadConstMap) {
10965 setPreloadPriority(ent.second, 1);
10967 for (const auto& ent : Option::AutoloadFuncMap) {
10968 setPreloadPriority(ent.second, 2);
10970 for (const auto& ent : Option::AutoloadClassMap) {
10971 setPreloadPriority(ent.second, 3);
10974 dispatcher.start();
10975 ar->visitFiles(addEmitterWorker, &dispatcher);
10977 auto ues = ar->getHhasFiles();
10978 if (!Option::UseHHBBC && ues.size()) {
10979 batchCommit(std::move(ues));
10982 if (Option::GenerateBinaryHHBC) {
10983 // kBatchSize needs to strike a balance between reducing transaction commit
10984 // overhead (bigger batches are better), and limiting the cost incurred by
10985 // failed commits due to identical units that require rollback and retry
10986 // (smaller batches have less to lose). Empirical results indicate that a
10987 // value in the 2-10 range is reasonable.
10988 static const unsigned kBatchSize = 8;
10990 // Gather up units created by the worker threads and commit them in
10991 // batches.
10992 bool didPop;
10993 bool inShutdown = false;
10994 while (true) {
10995 // Poll, but with a 100ms timeout so that this thread doesn't spin wildly
10996 // if it gets ahead of the workers.
10997 UnitEmitter* ue = s_ueq.tryPop(0, 100 * 1000 * 1000);
10998 if ((didPop = (ue != nullptr))) {
10999 ues.push_back(std::unique_ptr<UnitEmitter>{ue});
11001 if (!Option::UseHHBBC &&
11002 (ues.size() == kBatchSize ||
11003 (!didPop && inShutdown && ues.size() > 0))) {
11004 batchCommit(std::move(ues));
11006 if (!inShutdown) {
11007 inShutdown = dispatcher.pollEmpty();
11008 } else if (!didPop) {
11009 assert(Option::UseHHBBC || ues.size() == 0);
11010 break;
11014 if (!Option::UseHHBBC) {
11015 commitGlobalData(std::unique_ptr<ArrayTypeTable::Builder>{});
11017 } else {
11018 dispatcher.waitEmpty();
11021 assert(Option::UseHHBBC || ues.empty());
11023 ar->finish();
11024 ar.reset();
11026 if (!Option::UseHHBBC) {
11027 batchCommit(std::move(ues));
11028 return;
11031 RuntimeOption::EvalJit = false; // For HHBBC to invoke builtins.
11032 auto pair = HHBBC::whole_program(std::move(ues));
11033 batchCommit(std::move(pair.first));
11034 commitGlobalData(std::move(pair.second));
11037 namespace {
11039 bool startsWith(const char* big, const char* small) {
11040 return strncmp(big, small, strlen(small)) == 0;
11042 bool isFileHack(const char* code, int codeLen, bool allowPartial) {
11043 if (allowPartial) {
11044 return codeLen > strlen("<?hh") && startsWith(code, "<?hh");
11045 } else {
11046 return codeLen > strlen("<?hh // strict") &&
11047 (startsWith(code, "<?hh // strict") || startsWith(code, "<?hh //strict"));
11051 UnitEmitter* makeFatalUnit(const char* filename, const MD5& md5,
11052 const std::string& msg) {
11053 // basically duplicated from as.cpp but is maybe too janky to be
11054 // a common routine somewhere...
11056 // The line numbers we output are bogus, but it's not totally clear
11057 // what line numbers to put. It might be worth adding a mechanism for
11058 // the external emitter to emit a line number when it fails that we can
11059 // use when available.
11060 UnitEmitter* ue = new UnitEmitter(md5);
11061 ue->m_filepath = makeStaticString(filename);
11062 ue->initMain(1, 1);
11063 ue->emitOp(OpString);
11064 ue->emitInt32(ue->mergeLitstr(makeStaticString(msg)));
11065 ue->emitOp(OpFatal);
11066 ue->emitByte(static_cast<uint8_t>(FatalOp::Runtime));
11067 FuncEmitter* fe = ue->getMain();
11068 fe->maxStackCells = 1;
11069 fe->finish(ue->bcPos(), false);
11070 ue->recordFunction(fe);
11072 return ue;
11075 UnitEmitter* useExternalEmitter(const char* code, int codeLen,
11076 const char* filename, const MD5& md5) {
11077 #ifdef _MSC_VER
11078 Logger::Error("The external emitter is not supported under MSVC!");
11079 return nullptr;
11080 #else
11081 std::string hhas, errorOutput;
11083 try {
11084 std::vector<std::string> cmd({
11085 RuntimeOption::EvalUseExternalEmitter, "--stdin", filename});
11086 auto options = folly::Subprocess::pipeStdin().pipeStdout().pipeStderr();
11088 // Run the external emitter, sending the code to its stdin
11089 folly::Subprocess proc(cmd, options);
11090 std::tie(hhas, errorOutput) = proc.communicate(std::string(code, codeLen));
11091 proc.waitChecked();
11093 // External emitter succeeded; assemble its output
11094 // If assembly fails (probably because of malformed emitter
11095 // output), the assembler will return a unit that Fatals and we
11096 // won't do a fallback to the regular emitter. We may want to
11097 // revisit this.
11098 return assemble_string(hhas.data(), hhas.length(), filename, md5);
11100 } catch (const std::exception& e) {
11101 std::string errorMsg = e.what();
11102 if (!errorOutput.empty()) {
11103 // Add the stderr to the output
11104 errorMsg += folly::format(". Output: '{}'",
11105 folly::trimWhitespace(errorOutput)).str();
11108 // If we aren't going to fall back to the internal emitter, generate a
11109 // Fatal'ing unit.
11110 if (!RuntimeOption::EvalExternalEmitterFallback) {
11111 auto msg =
11112 folly::format("Failure running external emitter: {}", errorMsg);
11113 return makeFatalUnit(filename, md5, msg.str());
11116 // Unless we have fallback at the highest level, print a
11117 // diagnostic when we fail
11118 if (RuntimeOption::EvalExternalEmitterFallback < 2) {
11119 Logger::Warning("Failure running external emitter for %s: %s",
11120 filename,
11121 errorMsg.c_str());
11123 return nullptr;
11125 #endif
11130 extern "C" {
11133 * This is the entry point from the runtime; i.e. online bytecode generation.
11134 * The 'filename' parameter may be NULL if there is no file associated with
11135 * the source code.
11137 * Before being actually used, hphp_compiler_parse must be called with
11138 * a NULL `code' parameter to do initialization.
11141 Unit* hphp_compiler_parse(const char* code, int codeLen, const MD5& md5,
11142 const char* filename) {
11143 if (UNLIKELY(!code)) {
11144 // Do initialization when code is null; see above.
11145 Option::EnableHipHopSyntax = RuntimeOption::EnableHipHopSyntax;
11146 HHBBC::options.HardReturnTypeHints =
11147 RuntimeOption::EvalCheckReturnTypeHints >= 3;
11148 Option::EnableZendCompat = RuntimeOption::EnableZendCompat;
11149 Option::JitEnableRenameFunction =
11150 RuntimeOption::EvalJitEnableRenameFunction;
11151 for (auto& i : RuntimeOption::DynamicInvokeFunctions) {
11152 Option::DynamicInvokeFunctions.insert(i);
11154 Option::IntsOverflowToInts = RuntimeOption::IntsOverflowToInts;
11155 Option::RecordErrors = false;
11156 Option::ParseTimeOpts = false;
11157 Option::WholeProgram = false;
11158 BuiltinSymbols::LoadSuperGlobals();
11159 TypeConstraint tc;
11160 return nullptr;
11163 SCOPE_ASSERT_DETAIL("hphp_compiler_parse") { return filename; };
11165 try {
11166 UnitOrigin unitOrigin = UnitOrigin::File;
11167 if (!filename) {
11168 filename = "";
11169 unitOrigin = UnitOrigin::Eval;
11172 std::unique_ptr<UnitEmitter> ue;
11173 // Check if this file contains raw hip hop bytecode instead of
11174 // php. This is dictated by a special file extension.
11175 if (RuntimeOption::EvalAllowHhas) {
11176 if (const char* dot = strrchr(filename, '.')) {
11177 const char hhbc_ext[] = "hhas";
11178 if (!strcmp(dot + 1, hhbc_ext)) {
11179 ue.reset(assemble_string(code, codeLen, filename, md5));
11184 // If we are configured to use an external emitter and we are compiling
11185 // a strict mode hack file, try external emitting. Don't externally emit
11186 // systemlib because the external emitter can't handle that yet.
11187 if (!ue &&
11188 !RuntimeOption::EvalUseExternalEmitter.empty() &&
11189 isFileHack(code, codeLen,
11190 RuntimeOption::EvalExternalEmitterAllowPartial) &&
11191 SystemLib::s_inited) {
11192 ue.reset(useExternalEmitter(code, codeLen, filename, md5));
11195 if (!ue) {
11196 auto parseit = [=] (AnalysisResultPtr ar) {
11197 Scanner scanner(code, codeLen,
11198 RuntimeOption::GetScannerType(), filename);
11199 Parser parser(scanner, filename, ar, codeLen);
11200 parser.parse();
11201 return parser.getFileScope();
11204 if (BuiltinSymbols::s_systemAr) {
11205 parseit(BuiltinSymbols::s_systemAr)->setMd5(md5);
11208 auto ar = std::make_shared<AnalysisResult>();
11209 FileScopePtr fsp = parseit(ar);
11210 fsp->setOuterScope(ar);
11212 ar->setPhase(AnalysisResult::AnalyzeAll);
11213 fsp->analyzeProgram(ar);
11215 ue.reset(emitHHBCUnitEmitter(ar, fsp, md5));
11217 Repo::get().commitUnit(ue.get(), unitOrigin);
11219 auto unit = ue->create();
11220 ue.reset();
11222 if (unit->sn() == -1) {
11223 // the unit was not committed to the Repo, probably because
11224 // another thread did it first. Try to use the winner.
11225 auto u = Repo::get().loadUnit(filename ? filename : "", md5);
11226 if (u != nullptr) {
11227 return u.release();
11230 return unit.release();
11231 } catch (const std::exception&) {
11232 // extern "C" function should not be throwing exceptions...
11233 return nullptr;
11237 } // extern "C"
11239 ///////////////////////////////////////////////////////////////////////////////