Handle builtin default argument values which are constants
[hiphop-php.git] / hphp / compiler / analysis / emitter.cpp
blobfc4570fd43390f4b5b9d482791fecf5342de0133
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 <atomic>
21 #include <deque>
22 #include <exception>
23 #include <fstream>
24 #include <iomanip>
25 #include <iostream>
26 #include <memory>
27 #include <set>
28 #include <utility>
29 #include <vector>
31 #include <boost/algorithm/string/predicate.hpp>
33 #include <folly/MapUtil.h>
34 #include <folly/Memory.h>
35 #include <folly/ScopeGuard.h>
36 #ifndef _MSC_VER
37 #include <folly/Subprocess.h>
38 #endif
39 #include <folly/String.h>
41 #include "hphp/compiler/builtin_symbols.h"
42 #include "hphp/compiler/analysis/class_scope.h"
43 #include "hphp/compiler/analysis/code_error.h"
44 #include "hphp/compiler/analysis/file_scope.h"
45 #include "hphp/compiler/analysis/function_scope.h"
46 #include "hphp/compiler/expression/array_element_expression.h"
47 #include "hphp/compiler/expression/array_pair_expression.h"
48 #include "hphp/compiler/expression/assignment_expression.h"
49 #include "hphp/compiler/expression/binary_op_expression.h"
50 #include "hphp/compiler/expression/class_constant_expression.h"
51 #include "hphp/compiler/expression/class_expression.h"
52 #include "hphp/compiler/expression/closure_expression.h"
53 #include "hphp/compiler/expression/constant_expression.h"
54 #include "hphp/compiler/expression/dynamic_variable.h"
55 #include "hphp/compiler/expression/encaps_list_expression.h"
56 #include "hphp/compiler/expression/expression_list.h"
57 #include "hphp/compiler/expression/include_expression.h"
58 #include "hphp/compiler/expression/list_assignment.h"
59 #include "hphp/compiler/expression/modifier_expression.h"
60 #include "hphp/compiler/expression/new_object_expression.h"
61 #include "hphp/compiler/expression/object_method_expression.h"
62 #include "hphp/compiler/expression/parameter_expression.h"
63 #include "hphp/compiler/expression/qop_expression.h"
64 #include "hphp/compiler/expression/null_coalesce_expression.h"
65 #include "hphp/compiler/expression/scalar_expression.h"
66 #include "hphp/compiler/expression/simple_variable.h"
67 #include "hphp/compiler/expression/simple_function_call.h"
68 #include "hphp/compiler/expression/static_member_expression.h"
69 #include "hphp/compiler/expression/unary_op_expression.h"
70 #include "hphp/compiler/expression/yield_expression.h"
71 #include "hphp/compiler/expression/yield_from_expression.h"
72 #include "hphp/compiler/expression/await_expression.h"
73 #include "hphp/compiler/statement/block_statement.h"
74 #include "hphp/compiler/statement/break_statement.h"
75 #include "hphp/compiler/statement/case_statement.h"
76 #include "hphp/compiler/statement/catch_statement.h"
77 #include "hphp/compiler/statement/class_constant.h"
78 #include "hphp/compiler/statement/class_variable.h"
79 #include "hphp/compiler/statement/do_statement.h"
80 #include "hphp/compiler/statement/echo_statement.h"
81 #include "hphp/compiler/statement/exp_statement.h"
82 #include "hphp/compiler/statement/for_statement.h"
83 #include "hphp/compiler/statement/foreach_statement.h"
84 #include "hphp/compiler/statement/finally_statement.h"
85 #include "hphp/compiler/statement/function_statement.h"
86 #include "hphp/compiler/statement/global_statement.h"
87 #include "hphp/compiler/statement/goto_statement.h"
88 #include "hphp/compiler/statement/if_branch_statement.h"
89 #include "hphp/compiler/statement/if_statement.h"
90 #include "hphp/compiler/statement/label_statement.h"
91 #include "hphp/compiler/statement/method_statement.h"
92 #include "hphp/compiler/statement/return_statement.h"
93 #include "hphp/compiler/statement/statement_list.h"
94 #include "hphp/compiler/statement/static_statement.h"
95 #include "hphp/compiler/statement/switch_statement.h"
96 #include "hphp/compiler/statement/try_statement.h"
97 #include "hphp/compiler/statement/unset_statement.h"
98 #include "hphp/compiler/statement/while_statement.h"
99 #include "hphp/compiler/statement/use_trait_statement.h"
100 #include "hphp/compiler/statement/class_require_statement.h"
101 #include "hphp/compiler/statement/trait_prec_statement.h"
102 #include "hphp/compiler/statement/trait_alias_statement.h"
103 #include "hphp/compiler/statement/typedef_statement.h"
104 #include "hphp/compiler/statement/declare_statement.h"
105 #include "hphp/compiler/parser/parser.h"
106 #include "hphp/hhbbc/hhbbc.h"
107 #include "hphp/hhbbc/parallel.h"
109 #include "hphp/util/trace.h"
110 #include "hphp/util/safe-cast.h"
111 #include "hphp/util/logger.h"
112 #include "hphp/util/job-queue.h"
113 #include "hphp/parser/hphp.tab.hpp"
114 #include "hphp/runtime/base/repo-auth-type.h"
115 #include "hphp/runtime/vm/bytecode.h"
116 #include "hphp/runtime/vm/native.h"
117 #include "hphp/runtime/vm/repo.h"
118 #include "hphp/runtime/vm/repo-global-data.h"
119 #include "hphp/runtime/vm/as.h"
120 #include "hphp/runtime/base/packed-array.h"
121 #include "hphp/runtime/base/set-array.h"
122 #include "hphp/runtime/base/stats.h"
123 #include "hphp/runtime/base/static-string-table.h"
124 #include "hphp/runtime/base/runtime-option.h"
125 #include "hphp/runtime/base/zend-string.h"
126 #include "hphp/runtime/base/zend-functions.h"
127 #include "hphp/runtime/base/type-conversions.h"
128 #include "hphp/runtime/base/builtin-functions.h"
129 #include "hphp/runtime/base/variable-serializer.h"
130 #include "hphp/runtime/base/program-functions.h"
131 #include "hphp/runtime/base/unit-cache.h"
132 #include "hphp/runtime/base/user-attributes.h"
133 #include "hphp/runtime/base/collections.h"
134 #include "hphp/runtime/vm/preclass-emitter.h"
135 #include "hphp/runtime/vm/runtime.h"
137 #include "hphp/system/systemlib.h"
139 namespace HPHP {
141 DECLARE_BOOST_TYPES(AwaitExpression);
142 DECLARE_BOOST_TYPES(ClosureExpression);
143 DECLARE_BOOST_TYPES(FileScope);
144 DECLARE_BOOST_TYPES(ForEachStatement);
145 DECLARE_BOOST_TYPES(FunctionCall);
146 DECLARE_BOOST_TYPES(FunctionScope);
147 DECLARE_BOOST_TYPES(InterfaceStatement);
148 DECLARE_BOOST_TYPES(ListAssignment);
149 DECLARE_BOOST_TYPES(MethodStatement);
150 DECLARE_BOOST_TYPES(SimpleFunctionCall);
151 DECLARE_BOOST_TYPES(SwitchStatement);
153 namespace Compiler {
154 ///////////////////////////////////////////////////////////////////////////////
156 TRACE_SET_MOD(emitter);
158 const StaticString
159 s_ini_get("ini_get"),
160 s_is_deprecated("deprecated function"),
161 s_trigger_error("trigger_error"),
162 s_trigger_sampled_error("trigger_sampled_error"),
163 s_zend_assertions("zend.assertions");
165 struct Label;
166 struct EmitterVisitor;
168 using OptLocation = folly::Optional<Location::Range>;
170 namespace StackSym {
171 static const char None = 0x00;
174 * We don't actually track the U flavor (we treat it as a C),
175 * because there's nothing important to do with it for emission.
176 * The verifier will check they are only created at the appropriate
177 * times.
179 static const char C = 0x01; // Cell symbolic flavor
180 static const char V = 0x02; // Ref symbolic flavor
181 static const char A = 0x03; // Classref symbolic flavor
182 static const char R = 0x04; // Return value symbolic flavor
183 static const char F = 0x05; // Function argument symbolic flavor
184 static const char L = 0x06; // Local symbolic flavor
185 static const char T = 0x07; // String literal symbolic flavor
186 static const char I = 0x08; // int literal symbolic flavor
187 static const char H = 0x09; // $this symbolic flavor
189 static const char N = 0x10; // Name marker
190 static const char G = 0x20; // Global name marker
191 static const char E = 0x30; // Element marker
192 static const char W = 0x40; // New element marker
193 static const char P = 0x50; // Property marker
194 static const char S = 0x60; // Static property marker
195 static const char M = 0x70; // Non elem/prop/W part of M-vector
196 static const char K = (char)0x80u; // Class base marker
197 static const char Q = (char)0x90u; // NullSafe Property marker
199 static const char CN = C | N;
200 static const char CG = C | G;
201 static const char CS = C | S;
202 static const char LN = L | N;
203 static const char LG = L | G;
204 static const char LS = L | S;
205 static const char AM = A | M;
207 char GetSymFlavor(char sym) { return (sym & 0x0F); }
208 char GetMarker(char sym) { return (sym & 0xF0); }
211 * Return whether or not sym represents a symbolic stack element, rather than
212 * an actual stack element. Symbolic stack elements do not have corresponding
213 * values on the real eval stack at runtime, and represent things like local
214 * variable ids or literal ints and strings.
216 bool IsSymbolic(char sym) {
217 auto const flavor = GetSymFlavor(sym);
218 if (flavor == L || flavor == T || flavor == I || flavor == H) return true;
220 auto const marker = GetMarker(sym);
221 if (marker == W || marker == K) return true;
223 return false;
226 std::string ToString(char sym) {
227 char symFlavor = StackSym::GetSymFlavor(sym);
228 std::string res;
229 switch (symFlavor) {
230 case StackSym::C: res = "C"; break;
231 case StackSym::V: res = "V"; break;
232 case StackSym::A: res = "A"; break;
233 case StackSym::R: res = "R"; break;
234 case StackSym::F: res = "F"; break;
235 case StackSym::L: res = "L"; break;
236 case StackSym::T: res = "T"; break;
237 case StackSym::I: res = "I"; break;
238 case StackSym::H: res = "H"; break;
239 default: break;
241 char marker = StackSym::GetMarker(sym);
242 switch (marker) {
243 case StackSym::N: res += "N"; break;
244 case StackSym::G: res += "G"; break;
245 case StackSym::E: res += "E"; break;
246 case StackSym::W: res += "W"; break;
247 case StackSym::P: res += "P"; break;
248 case StackSym::Q: res += "Q"; break;
249 case StackSym::S: res += "S"; break;
250 case StackSym::K: res += "K"; break;
251 default: break;
253 if (res == "") {
254 if (sym == StackSym::None) {
255 res = "None";
256 } else {
257 res = "?";
260 return res;
264 struct Emitter {
265 Emitter(ConstructPtr node, UnitEmitter& ue, EmitterVisitor& ev)
266 : m_node(node), m_ue(ue), m_ev(ev) {}
267 UnitEmitter& getUnitEmitter() { return m_ue; }
268 ConstructPtr getNode() { return m_node; }
269 EmitterVisitor& getEmitterVisitor() { return m_ev; }
270 void setTempLocation(const OptLocation& r) {
271 m_tempLoc = r;
273 const OptLocation& getTempLocation() { return m_tempLoc; }
274 void incStat(int counter, int value) {
275 if (RuntimeOption::EnableEmitterStats) {
276 IncStat(counter, value);
280 struct StrOff {
281 StrOff(Id s, Label* d) : str(s), dest(d) {}
282 Id str;
283 Label* dest;
286 struct IterPair {
287 IterPair(IterKind k, Id i) : kind(k), id(i) {}
288 IterKind kind;
289 Id id;
292 #define NA
293 #define ONE(typ) \
294 IMM_##typ
295 #define TWO(typ1, typ2) \
296 IMM_##typ1, IMM_##typ2
297 #define THREE(typ1, typ2, typ3) \
298 IMM_##typ1, IMM_##typ2, IMM_##typ3
299 #define FOUR(typ1, typ2, typ3, typ4) \
300 IMM_##typ1, IMM_##typ2, IMM_##typ3, IMM_##typ4
301 #define IMM_BLA std::vector<Label*>&
302 #define IMM_SLA std::vector<StrOff>&
303 #define IMM_ILA std::vector<IterPair>&
304 #define IMM_IVA int32_t
305 #define IMM_LA int32_t
306 #define IMM_IA int32_t
307 #define IMM_I64A int64_t
308 #define IMM_DA double
309 #define IMM_SA const StringData*
310 #define IMM_RATA RepoAuthType
311 #define IMM_AA ArrayData*
312 #define IMM_BA Label&
313 #define IMM_OA(type) type
314 #define IMM_VSA std::vector<std::string>&
315 #define IMM_KA MemberKey
316 #define O(name, imm, pop, push, flags) void name(imm);
317 OPCODES
318 #undef O
319 #undef NA
320 #undef ONE
321 #undef TWO
322 #undef THREE
323 #undef FOUR
324 #undef IMM_MA
325 #undef IMM_BLA
326 #undef IMM_SLA
327 #undef IMM_ILA
328 #undef IMM_IVA
329 #undef IMM_LA
330 #undef IMM_IA
331 #undef IMM_I64A
332 #undef IMM_DA
333 #undef IMM_SA
334 #undef IMM_RATA
335 #undef IMM_AA
336 #undef IMM_BA
337 #undef IMM_OA
338 #undef IMM_VSA
339 #undef IMM_KA
341 private:
342 ConstructPtr m_node;
343 UnitEmitter& m_ue;
344 EmitterVisitor& m_ev;
345 OptLocation m_tempLoc;
348 struct SymbolicStack {
349 enum ClassBaseType {
350 CLS_INVALID,
351 CLS_LATE_BOUND,
352 CLS_UNNAMED_LOCAL, // loc is an unnamed local
353 CLS_NAMED_LOCAL, // loc is a normal program local
354 CLS_STRING_NAME, // name is the string to use
355 CLS_SELF,
356 CLS_PARENT
359 private:
361 * Symbolic stack (m_symStack)
363 * The symbolic stack is used to keep track of the flavor descriptors
364 * of values along with other contextual information. Each position in
365 * the symbolic stack can encode a "symbolic flavor" and a "marker".
366 * Most symbolic flavors correspond with flavor descriptors in the HHBC
367 * spec, but some symbolic flavors used in the symbolic stack (ex. "L")
368 * do not correspond with a flavor descriptor from the spec. Markers
369 * provide contextual information and are used by the emitter in various
370 * situations to determine the appropriate bytecode instruction to use.
372 * Note that not all positions on the symbolic stack correspond to a
373 * value on the actual evaluation stack as described in the HHBC spec.
375 struct SymEntry {
376 explicit SymEntry(char s = 0)
377 : sym(s)
378 , name(nullptr)
379 , className(nullptr)
380 , intval(-1)
381 , unnamedLocalStart(InvalidAbsoluteOffset)
382 , clsBaseType(CLS_INVALID)
384 char sym;
385 const StringData* name;
386 const StringData* className;
387 int64_t intval; // used for L and I symbolic flavors
389 // If intval is an unnamed local temporary, this offset is the start
390 // of the region we are using it (which we will need to have a
391 // fault funclet for).
392 Offset unnamedLocalStart;
394 // When class bases are emitted, we need to delay class lookup for
395 // evaluation order reasons, but may have to do some evaluation
396 // early. How this works depends on the type of class base---see
397 // emitResolveClsBase for details.
398 ClassBaseType clsBaseType;
400 std::string pretty() const;
402 std::vector<SymEntry> m_symStack;
405 * Actual stack (m_actualStack)
407 * The actual stack represents the evaluation stack as described in the
408 * HHBC spec. Each position in m_actualStack contains the index of the
409 * corresponding symbolic value in m_symStack.
411 std::vector<int> m_actualStack;
413 // The number of Func descriptors (in HHVM terms, ActRecs) currently on the
414 // stack.
415 int m_fdescCount;
417 public:
418 int* m_actualStackHighWaterPtr;
420 SymbolicStack() : m_fdescCount(0) {}
422 std::string pretty() const;
424 void updateHighWater();
425 void push(char sym);
426 void setInt(int64_t v);
427 void setString(const StringData* s);
428 void setKnownCls(const StringData* s, bool nonNull);
429 void cleanTopMeta();
430 void setClsBaseType(ClassBaseType);
431 void setUnnamedLocal(int index, int localId, Offset startOffset);
432 void pop();
433 char top() const;
434 char get(int index) const;
435 const StringData* getName(int index) const;
436 const StringData* getClsName(int index) const;
437 bool isCls(int index) const;
438 bool isTypePredicted(int index = -1 /* stack top */) const;
439 void set(int index, char sym);
440 size_t size() const;
441 size_t actualSize() const;
442 size_t fdescSize() const { return m_fdescCount; }
443 bool empty() const;
444 void clear();
447 * Erase a stack element depth below the top. This is used for some
448 * instructions that pull elements out of the middle, and for our
449 * ClassBaseType virtual elements.
451 void consumeBelowTop(int depth);
453 int getActualPos(int vpos) const;
454 char getActual(int index) const;
455 void setActual(int index, char sym);
456 void insertAt(int depth, char sym);
457 int sizeActual() const;
459 ClassBaseType getClsBaseType(int index) const;
460 int getLoc(int index) const;
461 int64_t getInt(int index) const;
462 Offset getUnnamedLocStart(int index) const;
464 void pushFDesc();
465 void popFDesc();
468 struct Label {
469 enum class NoEntryNopFlag {};
470 Label() : m_off(InvalidAbsoluteOffset) {}
471 explicit Label(Emitter& e) : m_off(InvalidAbsoluteOffset) {
472 set(e, true);
474 Label(Emitter& e, NoEntryNopFlag) : m_off(InvalidAbsoluteOffset) {
475 set(e, false);
477 Offset getAbsoluteOffset() const { return m_off; }
478 // Sets the Label to the bytecode offset of given by e,
479 // fixes up any instructions that have already been
480 // emitted that reference this Label, and fixes up the
481 // EmitterVisitor's jump target info
482 void set(Emitter& e, bool emitNopAtEntry = true);
483 // If a Label is has not been set, it is the Emitter's
484 // resposibility to call bind on the Label each time it
485 // prepares to emit an instruction that uses the Label
486 void bind(EmitterVisitor& ev, Offset instrAddr, Offset offAddr);
487 bool isSet() { return m_off != InvalidAbsoluteOffset; }
488 bool isUsed();
489 private:
490 Offset m_off;
491 std::vector<std::pair<Offset, Offset> > m_emittedOffs;
492 // m_evalStack is used to store the eval stack of the
493 // first forward jump we see that references this label
494 SymbolicStack m_evalStack;
497 struct Thunklet {
498 virtual ~Thunklet();
499 virtual void emit(Emitter& e) = 0;
502 struct Funclet {
503 explicit Funclet(Thunklet* body)
504 : m_body(body) {
506 Thunklet* m_body;
507 Label m_entry;
510 DECLARE_BOOST_TYPES(ControlTarget);
512 * The structure represents a code path that potentially requires
513 * running finally blocks. A code path has an assigned state ID that
514 * is used inside switch statements emitted at the end of finally
515 * blocks. It also has an optional label (the destination to jump
516 * to after all the required finally blocks are run).
518 struct ControlTarget {
519 static const int k_unsetState;
520 explicit ControlTarget(EmitterVisitor* router);
521 ~ControlTarget();
522 // Manage state ID reuse.
523 bool isRegistered();
524 EmitterVisitor* m_visitor;
525 // The target to jump to once all the necessary finally blocks are run.
526 Label m_label;
527 // The state ID that identifies this control target inside finally
528 // epilogues. This ID assigned to the "state" unnamed local variable.
529 int m_state;
532 struct ControlTargetInfo {
533 ControlTargetInfo() : used(false) {}
534 ControlTargetInfo(ControlTargetPtr t, bool b) : target(t), used(b) {}
535 ControlTargetPtr target;
536 bool used;
539 DECLARE_BOOST_TYPES(Region);
542 * Region represents a single level of the unified stack
543 * of constructs that are meaningful from the point of view of finally
544 * implementation. The levels are used to keep track of the information
545 * such as the control targets that can be taken inside a block.
547 struct Region {
548 enum Kind {
549 // Top-level (global) context.
550 Global,
551 // Function body / method body entry.
552 FuncBody,
553 // Entry for finally fault funclets emitted after the body of
554 // a function
555 FaultFunclet,
556 // Region by a finally clause
557 TryFinally,
558 // Finally block entry (begins after catches ends after finally)
559 Finally,
560 // Loop or switch statement.
561 LoopOrSwitch,
564 typedef Emitter::IterPair IterPair;
565 typedef std::vector<IterPair> IterVec;
567 Region(Region::Kind kind, RegionPtr parent);
569 // Helper for establishing the maximal depth of break / continue
570 // control targets that are allocated.
571 int getBreakContinueDepth();
573 // Returns the maximal break / continue depth admissable (aka the
574 // number of nested loops).
575 int getMaxBreakContinueDepth();
577 int getMaxState();
579 // The number of cases to be emitted. This is a helper used in
580 // establishing whether one of the optimized cases can be used.
581 int getCaseCount();
583 bool isForeach() { return m_iterId != -1; }
584 bool isTryFinally() { return m_kind == Region::Kind::TryFinally; }
585 bool isFinally() { return m_kind == Region::Kind::Finally; }
587 bool isBreakUsed(int i) {
588 auto it = m_breakTargets.find(i);
589 if (it == m_breakTargets.end()) return false;
590 return it->second.used;
593 bool isContinueUsed(int i) {
594 auto it = m_continueTargets.find(i);
595 if (it == m_continueTargets.end()) return false;
596 return it->second.used;
599 Region::Kind m_kind;
600 // Only used for loop / break kind of entries.
601 Id m_iterId;
602 IterKind m_iterKind;
603 // Because of a bug in code emission, functions sometimes have
604 // inconsistent return flavors. Therefore instead of a single
605 // return control target, there need to be one return control
606 // target per flavor used. Once the bug is removed, this code
607 // can be simplified.
608 std::map<char, ControlTargetInfo> m_returnTargets;
609 // Break and continue control targets identified by their depth.
610 std::map<int, ControlTargetInfo> m_breakTargets;
611 std::map<int, ControlTargetInfo> m_continueTargets;
612 // Goto control targets. Each goto control target is identified
613 // by the name of the destination label.
614 std::map<StringData*, ControlTargetInfo, string_data_lt> m_gotoTargets;
615 // A set of goto labels occurrning inside the statement represented
616 // by this entry. This value is used for establishing whether
617 // a finally block needs to be executed when performing gotos.
618 std::set<StringData*, string_data_lt> m_gotoLabels;
619 // The label denoting the beginning of a finally block inside the
620 // current try. Only used when the entry kind is a try statement.
621 Label m_finallyLabel;
622 // The parent entry.
623 RegionPtr m_parent;
626 struct EmitterVisitor {
627 friend struct UnsetUnnamedLocalThunklet;
628 friend struct FuncFinisher;
629 public:
630 typedef std::vector<int> IndexChain;
631 typedef std::pair<ExpressionPtr, IndexChain> IndexPair;
632 typedef Emitter::IterPair IterPair;
633 typedef std::vector<IterPair> IterVec;
635 explicit EmitterVisitor(UnitEmitter& ue);
636 ~EmitterVisitor();
638 bool visit(ConstructPtr c);
639 void visitKids(ConstructPtr c);
640 void visit(FileScopePtr file);
641 void assignLocalVariableIds(FunctionScopePtr fs);
642 void assignFinallyVariableIds();
643 void fixReturnType(Emitter& e, FunctionCallPtr fn,
644 Func* builtinFunc = nullptr);
646 void listAssignmentVisitLHS(Emitter& e, ExpressionPtr exp,
647 IndexChain& indexChain,
648 std::vector<IndexPair>& chainList);
649 void listAssignmentAssignElements(Emitter& e,
650 std::vector<IndexPair>& indexChains,
651 std::function<void()> emitSrc);
653 void visitIfCondition(ExpressionPtr cond, Emitter& e, Label& tru, Label& fals,
654 bool truFallthrough);
655 const SymbolicStack& getEvalStack() const { return m_evalStack; }
656 SymbolicStack& getEvalStack() { return m_evalStack; }
657 void setEvalStack(const SymbolicStack& es) {
658 m_evalStack = es;
659 m_evalStackIsUnknown = false;
661 bool evalStackIsUnknown() { return m_evalStackIsUnknown; }
662 void popEvalStack(char symFlavor);
663 void popSymbolicLocal(Op opcode);
664 void popEvalStackMMany();
665 void popEvalStackMany(int len, char symFlavor);
666 void popEvalStackCVMany(int len);
667 void pushEvalStack(char symFlavor);
668 void peekEvalStack(char symFlavor, int depthActual);
669 void pokeEvalStack(char symFlavor, int depthActual);
670 void prepareEvalStack();
671 void recordJumpTarget(Offset target, const SymbolicStack& evalStack);
672 void recordJumpTarget(Offset target) {
673 recordJumpTarget(target, m_evalStack);
675 void restoreJumpTargetEvalStack();
676 void recordCall();
677 bool isJumpTarget(Offset target);
678 void setPrevOpcode(Op op) { m_prevOpcode = op; }
679 Op getPrevOpcode() const { return m_prevOpcode; }
680 bool currentPositionIsReachable() {
681 return (m_ue.bcPos() == m_curFunc->base
682 || isJumpTarget(m_ue.bcPos())
683 || (instrFlags(getPrevOpcode()) & TF) == 0);
685 FuncEmitter* getFuncEmitter() { return m_curFunc; }
686 Id getStateLocal() {
687 assert(m_stateLocal >= 0);
688 return m_stateLocal;
690 Id getRetLocal() {
691 assert(m_retLocal >= 0);
692 return m_retLocal;
695 struct IncludeTimeFatalException : Exception {
696 ConstructPtr m_node;
697 bool m_parseFatal;
698 IncludeTimeFatalException(ConstructPtr node, const char* fmt, ...)
699 : Exception(), m_node(node), m_parseFatal(false) {
700 va_list ap; va_start(ap, fmt); format(fmt, ap); va_end(ap);
702 EXCEPTION_COMMON_IMPL(IncludeTimeFatalException);
703 void setParseFatal(bool b = true) { m_parseFatal = b; }
706 void pushIterScope(Id id, IterKind kind) {
707 m_pendingIters.emplace_back(id, kind);
709 void popIterScope() { m_pendingIters.pop_back(); }
711 private:
712 typedef std::pair<StringData*, bool> ClosureUseVar; // (name, byRef)
713 typedef std::vector<ClosureUseVar> ClosureUseVarVec;
714 typedef std::vector<std::pair<Id,IterKind> > PendingIterVec;
715 typedef std::pair<StringData*, ExpressionPtr> NonScalarPair;
716 typedef std::vector<NonScalarPair> NonScalarVec;
717 typedef std::pair<Id, int> StrCase;
719 struct PostponedMeth {
720 PostponedMeth(MethodStatementPtr m, FuncEmitter* fe, bool top,
721 ClosureUseVarVec* useVars)
722 : m_meth(m), m_fe(fe), m_top(top), m_closureUseVars(useVars) {}
723 MethodStatementPtr m_meth;
724 FuncEmitter* m_fe;
725 bool m_top;
726 ClosureUseVarVec* m_closureUseVars;
729 struct PostponedCtor {
730 PostponedCtor(InterfaceStatementPtr is, FuncEmitter* fe)
731 : m_is(is), m_fe(fe) {}
732 InterfaceStatementPtr m_is;
733 FuncEmitter* m_fe;
736 struct PostponedNonScalars {
737 PostponedNonScalars(InterfaceStatementPtr is, FuncEmitter* fe,
738 NonScalarVec* v)
739 : m_is(is), m_fe(fe), m_vec(v) {}
740 void release() {
741 delete m_vec;
743 InterfaceStatementPtr m_is;
744 FuncEmitter* m_fe;
745 NonScalarVec* m_vec;
748 struct PostponedClosureCtor {
749 PostponedClosureCtor(ClosureUseVarVec& v, ClosureExpressionPtr e,
750 FuncEmitter* fe)
751 : m_useVars(v), m_expr(e), m_fe(fe) {}
752 ClosureUseVarVec m_useVars;
753 ClosureExpressionPtr m_expr;
754 FuncEmitter* m_fe;
757 struct CatchRegion {
758 CatchRegion(Offset start, Offset end) : m_start(start),
759 m_end(end) {}
760 ~CatchRegion() {
761 for (std::vector<std::pair<StringData*, Label*> >::const_iterator it =
762 m_catchLabels.begin(); it != m_catchLabels.end(); it++) {
763 delete it->second;
766 Offset m_start;
767 Offset m_end;
768 std::set<StringData*, string_data_lt> m_names;
769 std::vector<std::pair<StringData*, Label*> > m_catchLabels;
772 struct FaultRegion {
773 FaultRegion(Offset start,
774 Offset end,
775 Label* func,
776 Id iterId,
777 IterKind kind)
778 : m_start(start)
779 , m_end(end)
780 , m_func(func)
781 , m_iterId(iterId)
782 , m_iterKind(kind) {}
784 Offset m_start;
785 Offset m_end;
786 Label* m_func;
787 Id m_iterId;
788 IterKind m_iterKind;
791 struct FPIRegion {
792 FPIRegion(Offset start, Offset end, Offset fpOff)
793 : m_start(start), m_end(end), m_fpOff(fpOff) {}
794 Offset m_start;
795 Offset m_end;
796 Offset m_fpOff;
799 struct SwitchState {
800 SwitchState() : nonZeroI(-1), defI(-1) {}
802 SwitchState(const SwitchState&) = delete;
803 SwitchState& operator=(const SwitchState&) = delete;
805 std::map<int64_t, int> cases; // a map from int (or litstr id) to case index
806 std::vector<StrCase> caseOrder; // for string switches, a list of the
807 // <litstr id, case index> in the order
808 // they appear in the source
809 int nonZeroI;
810 int defI;
813 void allocPipeLocal(Id pipeVar) { m_pipeVars.emplace(pipeVar); }
814 void releasePipeLocal(Id pipeVar) {
815 assert(!m_pipeVars.empty() && m_pipeVars.top() == pipeVar);
816 m_pipeVars.pop();
818 folly::Optional<Id> getPipeLocal() {
819 if (m_pipeVars.empty()) return folly::none;
820 return m_pipeVars.top();
823 private:
824 static constexpr size_t kMinStringSwitchCases = 8;
826 UnitEmitter& m_ue;
827 FuncEmitter* m_curFunc;
828 FileScopePtr m_file;
830 Op m_prevOpcode;
832 std::deque<PostponedMeth> m_postponedMeths;
833 std::deque<PostponedCtor> m_postponedCtors;
834 std::deque<PostponedNonScalars> m_postponedPinits;
835 std::deque<PostponedNonScalars> m_postponedSinits;
836 std::deque<PostponedNonScalars> m_postponedCinits;
837 std::deque<PostponedClosureCtor> m_postponedClosureCtors;
838 PendingIterVec m_pendingIters;
839 hphp_hash_map<std::string, FuncEmitter*> m_topMethodEmitted;
840 SymbolicStack m_evalStack;
841 bool m_evalStackIsUnknown;
842 hphp_hash_map<Offset, SymbolicStack> m_jumpTargetEvalStacks;
843 std::deque<Funclet*> m_funclets;
844 std::map<StatementPtr, Funclet*> m_memoizedFunclets;
845 std::deque<CatchRegion*> m_catchRegions;
846 std::deque<FaultRegion*> m_faultRegions;
847 std::deque<FPIRegion*> m_fpiRegions;
848 std::vector<Array> m_staticArrays;
849 std::vector<folly::Optional<HeaderKind>> m_staticColType;
850 std::set<std::string,stdltistr> m_hoistables;
851 OptLocation m_tempLoc;
852 std::unordered_set<std::string> m_staticEmitted;
854 // The stack of all Regions that this EmitterVisitor is currently inside
855 std::vector<RegionPtr> m_regions;
856 // The state IDs currently allocated for the "finally router" logic.
857 // See FIXME above the registerControlTarget() method.
858 std::set<int> m_states;
859 // Unnamed local variables used by the "finally router" logic
860 Id m_stateLocal;
861 Id m_retLocal;
862 // stack of nested unnamed pipe variables
863 std::stack<Id> m_pipeVars;
865 public:
866 bool checkIfStackEmpty(const char* forInstruction) const;
867 void unexpectedStackSym(char sym, const char* where) const;
869 int scanStackForLocation(int iLast);
872 * Emit bytecodes for the base and intermediate dims, returning the number of
873 * eval stack slots containing member keys that should be consumed by the
874 * final operation.
876 struct MInstrOpts {
877 explicit MInstrOpts(MOpMode mode)
878 : allowW{mode == MOpMode::Define}
879 , mode{mode}
882 explicit MInstrOpts(int32_t paramId)
883 : allowW{true}
884 , fpass{true}
885 , paramId{paramId}
888 MInstrOpts& rhs() {
889 rhsVal = true;
890 return *this;
893 bool allowW{false};
894 bool rhsVal{false};
895 bool fpass{false};
896 union {
897 MOpMode mode;
898 int32_t paramId;
902 MemberKey symToMemberKey(Emitter& e, int i, bool allowW);
903 size_t emitMOp(int iFirst, int& iLast, Emitter& e, MInstrOpts opts);
904 void emitQueryMOp(int iFirst, int iLast, Emitter& e, QueryMOp op);
906 enum class PassByRefKind {
907 AllowCell,
908 WarnOnCell,
909 ErrorOnCell,
911 PassByRefKind getPassByRefKind(ExpressionPtr exp);
912 void emitCall(Emitter& e, FunctionCallPtr func,
913 ExpressionListPtr params, Offset fpiStart);
914 void emitAGet(Emitter& e);
915 void emitCGetL2(Emitter& e);
916 void emitCGetL3(Emitter& e);
917 void emitPushL(Emitter& e);
918 void emitCGet(Emitter& e);
919 void emitCGetQuiet(Emitter& e);
920 bool emitVGet(Emitter& e, bool skipCells = false);
921 void emitIsset(Emitter& e);
922 void emitIsType(Emitter& e, IsTypeOp op);
923 void emitEmpty(Emitter& e);
924 void emitUnset(Emitter& e, ExpressionPtr exp = ExpressionPtr());
925 void emitUnsetL(Emitter& e, Id local);
926 void emitVisitAndUnset(Emitter& e, ExpressionPtr exp);
927 void emitSet(Emitter& e);
928 void emitSetL(Emitter& e, Id local);
929 void emitSetOp(Emitter& e, int op);
930 void emitBind(Emitter& e);
931 void emitBindL(Emitter& e, Id local);
932 void emitIncDec(Emitter& e, IncDecOp cop);
933 void emitPop(Emitter& e);
934 void emitConvertToCell(Emitter& e);
935 void emitConvertToCellIfVar(Emitter& e);
936 void emitConvertToCellOrLoc(Emitter& e);
937 void emitConvertSecondToCell(Emitter& e);
938 void emitConvertToVar(Emitter& e);
939 void emitFPass(Emitter& e, int paramID, PassByRefKind passByRefKind);
940 void emitVirtualLocal(int localId);
941 template<class Expr> void emitVirtualClassBase(Emitter&, Expr* node);
942 void emitResolveClsBase(Emitter& e, int pos);
943 void emitClsIfSPropBase(Emitter& e);
944 Id emitVisitAndSetUnnamedL(Emitter& e, ExpressionPtr exp);
945 Id emitSetUnnamedL(Emitter& e);
946 void emitFreeUnnamedL(Emitter& e, Id tempLocal, Offset start);
947 void emitPushAndFreeUnnamedL(Emitter& e, Id tempLocal, Offset start);
948 MaybeDataType analyzeSwitch(SwitchStatementPtr s, SwitchState& state);
949 void emitIntegerSwitch(Emitter& e, SwitchStatementPtr s,
950 std::vector<Label>& caseLabels, Label& done,
951 const SwitchState& state);
952 void emitStringSwitch(Emitter& e, SwitchStatementPtr s,
953 std::vector<Label>& caseLabels, Label& done,
954 const SwitchState& state);
955 void emitArrayInit(Emitter& e, ExpressionListPtr el,
956 folly::Optional<HeaderKind> ct = folly::none);
957 void emitPairInit(Emitter&e, ExpressionListPtr el);
958 void emitVectorInit(Emitter&e, CollectionType ct, ExpressionListPtr el);
959 void emitMapInit(Emitter&e, CollectionType ct, ExpressionListPtr el);
960 void emitSetInit(Emitter&e, CollectionType ct, ExpressionListPtr el);
961 void emitCollectionInit(Emitter& e, BinaryOpExpressionPtr exp);
962 void markElem(Emitter& e);
963 void markNewElem(Emitter& e);
964 void markProp(Emitter& e, PropAccessType propAccessType);
965 void markSProp(Emitter& e);
966 void markName(Emitter& e);
967 void markNameSecond(Emitter& e);
968 void markGlobalName(Emitter& e);
970 void emitNameString(Emitter& e, ExpressionPtr n, bool allowLiteral = false);
971 void emitAssignment(Emitter& e, ExpressionPtr c, int op, bool bind);
972 void emitListAssignment(Emitter& e, ListAssignmentPtr lst);
973 void postponeMeth(MethodStatementPtr m, FuncEmitter* fe, bool top,
974 ClosureUseVarVec* useVars = nullptr);
975 void postponeCtor(InterfaceStatementPtr m, FuncEmitter* fe);
976 void postponePinit(InterfaceStatementPtr m, FuncEmitter* fe, NonScalarVec* v);
977 void postponeSinit(InterfaceStatementPtr m, FuncEmitter* fe, NonScalarVec* v);
978 void postponeCinit(InterfaceStatementPtr m, FuncEmitter* fe, NonScalarVec* v);
979 void emitPostponedMeths();
980 void bindUserAttributes(MethodStatementPtr meth,
981 FuncEmitter *fe);
982 Attr bindNativeFunc(MethodStatementPtr meth,
983 FuncEmitter *fe,
984 bool dynCallWrapper);
985 int32_t emitNativeOpCodeImpl(MethodStatementPtr meth,
986 const char* funcName,
987 const char* className,
988 FuncEmitter* fe);
989 void emitMethodMetadata(MethodStatementPtr meth,
990 ClosureUseVarVec* useVars,
991 bool top);
992 void fillFuncEmitterParams(FuncEmitter* fe,
993 ExpressionListPtr params,
994 bool coerce_params = false);
995 void emitMethodPrologue(Emitter& e, MethodStatementPtr meth);
996 void emitDeprecationWarning(Emitter& e, MethodStatementPtr meth);
997 void emitMethod(MethodStatementPtr meth);
998 void emitMemoizeProp(Emitter& e, MethodStatementPtr meth, Id localID,
999 const std::vector<Id>& paramIDs, uint32_t numParams);
1000 void addMemoizeProp(MethodStatementPtr meth);
1001 void emitMemoizeMethod(MethodStatementPtr meth, const StringData* methName);
1002 void emitConstMethodCallNoParams(Emitter& e, const std::string& name);
1003 bool emitInlineGen(Emitter& e, const ExpressionPtr&);
1004 bool emitInlineGena(Emitter& e, const SimpleFunctionCallPtr& call);
1005 bool emitInlineGenva(Emitter& e, const SimpleFunctionCallPtr& call);
1006 bool emitInlineHHAS(Emitter& e, SimpleFunctionCallPtr);
1007 bool emitHHInvariant(Emitter& e, SimpleFunctionCallPtr);
1008 void emitMethodDVInitializers(Emitter& e,
1009 MethodStatementPtr& meth,
1010 Label& topOfBody);
1011 void emitPostponedCtors();
1012 void emitPostponedPSinit(PostponedNonScalars& p, bool pinit);
1013 void emitPostponedPinits();
1014 void emitPostponedSinits();
1015 void emitPostponedCinits();
1016 void emitPostponedClosureCtors();
1017 enum CallUserFuncFlags {
1018 CallUserFuncNone = -1,
1019 CallUserFuncPlain = 0,
1020 CallUserFuncArray = 1,
1021 CallUserFuncSafe = 2,
1022 CallUserFuncReturn = 4,
1023 CallUserFuncForward = 8,
1024 CallUserFuncSafeArray = CallUserFuncSafe | CallUserFuncArray,
1025 CallUserFuncSafeReturn = CallUserFuncSafe | CallUserFuncReturn,
1026 CallUserFuncForwardArray = CallUserFuncForward | CallUserFuncArray
1029 bool emitCallUserFunc(Emitter& e, SimpleFunctionCallPtr node);
1030 Func* canEmitBuiltinCall(const std::string& name,
1031 int numParams,
1032 AnalysisResultConstPtr ar,
1033 ExpressionListPtr& params);
1034 void emitFuncCall(Emitter& e, FunctionCallPtr node,
1035 const char* nameOverride = nullptr,
1036 ExpressionListPtr paramsOverride = nullptr);
1037 bool emitConstantFuncCall(Emitter& e, SimpleFunctionCallPtr call);
1038 void emitFuncCallArg(Emitter& e, ExpressionPtr exp, int paramId,
1039 bool isUnpack);
1040 bool emitBuiltinCallArg(Emitter& e, ExpressionPtr exp, int paramId,
1041 bool byRef, bool mustBeRef);
1042 bool emitScalarValue(Emitter& e, const Variant& value);
1043 void emitLambdaCaptureArg(Emitter& e, ExpressionPtr exp);
1044 void emitBuiltinDefaultArg(Emitter& e, Variant& v,
1045 MaybeDataType t, int paramId);
1046 Id emitClass(Emitter& e, ClassScopePtr cNode, bool topLevel);
1047 Id emitTypedef(Emitter& e, TypedefStatementPtr);
1048 void emitForeachListAssignment(Emitter& e,
1049 ListAssignmentPtr la,
1050 std::function<void()> emitSrc);
1051 void emitForeach(Emitter& e, ForEachStatementPtr fe);
1052 void emitForeachAwaitAs(Emitter& e, ForEachStatementPtr fe);
1053 void emitRestoreErrorReporting(Emitter& e, Id oldLevelLoc);
1054 void emitMakeUnitFatal(Emitter& e,
1055 const char* msg,
1056 FatalOp k = FatalOp::Runtime);
1058 // Emits a Jmp or IterBreak instruction to the specified target, freeing
1059 // the specified iterator variables. emitJump() cannot be used to leave a
1060 // try region, except if it jumps to the m_finallyLabel of the try region.
1061 void emitJump(Emitter& e, IterVec& iters, Label& target);
1063 // These methods handle the return, break, continue, and goto operations.
1064 // These methods are aware of try/finally blocks and foreach blocks and
1065 // will free iterators and jump to finally epilogues as appropriate.
1066 void emitReturn(Emitter& e, char sym, StatementPtr s);
1067 void emitBreak(Emitter& e, int depth, StatementPtr s);
1068 void emitContinue(Emitter& e, int depth, StatementPtr s);
1069 void emitGoto(Emitter& e, StringData* name, StatementPtr s);
1071 void emitYieldFrom(Emitter& e, ExpressionPtr exp);
1073 // Helper methods for emitting IterFree instructions
1074 void emitIterFree(Emitter& e, IterVec& iters);
1075 void emitIterFreeForReturn(Emitter& e);
1077 // A "finally epilogue" is a blob of bytecode that comes after an inline
1078 // copy of a "finally" clause body. Finally epilogues are used to ensure
1079 // that that the bodies of finally clauses are executed whenever a return,
1080 // break, continue, or goto operation jumps out of their corresponding
1081 // "try" blocks.
1082 void emitFinallyEpilogue(Emitter& e, Region* entry);
1083 void emitReturnTrampoline(Emitter& e, Region* entry,
1084 std::vector<Label*>& cases, char sym);
1085 void emitBreakTrampoline(Emitter& e, Region* entry,
1086 std::vector<Label*>& cases, int depth);
1087 void emitContinueTrampoline(Emitter& e, Region* entry,
1088 std::vector<Label*>& cases, int depth);
1089 void emitGotoTrampoline(Emitter& e, Region* entry,
1090 std::vector<Label*>& cases, StringData* name);
1092 // Returns true if VerifyRetType should be emitted before Ret for
1093 // the current function.
1094 bool shouldEmitVerifyRetType();
1096 Funclet* addFunclet(Thunklet* body);
1097 Funclet* addFunclet(StatementPtr stmt,
1098 Thunklet* body);
1099 Funclet* getFunclet(StatementPtr stmt);
1100 void emitFunclets(Emitter& e);
1102 struct FaultIterInfo {
1103 Id iterId;
1104 IterKind kind;
1107 void newFaultRegion(Offset start,
1108 Offset end,
1109 Label* entry,
1110 FaultIterInfo = FaultIterInfo { -1, KindOfIter });
1111 void newFaultRegion(StatementPtr stmt,
1112 Offset start,
1113 Offset end,
1114 Label* entry,
1115 FaultIterInfo = FaultIterInfo { -1, KindOfIter });
1116 void
1117 newFaultRegionAndFunclet(Offset start,
1118 Offset end,
1119 Thunklet* t,
1120 FaultIterInfo = FaultIterInfo { -1, KindOfIter });
1121 void
1122 newFaultRegionAndFunclet(StatementPtr stmt,
1123 Offset start,
1124 Offset end,
1125 Thunklet* t,
1126 FaultIterInfo = FaultIterInfo { -1, KindOfIter });
1128 void newFPIRegion(Offset start, Offset end, Offset fpOff);
1129 void copyOverCatchAndFaultRegions(FuncEmitter* fe);
1130 void copyOverFPIRegions(FuncEmitter* fe);
1131 void saveMaxStackCells(FuncEmitter* fe, int32_t stackPad);
1132 void finishFunc(Emitter& e, FuncEmitter* fe, int32_t stackPad);
1133 void initScalar(TypedValue& tvVal, ExpressionPtr val,
1134 folly::Optional<HeaderKind> ct = folly::none);
1135 bool requiresDeepInit(ExpressionPtr initExpr) const;
1137 void emitClassTraitPrecRule(PreClassEmitter* pce, TraitPrecStatementPtr rule);
1138 void emitClassTraitAliasRule(PreClassEmitter* pce,
1139 TraitAliasStatementPtr rule);
1140 void emitClassUseTrait(PreClassEmitter* pce, UseTraitStatementPtr useStmt);
1142 // Helper function for creating entries.
1143 RegionPtr createRegion(StatementPtr s, Region::Kind kind);
1144 // Enter/leave the passed in entry. Note that entries sometimes need be
1145 // to be constructed before they are entered, or need to be accessed
1146 // after they are left. This especially applies to constructs such
1147 // as loops and try blocks.
1148 void enterRegion(RegionPtr);
1149 void leaveRegion(RegionPtr);
1151 // Functions used for handling state IDs allocation.
1152 // FIXME (#3275259): This should be moved into global / func
1153 // body / fault funclet entries in order to optimize state
1154 // allocation. See the task description for more details.
1155 void registerControlTarget(ControlTarget* t);
1156 void unregisterControlTarget(ControlTarget* t);
1158 void registerReturn(StatementPtr s, Region* entry, char sym);
1159 void registerYieldAwait(ExpressionPtr e);
1160 ControlTargetPtr registerBreak(StatementPtr s, Region* entry, int depth,
1161 bool alloc);
1162 ControlTargetPtr registerContinue(StatementPtr s, Region* entry, int depth,
1163 bool alloc);
1164 ControlTargetPtr registerGoto(StatementPtr s, Region* entry,
1165 StringData* name, bool alloc);
1168 //=============================================================================
1169 // Emitter.
1171 #define InvariantViolation(...) do { \
1172 Logger::Warning(__VA_ARGS__); \
1173 Logger::Warning("Eval stack at the time of error: %s", \
1174 m_evalStack.pretty().c_str()); \
1175 assertx(false); \
1176 } while (0)
1179 * RAII guard for function creation.
1181 * This ensures that the eval stack's high water pointer is pointing
1182 * to the current function's maxStackCells (needed before we start
1183 * emitting bytecodes that manipulate the stack), and also that we
1184 * properly finish the function at the end of the scope.
1186 struct FuncFinisher {
1187 FuncFinisher(EmitterVisitor* ev, Emitter& e, FuncEmitter* fe,
1188 int32_t stackPad = 0)
1189 : m_ev(ev), m_e(e), m_fe(fe), m_stackPad(stackPad)
1191 assert(!ev->m_evalStack.m_actualStackHighWaterPtr ||
1192 ev->m_evalStack.m_actualStackHighWaterPtr == &fe->maxStackCells);
1193 ev->m_evalStack.m_actualStackHighWaterPtr = &fe->maxStackCells;
1196 ~FuncFinisher() {
1197 m_ev->finishFunc(m_e, m_fe, m_stackPad);
1200 void setStackPad(int32_t stackPad) {
1201 m_stackPad = stackPad;
1203 private:
1204 EmitterVisitor* m_ev;
1205 Emitter& m_e;
1206 FuncEmitter* m_fe;
1207 int32_t m_stackPad;
1210 // RAII guard for temporarily overriding an Emitter's location
1211 struct LocationGuard {
1212 LocationGuard(Emitter& e, const OptLocation& newLoc)
1213 : m_e(e), m_loc(e.getTempLocation()) {
1214 if (newLoc) m_e.setTempLocation(newLoc);
1216 ~LocationGuard() {
1217 m_e.setTempLocation(m_loc);
1220 private:
1221 Emitter& m_e;
1222 OptLocation m_loc;
1225 #define O(name, imm, pop, push, flags) \
1226 void Emitter::name(imm) { \
1227 auto const opcode = Op::name; \
1228 ITRACE(2, "{}\n", #name); \
1229 Trace::Indent indent; \
1230 ITRACE(3, "before: {}\n", m_ev.getEvalStack().pretty()); \
1231 /* Process opcode's effects on the EvalStack and emit it */ \
1232 Offset curPos UNUSED = getUnitEmitter().bcPos(); \
1234 Trace::Indent indent; \
1235 getEmitterVisitor().prepareEvalStack(); \
1236 char idxAPop UNUSED; \
1237 POP_##pop; \
1238 const int nIn UNUSED = COUNT_##pop; \
1239 POP_LA_##imm; \
1240 PUSH_##push; \
1241 getUnitEmitter().emitOp(Op##name); \
1242 IMPL_##imm; \
1244 ITRACE(3, "after: {}\n", m_ev.getEvalStack().pretty()); \
1245 auto& loc = m_tempLoc ? *m_tempLoc : m_node->getRange(); \
1246 auto UNUSED pc = m_ue.bc() + curPos; \
1247 ITRACE(2, "lines [{},{}] chars [{},{}]\n", \
1248 loc.line0, loc.line1, loc.char0, loc.char1); \
1249 /* Update various other metadata */ \
1250 getUnitEmitter().recordSourceLocation(loc, curPos); \
1251 if (flags & TF) { \
1252 getEmitterVisitor().restoreJumpTargetEvalStack(); \
1253 ITRACE(3, " jmp: {}\n", m_ev.getEvalStack().pretty()); \
1255 if (opcode == Op::FCall) getEmitterVisitor().recordCall(); \
1256 getEmitterVisitor().setPrevOpcode(opcode); \
1259 #define COUNT_NOV 0
1260 #define COUNT_ONE(t) 1
1261 #define COUNT_TWO(t1,t2) 2
1262 #define COUNT_THREE(t1,t2,t3) 3
1263 #define COUNT_FOUR(t1,t2,t3,t4) 4
1264 #define COUNT_MFINAL 0
1265 #define COUNT_F_MFINAL 0
1266 #define COUNT_C_MFINAL 0
1267 #define COUNT_V_MFINAL 0
1268 #define COUNT_FMANY 0
1269 #define COUNT_CVUMANY 0
1270 #define COUNT_CMANY 0
1271 #define COUNT_SMANY 0
1272 #define COUNT_IDX_A 0
1274 #define ONE(t) \
1275 DEC_##t a1
1276 #define TWO(t1, t2) \
1277 DEC_##t1 a1, DEC_##t2 a2
1278 #define THREE(t1, t2, t3) \
1279 DEC_##t1 a1, DEC_##t2 a2, DEC_##t3 a3
1280 #define FOUR(t1, t2, t3, t4) \
1281 DEC_##t1 a1, DEC_##t2 a2, DEC_##t3 a3, DEC_##t4 a4
1282 #define NA
1283 #define DEC_BLA std::vector<Label*>&
1284 #define DEC_SLA std::vector<StrOff>&
1285 #define DEC_ILA std::vector<IterPair>&
1286 #define DEC_IVA int32_t
1287 #define DEC_LA int32_t
1288 #define DEC_IA int32_t
1289 #define DEC_I64A int64_t
1290 #define DEC_DA double
1291 #define DEC_SA const StringData*
1292 #define DEC_RATA RepoAuthType
1293 #define DEC_AA ArrayData*
1294 #define DEC_BA Label&
1295 #define DEC_OA(type) type
1296 #define DEC_VSA std::vector<std::string>&
1297 #define DEC_KA MemberKey
1299 #define POP_NOV
1300 #define POP_ONE(t) \
1301 POP_##t(0)
1302 #define POP_TWO(t1, t2) \
1303 POP_##t1(0); \
1304 POP_##t2(1)
1305 #define POP_THREE(t1, t2, t3) \
1306 POP_##t1(0); \
1307 POP_##t2(1); \
1308 POP_##t3(2)
1309 #define POP_FOUR(t1, t2, t3, t4) \
1310 POP_##t1(0); \
1311 POP_##t2(1); \
1312 POP_##t3(2); \
1313 POP_##t4(3)
1314 #define POP_MFINAL \
1315 getEmitterVisitor().popEvalStackMMany()
1316 #define POP_F_MFINAL POP_MFINAL
1317 #define POP_C_MFINAL \
1318 getEmitterVisitor().popEvalStack(StackSym::C); \
1319 getEmitterVisitor().popEvalStackMMany()
1320 #define POP_V_MFINAL \
1321 getEmitterVisitor().popEvalStack(StackSym::V); \
1322 getEmitterVisitor().popEvalStackMMany()
1323 #define POP_FMANY \
1324 getEmitterVisitor().popEvalStackMany(a1, StackSym::F)
1325 #define POP_CVUMANY \
1326 getEmitterVisitor().popEvalStackCVMany(a1)
1327 #define POP_CMANY \
1328 getEmitterVisitor().popEvalStackMany(a1, StackSym::C)
1329 #define POP_SMANY \
1330 getEmitterVisitor().popEvalStackMany(a1.size(), StackSym::C)
1331 #define POP_IDX_A \
1332 idxAPop = getEmitterVisitor().getEvalStack().top(); \
1333 if (a2 == 1) getEmitterVisitor().popEvalStackCVMany(1); \
1334 getEmitterVisitor().popEvalStack(StackSym::A)
1336 #define POP_CV(i) getEmitterVisitor().popEvalStack(StackSym::C)
1337 #define POP_VV(i) getEmitterVisitor().popEvalStack(StackSym::V)
1338 #define POP_AV(i) getEmitterVisitor().popEvalStack(StackSym::A)
1339 #define POP_RV(i) getEmitterVisitor().popEvalStack(StackSym::R)
1340 #define POP_FV(i) getEmitterVisitor().popEvalStack(StackSym::F)
1342 // Pop of virtual "locs" on the stack that turn into immediates.
1343 #define POP_LA_ONE(t) \
1344 POP_LA_##t(nIn)
1345 #define POP_LA_TWO(t1, t2) \
1346 POP_LA_##t1(nIn); \
1347 POP_LA_##t2(nIn)
1348 #define POP_LA_THREE(t1, t2, t3) \
1349 POP_LA_##t1(nIn); \
1350 POP_LA_##t2(nIn); \
1351 POP_LA_##t3(nIn)
1352 #define POP_LA_FOUR(t1, t2, t3, t4) \
1353 POP_LA_##t1(nIn); \
1354 POP_LA_##t2(nIn); \
1355 POP_LA_##t3(nIn); \
1356 POP_LA_##t4(nIn)
1358 #define POP_LA_NA
1359 #define POP_LA_BLA(i)
1360 #define POP_LA_SLA(i)
1361 #define POP_LA_ILA(i)
1362 #define POP_LA_IVA(i)
1363 #define POP_LA_IA(i)
1364 #define POP_LA_I64A(i)
1365 #define POP_LA_DA(i)
1366 #define POP_LA_SA(i)
1367 #define POP_LA_RATA(i)
1368 #define POP_LA_AA(i)
1369 #define POP_LA_BA(i)
1370 #define POP_LA_IMPL(x)
1371 #define POP_LA_OA(i) POP_LA_IMPL
1372 #define POP_LA_VSA(i)
1373 #define POP_LA_KA(i)
1375 #define POP_LA_LA(i) \
1376 getEmitterVisitor().popSymbolicLocal(opcode)
1378 #define PUSH_NOV
1379 #define PUSH_ONE(t) \
1380 PUSH_##t
1381 #define PUSH_TWO(t1, t2) \
1382 PUSH_##t2; \
1383 PUSH_##t1
1384 #define PUSH_THREE(t1, t2, t3) \
1385 PUSH_##t3; \
1386 PUSH_##t2; \
1387 PUSH_##t1
1388 #define PUSH_FOUR(t1, t2, t3, t4) \
1389 PUSH_##t4; \
1390 PUSH_##t3; \
1391 PUSH_##t2; \
1392 PUSH_##t1
1393 #define PUSH_INS_1(t) PUSH_INS_1_##t
1394 #define PUSH_INS_2(t) PUSH_INS_2_##t
1396 #define PUSH_CV getEmitterVisitor().pushEvalStack(StackSym::C)
1397 #define PUSH_UV PUSH_CV
1398 #define PUSH_CUV PUSH_CV
1399 #define PUSH_VV getEmitterVisitor().pushEvalStack(StackSym::V)
1400 #define PUSH_AV getEmitterVisitor().pushEvalStack(StackSym::A)
1401 #define PUSH_RV getEmitterVisitor().pushEvalStack(StackSym::R)
1402 #define PUSH_FV getEmitterVisitor().pushEvalStack(StackSym::F)
1404 #define PUSH_INS_1_CV \
1405 getEmitterVisitor().getEvalStack().insertAt(1, StackSym::C);
1406 #define PUSH_INS_1_AV \
1407 getEmitterVisitor().getEvalStack().insertAt(1, StackSym::A);
1409 #define PUSH_INS_2_CV \
1410 getEmitterVisitor().getEvalStack().insertAt(2, StackSym::C);
1412 #define PUSH_IDX_A \
1413 if (a2 == 1) getEmitterVisitor().pushEvalStack(idxAPop);
1415 #define IMPL_NA
1416 #define IMPL_ONE(t) \
1417 IMPL1_##t
1418 #define IMPL_TWO(t1, t2) \
1419 IMPL1_##t1; \
1420 IMPL2_##t2
1421 #define IMPL_THREE(t1, t2, t3) \
1422 IMPL1_##t1; \
1423 IMPL2_##t2; \
1424 IMPL3_##t3
1425 #define IMPL_FOUR(t1, t2, t3, t4) \
1426 IMPL1_##t1; \
1427 IMPL2_##t2; \
1428 IMPL3_##t3; \
1429 IMPL4_##t4
1431 #define IMPL_BLA(var) do { \
1432 getUnitEmitter().emitInt32(var.size()); \
1433 for (unsigned int i = 0; i < var.size(); ++i) { \
1434 IMPL_BA(*var[i]); \
1436 } while (0)
1437 #define IMPL1_BLA IMPL_BLA(a1)
1438 #define IMPL2_BLA IMPL_BLA(a2)
1439 #define IMPL3_BLA IMPL_BLA(a3)
1440 #define IMPL4_BLA IMPL_BLA(a4)
1442 #define IMPL_ILA(var) do { \
1443 auto& ue = getUnitEmitter(); \
1444 ue.emitInt32(var.size()); \
1445 for (auto& i : var) { \
1446 ue.emitInt32(i.kind); \
1447 ue.emitInt32(i.id); \
1449 } while(0)
1450 #define IMPL1_ILA IMPL_ILA(a1)
1451 #define IMPL2_ILA IMPL_ILA(a2)
1452 #define IMPL3_ILA IMPL_ILA(a3)
1453 #define IMPL4_ILA IMPL_ILA(a4)
1455 #define IMPL_SLA(var) do { \
1456 auto& ue = getUnitEmitter(); \
1457 ue.emitInt32(var.size()); \
1458 for (auto& i : var) { \
1459 ue.emitInt32(i.str); \
1460 IMPL_BA(*i.dest); \
1462 } while (0)
1463 #define IMPL1_SLA IMPL_SLA(a1)
1464 #define IMPL2_SLA IMPL_SLA(a2)
1465 #define IMPL3_SLA IMPL_SLA(a3)
1467 #define IMPL_VSA(var) do { \
1468 auto n = var.size(); \
1469 getUnitEmitter().emitInt32(n); \
1470 for (size_t i = 0; i < n; ++i) { \
1471 IMPL_SA((HPHP::String(var[i])).get()); \
1473 } while (0)
1474 #define IMPL1_VSA IMPL_VSA(a1)
1475 #define IMPL2_VSA IMPL_VSA(a2)
1476 #define IMPL3_VSA IMPL_VSA(a3)
1477 #define IMPL4_VSA IMPL_VSA(a4)
1479 #define IMPL_IVA(var) do { \
1480 getUnitEmitter().emitIVA(var); \
1481 } while (0)
1482 #define IMPL1_IVA IMPL_IVA(a1)
1483 #define IMPL2_IVA IMPL_IVA(a2)
1484 #define IMPL3_IVA IMPL_IVA(a3)
1485 #define IMPL4_IVA IMPL_IVA(a4)
1487 #define IMPL1_LA IMPL_IVA(a1)
1488 #define IMPL2_LA IMPL_IVA(a2)
1489 #define IMPL3_LA IMPL_IVA(a3)
1490 #define IMPL4_LA IMPL_IVA(a4)
1492 #define IMPL1_IA IMPL_IVA(a1)
1493 #define IMPL2_IA IMPL_IVA(a2)
1494 #define IMPL3_IA IMPL_IVA(a3)
1495 #define IMPL4_IA IMPL_IVA(a4)
1497 #define IMPL_I64A(var) getUnitEmitter().emitInt64(var)
1498 #define IMPL1_I64A IMPL_I64A(a1)
1499 #define IMPL2_I64A IMPL_I64A(a2)
1500 #define IMPL3_I64A IMPL_I64A(a3)
1501 #define IMPL4_I64A IMPL_I64A(a4)
1503 #define IMPL_SA(var) \
1504 getUnitEmitter().emitInt32(getUnitEmitter().mergeLitstr(var))
1505 #define IMPL1_SA IMPL_SA(a1)
1506 #define IMPL2_SA IMPL_SA(a2)
1507 #define IMPL3_SA IMPL_SA(a3)
1508 #define IMPL4_SA IMPL_SA(a4)
1510 // Emitting RATAs isn't supported here right now. (They're only
1511 // created in hhbbc.)
1512 #define IMPL_RATA(var) not_reached()
1513 #define IMPL1_RATA IMPL_RATA(a1)
1514 #define IMPL2_RATA IMPL_RATA(a2)
1515 #define IMPL3_RATA IMPL_RATA(a3)
1516 #define IMPL4_RATA IMPL_RATA(a4)
1518 #define IMPL_AA(var) \
1519 getUnitEmitter().emitInt32(getUnitEmitter().mergeArray(var))
1520 #define IMPL1_AA IMPL_AA(a1)
1521 #define IMPL2_AA IMPL_AA(a2)
1522 #define IMPL3_AA IMPL_AA(a3)
1523 #define IMPL4_AA IMPL_AA(a4)
1525 #define IMPL_DA(var) getUnitEmitter().emitDouble(var)
1526 #define IMPL1_DA IMPL_DA(a1)
1527 #define IMPL2_DA IMPL_DA(a2)
1528 #define IMPL3_DA IMPL_DA(a3)
1529 #define IMPL4_DA IMPL_DA(a4)
1531 #define IMPL_BA(var) \
1532 if ((var).getAbsoluteOffset() == InvalidAbsoluteOffset) { \
1533 /* For forward jumps, we store information about the */ \
1534 /* current instruction in the Label. When the Label is */ \
1535 /* set, it will fix up any instructions that reference */ \
1536 /* it, and then it will call recordJumpTarget */ \
1537 (var).bind(getEmitterVisitor(), curPos, getUnitEmitter().bcPos()); \
1538 } else { \
1539 /* For backward jumps, we simply call recordJumpTarget */ \
1540 getEmitterVisitor().recordJumpTarget((var).getAbsoluteOffset()); \
1542 getUnitEmitter().emitInt32((var).getAbsoluteOffset() - curPos);
1543 #define IMPL1_BA IMPL_BA(a1)
1544 #define IMPL2_BA IMPL_BA(a2)
1545 #define IMPL3_BA IMPL_BA(a3)
1546 #define IMPL4_BA IMPL_BA(a4)
1548 #define IMPL_OA(var) getUnitEmitter().emitByte(static_cast<uint8_t>(var))
1549 #define IMPL1_OA(type) IMPL_OA(a1)
1550 #define IMPL2_OA(type) IMPL_OA(a2)
1551 #define IMPL3_OA(type) IMPL_OA(a3)
1552 #define IMPL4_OA(type) IMPL_OA(a4)
1554 #define IMPL_KA(var) encode_member_key(var, getUnitEmitter())
1555 #define IMPL1_KA IMPL_KA(a1)
1556 #define IMPL2_KA IMPL_KA(a2)
1557 #define IMPL3_KA IMPL_KA(a3)
1558 #define IMPL4_KA IMPL_KA(a4)
1560 OPCODES
1562 #undef O
1563 #undef ONE
1564 #undef TWO
1565 #undef THREE
1566 #undef FOUR
1567 #undef NA
1568 #undef DEC_IVA
1569 #undef DEC_LA
1570 #undef DEC_IA
1571 #undef DEC_I64A
1572 #undef DEC_DA
1573 #undef DEC_SA
1574 #undef DEC_RATA
1575 #undef DEC_AA
1576 #undef DEC_BA
1577 #undef DEC_OA
1578 #undef DEC_KA
1579 #undef POP_NOV
1580 #undef POP_ONE
1581 #undef POP_TWO
1582 #undef POP_THREE
1583 #undef POP_FOUR
1584 #undef POP_MFINAL
1585 #undef POP_F_MFINAL
1586 #undef POP_C_MFINAL
1587 #undef POP_V_MFINAL
1588 #undef POP_CV
1589 #undef POP_VV
1590 #undef POP_HV
1591 #undef POP_AV
1592 #undef POP_RV
1593 #undef POP_FV
1594 #undef POP_LREST
1595 #undef POP_FMANY
1596 #undef POP_CVUMANY
1597 #undef POP_CMANY
1598 #undef POP_SMANY
1599 #undef POP_IDX_A
1600 #undef POP_LA_ONE
1601 #undef POP_LA_TWO
1602 #undef POP_LA_THREE
1603 #undef POP_LA_FOUR
1604 #undef POP_LA_NA
1605 #undef POP_LA_IVA
1606 #undef POP_LA_IA
1607 #undef POP_LA_I64A
1608 #undef POP_LA_DA
1609 #undef POP_LA_SA
1610 #undef POP_LA_RATA
1611 #undef POP_LA_AA
1612 #undef POP_LA_BA
1613 #undef POP_LA_IMPL
1614 #undef POP_LA_OA
1615 #undef POP_LA_LA
1616 #undef POP_LA_KA
1617 #undef PUSH_NOV
1618 #undef PUSH_ONE
1619 #undef PUSH_TWO
1620 #undef PUSH_THREE
1621 #undef PUSH_FOUR
1622 #undef PUSH_CV
1623 #undef PUSH_UV
1624 #undef PUSH_CUV
1625 #undef PUSH_VV
1626 #undef PUSH_HV
1627 #undef PUSH_AV
1628 #undef PUSH_RV
1629 #undef PUSH_FV
1630 #undef PUSH_IDX_A
1631 #undef IMPL_ONE
1632 #undef IMPL_TWO
1633 #undef IMPL_THREE
1634 #undef IMPL_FOUR
1635 #undef IMPL_NA
1636 #undef IMPL_BLA
1637 #undef IMPL1_BLA
1638 #undef IMPL2_BLA
1639 #undef IMPL3_BLA
1640 #undef IMPL4_BLA
1641 #undef IMPL_SLA
1642 #undef IMPL1_SLA
1643 #undef IMPL2_SLA
1644 #undef IMPL3_SLA
1645 #undef IMPL4_SLA
1646 #undef IMPL_ILA
1647 #undef IMPL1_ILA
1648 #undef IMPL2_ILA
1649 #undef IMPL3_ILA
1650 #undef IMPL4_ILA
1651 #undef IMPL_IVA
1652 #undef IMPL1_IVA
1653 #undef IMPL2_IVA
1654 #undef IMPL3_IVA
1655 #undef IMPL4_IVA
1656 #undef IMPL1_LA
1657 #undef IMPL2_LA
1658 #undef IMPL3_LA
1659 #undef IMPL4_LA
1660 #undef IMPL1_IA
1661 #undef IMPL2_IA
1662 #undef IMPL3_IA
1663 #undef IMPL4_IA
1664 #undef IMPL_I64A
1665 #undef IMPL1_I64A
1666 #undef IMPL2_I64A
1667 #undef IMPL3_I64A
1668 #undef IMPL4_I64A
1669 #undef IMPL_DA
1670 #undef IMPL1_DA
1671 #undef IMPL2_DA
1672 #undef IMPL3_DA
1673 #undef IMPL4_DA
1674 #undef IMPL_SA
1675 #undef IMPL1_SA
1676 #undef IMPL2_SA
1677 #undef IMPL3_SA
1678 #undef IMPL4_SA
1679 #undef IMPL_RATA
1680 #undef IMPL1_RATA
1681 #undef IMPL2_RATA
1682 #undef IMPL3_RATA
1683 #undef IMPL4_RATA
1684 #undef IMPL_AA
1685 #undef IMPL1_AA
1686 #undef IMPL2_AA
1687 #undef IMPL3_AA
1688 #undef IMPL4_AA
1689 #undef IMPL_BA
1690 #undef IMPL1_BA
1691 #undef IMPL2_BA
1692 #undef IMPL3_BA
1693 #undef IMPL4_BA
1694 #undef IMPL_OA
1695 #undef IMPL1_OA
1696 #undef IMPL2_OA
1697 #undef IMPL3_OA
1698 #undef IMPL4_OA
1699 #undef IMPL_KA
1700 #undef IMPL1_KA
1701 #undef IMPL2_KA
1702 #undef IMPL3_KA
1703 #undef IMPL4_KA
1705 static void checkJmpTargetEvalStack(const SymbolicStack& source,
1706 const SymbolicStack& dest) {
1707 if (source.size() != dest.size()) {
1708 Logger::FWarning("Emitter detected a point in the bytecode where the "
1709 "depth of the stack is not the same for all possible "
1710 "control flow paths. source size: {}. dest size: {}",
1711 source.size(),
1712 dest.size());
1713 Logger::Warning("src stack : %s", source.pretty().c_str());
1714 Logger::Warning("dest stack: %s", dest.pretty().c_str());
1715 assertx(false);
1716 return;
1719 for (unsigned int i = 0; i < source.size(); ++i) {
1720 char flavor = StackSym::GetSymFlavor(source.get(i));
1721 bool matches = source.get(i) == dest.get(i) &&
1722 (flavor != StackSym::L || source.getLoc(i) == dest.getLoc(i)) &&
1723 (flavor != StackSym::T || source.getName(i) == dest.getName(i)) &&
1724 (flavor != StackSym::I || source.getInt(i) == dest.getInt(i));
1725 if (!matches) {
1726 Logger::Warning("Emitter detected a point in the bytecode where the "
1727 "symbolic flavor of a slot on the stack is not the same "
1728 "for all possible control flow paths");
1729 Logger::Warning("src stack : %s", source.pretty().c_str());
1730 Logger::Warning("dest stack: %s", dest.pretty().c_str());
1731 assert(false);
1732 return;
1737 std::string SymbolicStack::SymEntry::pretty() const {
1738 std::string ret;
1739 ret += StackSym::ToString(sym);
1740 char flavor = StackSym::GetSymFlavor(sym);
1741 if (flavor == StackSym::L || flavor == StackSym::I) {
1742 folly::toAppend(':', intval, &ret);
1743 } else if (flavor == StackSym::T && name) {
1744 folly::toAppend(':', name->data(), &ret);
1746 return ret;
1749 std::string SymbolicStack::pretty() const {
1750 std::ostringstream out;
1751 out << "[" << std::hex;
1752 size_t j = 0;
1753 auto sep = "";
1754 for (size_t i = 0; i < m_symStack.size(); ++i) {
1755 out << sep;
1756 sep = " ";
1757 while (j < m_actualStack.size() && m_actualStack[j] < int(i)) {
1758 ++j;
1760 if (j < m_actualStack.size() && m_actualStack[j] == int(i)) {
1761 out << "*";
1763 out << m_symStack[i].pretty();
1765 out << ']';
1766 return out.str();
1769 void SymbolicStack::updateHighWater() {
1770 *m_actualStackHighWaterPtr =
1771 std::max(*m_actualStackHighWaterPtr,
1772 static_cast<int>(m_actualStack.size() + m_fdescCount));
1775 void SymbolicStack::push(char sym) {
1776 if (!StackSym::IsSymbolic(sym)) {
1777 m_actualStack.push_back(m_symStack.size());
1778 updateHighWater();
1780 m_symStack.push_back(SymEntry(sym));
1781 ITRACE(4, "push: {}\n", m_symStack.back().pretty());
1784 void SymbolicStack::pop() {
1785 // TODO(drew): assert eval stack unknown is false?
1786 assert(!m_symStack.empty());
1787 char sym = m_symStack.back().sym;
1788 char flavor = StackSym::GetSymFlavor(sym);
1789 if (StackSym::GetMarker(sym) != StackSym::W &&
1790 flavor != StackSym::L && flavor != StackSym::T && flavor != StackSym::I &&
1791 flavor != StackSym::H) {
1792 assert(!m_actualStack.empty());
1793 m_actualStack.pop_back();
1795 ITRACE(4, "pop: {}\n", m_symStack.back().pretty());
1796 m_symStack.pop_back();
1799 char SymbolicStack::top() const {
1800 assert(!m_symStack.empty());
1801 return m_symStack.back().sym;
1804 char SymbolicStack::get(int index) const {
1805 assert(index >= 0 && index < (int)m_symStack.size());
1806 return m_symStack[index].sym;
1809 const StringData* SymbolicStack::getName(int index) const {
1810 assert(index >= 0 && index < (int)m_symStack.size());
1811 return m_symStack[index].name;
1814 const StringData* SymbolicStack::getClsName(int index) const {
1815 assert(index >= 0 && index < (int)m_symStack.size());
1816 return m_symStack[index].className;
1819 bool SymbolicStack::isCls(int index) const {
1820 assert(index >= 0 && index < (int)m_symStack.size());
1821 return m_symStack[index].className != nullptr;
1824 void SymbolicStack::setString(const StringData* s) {
1825 assert(m_symStack.size());
1826 SymEntry& se = m_symStack.back();
1827 assert(!se.name || se.name == s);
1828 se.name = s;
1831 void SymbolicStack::setKnownCls(const StringData* s, bool nonNull) {
1832 assert(m_symStack.size());
1833 SymEntry& se = m_symStack.back();
1834 assert(!se.className || se.className == s);
1835 se.className = s;
1838 void SymbolicStack::setInt(int64_t v) {
1839 assert(m_symStack.size());
1840 m_symStack.back().intval = v;
1843 void SymbolicStack::cleanTopMeta() {
1844 SymEntry& se = m_symStack.back();
1845 se.clsBaseType = CLS_INVALID;
1846 se.name = nullptr;
1849 void SymbolicStack::setClsBaseType(ClassBaseType type) {
1850 assert(!m_symStack.empty());
1851 m_symStack.back().clsBaseType = type;
1854 void SymbolicStack::setUnnamedLocal(int index,
1855 int localId,
1856 Offset startOff) {
1857 assert(size_t(index) < m_symStack.size());
1858 assert(m_symStack[index].sym == StackSym::K);
1859 assert(m_symStack[index].clsBaseType == CLS_UNNAMED_LOCAL);
1860 m_symStack[index].intval = localId;
1861 m_symStack[index].unnamedLocalStart = startOff;
1864 void SymbolicStack::set(int index, char sym) {
1865 assert(index >= 0 && index < (int)m_symStack.size());
1866 // XXX Add assert in debug build to make sure W is not getting
1867 // written or overwritten by something else
1868 m_symStack[index].sym = sym;
1869 ITRACE(4, " set: {} -> {}\n", index, m_symStack[index].pretty());
1872 size_t SymbolicStack::size() const {
1873 return m_symStack.size();
1876 size_t SymbolicStack::actualSize() const {
1877 return m_actualStack.size();
1880 bool SymbolicStack::empty() const {
1881 return m_symStack.empty();
1884 void SymbolicStack::clear() {
1885 m_symStack.clear();
1886 m_actualStack.clear();
1887 m_fdescCount = 0;
1890 void SymbolicStack::consumeBelowTop(int depth) {
1891 if (int(m_symStack.size()) < depth + 1) {
1892 Logger::Warning(
1893 "Emitter tried to consumeBelowTop() when the symbolic "
1894 "stack did not have enough elements in it.");
1895 assert(false);
1896 return;
1898 assert(int(m_symStack.size()) >= depth + 1);
1899 int index = m_symStack.size() - depth - 1;
1900 m_symStack.erase(m_symStack.begin() + index);
1903 * Update any indexes into the actual stack that pointed to or past
1904 * this element.
1906 * (In practice they should all be past---we don't currently ever
1907 * remove below the top for actual stack elements.)
1909 for (size_t i = 0; i < m_actualStack.size(); ++i) {
1910 if (m_actualStack[i] >= index) {
1911 --m_actualStack[i];
1916 int SymbolicStack::getActualPos(int vpos) const {
1917 assert(vpos >= 0 && vpos < int(m_symStack.size()));
1918 assert(!m_actualStack.empty());
1919 for (int j = int(m_actualStack.size()) - 1; j >= 0; --j) {
1920 if (m_actualStack[j] == vpos) {
1921 return j;
1924 not_reached();
1927 char SymbolicStack::getActual(int index) const {
1928 assert(index >= 0 && index < (int)m_actualStack.size());
1929 return get(m_actualStack[index]);
1932 void SymbolicStack::setActual(int index, char sym) {
1933 assert(index >= 0 && index < (int)m_actualStack.size());
1934 set(m_actualStack[index], sym);
1937 SymbolicStack::ClassBaseType
1938 SymbolicStack::getClsBaseType(int index) const {
1939 assert(m_symStack.size() > size_t(index));
1940 assert(m_symStack[index].sym == StackSym::K);
1941 assert(m_symStack[index].clsBaseType != CLS_INVALID);
1942 return m_symStack[index].clsBaseType;
1945 int SymbolicStack::getLoc(int index) const {
1946 assert(m_symStack.size() > size_t(index));
1947 assert(StackSym::GetSymFlavor(m_symStack[index].sym) == StackSym::L ||
1948 m_symStack[index].clsBaseType == CLS_NAMED_LOCAL ||
1949 m_symStack[index].clsBaseType == CLS_UNNAMED_LOCAL);
1950 assert(m_symStack[index].intval != -1);
1951 return m_symStack[index].intval;
1954 int64_t SymbolicStack::getInt(int index) const {
1955 assert(m_symStack.size() > size_t(index));
1956 assert(StackSym::GetSymFlavor(m_symStack[index].sym) == StackSym::I);
1957 return m_symStack[index].intval;
1960 Offset SymbolicStack::getUnnamedLocStart(int index) const {
1961 assert(m_symStack.size() > size_t(index));
1962 assert(m_symStack[index].sym == StackSym::K);
1963 assert(m_symStack[index].clsBaseType == CLS_UNNAMED_LOCAL);
1964 return m_symStack[index].unnamedLocalStart;
1967 // Insert an element in the actual stack at the specified depth of the
1968 // actual stack.
1969 void SymbolicStack::insertAt(int depth, char sym) {
1970 assert(depth <= sizeActual() && depth > 0);
1971 int virtIdx = m_actualStack[sizeActual() - depth];
1973 m_symStack.insert(m_symStack.begin() + virtIdx, SymEntry(sym));
1974 m_actualStack.insert(m_actualStack.end() - depth, virtIdx);
1976 for (size_t i = sizeActual() - depth + 1; i < m_actualStack.size(); ++i) {
1977 ++m_actualStack[i];
1981 int SymbolicStack::sizeActual() const {
1982 return m_actualStack.size();
1985 void SymbolicStack::pushFDesc() {
1986 m_fdescCount += kNumActRecCells;
1987 updateHighWater();
1990 void SymbolicStack::popFDesc() {
1991 m_fdescCount -= kNumActRecCells;
1994 void Label::set(Emitter& e, bool emitNopAtEntry /* = true */) {
1995 auto const off = e.getUnitEmitter().bcPos();
1996 if (isSet()) {
1997 InvariantViolation(
1998 "Label::set was called more than once on the same "
1999 "Label; originally set to %d; now %d",
2000 m_off,
2001 off);
2002 return;
2004 EmitterVisitor& ev = e.getEmitterVisitor();
2005 if (emitNopAtEntry && ev.getFuncEmitter()->base == off) {
2006 // Make sure there are no jumps to the base of functions.
2007 // The JIT relies on this in some analysis.
2008 e.getUnitEmitter().emitOp(OpEntryNop);
2010 m_off = e.getUnitEmitter().bcPos();
2011 // Fix up any forward jumps that reference to this Label
2012 for (std::vector<std::pair<Offset, Offset> >::const_iterator it =
2013 m_emittedOffs.begin(); it != m_emittedOffs.end(); ++it) {
2014 e.getUnitEmitter().emitInt32(m_off - it->first, it->second);
2016 if (!m_emittedOffs.empty()) {
2017 // If there were forward jumps that referenced this Label,
2018 // compare the the eval stack from the first foward jump we
2019 // saw with the current eval stack
2020 if (!ev.evalStackIsUnknown()) {
2021 checkJmpTargetEvalStack(m_evalStack, ev.getEvalStack());
2022 } else {
2023 // Assume the current eval stack matches that of the forward branch
2024 ITRACE(3, "bind: {}\n", m_evalStack.pretty());
2025 ev.setEvalStack(m_evalStack);
2027 // Fix up the EmitterVisitor's table of jump targets
2028 ev.recordJumpTarget(m_off, ev.getEvalStack());
2029 } else {
2030 // There were no forward jumps that referenced this label
2031 ev.prepareEvalStack();
2032 // Fix up the EmitterVisitor's table of jump targets
2033 ev.recordJumpTarget(m_off, ev.getEvalStack());
2037 bool Label::isUsed() {
2038 return (m_off != InvalidAbsoluteOffset || !m_emittedOffs.empty());
2041 void Label::bind(EmitterVisitor& ev, Offset instrAddr, Offset offAddr) {
2042 if (m_off != InvalidAbsoluteOffset) {
2043 InvariantViolation("Label::bind was called on a Label that has already "
2044 "been set to %d",
2045 m_off);
2046 return;
2048 bool labelHasEvalStack = !m_emittedOffs.empty();
2049 m_emittedOffs.push_back(std::pair<Offset, Offset>(instrAddr, offAddr));
2050 if (labelHasEvalStack) {
2051 checkJmpTargetEvalStack(m_evalStack, ev.getEvalStack());
2052 } else {
2053 m_evalStack = ev.getEvalStack();
2057 struct FPIRegionRecorder {
2058 FPIRegionRecorder(EmitterVisitor* ev, UnitEmitter& ue, SymbolicStack& stack,
2059 Offset start)
2060 : m_ev(ev), m_ue(ue), m_stack(stack), m_fpOff(m_stack.sizeActual()),
2061 m_start(start) {
2062 m_stack.pushFDesc();
2064 ~FPIRegionRecorder() {
2065 m_stack.popFDesc();
2066 m_ev->newFPIRegion(m_start, m_ue.bcPos(), m_fpOff);
2068 private:
2069 EmitterVisitor* m_ev;
2070 UnitEmitter& m_ue;
2071 SymbolicStack& m_stack;
2072 int m_fpOff;
2073 Offset m_start;
2076 //=============================================================================
2077 // ControlTarget.
2079 const int ControlTarget::k_unsetState = -1;
2081 ControlTarget::ControlTarget(EmitterVisitor* router)
2082 : m_visitor(router), m_label(), m_state(k_unsetState) {
2083 assert(m_visitor != nullptr);
2086 ControlTarget::~ControlTarget() {
2087 // The scope of states used in finally router is controled
2088 // using shared pointer refcounting. State numbers can be reused once
2089 // all the references are released.
2090 if (isRegistered()) {
2091 m_visitor->unregisterControlTarget(this);
2092 m_state = k_unsetState;
2096 bool ControlTarget::isRegistered() {
2097 return m_state != k_unsetState;
2100 //=============================================================================
2101 // Region.
2103 Region::Region(Region::Kind kind, RegionPtr parent)
2104 : m_kind(kind),
2105 m_iterId(-1),
2106 m_iterKind(KindOfIter),
2107 m_parent(parent) {
2110 void
2111 EmitterVisitor::registerReturn(StatementPtr s, Region* region, char sym) {
2112 ControlTargetPtr t;
2113 Region* r;
2114 for (r = region; true; r = r->m_parent.get()) {
2115 assert(r);
2116 if (r->isFinally()) {
2117 throw EmitterVisitor::IncludeTimeFatalException(s,
2118 "Return inside a finally block is not supported");
2120 if (r->m_returnTargets.count(sym)) {
2121 // We registered the control target before. Just return the existing one.
2122 t = r->m_returnTargets[sym].target;
2123 break;
2125 // Haven't registered the control target with region r yet.
2126 if (r->m_parent.get() == nullptr) {
2127 // Top of the region hierarchy - allocate a fresh control target.
2128 t = std::make_shared<ControlTarget>(this);
2129 r = r->m_parent.get();
2130 break;
2133 assert(t != nullptr);
2134 if (!t->isRegistered()) {
2135 registerControlTarget(t.get());
2137 // For all entries we visited that did not have this control target in
2138 // m_returnTargets, add this control target to these entries' m_returnTargets
2139 // fields as appropriate.
2140 Region* end = r;
2141 for (r = region; r != end; r = r->m_parent.get()) {
2142 r->m_returnTargets[sym] = ControlTargetInfo(t, r->isTryFinally());
2146 ControlTargetPtr
2147 EmitterVisitor::registerGoto(StatementPtr s, Region* region, StringData* name,
2148 bool alloc) {
2149 ControlTargetPtr t;
2150 Region* r;
2151 for (r = region; true; r = r->m_parent.get()) {
2152 assert(r);
2153 if (r->m_gotoTargets.count(name)) {
2154 // We registered the control target before. Just return the existing one.
2155 t = r->m_gotoTargets[name].target;
2156 if (alloc && r->isTryFinally()) {
2157 r->m_gotoTargets[name].used = true;
2159 break;
2161 // Haven't registered the control target in this region yet.
2162 if (r->m_parent.get() == nullptr) {
2163 // Top of the region hierarchy - allocate a fresh control target.
2164 t = std::make_shared<ControlTarget>(this);
2165 r = r->m_parent.get();
2166 break;
2169 assert(t != nullptr);
2170 if (alloc && !t->isRegistered()) {
2171 registerControlTarget(t.get());
2173 // For all entries we visited that did not have this control target in
2174 // m_gotoTargets, add this control target to these entries' m_gotoTargets
2175 // fields as appropriate.
2176 Region* end = r;
2177 for (r = region; r != end; r = r->m_parent.get()) {
2178 r->m_gotoTargets[name] = ControlTargetInfo(t, alloc && r->isTryFinally());
2180 return t;
2183 void EmitterVisitor::registerYieldAwait(ExpressionPtr e) {
2184 Region* region = m_regions.back().get();
2185 for (; region; region = region->m_parent.get()) {
2186 if (region->isFinally()) {
2187 throw EmitterVisitor::IncludeTimeFatalException(e,
2188 "Yield expression inside a finally block is not supported");
2193 ControlTargetPtr
2194 EmitterVisitor::registerBreak(StatementPtr s, Region* region, int depth,
2195 bool alloc) {
2196 ControlTargetPtr t;
2197 assert(depth >= 1);
2198 int d = depth;
2199 Region* r;
2200 for (r = region; true; r = r->m_parent.get()) {
2201 assert(r);
2202 if (r->isFinally()) {
2203 throw EmitterVisitor::IncludeTimeFatalException(s,
2204 "Break jump is not allowed to leave a finally block");
2206 if (r->m_breakTargets.count(d)) {
2207 // We registered the control target before. Just return the existing one.
2208 t = r->m_breakTargets[d].target;
2209 if (alloc && r->isTryFinally()) {
2210 r->m_breakTargets[d].used = true;
2212 break;
2214 if (r->m_kind != Region::Kind::LoopOrSwitch) {
2215 continue;
2217 if (d == 1) {
2218 // We should never reach this case if alloc == true, since the loop or
2219 // switch should have registered its break target in advance
2220 assert(!alloc);
2221 // If this is a loop, and depth is one, just allocate a fresh
2222 // control target, since there are no more entries to delegate to.
2223 t = std::make_shared<ControlTarget>(this);
2224 r = r->m_parent.get();
2225 break;
2227 // Otherwise, delegate to the parent. One break level has been
2228 // taken care of by this region.
2229 --d;
2231 assert(t != nullptr);
2232 if (alloc) {
2233 if (!t->isRegistered()) {
2234 registerControlTarget(t.get());
2237 // For all of the entries that did not have this control target in
2238 // m_breakTargets, add this control target to these entries' m_breakTargets
2239 // fields as appropriate.
2240 Region* end = r;
2241 for (r = region; r != end; r = r->m_parent.get()) {
2242 r->m_breakTargets[depth] =
2243 ControlTargetInfo(t, alloc && r->isTryFinally());
2244 if (r->m_kind == Region::Kind::LoopOrSwitch) {
2245 --depth;
2248 return t;
2251 ControlTargetPtr
2252 EmitterVisitor::registerContinue(StatementPtr s, Region* region, int depth,
2253 bool alloc) {
2254 ControlTargetPtr t;
2255 assert(depth >= 1);
2256 int d = depth;
2257 Region* r;
2258 for (r = region; true; r = r->m_parent.get()) {
2259 assert(r);
2260 if (r->isFinally()) {
2261 throw EmitterVisitor::IncludeTimeFatalException(s,
2262 "Continue jump is not allowed to leave a finally block");
2264 if (r->m_continueTargets.count(d)) {
2265 // We registered the control target before. Just return the existing one.
2266 t = r->m_continueTargets[d].target;
2267 if (alloc && r->isTryFinally()) {
2268 r->m_continueTargets[d].used = true;
2270 break;
2272 if (r->m_kind != Region::Kind::LoopOrSwitch) {
2273 continue;
2275 if (d == 1) {
2276 // We should never reach this case if alloc == true, since the loop or
2277 // switch should have registered its continue target in advance
2278 assert(!alloc);
2279 t = std::make_shared<ControlTarget>(this);
2280 r = r->m_parent.get();
2281 break;
2283 // Otherwise, delegate to the parent. One continue level has been
2284 // taken care of by this region.
2285 --d;
2287 assert(t != nullptr);
2288 if (alloc && !t->isRegistered()) {
2289 registerControlTarget(t.get());
2291 // For all of the entries that did not have this control target in
2292 // m_continueTargets, add this control target to these entries'
2293 // m_continueTargets fields as appropriate.
2294 Region* end = r;
2295 for (r = region; r != end; r = r->m_parent.get()) {
2296 r->m_continueTargets[depth] =
2297 ControlTargetInfo(t, alloc && r->isTryFinally());
2298 if (r->m_kind == Region::Kind::LoopOrSwitch) {
2299 --depth;
2302 return t;
2305 int Region::getCaseCount() {
2306 int count = 1; // The fall-through case.
2307 for (auto& t : m_returnTargets) {
2308 if (t.second.target->isRegistered()) ++count;
2310 for (auto& t : m_breakTargets) {
2311 if (t.second.target->isRegistered()) ++count;
2313 for (auto& t : m_continueTargets) {
2314 if (t.second.target->isRegistered()) ++count;
2316 for (auto& t : m_gotoTargets) {
2317 if (t.second.target->isRegistered()) ++count;
2319 return count;
2322 void EmitterVisitor::emitIterFree(Emitter& e, IterVec& iters) {
2323 for (auto& iter : iters) {
2324 assert(iter.id != -1);
2325 if (iter.kind == KindOfMIter) {
2326 e.MIterFree(iter.id);
2327 } else {
2328 assert(iter.kind == KindOfIter);
2329 e.IterFree(iter.id);
2334 void EmitterVisitor::emitJump(Emitter& e, IterVec& iters, Label& target) {
2335 if (!iters.empty()) {
2336 e.IterBreak(target, iters);
2337 iters.clear();
2338 } else {
2339 e.Jmp(target);
2343 void EmitterVisitor::emitReturn(Emitter& e, char sym, StatementPtr s) {
2344 Region* region = m_regions.back().get();
2345 registerReturn(s, region, sym);
2346 assert(getEvalStack().size() == 1);
2347 assert(region->m_returnTargets.count(sym));
2348 IterVec iters;
2349 for (Region* r = region; true; r = r->m_parent.get()) {
2350 auto& t = r->m_returnTargets[sym].target;
2351 if (r->m_parent == nullptr) {
2352 // At the top of the hierarchy, no more finally blocks to run.
2353 // Check return type, free pending iterators and actually return.
2354 if (sym == StackSym::C) {
2355 if (shouldEmitVerifyRetType()) {
2356 e.VerifyRetTypeC();
2358 // IterFree must come after VerifyRetType, because VerifyRetType may
2359 // throw, in which case any Iters will be freed by the fault funclet.
2360 emitIterFree(e, iters);
2361 e.RetC();
2362 } else {
2363 assert(sym == StackSym::V);
2364 if (shouldEmitVerifyRetType()) {
2365 e.VerifyRetTypeV();
2367 emitIterFree(e, iters);
2368 e.RetV();
2370 return;
2373 if (r->isTryFinally()) {
2374 // We encountered a try block - a finally needs to be run
2375 // before returning.
2376 assert(t->isRegistered());
2377 e.Int(t->m_state);
2378 emitSetL(e, getStateLocal());
2379 e.PopC();
2380 // Emit code stashing the current return value in the "ret" unnamed
2381 // local
2382 if (sym == StackSym::C) {
2383 emitSetL(e, getRetLocal());
2384 e.PopC();
2385 } else {
2386 assert(sym == StackSym::V);
2387 emitBindL(e, getRetLocal());
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 assert(t->isRegistered());
2421 e.Int(t->m_state);
2422 emitSetL(e, getStateLocal());
2423 e.PopC();
2424 // Jump to the finally block and free any pending iterators on the
2425 // way.
2426 emitJump(e, iters, r->m_finallyLabel);
2427 return;
2429 if (r->isForeach()) {
2430 iters.push_back(IterPair(r->m_iterKind, r->m_iterId));
2435 void EmitterVisitor::emitBreak(Emitter& e, int depth, StatementPtr s) {
2436 Region* region = m_regions.back().get();
2437 registerBreak(s, region, depth, true);
2438 assert(depth >= 1);
2439 assert(!region->isFinally());
2440 assert(region->m_parent != nullptr);
2441 assert(region->m_breakTargets.count(depth));
2442 IterVec iters;
2444 for (Region* r = region; true; r = r->m_parent.get()) {
2445 auto t = r->m_breakTargets[depth].target;
2446 if (r->isTryFinally()) {
2447 // Encountered a try block, need to run finally.
2448 assert(r->m_breakTargets.count(depth));
2449 assert(t->isRegistered());
2450 e.Int(t->m_state);
2451 emitSetL(e, getStateLocal());
2452 e.PopC();
2453 emitJump(e, iters, r->m_finallyLabel);
2454 return;
2456 if (r->m_kind != Region::Kind::LoopOrSwitch) {
2457 continue;
2459 // Free iterator for the current loop whether or not
2460 // this is the last loop that we jump out of.
2461 if (r->isForeach()) {
2462 iters.push_back(IterPair(r->m_iterKind, r->m_iterId));
2464 if (depth == 1) {
2465 // Last loop to jumpt out of. Performa direct jump to the
2466 // break lable and free any pending iterators left.
2467 emitJump(e, iters, t->m_label);
2468 return;
2470 --depth;
2474 void EmitterVisitor::emitContinue(Emitter& e, int depth, StatementPtr s) {
2475 Region* region = m_regions.back().get();
2476 registerContinue(s, region, depth, true);
2477 assert(depth >= 1);
2478 assert(!region->isFinally());
2479 assert(region->m_parent != nullptr);
2480 assert(region->m_continueTargets.count(depth));
2481 IterVec iters;
2483 for (Region* r = region; true; r = r->m_parent.get()) {
2484 auto t = r->m_continueTargets[depth].target;
2485 if (r->isTryFinally()) {
2486 // Encountered a try block, need to run finally.
2487 assert(r->m_continueTargets.count(depth));
2488 assert(t->isRegistered());
2489 e.Int(t->m_state);
2490 emitSetL(e, getStateLocal());
2491 e.PopC();
2492 emitJump(e, iters, r->m_finallyLabel);
2493 return;
2495 if (r->m_kind != Region::Kind::LoopOrSwitch) {
2496 continue;
2498 if (depth == 1) {
2499 // Last level. Don't free the iterator for the current loop
2500 // however free any earlier pending iterators.
2501 emitJump(e, iters, t->m_label);
2502 return;
2504 // Only free the iterator for the current loop if this is
2505 // NOT the last level to continue out of.
2506 if (r->isForeach()) {
2507 iters.push_back(IterPair(r->m_iterKind, r->m_iterId));
2509 --depth;
2513 void EmitterVisitor::emitFinallyEpilogue(Emitter& e, Region* region) {
2514 assert(region != nullptr);
2515 assert(region->isTryFinally());
2516 assert(region->m_finallyLabel.isSet());
2517 int count = region->getCaseCount();
2518 assert(count >= 1);
2519 Label after;
2520 if (count == 1) {
2521 // If there is only one case (the fall-through case) then we're done
2522 after.set(e);
2523 return;
2525 // Otherwise, we need to emit some conditional jumps/switches to handle
2526 // the different cases. We start by builing up a vector of Label* that
2527 // we'll use for the Switch instruction and/or for conditional branches.
2528 int maxState = region->getMaxState();
2529 std::vector<Label*> cases;
2530 while (cases.size() <= maxState) {
2531 cases.push_back(new Label());
2533 // Now that we have our vector of Label*'s ready, we can emit a
2534 // Switch instruction and/or conditional branches, and we can
2535 // emit the body of each case.
2536 emitVirtualLocal(getStateLocal());
2537 emitIsset(e);
2538 e.JmpZ(after);
2539 if (count >= 3) {
2540 // A switch is needed since there are more than two cases.
2541 emitVirtualLocal(getStateLocal());
2542 emitCGet(e);
2543 e.Switch(SwitchKind::Unbounded, 0, cases);
2545 for (auto& p : region->m_returnTargets) {
2546 if (p.second.used) emitReturnTrampoline(e, region, cases, p.first);
2548 assert(region->isTryFinally());
2549 int max_depth = region->getBreakContinueDepth();
2550 for (int i = 1; i <= max_depth; ++i) {
2551 if (region->isBreakUsed(i)) emitBreakTrampoline(e, region, cases, i);
2552 if (region->isContinueUsed(i)) emitContinueTrampoline(e, region, cases, i);
2554 for (auto& p : region->m_gotoTargets) {
2555 if (p.second.used) emitGotoTrampoline(e, region, cases, p.first);
2557 for (auto c : cases) {
2558 // Some cases might get assigned state numbers but not actually
2559 // occur in the try block. We need to set /some/ target for them,
2560 // so point them here.
2561 if (!c->isSet()) c->set(e);
2562 delete c;
2564 after.set(e);
2567 void EmitterVisitor::emitReturnTrampoline(Emitter& e,
2568 Region* region,
2569 std::vector<Label*>& cases,
2570 char sym) {
2571 assert(region->isTryFinally());
2572 assert(region->m_parent != nullptr);
2573 assert(region->m_returnTargets.count(sym));
2574 auto& t = region->m_returnTargets[sym].target;
2575 cases[t->m_state]->set(e);
2577 IterVec iters;
2578 // We are emitting a case in a finally epilogue, therefore skip
2579 // the current try region and start from its parent
2580 for (region = region->m_parent.get(); true; region = region->m_parent.get()) {
2581 assert(region->m_returnTargets.count(sym));
2582 assert(region->m_returnTargets[sym].target->isRegistered());
2583 // Add pending iterator if applicable
2584 if (region->isForeach()) {
2585 iters.push_back(IterPair(region->m_iterKind, region->m_iterId));
2587 if (region->m_parent == nullptr) {
2588 // At the bottom of the hierarchy. Restore the return value
2589 // and perform the actual return.
2590 emitVirtualLocal(getRetLocal());
2591 if (sym == StackSym::C) {
2592 emitCGet(e);
2593 if (shouldEmitVerifyRetType()) {
2594 e.VerifyRetTypeC();
2596 e.RetC();
2597 } else {
2598 assert(sym == StackSym::V);
2599 emitVGet(e);
2600 if (shouldEmitVerifyRetType()) {
2601 e.VerifyRetTypeV();
2603 e.RetV();
2605 return;
2607 if (region->isTryFinally()) {
2608 // Encountered another try block, jump to its finally and free
2609 // iterators on the way.
2610 emitJump(e, iters, region->m_finallyLabel);
2611 return;
2616 void EmitterVisitor::emitGotoTrampoline(Emitter& e,
2617 Region* region,
2618 std::vector<Label*>& cases,
2619 StringData* name) {
2620 assert(region->m_gotoTargets.count(name));
2621 auto t = region->m_gotoTargets[name].target;
2622 cases[t->m_state]->set(e);
2623 assert(region->m_parent != nullptr);
2624 IterVec iters;
2625 for (region = region->m_parent.get(); true; region = region->m_parent.get()) {
2626 assert(region->m_gotoTargets.count(name));
2627 auto t = region->m_gotoTargets[name].target;
2628 if (region->m_gotoLabels.count(name)) {
2629 // If only there is the appropriate label inside the current region
2630 // perform a jump.
2631 // We need to unset the state unnamed local in order to correctly
2632 // fall through any future finally blocks.
2633 emitUnsetL(e, getStateLocal());
2634 // Jump to the label and free any pending iterators.
2635 emitJump(e, iters, t->m_label);
2636 return;
2638 if (region->isTryFinally()) {
2639 // Encountered a finally block, jump and free any pending iterators
2640 emitJump(e, iters, region->m_finallyLabel);
2641 return;
2643 // Otherwise we will be jumping out of the current context,
2644 // therefore if we are in a loop, we need to free the iterator.
2645 if (region->isForeach()) {
2646 iters.push_back(IterPair(region->m_iterKind, region->m_iterId));
2648 // Error, because the label is crossing a finally
2649 if (region->isFinally()) {
2650 throw EmitterVisitor::IncludeTimeFatalException(e.getNode(),
2651 "jump out of a finally block is disallowed");
2653 // We should never break out of a function, therefore there
2654 // should always be a parent
2655 assert(region->m_parent != nullptr);
2659 void EmitterVisitor::emitBreakTrampoline(Emitter& e, Region* region,
2660 std::vector<Label*>& cases,
2661 int depth) {
2662 assert(depth >= 1);
2663 assert(region->isTryFinally());
2664 assert(region->m_breakTargets.count(depth));
2665 auto t = region->m_breakTargets[depth].target;
2666 cases[t->m_state]->set(e);
2667 assert(region->m_parent != nullptr);
2668 IterVec iters;
2669 for (region = region->m_parent.get(); true; region = region->m_parent.get()) {
2670 assert(depth >= 1);
2671 assert(!region->isFinally());
2672 assert(region->m_parent != nullptr);
2673 assert(region->m_breakTargets.count(depth));
2674 auto t = region->m_breakTargets[depth].target;
2675 if (region->isTryFinally()) {
2676 // We encountered another try block, jump to the corresponding
2677 // finally, freeing any iterators on the way.
2678 emitJump(e, iters, region->m_finallyLabel);
2679 return;
2681 if (region->m_kind != Region::Kind::LoopOrSwitch) {
2682 continue;
2684 // Whether or not this is the last loop to break out of, we
2685 // will be freeing the current iterator
2686 if (region->isForeach()) {
2687 iters.push_back(IterPair(region->m_iterKind, region->m_iterId));
2689 if (depth == 1) {
2690 // This is the last loop to break out of. Unset the state local in
2691 // order to correctly fall through any future finally blocks
2692 emitUnsetL(e, getStateLocal());
2693 // Jump to the break label and free any pending iterators on the
2694 // way.
2695 emitJump(e, iters, t->m_label);
2696 return;
2698 // Otherwise just delegate to the parent. One loop level has been
2699 // taken care of.
2700 --depth;
2704 void EmitterVisitor::emitContinueTrampoline(Emitter& e, Region* region,
2705 std::vector<Label*>& cases,
2706 int depth) {
2707 assert(depth >= 1);
2708 assert(region->isTryFinally());
2709 assert(region->m_continueTargets.count(depth));
2710 auto t = region->m_continueTargets[depth].target;
2711 cases[t->m_state]->set(e);
2712 assert(region->m_parent != nullptr);
2713 IterVec iters;
2714 for (region = region->m_parent.get(); true; region = region->m_parent.get()) {
2715 assert(depth >= 1);
2716 assert(region->m_parent != nullptr);
2717 assert(!region->isFinally());
2718 auto t = region->m_continueTargets[depth].target;
2719 if (region->isTryFinally()) {
2720 emitJump(e, iters, region->m_finallyLabel);
2721 return;
2723 if (region->m_kind != Region::Kind::LoopOrSwitch) {
2724 continue;
2726 if (depth == 1) {
2727 // This is the last loop level to continue out of. Don't free the
2728 // iterator for the current loop. We need to free the state unnamed
2729 // local in order to fall through any future finallies correctly
2730 emitUnsetL(e, getStateLocal());
2731 // Jump to the continue label and free any pending iterators
2732 emitJump(e, iters, t->m_label);
2733 return;
2735 // This is not the last loop level, therefore the current
2736 // iterator should be freed.
2737 if (region->isForeach()) {
2738 iters.push_back(IterPair(region->m_iterKind, region->m_iterId));
2740 --depth;
2744 bool EmitterVisitor::shouldEmitVerifyRetType() {
2745 return (m_curFunc->retTypeConstraint.hasConstraint() &&
2746 !m_curFunc->isGenerator);
2749 int Region::getMaxBreakContinueDepth() {
2750 if (m_parent == nullptr || isFinally()) {
2751 return 0;
2752 } else if (m_kind == Region::Kind::LoopOrSwitch) {
2753 return m_parent->getMaxBreakContinueDepth() + 1;
2754 } else {
2755 return m_parent->getMaxBreakContinueDepth();
2759 int Region::getBreakContinueDepth() {
2760 int depth = 0;
2761 for (auto& p : m_breakTargets) {
2762 depth = std::max(depth, p.first);
2764 for (auto& p : m_continueTargets) {
2765 depth = std::max(depth, p.first);
2767 return depth;
2770 int Region::getMaxState() {
2771 int maxState = -1;
2772 for (auto& p : m_returnTargets) {
2773 if (p.second.used) {
2774 maxState = std::max(maxState, p.second.target->m_state);
2777 int max_depth = getBreakContinueDepth();
2778 for (int i = 1; i <= max_depth; ++i) {
2779 if (isBreakUsed(i)) {
2780 maxState = std::max(maxState, m_breakTargets[i].target->m_state);
2782 if (isContinueUsed(i)) {
2783 maxState = std::max(maxState, m_continueTargets[i].target->m_state);
2786 for (auto& p : m_gotoTargets) {
2787 if (p.second.used) {
2788 maxState = std::max(maxState, p.second.target->m_state);
2791 return maxState;
2794 RegionPtr
2795 EmitterVisitor::createRegion(StatementPtr s, Region::Kind kind) {
2796 RegionPtr parent = nullptr;
2797 if (kind != Region::Kind::FuncBody && kind != Region::Kind::FaultFunclet &&
2798 kind != Region::Kind::Global && !m_regions.empty()) {
2799 parent = m_regions.back();
2801 auto region = std::make_shared<Region>(kind, parent);
2802 // We preregister all the labels occurring in the provided statement
2803 // ahead of the time. Therefore at the time of emitting the actual
2804 // goto instructions we can reliably tell which finally blocks to
2805 // run.
2806 for (auto& label : s->getLabelScope()->getLabels()) {
2807 StringData* nName = makeStaticString(label.getName().c_str());
2808 if (!region->m_gotoLabels.count(nName)) {
2809 region->m_gotoLabels.insert(nName);
2812 return region;
2815 void EmitterVisitor::enterRegion(RegionPtr region) {
2816 assert(region != nullptr);
2817 m_regions.push_back(region);
2820 void EmitterVisitor::leaveRegion(RegionPtr region) {
2821 assert(region != nullptr);
2822 assert(m_regions.size() > 0);
2823 assert(m_regions.back() == region);
2824 m_regions.pop_back();
2827 void EmitterVisitor::registerControlTarget(ControlTarget* t) {
2828 assert(!t->isRegistered());
2829 int state = 0;
2830 while (m_states.count(state)) {
2831 ++state;
2833 m_states.insert(state);
2834 t->m_state = state;
2837 void EmitterVisitor::unregisterControlTarget(ControlTarget* t) {
2838 assert(t->isRegistered());
2839 int state = t->m_state;
2840 assert(m_states.count(state));
2841 m_states.erase(state);
2842 t->m_state = ControlTarget::k_unsetState;
2845 //=============================================================================
2846 // EmitterVisitor.
2848 EmitterVisitor::EmitterVisitor(UnitEmitter& ue)
2849 : m_ue(ue),
2850 m_curFunc(ue.getMain()),
2851 m_evalStackIsUnknown(false),
2852 m_stateLocal(-1),
2853 m_retLocal(-1) {
2854 m_prevOpcode = OpLowInvalid;
2855 m_evalStack.m_actualStackHighWaterPtr = &m_curFunc->maxStackCells;
2858 EmitterVisitor::~EmitterVisitor() {
2859 // If a fatal occurs during emission, some extra cleanup is necessary.
2860 for (std::deque<CatchRegion*>::const_iterator it = m_catchRegions.begin();
2861 it != m_catchRegions.end(); ++it) {
2862 delete *it;
2866 bool EmitterVisitor::checkIfStackEmpty(const char* forInstruction) const {
2867 if (m_evalStack.empty()) {
2868 InvariantViolation("Emitter tried to emit a %s instruction when the "
2869 "evaluation stack is empty (at offset %d)",
2870 forInstruction,
2871 m_ue.bcPos());
2872 return true;
2874 return false;
2877 void EmitterVisitor::unexpectedStackSym(char sym, const char* where) const {
2878 InvariantViolation("Emitter encountered an unexpected StackSym \"%s\""
2879 " in %s() (at offset %d)",
2880 StackSym::ToString(sym).c_str(),
2881 where,
2882 m_ue.bcPos());
2885 void EmitterVisitor::popEvalStack(char expected) {
2886 // Pop a value off of the evaluation stack, and verify that it
2887 // matches the specified symbolic flavor
2888 if (m_evalStack.size() == 0) {
2889 InvariantViolation("Emitter emitted an instruction that tries to consume "
2890 "a value from the stack when the stack is empty "
2891 "(expected symbolic flavor \"%s\" at offset %d)",
2892 StackSym::ToString(expected).c_str(),
2893 m_ue.bcPos());
2894 return;
2897 char sym = m_evalStack.top();
2898 char actual = StackSym::GetSymFlavor(sym);
2899 m_evalStack.pop();
2900 if (actual != expected) {
2901 InvariantViolation(
2902 "Emitter emitted an instruction that tries to consume a "
2903 "value from the stack when the top of the stack does not "
2904 "match the symbolic flavor that the instruction expects "
2905 "(expected symbolic flavor \"%s\", actual symbolic flavor \"%s\" "
2906 "at offset %d)",
2907 StackSym::ToString(expected).c_str(),
2908 StackSym::ToString(actual).c_str(),
2909 m_ue.bcPos());
2913 void EmitterVisitor::popSymbolicLocal(Op op) {
2914 // A number of member instructions read locals without consuming an L from
2915 // the symbolic stack through the normal path.
2916 if (isMemberBaseOp(op) || isMemberDimOp(op) || isMemberFinalOp(op)) {
2917 return;
2920 int belowTop = -1;
2921 if (op == OpCGetL3) {
2922 belowTop = 3;
2923 } else if (op == OpCGetL2) {
2924 belowTop = 2;
2927 if (belowTop != -1) {
2928 char symFlavor = StackSym::GetSymFlavor(
2929 m_evalStack.get(m_evalStack.size() - belowTop));
2930 if (symFlavor != StackSym::L) {
2931 InvariantViolation("Operation tried to remove a local below the top of"
2932 " the symbolic stack but instead found \"%s\"",
2933 StackSym::ToString(symFlavor).c_str());
2935 m_evalStack.consumeBelowTop(belowTop - 1);
2936 } else {
2937 popEvalStack(StackSym::L);
2941 void EmitterVisitor::popEvalStackMMany() {
2942 ITRACE(3, "popEvalStackMMany()\n");
2943 Trace::Indent i;
2945 ITRACE(3, "popping member codes\n");
2946 while (!m_evalStack.empty()) {
2947 char sym = m_evalStack.top();
2948 char symFlavor = StackSym::GetSymFlavor(sym);
2949 char marker = StackSym::GetMarker(sym);
2950 if (marker == StackSym::E || marker == StackSym::P ||
2951 marker == StackSym::Q) {
2952 if (symFlavor != StackSym::C && symFlavor != StackSym::L &&
2953 symFlavor != StackSym::T && symFlavor != StackSym::I) {
2954 InvariantViolation(
2955 "Emitter emitted an instruction that tries to consume "
2956 "a value from the stack when the top of the stack "
2957 "does not match the symbolic flavor that the instruction "
2958 "expects (expected symbolic flavor \"C\", \"L\", \"T\", or \"I\", "
2959 "actual symbolic flavor \"%s\" at offset %d)",
2960 StackSym::ToString(symFlavor).c_str(),
2961 m_ue.bcPos());
2963 } else if (marker == StackSym::W) {
2964 if (symFlavor != StackSym::None) {
2965 InvariantViolation(
2966 "Emitter emitted an instruction that tries to consume "
2967 "a value from the stack when the top of the stack "
2968 "does not match the symbolic flavor that the instruction "
2969 "expects (expected symbolic flavor \"None\", actual "
2970 "symbolic flavor \"%s\" at offset %d)",
2971 StackSym::ToString(symFlavor).c_str(),
2972 m_ue.bcPos());
2974 } else if (marker == StackSym::M) {
2975 assert(symFlavor == StackSym::A);
2976 } else {
2977 break;
2979 m_evalStack.pop();
2982 if (m_evalStack.empty()) {
2983 InvariantViolation("Emitter emitted an instruction that tries to consume "
2984 "a value from the stack when the stack is empty "
2985 "(at offset %d)",
2986 m_ue.bcPos());
2987 return;
2990 ITRACE(3, "popping location\n");
2991 char sym = m_evalStack.top();
2992 char symFlavor = StackSym::GetSymFlavor(sym);
2993 m_evalStack.pop();
2994 if (symFlavor != StackSym::C && symFlavor != StackSym::L &&
2995 symFlavor != StackSym::R && symFlavor != StackSym::H) {
2996 InvariantViolation(
2997 "Emitter emitted an instruction that tries to consume a "
2998 "value from the stack when the top of the stack does not "
2999 "match the symbolic flavor that the instruction expects "
3000 "(expected symbolic flavor \"C\", \"L\", \"R\", or \"H\", actual "
3001 "symbolic flavor \"%s\" at offset %d)",
3002 StackSym::ToString(symFlavor).c_str(),
3003 m_ue.bcPos());
3007 void EmitterVisitor::popEvalStackMany(int len, char symFlavor) {
3008 for (int i = 0; i < len; ++i) {
3009 popEvalStack(symFlavor);
3013 void EmitterVisitor::popEvalStackCVMany(int len) {
3014 for (int i = 0; i < len; i++) {
3015 if (m_evalStack.size() == 0) {
3016 InvariantViolation("Emitter emitted an instruction that tries to consume "
3017 "a value from the stack when the stack is empty "
3018 "(expected symbolic flavor C or V at offset %d)",
3019 m_ue.bcPos());
3020 return;
3023 char sym = m_evalStack.top();
3024 char actual = StackSym::GetSymFlavor(sym);
3025 m_evalStack.pop();
3026 if (actual != StackSym::C && actual != StackSym::V) {
3027 InvariantViolation(
3028 "Emitter emitted an instruction that tries to consume a "
3029 "value from the stack when the top of the stack does not "
3030 "match the symbolic flavor that the instruction expects "
3031 "(expected symbolic flavor C or V, actual symbolic flavor \"%s\" "
3032 "at offset %d)",
3033 StackSym::ToString(actual).c_str(),
3034 m_ue.bcPos());
3039 void EmitterVisitor::pushEvalStack(char symFlavor) {
3040 // Push a value from the evaluation stack with the specified
3041 // symbolic flavor
3042 m_evalStack.push(symFlavor);
3045 void EmitterVisitor::peekEvalStack(char expected, int depthActual) {
3046 int posActual = (m_evalStack.sizeActual() - depthActual - 1);
3047 if (posActual >= 0 && posActual < (int)m_evalStack.sizeActual()) {
3048 char sym = m_evalStack.getActual(posActual);
3049 char actual = StackSym::GetSymFlavor(sym);
3050 if (actual != expected) {
3051 InvariantViolation(
3052 "Emitter emitted an instruction that tries to consume a "
3053 "value from the stack whose symbolic flavor does not match "
3054 "the symbolic flavor that the instruction expects (expected "
3055 "symbolic flavor \"%s\", actual symbolic flavor \"%s\" at "
3056 "offset %d)",
3057 StackSym::ToString(expected).c_str(),
3058 StackSym::ToString(actual).c_str(),
3059 m_ue.bcPos());
3064 void EmitterVisitor::pokeEvalStack(char symFlavor, int depthActual) {
3065 int sizeActual = m_evalStack.sizeActual();
3066 int posActual = sizeActual - depthActual - 1;
3067 if (posActual >= 0 && posActual < sizeActual) {
3068 m_evalStack.setActual(posActual, symFlavor);
3073 * Prior to making any changes to the evaluation stack in between
3074 * instructions, this function should be called.
3076 * What this handles is recording the evaluation stack state at
3077 * instruction boundaries so that jumps know what their stack state
3078 * will be at the destination.
3080 * When m_evalStackIsUnknown, it means we have hit a place in the
3081 * bytecode where the offset cannot be reached via fallthrough, but no
3082 * forward jumps targeted it either. In this case, the stack must be
3083 * empty, and we need to record that this is the case so that later
3084 * backward jumps can check this is the case at their jump site.
3086 void EmitterVisitor::prepareEvalStack() {
3087 if (m_evalStackIsUnknown) {
3088 if (!m_evalStack.empty()) {
3089 InvariantViolation("Emitter expected to have an empty evaluation "
3090 "stack because the eval stack was unknown, but "
3091 "it was non-empty.");
3092 return;
3094 // Record that we are assuming that the eval stack is empty
3095 recordJumpTarget(m_ue.bcPos(), m_evalStack);
3096 m_evalStackIsUnknown = false;
3100 void EmitterVisitor::recordJumpTarget(Offset target,
3101 const SymbolicStack& evalStack) {
3102 if (target == InvalidAbsoluteOffset) {
3103 InvariantViolation(
3104 "Offset passed to EmitterVisitor::recordJumpTarget was invalid");
3106 auto it = m_jumpTargetEvalStacks.find(target);
3107 if (it == m_jumpTargetEvalStacks.end()) {
3108 m_jumpTargetEvalStacks[target] = evalStack;
3109 return;
3111 checkJmpTargetEvalStack(evalStack, it->second);
3114 void EmitterVisitor::restoreJumpTargetEvalStack() {
3115 m_evalStack.clear();
3116 auto it = m_jumpTargetEvalStacks.find(m_ue.bcPos());
3117 if (it == m_jumpTargetEvalStacks.end()) {
3118 m_evalStackIsUnknown = true;
3119 return;
3121 m_evalStack = it->second;
3124 void EmitterVisitor::recordCall() {
3125 m_curFunc->containsCalls = true;
3128 bool EmitterVisitor::isJumpTarget(Offset target) {
3129 // Returns true iff one of the following conditions is true:
3130 // 1) We have seen an instruction that jumps to the specified offset
3131 // 2) We know of a Label that has been set to the specified offset
3132 // 3) We have seen a try region that ends at the specified offset
3133 auto it = m_jumpTargetEvalStacks.find(target);
3134 return (it != m_jumpTargetEvalStacks.end());
3137 struct IterFreeThunklet final : Thunklet {
3138 IterFreeThunklet(Id iterId, bool itRef)
3139 : m_id(iterId), m_itRef(itRef) {}
3140 void emit(Emitter& e) override {
3141 if (m_itRef) {
3142 e.MIterFree(m_id);
3143 } else {
3144 e.IterFree(m_id);
3146 e.Unwind();
3148 private:
3149 Id m_id;
3150 bool m_itRef;
3154 * A thunklet for the fault region protecting a silenced (@) expression.
3156 struct RestoreErrorReportingThunklet final : Thunklet {
3157 explicit RestoreErrorReportingThunklet(Id loc)
3158 : m_oldLevelLoc(loc) {}
3159 void emit(Emitter& e) override {
3160 e.getEmitterVisitor().emitRestoreErrorReporting(e, m_oldLevelLoc);
3161 e.Unwind();
3163 private:
3164 Id m_oldLevelLoc;
3167 struct UnsetUnnamedLocalThunklet final : Thunklet {
3168 explicit UnsetUnnamedLocalThunklet(Id loc)
3169 : m_loc(loc) {}
3170 void emit(Emitter& e) override {
3171 e.getEmitterVisitor().emitUnsetL(e, m_loc);
3172 e.Unwind();
3174 private:
3175 Id m_loc;
3178 struct UnsetUnnamedLocalsThunklet final : Thunklet {
3179 explicit UnsetUnnamedLocalsThunklet(std::vector<Id>&& locs)
3180 : m_locs(std::move(locs)) {}
3181 void emit(Emitter& e) override {
3182 auto& visitor = e.getEmitterVisitor();
3183 for (auto loc : m_locs) {
3184 visitor.emitUnsetL(e, loc);
3186 e.Unwind();
3188 private:
3189 const std::vector<Id> m_locs;
3192 struct UnsetGeneratorDelegateThunklet final : Thunklet {
3193 explicit UnsetGeneratorDelegateThunklet(Id iterId)
3194 : m_id(iterId) {}
3195 void emit(Emitter& e) override {
3196 e.ContUnsetDelegate(true, m_id);
3197 e.Unwind();
3199 private:
3200 Id m_id;
3203 struct FinallyThunklet final : Thunklet {
3204 explicit FinallyThunklet(FinallyStatementPtr finallyStatement,
3205 int numLiveIters)
3206 : m_finallyStatement(finallyStatement), m_numLiveIters(numLiveIters) {}
3207 void emit(Emitter& e) override {
3208 auto& visitor = e.getEmitterVisitor();
3209 auto region =
3210 visitor.createRegion(m_finallyStatement, Region::Kind::FaultFunclet);
3211 visitor.enterRegion(region);
3212 SCOPE_EXIT { visitor.leaveRegion(region); };
3213 visitor.emitUnsetL(e, visitor.getStateLocal());
3214 visitor.emitUnsetL(e, visitor.getRetLocal());
3215 auto* func = visitor.getFuncEmitter();
3216 int oldNumLiveIters = func->numLiveIterators();
3217 func->setNumLiveIterators(m_numLiveIters);
3218 SCOPE_EXIT { func->setNumLiveIterators(oldNumLiveIters); };
3219 visitor.visit(m_finallyStatement);
3220 e.Unwind();
3222 private:
3223 FinallyStatementPtr m_finallyStatement;
3224 int m_numLiveIters;
3228 * Helper to deal with emitting list assignment and keeping track of some
3229 * associated info. A list assignment can be thought of as a list of "index
3230 * chains"; that is, sequences of indices that should be accessed for each
3231 * bottom-level expression in the list assignment. We recursively walk down the
3232 * LHS, building up index chains and copying them into the top-level list as we
3233 * reach the leaves of the tree.
3235 void EmitterVisitor::listAssignmentVisitLHS(Emitter& e, ExpressionPtr exp,
3236 IndexChain& indexChain,
3237 std::vector<IndexPair>& all) {
3238 if (!exp) {
3239 // Empty slot
3240 return;
3243 if (exp->is(Expression::KindOfListAssignment)) {
3244 // Nested assignment
3245 auto la = static_pointer_cast<ListAssignment>(exp);
3246 auto lhs = la->getVariables();
3247 int n = lhs->getCount();
3248 for (int i = 0; i < n; ++i) {
3249 indexChain.push_back(i);
3250 listAssignmentVisitLHS(e, (*lhs)[i], indexChain, all);
3251 indexChain.pop_back();
3253 } else {
3254 // Reached a "leaf". Lock in this index chain and deal with this exp.
3255 assert(!indexChain.empty());
3256 all.emplace_back(exp, IndexChain(indexChain));
3258 // First: the order we visit the LHS elements matters, as does whether we
3259 // do the RHS or LHS first, for things like:
3260 // list($a[$n++], $b[$n++]) = $c[$n++]
3262 // In PHP5 mode, we visit the LHS elements of the list() now. This does
3263 // two things: it causes their side effects to happen in LTR order, but
3264 // since they are pushed onto the m_evalStack they are actually asigned to
3265 // in LIFO order, e.g., RTL, in listAssignmentAssignElements below.
3267 // In PHP7 mode, we need to visit the elements in LTR order so their side
3268 // effects take place in that order, but we *also* need to assign them in
3269 // LTR order, so we can't push them onto the m_evalStack right now. Since
3270 // visit() does both of these things, we need to delay calling visit()
3271 // until listAssignmentAssignElements below. This also has the side effect
3272 // of making isRhsFirst() effectively always on in PHP7 mode when doing
3273 // list() assignment (since we delay the visit() until after the check
3274 // anyways), which turns out to be the right behavior.
3275 if (!RuntimeOption::PHP7_LTR_assign) {
3276 visit(exp);
3277 emitClsIfSPropBase(e);
3282 void EmitterVisitor::listAssignmentAssignElements(
3283 Emitter& e,
3284 std::vector<IndexPair>& indexPairs,
3285 std::function<void()> emitSrc
3288 // PHP5 does list() assignments RTL, PHP7 does them LTR, so this loop can go
3289 // either way and looks a little ugly. The assignment order normally isn't
3290 // visible, but it is if you do something like:
3291 // list($a[], $a[]) = $foo
3292 auto const ltr = RuntimeOption::PHP7_LTR_assign;
3293 for (int i = ltr ? 0 : (int)indexPairs.size() - 1;
3294 i >= 0 && i < (int)indexPairs.size();
3295 ltr ? i++ : i--) {
3296 if (ltr) {
3297 // Visit now, so we can both eval LTR and assign LTR. See comment in
3298 // listAssignmentVisitLHS.
3299 visit(indexPairs[i].first);
3300 emitClsIfSPropBase(e);
3303 IndexChain& currIndexChain = indexPairs[i].second;
3304 if (currIndexChain.empty()) {
3305 continue;
3308 if (emitSrc == nullptr) {
3309 e.Null();
3310 } else {
3311 emitSrc();
3312 for (int j = 0; j < (int)currIndexChain.size(); ++j) {
3313 m_evalStack.push(StackSym::I);
3314 m_evalStack.setInt(currIndexChain[j]);
3315 markElem(e);
3317 emitCGet(e);
3320 emitSet(e);
3321 emitPop(e);
3326 * visitIfCondition() serves as a helper method for visiting the condition
3327 * of an "if" statement. This function recursively visits each node in an
3328 * expression, keeping track of where to jump if an "and" or "or" expression
3329 * short circuits. If an expression other than "and", "or", or "not" is
3330 * encountered, this method calls EmitterVisitor::visit() to handle the
3331 * expression.
3333 void EmitterVisitor::visitIfCondition(
3334 ExpressionPtr cond, Emitter& e, Label& tru, Label& fals,
3335 bool truFallthrough) {
3337 auto binOpNode = dynamic_pointer_cast<BinaryOpExpression>(cond);
3339 if (binOpNode) {
3340 int op = binOpNode->getOp();
3341 // Short circuit && and ||
3342 if (op == T_LOGICAL_OR || op == T_LOGICAL_AND ||
3343 op == T_BOOLEAN_OR || op == T_BOOLEAN_AND) {
3344 bool isOr = (op == T_LOGICAL_OR || op == T_BOOLEAN_OR);
3345 Label localLabel;
3346 ExpressionPtr lhs = binOpNode->getExp1();
3347 if (isOr) {
3348 Emitter lhsEmitter(lhs, m_ue, *this);
3349 visitIfCondition(lhs, lhsEmitter, tru, localLabel, false);
3350 // falls through if that condition was false
3351 } else {
3352 Emitter lhsEmitter(lhs, m_ue, *this);
3353 visitIfCondition(lhs, lhsEmitter, localLabel, fals, true);
3354 // falls through if that condition was true
3356 if (localLabel.isUsed()) {
3357 localLabel.set(e);
3359 if (currentPositionIsReachable()) {
3360 ExpressionPtr rhs = binOpNode->getExp2();
3361 Emitter rhsEmitter(rhs, m_ue, *this);
3362 visitIfCondition(rhs, rhsEmitter, tru, fals, truFallthrough);
3364 return;
3368 auto unOpNode = dynamic_pointer_cast<UnaryOpExpression>(cond);
3369 if (unOpNode) {
3370 int op = unOpNode->getOp();
3371 // Logical not
3372 if (op == '!') {
3373 ExpressionPtr val = unOpNode->getExpression();
3374 Emitter valEmitter(val, m_ue, *this);
3375 visitIfCondition(val, valEmitter, fals, tru, !truFallthrough);
3376 return;
3380 Variant val;
3381 if (cond->getScalarValue(val)) {
3382 if (truFallthrough) {
3383 if (!val.toBoolean()) e.Jmp(fals);
3384 } else {
3385 if (val.toBoolean()) e.Jmp(tru);
3387 return;
3390 visit(cond);
3391 emitConvertToCell(e);
3392 if (truFallthrough) {
3393 e.JmpZ(fals);
3394 } else {
3395 e.JmpNZ(tru);
3399 // Assigns ids to all of the local variables eagerly. This gives us the
3400 // nice property that all named local variables will be assigned ids
3401 // 0 through k-1, while any unnamed local variable will have an id >= k.
3402 void EmitterVisitor::assignLocalVariableIds(FunctionScopePtr fs) {
3403 VariableTablePtr variables = fs->getVariables();
3404 std::vector<std::string> localNames;
3405 variables->getLocalVariableNames(localNames);
3406 for (int i = 0; i < (int)localNames.size(); ++i) {
3407 StringData* nLiteral = makeStaticString(localNames[i].c_str());
3408 m_curFunc->allocVarId(nLiteral);
3412 void EmitterVisitor::assignFinallyVariableIds() {
3413 assert(m_stateLocal < 0);
3414 m_stateLocal = m_curFunc->allocUnnamedLocal();
3415 assert(m_retLocal < 0);
3416 m_retLocal = m_curFunc->allocUnnamedLocal();
3419 void EmitterVisitor::visit(FileScopePtr file) {
3420 const std::string& filename = file->getName();
3421 m_ue.m_filepath = makeStaticString(filename);
3422 m_ue.m_isHHFile = file->isHHFile();
3423 m_ue.m_useStrictTypes = file->useStrictTypes();
3425 FunctionScopePtr func(file->getPseudoMain());
3426 if (!func) return;
3428 SCOPE_ASSERT_DETAIL("visit FileScope") { return m_evalStack.pretty(); };
3429 ITRACE(1, "Emitting file {}\n", file->getName());
3430 Trace::Indent indent;
3432 m_file = file;
3433 assignLocalVariableIds(func);
3435 AnalysisResultPtr ar(file->getContainingProgram());
3436 assert(ar);
3437 auto m = dynamic_pointer_cast<MethodStatement>(func->getStmt());
3438 if (!m) return;
3439 StatementListPtr stmts(m->getStmts());
3440 if (!stmts) return;
3442 Emitter e(m, m_ue, *this);
3444 int i, nk = stmts->getCount();
3445 for (i = 0; i < nk; i++) {
3446 StatementPtr s = (*stmts)[i];
3447 if (auto meth = dynamic_pointer_cast<MethodStatement>(s)) {
3448 if (auto msg = s->getFunctionScope()->getFatalMessage()) {
3449 throw IncludeTimeFatalException(s, msg->data());
3451 // Emit afterwards
3452 postponeMeth(meth, nullptr, true);
3456 FunctionScopePtr fsp = m->getFunctionScope();
3457 if (fsp->needsLocalThis()) {
3458 static const StringData* thisStr = makeStaticString("this");
3459 Id thisId = m_curFunc->lookupVarId(thisStr);
3460 emitVirtualLocal(thisId);
3461 e.InitThisLoc(thisId);
3463 if (fsp->needsFinallyLocals()) {
3464 assignFinallyVariableIds();
3466 FuncFinisher ff(this, e, m_curFunc);
3467 TypedValue mainReturn;
3468 mainReturn.m_type = kInvalidDataType;
3469 bool notMergeOnly = false;
3471 if (Option::UseHHBBC && SystemLib::s_inited) notMergeOnly = true;
3473 auto region = createRegion(stmts, Region::Kind::Global);
3474 enterRegion(region);
3475 SCOPE_EXIT { leaveRegion(region); };
3477 for (i = 0; i < nk; i++) {
3478 StatementPtr s = (*stmts)[i];
3479 e.setTempLocation(s->getRange());
3480 switch (s->getKindOf()) {
3481 case Statement::KindOfMethodStatement:
3482 case Statement::KindOfFunctionStatement:
3483 break;
3484 case Statement::KindOfInterfaceStatement:
3485 case Statement::KindOfClassStatement: {
3486 // Handle classes directly here, since only top-level classes are
3487 // hoistable.
3488 ClassScopePtr cNode = s->getClassScope();
3489 emitClass(e, cNode, true);
3490 if (cNode->getFatalMessage()) {
3491 notMergeOnly = true;
3493 break;
3495 case Construct::KindOfDeclareStatement: {
3496 auto ds = static_pointer_cast<DeclareStatement>(s);
3497 for (auto& decl : ds->getDeclareMap()) {
3498 if (decl.first == "strict_types") {
3499 if (ds->getBlock()->getStmts()->getCount()) {
3500 emitMakeUnitFatal(e, "strict_types declaration must not use "
3501 "block mode");
3502 break;
3504 if (!RuntimeOption::PHP7_ScalarTypes) {
3505 emitMakeUnitFatal(e, "strict_types can only be used when "
3506 "hhvm.php7.scalar_types = true");
3507 break;
3512 visit(ds->getBlock());
3513 break;
3515 case Statement::KindOfTypedefStatement: {
3516 auto const id =
3517 emitTypedef(e, static_pointer_cast<TypedefStatement>(s));
3518 m_ue.pushMergeableTypeAlias(Unit::MergeKind::TypeAlias, id);
3519 break;
3521 case Statement::KindOfReturnStatement:
3522 if (mainReturn.m_type != kInvalidDataType) break;
3524 visit(s);
3525 if (notMergeOnly) {
3526 tvWriteUninit(&mainReturn);
3527 m_ue.m_returnSeen = true;
3528 continue;
3532 auto r = static_pointer_cast<ReturnStatement>(s);
3533 Variant v((Variant::NullInit()));
3534 if (r->getRetExp() &&
3535 !r->getRetExp()->getScalarValue(v)) {
3536 tvWriteUninit(&mainReturn);
3537 notMergeOnly = true;
3538 continue;
3540 if (v.isString()) {
3541 v = String(makeStaticString(v.asCStrRef().get()));
3542 } else if (v.isArray()) {
3543 v = Array(ArrayData::GetScalarArray(v.asCArrRef().get()));
3544 } else {
3545 assert(v.isInitialized());
3546 assert(!isRefcountedType(v.getType()));
3548 mainReturn = *v.asCell();
3549 m_ue.m_returnSeen = true;
3551 break;
3552 case Statement::KindOfExpStatement:
3553 if (mainReturn.m_type == kInvalidDataType) {
3554 auto e = static_pointer_cast<ExpStatement>(s)->getExpression();
3555 switch (e->getKindOf()) {
3556 case Expression::KindOfSimpleFunctionCall: {
3557 auto func = static_pointer_cast<SimpleFunctionCall>(e);
3558 StringData *name;
3559 TypedValue tv;
3560 if (func->isSimpleDefine(&name, &tv)) {
3561 auto k = func->isDefineWithoutImpl(ar)
3562 ? Unit::MergeKind::PersistentDefine
3563 : Unit::MergeKind::Define;
3564 if (tv.m_type == KindOfUninit) {
3565 tv.m_type = KindOfNull;
3567 m_ue.pushMergeableDef(k, name, tv);
3568 visit(s);
3569 continue;
3571 break;
3573 case Expression::KindOfAssignmentExpression: {
3574 auto ae = static_pointer_cast<AssignmentExpression>(e);
3575 StringData *name;
3576 TypedValue tv;
3577 if (ae->isSimpleGlobalAssign(&name, &tv)) {
3578 m_ue.pushMergeableDef(Unit::MergeKind::Global, name, tv);
3579 visit(s);
3580 continue;
3582 break;
3584 case Expression::KindOfIncludeExpression: {
3585 auto inc = static_pointer_cast<IncludeExpression>(e);
3586 if (inc->isReqLit()) {
3587 if (FileScopeRawPtr f = inc->getIncludedFile(ar)) {
3588 if (StatementListPtr sl = f->getStmt()) {
3589 FunctionScopeRawPtr ps DEBUG_ONLY =
3590 sl->getFunctionScope();
3591 assert(ps && ps->inPseudoMain());
3592 m_ue.pushMergeableInclude(
3593 Unit::MergeKind::ReqDoc,
3594 makeStaticString(inc->includePath()));
3595 visit(s);
3596 continue;
3600 break;
3602 default:
3603 break;
3605 } // fall through
3606 default:
3607 if (mainReturn.m_type != kInvalidDataType) break;
3608 notMergeOnly = true;
3609 visit(s);
3613 if (!notMergeOnly) {
3614 m_ue.m_mergeOnly = true;
3615 if (mainReturn.m_type == kInvalidDataType) {
3616 tvWriteUninit(&mainReturn);
3617 if (boost::algorithm::ends_with(filename, EVAL_FILENAME_SUFFIX)) {
3618 tvAsVariant(&mainReturn) = init_null();
3619 } else {
3620 tvAsVariant(&mainReturn) = 1;
3623 m_ue.m_mainReturn = mainReturn;
3626 // Pseudo-main returns the integer value 1 by default. If the
3627 // current position in the bytecode is reachable, emit code to
3628 // return 1.
3629 if (currentPositionIsReachable()) {
3630 Location::Range loc;
3631 if (m_ue.bcPos() > 0) loc.line0 = -1;
3632 e.setTempLocation(loc);
3633 if (boost::algorithm::ends_with(filename, EVAL_FILENAME_SUFFIX)) {
3634 e.Null();
3635 } else {
3636 e.Int(1);
3638 e.RetC();
3639 e.setTempLocation(OptLocation());
3643 if (!m_evalStack.empty()) {
3644 InvariantViolation("Eval stack was not empty as expected before "
3645 "emitPostponed* phase");
3648 // Method bodies
3649 emitPostponedMeths();
3650 emitPostponedCtors();
3651 emitPostponedPinits();
3652 emitPostponedSinits();
3653 emitPostponedCinits();
3656 static StringData* getClassName(ExpressionPtr e) {
3657 ClassScopeRawPtr cls;
3658 if (e->isThis()) {
3659 cls = e->getClassScope();
3661 if (cls && !cls->isTrait()) {
3662 return makeStaticString(cls->getScopeName());
3664 return nullptr;
3667 void EmitterVisitor::fixReturnType(Emitter& e, FunctionCallPtr fn,
3668 Func* builtinFunc) {
3669 int ref = -1;
3670 if (fn->hasAnyContext(Expression::RefValue |
3671 Expression::DeepReference |
3672 Expression::LValue |
3673 Expression::OprLValue |
3674 Expression::UnsetContext)) {
3675 return;
3677 if (builtinFunc) {
3678 ref = (builtinFunc->attrs() & AttrReference) != 0;
3679 } else if (fn->isValid() && fn->getFuncScope()) {
3680 ref = fn->getFuncScope()->isRefReturn();
3681 } else if (!fn->getOriginalName().empty()) {
3682 FunctionScope::FunctionInfoPtr fi =
3683 FunctionScope::GetFunctionInfo(fn->getOriginalName());
3684 if (!fi || !fi->getMaybeRefReturn()) ref = false;
3687 if (!fn->isUnused() &&
3688 ref >= 0 &&
3689 (!ref || !fn->hasAnyContext(Expression::AccessContext |
3690 Expression::ObjectContext))) {
3691 /* we dont support V in M-vectors, so leave it as an R in that
3692 case */
3693 assert(m_evalStack.get(m_evalStack.size() - 1) == StackSym::R);
3694 if (ref) {
3695 e.BoxRNop();
3696 } else {
3697 e.UnboxRNop();
3702 void EmitterVisitor::visitKids(ConstructPtr c) {
3703 for (int i = 0, nk = c->getKidCount(); i < nk; i++) {
3704 ConstructPtr kid(c->getNthKid(i));
3705 visit(kid);
3709 template<uint32_t MaxMakeSize, class Fun>
3710 bool checkKeys(ExpressionPtr init_expr, bool check_size, Fun fun) {
3711 if (init_expr->getKindOf() != Expression::KindOfExpressionList) {
3712 return false;
3715 auto el = static_pointer_cast<ExpressionList>(init_expr);
3716 int n = el->getCount();
3717 if (n < 1 || (check_size && n > MaxMakeSize)) {
3718 return false;
3721 for (int i = 0, n = el->getCount(); i < n; ++i) {
3722 ExpressionPtr ex = (*el)[i];
3723 if (ex->getKindOf() != Expression::KindOfArrayPairExpression) {
3724 return false;
3726 auto ap = static_pointer_cast<ArrayPairExpression>(ex);
3727 if (ap->isRef()) return false;
3728 if (!fun(ap)) return false;
3730 return true;
3734 * isPackedInit() returns true if this expression list looks like an
3735 * array with no keys and no ref values; e.g. array(x,y,z).
3737 * In this case we can NewPackedArray to create the array. The elements are
3738 * pushed on the stack, so we arbitrarily limit this to a small multiple of
3739 * MixedArray::SmallSize (12).
3741 bool isPackedInit(ExpressionPtr init_expr, int* size,
3742 bool check_size = true) {
3743 *size = 0;
3744 return checkKeys<MixedArray::MaxMakeSize>(init_expr, check_size,
3745 [&](ArrayPairExpressionPtr ap) {
3746 Variant key;
3748 // If we have a key...
3749 if (ap->getName() != nullptr) {
3750 // ...and it has no scalar value, bail.
3751 if (!ap->getName()->getScalarValue(key)) return false;
3753 if (key.isInteger()) {
3754 // If it's an integer key, check if it's the next packed index.
3755 if (key.asInt64Val() != *size) return false;
3756 } else if (key.isBoolean()) {
3757 // Bool to Int conversion
3758 if (static_cast<int>(key.asBooleanVal()) != *size) return false;
3759 } else {
3760 // Give up if it's not a string.
3761 if (!key.isString()) return false;
3763 int64_t i; double d;
3764 auto numtype = key.getStringData()->isNumericWithVal(i, d, false);
3766 // If it's a string of the next packed index,
3767 if (numtype != KindOfInt64 || i != *size) return false;
3771 (*size)++;
3772 return true;
3777 * isStructInit() is like isPackedInit(), but returns true if the keys are
3778 * all static strings with no duplicates.
3780 bool isStructInit(ExpressionPtr init_expr, std::vector<std::string>& keys) {
3781 return checkKeys<MixedArray::MaxStructMakeSize>(init_expr, true,
3782 [&](ArrayPairExpressionPtr ap) {
3783 auto key = ap->getName();
3784 if (key == nullptr || !key->isLiteralString()) return false;
3785 auto name = key->getLiteralString();
3786 int64_t ival;
3787 double dval;
3788 auto kind = is_numeric_string(name.data(), name.size(), &ival, &dval, 0);
3789 if (kind != KindOfNull) return false; // don't allow numeric keys
3790 if (std::find(keys.begin(), keys.end(), name) != keys.end()) return false;
3791 keys.push_back(name);
3792 return true;
3796 void EmitterVisitor::emitCall(Emitter& e,
3797 FunctionCallPtr func,
3798 ExpressionListPtr params,
3799 Offset fpiStart) {
3800 auto const numParams = params ? params->getCount() : 0;
3801 auto const unpack = func->hasUnpack();
3802 if (!func->checkUnpackParams()) {
3803 throw IncludeTimeFatalException(
3804 func, "Only the last parameter in a function call is allowed to use ...");
3807 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
3808 for (int i = 0; i < numParams; i++) {
3809 auto param = (*params)[i];
3810 emitFuncCallArg(e, param, i, param->isUnpack());
3814 if (unpack) {
3815 e.FCallUnpack(numParams);
3816 } else {
3817 e.FCall(numParams);
3821 bool EmitterVisitor::visit(ConstructPtr node) {
3822 if (!node) return false;
3824 SCOPE_ASSERT_DETAIL("visit Construct") { return node->getText(); };
3826 Emitter e(node, m_ue, *this);
3828 switch (node->getKindOf()) {
3829 case Construct::KindOfBlockStatement:
3830 case Construct::KindOfStatementList:
3831 visitKids(node);
3832 return false;
3834 case Construct::KindOfTypedefStatement: {
3835 emitMakeUnitFatal(e, "Type statements are currently only allowed at "
3836 "the top-level");
3837 return false;
3840 case Construct::KindOfDeclareStatement: {
3841 auto ds = static_pointer_cast<DeclareStatement>(node);
3842 for (auto& decl : ds->getDeclareMap()) {
3843 if (decl.first == "strict_types") {
3844 emitMakeUnitFatal(e, "strict_types declaration must not use "
3845 "block mode");
3849 visit(ds->getBlock());
3850 return false;
3853 case Construct::KindOfContinueStatement:
3854 case Construct::KindOfBreakStatement: {
3855 auto s = static_pointer_cast<Statement>(node);
3856 auto bs = static_pointer_cast<BreakStatement>(s);
3857 uint64_t destLevel = bs->getDepth();
3859 if (destLevel > m_regions.back()->getMaxBreakContinueDepth()) {
3860 std::ostringstream msg;
3861 msg << "Cannot break/continue " << destLevel << " level";
3862 if (destLevel > 1) {
3863 msg << "s";
3865 emitMakeUnitFatal(e, msg.str().c_str());
3866 return false;
3869 if (bs->is(Construct::KindOfBreakStatement)) {
3870 emitBreak(e, destLevel, bs);
3871 } else {
3872 emitContinue(e, destLevel, bs);
3875 return false;
3878 case Construct::KindOfDoStatement: {
3879 auto s = static_pointer_cast<Statement>(node);
3880 auto region = createRegion(s, Region::Kind::LoopOrSwitch);
3881 auto ds = static_pointer_cast<DoStatement>(s);
3883 Label top(e);
3884 Label& condition =
3885 registerContinue(ds, region.get(), 1, false)->m_label;
3886 Label& exit =
3887 registerBreak(ds, region.get(), 1, false)->m_label;
3889 enterRegion(region);
3890 SCOPE_EXIT { leaveRegion(region); };
3891 visit(ds->getBody());
3893 condition.set(e);
3895 ExpressionPtr c = ds->getCondExp();
3896 Emitter condEmitter(c, m_ue, *this);
3897 visitIfCondition(c, condEmitter, top, exit, false);
3900 if (exit.isUsed()) exit.set(e);
3901 return false;
3904 case Construct::KindOfCaseStatement: {
3905 // Should never be called. Handled in visitSwitch.
3906 not_reached();
3909 case Construct::KindOfCatchStatement: {
3910 // Store the current exception object in the appropriate local variable
3911 auto cs = static_pointer_cast<CatchStatement>(node);
3912 StringData* vName = makeStaticString(cs->getVariable()->getName());
3913 Id i = m_curFunc->lookupVarId(vName);
3914 emitVirtualLocal(i);
3915 e.Catch();
3916 emitSet(e);
3917 emitPop(e);
3918 visit(cs->getStmt());
3919 return false;
3922 case Construct::KindOfEchoStatement: {
3923 auto es = static_pointer_cast<EchoStatement>(node);
3924 auto exps = es->getExpressionList();
3925 int count = exps->getCount();
3926 for (int i = 0; i < count; i++) {
3927 visit((*exps)[i]);
3928 emitConvertToCell(e);
3929 e.Print();
3930 e.PopC();
3932 return false;
3935 case Construct::KindOfExpStatement: {
3936 auto s = static_pointer_cast<Statement>(node);
3937 auto es = static_pointer_cast<ExpStatement>(s);
3938 if (visit(es->getExpression())) {
3939 // reachability tracking isn't very sophisticated; emitting a pop
3940 // when we're unreachable will make the emitter think the next
3941 // position is reachable.
3942 // In that case, it will spit out Null;RetC at the end of the
3943 // function, which can cause issues if an asm expression has
3944 // already output fault funclets.
3945 if (currentPositionIsReachable()) {
3946 emitPop(e);
3947 } else {
3948 popEvalStack(StackSym::C);
3951 return false;
3954 case Construct::KindOfForStatement: {
3955 auto s = static_pointer_cast<Statement>(node);
3956 auto region = createRegion(s, Region::Kind::LoopOrSwitch);
3957 auto fs = static_pointer_cast<ForStatement>(s);
3959 if (visit(fs->getInitExp())) {
3960 emitPop(e);
3962 Label preCond(e);
3963 Label& preInc = registerContinue(fs, region.get(), 1, false)->m_label;
3964 Label& fail = registerBreak(fs, region.get(), 1, false)->m_label;
3965 ExpressionPtr condExp = fs->getCondExp();
3966 auto emit_cond = [&] (Label& tru, bool truFallthrough) {
3967 if (!condExp) return;
3968 Emitter condEmitter(condExp, m_ue, *this);
3969 visitIfCondition(condExp, condEmitter, tru, fail, truFallthrough);
3971 Label top;
3972 emit_cond(top, true);
3973 top.set(e);
3975 enterRegion(region);
3976 SCOPE_EXIT { leaveRegion(region); };
3977 visit(fs->getBody());
3979 preInc.set(e);
3980 if (visit(fs->getIncExp())) {
3981 emitPop(e);
3983 if (!condExp) {
3984 e.Jmp(top);
3985 } else {
3986 emit_cond(top, false);
3988 if (fail.isUsed()) fail.set(e);
3989 return false;
3992 case Construct::KindOfForEachStatement: {
3993 auto fe = static_pointer_cast<ForEachStatement>(node);
3994 if (fe->isAwaitAs()) {
3995 emitForeachAwaitAs(e, fe);
3996 } else {
3997 emitForeach(e, fe);
3999 return false;
4002 case Construct::KindOfGlobalStatement: {
4003 auto vars = static_pointer_cast<GlobalStatement>(node)->getVars();
4004 for (int i = 0, n = vars->getCount(); i < n; i++) {
4005 ExpressionPtr var((*vars)[i]);
4006 if (var->is(Construct::KindOfSimpleVariable)) {
4007 auto sv = static_pointer_cast<SimpleVariable>(var);
4008 if (sv->isSuperGlobal()) {
4009 continue;
4011 StringData* nLiteral = makeStaticString(sv->getName());
4012 Id i = m_curFunc->lookupVarId(nLiteral);
4013 emitVirtualLocal(i);
4014 e.String(nLiteral);
4015 markGlobalName(e);
4016 e.VGetG();
4017 emitBind(e);
4018 e.PopV();
4019 } else if (var->is(Construct::KindOfDynamicVariable)) {
4020 // global $<exp> =& $GLOBALS[<exp>]
4021 auto dv = static_pointer_cast<DynamicVariable>(var);
4022 // Get the variable name as a cell, for the LHS
4023 visit(dv->getSubExpression());
4024 emitConvertToCell(e);
4025 // Copy the variable name, for indexing into $GLOBALS
4026 e.Dup();
4027 markNameSecond(e);
4028 markGlobalName(e);
4029 e.VGetG();
4030 e.BindN();
4031 e.PopV();
4032 } else {
4033 not_implemented();
4036 return false;
4039 case Construct::KindOfIfStatement: {
4040 auto ifp = static_pointer_cast<IfStatement>(node);
4041 StatementListPtr branches(ifp->getIfBranches());
4042 int nb = branches->getCount();
4043 Label done;
4044 for (int i = 0; i < nb; i++) {
4045 auto branch = static_pointer_cast<IfBranchStatement>((*branches)[i]);
4046 Label fals;
4047 if (branch->getCondition()) {
4048 Label tru;
4049 Emitter condEmitter(branch->getCondition(), m_ue, *this);
4050 visitIfCondition(branch->getCondition(), condEmitter,
4051 tru, fals, true);
4052 if (tru.isUsed()) {
4053 tru.set(e);
4056 visit(branch->getStmt());
4057 if (currentPositionIsReachable() && i + 1 < nb) {
4058 e.Jmp(done);
4060 if (fals.isUsed()) {
4061 fals.set(e);
4064 if (done.isUsed()) {
4065 done.set(e);
4067 return false;
4070 case Construct::KindOfIfBranchStatement:
4071 not_reached(); // handled by KindOfIfStatement
4073 case Construct::KindOfReturnStatement: {
4074 auto r = static_pointer_cast<ReturnStatement>(node);
4076 char retSym = StackSym::C;
4077 if (visit(r->getRetExp())) {
4078 if (r->getRetExp()->getContext() & Expression::RefValue &&
4079 // Generators don't support returning by references
4080 !m_curFunc->isGenerator) {
4081 emitConvertToVar(e);
4082 retSym = StackSym::V;
4083 } else {
4084 emitConvertToCell(e);
4086 } else {
4087 e.Null();
4089 assert(m_evalStack.size() == 1);
4090 assert(IMPLIES(m_curFunc->isAsync || m_curFunc->isGenerator,
4091 retSym == StackSym::C));
4092 emitReturn(e, retSym, r);
4093 return false;
4096 case Construct::KindOfStaticStatement: {
4097 auto vars = static_pointer_cast<StaticStatement>(node)->getVars();
4098 for (int i = 0, n = vars->getCount(); i < n; i++) {
4099 ExpressionPtr se((*vars)[i]);
4100 assert(se->is(Construct::KindOfAssignmentExpression));
4101 auto ae = static_pointer_cast<AssignmentExpression>(se);
4102 ExpressionPtr var(ae->getVariable());
4103 ExpressionPtr value(ae->getValue());
4104 assert(var->is(Construct::KindOfSimpleVariable));
4105 auto sv = static_pointer_cast<SimpleVariable>(var);
4106 StringData* name = makeStaticString(sv->getName());
4107 Id local = m_curFunc->lookupVarId(name);
4109 if (m_staticEmitted.insert(sv->getName()).second) {
4110 Func::SVInfo svInfo;
4111 svInfo.name = name;
4112 std::ostringstream os;
4113 CodeGenerator cg(&os, CodeGenerator::PickledPHP);
4114 auto ar = std::make_shared<AnalysisResult>();
4115 value->outputPHP(cg, ar);
4116 svInfo.phpCode = makeStaticString(os.str());
4117 m_curFunc->staticVars.push_back(svInfo);
4120 if (value->isScalar()) {
4121 emitVirtualLocal(local);
4122 visit(value);
4123 emitConvertToCell(e);
4124 e.StaticLocInit(local, name);
4125 } else {
4126 Label done;
4127 emitVirtualLocal(local);
4128 e.StaticLoc(local, name);
4129 e.JmpNZ(done);
4131 emitVirtualLocal(local);
4132 visit(value);
4133 emitConvertToCell(e);
4134 emitSet(e);
4135 emitPop(e);
4137 done.set(e);
4140 return false;
4143 case Construct::KindOfSwitchStatement: {
4144 auto s = static_pointer_cast<Statement>(node);
4145 auto region = createRegion(s, Region::Kind::LoopOrSwitch);
4146 auto sw = static_pointer_cast<SwitchStatement>(node);
4148 auto cases = sw->getCases();
4149 if (!cases) {
4150 visit(sw->getExp());
4151 emitPop(e);
4152 return false;
4154 uint32_t ncase = cases->getCount();
4155 std::vector<Label> caseLabels(ncase);
4156 Label& brkTarget = registerBreak(sw, region.get(), 1, false)->m_label;
4157 Label& contTarget =
4158 registerContinue(sw, region.get(), 1, false)->m_label;
4159 // There are two different ways this can go. If the subject is a simple
4160 // variable, then we have to evaluate it every time we compare against a
4161 // case condition. Otherwise, we evaluate it once and store it in an
4162 // unnamed local. This is because (a) switch statements are equivalent
4163 // to a series of if-elses, and (b) Zend has some weird evaluation order
4164 // rules. For example, "$a == ++$a" is true but "$a[0] == ++$a[0]" is
4165 // false. In particular, if a case condition modifies the switch
4166 // subject, things behave differently depending on whether the subject
4167 // is a simple variable.
4168 auto subject = sw->getExp();
4169 bool simpleSubject = subject->is(Construct::KindOfSimpleVariable)
4170 && !static_pointer_cast<SimpleVariable>(subject)->getAlwaysStash();
4171 Id tempLocal = -1;
4172 Offset start = InvalidAbsoluteOffset;
4174 bool enabled = RuntimeOption::EvalEmitSwitch;
4175 auto call = dynamic_pointer_cast<SimpleFunctionCall>(subject);
4177 SwitchState state;
4178 bool didSwitch = false;
4179 if (enabled) {
4180 MaybeDataType stype = analyzeSwitch(sw, state);
4181 if (stype) {
4182 e.incStat(stype == KindOfInt64 ? Stats::Switch_Integer
4183 : Stats::Switch_String,
4185 if (state.cases.empty()) {
4186 // If there are no non-default cases, evaluate the subject for
4187 // side effects and fall through. If there's a default case it
4188 // will be emitted immediately after this.
4189 visit(sw->getExp());
4190 emitPop(e);
4191 } else if (stype == KindOfInt64) {
4192 emitIntegerSwitch(e, sw, caseLabels, brkTarget, state);
4193 } else {
4194 assert(isStringType(*stype));
4195 emitStringSwitch(e, sw, caseLabels, brkTarget, state);
4197 didSwitch = true;
4200 if (!didSwitch) {
4201 e.incStat(Stats::Switch_Generic, 1);
4202 if (!simpleSubject) {
4203 // Evaluate the subject once and stash it in a local
4204 tempLocal = m_curFunc->allocUnnamedLocal();
4205 emitVirtualLocal(tempLocal);
4206 visit(subject);
4207 emitConvertToCell(e);
4208 emitSet(e);
4209 emitPop(e);
4210 start = m_ue.bcPos();
4213 int defI = -1;
4214 for (uint32_t i = 0; i < ncase; i++) {
4215 auto c = static_pointer_cast<CaseStatement>((*cases)[i]);
4216 ExpressionPtr condition = c->getCondition();
4217 if (condition) {
4218 if (simpleSubject) {
4219 // Evaluate the subject every time.
4220 visit(subject);
4221 emitConvertToCellOrLoc(e);
4222 visit(condition);
4223 emitConvertToCell(e);
4224 emitConvertSecondToCell(e);
4225 } else {
4226 emitVirtualLocal(tempLocal);
4227 emitCGet(e);
4228 visit(condition);
4229 emitConvertToCell(e);
4231 e.Eq();
4232 e.JmpNZ(caseLabels[i]);
4233 } else if (LIKELY(defI == -1)) {
4234 // Default clause.
4235 defI = i;
4236 } else {
4237 throw IncludeTimeFatalException(
4238 c, "Switch statements may only contain one default: clause");
4241 if (defI != -1) {
4242 e.Jmp(caseLabels[defI]);
4243 } else {
4244 e.Jmp(brkTarget);
4247 for (uint32_t i = 0; i < ncase; i++) {
4248 caseLabels[i].set(e);
4249 auto c = static_pointer_cast<CaseStatement>((*cases)[i]);
4250 enterRegion(region);
4251 SCOPE_EXIT { leaveRegion(region); };
4252 visit(c->getStatement());
4254 if (brkTarget.isUsed()) brkTarget.set(e);
4255 if (contTarget.isUsed()) contTarget.set(e);
4256 if (!didSwitch && !simpleSubject) {
4257 // Null out temp local, to invoke any needed refcounting
4258 assert(tempLocal >= 0);
4259 assert(start != InvalidAbsoluteOffset);
4260 newFaultRegionAndFunclet(start, m_ue.bcPos(),
4261 new UnsetUnnamedLocalThunklet(tempLocal));
4262 emitUnsetL(e, tempLocal);
4263 m_curFunc->freeUnnamedLocal(tempLocal);
4265 return false;
4268 case Construct::KindOfThrowStatement: {
4269 visitKids(node);
4270 emitConvertToCell(e);
4271 e.Throw();
4272 return false;
4275 case Construct::KindOfFinallyStatement: {
4276 auto s = static_pointer_cast<Statement>(node);
4277 auto region = createRegion(s, Region::Kind::Finally);
4278 enterRegion(region);
4279 SCOPE_EXIT { leaveRegion(region); };
4281 auto fs = static_pointer_cast<FinallyStatement>(node);
4282 visit(fs->getBody());
4283 return false;
4286 case Construct::KindOfTryStatement: {
4287 auto s = static_pointer_cast<Statement>(node);
4288 auto region = createRegion(s, Region::Kind::TryFinally);
4289 if (!m_evalStack.empty()) {
4290 InvariantViolation(
4291 "Emitter detected that the evaluation stack is not empty "
4292 "at the beginning of a try region: %d", m_ue.bcPos());
4295 auto ts = static_pointer_cast<TryStatement>(node);
4296 auto f = static_pointer_cast<FinallyStatement>(ts->getFinally());
4298 Offset start = m_ue.bcPos();
4299 Offset end;
4300 Label after;
4303 if (f) {
4304 enterRegion(region);
4306 SCOPE_EXIT {
4307 if (f) {
4308 leaveRegion(region);
4312 visit(ts->getBody());
4314 StatementListPtr catches = ts->getCatches();
4315 int catch_count = catches->getCount();
4316 if (catch_count > 0) {
4317 // include the jump out of the try-catch block in the
4318 // exception handler address range
4319 e.Jmp(after);
4321 end = m_ue.bcPos();
4322 if (!m_evalStack.empty()) {
4323 InvariantViolation("Emitter detected that the evaluation stack "
4324 "is not empty at the end of a try region: %d",
4325 end);
4328 if (catch_count > 0) {
4329 CatchRegion* r = new CatchRegion(start, end);
4330 m_catchRegions.push_back(r);
4332 bool firstHandler = true;
4333 for (int i = 0; i < catch_count; i++) {
4334 auto c = static_pointer_cast<CatchStatement>((*catches)[i]);
4335 StringData* eName = makeStaticString(c->getOriginalClassName());
4337 // If there's already a catch of this class, skip;
4338 // the first one wins
4339 if (r->m_names.find(eName) == r->m_names.end()) {
4340 // Don't let execution of the try body, or the
4341 // previous catch body,
4342 // fall into here.
4343 if (!firstHandler) {
4344 e.Jmp(after);
4345 } else {
4346 firstHandler = false;
4349 Label* label = new Label(e);
4350 r->m_names.insert(eName);
4351 r->m_catchLabels.push_back(std::pair<StringData*, Label*>(eName,
4352 label));
4353 visit(c);
4359 Offset end_catches = m_ue.bcPos();
4360 if (after.isUsed()) after.set(e);
4362 if (f) {
4363 region->m_finallyLabel.set(e);
4364 visit(f);
4365 emitFinallyEpilogue(e, region.get());
4366 auto func = getFunclet(f);
4367 if (func == nullptr) {
4368 auto thunklet =
4369 new FinallyThunklet(f, m_curFunc->numLiveIterators());
4370 func = addFunclet(f, thunklet);
4372 newFaultRegion(start, end_catches, &func->m_entry);
4375 return false;
4378 case Construct::KindOfUnsetStatement: {
4379 auto exps = static_pointer_cast<UnsetStatement>(node)->getExps();
4380 for (int i = 0, n = exps->getCount(); i < n; i++) {
4381 emitVisitAndUnset(e, (*exps)[i]);
4383 return false;
4386 case Construct::KindOfWhileStatement: {
4387 auto s = static_pointer_cast<Statement>(node);
4388 auto region = createRegion(s, Region::Kind::LoopOrSwitch);
4389 auto ws = static_pointer_cast<WhileStatement>(s);
4390 ExpressionPtr condExp(ws->getCondExp());
4391 Label& lcontinue = registerContinue(ws, region.get(), 1,
4392 false)->m_label;
4393 Label& fail = registerBreak(ws, region.get(), 1, false)->m_label;
4394 Label top;
4395 auto emit_cond = [&] (Label& tru, bool truFallthrough) {
4396 Emitter condEmitter(condExp, m_ue, *this);
4397 visitIfCondition(condExp, condEmitter, tru, fail, truFallthrough);
4399 emit_cond(top, true);
4400 top.set(e);
4402 enterRegion(region);
4403 SCOPE_EXIT { leaveRegion(region); };
4404 visit(ws->getBody());
4406 if (lcontinue.isUsed()) lcontinue.set(e);
4407 emit_cond(top, false);
4408 if (fail.isUsed()) fail.set(e);
4409 return false;
4412 case Construct::KindOfInterfaceStatement:
4413 case Construct::KindOfClassStatement: {
4414 emitClass(e, node->getClassScope(), false);
4415 return false;
4418 case Construct::KindOfClassVariable:
4419 case Construct::KindOfClassConstant:
4420 case Construct::KindOfMethodStatement:
4421 // handled by emitClass
4422 not_reached();
4424 case Construct::KindOfFunctionStatement: {
4425 auto m = static_pointer_cast<MethodStatement>(node);
4426 // Only called for fn defs not on the top level
4427 assert(!node->getClassScope()); // Handled directly by emitClass().
4428 if (auto msg = node->getFunctionScope()->getFatalMessage()) {
4429 emitMakeUnitFatal(e, msg->data());
4430 return false;
4432 StringData* nName = makeStaticString(m->getOriginalName());
4433 FuncEmitter* fe = m_ue.newFuncEmitter(nName);
4434 e.DefFunc(fe->id());
4435 postponeMeth(m, fe, false);
4436 return false;
4439 case Construct::KindOfGotoStatement: {
4440 auto g = static_pointer_cast<GotoStatement>(node);
4441 StringData* nName = makeStaticString(g->label());
4442 emitGoto(e, nName, g);
4443 return false;
4446 case Construct::KindOfLabelStatement: {
4447 auto l = static_pointer_cast<LabelStatement>(node);
4448 StringData* nName = makeStaticString(l->label());
4449 registerGoto(l, m_regions.back().get(), nName, false)
4450 ->m_label.set(e);
4451 return false;
4453 case Construct::KindOfStatement:
4454 case Construct::KindOfUseTraitStatement:
4455 case Construct::KindOfClassRequireStatement:
4456 case Construct::KindOfTraitPrecStatement:
4457 case Construct::KindOfTraitAliasStatement: {
4458 not_implemented();
4460 case Construct::KindOfUnaryOpExpression: {
4461 auto u = static_pointer_cast<UnaryOpExpression>(node);
4462 int op = u->getOp();
4464 if (op == T_UNSET) {
4465 // php doesnt have an unset expression, but hphp's optimizations
4466 // sometimes introduce them
4467 auto exp = u->getExpression();
4468 if (exp->is(Construct::KindOfExpressionList)) {
4469 auto exps = static_pointer_cast<ExpressionList>(exp);
4470 if (exps->getListKind() == ExpressionList::ListKindParam) {
4471 for (int i = 0, n = exps->getCount(); i < n; i++) {
4472 emitVisitAndUnset(e, (*exps)[i]);
4474 e.Null();
4475 return true;
4478 emitVisitAndUnset(e, exp);
4479 e.Null();
4480 return true;
4483 if (op == T_ARRAY) {
4484 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
4485 emitArrayInit(e, el);
4486 return true;
4489 if (op == T_DICT) {
4490 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
4491 emitArrayInit(e, el, HeaderKind::Dict);
4492 return true;
4495 if (op == T_VEC) {
4496 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
4497 emitArrayInit(e, el, HeaderKind::VecArray);
4498 return true;
4501 if (op == T_KEYSET) {
4502 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
4503 emitArrayInit(e, el, HeaderKind::Keyset);
4504 return true;
4507 if (op == T_ISSET) {
4508 auto list = dynamic_pointer_cast<ExpressionList>(u->getExpression());
4509 if (list) {
4510 // isset($a, $b, ...) ==> isset($a) && isset($b) && ...
4511 Label done;
4512 int n = list->getCount();
4513 for (int i = 0; i < n - 1; ++i) {
4514 visit((*list)[i]);
4515 emitIsset(e);
4516 e.Dup();
4517 e.JmpZ(done);
4518 emitPop(e);
4520 // Treat the last one specially; let it fall through
4521 visit((*list)[n - 1]);
4522 emitIsset(e);
4523 done.set(e);
4524 } else {
4525 // Simple case
4526 visit(u->getExpression());
4527 emitIsset(e);
4529 return true;
4530 } else if (op == '+' || op == '-') {
4531 e.Int(0);
4534 Id oldErrorLevelLoc = -1;
4535 Offset start = InvalidAbsoluteOffset;
4536 if (op == '@') {
4537 oldErrorLevelLoc = m_curFunc->allocUnnamedLocal();
4538 emitVirtualLocal(oldErrorLevelLoc);
4539 auto idx = m_evalStack.size() - 1;
4540 e.Silence(m_evalStack.getLoc(idx), SilenceOp::Start);
4541 start = m_ue.bcPos();
4544 ExpressionPtr exp = u->getExpression();
4545 if (exp && visit(exp)) {
4546 if (op != T_EMPTY && op != T_INC && op != T_DEC) {
4547 emitConvertToCell(e);
4549 } else if (op == T_EXIT) {
4550 // exit without an expression is treated as exit(0)
4551 e.Int(0);
4552 } else {
4553 // __FILE__ and __DIR__ are special unary ops that don't
4554 // have expressions
4555 assert(op == T_FILE || op == T_DIR || op == T_METHOD_C);
4557 switch (op) {
4558 case T_INC:
4559 case T_DEC: {
4560 // $this++ is a no-op
4561 if (auto var = dynamic_pointer_cast<SimpleVariable>(exp)) {
4562 if (var->isThis()) break;
4565 auto const cop = [&] {
4566 if (op == T_INC) {
4567 if (RuntimeOption::IntsOverflowToInts) {
4568 return u->getFront() ? IncDecOp::PreInc : IncDecOp::PostInc;
4570 return u->getFront() ? IncDecOp::PreIncO : IncDecOp::PostIncO;
4572 if (RuntimeOption::IntsOverflowToInts) {
4573 return u->getFront() ? IncDecOp::PreDec : IncDecOp::PostDec;
4575 return u->getFront() ? IncDecOp::PreDecO : IncDecOp::PostDecO;
4576 }();
4577 emitIncDec(e, cop);
4578 break;
4580 case T_EMPTY: emitEmpty(e); break;
4581 case T_CLONE: e.Clone(); break;
4582 case '+':
4583 RuntimeOption::IntsOverflowToInts ? e.Add() : e.AddO();
4584 break;
4585 case '-':
4586 RuntimeOption::IntsOverflowToInts ? e.Sub() : e.SubO();
4587 break;
4588 case '!': e.Not(); break;
4589 case '~': e.BitNot(); break;
4590 case '(': break;
4591 case T_INT_CAST: e.CastInt(); break;
4592 case T_DOUBLE_CAST: e.CastDouble(); break;
4593 case T_STRING_CAST: e.CastString(); break;
4594 case T_ARRAY_CAST: e.CastArray(); break;
4595 case T_OBJECT_CAST: e.CastObject(); break;
4596 case T_BOOL_CAST: e.CastBool(); break;
4597 case T_UNSET_CAST: emitPop(e); e.Null(); break;
4598 case T_EXIT: e.Exit(); break;
4599 case '@': {
4600 assert(oldErrorLevelLoc >= 0);
4601 assert(start != InvalidAbsoluteOffset);
4602 newFaultRegionAndFunclet(start, m_ue.bcPos(),
4603 new RestoreErrorReportingThunklet(oldErrorLevelLoc));
4604 emitRestoreErrorReporting(e, oldErrorLevelLoc);
4605 m_curFunc->freeUnnamedLocal(oldErrorLevelLoc);
4606 break;
4608 case T_PRINT: e.Print(); break;
4609 case T_EVAL: e.Eval(); break;
4610 case T_FILE: {
4611 e.File();
4612 break;
4614 case T_DIR: {
4615 e.Dir();
4616 break;
4618 case T_METHOD_C: {
4619 e.Method();
4620 break;
4622 default:
4623 assert(false);
4625 return true;
4628 case Construct::KindOfAssignmentExpression: {
4629 auto ae = static_pointer_cast<AssignmentExpression>(node);
4630 ExpressionPtr rhs = ae->getValue();
4631 Id tempLocal = -1;
4632 Offset start = InvalidAbsoluteOffset;
4634 if (ae->isRhsFirst()) {
4635 assert(!rhs->hasContext(Expression::RefValue));
4636 tempLocal = emitVisitAndSetUnnamedL(e, rhs);
4637 start = m_ue.bcPos();
4640 visit(ae->getVariable());
4641 emitClsIfSPropBase(e);
4643 if (ae->isRhsFirst()) {
4644 emitPushAndFreeUnnamedL(e, tempLocal, start);
4645 } else {
4646 visit(rhs);
4649 if (rhs->hasContext(Expression::RefValue)) {
4650 emitConvertToVar(e);
4651 emitBind(e);
4652 if (ae->hasAnyContext(Expression::AccessContext|
4653 Expression::ObjectContext|
4654 Expression::ExistContext)) {
4656 * hphpc optimizations can result in
4657 * ($x =& $y)->foo or ($x =& $y)['foo'] or empty($x =& $y)
4659 emitConvertToCellIfVar(e);
4661 } else {
4662 emitConvertToCell(e);
4663 emitSet(e);
4665 return true;
4668 case Construct::KindOfBinaryOpExpression: {
4669 auto b = static_pointer_cast<BinaryOpExpression>(node);
4670 int op = b->getOp();
4671 if (b->isAssignmentOp()) {
4672 visit(b->getExp1());
4673 emitClsIfSPropBase(e);
4674 visit(b->getExp2());
4675 emitConvertToCell(e);
4676 emitSetOp(e, op);
4677 return true;
4680 if (b->isShortCircuitOperator()) {
4681 Label tru, fls, done;
4682 visitIfCondition(b, e, tru, fls, false);
4683 if (fls.isUsed()) fls.set(e);
4684 if (currentPositionIsReachable()) {
4685 e.False();
4686 e.Jmp(done);
4688 if (tru.isUsed()) tru.set(e);
4689 if (currentPositionIsReachable()) {
4690 e.True();
4692 done.set(e);
4693 return true;
4696 if (op == T_INSTANCEOF) {
4697 visit(b->getExp1());
4698 emitConvertToCell(e);
4699 ExpressionPtr second = b->getExp2();
4700 if (second->isScalar()) {
4701 auto scalar = dynamic_pointer_cast<ScalarExpression>(second);
4702 bool notQuoted = scalar && !scalar->isQuoted();
4703 std::string s = second->getLiteralString();
4705 const auto isame =
4706 [](const std::string& a, const std::string& b) {
4707 return (a.size() == b.size()) &&
4708 !strncasecmp(a.c_str(), b.c_str(), a.size());
4711 if (notQuoted && isame(s, "static")) {
4712 // Can't resolve this to a literal name at emission time
4713 static const StringData* fname
4714 = makeStaticString("get_called_class");
4715 e.FCallBuiltin(0, 0, fname);
4716 e.UnboxRNop();
4717 e.InstanceOf();
4718 } else if (s != "") {
4719 ClassScopeRawPtr cls = second->getClassScope();
4720 bool isTrait = cls && cls->isTrait();
4721 bool isSelf = notQuoted && isame(s, "self");
4722 bool isParent = notQuoted && isame(s, "parent");
4724 if (isTrait && (isSelf || isParent)) {
4725 emitConvertToCell(e);
4726 if (isSelf) {
4727 e.Self();
4728 } else if (isParent) {
4729 e.Parent();
4732 e.NameA();
4733 e.InstanceOf();
4734 } else {
4735 if (cls) {
4736 if (isSelf) {
4737 s = cls->getScopeName();
4738 } else if (isParent) {
4739 s = cls->getOriginalParent();
4743 StringData* nLiteral = makeStaticString(s);
4744 e.InstanceOfD(nLiteral);
4746 } else {
4747 visit(b->getExp2());
4748 emitConvertToCell(e);
4749 e.InstanceOf();
4751 } else {
4752 visit(b->getExp2());
4753 emitConvertToCell(e);
4754 e.InstanceOf();
4756 return true;
4759 if (op == T_COLLECTION) {
4760 emitCollectionInit(e, b);
4761 return true;
4764 if (op == T_PIPE) {
4765 Id pipeVar = emitVisitAndSetUnnamedL(e, b->getExp1());
4766 allocPipeLocal(pipeVar);
4767 visit(b->getExp2());
4768 emitConvertToCell(e);
4769 releasePipeLocal(pipeVar);
4770 emitFreeUnnamedL(e, pipeVar, m_ue.bcPos());
4771 return true;
4774 visit(b->getExp1());
4775 emitConvertToCellOrLoc(e);
4776 visit(b->getExp2());
4777 emitConvertToCell(e);
4778 emitConvertSecondToCell(e);
4779 switch (op) {
4780 case T_LOGICAL_XOR: e.Xor(); break;
4781 case '|': e.BitOr(); break;
4782 case '&': e.BitAnd(); break;
4783 case '^': e.BitXor(); break;
4784 case '.': e.Concat(); break;
4785 case '+':
4786 RuntimeOption::IntsOverflowToInts ? e.Add() : e.AddO();
4787 break;
4788 case '-':
4789 RuntimeOption::IntsOverflowToInts ? e.Sub() : e.SubO();
4790 break;
4791 case '*':
4792 RuntimeOption::IntsOverflowToInts ? e.Mul() : e.MulO();
4793 break;
4794 case '/': e.Div(); break;
4795 case '%': e.Mod(); break;
4796 case T_SL: e.Shl(); break;
4797 case T_SR: e.Shr(); break;
4798 case T_IS_IDENTICAL: e.Same(); break;
4799 case T_IS_NOT_IDENTICAL: e.NSame(); break;
4800 case T_IS_EQUAL: e.Eq(); break;
4801 case T_IS_NOT_EQUAL: e.Neq(); break;
4802 case '<': e.Lt(); break;
4803 case T_IS_SMALLER_OR_EQUAL: e.Lte(); break;
4804 case '>': e.Gt(); break;
4805 case T_IS_GREATER_OR_EQUAL: e.Gte(); break;
4806 case T_SPACESHIP: e.Cmp(); break;
4807 case T_POW: e.Pow(); break;
4808 default: assert(false);
4810 return true;
4813 case Construct::KindOfClassConstantExpression: {
4814 auto cc = static_pointer_cast<ClassConstantExpression>(node);
4815 auto const nName = makeStaticString(cc->getConName());
4816 auto const getOriginalClassName = [&] {
4817 const std::string& clsName = cc->getOriginalClassName();
4818 return makeStaticString(clsName);
4821 // We treat ::class as a class constant in the AST and the
4822 // parser, but at the bytecode and runtime level it isn't
4823 // one.
4824 auto const emitClsCns = [&] {
4825 if (cc->isColonColonClass()) {
4826 e.NameA();
4827 return;
4829 e.ClsCns(nName);
4831 auto const noClassAllowed = [&] {
4832 auto const nCls = getOriginalClassName();
4833 std::ostringstream s;
4834 s << "Cannot access " << nCls->data() << "::" << nName->data() <<
4835 " when no class scope is active";
4836 throw IncludeTimeFatalException(cc, s.str().c_str());
4839 if (cc->isStatic()) {
4840 // static::Constant
4841 e.LateBoundCls();
4842 emitClsCns();
4843 } else if (cc->getClass()) {
4844 // $x::Constant
4845 ExpressionPtr cls(cc->getClass());
4846 visit(cls);
4847 emitAGet(e);
4848 emitClsCns();
4849 } else if (cc->getOriginalClassScope() &&
4850 !cc->getOriginalClassScope()->isTrait()) {
4851 // C::Constant inside a class
4852 auto nCls = getOriginalClassName();
4853 if (cc->isColonColonClass()) {
4854 e.String(nCls);
4855 } else {
4856 e.ClsCnsD(nName, nCls);
4858 } else if (cc->isSelf()) {
4859 // self::Constant inside trait or pseudomain
4860 e.Self();
4861 if (cc->isColonColonClass() &&
4862 cc->getFunctionScope()->inPseudoMain()) {
4863 noClassAllowed();
4865 emitClsCns();
4866 } else if (cc->isParent()) {
4867 // parent::Constant inside trait or pseudomain
4868 e.Parent();
4869 if (cc->isColonColonClass() &&
4870 cc->getFunctionScope()->inPseudoMain()) {
4871 noClassAllowed();
4873 emitClsCns();
4874 } else {
4875 // C::Constant inside a trait or pseudomain
4876 // Be careful to keep this case here after the isSelf and
4877 // isParent cases because StaticClassName::resolveClass()
4878 // will set cc->originalClassName to the trait's name for
4879 // the isSelf and isParent cases, but self and parent must
4880 // be resolved dynamically when used inside of traits.
4881 auto nCls = getOriginalClassName();
4882 if (cc->isColonColonClass()) noClassAllowed();
4883 e.ClsCnsD(nName, nCls);
4885 return true;
4888 case Construct::KindOfConstantExpression: {
4889 auto c = static_pointer_cast<ConstantExpression>(node);
4890 if (c->isNull()) {
4891 e.Null();
4892 } else if (c->isBoolean()) {
4893 if (c->getBooleanValue()) {
4894 e.True();
4895 } else {
4896 e.False();
4898 return true;
4899 } else {
4900 std::string nameStr = c->getOriginalName();
4901 StringData* nName = makeStaticString(nameStr);
4902 if (c->hadBackslash()) {
4903 e.CnsE(nName);
4904 } else {
4905 const std::string& nonNSName = c->getNonNSOriginalName();
4906 if (nonNSName != nameStr) {
4907 StringData* nsName = nName;
4908 nName = makeStaticString(nonNSName);
4909 e.CnsU(nsName, nName);
4910 } else {
4911 e.Cns(makeStaticString(c->getName()));
4915 return true;
4918 case Construct::KindOfEncapsListExpression: {
4919 auto el = static_pointer_cast<EncapsListExpression>(node);
4920 auto args = el->getExpressions();
4921 int n = args ? args->getCount() : 0;
4922 int i = 0;
4923 FPIRegionRecorder* fpi = nullptr;
4924 if (el->getType() == '`') {
4925 const static StringData* s_shell_exec =
4926 makeStaticString("shell_exec");
4927 Offset fpiStart = m_ue.bcPos();
4928 e.FPushFuncD(1, s_shell_exec);
4929 fpi = new FPIRegionRecorder(this, m_ue, m_evalStack, fpiStart);
4932 if (n) {
4933 visit((*args)[i++]);
4934 emitConvertToCellOrLoc(e);
4935 if (i == n) {
4936 emitConvertToCell(e);
4937 e.CastString();
4938 } else {
4939 while (i < n) {
4940 visit((*args)[i++]);
4941 emitConvertToCell(e);
4942 emitConvertSecondToCell(e);
4943 e.Concat();
4946 } else {
4947 e.String(staticEmptyString());
4950 if (el->getType() == '`') {
4951 emitConvertToCell(e);
4952 e.FPassC(0);
4953 delete fpi;
4954 e.FCall(1);
4956 return true;
4959 case Construct::KindOfArrayElementExpression: {
4960 auto ae = static_pointer_cast<ArrayElementExpression>(node);
4961 if (!ae->isSuperGlobal() || !ae->getOffset()) {
4962 visit(ae->getVariable());
4963 // XHP syntax allows for expressions like "($a =& $b)[0]". We
4964 // handle this by unboxing the var produced by "($a =& $b)".
4965 emitConvertToCellIfVar(e);
4968 ExpressionPtr offset = ae->getOffset();
4969 Variant v;
4970 if (!ae->isSuperGlobal() && offset &&
4971 offset->getScalarValue(v) && (v.isInteger() || v.isString())) {
4972 if (v.isString()) {
4973 m_evalStack.push(StackSym::T);
4974 m_evalStack.setString(
4975 makeStaticString(v.toCStrRef().get()));
4976 } else {
4977 m_evalStack.push(StackSym::I);
4978 m_evalStack.setInt(v.asInt64Val());
4980 markElem(e);
4981 } else if (visit(offset)) {
4982 emitConvertToCellOrLoc(e);
4983 if (ae->isSuperGlobal()) {
4984 markGlobalName(e);
4985 } else {
4986 markElem(e);
4988 } else {
4989 markNewElem(e);
4991 if (!ae->hasAnyContext(Expression::AccessContext|
4992 Expression::ObjectContext)) {
4993 m_tempLoc = ae->getRange();
4995 return true;
4998 case Construct::KindOfSimpleFunctionCall: {
4999 auto call = static_pointer_cast<SimpleFunctionCall>(node);
5000 auto params = call->getParams();
5002 if (call->isFatalFunction()) {
5003 if (params && params->getCount() == 1) {
5004 ExpressionPtr p = (*params)[0];
5005 Variant v;
5006 if (p->getScalarValue(v)) {
5007 assert(v.isString());
5008 StringData* msg = makeStaticString(v.toString());
5009 auto exn = IncludeTimeFatalException(call, "%s", msg->data());
5010 exn.setParseFatal(call->isParseFatalFunction());
5011 throw exn;
5013 not_reached();
5015 } else if (emitCallUserFunc(e, call)) {
5016 return true;
5017 } else if (call->isCallToFunction("array_key_exists")) {
5018 if (params && params->getCount() == 2) {
5019 visit((*params)[0]);
5020 emitConvertToCell(e);
5021 visit((*params)[1]);
5022 emitConvertToCell(e);
5023 call->changeToBytecode();
5024 e.AKExists();
5025 return true;
5027 } else if (call->isCallToFunction("hh\\asm")) {
5028 if (emitInlineHHAS(e, call)) return true;
5029 } else if (call->isCallToFunction("hh\\invariant")) {
5030 if (emitHHInvariant(e, call)) return true;
5031 } else if (call->isCallToFunction("hh\\idx") &&
5032 !RuntimeOption::EvalJitEnableRenameFunction) {
5033 if (params && (params->getCount() == 2 || params->getCount() == 3)) {
5034 visit((*params)[0]);
5035 emitConvertToCell(e);
5036 visit((*params)[1]);
5037 emitConvertToCell(e);
5038 if (params->getCount() == 2) {
5039 e.Null();
5040 } else {
5041 visit((*params)[2]);
5042 emitConvertToCell(e);
5044 call->changeToBytecode();
5045 e.Idx();
5046 return true;
5048 } else if (call->isCallToFunction("hphp_array_idx")) {
5049 if (params && params->getCount() == 3) {
5050 visit((*params)[0]);
5051 emitConvertToCell(e);
5052 visit((*params)[1]);
5053 emitConvertToCell(e);
5054 visit((*params)[2]);
5055 emitConvertToCell(e);
5056 call->changeToBytecode();
5057 e.ArrayIdx();
5058 return true;
5060 } else if (call->isCallToFunction("max")) {
5061 if (params && params->getCount() == 2) {
5062 emitFuncCall(e, call, "__SystemLib\\max2", params);
5063 return true;
5065 } else if (call->isCallToFunction("min")) {
5066 if (params && params->getCount() == 2) {
5067 emitFuncCall(e, call, "__SystemLib\\min2", params);
5068 return true;
5070 } else if (call->isCallToFunction("define")) {
5071 if (params && params->getCount() == 2) {
5072 ExpressionPtr p0 = (*params)[0];
5073 Variant v0;
5074 if (p0->getScalarValue(v0) && v0.isString()) {
5075 const StringData* cname =
5076 makeStaticString(v0.toString());
5077 visit((*params)[1]);
5078 emitConvertToCell(e);
5079 e.DefCns(cname);
5080 return true;
5083 } else if (call->isCallToFunction("assert")) {
5084 // Special-case some logic around emitting assert(), or jumping around
5085 // it. This all applies only for direct calls to assert() -- dynamic
5086 // calls don't get this special logic, and don't in PHP7 either.
5088 if (!RuntimeOption::AssertEmitted) {
5089 e.True();
5090 return true;
5093 // We need to emit an ini_get around all asserts to check if the
5094 // zend.assertions option is enabled -- you can switch between 0 and 1
5095 // at runtime, and having it set to 0 disables the assert from running,
5096 // including side effects of function arguments, so we need to jump
5097 // around it if so. (The -1 value of zend.assertions corresponds to
5098 // AssertEmitted being set to 0 above, and is not changeable at
5099 // runtime.)
5100 Label disabled, after;
5101 e.String(s_zend_assertions.get());
5102 e.FCallBuiltin(1, 1, s_ini_get.get());
5103 e.UnboxRNop();
5104 e.Int(0);
5105 e.Gt();
5106 e.JmpZ(disabled);
5108 emitFuncCall(e, call, "assert", call->getParams());
5109 emitConvertToCell(e);
5110 e.Jmp(after);
5112 disabled.set(e);
5113 e.True();
5115 after.set(e);
5116 return true;
5117 } else if (call->isCallToFunction("array_slice") &&
5118 params && params->getCount() == 2 &&
5119 !RuntimeOption::EvalJitEnableRenameFunction) {
5120 ExpressionPtr p0 = (*params)[0];
5121 ExpressionPtr p1 = (*params)[1];
5122 Variant v1;
5123 if (p0->getKindOf() == Construct::KindOfSimpleFunctionCall &&
5124 p1->getScalarValue(v1) && v1.isInteger()) {
5125 auto innerCall = static_pointer_cast<SimpleFunctionCall>(p0);
5126 auto innerParams = innerCall->getParams();
5127 if (innerCall->isCallToFunction("func_get_args") &&
5128 (!innerParams || innerParams->getCount() == 0)) {
5129 params->removeElement(0);
5130 emitFuncCall(e, innerCall,
5131 "__SystemLib\\func_slice_args", params);
5132 return true;
5135 // fall through
5136 } else if ((call->isCallToFunction("class_exists") ||
5137 call->isCallToFunction("interface_exists") ||
5138 call->isCallToFunction("trait_exists"))
5139 && params
5140 && (params->getCount() == 1 || params->getCount() == 2)) {
5141 // Push name
5142 emitNameString(e, (*params)[0]);
5143 emitConvertToCell(e);
5144 e.CastString();
5146 // Push autoload, defaulting to true
5147 if (params->getCount() == 1) {
5148 e.True();
5149 } else {
5150 visit((*params)[1]);
5151 emitConvertToCell(e);
5152 e.CastBool();
5154 if (call->isCallToFunction("class_exists")) {
5155 e.OODeclExists(OODeclExistsOp::Class);
5156 } else if (call->isCallToFunction("interface_exists")) {
5157 e.OODeclExists(OODeclExistsOp::Interface);
5158 } else {
5159 assert(call->isCallToFunction("trait_exists"));
5160 e.OODeclExists(OODeclExistsOp::Trait);
5162 return true;
5163 } else if (call->isCallToFunction("get_class") &&
5164 !params &&
5165 call->getClassScope() &&
5166 !call->getClassScope()->isTrait()) {
5167 StringData* name =
5168 makeStaticString(call->getClassScope()->getScopeName());
5169 e.String(name);
5170 return true;
5171 } else if (((call->isCallToFunction("dict") &&
5172 (m_ue.m_isHHFile || RuntimeOption::EnableHipHopSyntax)) ||
5173 call->isCallToFunction("HH\\dict")) &&
5174 params && params->getCount() == 1) {
5175 visit((*params)[0]);
5176 emitConvertToCell(e);
5177 e.CastDict();
5178 return true;
5179 } else if (((call->isCallToFunction("vec") &&
5180 (m_ue.m_isHHFile || RuntimeOption::EnableHipHopSyntax)) ||
5181 call->isCallToFunction("HH\\vec")) &&
5182 params && params->getCount() == 1) {
5183 visit((*params)[0]);
5184 emitConvertToCell(e);
5185 e.CastVec();
5186 return true;
5187 } else if (((call->isCallToFunction("keyset") &&
5188 (m_ue.m_isHHFile || RuntimeOption::EnableHipHopSyntax)) ||
5189 call->isCallToFunction("HH\\keyset")) &&
5190 params && params->getCount() == 1) {
5191 visit((*params)[0]);
5192 emitConvertToCell(e);
5193 e.CastKeyset();
5194 return true;
5195 } else if (((call->isCallToFunction("is_vec") &&
5196 (m_ue.m_isHHFile || RuntimeOption::EnableHipHopSyntax)) ||
5197 call->isCallToFunction("HH\\is_vec")) &&
5198 params && params->getCount() == 1) {
5199 visit((*call->getParams())[0]);
5200 emitIsType(e, IsTypeOp::Vec);
5201 return true;
5202 } else if (((call->isCallToFunction("is_dict") &&
5203 (m_ue.m_isHHFile || RuntimeOption::EnableHipHopSyntax)) ||
5204 call->isCallToFunction("HH\\is_dict")) &&
5205 params && params->getCount() == 1) {
5206 visit((*call->getParams())[0]);
5207 emitIsType(e, IsTypeOp::Dict);
5208 return true;
5209 } else if (((call->isCallToFunction("is_keyset") &&
5210 (m_ue.m_isHHFile || RuntimeOption::EnableHipHopSyntax)) ||
5211 call->isCallToFunction("HH\\is_keyset")) &&
5212 params && params->getCount() == 1) {
5213 visit((*call->getParams())[0]);
5214 emitIsType(e, IsTypeOp::Keyset);
5215 return true;
5217 #define TYPE_CONVERT_INSTR(what, What) \
5218 else if (call->isCallToFunction(#what"val") && \
5219 params && params->getCount() == 1) { \
5220 visit((*params)[0]); \
5221 emitConvertToCell(e); \
5222 e.Cast ## What(); \
5223 return true; \
5225 TYPE_CONVERT_INSTR(bool, Bool)
5226 TYPE_CONVERT_INSTR(int, Int)
5227 TYPE_CONVERT_INSTR(double, Double)
5228 TYPE_CONVERT_INSTR(float, Double)
5229 TYPE_CONVERT_INSTR(str, String)
5230 #undef TYPE_CONVERT_INSTR
5232 #define TYPE_CHECK_INSTR(what, What) \
5233 else if (call->isCallToFunction("is_"#what) && \
5234 params && params->getCount() == 1) { \
5235 visit((*call->getParams())[0]); \
5236 emitIsType(e, IsTypeOp::What); \
5237 return true; \
5240 TYPE_CHECK_INSTR(null, Null)
5241 TYPE_CHECK_INSTR(object, Obj)
5242 TYPE_CHECK_INSTR(array, Arr)
5243 TYPE_CHECK_INSTR(string, Str)
5244 TYPE_CHECK_INSTR(int, Int)
5245 TYPE_CHECK_INSTR(integer, Int)
5246 TYPE_CHECK_INSTR(long, Int)
5247 TYPE_CHECK_INSTR(bool, Bool)
5248 TYPE_CHECK_INSTR(double, Dbl)
5249 TYPE_CHECK_INSTR(real, Dbl)
5250 TYPE_CHECK_INSTR(float, Dbl)
5251 TYPE_CHECK_INSTR(scalar, Scalar)
5252 #undef TYPE_CHECK_INSTR
5253 else if (emitConstantFuncCall(e, call)) {
5254 return true;
5257 // fall through
5258 case Construct::KindOfDynamicFunctionCall: {
5259 emitFuncCall(e, static_pointer_cast<FunctionCall>(node));
5260 return true;
5263 case Construct::KindOfIncludeExpression: {
5264 auto ie = static_pointer_cast<IncludeExpression>(node);
5265 if (ie->isReqLit()) {
5266 StringData* nValue = makeStaticString(ie->includePath());
5267 e.String(nValue);
5268 } else {
5269 visit(ie->getExpression());
5270 emitConvertToCell(e);
5272 switch (ie->getOp()) {
5273 case T_INCLUDE:
5274 e.Incl();
5275 break;
5276 case T_INCLUDE_ONCE:
5277 e.InclOnce();
5278 break;
5279 case T_REQUIRE:
5280 e.Req();
5281 break;
5282 case T_REQUIRE_ONCE:
5283 if (ie->isDocumentRoot()) {
5284 e.ReqDoc();
5285 } else {
5286 e.ReqOnce();
5288 break;
5290 return true;
5293 case Construct::KindOfListAssignment: {
5294 auto la = static_pointer_cast<ListAssignment>(node);
5295 auto rhs = la->getArray();
5297 // listAssignmentVisitLHS should have handled this
5298 assert(rhs);
5300 bool nullRHS = la->getRHSKind() == ListAssignment::Null;
5301 // If the RHS is not a simple variable, we need to evaluate it and assign
5302 // it to a temp local. If it is, whether or not we directly use it or copy
5303 // it into a temp local is visible in perverse statements like:
5304 // list($a, $b) = $a
5305 // The behavior of that changed between PHP5 and PHP7; in PHP5 we directly
5306 // use the temp local, in PHP7 we need to copy it.
5307 bool simpleRHS = rhs->is(Construct::KindOfSimpleVariable)
5308 && !static_pointer_cast<SimpleVariable>(rhs)->getAlwaysStash()
5309 && !RuntimeOption::PHP7_LTR_assign;
5310 Id tempLocal = -1;
5311 Offset start = InvalidAbsoluteOffset;
5313 if (!simpleRHS && la->isRhsFirst()) {
5314 tempLocal = emitVisitAndSetUnnamedL(e, rhs);
5315 start = m_ue.bcPos();
5318 // We use "index chains" to deal with nested list assignment. We will
5319 // end up with one index chain per expression we need to assign to.
5320 // The helper function will populate indexChains.
5322 // In PHP5 mode, this will also evaluate the LHS; in PHP7 mode, that is
5323 // always delayed until listAssignmentAssignElements below. This means
5324 // that isRhsFirst() has no effect in PHP7 mode. See comments in
5325 // listAssignmentVisitLHS and listAssignmentAssignElements for more
5326 // explanation.
5327 std::vector<IndexPair> indexPairs;
5328 IndexChain workingChain;
5329 listAssignmentVisitLHS(e, la, workingChain, indexPairs);
5331 if (!simpleRHS && !la->isRhsFirst()) {
5332 assert(tempLocal == -1);
5333 assert(start == InvalidAbsoluteOffset);
5334 tempLocal = emitVisitAndSetUnnamedL(e, rhs);
5335 start = m_ue.bcPos();
5338 // Assign elements.
5339 if (nullRHS) {
5340 listAssignmentAssignElements(e, indexPairs, nullptr);
5341 } else if (simpleRHS) {
5342 listAssignmentAssignElements(e, indexPairs, [&] { visit(rhs); });
5343 } else {
5344 listAssignmentAssignElements(
5345 e, indexPairs,
5346 [&] { emitVirtualLocal(tempLocal); }
5350 // Leave the RHS on the stack
5351 if (simpleRHS) {
5352 visit(rhs);
5353 } else {
5354 emitPushAndFreeUnnamedL(e, tempLocal, start);
5357 return true;
5360 case Construct::KindOfNewObjectExpression: {
5361 auto ne = static_pointer_cast<NewObjectExpression>(node);
5362 auto params = ne->getParams();
5363 int numParams = params ? params->getCount() : 0;
5364 ClassScopeRawPtr cls = ne->getClassScope();
5366 Offset fpiStart;
5367 if (ne->isStatic()) {
5368 // new static()
5369 e.LateBoundCls();
5370 fpiStart = m_ue.bcPos();
5371 e.FPushCtor(numParams);
5372 } else if (ne->getOriginalName().empty()) {
5373 // new $x()
5374 visit(ne->getNameExp());
5375 emitAGet(e);
5376 fpiStart = m_ue.bcPos();
5377 e.FPushCtor(numParams);
5378 } else if ((ne->isSelf() || ne->isParent()) &&
5379 (!cls || cls->isTrait() ||
5380 (ne->isParent() && cls->getOriginalParent().empty()))) {
5381 if (ne->isSelf()) {
5382 // new self() inside a trait or code statically not inside any class
5383 e.Self();
5384 } else {
5385 // new parent() inside a trait, code statically not inside any
5386 // class, or a class with no parent
5387 e.Parent();
5389 fpiStart = m_ue.bcPos();
5390 e.FPushCtor(numParams);
5391 } else {
5392 // new C() inside trait or pseudomain
5393 fpiStart = m_ue.bcPos();
5394 e.FPushCtorD(numParams,
5395 makeStaticString(ne->getOriginalClassName()));
5398 emitCall(e, ne, params, fpiStart);
5399 e.PopR();
5400 return true;
5403 case Construct::KindOfObjectMethodExpression: {
5404 auto om = static_pointer_cast<ObjectMethodExpression>(node);
5405 // $obj->name(...)
5406 // ^^^^
5407 visit(om->getObject());
5408 m_tempLoc = om->getRange();
5409 emitConvertToCell(e);
5410 ExpressionListPtr params(om->getParams());
5411 int numParams = params ? params->getCount() : 0;
5413 Offset fpiStart = 0;
5414 ExpressionPtr methName = om->getNameExp();
5415 bool useDirectForm = false;
5416 if (methName->is(Construct::KindOfScalarExpression)) {
5417 auto sval = static_pointer_cast<ScalarExpression>(methName);
5418 const std::string& methStr = sval->getOriginalLiteralString();
5419 if (!methStr.empty()) {
5420 // $obj->name(...)
5421 // ^^^^
5422 // Use getOriginalLiteralString(), which hasn't been
5423 // case-normalized, since __call() needs to preserve
5424 // the case.
5425 StringData* nameLiteral = makeStaticString(methStr);
5426 fpiStart = m_ue.bcPos();
5427 e.FPushObjMethodD(
5428 numParams,
5429 nameLiteral,
5430 om->isNullSafe() ? ObjMethodOp::NullSafe : ObjMethodOp::NullThrows
5432 useDirectForm = true;
5435 if (!useDirectForm) {
5436 // $obj->{...}(...)
5437 // ^^^^^
5438 visit(methName);
5439 emitConvertToCell(e);
5440 fpiStart = m_ue.bcPos();
5441 e.FPushObjMethod(
5442 numParams,
5443 om->isNullSafe() ? ObjMethodOp::NullSafe : ObjMethodOp::NullThrows
5446 // $obj->name(...)
5447 // ^^^^^
5448 emitCall(e, om, params, fpiStart);
5449 return true;
5452 case Construct::KindOfObjectPropertyExpression: {
5453 auto op = static_pointer_cast<ObjectPropertyExpression>(node);
5454 if (op->isNullSafe() &&
5455 op->hasAnyContext(
5456 Expression::RefValue
5457 | Expression::LValue
5458 | Expression::DeepReference
5459 ) && !op->hasContext(Expression::InvokeArgument)
5461 throw IncludeTimeFatalException(op,
5462 Strings::NULLSAFE_PROP_WRITE_ERROR);
5464 ExpressionPtr obj = op->getObject();
5465 auto sv = dynamic_pointer_cast<SimpleVariable>(obj);
5466 if (sv && sv->isThis() && sv->hasContext(Expression::ObjectContext)) {
5467 e.CheckThis();
5468 m_evalStack.push(StackSym::H);
5469 } else {
5470 visit(obj);
5472 StringData* clsName = getClassName(op->getObject());
5473 if (clsName) {
5474 m_evalStack.setKnownCls(clsName, false);
5476 emitNameString(e, op->getProperty(), true);
5477 if (!op->hasAnyContext(Expression::AccessContext|
5478 Expression::ObjectContext)) {
5479 m_tempLoc = op->getRange();
5481 markProp(
5483 op->isNullSafe()
5484 ? PropAccessType::NullSafe
5485 : PropAccessType::Normal
5487 return true;
5490 case Construct::KindOfQOpExpression: {
5491 auto q = static_pointer_cast<QOpExpression>(node);
5492 if (q->getYes()) {
5493 // <expr> ? <expr> : <expr>
5494 Label tru, fals, done;
5496 Emitter condEmitter(q->getCondition(), m_ue, *this);
5497 visitIfCondition(q->getCondition(), condEmitter,
5498 tru, fals, true);
5500 if (tru.isUsed()) {
5501 tru.set(e);
5503 if (currentPositionIsReachable()) {
5504 visit(q->getYes());
5505 emitConvertToCell(e);
5506 e.Jmp(done);
5508 if (fals.isUsed()) fals.set(e);
5509 if (currentPositionIsReachable()) {
5510 visit(q->getNo());
5511 emitConvertToCell(e);
5513 if (done.isUsed()) {
5514 done.set(e);
5515 m_evalStack.cleanTopMeta();
5517 } else {
5518 // <expr> ?: <expr>
5519 Label done;
5520 visit(q->getCondition());
5521 emitConvertToCell(e);
5522 e.Dup();
5523 e.JmpNZ(done);
5524 e.PopC();
5525 visit(q->getNo());
5526 emitConvertToCell(e);
5527 done.set(e);
5528 m_evalStack.cleanTopMeta();
5530 return true;
5533 case Construct::KindOfNullCoalesceExpression: {
5534 auto q = static_pointer_cast<NullCoalesceExpression>(node);
5536 Label done;
5537 visit(q->getFirst());
5538 emitCGetQuiet(e);
5539 e.Dup();
5540 emitIsset(e);
5541 e.JmpNZ(done);
5542 e.PopC();
5543 visit(q->getSecond());
5544 emitConvertToCell(e);
5545 done.set(e);
5546 m_evalStack.cleanTopMeta();
5548 return true;
5551 case Construct::KindOfScalarExpression: {
5552 auto ex = static_pointer_cast<Expression>(node);
5553 Variant v;
5554 ex->getScalarValue(v);
5555 auto const emitted = emitScalarValue(e, v);
5556 always_assert(emitted);
5557 return true;
5560 case Construct::KindOfPipeVariable: {
5561 if (auto pipeVar = getPipeLocal()) {
5562 emitVirtualLocal(*pipeVar);
5563 return true;
5566 throw IncludeTimeFatalException(
5567 node, "Pipe variables must occur only in the RHS of pipe expressions");
5570 case Construct::KindOfSimpleVariable: {
5571 auto sv = static_pointer_cast<SimpleVariable>(node);
5572 if (sv->isThis()) {
5573 if (sv->hasContext(Expression::ObjectContext)) {
5574 e.This();
5575 } else if (sv->getFunctionScope()->needsLocalThis()) {
5576 static const StringData* thisStr = makeStaticString("this");
5577 Id thisId = m_curFunc->lookupVarId(thisStr);
5578 emitVirtualLocal(thisId);
5579 } else {
5580 auto const subop = sv->hasContext(Expression::ExistContext)
5581 ? BareThisOp::NoNotice
5582 : BareThisOp::Notice;
5583 e.BareThis(subop);
5585 } else {
5586 StringData* nLiteral = makeStaticString(sv->getName());
5587 if (sv->isSuperGlobal()) {
5588 e.String(nLiteral);
5589 markGlobalName(e);
5590 return true;
5592 Id i = m_curFunc->lookupVarId(nLiteral);
5593 emitVirtualLocal(i);
5594 if (sv->getAlwaysStash() &&
5595 !sv->hasAnyContext(Expression::ExistContext |
5596 Expression::RefValue |
5597 Expression::LValue |
5598 Expression::RefParameter)) {
5599 emitConvertToCell(e);
5603 return true;
5606 case Construct::KindOfDynamicVariable: {
5607 auto dv = static_pointer_cast<DynamicVariable>(node);
5608 visit(dv->getSubExpression());
5609 emitConvertToCellOrLoc(e);
5610 markName(e);
5611 return true;
5614 case Construct::KindOfStaticMemberExpression: {
5615 auto sm = static_pointer_cast<StaticMemberExpression>(node);
5616 emitVirtualClassBase(e, sm.get());
5617 emitNameString(e, sm->getExp());
5618 markSProp(e);
5619 return true;
5622 case Construct::KindOfArrayPairExpression: {
5623 auto ap = static_pointer_cast<ArrayPairExpression>(node);
5625 auto key = ap->getName();
5626 if (!m_staticArrays.empty()) {
5627 auto val = ap->getValue();
5629 TypedValue tvVal;
5630 initScalar(tvVal, val);
5632 if (key != nullptr) {
5633 assert(key->isScalar());
5634 TypedValue tvKey = make_tv<KindOfNull>();
5635 if (!key->getScalarValue(tvAsVariant(&tvKey))) {
5636 InvariantViolation("Expected scalar value for array key\n");
5637 always_assert(0);
5639 m_staticArrays.back().set(tvAsCVarRef(&tvKey),
5640 tvAsVariant(&tvVal));
5641 } else {
5642 // Set/ImmSet, val is the key
5643 if (m_staticColType.back() == HeaderKind::Set ||
5644 m_staticColType.back() == HeaderKind::ImmSet) {
5645 m_staticArrays.back().set(tvAsVariant(&tvVal),
5646 tvAsVariant(&tvVal));
5647 } else {
5648 m_staticArrays.back().append(tvAsCVarRef(&tvVal));
5651 } else {
5652 // Assume new array is on top of stack
5653 bool hasKey = (bool)key;
5654 if (hasKey) {
5655 visit(key);
5656 emitConvertToCellOrLoc(e);
5658 visit(ap->getValue());
5659 if (ap->isRef()) {
5660 emitConvertToVar(e);
5661 if (hasKey) {
5662 emitConvertSecondToCell(e);
5663 e.AddElemV();
5664 } else {
5665 e.AddNewElemV();
5667 } else {
5668 emitConvertToCell(e);
5669 if (hasKey) {
5670 emitConvertSecondToCell(e);
5671 e.AddElemC();
5672 } else {
5673 e.AddNewElemC();
5677 return true;
5679 case Construct::KindOfExpressionList: {
5680 auto el = static_pointer_cast<ExpressionList>(node);
5681 if (!m_staticArrays.empty() &&
5682 (m_staticColType.back() == HeaderKind::VecArray ||
5683 m_staticColType.back() == HeaderKind::Keyset)) {
5684 auto const nelem = el->getCount();
5685 for (int i = 0; i < nelem; ++i) {
5686 auto const expr = (*el)[i];
5687 assert(expr->isScalar());
5688 TypedValue tvVal;
5689 initScalar(tvVal, expr);
5690 m_staticArrays.back().append(tvAsCVarRef(&tvVal));
5692 return true;
5693 } else {
5694 int nelem = el->getCount(), i;
5695 bool pop = el->getListKind() != ExpressionList::ListKindParam;
5696 int keep = el->getListKind() == ExpressionList::ListKindLeft ?
5697 0 : nelem - 1;
5698 int cnt = 0;
5699 for (i = 0; i < nelem; i++) {
5700 ExpressionPtr p((*el)[i]);
5701 if (visit(p)) {
5702 if (pop && i != keep) {
5703 emitPop(e);
5704 } else {
5705 cnt++;
5709 return cnt != 0;
5712 case Construct::KindOfParameterExpression: {
5713 not_implemented();
5715 case Construct::KindOfModifierExpression: {
5716 not_implemented();
5718 case Construct::KindOfUserAttribute: {
5719 not_implemented();
5721 case Construct::KindOfClosureExpression: {
5722 // Closures are implemented by anonymous classes that extend Closure.
5723 // There is one anonymous class per closure body.
5724 auto ce = static_pointer_cast<ClosureExpression>(node);
5726 // Build a convenient list of use-variables. Each one corresponds to:
5727 // (a) an instance variable, to store the value until call time
5728 // (b) a parameter of the generated constructor
5729 // (c) an argument to the constructor at the definition site
5730 // (d) a line of code in the generated constructor;
5731 // (e) a line of code in the generated prologue to the closure body
5732 auto useList = ce->getClosureVariables();
5733 ClosureUseVarVec useVars;
5734 int useCount = (useList ? useList->getCount() : 0);
5735 if (useList) {
5736 for (int i = 0; i < useCount; ++i) {
5737 auto var = static_pointer_cast<ParameterExpression>((*useList)[i]);
5738 StringData* varName = makeStaticString(var->getName());
5739 useVars.push_back(ClosureUseVar(varName, var->isRef()));
5743 // We're still at the closure definition site. Emit code to instantiate
5744 // the new anonymous class, with the use variables as arguments.
5745 ExpressionListPtr valuesList(ce->getClosureValues());
5746 for (int i = 0; i < useCount; ++i) {
5747 ce->type() == ClosureType::Short
5748 ? emitLambdaCaptureArg(e, (*valuesList)[i])
5749 : (void)emitBuiltinCallArg(e, (*valuesList)[i], i,
5750 useVars[i].second, false);
5753 // Create a new class to hold the closure.
5754 PreClassEmitter* pce = m_ue.newPreClassEmitter(
5755 ce->getClosureFunction()->getOriginalName(), PreClass::NotHoistable);
5757 auto const attrs = AttrNoOverride | AttrUnique | AttrPersistent;
5759 static auto const parentName = makeStaticString("Closure");
5760 pce->init(ce->line0(), ce->line1(), m_ue.bcPos(),
5761 attrs, parentName, nullptr);
5763 // Instance properties---one for each use var, and one for
5764 // each static local.
5765 TypedValue uninit;
5766 tvWriteUninit(&uninit);
5767 for (auto& useVar : useVars) {
5768 pce->addProperty(useVar.first, AttrPrivate, nullptr, nullptr,
5769 &uninit, RepoAuthType{});
5772 // The __invoke method. This is the body of the closure, preceded by
5773 // code that pulls the object's instance variables into locals.
5774 static auto const invokeName = makeStaticString("__invoke");
5775 auto const invoke = m_ue.newMethodEmitter(invokeName, pce);
5776 invoke->isClosureBody = true;
5777 pce->addMethod(invoke);
5778 auto const body =
5779 static_pointer_cast<MethodStatement>(ce->getClosureFunction());
5780 postponeMeth(body, invoke, false, new ClosureUseVarVec(useVars));
5782 e.CreateCl(useCount, pce->id());
5784 return true;
5786 case Construct::KindOfClassExpression: {
5787 auto ce = static_pointer_cast<ClassExpression>(node);
5789 auto save = m_evalStack;
5790 auto const id = emitClass(e, ce->getClass()->getClassScope(), false);
5791 if (id < 0) {
5792 m_evalStackIsUnknown = false;
5793 m_evalStack = save;
5794 e.Null();
5795 } else {
5796 auto fpiStart = m_ue.bcPos();
5797 auto params = ce->getParams();
5798 int numParams = params ? params->getCount() : 0;
5799 e.FPushCtorI(numParams, id);
5800 emitCall(e, ce, ce->getParams(), fpiStart);
5801 e.PopR();
5803 return true;
5805 case Construct::KindOfYieldExpression: {
5806 auto y = static_pointer_cast<YieldExpression>(node);
5808 registerYieldAwait(y);
5809 assert(m_evalStack.size() == 0);
5811 // evaluate key passed to yield, if applicable
5812 ExpressionPtr keyExp = y->getKeyExpression();
5813 if (keyExp) {
5814 m_curFunc->isPairGenerator = true;
5815 visit(keyExp);
5816 emitConvertToCell(e);
5819 // evaluate value expression passed to yield
5820 visit(y->getValueExpression());
5821 emitConvertToCell(e);
5823 // suspend generator
5824 if (keyExp) {
5825 assert(m_evalStack.size() == 2);
5826 e.YieldK();
5827 } else {
5828 assert(m_evalStack.size() == 1);
5829 e.Yield();
5832 // continue with the received result on the stack
5833 assert(m_evalStack.size() == 1);
5834 return true;
5836 case Construct::KindOfYieldFromExpression: {
5837 auto yf = static_pointer_cast<YieldFromExpression>(node);
5839 registerYieldAwait(yf);
5840 assert(m_evalStack.size() == 0);
5842 emitYieldFrom(e, yf->getExpression());
5844 return true;
5846 case Construct::KindOfAwaitExpression: {
5847 auto await = static_pointer_cast<AwaitExpression>(node);
5849 registerYieldAwait(await);
5850 assert(m_evalStack.size() == 0);
5852 auto expression = await->getExpression();
5853 if (emitInlineGen(e, expression)) return true;
5855 Label resume;
5857 // evaluate expression passed to await
5858 visit(expression);
5859 emitConvertToCell(e);
5861 // if expr is null, just continue
5862 e.Dup();
5863 emitIsType(e, IsTypeOp::Null);
5864 e.JmpNZ(resume);
5866 assert(m_evalStack.size() == 1);
5868 e.Await();
5870 resume.set(e);
5871 return true;
5873 case Construct::KindOfUseDeclarationStatementFragment:
5874 case Construct::KindOfExpression: {
5875 not_reached();
5879 not_reached();
5882 bool EmitterVisitor::emitScalarValue(Emitter& e, const Variant& v) {
5883 switch (v.getRawType()) {
5884 case KindOfUninit:
5885 e.NullUninit();
5886 return true;
5888 case KindOfNull:
5889 e.Null();
5890 return true;
5892 case KindOfBoolean:
5893 v.asBooleanVal() ? e.True() : e.False();
5894 return true;
5896 case KindOfInt64:
5897 e.Int(v.getInt64());
5898 return true;
5900 case KindOfDouble:
5901 e.Double(v.getDouble());
5902 return true;
5904 case KindOfPersistentString:
5905 case KindOfString:
5906 e.String(makeStaticString(v.getStringData()));
5907 return true;
5909 case KindOfPersistentVec:
5910 case KindOfVec:
5911 assert(v.isVecArray());
5912 e.Vec(ArrayData::GetScalarArray(v.getArrayData()));
5913 return true;
5915 case KindOfPersistentDict:
5916 case KindOfDict:
5917 assert(v.isDict());
5918 e.Dict(ArrayData::GetScalarArray(v.getArrayData()));
5919 return true;
5921 case KindOfPersistentKeyset:
5922 case KindOfKeyset:
5923 assert(v.isKeyset());
5924 e.Keyset(ArrayData::GetScalarArray(v.getArrayData()));
5925 return true;
5927 case KindOfPersistentArray:
5928 case KindOfArray:
5929 assert(v.isPHPArray());
5930 e.Array(ArrayData::GetScalarArray(v.getArrayData()));
5931 return true;
5933 case KindOfObject:
5934 case KindOfResource:
5935 case KindOfRef:
5936 case KindOfClass:
5937 return false;
5939 not_reached();
5942 void EmitterVisitor::emitConstMethodCallNoParams(Emitter& e,
5943 const std::string& name) {
5944 auto const nameLit = makeStaticString(name);
5945 auto const fpiStart = m_ue.bcPos();
5946 e.FPushObjMethodD(0, nameLit, ObjMethodOp::NullThrows);
5948 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
5950 e.FCall(0);
5951 emitConvertToCell(e);
5954 namespace {
5955 const StaticString
5956 s_hh_invariant_violation("hh\\invariant_violation"),
5957 s_invariant_violation("invariant_violation"),
5958 s_gennull("HH\\Asio\\null"),
5959 s_fromArray("fromArray"),
5960 s_AwaitAllWaitHandle("HH\\AwaitAllWaitHandle")
5964 bool EmitterVisitor::emitInlineGenva(
5965 Emitter& e,
5966 const SimpleFunctionCallPtr& call
5968 assert(call->isCallToFunction("genva"));
5969 const auto params = call->getParams();
5970 if (!params) {
5971 e.Array(staticEmptyArray());
5972 return true;
5974 if (params->containsUnpack()) {
5975 throw IncludeTimeFatalException(params, "do not use ...$args with genva()");
5977 const auto num_params = params->getCount();
5978 assertx(num_params > 0);
5980 for (auto i = int{0}; i < num_params; i++) {
5981 Label gwh;
5983 visit((*params)[i]);
5984 emitConvertToCell(e);
5986 // ($_ !== null ? HH\Asio\null() : $_)->getWaitHandle()
5987 e.Dup();
5988 emitIsType(e, IsTypeOp::Null);
5989 e.JmpZ(gwh);
5990 emitPop(e);
5992 Offset fpiStart = m_ue.bcPos();
5993 e.FPushFuncD(0, s_gennull.get());
5995 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
5997 e.FCall(0);
5998 e.UnboxR();
5999 gwh.set(e);
6000 emitConstMethodCallNoParams(e, "getWaitHandle");
6003 std::vector<Id> waithandles(num_params);
6004 for (auto i = int{num_params - 1}; i >= 0; --i) {
6005 waithandles[i] = emitSetUnnamedL(e);
6007 assertx(waithandles.size() == num_params);
6009 // AwaitAllWaitHandle::fromArray() always returns a WaitHandle.
6010 Offset fpiStart = m_ue.bcPos();
6011 e.FPushClsMethodD(1, s_fromArray.get(), s_AwaitAllWaitHandle.get());
6013 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
6014 // create a packed array of the waithandles
6015 for (const auto wh : waithandles) {
6016 emitVirtualLocal(wh);
6017 emitCGet(e);
6019 e.NewPackedArray(num_params);
6020 emitFPass(e, 0, PassByRefKind::ErrorOnCell);
6022 e.FCall(1);
6023 e.UnboxR();
6025 e.Await();
6026 // result of AwaitAllWaitHandle does not matter
6027 emitPop(e);
6029 for (const auto wh : waithandles) {
6030 emitVirtualLocal(wh);
6031 emitPushL(e);
6032 e.WHResult();
6034 e.NewPackedArray(num_params);
6036 for (auto wh : waithandles) {
6037 m_curFunc->freeUnnamedLocal(wh);
6040 newFaultRegionAndFunclet(
6041 fpiStart, m_ue.bcPos(),
6042 new UnsetUnnamedLocalsThunklet(std::move(waithandles)));
6044 return true;
6047 bool EmitterVisitor::emitInlineGena(
6048 Emitter& e,
6049 const SimpleFunctionCallPtr& call
6051 assert(call->isCallToFunction("gena"));
6052 const auto params = call->getParams();
6054 if (params->getCount() != 1) return false;
6057 // Convert input into an array of WH (inline this?)
6058 // Two elements is the most common size.
6060 e.NewArray(2);
6061 const auto array = emitSetUnnamedL(e);
6062 const auto arrayStart = m_ue.bcPos();
6065 // Iterate over input and store wait handles for all elements in
6066 // a new array.
6068 const auto key = m_curFunc->allocUnnamedLocal();
6069 const auto item = m_curFunc->allocUnnamedLocal();
6071 emitVirtualLocal(key);
6072 emitVirtualLocal(item);
6074 visit((*params)[0]);
6075 emitConvertToCell(e);
6077 Label endloop;
6078 const auto initItId = m_curFunc->allocIterator();
6079 e.IterInitK(initItId, endloop, item, key);
6080 const auto iterStart = m_ue.bcPos();
6082 Label loop(e);
6084 emitVirtualLocal(array); // for Set below
6085 emitVirtualLocal(key); // for Set below
6086 markElem(e);
6088 emitVirtualLocal(item);
6089 emitCGet(e);
6090 emitConstMethodCallNoParams(e, "getWaitHandle");
6092 emitSet(e); // array[$key] = $item->getWaitHandle();
6093 emitPop(e);
6095 emitVirtualLocal(key);
6096 emitVirtualLocal(item);
6097 e.IterNextK(initItId, loop, item, key);
6098 endloop.set(e);
6100 // Clear item and key. Free iterator.
6101 emitUnsetL(e, item);
6102 emitUnsetL(e, key);
6103 m_curFunc->freeIterator(initItId);
6105 newFaultRegionAndFunclet(iterStart, m_ue.bcPos(),
6106 new UnsetUnnamedLocalsThunklet({item, key}));
6107 newFaultRegionAndFunclet(iterStart, m_ue.bcPos(),
6108 new IterFreeThunklet(initItId, false),
6109 { initItId, KindOfIter });
6113 // Construct an AAWH from the array.
6115 const auto fromArrayStart = m_ue.bcPos();
6116 e.FPushClsMethodD(1, s_fromArray.get(), s_AwaitAllWaitHandle.get());
6118 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fromArrayStart);
6119 emitVirtualLocal(array);
6120 e.FPassL(0, array);
6122 e.FCall(1);
6123 e.UnboxR();
6126 // Await on the AAWH. Note: the result of Await does not matter.
6128 e.Await();
6129 emitPop(e);
6132 // Iterate over results and store in array. Reuse same temporary array.
6135 emitVirtualLocal(key);
6136 emitVirtualLocal(item);
6138 emitVirtualLocal(array);
6139 emitCGet(e);
6141 Label endloop2;
6142 const auto resultItId = m_curFunc->allocIterator();
6143 e.IterInitK(resultItId, endloop2, item, key);
6144 const auto iterStart2 = m_ue.bcPos();
6146 Label loop2(e);
6148 emitVirtualLocal(array); // for Set below
6149 emitVirtualLocal(key); // for Set below
6150 markElem(e);
6152 emitVirtualLocal(item);
6153 emitCGet(e);
6154 e.WHResult();
6156 emitSet(e); // array[$key] = WHResult($item);
6157 emitPop(e);
6159 emitVirtualLocal(key);
6160 emitVirtualLocal(item);
6161 e.IterNextK(resultItId, loop2, item, key);
6162 endloop2.set(e);
6164 // Clear item and key. Free iterator.
6165 emitUnsetL(e, item);
6166 emitUnsetL(e, key);
6167 m_curFunc->freeIterator(resultItId);
6169 newFaultRegionAndFunclet(iterStart2, m_ue.bcPos(),
6170 new UnsetUnnamedLocalsThunklet({item, key}));
6172 newFaultRegionAndFunclet(iterStart2, m_ue.bcPos(),
6173 new IterFreeThunklet(resultItId, false),
6174 { resultItId, KindOfIter });
6177 // clean up locals
6178 m_curFunc->freeUnnamedLocal(item);
6179 m_curFunc->freeUnnamedLocal(key);
6181 // Leave result array on the stack.
6182 emitPushAndFreeUnnamedL(e, array, arrayStart);
6184 return true;
6187 bool EmitterVisitor::emitInlineGen(
6188 Emitter& e,
6189 const ExpressionPtr& expression
6191 if (!m_ue.m_isHHFile || !RuntimeOption::EnableHipHopSyntax ||
6192 !expression->is(Expression::KindOfSimpleFunctionCall) ||
6193 RuntimeOption::EvalJitEnableRenameFunction) {
6194 return false;
6197 const auto call = static_pointer_cast<SimpleFunctionCall>(expression);
6198 if (call->isCallToFunction("genva")) {
6199 return emitInlineGenva(e, call);
6200 } else if (call->isCallToFunction("gena")) {
6201 return emitInlineGena(e, call);
6203 return false;
6206 // Compile a static string as HHAS
6208 // The hhas bytecodes should either leave the stack untouched, in
6209 // which case the result of the hh\asm() expression will be null; or
6210 // they should push exactly one cell, which will be the result of the
6211 // hh\asm() expression.
6212 bool EmitterVisitor::emitInlineHHAS(Emitter& e, SimpleFunctionCallPtr func) {
6213 if (SystemLib::s_inited &&
6214 !func->getFunctionScope()->isSystem() &&
6215 !RuntimeOption::EvalAllowHhas) {
6216 throw IncludeTimeFatalException(func,
6217 "Inline hhas only allowed in systemlib");
6219 auto const params = func->getParams();
6220 if (!params || params->getCount() != 1) {
6221 throw IncludeTimeFatalException(func,
6222 "Inline hhas expects exactly one argument");
6224 Variant v;
6225 if (!((*params)[0]->getScalarValue(v)) || !v.isString()) {
6226 throw IncludeTimeFatalException(func,
6227 "Inline hhas must be string literal");
6230 try {
6231 auto result =
6232 assemble_expression(m_ue, m_curFunc,
6233 m_evalStack.size() + m_evalStack.fdescSize(),
6234 v.toString().toCppString());
6235 switch (result) {
6236 case AsmResult::NoResult:
6237 e.Null();
6238 break;
6239 case AsmResult::ValuePushed:
6240 pushEvalStack(StackSym::C);
6241 break;
6242 case AsmResult::Unreachable:
6243 // PrevOpcode is only used to determine whether the current position
6244 // is reachable. Arbitrarily set it to Jmp to ensure that the emitter
6245 // knows the current position is not reachable
6246 setPrevOpcode(Op::Jmp);
6247 pushEvalStack(StackSym::C);
6248 break;
6250 } catch (const std::exception& ex) {
6251 throw IncludeTimeFatalException(func, ex.what());
6254 return true;
6257 bool EmitterVisitor::emitHHInvariant(Emitter& e, SimpleFunctionCallPtr call) {
6258 if (!m_ue.m_isHHFile && !RuntimeOption::EnableHipHopSyntax) return false;
6260 auto const params = call->getParams();
6261 if (!params || params->getCount() < 1) return false;
6263 Label ok;
6265 visit((*params)[0]);
6266 emitCGet(e);
6267 e.JmpNZ(ok);
6269 auto const fpiStart = m_ue.bcPos();
6270 e.FPushFuncD(params->getCount() - 1, s_hh_invariant_violation.get());
6272 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
6273 for (auto i = uint32_t{1}; i < params->getCount(); ++i) {
6274 emitFuncCallArg(e, (*params)[i], i - 1, false);
6277 e.FCall(params->getCount() - 1);
6278 emitPop(e);
6279 // The invariant_violation can't return; but bytecode invariants mandate an
6280 // opcode that can't fall through:
6281 e.String(s_invariant_violation.get());
6282 e.Fatal(FatalOp::Runtime);
6284 ok.set(e);
6285 e.Null(); // invariant returns null if used in an expression, void according
6286 // to the typechecker.
6287 return true;
6290 int EmitterVisitor::scanStackForLocation(int iLast) {
6291 assertx(iLast >= 0);
6292 assertx(iLast < (int)m_evalStack.size());
6293 for (int i = iLast; i >= 0; --i) {
6294 char marker = StackSym::GetMarker(m_evalStack.get(i));
6295 if (marker != StackSym::E && marker != StackSym::W &&
6296 marker != StackSym::P && marker != StackSym::M &&
6297 marker != StackSym::Q) {
6298 return i;
6301 InvariantViolation("Emitter expected a location on the stack but none "
6302 "was found (at offset %d)",
6303 m_ue.bcPos());
6304 return 0;
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 baseMode = opts.fpass ? MOpMode::None :
6318 opts.mode == MOpMode::Unset ? MOpMode::None :
6319 opts.mode;
6321 // Emit the base location operation.
6322 auto sym = m_evalStack.get(iFirst);
6323 auto flavor = StackSym::GetSymFlavor(sym);
6324 switch (StackSym::GetMarker(sym)) {
6325 case StackSym::N:
6326 switch (flavor) {
6327 case StackSym::C:
6328 if (opts.fpass) {
6329 e.FPassBaseNC(opts.paramId, stackIdx(iFirst));
6330 } else {
6331 e.BaseNC(stackIdx(iFirst), baseMode);
6333 break;
6334 case StackSym::L:
6335 if (opts.fpass) {
6336 e.FPassBaseNL(opts.paramId, m_evalStack.getLoc(iFirst));
6337 } else {
6338 e.BaseNL(m_evalStack.getLoc(iFirst), baseMode);
6340 break;
6341 default:
6342 always_assert(false);
6344 break;
6346 case StackSym::G:
6347 switch (flavor) {
6348 case StackSym::C:
6349 if (opts.fpass) {
6350 e.FPassBaseGC(opts.paramId, stackIdx(iFirst));
6351 } else {
6352 e.BaseGC(stackIdx(iFirst), baseMode);
6354 break;
6355 case StackSym::L:
6356 if (opts.fpass) {
6357 e.FPassBaseGL(opts.paramId, m_evalStack.getLoc(iFirst));
6358 } else {
6359 e.BaseGL(m_evalStack.getLoc(iFirst), baseMode);
6361 break;
6362 default:
6363 always_assert(false);
6365 break;
6367 case StackSym::S: {
6368 if (m_evalStack.get(iLast) != StackSym::AM) {
6369 unexpectedStackSym(sym, "S-vector base, class ref");
6372 auto const clsIdx = opts.rhsVal ? 1 : 0;
6373 switch (flavor) {
6374 case StackSym::C:
6375 e.BaseSC(stackIdx(iFirst), clsIdx);
6376 break;
6377 case StackSym::L:
6378 e.BaseSL(m_evalStack.getLoc(iFirst), clsIdx);
6379 break;
6380 default:
6381 unexpectedStackSym(sym, "S-vector base, prop name");
6382 break;
6384 // The BaseS* bytecodes consume the Class from the eval stack so the
6385 // final operations don't have to expect an A-flavored input. Adjust
6386 // iLast accordingly.
6387 --iLast;
6388 break;
6391 case StackSym::None:
6392 switch (flavor) {
6393 case StackSym::L:
6394 if (opts.fpass) {
6395 e.FPassBaseL(opts.paramId, m_evalStack.getLoc(iFirst));
6396 } else {
6397 e.BaseL(m_evalStack.getLoc(iFirst), baseMode);
6399 break;
6400 case StackSym::C:
6401 e.BaseC(stackIdx(iFirst));
6402 break;
6403 case StackSym::R:
6404 e.BaseR(stackIdx(iFirst));
6405 break;
6406 case StackSym::H:
6407 e.BaseH();
6408 break;
6409 default:
6410 always_assert(false);
6412 break;
6414 default:
6415 always_assert(false && "Bad base marker");
6418 assert(StackSym::GetMarker(m_evalStack.get(iLast)) != StackSym::M);
6420 // Emit all intermediate operations, leaving the final operation up to our
6421 // caller.
6422 for (auto i = iFirst + 1; i < iLast; ++i) {
6423 if (opts.fpass) {
6424 e.FPassDim(opts.paramId, symToMemberKey(e, i, opts.allowW));
6425 } else {
6426 e.Dim(opts.mode, symToMemberKey(e, i, opts.allowW));
6430 size_t stackCount = 0;
6431 for (int i = iFirst; i <= iLast; ++i) {
6432 if (!StackSym::IsSymbolic(m_evalStack.get(i))) ++stackCount;
6434 return stackCount;
6437 MemberKey EmitterVisitor::symToMemberKey(Emitter& e, int i, bool allowW) {
6438 auto const sym = m_evalStack.get(i);
6439 auto const marker = StackSym::GetMarker(sym);
6440 if (marker == StackSym::W) {
6441 if (allowW) return MemberKey{};
6443 throw EmitterVisitor::IncludeTimeFatalException(
6444 e.getNode(), "Cannot use [] for reading"
6448 switch (StackSym::GetSymFlavor(sym)) {
6449 case StackSym::L: {
6450 auto const local = m_evalStack.getLoc(i);
6451 switch (marker) {
6452 case StackSym::E: return MemberKey{MEL, local};
6453 case StackSym::P: return MemberKey{MPL, local};
6454 default: always_assert(false);
6457 case StackSym::C: {
6458 auto const idx =
6459 int32_t(m_evalStack.actualSize() - 1 - m_evalStack.getActualPos(i));
6460 switch (marker) {
6461 case StackSym::E: return MemberKey{MEC, idx};
6462 case StackSym::P: return MemberKey{MPC, idx};
6463 default: always_assert(false);
6466 case StackSym::I: {
6467 auto const int64 = m_evalStack.getInt(i);
6468 switch (marker) {
6469 case StackSym::E: return MemberKey{MEI, int64};
6470 default: always_assert(false);
6473 case StackSym::T: {
6474 auto const str = m_evalStack.getName(i);
6475 switch (marker) {
6476 case StackSym::E: return MemberKey{MET, str};
6477 case StackSym::P: return MemberKey{MPT, str};
6478 case StackSym::Q: return MemberKey{MQT, str};
6479 default: always_assert(false);
6482 default:
6483 always_assert(false);
6487 void EmitterVisitor::emitPop(Emitter& e) {
6488 if (checkIfStackEmpty("Pop*")) return;
6489 LocationGuard loc(e, m_tempLoc);
6490 m_tempLoc.clear();
6492 emitClsIfSPropBase(e);
6493 int iLast = m_evalStack.size()-1;
6494 int i = scanStackForLocation(iLast);
6495 int sz = iLast - i;
6496 assert(sz >= 0);
6497 char sym = m_evalStack.get(i);
6498 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
6499 switch (sym) {
6500 case StackSym::L: e.CGetL(m_evalStack.getLoc(i)); // fall through
6501 case StackSym::C: e.PopC(); break;
6502 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
6503 case StackSym::CN: e.CGetN(); e.PopC(); break;
6504 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
6505 case StackSym::CG: e.CGetG(); e.PopC(); break;
6506 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
6507 case StackSym::CS: e.CGetS(); e.PopC(); break;
6508 case StackSym::V: e.PopV(); break;
6509 case StackSym::R: e.PopR(); break;
6510 default: {
6511 unexpectedStackSym(sym, "emitPop");
6512 break;
6515 } else {
6516 emitQueryMOp(i, iLast, e, QueryMOp::CGet);
6517 e.PopC();
6521 void EmitterVisitor::emitCGetL2(Emitter& e) {
6522 assert(m_evalStack.size() >= 2);
6523 assert(m_evalStack.sizeActual() >= 1);
6524 assert(StackSym::GetSymFlavor(m_evalStack.get(m_evalStack.size() - 2))
6525 == StackSym::L);
6526 int localIdx = m_evalStack.getLoc(m_evalStack.size() - 2);
6527 e.CGetL2(localIdx);
6530 void EmitterVisitor::emitCGetL3(Emitter& e) {
6531 assert(m_evalStack.size() >= 3);
6532 assert(m_evalStack.sizeActual() >= 2);
6533 assert(StackSym::GetSymFlavor(m_evalStack.get(m_evalStack.size() - 3))
6534 == StackSym::L);
6535 int localIdx = m_evalStack.getLoc(m_evalStack.size() - 3);
6536 e.CGetL3(localIdx);
6539 void EmitterVisitor::emitPushL(Emitter& e) {
6540 assert(m_evalStack.size() >= 1);
6541 auto const idx = m_evalStack.size() - 1;
6542 assert(StackSym::GetSymFlavor(m_evalStack.get(idx)) == StackSym::L);
6543 e.PushL(m_evalStack.getLoc(idx));
6546 void EmitterVisitor::emitAGet(Emitter& e) {
6547 if (checkIfStackEmpty("AGet*")) return;
6549 emitConvertToCellOrLoc(e);
6550 switch (char sym = m_evalStack.top()) {
6551 case StackSym::L:
6552 e.AGetL(m_evalStack.getLoc(m_evalStack.size() - 1));
6553 break;
6554 case StackSym::C:
6555 e.AGetC();
6556 break;
6557 default:
6558 unexpectedStackSym(sym, "emitAGet");
6562 void EmitterVisitor::emitQueryMOp(int iFirst, int iLast, Emitter& e,
6563 QueryMOp op) {
6564 auto const flags = getQueryMOpMode(op);
6565 auto const stackCount = emitMOp(iFirst, iLast, e, MInstrOpts{flags});
6566 e.QueryM(stackCount, op, symToMemberKey(e, iLast, false /* allowW */));
6569 void EmitterVisitor::emitCGet(Emitter& e) {
6570 if (checkIfStackEmpty("CGet*")) return;
6571 LocationGuard loc(e, m_tempLoc);
6572 m_tempLoc.clear();
6574 emitClsIfSPropBase(e);
6575 int iLast = m_evalStack.size()-1;
6576 int i = scanStackForLocation(iLast);
6577 int sz = iLast - i;
6578 assert(sz >= 0);
6579 char sym = m_evalStack.get(i);
6580 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
6581 switch (sym) {
6582 case StackSym::L: e.CGetL(m_evalStack.getLoc(i)); break;
6583 case StackSym::C: /* nop */ break;
6584 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
6585 case StackSym::CN: e.CGetN(); break;
6586 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
6587 case StackSym::CG: e.CGetG(); break;
6588 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
6589 case StackSym::CS: e.CGetS(); break;
6590 case StackSym::V: e.Unbox(); break;
6591 case StackSym::R: e.UnboxR(); break;
6592 default: {
6593 unexpectedStackSym(sym, "emitCGet");
6594 break;
6597 } else {
6598 emitQueryMOp(i, iLast, e, QueryMOp::CGet);
6602 void EmitterVisitor::emitCGetQuiet(Emitter& e) {
6603 if (checkIfStackEmpty("CGetQuiet*")) return;
6604 LocationGuard loc(e, m_tempLoc);
6605 m_tempLoc.clear();
6607 emitClsIfSPropBase(e);
6608 int iLast = m_evalStack.size()-1;
6609 int i = scanStackForLocation(iLast);
6610 int sz = iLast - i;
6611 assert(sz >= 0);
6612 char sym = m_evalStack.get(i);
6613 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
6614 switch (sym) {
6615 case StackSym::L: e.CGetQuietL(m_evalStack.getLoc(i)); break;
6616 case StackSym::C: /* nop */ break;
6617 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
6618 case StackSym::CN: e.CGetQuietN(); break;
6619 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
6620 case StackSym::CG: e.CGetQuietG(); break;
6621 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
6622 case StackSym::CS: e.CGetS(); break;
6623 case StackSym::V: e.Unbox(); break;
6624 case StackSym::R: e.UnboxR(); break;
6625 default: {
6626 unexpectedStackSym(sym, "emitCGetQuiet");
6627 break;
6631 } else {
6632 emitQueryMOp(i, iLast, e, QueryMOp::CGetQuiet);
6636 bool EmitterVisitor::emitVGet(Emitter& e, bool skipCells) {
6637 if (checkIfStackEmpty("VGet*")) return false;
6638 LocationGuard loc(e, m_tempLoc);
6639 m_tempLoc.clear();
6641 emitClsIfSPropBase(e);
6642 int iLast = m_evalStack.size()-1;
6643 int i = scanStackForLocation(iLast);
6644 int sz = iLast - i;
6645 assert(sz >= 0);
6646 char sym = m_evalStack.get(i);
6647 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
6648 switch (sym) {
6649 case StackSym::L: e.VGetL(m_evalStack.getLoc(i)); break;
6650 case StackSym::C: if (skipCells) return true; e.Box(); break;
6651 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
6652 case StackSym::CN: e.VGetN(); break;
6653 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
6654 case StackSym::CG: e.VGetG(); break;
6655 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
6656 case StackSym::CS: e.VGetS(); break;
6657 case StackSym::V: /* nop */ break;
6658 case StackSym::R: e.BoxR(); break;
6659 default: {
6660 unexpectedStackSym(sym, "emitVGet");
6661 break;
6664 } else {
6665 auto const stackCount =
6666 emitMOp(i, iLast, e, MInstrOpts{MOpMode::Define});
6667 e.VGetM(stackCount, symToMemberKey(e, iLast, true /* allowW */));
6669 return false;
6672 Id EmitterVisitor::emitVisitAndSetUnnamedL(Emitter& e, ExpressionPtr exp) {
6673 visit(exp);
6674 emitConvertToCell(e);
6676 return emitSetUnnamedL(e);
6679 Id EmitterVisitor::emitSetUnnamedL(Emitter& e) {
6680 Id tempLocal = m_curFunc->allocUnnamedLocal();
6681 emitSetL(e, tempLocal);
6682 emitPop(e);
6683 return tempLocal;
6686 void EmitterVisitor::emitFreeUnnamedL(Emitter& e, Id tempLocal, Offset start) {
6687 assert(tempLocal >= 0);
6688 assert(start != InvalidAbsoluteOffset);
6689 newFaultRegionAndFunclet(start, m_ue.bcPos(),
6690 new UnsetUnnamedLocalThunklet(tempLocal));
6691 emitUnsetL(e, tempLocal);
6692 m_curFunc->freeUnnamedLocal(tempLocal);
6695 void EmitterVisitor::emitPushAndFreeUnnamedL(Emitter& e, Id tempLocal,
6696 Offset start) {
6697 assert(tempLocal >= 0);
6698 assert(start != InvalidAbsoluteOffset);
6699 newFaultRegionAndFunclet(start, m_ue.bcPos(),
6700 new UnsetUnnamedLocalThunklet(tempLocal));
6701 emitVirtualLocal(tempLocal);
6702 emitPushL(e);
6703 m_curFunc->freeUnnamedLocal(tempLocal);
6706 EmitterVisitor::PassByRefKind
6707 EmitterVisitor::getPassByRefKind(ExpressionPtr exp) {
6708 auto permissiveKind = PassByRefKind::AllowCell;
6710 // The PassByRefKind of a list assignment expression is determined
6711 // by the PassByRefKind of the RHS. This loop will repeatedly recurse
6712 // on the RHS until it encounters an expression other than a list
6713 // assignment expression.
6714 while (exp->is(Expression::KindOfListAssignment)) {
6715 exp = static_pointer_cast<ListAssignment>(exp)->getArray();
6716 permissiveKind = PassByRefKind::WarnOnCell;
6719 switch (exp->getKindOf()) {
6720 case Expression::KindOfSimpleFunctionCall: {
6721 auto sfc = static_pointer_cast<SimpleFunctionCall>(exp);
6722 // this only happens for calls that have been morphed into bytecode
6723 // e.g. idx(), abs(), strlen(), etc..
6724 // It is to allow the following code to work
6725 // function f(&$arg) {...}
6726 // f(idx($array, 'key')); <- this fails otherwise
6727 if (sfc->hasBeenChangedToBytecode()) {
6728 return PassByRefKind::AllowCell;
6730 } break;
6731 case Expression::KindOfNewObjectExpression:
6732 case Expression::KindOfIncludeExpression:
6733 case Expression::KindOfSimpleVariable:
6734 // New and include/require
6735 return PassByRefKind::AllowCell;
6736 case Expression::KindOfArrayElementExpression:
6737 // Allow if bare; warn if inside list assignment
6738 return permissiveKind;
6739 case Expression::KindOfAssignmentExpression:
6740 // Assignment (=) and binding assignment (=&)
6741 return PassByRefKind::WarnOnCell;
6742 case Expression::KindOfBinaryOpExpression: {
6743 auto b = static_pointer_cast<BinaryOpExpression>(exp);
6744 // Assignment op (+=, -=, *=, etc)
6745 if (b->isAssignmentOp()) return PassByRefKind::WarnOnCell;
6746 } break;
6747 case Expression::KindOfUnaryOpExpression: {
6748 auto u = static_pointer_cast<UnaryOpExpression>(exp);
6749 int op = u->getOp();
6750 if (op == T_CLONE) {
6751 // clone
6752 return PassByRefKind::AllowCell;
6753 } else if (op == '@' || op == T_EVAL ||
6754 ((op == T_INC || op == T_DEC) && u->getFront())) {
6755 // Silence operator, eval, preincrement, and predecrement
6756 return PassByRefKind::WarnOnCell;
6758 } break;
6759 case Expression::KindOfExpressionList: {
6760 auto el = static_pointer_cast<ExpressionList>(exp);
6761 if (el->getListKind() != ExpressionList::ListKindParam) {
6762 return el->getListKind() == ExpressionList::ListKindWrappedNoWarn ?
6763 PassByRefKind::AllowCell : PassByRefKind::WarnOnCell;
6765 } break;
6766 default:
6767 break;
6769 // All other cases
6770 return PassByRefKind::ErrorOnCell;
6773 bool EmitterVisitor::emitBuiltinCallArg(Emitter& e,
6774 ExpressionPtr exp,
6775 int paramId,
6776 bool byRef,
6777 bool mustBeRef) {
6778 visit(exp);
6779 if (checkIfStackEmpty("Builtin arg*")) return true;
6780 if (byRef) {
6781 auto wasCell = emitVGet(e, true);
6782 if (wasCell && mustBeRef) {
6783 auto kind = getPassByRefKind(exp);
6784 switch (kind) {
6785 case PassByRefKind::AllowCell:
6786 // nop
6787 break;
6788 case PassByRefKind::WarnOnCell:
6789 e.String(
6790 makeStaticString("Only variables should be passed by reference"));
6791 e.Int(k_E_STRICT);
6792 e.FCallBuiltin(2, 2, s_trigger_error.get());
6793 emitPop(e);
6794 break;
6795 case PassByRefKind::ErrorOnCell:
6796 auto save = m_evalStack;
6797 e.String(
6798 makeStaticString("Only variables can be passed by reference"));
6799 e.Fatal(FatalOp::Runtime);
6800 m_evalStackIsUnknown = false;
6801 m_evalStack = save;
6802 return false;
6805 } else {
6806 emitCGet(e);
6808 return true;
6811 static bool isNormalLocalVariable(const ExpressionPtr& expr) {
6812 SimpleVariable* sv = static_cast<SimpleVariable*>(expr.get());
6813 return (expr->is(Expression::KindOfSimpleVariable) &&
6814 !sv->isSuperGlobal() &&
6815 !sv->isThis());
6818 void EmitterVisitor::emitLambdaCaptureArg(Emitter& e, ExpressionPtr exp) {
6819 // Constant folding may lead this to be not a var anymore,
6820 // so we should not be emitting *GetL in this case.
6821 if (!isNormalLocalVariable(exp)) {
6822 visit(exp);
6823 return;
6825 auto const sv = static_cast<SimpleVariable*>(exp.get());
6826 Id locId = m_curFunc->lookupVarId(makeStaticString(sv->getName()));
6827 emitVirtualLocal(locId);
6828 e.CUGetL(locId);
6831 void EmitterVisitor::emitBuiltinDefaultArg(Emitter& e, Variant& v,
6832 MaybeDataType t, int paramId) {
6833 switch (v.getType()) {
6834 case KindOfNull:
6835 if (t) {
6836 [&] {
6837 switch (*t) {
6838 case KindOfPersistentString:
6839 case KindOfString:
6840 case KindOfPersistentVec:
6841 case KindOfVec:
6842 case KindOfPersistentDict:
6843 case KindOfDict:
6844 case KindOfPersistentKeyset:
6845 case KindOfKeyset:
6846 case KindOfPersistentArray:
6847 case KindOfArray:
6848 case KindOfObject:
6849 case KindOfResource:
6850 e.Int(0);
6851 return;
6852 case KindOfUninit:
6853 case KindOfNull:
6854 case KindOfBoolean:
6855 case KindOfInt64:
6856 case KindOfDouble:
6857 case KindOfRef:
6858 case KindOfClass:
6859 break;
6861 not_reached();
6862 }();
6863 } else {
6864 e.NullUninit();
6866 return;
6868 case KindOfBoolean:
6869 if (v.getBoolean()) {
6870 e.True();
6871 } else {
6872 e.False();
6874 return;
6876 case KindOfInt64:
6877 e.Int(v.getInt64());
6878 return;
6880 case KindOfDouble:
6881 e.Double(v.toDouble());
6882 return;
6884 case KindOfPersistentString:
6885 case KindOfString: {
6886 StringData *nValue = makeStaticString(v.getStringData());
6887 e.String(nValue);
6888 return;
6891 case KindOfPersistentVec:
6892 case KindOfVec:
6893 assert(v.isVecArray());
6894 e.Vec(v.getArrayData());
6895 return;
6897 case KindOfPersistentDict:
6898 case KindOfDict:
6899 assert(v.isDict());
6900 e.Dict(v.getArrayData());
6901 return;
6903 case KindOfPersistentKeyset:
6904 case KindOfKeyset:
6905 assert(v.isKeyset());
6906 e.Keyset(v.getArrayData());
6907 return;
6909 case KindOfPersistentArray:
6910 case KindOfArray:
6911 assert(v.isPHPArray());
6912 e.Array(v.getArrayData());
6913 return;
6915 case KindOfUninit:
6916 case KindOfObject:
6917 case KindOfResource:
6918 case KindOfRef:
6919 case KindOfClass:
6920 break;
6922 not_reached();
6925 void EmitterVisitor::emitFuncCallArg(Emitter& e,
6926 ExpressionPtr exp,
6927 int paramId,
6928 bool isUnpack) {
6929 visit(exp);
6930 if (checkIfStackEmpty("FPass*")) return;
6932 // TODO(4599379): if dealing with an unpack, here is where we'd want to
6933 // emit a bytecode to traverse any containers;
6935 auto kind = getPassByRefKind(exp);
6936 if (isUnpack) {
6937 // This deals with the case where the called function has a
6938 // by ref param at the index of the unpack (because we don't
6939 // want to box the unpack itself).
6940 // But note that unless the user created the array manually,
6941 // and added reference params at the correct places, we'll
6942 // still get warnings, and the array elements will not be
6943 // passed by reference.
6944 emitConvertToCell(e);
6945 kind = PassByRefKind::AllowCell;
6947 emitFPass(e, paramId, kind);
6950 void EmitterVisitor::emitFPass(Emitter& e, int paramId,
6951 PassByRefKind passByRefKind) {
6952 if (checkIfStackEmpty("FPass*")) return;
6953 LocationGuard locGuard(e, m_tempLoc);
6954 m_tempLoc.clear();
6956 emitClsIfSPropBase(e);
6957 int iLast = m_evalStack.size()-1;
6958 int i = scanStackForLocation(iLast);
6959 int sz = iLast - i;
6960 assert(sz >= 0);
6961 char sym = m_evalStack.get(i);
6962 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
6963 switch (sym) {
6964 case StackSym::L: e.FPassL(paramId, m_evalStack.getLoc(i)); break;
6965 case StackSym::C:
6966 switch (passByRefKind) {
6967 case PassByRefKind::AllowCell: e.FPassC(paramId); break;
6968 case PassByRefKind::WarnOnCell: e.FPassCW(paramId); break;
6969 case PassByRefKind::ErrorOnCell: e.FPassCE(paramId); break;
6970 default: assert(false);
6972 break;
6973 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
6974 case StackSym::CN: e.FPassN(paramId); break;
6975 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
6976 case StackSym::CG: e.FPassG(paramId); break;
6977 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
6978 case StackSym::CS: e.FPassS(paramId); break;
6979 case StackSym::V: e.FPassV(paramId); break;
6980 case StackSym::R: e.FPassR(paramId); break;
6981 default: {
6982 unexpectedStackSym(sym, "emitFPass");
6983 break;
6986 } else {
6987 auto const stackCount = emitMOp(i, iLast, e, MInstrOpts{paramId});
6988 e.FPassM(paramId, stackCount, symToMemberKey(e, iLast, true /* allowW */));
6992 void EmitterVisitor::emitIsset(Emitter& e) {
6993 if (checkIfStackEmpty("Isset*")) return;
6995 emitClsIfSPropBase(e);
6996 int iLast = m_evalStack.size()-1;
6997 int i = scanStackForLocation(iLast);
6998 int sz = iLast - i;
6999 assert(sz >= 0);
7000 char sym = m_evalStack.get(i);
7001 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7002 switch (sym) {
7003 case StackSym::L: e.IssetL(m_evalStack.getLoc(i)); break;
7004 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
7005 case StackSym::CN: e.IssetN(); break;
7006 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
7007 case StackSym::CG: e.IssetG(); break;
7008 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
7009 case StackSym::CS: e.IssetS(); break;
7010 //XXX: Zend does not allow isset() on the result
7011 // of a function call. We allow it here so that emitted
7012 // code is valid. Once the parser handles this correctly,
7013 // the R and C cases can go.
7014 case StackSym::R: e.UnboxR(); // fall through
7015 case StackSym::C:
7016 e.IsTypeC(IsTypeOp::Null);
7017 e.Not();
7018 break;
7019 default: {
7020 unexpectedStackSym(sym, "emitIsset");
7021 break;
7024 } else {
7025 emitQueryMOp(i, iLast, e, QueryMOp::Isset);
7029 void EmitterVisitor::emitIsType(Emitter& e, IsTypeOp op) {
7030 if (checkIfStackEmpty("IsType")) return;
7032 emitConvertToCellOrLoc(e);
7033 switch (char sym = m_evalStack.top()) {
7034 case StackSym::L:
7035 e.IsTypeL(m_evalStack.getLoc(m_evalStack.size() - 1), op);
7036 break;
7037 case StackSym::C:
7038 e.IsTypeC(op);
7039 break;
7040 default:
7041 unexpectedStackSym(sym, "emitIsType");
7045 void EmitterVisitor::emitEmpty(Emitter& e) {
7046 if (checkIfStackEmpty("Empty*")) 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.EmptyL(m_evalStack.getLoc(i)); break;
7057 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
7058 case StackSym::CN: e.EmptyN(); break;
7059 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
7060 case StackSym::CG: e.EmptyG(); break;
7061 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
7062 case StackSym::CS: e.EmptyS(); break;
7063 case StackSym::R: e.UnboxR(); // fall through
7064 case StackSym::C: e.Not(); break;
7065 default: {
7066 unexpectedStackSym(sym, "emitEmpty");
7067 break;
7070 } else {
7071 emitQueryMOp(i, iLast, e, QueryMOp::Empty);
7075 void EmitterVisitor::emitUnset(Emitter& e,
7076 ExpressionPtr exp /* = ExpressionPtr() */) {
7077 if (checkIfStackEmpty("Unset*")) return;
7079 emitClsIfSPropBase(e);
7080 int iLast = m_evalStack.size()-1;
7081 int i = scanStackForLocation(iLast);
7082 int sz = iLast - i;
7083 assert(sz >= 0);
7084 char sym = m_evalStack.get(i);
7085 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7086 switch (sym) {
7087 case StackSym::L: e.UnsetL(m_evalStack.getLoc(i)); break;
7088 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
7089 case StackSym::CN: e.UnsetN(); break;
7090 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
7091 case StackSym::CG: e.UnsetG(); break;
7092 case StackSym::LS: // fall through
7093 case StackSym::CS: {
7094 assert(exp);
7096 std::ostringstream s;
7097 s << "Attempt to unset static property " << exp->getText();
7098 emitMakeUnitFatal(e, s.str().c_str());
7099 break;
7101 default: {
7102 unexpectedStackSym(sym, "emitUnset");
7103 break;
7106 } else {
7107 auto const stackCount = emitMOp(i, iLast, e, MInstrOpts{MOpMode::Unset});
7108 e.UnsetM(stackCount, symToMemberKey(e, iLast, false /* allowW */));
7112 void EmitterVisitor::emitUnsetL(Emitter&e, Id local) {
7113 emitVirtualLocal(local);
7114 e.UnsetL(local);
7117 void EmitterVisitor::emitVisitAndUnset(Emitter& e, ExpressionPtr exp) {
7118 visit(exp);
7119 emitUnset(e, exp);
7122 void EmitterVisitor::emitSet(Emitter& e) {
7123 if (checkIfStackEmpty("Set*")) return;
7125 int iLast = m_evalStack.size()-2;
7126 int i = scanStackForLocation(iLast);
7127 int sz = iLast - i;
7128 assert(sz >= 0);
7129 char sym = m_evalStack.get(i);
7130 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7131 switch (sym) {
7132 case StackSym::L: e.SetL(m_evalStack.getLoc(i)); break;
7133 case StackSym::LN: emitCGetL2(e); // fall through
7134 case StackSym::CN: e.SetN(); break;
7135 case StackSym::LG: emitCGetL2(e); // fall through
7136 case StackSym::CG: e.SetG(); break;
7137 case StackSym::LS: emitCGetL3(e); // fall through
7138 case StackSym::CS: e.SetS(); break;
7139 default: {
7140 unexpectedStackSym(sym, "emitSet");
7141 break;
7144 } else {
7145 auto const stackCount =
7146 emitMOp(i, iLast, e, MInstrOpts{MOpMode::Define}.rhs());
7147 return e.SetM(stackCount, symToMemberKey(e, iLast, true /* allowW */));
7152 * emitSet() requires the destination to be placed on the stack before the
7153 * source cell. If the destination is a local, this restriction imposed by
7154 * the symbolic stack abstraction goes beyond the actual requirements of HHBC,
7155 * where the local is represented by an immediate argument passed to SetL.
7156 * This function allows you to bypass this requirement.
7158 void EmitterVisitor::emitSetL(Emitter& e, Id local) {
7159 popEvalStack(StackSym::C);
7160 emitVirtualLocal(local);
7161 pushEvalStack(StackSym::C);
7162 e.SetL(local);
7165 void EmitterVisitor::emitSetOp(Emitter& e, int tokenOp) {
7166 if (checkIfStackEmpty("SetOp*")) return;
7168 auto ifIntOverflow = [](SetOpOp trueVal, SetOpOp falseVal) {
7169 return RuntimeOption::IntsOverflowToInts ? trueVal : falseVal;
7172 auto const op = [&] {
7173 switch (tokenOp) {
7174 case T_PLUS_EQUAL:
7175 return ifIntOverflow(SetOpOp::PlusEqual, SetOpOp::PlusEqualO);
7176 case T_MINUS_EQUAL:
7177 return ifIntOverflow(SetOpOp::MinusEqual, SetOpOp::MinusEqualO);
7178 case T_MUL_EQUAL:
7179 return ifIntOverflow(SetOpOp::MulEqual, SetOpOp::MulEqualO);
7180 case T_POW_EQUAL: return SetOpOp::PowEqual;
7181 case T_DIV_EQUAL: return SetOpOp::DivEqual;
7182 case T_CONCAT_EQUAL: return SetOpOp::ConcatEqual;
7183 case T_MOD_EQUAL: return SetOpOp::ModEqual;
7184 case T_AND_EQUAL: return SetOpOp::AndEqual;
7185 case T_OR_EQUAL: return SetOpOp::OrEqual;
7186 case T_XOR_EQUAL: return SetOpOp::XorEqual;
7187 case T_SL_EQUAL: return SetOpOp::SlEqual;
7188 case T_SR_EQUAL: return SetOpOp::SrEqual;
7189 default: break;
7191 not_reached();
7192 }();
7194 int iLast = m_evalStack.size()-2;
7195 int i = scanStackForLocation(iLast);
7196 int sz = iLast - i;
7197 assert(sz >= 0);
7198 char sym = m_evalStack.get(i);
7199 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7200 switch (sym) {
7201 case StackSym::L: e.SetOpL(m_evalStack.getLoc(i), op); break;
7202 case StackSym::LN: emitCGetL2(e); // fall through
7203 case StackSym::CN: e.SetOpN(op); break;
7204 case StackSym::LG: emitCGetL2(e); // fall through
7205 case StackSym::CG: e.SetOpG(op); break;
7206 case StackSym::LS: emitCGetL3(e); // fall through
7207 case StackSym::CS: e.SetOpS(op); break;
7208 default: {
7209 unexpectedStackSym(sym, "emitSetOp");
7210 break;
7213 } else {
7214 auto const stackCount =
7215 emitMOp(i, iLast, e, MInstrOpts{MOpMode::Define}.rhs());
7216 e.SetOpM(stackCount, op, symToMemberKey(e, iLast, true /* allowW */));
7220 void EmitterVisitor::emitBind(Emitter& e) {
7221 if (checkIfStackEmpty("Bind*")) return;
7223 int iLast = m_evalStack.size()-2;
7224 int i = scanStackForLocation(iLast);
7225 int sz = iLast - i;
7226 assert(sz >= 0);
7227 char sym = m_evalStack.get(i);
7228 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7229 switch (sym) {
7230 case StackSym::L: e.BindL(m_evalStack.getLoc(i)); break;
7231 case StackSym::LN: emitCGetL2(e); // fall through
7232 case StackSym::CN: e.BindN(); break;
7233 case StackSym::LG: emitCGetL2(e); // fall through
7234 case StackSym::CG: e.BindG(); break;
7235 case StackSym::LS: emitCGetL3(e); // fall through
7236 case StackSym::CS: e.BindS(); break;
7237 default: {
7238 unexpectedStackSym(sym, "emitBind");
7239 break;
7242 } else {
7243 auto const stackCount =
7244 emitMOp(i, iLast, e, MInstrOpts{MOpMode::Define}.rhs());
7245 e.BindM(stackCount, symToMemberKey(e, iLast, true /* allowW */));
7249 // See EmitterVisitor::emitSetL().
7250 void EmitterVisitor::emitBindL(Emitter& e, Id local) {
7251 popEvalStack(StackSym::V);
7252 emitVirtualLocal(local);
7253 pushEvalStack(StackSym::V);
7254 e.BindL(local);
7257 void EmitterVisitor::emitIncDec(Emitter& e, IncDecOp op) {
7258 if (checkIfStackEmpty("IncDec*")) return;
7260 emitClsIfSPropBase(e);
7261 int iLast = m_evalStack.size()-1;
7262 int i = scanStackForLocation(iLast);
7263 int sz = iLast - i;
7264 assert(sz >= 0);
7265 char sym = m_evalStack.get(i);
7266 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7267 switch (sym) {
7268 case StackSym::L: e.IncDecL(m_evalStack.getLoc(i), op); break;
7269 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
7270 case StackSym::CN: e.IncDecN(op); break;
7271 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
7272 case StackSym::CG: e.IncDecG(op); break;
7273 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
7274 case StackSym::CS: e.IncDecS(op); break;
7275 default: {
7276 unexpectedStackSym(sym, "emitIncDec");
7277 break;
7280 } else {
7281 auto const stackCount =
7282 emitMOp(i, iLast, e, MInstrOpts{MOpMode::Define});
7283 e.IncDecM(stackCount, op, symToMemberKey(e, iLast, true /* allowW */));
7287 void EmitterVisitor::emitConvertToCell(Emitter& e) {
7288 emitCGet(e);
7291 void EmitterVisitor::emitConvertSecondToCell(Emitter& e) {
7292 if (m_evalStack.size() <= 1) {
7293 InvariantViolation(
7294 "Emitter encounted an empty evaluation stack when inside "
7295 "the emitConvertSecondToCell() function (at offset %d)",
7296 m_ue.bcPos());
7297 return;
7299 char sym = m_evalStack.get(m_evalStack.size() - 2);
7300 char symFlavor = StackSym::GetSymFlavor(sym);
7301 if (symFlavor == StackSym::C) {
7302 // do nothing
7303 } else if (symFlavor == StackSym::L) {
7304 emitCGetL2(e);
7305 } else {
7306 // emitConvertSecondToCell() should never be used for symbolic flavors
7307 // other than C or L
7308 InvariantViolation(
7309 "Emitter encountered an unsupported StackSym \"%s\" on "
7310 "the evaluation stack inside the emitConvertSecondToCell()"
7311 " function (at offset %d)",
7312 StackSym::ToString(sym).c_str(),
7313 m_ue.bcPos());
7317 void EmitterVisitor::emitConvertToCellIfVar(Emitter& e) {
7318 if (!m_evalStack.empty()) {
7319 char sym = m_evalStack.top();
7320 if (sym == StackSym::V) {
7321 emitConvertToCell(e);
7326 void EmitterVisitor::emitConvertToCellOrLoc(Emitter& e) {
7327 if (m_evalStack.empty()) {
7328 InvariantViolation(
7329 "Emitter encounted an empty evaluation stack when inside "
7330 "the emitConvertToCellOrLoc() function (at offset %d)",
7331 m_ue.bcPos());
7332 return;
7334 char sym = m_evalStack.top();
7335 if (sym == StackSym::L) {
7336 // If the top of stack is a loc that is not marked, do nothing
7337 } else {
7338 // Otherwise, call emitCGet to convert the top of stack to cell
7339 emitCGet(e);
7343 void EmitterVisitor::emitConvertToVar(Emitter& e) {
7344 emitVGet(e);
7348 * Class bases are stored on the symbolic stack in a "virtual" way so
7349 * we can resolve them later (here) in order to properly handle php
7350 * evaluation order.
7352 * For example, in:
7354 * $cls = 'cls';
7355 * $cls::$x[0][f()] = g();
7357 * We need to evaluate f(), then resolve $cls to an A (possibly
7358 * invoking an autoload handler), then evaluate g(), then do the set.
7360 * Complex cases involve unnamed local temporaries. For example, in:
7362 * ${func()}::${f()} = g();
7364 * We'll emit code which calls func() and stashes the result in a
7365 * unnamed local. Then we call f(), then we turn the unnamed local
7366 * into an 'A' so that autoload handlers will run after f(). Then g()
7367 * is evaluated and then the set happens.
7369 void EmitterVisitor::emitResolveClsBase(Emitter& e, int pos) {
7370 switch (m_evalStack.getClsBaseType(pos)) {
7371 case SymbolicStack::CLS_STRING_NAME:
7372 e.String(m_evalStack.getName(pos));
7373 emitAGet(e);
7374 break;
7375 case SymbolicStack::CLS_LATE_BOUND:
7376 e.LateBoundCls();
7377 break;
7378 case SymbolicStack::CLS_SELF:
7379 e.Self();
7380 break;
7381 case SymbolicStack::CLS_PARENT:
7382 e.Parent();
7383 break;
7384 case SymbolicStack::CLS_NAMED_LOCAL: {
7385 int loc = m_evalStack.getLoc(pos);
7386 emitVirtualLocal(loc);
7387 emitAGet(e);
7388 break;
7390 case SymbolicStack::CLS_UNNAMED_LOCAL: {
7391 int loc = m_evalStack.getLoc(pos);
7392 emitVirtualLocal(loc);
7393 emitAGet(e);
7394 emitUnsetL(e, loc);
7395 newFaultRegionAndFunclet(m_evalStack.getUnnamedLocStart(pos),
7396 m_ue.bcPos(),
7397 new UnsetUnnamedLocalThunklet(loc));
7398 m_curFunc->freeUnnamedLocal(loc);
7399 break;
7401 case SymbolicStack::CLS_INVALID:
7402 default:
7403 assert(false);
7406 m_evalStack.consumeBelowTop(m_evalStack.size() - pos - 1);
7409 void EmitterVisitor::emitClsIfSPropBase(Emitter& e) {
7410 // If the eval stack is empty, then there is no work to do
7411 if (m_evalStack.empty()) return;
7413 // Scan past any values marked with the Elem, NewElem, or Prop markers
7414 int pos = m_evalStack.size() - 1;
7415 for (;;) {
7416 char marker = StackSym::GetMarker(m_evalStack.get(pos));
7417 if (marker != StackSym::E && marker != StackSym::W &&
7418 marker != StackSym::P && marker != StackSym::Q) {
7419 break;
7421 --pos;
7422 if (pos < 0) {
7423 InvariantViolation("Emitter expected a location on the stack but none "
7424 "was found (at offset %d)",
7425 m_ue.bcPos());
7426 return;
7429 // After scanning, if we did not find a value marked with the SProp
7430 // marker then there is no work to do
7431 if (StackSym::GetMarker(m_evalStack.get(pos)) != StackSym::S) {
7432 return;
7435 --pos;
7436 if (pos < 0) {
7437 InvariantViolation(
7438 "Emitter emitted an instruction that tries to consume "
7439 "a value from the stack when the stack is empty "
7440 "(expected symbolic flavor \"C\" or \"L\" at offset %d)",
7441 m_ue.bcPos());
7444 emitResolveClsBase(e, pos);
7445 m_evalStack.set(m_evalStack.size() - 1,
7446 m_evalStack.get(m_evalStack.size() - 1) | StackSym::M);
7449 MaybeDataType EmitterVisitor::analyzeSwitch(SwitchStatementPtr sw,
7450 SwitchState& state) {
7451 auto& caseMap = state.cases;
7452 DataType t = KindOfUninit;
7453 StatementListPtr cases(sw->getCases());
7454 const int ncase = cases->getCount();
7456 // Bail if the cases aren't homogeneous
7457 for (int i = 0; i < ncase; ++i) {
7458 auto c = static_pointer_cast<CaseStatement>((*cases)[i]);
7459 auto condition = c->getCondition();
7460 if (condition) {
7461 Variant cval;
7462 DataType caseType;
7463 if (condition->getScalarValue(cval)) {
7464 caseType = cval.getType();
7465 if (caseType == KindOfPersistentString) caseType = KindOfString;
7466 if ((caseType != KindOfInt64 && caseType != KindOfString) ||
7467 !IMPLIES(t != KindOfUninit, caseType == t)) {
7468 return folly::none;
7470 t = caseType;
7471 } else {
7472 return folly::none;
7474 int64_t n;
7475 bool isNonZero;
7476 if (t == KindOfInt64) {
7477 n = cval.asInt64Val();
7478 isNonZero = n;
7479 } else {
7480 always_assert(t == KindOfString);
7481 n = m_ue.mergeLitstr(cval.asStrRef().get());
7482 isNonZero = false; // not used for string switches
7484 if (!caseMap.count(n)) {
7485 // If 'case n:' appears multiple times, only the first will
7486 // ever match
7487 caseMap[n] = i;
7488 if (t == KindOfString) {
7489 // We have to preserve the original order of the cases for string
7490 // switches because of insane things like 0 being equal to any string
7491 // that is not a nonzero numeric string.
7492 state.caseOrder.push_back(StrCase(safe_cast<Id>(n), i));
7495 if (state.nonZeroI == -1 && isNonZero) {
7496 // true is equal to any non-zero integer, so to preserve php's
7497 // switch semantics we have to remember the first non-zero
7498 // case to appear in the source text
7499 state.nonZeroI = i;
7501 } else if (LIKELY(state.defI == -1)) {
7502 state.defI = i;
7503 } else {
7504 // Multiple defaults are not allowed
7505 throw IncludeTimeFatalException(
7506 c, "Switch statements may only contain one default: clause");
7510 if (t == KindOfInt64) {
7511 int64_t base = caseMap.begin()->first;
7512 int64_t nTargets = caseMap.rbegin()->first - base + 1;
7513 // Fail if the cases are too sparse. We emit Switch even for absurdly small
7514 // cases to allow the jit to decide when to lower back to comparisons.
7515 if ((float)caseMap.size() / nTargets < 0.5) {
7516 return folly::none;
7518 } else if (t == KindOfString) {
7519 if (caseMap.size() < kMinStringSwitchCases) {
7520 return folly::none;
7524 return t;
7527 void EmitterVisitor::emitIntegerSwitch(Emitter& e, SwitchStatementPtr sw,
7528 std::vector<Label>& caseLabels,
7529 Label& done, const SwitchState& state) {
7530 auto& caseMap = state.cases;
7531 int64_t base = caseMap.begin()->first;
7532 int64_t nTargets = caseMap.rbegin()->first - base + 1;
7534 // It's on. Map case values to Labels, filling in the blanks as
7535 // appropriate.
7536 Label* defLabel = state.defI == -1 ? &done : &caseLabels[state.defI];
7537 std::vector<Label*> labels(nTargets + 2);
7538 for (int i = 0; i < nTargets; ++i) {
7539 if (auto const caseIdx = folly::get_ptr(caseMap, base + i)) {
7540 labels[i] = &caseLabels[*caseIdx];
7541 } else {
7542 labels[i] = defLabel;
7546 // Fill in offsets for the first non-zero case and default
7547 labels[labels.size() - 2] =
7548 state.nonZeroI == -1 ? defLabel : &caseLabels[state.nonZeroI];
7549 labels[labels.size() - 1] = defLabel;
7551 visit(sw->getExp());
7552 emitConvertToCell(e);
7553 e.Switch(SwitchKind::Bounded, base, labels);
7556 void EmitterVisitor::emitStringSwitch(Emitter& e, SwitchStatementPtr sw,
7557 std::vector<Label>& caseLabels,
7558 Label& done, const SwitchState& state) {
7559 std::vector<Emitter::StrOff> labels;
7560 for (auto& pair : state.caseOrder) {
7561 labels.push_back(Emitter::StrOff(pair.first, &caseLabels[pair.second]));
7564 // Default case comes last
7565 Label* defLabel = state.defI == -1 ? &done : &caseLabels[state.defI];
7566 labels.push_back(Emitter::StrOff(-1, defLabel));
7568 visit(sw->getExp());
7569 emitConvertToCell(e);
7570 e.SSwitch(labels);
7573 void EmitterVisitor::markElem(Emitter& e) {
7574 if (m_evalStack.empty()) {
7575 InvariantViolation("Emitter encountered an empty evaluation stack inside"
7576 " the markElem function (at offset %d)",
7577 m_ue.bcPos());
7578 return;
7580 char sym = m_evalStack.top();
7581 if (sym == StackSym::C || sym == StackSym::L || sym == StackSym::T ||
7582 sym == StackSym::I) {
7583 m_evalStack.set(m_evalStack.size()-1, (sym | StackSym::E));
7584 } else {
7585 InvariantViolation(
7586 "Emitter encountered an unsupported StackSym \"%s\" on "
7587 "the evaluation stack inside the markElem function (at "
7588 "offset %d)",
7589 StackSym::ToString(sym).c_str(),
7590 m_ue.bcPos());
7594 void EmitterVisitor::markNewElem(Emitter& e) {
7595 m_evalStack.push(StackSym::W);
7598 void EmitterVisitor::markProp(Emitter& e, PropAccessType propAccessType) {
7599 if (m_evalStack.empty()) {
7600 InvariantViolation(
7601 "Emitter encountered an empty evaluation stack inside "
7602 "the markProp function (at offset %d)",
7603 m_ue.bcPos());
7604 return;
7606 char sym = m_evalStack.top();
7607 if (sym == StackSym::C || sym == StackSym::L || sym == StackSym::T) {
7608 m_evalStack.set(
7609 m_evalStack.size()-1,
7610 (sym | (propAccessType == PropAccessType::NullSafe
7611 ? StackSym::Q
7612 : StackSym::P
7615 } else {
7616 InvariantViolation(
7617 "Emitter encountered an unsupported StackSym \"%s\" on "
7618 "the evaluation stack inside the markProp function (at "
7619 "offset %d)",
7620 StackSym::ToString(sym).c_str(),
7621 m_ue.bcPos());
7625 void EmitterVisitor::markSProp(Emitter& e) {
7626 if (m_evalStack.empty()) {
7627 InvariantViolation(
7628 "Emitter encountered an empty evaluation stack inside "
7629 "the markSProp function (at offset %d)",
7630 m_ue.bcPos());
7631 return;
7633 char sym = m_evalStack.top();
7634 if (sym == StackSym::C || sym == StackSym::L) {
7635 m_evalStack.set(m_evalStack.size()-1, (sym | StackSym::S));
7636 } else {
7637 InvariantViolation(
7638 "Emitter encountered an unsupported StackSym \"%s\" on "
7639 "the evaluation stack inside the markSProp function "
7640 "(at offset %d)",
7641 StackSym::ToString(sym).c_str(),
7642 m_ue.bcPos());
7646 #define MARK_NAME_BODY(index, requiredStackSize) \
7647 if (m_evalStack.size() < requiredStackSize) { \
7648 InvariantViolation( \
7649 "Emitter encountered an evaluation stack with %lu" \
7650 " elements inside the %s function (at offset %d)", \
7651 (unsigned long)m_evalStack.size(), \
7652 __FUNCTION__, m_ue.bcPos()); \
7653 return; \
7655 char sym = m_evalStack.get(index); \
7656 if (sym == StackSym::C || sym == StackSym::L) { \
7657 m_evalStack.set(index, (sym | StackSym::N)); \
7658 } else { \
7659 InvariantViolation( \
7660 "Emitter encountered an unsupported StackSym \"%s\" " \
7661 "on the evaluation stack inside the %s function (at " \
7662 "offset %d)", \
7663 StackSym::ToString(sym).c_str(), __FUNCTION__, \
7664 m_ue.bcPos()); \
7667 void EmitterVisitor::markName(Emitter& e) {
7668 int index = m_evalStack.size() - 1;
7669 MARK_NAME_BODY(index, 1);
7672 void EmitterVisitor::markNameSecond(Emitter& e) {
7673 int index = m_evalStack.size() - 2;
7674 MARK_NAME_BODY(index, 2);
7677 #undef MARK_NAME_BODY
7679 void EmitterVisitor::markGlobalName(Emitter& e) {
7680 if (m_evalStack.empty()) {
7681 InvariantViolation(
7682 "Emitter encountered an empty evaluation stack inside "
7683 "the markGlobalName function (at offset %d)",
7684 m_ue.bcPos());
7685 return;
7687 char sym = m_evalStack.top();
7688 if (sym == StackSym::C || sym == StackSym::L) {
7689 m_evalStack.set(m_evalStack.size()-1, (sym | StackSym::G));
7690 } else {
7691 InvariantViolation(
7692 "Emitter encountered an unsupported StackSym \"%s\" on "
7693 "the evaluation stack inside the markGlobalName function "
7694 "(at offset %d)",
7695 StackSym::ToString(sym).c_str(),
7696 m_ue.bcPos());
7700 void EmitterVisitor::emitNameString(Emitter& e, ExpressionPtr n,
7701 bool allowLiteral) {
7702 Variant v;
7703 if (n->getScalarValue(v) && v.isString()) {
7704 StringData* nLiteral = makeStaticString(v.toCStrRef().get());
7705 if (allowLiteral) {
7706 m_evalStack.push(StackSym::T);
7707 } else {
7708 e.String(nLiteral);
7710 m_evalStack.setString(nLiteral);
7711 } else {
7712 visit(n);
7713 emitConvertToCellOrLoc(e);
7717 void EmitterVisitor::postponeMeth(MethodStatementPtr m, FuncEmitter* fe,
7718 bool top,
7719 ClosureUseVarVec* useVars /* = NULL */) {
7720 m_postponedMeths.push_back(PostponedMeth(m, fe, top, useVars));
7723 void EmitterVisitor::postponeCtor(InterfaceStatementPtr is, FuncEmitter* fe) {
7724 m_postponedCtors.push_back(PostponedCtor(is, fe));
7727 void EmitterVisitor::postponePinit(InterfaceStatementPtr is, FuncEmitter* fe,
7728 NonScalarVec* v) {
7729 m_postponedPinits.push_back(PostponedNonScalars(is, fe, v));
7732 void EmitterVisitor::postponeSinit(InterfaceStatementPtr is, FuncEmitter* fe,
7733 NonScalarVec* v) {
7734 m_postponedSinits.push_back(PostponedNonScalars(is, fe, v));
7737 void EmitterVisitor::postponeCinit(InterfaceStatementPtr is, FuncEmitter* fe,
7738 NonScalarVec* v) {
7739 m_postponedCinits.push_back(PostponedNonScalars(is, fe, v));
7742 static Attr buildAttrs(ModifierExpressionPtr mod, bool isRef = false) {
7743 int attrs = AttrNone;
7744 if (isRef) {
7745 attrs |= AttrReference;
7747 if (mod) {
7748 attrs |= mod->isPublic() ? AttrPublic :
7749 mod->isPrivate() ? AttrPrivate :
7750 mod->isProtected() ? AttrProtected : AttrNone;
7751 if (mod->isStatic()) {
7752 attrs |= AttrStatic;
7754 if (mod->isAbstract()) {
7755 attrs |= AttrAbstract;
7757 if (mod->isFinal()) {
7758 attrs |= AttrFinal;
7761 return Attr(attrs);
7765 * <<__HipHopSpecific>> user attribute marks funcs/methods as HipHop specific
7766 * for reflection.
7767 * <<__IsFoldable>> Function has no side-effects and may be called at
7768 * compile time with constant input to get deterministic output.
7770 const StaticString
7771 s_IsFoldable("__IsFoldable"),
7772 s_AllowStatic("__AllowStatic"),
7773 s_ParamCoerceModeNull("__ParamCoerceModeNull"),
7774 s_ParamCoerceModeFalse("__ParamCoerceModeFalse");
7776 static void parseUserAttributes(FuncEmitter* fe, Attr& attrs) {
7777 if (fe->userAttributes.count(s_IsFoldable.get())) {
7778 attrs = attrs | AttrIsFoldable;
7780 if ((attrs & AttrBuiltin) &&
7781 fe->pce() &&
7782 !(attrs & AttrStatic) &&
7783 !fe->userAttributes.count(s_AllowStatic.get())) {
7784 attrs |= AttrRequiresThis;
7786 if (fe->userAttributes.count(s_ParamCoerceModeNull.get())) {
7787 attrs = attrs | AttrParamCoerceModeNull;
7788 } else if (fe->userAttributes.count(s_ParamCoerceModeFalse.get())) {
7789 attrs = attrs | AttrParamCoerceModeFalse;
7793 static Attr buildMethodAttrs(MethodStatementPtr meth, FuncEmitter* fe,
7794 bool top) {
7795 FunctionScopePtr funcScope = meth->getFunctionScope();
7796 ModifierExpressionPtr mod(meth->getModifiers());
7797 Attr attrs = buildAttrs(mod, meth->isRef());
7799 // Be conservative by default. HHBBC can clear it where appropriate.
7800 attrs |= AttrMayUseVV;
7802 if (funcScope->hasRefVariadicParam()) {
7803 attrs |= AttrVariadicByRef;
7806 auto fullName = meth->getOriginalFullName();
7807 auto it = Option::FunctionSections.find(fullName);
7808 if ((it != Option::FunctionSections.end() && it->second == "hot") ||
7809 (RuntimeOption::EvalRandomHotFuncs &&
7810 (hash_string_i_unsafe(fullName.c_str(), fullName.size()) & 8))) {
7811 attrs = attrs | AttrHot;
7814 if (!SystemLib::s_inited) {
7815 // we're building systemlib. everything is unique
7816 attrs = attrs | AttrBuiltin | AttrUnique | AttrPersistent;
7817 } else if (Option::WholeProgram) {
7818 if (!funcScope->isRedeclaring()) {
7819 attrs = attrs | AttrUnique;
7820 if (top &&
7821 (!funcScope->isVolatile() ||
7822 funcScope->isPersistent())) {
7823 attrs = attrs | AttrPersistent;
7826 if (meth->getClassScope() &&
7827 !funcScope->hasOverride() &&
7828 !meth->getClassScope()->isRedeclaring()) {
7829 attrs = attrs | AttrNoOverride;
7831 if (funcScope->isFromTrait()) {
7832 attrs = attrs | AttrTrait;
7834 if (funcScope->isSystem()) {
7835 assert((attrs & AttrPersistent) || meth->getClassScope());
7836 attrs = attrs | AttrBuiltin;
7840 // For closures, the MethodStatement didn't have real attributes; enforce
7841 // that the __invoke method is public here
7842 if (fe->isClosureBody) {
7843 assert(!(attrs & (AttrProtected | AttrPrivate)));
7844 attrs = attrs | AttrPublic;
7847 // Coerce memoized methods to private. This is needed for code that uses
7848 // parent:: to call through to the correct underlying function
7849 if (meth->is(Statement::KindOfMethodStatement) && fe->isMemoizeImpl) {
7850 attrs = static_cast<Attr>(attrs & ~(AttrPublic | AttrProtected));
7851 attrs = attrs | AttrPrivate;
7854 parseUserAttributes(fe, attrs);
7855 // Not supported except in __Native functions
7856 attrs = static_cast<Attr>(
7857 attrs & ~(AttrParamCoerceModeNull | AttrParamCoerceModeFalse));
7859 return attrs;
7863 * The code below is used for both, function/method parameter type as well as
7864 * for function/method return type.
7866 static TypeConstraint
7867 determine_type_constraint_from_annot(const TypeAnnotationPtr annot,
7868 bool is_return) {
7869 if (annot) {
7870 auto flags = TypeConstraint::ExtendedHint | TypeConstraint::HHType;
7872 // We only care about a subset of extended type constaints:
7873 // typevar, nullable, soft, return types.
7875 // For everything else, we return {}. We also return {} for annotations
7876 // we don't know how to handle.
7877 if (annot->isFunction() || annot->isMixed()) {
7878 return {};
7880 if (annot->isTypeAccess()) {
7881 flags = flags | TypeConstraint::TypeConstant;
7883 if (annot->isTypeVar()) {
7884 flags = flags | TypeConstraint::TypeVar;
7886 if (annot->isNullable()) {
7887 flags = flags | TypeConstraint::Nullable;
7889 if (annot->isSoft()) {
7890 flags = flags | TypeConstraint::Soft;
7892 if (!is_return &&
7893 (flags == (TypeConstraint::ExtendedHint | TypeConstraint::HHType))) {
7894 return {};
7897 auto strippedName = annot->stripNullable().stripSoft().vanillaName();
7899 return TypeConstraint{
7900 makeStaticString(strippedName),
7901 flags
7905 return {};
7908 static TypeConstraint
7909 determine_type_constraint(const ParameterExpressionPtr& par) {
7910 if (par->hasTypeHint()) {
7911 auto ce = dynamic_pointer_cast<ConstantExpression>(par->defaultValue());
7912 auto flags = TypeConstraint::NoFlags;
7913 if (ce && ce->isNull()) {
7914 flags = flags|TypeConstraint::Nullable;
7916 if (par->hhType()) {
7917 flags = flags|TypeConstraint::HHType;
7919 return TypeConstraint{
7920 makeStaticString(par->getOriginalTypeHint()),
7921 flags
7925 return determine_type_constraint_from_annot(par->annotation(), false);
7928 void EmitterVisitor::emitPostponedMeths() {
7929 std::vector<FuncEmitter*> top_fes;
7930 while (!m_postponedMeths.empty()) {
7931 assert(m_evalStack.m_actualStackHighWaterPtr == nullptr);
7932 PostponedMeth& p = m_postponedMeths.front();
7933 MethodStatementPtr meth = p.m_meth;
7934 FuncEmitter* fe = p.m_fe;
7936 ITRACE(1, "Emitting postponed method {}\n", meth->getOriginalFullName());
7937 Trace::Indent indent;
7939 if (!fe) {
7940 assert(p.m_top);
7941 const StringData* methName = makeStaticString(meth->getOriginalName());
7942 fe = new FuncEmitter(m_ue, -1, -1, methName);
7943 auto oldFunc = m_topMethodEmitted.find(meth->getOriginalName());
7944 if (oldFunc != m_topMethodEmitted.end()) {
7945 throw IncludeTimeFatalException(
7946 meth,
7947 "Cannot redeclare %s() (previously declared in %s:%d)",
7948 meth->getOriginalName().c_str(),
7949 oldFunc->second->ue().m_filepath->data(),
7950 oldFunc->second->getLocation().second);
7952 m_topMethodEmitted.emplace(meth->getOriginalName(), fe);
7954 p.m_fe = fe;
7955 top_fes.push_back(fe);
7958 auto funcScope = meth->getFunctionScope();
7959 m_curFunc = fe;
7960 fe->isAsync = funcScope->isAsync();
7961 fe->isGenerator = funcScope->isGenerator();
7963 if (fe->isAsync && !fe->isGenerator && meth->retTypeAnnotation()) {
7964 auto rta = meth->retTypeAnnotation();
7965 auto nTypeArgs = rta->numTypeArgs();
7966 if (!rta->isAwaitable() && !rta->isWaitHandle()) {
7967 if (fe->isClosureBody) {
7968 throw IncludeTimeFatalException(
7969 meth,
7970 "Return type hint for async closure must be awaitable"
7972 } else {
7973 throw IncludeTimeFatalException(
7974 meth,
7975 "Return type hint for async %s %s() must be awaitable",
7976 meth->getClassScope() ? "method" : "function",
7977 meth->getOriginalFullName().c_str()
7981 if (nTypeArgs >= 2) {
7982 throw IncludeTimeFatalException(
7983 meth,
7984 "Awaitable interface expects 1 type argument, %d given",
7985 nTypeArgs);
7989 if (funcScope->userAttributes().count("__Memoize") &&
7990 !funcScope->isAbstract()) {
7991 auto const originalName = fe->name;
7992 auto const rewrittenName = makeStaticString(
7993 folly::sformat("{}$memoize_impl", fe->name->data()));
7995 FuncEmitter* memoizeFe = nullptr;
7996 if (meth->is(Statement::KindOfFunctionStatement)) {
7997 if (!p.m_top) {
7998 throw IncludeTimeFatalException(meth,
7999 "<<__Memoize>> cannot be applied to closures and inline functions");
8002 memoizeFe = new FuncEmitter(m_ue, -1, -1, originalName);
8003 fe->name = rewrittenName;
8004 top_fes.push_back(memoizeFe);
8005 } else {
8006 // Rename the method and create a new method with the original name
8007 fe->pce()->renameMethod(originalName, rewrittenName);
8008 memoizeFe = m_ue.newMethodEmitter(originalName, fe->pce());
8009 bool added UNUSED = fe->pce()->addMethod(memoizeFe);
8010 assert(added);
8013 // Emit the new method that handles the memoization
8014 m_curFunc = memoizeFe;
8015 m_curFunc->isMemoizeWrapper = true;
8016 addMemoizeProp(meth);
8017 emitMethodMetadata(meth, p.m_closureUseVars, p.m_top);
8018 emitMemoizeMethod(meth, rewrittenName);
8020 // Switch back to the original method and mark it as a memoize
8021 // implementation
8022 m_curFunc = fe;
8023 m_curFunc->isMemoizeImpl = true;
8026 if (funcScope->isNative()) {
8027 auto const attr = bindNativeFunc(meth, fe, false);
8028 if (attr & (AttrReadsCallerFrame | AttrWritesCallerFrame)) {
8029 // If this is a builtin which may access the caller's frame, generate a
8030 // dynamic call wrapper function. Dynamic calls to the builtin will be
8031 // routed to this wrapper instead. This function is identical to the
8032 // normal builtin function, except it includes the VarEnvDynCall opcode.
8033 if (!meth->is(Statement::KindOfFunctionStatement) ||
8034 !p.m_top || fe->pce()) {
8035 throw IncludeTimeFatalException(
8036 meth,
8037 "ReadsCallerFrame or WritesCallerFrame can only "
8038 "be applied to top-level functions"
8041 auto const rewrittenName = makeStaticString(
8042 folly::sformat("{}$dyncall_wrapper", fe->name->data()));
8043 auto stub = m_ue.newFuncEmitter(rewrittenName);
8044 m_curFunc = stub;
8045 SCOPE_EXIT { m_curFunc = fe; };
8046 bindNativeFunc(meth, stub, true);
8047 assert(stub->id() != kInvalidId);
8048 fe->dynCallWrapperId = stub->id();
8050 } else {
8051 emitMethodMetadata(meth, p.m_closureUseVars, p.m_top);
8052 emitMethod(meth);
8055 if (fe->isClosureBody) {
8056 TypedValue uninit;
8057 tvWriteUninit(&uninit);
8058 for (auto& sv : m_curFunc->staticVars) {
8059 auto const str = makeStaticString(
8060 folly::format("86static_{}", sv.name->data()).str());
8061 fe->pce()->addProperty(str, AttrPrivate, nullptr, nullptr,
8062 &uninit, RepoAuthType{});
8066 delete p.m_closureUseVars;
8067 m_postponedMeths.pop_front();
8070 for (size_t i = 0; i < top_fes.size(); i++) {
8071 m_ue.appendTopEmitter(top_fes[i]);
8075 void EmitterVisitor::bindUserAttributes(MethodStatementPtr meth,
8076 FuncEmitter *fe) {
8077 auto const& userAttrs = meth->getFunctionScope()->userAttributes();
8078 for (auto& attr : userAttrs) {
8079 const StringData* uaName = makeStaticString(attr.first);
8080 ExpressionPtr uaValue = attr.second;
8081 assert(uaValue);
8082 assert(uaValue->isScalar());
8083 TypedValue tv;
8084 initScalar(tv, uaValue);
8085 fe->userAttributes[uaName] = tv;
8089 const StaticString s_Void("HH\\void");
8090 const char* attr_Deprecated = "__Deprecated";
8091 const StaticString s_attr_Deprecated(attr_Deprecated);
8093 Attr EmitterVisitor::bindNativeFunc(MethodStatementPtr meth,
8094 FuncEmitter *fe,
8095 bool dynCallWrapper) {
8096 if (SystemLib::s_inited &&
8097 !(Option::WholeProgram && meth->isSystem())) {
8098 throw IncludeTimeFatalException(meth,
8099 "Native functions/methods may only be defined in systemlib");
8102 auto modifiers = meth->getModifiers();
8103 bindUserAttributes(meth, fe);
8105 Attr attributes = AttrBuiltin | AttrUnique | AttrPersistent;
8106 if (meth->isRef()) {
8107 attributes = attributes | AttrReference;
8109 auto pce = fe->pce();
8110 if (pce) {
8111 if (modifiers->isStatic()) {
8112 attributes = attributes | AttrStatic;
8114 if (modifiers->isFinal()) {
8115 attributes = attributes | AttrFinal;
8117 if (modifiers->isAbstract()) {
8118 attributes = attributes | AttrAbstract;
8120 if (modifiers->isPrivate()) {
8121 attributes = attributes | AttrPrivate;
8122 } else {
8123 attributes = attributes | (modifiers->isProtected()
8124 ? AttrProtected : AttrPublic);
8127 parseUserAttributes(fe, attributes);
8128 if (!(attributes & (AttrParamCoerceModeFalse | AttrParamCoerceModeNull))) {
8129 attributes = attributes | AttrParamCoerceModeNull;
8132 fe->setLocation(meth->line0(), meth->line1());
8133 fe->docComment = makeStaticString(
8134 Option::GenerateDocComments ? meth->getDocComment().c_str() : ""
8136 auto retType = meth->retTypeAnnotation();
8137 assert(retType ||
8138 meth->isNamed("__construct") ||
8139 meth->isNamed("__destruct"));
8140 fe->returnType = retType ? retType->dataType() : KindOfNull;
8141 fe->retUserType = makeStaticString(meth->getReturnTypeConstraint());
8143 FunctionScopePtr funcScope = meth->getFunctionScope();
8144 const char *funcname = funcScope->getScopeName().c_str();
8145 const char *classname = pce ? pce->name()->data() : nullptr;
8146 auto const& info = Native::GetBuiltinFunction(funcname, classname,
8147 modifiers->isStatic());
8149 if (!classname && (
8150 !strcasecmp(funcname, "fb_call_user_func_safe") ||
8151 !strcasecmp(funcname, "fb_call_user_func_safe_return") ||
8152 !strcasecmp(funcname, "fb_call_user_func_array_safe"))) {
8153 // Legacy optimization functions
8154 funcScope->setOptFunction(hphp_opt_fb_call_user_func);
8157 int nativeAttrs = fe->parseNativeAttributes(attributes);
8158 BuiltinFunction bif = nullptr, nif = nullptr;
8159 Native::getFunctionPointers(info, nativeAttrs, bif, nif);
8160 if (nif && !(nativeAttrs & Native::AttrZendCompat)) {
8161 if (retType) {
8162 fe->retTypeConstraint =
8163 determine_type_constraint_from_annot(retType, true);
8164 } else {
8165 fe->retTypeConstraint = TypeConstraint {
8166 s_Void.get(),
8167 TypeConstraint::ExtendedHint | TypeConstraint::HHType
8172 Emitter e(meth, m_ue, *this);
8173 FuncFinisher ff(this, e, fe, 0);
8174 Label topOfBody(e, Label::NoEntryNopFlag{});
8176 Offset base = m_ue.bcPos();
8178 if (meth->getFunctionScope()->userAttributes().count(attr_Deprecated)) {
8179 emitDeprecationWarning(e, meth);
8182 fe->setBuiltinFunc(attributes, base);
8183 fillFuncEmitterParams(fe, meth->getParams(), true);
8185 if (dynCallWrapper) e.VarEnvDynCall();
8187 if (nativeAttrs & Native::AttrOpCodeImpl) {
8188 ff.setStackPad(emitNativeOpCodeImpl(meth, funcname, classname, fe));
8189 } else {
8190 e.NativeImpl();
8192 emitMethodDVInitializers(e, meth, topOfBody);
8193 return attributes;
8196 void EmitterVisitor::emitMethodMetadata(MethodStatementPtr meth,
8197 ClosureUseVarVec* useVars,
8198 bool top) {
8199 FuncEmitter* fe = m_curFunc;
8200 bindUserAttributes(meth, fe);
8202 // assign ids to parameters (all methods)
8203 int numParam = meth->getParams() ? meth->getParams()->getCount() : 0;
8204 for (int i = 0; i < numParam; i++) {
8205 auto par =
8206 static_pointer_cast<ParameterExpression>((*meth->getParams())[i]);
8207 fe->allocVarId(makeStaticString(par->getName()));
8210 // assign ids to 0Closure and use parameters (closures)
8211 if (fe->isClosureBody) {
8212 fe->allocVarId(makeStaticString("0Closure"));
8214 for (auto& useVar : *useVars) {
8215 fe->allocVarId(useVar.first);
8219 // assign id to 86metadata local representing frame metadata
8220 if (meth->mayCallSetFrameMetadata()) {
8221 fe->allocVarId(makeStaticString("86metadata"));
8224 // assign ids to local variables
8225 if (!fe->isMemoizeWrapper) {
8226 assignLocalVariableIds(meth->getFunctionScope());
8229 // add parameter info
8230 fillFuncEmitterParams(fe, meth->getParams(),
8231 meth->getFunctionScope()->isParamCoerceMode());
8233 // copy declared return type (hack)
8234 fe->retUserType = makeStaticString(meth->getReturnTypeConstraint());
8236 auto annot = meth->retTypeAnnotation();
8237 // For a non-generator async function with a return annotation of the form
8238 // "Awaitable<T>", we set m_retTypeConstraint to T. For all other async
8239 // functions, we leave m_retTypeConstraint empty.
8240 if (annot && fe->isAsync && !fe->isGenerator) {
8241 // Semantic checks ensure that the return annotation is "Awaitable" or
8242 // "WaitHandle" and that it has at most one type parameter
8243 assert(annot->isAwaitable() || annot->isWaitHandle());
8244 assert(annot->numTypeArgs() <= 1);
8245 bool isSoft = annot->isSoft();
8246 // If annot was "Awaitable" with no type args, getTypeArg() will return an
8247 // empty annotation
8248 annot = annot->getTypeArg(0);
8249 // If the original annotation was soft, make sure we preserve the softness
8250 if (annot && isSoft) annot->setSoft();
8252 // Ideally we should handle the void case in TypeConstraint::check. This
8253 // should however get done in a different diff, since it could impact
8254 // perf in a negative way (#3145038)
8255 if (annot && !annot->isVoid() && !annot->isThis()) {
8256 fe->retTypeConstraint = determine_type_constraint_from_annot(annot, true);
8259 // add the original filename for flattened traits
8260 auto const originalFilename = meth->getOriginalFilename();
8261 if (!originalFilename.empty()) {
8262 fe->originalFilename = makeStaticString(originalFilename);
8265 StringData* methDoc = Option::GenerateDocComments ?
8266 makeStaticString(meth->getDocComment()) : staticEmptyString();
8268 fe->init(meth->line0(),
8269 meth->line1(),
8270 m_ue.bcPos(),
8271 buildMethodAttrs(meth, fe, top),
8272 top,
8273 methDoc);
8275 if (meth->getFunctionScope()->needsFinallyLocals()) {
8276 assignFinallyVariableIds();
8280 void EmitterVisitor::fillFuncEmitterParams(FuncEmitter* fe,
8281 ExpressionListPtr params,
8282 bool coerce_params /*= false */) {
8283 int numParam = params ? params->getCount() : 0;
8284 for (int i = 0; i < numParam; i++) {
8285 auto par = static_pointer_cast<ParameterExpression>((*params)[i]);
8286 StringData* parName = makeStaticString(par->getName());
8288 FuncEmitter::ParamInfo pi;
8289 auto const typeConstraint = determine_type_constraint(par);
8290 if (typeConstraint.hasConstraint()) {
8291 pi.typeConstraint = typeConstraint;
8293 if (coerce_params) {
8294 if (auto const typeAnnotation = par->annotation()) {
8295 pi.builtinType = typeAnnotation->dataType();
8299 if (par->hasUserType()) {
8300 pi.userType = makeStaticString(par->getUserTypeHint());
8303 // Store info about the default value if there is one.
8304 if (par->isOptional()) {
8305 const StringData* phpCode;
8306 ExpressionPtr vNode = par->defaultValue();
8307 if (vNode->isScalar()) {
8308 TypedValue dv;
8309 initScalar(dv, vNode);
8310 pi.defaultValue = dv;
8312 std::string orig = vNode->getComment();
8313 if (orig.empty()) {
8314 // Simple case: it's a scalar value so we just serialize it
8315 VariableSerializer vs(VariableSerializer::Type::PHPOutput);
8316 String result = vs.serialize(tvAsCVarRef(&dv), true);
8317 phpCode = makeStaticString(result.get());
8318 } else {
8319 // This was optimized from a Constant, or ClassConstant
8320 // use the original string
8321 phpCode = makeStaticString(orig);
8323 } else {
8324 // Non-scalar, so we have to output PHP from the AST node
8325 std::ostringstream os;
8326 CodeGenerator cg(&os, CodeGenerator::PickledPHP);
8327 auto ar = std::make_shared<AnalysisResult>();
8328 vNode->outputPHP(cg, ar);
8329 phpCode = makeStaticString(os.str());
8331 pi.phpCode = phpCode;
8334 auto paramUserAttrs =
8335 dynamic_pointer_cast<ExpressionList>(par->userAttributeList());
8336 if (paramUserAttrs) {
8337 for (int j = 0; j < paramUserAttrs->getCount(); ++j) {
8338 auto a = dynamic_pointer_cast<UserAttribute>((*paramUserAttrs)[j]);
8339 StringData* uaName = makeStaticString(a->getName());
8340 ExpressionPtr uaValue = a->getExp();
8341 assert(uaValue);
8342 assert(uaValue->isScalar());
8343 TypedValue tv;
8344 initScalar(tv, uaValue);
8345 pi.userAttributes[uaName] = tv;
8349 pi.byRef = par->isRef();
8350 pi.variadic = par->isVariadic();
8351 fe->appendParam(parName, pi);
8355 void EmitterVisitor::emitMethodPrologue(Emitter& e, MethodStatementPtr meth) {
8356 FunctionScopePtr funcScope = meth->getFunctionScope();
8358 if (!m_curFunc->isMemoizeWrapper &&
8359 funcScope->needsLocalThis() && !funcScope->isStatic()) {
8360 assert(!m_curFunc->top);
8361 static const StringData* thisStr = makeStaticString("this");
8362 Id thisId = m_curFunc->lookupVarId(thisStr);
8363 emitVirtualLocal(thisId);
8364 e.InitThisLoc(thisId);
8367 if (!m_curFunc->isMemoizeImpl) {
8368 for (uint32_t i = 0; i < m_curFunc->params.size(); i++) {
8369 const TypeConstraint& tc = m_curFunc->params[i].typeConstraint;
8370 if (!tc.hasConstraint()) continue;
8371 emitVirtualLocal(i);
8372 e.VerifyParamType(i);
8376 if (funcScope->isAbstract()) {
8377 std::ostringstream s;
8378 s << "Cannot call abstract method " << meth->getOriginalFullName() << "()";
8379 emitMakeUnitFatal(e, s.str().c_str(), FatalOp::RuntimeOmitFrame);
8383 void EmitterVisitor::emitDeprecationWarning(Emitter& e,
8384 MethodStatementPtr meth) {
8385 auto funcScope = meth->getFunctionScope();
8387 auto userAttributes DEBUG_ONLY = funcScope->userAttributes();
8388 assert(userAttributes.find(attr_Deprecated) != userAttributes.end());
8390 // Include the message from <<__Deprecated('<message>')>> in the warning
8391 auto deprArgs = funcScope->getUserAttributeParams(attr_Deprecated);
8392 auto deprMessage = deprArgs.empty()
8393 ? s_is_deprecated.data()
8394 : deprArgs.front()->getString();
8396 // how often to display the warning (1 / rate)
8397 auto rate = deprArgs.size() > 1 ? deprArgs[1]->getLiteralInteger() : 1;
8398 if (rate <= 0) {
8399 // deprecation warnings disabled
8400 return;
8403 { // preface the message with the name of the offending function
8404 auto funcName = funcScope->getScopeName();
8405 BlockScopeRawPtr b = funcScope->getOuterScope();
8406 if (b && b->is(BlockScope::ClassScope)) {
8407 auto clsScope = dynamic_pointer_cast<ClassScope>(b);
8408 if (clsScope->isTrait()) {
8409 e.Self();
8410 e.NameA();
8411 e.String(makeStaticString("::" + funcName + ": " + deprMessage));
8412 e.Concat();
8413 } else {
8414 e.String(makeStaticString(
8415 clsScope->getScopeName() + "::" + funcName
8416 + ": " + deprMessage));
8418 } else {
8419 e.String(makeStaticString(funcName + ": " + deprMessage));
8423 e.Int(rate);
8424 e.Int((funcScope->isSystem() || funcScope->isNative())
8425 ? k_E_DEPRECATED : k_E_USER_DEPRECATED);
8426 e.FCallBuiltin(3, 3, s_trigger_sampled_error.get());
8427 emitPop(e);
8430 void EmitterVisitor::emitMethod(MethodStatementPtr meth) {
8431 auto region = createRegion(meth, Region::Kind::FuncBody);
8432 enterRegion(region);
8433 SCOPE_EXIT { leaveRegion(region); };
8435 Emitter e(meth, m_ue, *this);
8436 FuncFinisher ff(this, e, m_curFunc);
8437 Label topOfBody(e, Label::NoEntryNopFlag{});
8438 emitMethodPrologue(e, meth);
8440 if (meth->getFunctionScope()->userAttributes().count(attr_Deprecated)) {
8441 emitDeprecationWarning(e, meth);
8444 // emit code to create generator object
8445 if (m_curFunc->isGenerator) {
8446 e.CreateCont();
8447 e.PopC();
8450 // emit method body
8451 visit(meth->getStmts());
8452 assert(m_evalStack.size() == 0);
8454 // if the current position is reachable, emit code to return null
8455 if (currentPositionIsReachable()) {
8456 auto r = meth->getRange();
8457 r.line0 = r.line1;
8458 r.char0 = r.char1 - 1;
8459 e.setTempLocation(r);
8460 e.Null();
8461 if (shouldEmitVerifyRetType()) {
8462 e.VerifyRetTypeC();
8464 e.RetC();
8465 e.setTempLocation(OptLocation());
8468 if (!m_curFunc->isMemoizeImpl) {
8469 emitMethodDVInitializers(e, meth, topOfBody);
8473 void EmitterVisitor::emitMethodDVInitializers(Emitter& e,
8474 MethodStatementPtr& meth,
8475 Label& topOfBody) {
8476 bool hasOptional = false;
8477 ExpressionListPtr params = meth->getParams();
8478 int numParam = params ? params->getCount() : 0;
8479 for (int i = 0; i < numParam; i++) {
8480 auto par = static_pointer_cast<ParameterExpression>((*params)[i]);
8481 if (par->isOptional()) {
8482 hasOptional = true;
8483 Label entryPoint(e);
8484 emitVirtualLocal(i);
8485 visit(par->defaultValue());
8486 emitCGet(e);
8487 emitSet(e);
8488 e.PopC();
8489 m_curFunc->params[i].funcletOff = entryPoint.getAbsoluteOffset();
8492 if (hasOptional) e.JmpNS(topOfBody);
8495 void EmitterVisitor::addMemoizeProp(MethodStatementPtr meth) {
8496 assert(m_curFunc->isMemoizeWrapper);
8498 if (meth->is(Statement::KindOfFunctionStatement)) {
8499 // Functions use statics within themselves. So all we need to do here is
8500 // set the name
8501 m_curFunc->memoizePropName = makeStaticString("static$memoize_cache");
8502 return;
8505 auto pce = m_curFunc->pce();
8506 auto classScope = meth->getClassScope();
8507 auto funcScope = meth->getFunctionScope();
8508 bool useSharedProp = !funcScope->isStatic();
8510 std::string propNameBase;
8511 if (useSharedProp) {
8512 propNameBase = "$shared";
8513 m_curFunc->hasMemoizeSharedProp = true;
8514 m_curFunc->memoizeSharedPropIndex = pce->getNextMemoizeCacheKey();
8515 } else {
8516 propNameBase = toLower(funcScope->getScopeName());
8519 // The prop definition in traits conflicts with the definition in a class
8520 // so make a different prop for each trait
8521 std::string traitNamePart;
8522 if (classScope && classScope->isTrait()) {
8523 traitNamePart = toLower(classScope->getScopeName());
8524 // the backslash comes from namespaces. @jan thought that would cause
8525 // issues, so use $ instead
8526 for (char &c: traitNamePart) {
8527 c = (c == '\\' ? '$' : c);
8529 traitNamePart += "$";
8532 m_curFunc->memoizePropName = makeStaticString(
8533 folly::sformat("{}${}memoize_cache", propNameBase, traitNamePart));
8535 TypedValue tvProp;
8536 if (useSharedProp ||
8537 (meth->getParams() && meth->getParams()->getCount() > 0)) {
8538 tvProp = make_tv<KindOfPersistentArray>(staticEmptyArray());
8539 } else {
8540 tvWriteNull(&tvProp);
8543 Attr attrs = AttrPrivate | AttrNoSerialize;
8544 attrs = attrs | (funcScope->isStatic() ? AttrStatic : AttrNone);
8545 pce->addProperty(m_curFunc->memoizePropName, attrs, nullptr, nullptr, &tvProp,
8546 RepoAuthType{});
8549 void EmitterVisitor::emitMemoizeProp(Emitter& e,
8550 MethodStatementPtr meth,
8551 Id localID,
8552 const std::vector<Id>& paramIDs,
8553 uint32_t numParams) {
8554 assert(m_curFunc->isMemoizeWrapper);
8556 if (meth->is(Statement::KindOfFunctionStatement)) {
8557 emitVirtualLocal(localID);
8558 } else if (meth->getFunctionScope()->isStatic()) {
8559 m_evalStack.push(StackSym::K);
8560 m_evalStack.setClsBaseType(SymbolicStack::CLS_SELF);
8561 e.String(m_curFunc->memoizePropName);
8562 markSProp(e);
8563 } else {
8564 m_evalStack.push(StackSym::H);
8565 m_evalStack.setKnownCls(m_curFunc->pce()->name(), false);
8566 m_evalStack.push(StackSym::T);
8567 m_evalStack.setString(m_curFunc->memoizePropName);
8568 markProp(e, PropAccessType::Normal);
8571 assert(numParams <= paramIDs.size());
8572 for (uint32_t i = 0; i < numParams; i++) {
8573 if (i == 0 && m_curFunc->hasMemoizeSharedProp) {
8574 e.Int(m_curFunc->memoizeSharedPropIndex);
8575 } else {
8576 emitVirtualLocal(paramIDs[i]);
8578 markElem(e);
8582 void EmitterVisitor::emitMemoizeMethod(MethodStatementPtr meth,
8583 const StringData* methName) {
8584 assert(m_curFunc->isMemoizeWrapper);
8586 if (meth->getFunctionScope()->isRefReturn()) {
8587 throw IncludeTimeFatalException(meth,
8588 "<<__Memoize>> cannot be used on functions that return by reference");
8590 if (meth->getFunctionScope()->allowsVariableArguments()) {
8591 throw IncludeTimeFatalException(meth,
8592 "<<__Memoize>> cannot be used on functions with variable arguments");
8595 auto classScope = meth->getClassScope();
8596 if (classScope && classScope->isInterface()) {
8597 throw IncludeTimeFatalException(meth,
8598 "<<__Memoize>> cannot be used in interfaces");
8601 bool isFunc = meth->is(Statement::KindOfFunctionStatement);
8602 int numParams = m_curFunc->params.size();
8603 std::vector<Id> cacheLookup;
8605 auto region = createRegion(meth, Region::Kind::FuncBody);
8606 enterRegion(region);
8607 SCOPE_EXIT { leaveRegion(region); };
8609 Emitter e(meth, m_ue, *this);
8610 FuncFinisher ff(this, e, m_curFunc);
8612 Label topOfBody(e);
8613 Label cacheMiss;
8615 emitMethodPrologue(e, meth);
8617 // Function start
8618 int staticLocalID = 0;
8619 if (isFunc) {
8620 // static ${propName} = {numParams > 0 ? array() : null};
8621 staticLocalID = m_curFunc->allocUnnamedLocal();
8622 emitVirtualLocal(staticLocalID);
8623 if (numParams == 0) {
8624 e.Null();
8625 } else {
8626 e.Array(staticEmptyArray());
8628 e.StaticLocInit(staticLocalID, m_curFunc->memoizePropName);
8629 } else if (!meth->getFunctionScope()->isStatic()) {
8630 e.CheckThis();
8633 if (m_curFunc->hasMemoizeSharedProp) {
8634 // The code below depends on cacheLookup having the right number of elements
8635 // Push a dummy value even though we'll use the cacheID as an int instead
8636 // instead of emitting a local
8637 cacheLookup.push_back(0);
8640 if (numParams == 0 && cacheLookup.size() == 0) {
8641 // if (${propName} !== null)
8642 emitMemoizeProp(e, meth, staticLocalID, cacheLookup, 0);
8643 emitIsType(e, IsTypeOp::Null);
8644 e.JmpNZ(cacheMiss);
8645 } else {
8646 // Serialize all the params into something we can use for the key
8647 for (int i = 0; i < numParams; i++) {
8648 if (m_curFunc->params[i].byRef) {
8649 throw IncludeTimeFatalException(meth,
8650 "<<__Memoize>> cannot be used on functions with args passed by "
8651 "reference");
8654 // Translate the arg to a memoize key
8655 int serResultLocal = m_curFunc->allocUnnamedLocal();
8656 cacheLookup.push_back(serResultLocal);
8658 emitVirtualLocal(serResultLocal);
8659 emitVirtualLocal(i);
8660 emitCGet(e);
8661 e.GetMemoKey();
8662 emitSet(e);
8663 emitPop(e);
8666 // isset returns false for null values. Given that:
8667 // - If we know that we can't return null, we can do a single isset check
8668 // - If we could return null, but there's only one arg, we can do a single
8669 // array_key_exists() check
8670 // - Otherwise we need an isset check to make sure we can dereference the
8671 // first N - 1 args, and then an array_key_exists() check
8672 int cacheLookupLen = cacheLookup.size();
8673 bool noRetNull =
8674 meth->getFunctionScope()->isAsync() ||
8675 (m_curFunc->retTypeConstraint.hasConstraint() &&
8676 !m_curFunc->retTypeConstraint.isSoft() &&
8677 !m_curFunc->retTypeConstraint.isNullable());
8679 if (cacheLookupLen > 1 || noRetNull) {
8680 // if (isset(${propName}[$param1]...[noRetNull ? $paramN : $paramN-1]))
8681 emitMemoizeProp(e, meth, staticLocalID, cacheLookup,
8682 noRetNull ? cacheLookupLen : cacheLookupLen - 1);
8683 emitIsset(e);
8684 e.JmpZ(cacheMiss);
8687 if (!noRetNull) {
8688 // if (array_key_exists($paramN, ${propName}[$param1][...][$paramN-1]))
8689 if (cacheLookupLen == 1 && m_curFunc->hasMemoizeSharedProp) {
8690 e.Int(m_curFunc->memoizeSharedPropIndex);
8691 } else {
8692 emitVirtualLocal(cacheLookup[cacheLookupLen - 1]);
8693 emitCGet(e);
8695 emitMemoizeProp(e, meth, staticLocalID, cacheLookup, cacheLookupLen - 1);
8696 emitCGet(e);
8697 e.AKExists();
8698 e.JmpZ(cacheMiss);
8702 // return $<propName>[$param1][...][$paramN]
8703 int cacheLookupLen = cacheLookup.size();
8704 emitMemoizeProp(e, meth, staticLocalID, cacheLookup, cacheLookupLen);
8705 emitCGet(e);
8706 e.RetC();
8708 // Otherwise, call the memoized func, store the result, and return it
8709 cacheMiss.set(e);
8710 emitMemoizeProp(e, meth, staticLocalID, cacheLookup, cacheLookupLen);
8711 auto fpiStart = m_ue.bcPos();
8712 if (isFunc) {
8713 e.FPushFuncD(numParams, methName);
8714 } else if (meth->getFunctionScope()->isStatic()) {
8715 emitClsIfSPropBase(e);
8717 if (classScope && classScope->isTrait()) {
8718 e.String(methName);
8719 e.Self();
8720 fpiStart = m_ue.bcPos();
8721 e.FPushClsMethodF(numParams);
8722 } else {
8723 fpiStart = m_ue.bcPos();
8724 e.FPushClsMethodD(numParams, methName, m_curFunc->pce()->name());
8726 } else {
8727 e.This();
8728 fpiStart = m_ue.bcPos();
8729 e.FPushObjMethodD(numParams, methName, ObjMethodOp::NullThrows);
8732 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
8733 for (uint32_t i = 0; i < numParams; i++) {
8734 emitVirtualLocal(i);
8735 emitFPass(e, i, PassByRefKind::ErrorOnCell);
8738 e.FCall(numParams);
8739 emitConvertToCell(e);
8741 emitSet(e);
8742 e.RetC();
8744 assert(m_evalStack.size() == 0);
8746 emitMethodDVInitializers(e, meth, topOfBody);
8749 void EmitterVisitor::emitPostponedCtors() {
8750 while (!m_postponedCtors.empty()) {
8751 PostponedCtor& p = m_postponedCtors.front();
8753 Attr attrs = AttrPublic;
8754 if (!SystemLib::s_inited || p.m_is->getClassScope()->isSystem()) {
8755 attrs = attrs | AttrBuiltin;
8757 StringData* methDoc = staticEmptyString();
8758 p.m_fe->init(p.m_is->line0(), p.m_is->line1(),
8759 m_ue.bcPos(), attrs, false, methDoc);
8760 Emitter e(p.m_is, m_ue, *this);
8761 FuncFinisher ff(this, e, p.m_fe);
8762 e.Null();
8763 e.RetC();
8765 m_postponedCtors.pop_front();
8769 void EmitterVisitor::emitPostponedPSinit(PostponedNonScalars& p, bool pinit) {
8770 Attr attrs = (Attr)(AttrPrivate | AttrStatic);
8771 if (!SystemLib::s_inited || p.m_is->getClassScope()->isSystem()) {
8772 attrs = attrs | AttrBuiltin;
8774 StringData* methDoc = staticEmptyString();
8775 p.m_fe->init(p.m_is->line0(), p.m_is->line1(),
8776 m_ue.bcPos(), attrs, false, methDoc);
8778 Emitter e(p.m_is, m_ue, *this);
8779 FuncFinisher ff(this, e, p.m_fe);
8781 // Private instance and static properties are initialized using
8782 // InitProp.
8783 size_t nProps = p.m_vec->size();
8784 assert(nProps > 0);
8785 for (size_t i = 0; i < nProps; ++i) {
8786 const StringData* propName =
8787 makeStaticString(((*p.m_vec)[i]).first);
8789 Label isset;
8790 InitPropOp op = InitPropOp::NonStatic;
8791 const PreClassEmitter::Prop& preProp =
8792 p.m_fe->pce()->lookupProp(propName);
8793 if ((preProp.attrs() & AttrStatic) == AttrStatic) {
8794 op = InitPropOp::Static;
8795 } else if ((preProp.attrs() & (AttrPrivate|AttrStatic)) != AttrPrivate) {
8796 e.CheckProp(const_cast<StringData*>(propName));
8797 e.JmpNZ(isset);
8799 visit((*p.m_vec)[i].second);
8800 e.InitProp(const_cast<StringData*>(propName), op);
8801 isset.set(e);
8803 e.Null();
8804 e.RetC();
8807 void EmitterVisitor::emitPostponedPinits() {
8808 while (!m_postponedPinits.empty()) {
8809 PostponedNonScalars& p = m_postponedPinits.front();
8810 emitPostponedPSinit(p, true);
8811 p.release(); // Manually trigger memory cleanup.
8812 m_postponedPinits.pop_front();
8816 void EmitterVisitor::emitPostponedSinits() {
8817 while (!m_postponedSinits.empty()) {
8818 PostponedNonScalars& p = m_postponedSinits.front();
8819 emitPostponedPSinit(p, false);
8820 p.release(); // Manually trigger memory cleanup.
8821 m_postponedSinits.pop_front();
8825 void EmitterVisitor::emitPostponedCinits() {
8826 while (!m_postponedCinits.empty()) {
8827 PostponedNonScalars& p = m_postponedCinits.front();
8829 Attr attrs = (Attr)(AttrPrivate | AttrStatic);
8830 if (!SystemLib::s_inited || p.m_is->getClassScope()->isSystem()) {
8831 attrs = attrs | AttrBuiltin;
8833 StringData* methDoc = staticEmptyString();
8834 p.m_fe->init(p.m_is->line0(), p.m_is->line1(),
8835 m_ue.bcPos(), attrs, false, methDoc);
8836 static const StringData* s_constName = makeStaticString("constName");
8837 p.m_fe->appendParam(s_constName, FuncEmitter::ParamInfo());
8839 Emitter e(p.m_is, m_ue, *this);
8840 FuncFinisher ff(this, e, p.m_fe);
8842 // Generate HHBC of the structure:
8844 // private static function 86cinit(constName) {
8845 // if (constName == "FOO") {
8846 // return <expr for FOO>;
8847 // } else if (constName == "BAR") {
8848 // return <expr for BAR>;
8849 // } else { # (constName == "BAZ")
8850 // return <expr for BAZ>;
8851 // }
8852 // }
8853 size_t nConsts = p.m_vec->size();
8854 assert(nConsts > 0);
8855 Label retC;
8856 for (size_t i = 0; i < nConsts - 1; ++i) {
8857 Label mismatch;
8859 emitVirtualLocal(0);
8860 emitCGet(e);
8861 e.String((StringData*)makeStaticString(((*p.m_vec)[i]).first));
8862 e.Eq();
8863 e.JmpZ(mismatch);
8865 visit((*p.m_vec)[i].second);
8867 e.Jmp(retC);
8868 mismatch.set(e);
8870 visit((*p.m_vec)[nConsts-1].second);
8871 retC.set(e);
8872 e.RetC();
8874 p.release(); // Manually trigger memory cleanup.
8875 m_postponedCinits.pop_front();
8879 void EmitterVisitor::emitVirtualLocal(int localId) {
8880 prepareEvalStack();
8882 m_evalStack.push(StackSym::L);
8883 m_evalStack.setInt(localId);
8886 template<class Expr>
8887 void EmitterVisitor::emitVirtualClassBase(Emitter& e, Expr* node) {
8888 prepareEvalStack();
8890 m_evalStack.push(StackSym::K);
8891 auto const func = node->getFunctionScope();
8893 if (node->isStatic()) {
8894 m_evalStack.setClsBaseType(SymbolicStack::CLS_LATE_BOUND);
8895 } else if (node->getClass()) {
8896 const ExpressionPtr& expr = node->getClass();
8897 if (isNormalLocalVariable(expr)) {
8898 SimpleVariable* sv = static_cast<SimpleVariable*>(expr.get());
8899 StringData* name = makeStaticString(sv->getName());
8900 Id locId = m_curFunc->lookupVarId(name);
8901 m_evalStack.setClsBaseType(SymbolicStack::CLS_NAMED_LOCAL);
8902 m_evalStack.setInt(locId);
8903 } else {
8905 * More complex expressions get stashed into an unnamed local so
8906 * we can evaluate them at the proper time.
8908 * See emitResolveClsBase() for examples.
8910 int unnamedLoc = m_curFunc->allocUnnamedLocal();
8911 int clsBaseIdx = m_evalStack.size() - 1;
8912 m_evalStack.setClsBaseType(SymbolicStack::CLS_UNNAMED_LOCAL);
8913 emitVirtualLocal(unnamedLoc);
8914 visit(node->getClass());
8915 emitConvertToCell(e);
8916 emitSet(e);
8917 m_evalStack.setUnnamedLocal(clsBaseIdx, unnamedLoc, m_ue.bcPos());
8918 emitPop(e);
8920 } else if (!node->getClassScope() ||
8921 node->getClassScope()->isTrait() ||
8922 (func && func->isClosure())) {
8923 // In a trait, a potentially rebound closure or psuedo-main, we can't
8924 // resolve self:: or parent:: yet, so we emit special instructions that do
8925 // those lookups.
8926 if (node->isParent()) {
8927 m_evalStack.setClsBaseType(SymbolicStack::CLS_PARENT);
8928 } else if (node->isSelf()) {
8929 m_evalStack.setClsBaseType(SymbolicStack::CLS_SELF);
8930 } else {
8931 m_evalStack.setClsBaseType(SymbolicStack::CLS_STRING_NAME);
8932 m_evalStack.setString(
8933 makeStaticString(node->getOriginalClassName()));
8935 } else if (node->isParent() &&
8936 node->getClassScope()->getOriginalParent().empty()) {
8937 // parent:: in a class without a parent. We'll emit a Parent
8938 // opcode because it can handle this error case.
8939 m_evalStack.setClsBaseType(SymbolicStack::CLS_PARENT);
8940 } else {
8941 m_evalStack.setClsBaseType(SymbolicStack::CLS_STRING_NAME);
8942 m_evalStack.setString(
8943 makeStaticString(node->getOriginalClassName()));
8947 bool EmitterVisitor::emitCallUserFunc(Emitter& e, SimpleFunctionCallPtr func) {
8948 static struct {
8949 const char* name;
8950 int minParams, maxParams;
8951 CallUserFuncFlags flags;
8952 } cufTab[] = {
8953 { "call_user_func", 1, INT_MAX, CallUserFuncPlain },
8954 { "call_user_func_array", 2, 2, CallUserFuncArray },
8955 { "forward_static_call", 1, INT_MAX, CallUserFuncForward },
8956 { "forward_static_call_array", 2, 2, CallUserFuncForwardArray },
8957 { "fb_call_user_func_safe", 1, INT_MAX, CallUserFuncSafe },
8958 { "fb_call_user_func_array_safe", 2, 2, CallUserFuncSafeArray },
8959 { "fb_call_user_func_safe_return", 2, INT_MAX, CallUserFuncSafeReturn },
8962 ExpressionListPtr params = func->getParams();
8963 if (!params) return false;
8964 int nParams = params->getCount();
8965 if (!nParams) return false;
8966 CallUserFuncFlags flags = CallUserFuncNone;
8967 for (unsigned i = 0; i < sizeof(cufTab) / sizeof(cufTab[0]); i++) {
8968 if (func->isCallToFunction(cufTab[i].name) &&
8969 nParams >= cufTab[i].minParams &&
8970 nParams <= cufTab[i].maxParams) {
8971 flags = cufTab[i].flags;
8972 break;
8975 if (flags == CallUserFuncNone) return false;
8976 if (func->hasUnpack()) {
8977 throw EmitterVisitor::IncludeTimeFatalException(
8978 func,
8979 "Using argument unpacking for a call_user_func is not supported"
8982 int param = 1;
8983 ExpressionPtr callable = (*params)[0];
8984 visit(callable);
8985 emitConvertToCell(e);
8986 Offset fpiStart = m_ue.bcPos();
8987 if (flags & CallUserFuncForward) {
8988 e.FPushCufF(nParams - param);
8989 } else if (flags & CallUserFuncSafe) {
8990 if (flags & CallUserFuncReturn) {
8991 assert(nParams >= 2);
8992 visit((*params)[param++]);
8993 emitConvertToCell(e);
8994 } else {
8995 e.Null();
8997 fpiStart = m_ue.bcPos();
8998 e.FPushCufSafe(nParams - param);
8999 } else {
9000 e.FPushCuf(nParams - param);
9004 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
9005 for (int i = param; i < nParams; i++) {
9006 visit((*params)[i]);
9007 emitConvertToCell(e);
9008 e.FPassC(i - param);
9012 if (flags & CallUserFuncArray) {
9013 e.FCallArray();
9014 } else {
9015 e.FCall(nParams - param);
9017 if (flags & CallUserFuncSafe) {
9018 if (flags & CallUserFuncReturn) {
9019 e.CufSafeReturn();
9020 } else {
9021 e.CufSafeArray();
9024 return true;
9027 Func* EmitterVisitor::canEmitBuiltinCall(const std::string& name,
9028 int numParams,
9029 AnalysisResultConstPtr ar,
9030 ExpressionListPtr& funcParams) {
9031 if (RuntimeOption::EvalJitEnableRenameFunction ||
9032 !RuntimeOption::EvalEnableCallBuiltin) {
9033 return nullptr;
9035 if (RuntimeOption::DynamicInvokeFunctions.count(name)) {
9036 return nullptr;
9038 Func* f = Unit::lookupFunc(makeStaticString(name));
9039 if (!f ||
9040 (f->attrs() & AttrNoFCallBuiltin) ||
9041 !f->nativeFuncPtr() ||
9042 f->isMethod() ||
9043 (f->numParams() > Native::maxFCallBuiltinArgs()) ||
9044 (f->userAttributes().count(
9045 LowStringPtr(s_attr_Deprecated.get())))) return nullptr;
9047 auto variadic = f->hasVariadicCaptureParam();
9049 // Only allowed to overrun the signature if we have somewhere to put it
9050 if ((numParams > f->numParams()) && !variadic) return nullptr;
9052 if ((f->returnType() == KindOfDouble) &&
9053 !Native::allowFCallBuiltinDoubles()) return nullptr;
9055 bool allowDoubleArgs = Native::allowFCallBuiltinDoubles();
9056 auto concrete_params = f->numParams();
9057 if (variadic) {
9058 assertx(concrete_params > 0);
9059 --concrete_params;
9062 auto params = [&]{
9063 auto funcScope = ar->findFunction(name);
9064 if (!funcScope) return ExpressionListPtr();
9065 auto stmt = funcScope->getStmt();
9066 if (!stmt) return ExpressionListPtr();
9067 auto methStmt = dynamic_pointer_cast<MethodStatement>(stmt);
9068 if (!methStmt) return ExpressionListPtr();
9069 return methStmt->getParams();
9070 }();
9072 for (int i = 0; i < concrete_params; i++) {
9073 if ((!allowDoubleArgs) &&
9074 (f->params()[i].builtinType == KindOfDouble)) {
9075 return nullptr;
9077 if (i >= numParams) {
9078 auto &pi = f->params()[i];
9079 if (pi.isVariadic()) continue;
9080 if (!pi.hasDefaultValue()) {
9081 return nullptr;
9083 if (pi.defaultValue.m_type == KindOfUninit) {
9084 if (!params || i >= params->getCount()) return nullptr;
9085 auto param = dynamic_pointer_cast<ParameterExpression>((*params)[i]);
9086 if (!param) return nullptr;
9087 auto value = param->defaultValue();
9088 if (!value || !value->isScalar()) return nullptr;
9089 Variant scalarValue;
9090 if (!value->getScalarValue(scalarValue) ||
9091 !scalarValue.isAllowedAsConstantValue()) return nullptr;
9096 funcParams = params;
9097 return f;
9100 void EmitterVisitor::emitFuncCall(Emitter& e, FunctionCallPtr node,
9101 const char* nameOverride,
9102 ExpressionListPtr paramsOverride) {
9103 ExpressionPtr nameExp = node->getNameExp();
9104 const std::string& nameStr = nameOverride ? nameOverride :
9105 node->getOriginalName();
9106 ExpressionListPtr params(paramsOverride ? paramsOverride :
9107 node->getParams());
9108 int numParams = params ? params->getCount() : 0;
9109 auto const unpack = node->hasUnpack();
9110 assert(!paramsOverride || !unpack);
9112 Func* fcallBuiltin = nullptr;
9113 ExpressionListPtr funcParams;
9114 StringData* nLiteral = nullptr;
9115 Offset fpiStart = 0;
9116 if (node->getClass() || node->hasStaticClass()) {
9117 bool isSelfOrParent = node->isSelf() || node->isParent();
9118 if (!node->isStatic() && !isSelfOrParent &&
9119 !node->getOriginalClassName().empty() && !nameStr.empty()) {
9120 // cls::foo()
9121 StringData* cLiteral =
9122 makeStaticString(node->getOriginalClassName());
9123 StringData* nLiteral = makeStaticString(nameStr);
9124 fpiStart = m_ue.bcPos();
9125 e.FPushClsMethodD(numParams, nLiteral, cLiteral);
9126 } else {
9127 emitVirtualClassBase(e, node.get());
9128 if (!nameStr.empty()) {
9129 // ...::foo()
9130 StringData* nLiteral = makeStaticString(nameStr);
9131 e.String(nLiteral);
9132 } else {
9133 // ...::$foo()
9134 visit(nameExp);
9135 emitConvertToCell(e);
9137 emitResolveClsBase(e, m_evalStack.size() - 2);
9138 fpiStart = m_ue.bcPos();
9139 if (isSelfOrParent) {
9140 // self and parent are "forwarding" calls, so we need to
9141 // use FPushClsMethodF instead
9142 e.FPushClsMethodF(numParams);
9143 } else {
9144 e.FPushClsMethod(numParams);
9147 } else if (!nameStr.empty()) {
9148 // foo()
9149 nLiteral = makeStaticString(nameStr);
9150 fcallBuiltin = canEmitBuiltinCall(
9151 nameStr,
9152 numParams,
9153 node->getFileScope()->getContainingProgram(),
9154 funcParams
9156 if (unpack &&
9157 fcallBuiltin &&
9158 (!fcallBuiltin->hasVariadicCaptureParam() ||
9159 numParams != fcallBuiltin->numParams())) {
9160 fcallBuiltin = nullptr;
9162 StringData* nsName = nullptr;
9163 if (!node->hadBackslash() && !nameOverride) {
9164 // nameOverride is to be used only when there's an exact function
9165 // to be called ... supporting a fallback doesn't make sense
9166 const std::string& nonNSName = node->getNonNSOriginalName();
9167 if (nonNSName != nameStr) {
9168 nsName = nLiteral;
9169 nLiteral = makeStaticString(nonNSName);
9170 fcallBuiltin = nullptr;
9174 if (!fcallBuiltin) {
9175 fpiStart = m_ue.bcPos();
9176 if (nsName == nullptr) {
9177 e.FPushFuncD(numParams, nLiteral);
9178 } else {
9179 assert(!nameOverride);
9180 e.FPushFuncU(numParams, nsName, nLiteral);
9183 } else {
9184 // $foo()
9185 visit(nameExp);
9186 emitConvertToCell(e);
9187 // FPushFunc consumes method name from stack
9188 fpiStart = m_ue.bcPos();
9189 e.FPushFunc(numParams);
9191 if (fcallBuiltin) {
9192 auto variadic = !unpack && fcallBuiltin->hasVariadicCaptureParam();
9193 assertx((numParams <= fcallBuiltin->numParams()) || variadic);
9195 auto concreteParams = fcallBuiltin->numParams();
9196 if (variadic) {
9197 assertx(concreteParams > 0);
9198 --concreteParams;
9201 int i = 0;
9202 for (; i < numParams; i++) {
9203 // for builtin calls, since we don't push the ActRec, we
9204 // must determine the reffiness statically
9205 bool byRef = fcallBuiltin->byRef(i);
9206 bool mustBeRef = fcallBuiltin->mustBeRef(i);
9207 if (!emitBuiltinCallArg(e, (*params)[i], i, byRef, mustBeRef)) {
9208 while (i--) emitPop(e);
9209 return;
9213 for (; i < concreteParams; i++) {
9214 auto &pi = fcallBuiltin->params()[i];
9215 assert(pi.hasDefaultValue());
9216 auto &def = pi.defaultValue;
9217 if (def.m_type == KindOfUninit) {
9218 assert(funcParams && i < funcParams->getCount());
9219 auto param =
9220 dynamic_pointer_cast<ParameterExpression>((*funcParams)[i]);
9221 assert(param &&
9222 param->defaultValue() &&
9223 param->defaultValue()->isScalar());
9224 Variant scalarValue;
9225 DEBUG_ONLY bool success =
9226 param->defaultValue()->getScalarValue(scalarValue);
9227 assert(success && scalarValue.isAllowedAsConstantValue());
9228 emitBuiltinDefaultArg(e, scalarValue, pi.builtinType, i);
9229 } else {
9230 emitBuiltinDefaultArg(e, tvAsVariant(const_cast<TypedValue*>(&def)),
9231 pi.builtinType, i);
9235 if (variadic) {
9236 if (numParams <= concreteParams) {
9237 e.Array(staticEmptyArray());
9238 } else {
9239 e.NewPackedArray(numParams - concreteParams);
9242 e.FCallBuiltin(fcallBuiltin->numParams(),
9243 std::min<int32_t>(numParams, fcallBuiltin->numParams()),
9244 nLiteral);
9245 } else {
9246 emitCall(e, node, params, fpiStart);
9248 if (fcallBuiltin) {
9249 fixReturnType(e, node, fcallBuiltin);
9253 bool EmitterVisitor::emitConstantFuncCall(Emitter& e,
9254 SimpleFunctionCallPtr call) {
9255 if (!Option::WholeProgram || Option::ConstantFunctions.empty()) return false;
9257 if (call->getClass()) {
9258 // The class expression was either non-scalar or static, neither of which
9259 // we want to optimize.
9260 return false;
9263 auto const name = call->getFullName();
9264 auto const it = Option::ConstantFunctions.find(name);
9265 if (it == Option::ConstantFunctions.end()) return false;
9267 VariableUnserializer uns{
9268 it->second.data(), it->second.size(), VariableUnserializer::Type::Serialize,
9269 false, empty_array()
9272 try {
9273 return emitScalarValue(e, uns.unserialize());
9274 } catch (const Exception& e) {
9275 throw IncludeTimeFatalException(call,
9276 "Bad ConstantValue for %s: '%s'",
9277 name.c_str(), it->second.c_str());
9281 void EmitterVisitor::emitClassTraitPrecRule(PreClassEmitter* pce,
9282 TraitPrecStatementPtr stmt) {
9283 StringData* traitName = makeStaticString(stmt->getTraitName());
9284 StringData* methodName = makeStaticString(stmt->getMethodName());
9286 PreClass::TraitPrecRule rule(traitName, methodName);
9288 hphp_string_iset otherTraitNames;
9289 stmt->getOtherTraitNames(otherTraitNames);
9290 for (auto const& name : otherTraitNames) {
9291 rule.addOtherTraitName(makeStaticString(name));
9294 pce->addTraitPrecRule(rule);
9297 void EmitterVisitor::emitClassTraitAliasRule(PreClassEmitter* pce,
9298 TraitAliasStatementPtr stmt) {
9299 StringData* traitName = makeStaticString(stmt->getTraitName());
9300 StringData* origMethName = makeStaticString(stmt->getMethodName());
9301 StringData* newMethName = makeStaticString(stmt->getNewMethodName());
9302 // If there are no modifiers, buildAttrs() defaults to AttrPublic.
9303 // Here we don't want that. Instead, set AttrNone so that the modifiers of the
9304 // original method are preserved.
9305 Attr attr = (stmt->getModifiers()->getCount() == 0 ? AttrNone :
9306 buildAttrs(stmt->getModifiers()));
9308 PreClass::TraitAliasRule rule(traitName, origMethName, newMethName, attr);
9310 pce->addTraitAliasRule(rule);
9313 void EmitterVisitor::emitClassUseTrait(PreClassEmitter* pce,
9314 UseTraitStatementPtr useStmt) {
9315 auto rules = useStmt->getStmts();
9316 for (int r = 0; r < rules->getCount(); r++) {
9317 auto rule = (*rules)[r];
9318 auto precStmt = dynamic_pointer_cast<TraitPrecStatement>(rule);
9319 if (precStmt) {
9320 emitClassTraitPrecRule(pce, precStmt);
9321 } else {
9322 auto aliasStmt = dynamic_pointer_cast<TraitAliasStatement>(rule);
9323 assert(aliasStmt);
9324 emitClassTraitAliasRule(pce, aliasStmt);
9329 Id EmitterVisitor::emitTypedef(Emitter& e, TypedefStatementPtr td) {
9330 auto const nullable = td->annot->isNullable();
9331 auto const annot = td->annot->stripNullable();
9332 auto const valueStr = annot.vanillaName();
9334 // We have to merge the strings as litstrs to ensure namedentity
9335 // creation.
9336 auto const name = makeStaticString(td->name);
9337 auto const value = makeStaticString(valueStr);
9338 m_ue.mergeLitstr(name);
9339 m_ue.mergeLitstr(value);
9341 AnnotType type;
9342 if (annot.isFunction() || annot.isMixed()) {
9343 type = AnnotType::Mixed;
9344 } else {
9345 auto const at = nameToAnnotType(value);
9346 type = at ? *at : AnnotType::Object;
9347 // Type aliases are always defined at top-level scope, so
9348 // they're not allowed to reference "self" or "parent" (and
9349 // "static" is already disallowed by the parser, so we don't
9350 // need to worry about it here).
9351 if (UNLIKELY(type == AnnotType::Self || type == AnnotType::Parent)) {
9352 throw IncludeTimeFatalException(
9354 "Cannot access %s when no class scope is active",
9355 type == AnnotType::Self ? "self" : "parent");
9359 UserAttributeMap userAttrs;
9360 ExpressionListPtr attrList = td->attrList;
9361 if (attrList) {
9362 for (int i = 0; i < attrList->getCount(); ++i) {
9363 auto attr = dynamic_pointer_cast<UserAttribute>((*attrList)[i]);
9364 auto const uaName = makeStaticString(attr->getName());
9365 auto uaValue = attr->getExp();
9366 assert(uaValue);
9367 assert(uaValue->isScalar());
9368 TypedValue tv;
9369 initScalar(tv, uaValue);
9370 userAttrs[uaName] = tv;
9374 TypeAlias record;
9375 record.typeStructure = Array(td->annot->getScalarArrayRep());
9376 record.name = name;
9377 record.value = value;
9378 record.type = type;
9379 record.nullable = nullable;
9380 record.userAttrs = userAttrs;
9381 record.attrs = !SystemLib::s_inited ? AttrPersistent : AttrNone;
9382 Id id = m_ue.addTypeAlias(record);
9383 e.DefTypeAlias(id);
9385 return id;
9388 Id EmitterVisitor::emitClass(Emitter& e,
9389 ClassScopePtr cNode,
9390 bool toplevel) {
9392 const StringData* fatal_msg = cNode->getFatalMessage();
9393 if (fatal_msg != nullptr) {
9394 e.String(fatal_msg);
9395 e.Fatal(FatalOp::Runtime);
9396 return -1;
9399 auto is = static_pointer_cast<InterfaceStatement>(cNode->getStmt());
9400 StringData* parentName = makeStaticString(cNode->getOriginalParent());
9401 StringData* classDoc = Option::GenerateDocComments ?
9402 makeStaticString(cNode->getDocComment()) : staticEmptyString();
9403 Attr attr = cNode->isInterface() ? AttrInterface :
9404 cNode->isTrait() ? AttrTrait :
9405 cNode->isAbstract() ? AttrAbstract :
9406 cNode->isEnum() ? (AttrEnum | AttrFinal) :
9407 AttrNone;
9408 if (cNode->isFinal()) {
9409 attr = attr | AttrFinal;
9411 if (Option::WholeProgram) {
9412 if (!cNode->isRedeclaring() &&
9413 cNode->derivesFromRedeclaring() == Derivation::Normal) {
9414 attr = attr | AttrUnique;
9415 if (!cNode->isVolatile()) {
9416 attr = attr | AttrPersistent;
9419 if (cNode->isSystem()) {
9420 assert(attr & AttrPersistent);
9421 attr = attr | AttrBuiltin;
9423 if (!cNode->getAttribute(ClassScope::NotFinal)) {
9424 attr = attr | AttrNoOverride;
9426 if (cNode->getUsedTraitNames().size()) {
9427 attr = attr | AttrNoExpandTrait;
9429 } else if (!SystemLib::s_inited) {
9430 // we're building systemlib. everything is unique
9431 attr = attr | AttrBuiltin | AttrUnique | AttrPersistent;
9434 const std::vector<std::string>& bases(cNode->getBases());
9435 int firstInterface = cNode->getOriginalParent().empty() ? 0 : 1;
9436 int nInterfaces = bases.size();
9437 PreClass::Hoistable hoistable = PreClass::NotHoistable;
9438 if (toplevel) {
9439 if (SystemLib::s_inited && !cNode->isSystem()) {
9440 if (nInterfaces > firstInterface
9441 || cNode->getUsedTraitNames().size()
9442 || cNode->getClassRequiredExtends().size()
9443 || cNode->getClassRequiredImplements().size()
9444 || cNode->isEnum()
9446 hoistable = PreClass::Mergeable;
9447 } else if (firstInterface &&
9448 !m_hoistables.count(cNode->getOriginalParent())) {
9449 hoistable = PreClass::MaybeHoistable;
9452 if (hoistable == PreClass::NotHoistable) {
9453 hoistable = attr & AttrUnique ?
9454 PreClass::AlwaysHoistable : PreClass::MaybeHoistable;
9455 m_hoistables.insert(cNode->getOriginalName());
9458 PreClassEmitter* pce = m_ue.newPreClassEmitter(cNode->getOriginalName(),
9459 hoistable);
9460 pce->init(is->line0(), is->line1(), m_ue.bcPos(), attr, parentName,
9461 classDoc);
9462 auto r = is->getRange();
9463 r.line1 = r.line0;
9464 r.char1 = r.char0;
9465 e.setTempLocation(r);
9466 if (hoistable != PreClass::AlwaysHoistable) {
9467 e.DefCls(pce->id());
9468 } else {
9469 // To attach the line number to for error reporting.
9470 e.DefClsNop(pce->id());
9472 e.setTempLocation(OptLocation());
9473 for (int i = firstInterface; i < nInterfaces; ++i) {
9474 pce->addInterface(makeStaticString(bases[i]));
9477 const std::vector<std::string>& usedTraits = cNode->getUsedTraitNames();
9478 for (size_t i = 0; i < usedTraits.size(); i++) {
9479 pce->addUsedTrait(makeStaticString(usedTraits[i]));
9481 pce->setNumDeclMethods(cNode->getNumDeclMethods());
9482 if (cNode->isTrait() || cNode->isInterface() || Option::WholeProgram) {
9483 for (auto& reqExtends : cNode->getClassRequiredExtends()) {
9484 pce->addClassRequirement(
9485 PreClass::ClassRequirement(makeStaticString(reqExtends), true));
9487 for (auto& reqImplements : cNode->getClassRequiredImplements()) {
9488 pce->addClassRequirement(
9489 PreClass::ClassRequirement(makeStaticString(reqImplements), false));
9492 auto const& userAttrs = cNode->userAttributes();
9493 for (auto it = userAttrs.begin(); it != userAttrs.end(); ++it) {
9494 const StringData* uaName = makeStaticString(it->first);
9495 ExpressionPtr uaValue = it->second;
9496 assert(uaValue);
9497 assert(uaValue->isScalar());
9498 TypedValue tv;
9499 initScalar(tv, uaValue);
9500 pce->addUserAttribute(uaName, tv);
9503 NonScalarVec* nonScalarPinitVec = nullptr;
9504 NonScalarVec* nonScalarSinitVec = nullptr;
9505 NonScalarVec* nonScalarConstVec = nullptr;
9506 if (StatementListPtr stmts = is->getStmts()) {
9507 int i, n = stmts->getCount();
9508 for (i = 0; i < n; i++) {
9509 if (auto meth = dynamic_pointer_cast<MethodStatement>((*stmts)[i])) {
9510 StringData* methName = makeStaticString(meth->getOriginalName());
9511 FuncEmitter* fe = m_ue.newMethodEmitter(methName, pce);
9512 bool added UNUSED = pce->addMethod(fe);
9513 assert(added);
9514 postponeMeth(meth, fe, false);
9515 } else if (auto cv = dynamic_pointer_cast<ClassVariable>((*stmts)[i])) {
9516 ModifierExpressionPtr mod(cv->getModifiers());
9517 ExpressionListPtr el(cv->getVarList());
9518 Attr declAttrs = buildAttrs(mod);
9519 StringData* typeConstraint = makeStaticString(
9520 cv->getTypeConstraint());
9521 int nVars = el->getCount();
9522 for (int ii = 0; ii < nVars; ii++) {
9523 ExpressionPtr exp((*el)[ii]);
9524 ExpressionPtr vNode;
9525 SimpleVariablePtr var;
9526 if (exp->is(Expression::KindOfAssignmentExpression)) {
9527 auto ae = static_pointer_cast<AssignmentExpression>(exp);
9528 var = static_pointer_cast<SimpleVariable>(ae->getVariable());
9529 vNode = ae->getValue();
9530 } else {
9531 var = static_pointer_cast<SimpleVariable>(exp);
9534 auto const propName = makeStaticString(var->getName());
9535 auto const propDoc = Option::GenerateDocComments ?
9536 makeStaticString(var->getDocComment()) : staticEmptyString();
9537 TypedValue tvVal;
9538 // Some properties may need to be marked with the AttrDeepInit
9539 // attribute, while other properties should not be marked with
9540 // this attrbiute. We copy declAttrs into propAttrs for each loop
9541 // iteration so that we can safely add AttrDeepInit to propAttrs
9542 // without mutating the original declAttrs.
9543 Attr propAttrs = declAttrs;
9544 if (vNode) {
9545 if (vNode->isScalar()) {
9546 initScalar(tvVal, vNode);
9547 } else {
9548 tvWriteUninit(&tvVal);
9549 if (!(declAttrs & AttrStatic)) {
9550 if (requiresDeepInit(vNode)) {
9551 propAttrs = propAttrs | AttrDeepInit;
9553 if (nonScalarPinitVec == nullptr) {
9554 nonScalarPinitVec = new NonScalarVec();
9556 nonScalarPinitVec->push_back(NonScalarPair(propName, vNode));
9557 } else {
9558 if (nonScalarSinitVec == nullptr) {
9559 nonScalarSinitVec = new NonScalarVec();
9561 nonScalarSinitVec->push_back(NonScalarPair(propName, vNode));
9564 } else {
9565 tvWriteNull(&tvVal);
9567 bool added UNUSED =
9568 pce->addProperty(propName, propAttrs, typeConstraint,
9569 propDoc, &tvVal, RepoAuthType{});
9570 assert(added);
9572 } else if (auto cc = dynamic_pointer_cast<ClassConstant>((*stmts)[i])) {
9574 ExpressionListPtr el(cc->getConList());
9575 StringData* typeConstraint =
9576 makeStaticString(cc->getTypeConstraint());
9577 int nCons = el->getCount();
9579 if (cc->isAbstract()) {
9580 for (int ii = 0; ii < nCons; ii++) {
9581 auto con = static_pointer_cast<ConstantExpression>((*el)[ii]);
9582 StringData* constName = makeStaticString(con->getName());
9583 bool added UNUSED =
9584 pce->addAbstractConstant(constName, typeConstraint,
9585 cc->isTypeconst());
9586 assert(added);
9588 } else {
9589 for (int ii = 0; ii < nCons; ii++) {
9590 auto ae = static_pointer_cast<AssignmentExpression>((*el)[ii]);
9591 auto con =
9592 static_pointer_cast<ConstantExpression>(ae->getVariable());
9593 auto vNode = ae->getValue();
9594 StringData* constName = makeStaticString(con->getName());
9595 assert(vNode);
9596 TypedValue tvVal;
9597 if (vNode->isArray()) {
9598 throw IncludeTimeFatalException(
9599 cc, "Arrays are not allowed in class constants");
9600 } else if (vNode->isCollection()) {
9601 throw IncludeTimeFatalException(
9602 cc, "Collections are not allowed in class constants");
9603 } else if (vNode->isScalar()) {
9604 initScalar(tvVal, vNode);
9605 } else {
9606 tvWriteUninit(&tvVal);
9607 if (nonScalarConstVec == nullptr) {
9608 nonScalarConstVec = new NonScalarVec();
9610 nonScalarConstVec->push_back(NonScalarPair(constName, vNode));
9612 // Store PHP source code for constant initializer.
9613 std::ostringstream os;
9614 CodeGenerator cg(&os, CodeGenerator::PickledPHP);
9615 auto ar = std::make_shared<AnalysisResult>();
9616 vNode->outputPHP(cg, ar);
9617 bool added UNUSED = pce->addConstant(
9618 constName, typeConstraint, &tvVal,
9619 makeStaticString(os.str()),
9620 cc->isTypeconst(),
9621 cc->getTypeStructure());
9622 assert(added);
9625 } else if (auto useStmt =
9626 dynamic_pointer_cast<UseTraitStatement>((*stmts)[i])) {
9627 emitClassUseTrait(pce, useStmt);
9632 if (!cNode->getAttribute(ClassScope::HasConstructor) &&
9633 !cNode->getAttribute(ClassScope::ClassNameConstructor)) {
9634 // cNode does not have a constructor; synthesize 86ctor() so that the class
9635 // will always have a method that can be called during construction.
9636 static const StringData* methName = makeStaticString("86ctor");
9637 FuncEmitter* fe = m_ue.newMethodEmitter(methName, pce);
9638 bool added UNUSED = pce->addMethod(fe);
9639 assert(added);
9640 postponeCtor(is, fe);
9643 if (nonScalarPinitVec != nullptr) {
9644 // Non-scalar property initializers require 86pinit() for run-time
9645 // initialization support.
9646 static const StringData* methName = makeStaticString("86pinit");
9647 FuncEmitter* fe = m_ue.newMethodEmitter(methName, pce);
9648 pce->addMethod(fe);
9649 postponePinit(is, fe, nonScalarPinitVec);
9652 if (nonScalarSinitVec != nullptr) {
9653 // Non-scalar property initializers require 86sinit() for run-time
9654 // initialization support.
9655 static const StringData* methName = makeStaticString("86sinit");
9656 FuncEmitter* fe = m_ue.newMethodEmitter(methName, pce);
9657 pce->addMethod(fe);
9658 postponeSinit(is, fe, nonScalarSinitVec);
9661 if (nonScalarConstVec != nullptr) {
9662 // Non-scalar constant initializers require 86cinit() for run-time
9663 // initialization support.
9664 static const StringData* methName = makeStaticString("86cinit");
9665 FuncEmitter* fe = m_ue.newMethodEmitter(methName, pce);
9666 assert(!(attr & AttrTrait));
9667 bool added UNUSED = pce->addMethod(fe);
9668 assert(added);
9669 postponeCinit(is, fe, nonScalarConstVec);
9672 // If this is an enum, get its type constraint.
9673 if (cNode->isEnum()) {
9674 auto cs = static_pointer_cast<ClassStatement>(is);
9675 auto const typeConstraint =
9676 determine_type_constraint_from_annot(cs->getEnumBaseTy(), true);
9677 pce->setEnumBaseTy(typeConstraint);
9680 return pce->id();
9683 namespace {
9685 struct ForeachIterGuard {
9686 ForeachIterGuard(EmitterVisitor& ev,
9687 Id iterId,
9688 IterKind kind)
9689 : m_ev(ev)
9691 m_ev.pushIterScope(iterId, kind);
9693 ~ForeachIterGuard() {
9694 m_ev.popIterScope();
9697 private:
9698 EmitterVisitor& m_ev;
9703 void EmitterVisitor::emitForeachListAssignment(Emitter& e,
9704 ListAssignmentPtr la,
9705 std::function<void()> emitSrc) {
9706 std::vector<IndexPair> indexPairs;
9707 IndexChain workingChain;
9708 listAssignmentVisitLHS(e, la, workingChain, indexPairs);
9710 if (indexPairs.size() == 0) {
9711 throw IncludeTimeFatalException(la, "Cannot use empty list");
9714 listAssignmentAssignElements(e, indexPairs, emitSrc);
9717 void EmitterVisitor::emitForeach(Emitter& e,
9718 ForEachStatementPtr fe) {
9719 auto region = createRegion(fe, Region::Kind::LoopOrSwitch);
9720 ExpressionPtr ae(fe->getArrayExp());
9721 ExpressionPtr val(fe->getValueExp());
9722 ExpressionPtr key(fe->getNameExp());
9723 StatementPtr body(fe->getBody());
9724 int keyTempLocal;
9725 int valTempLocal;
9726 bool strong = fe->isStrong();
9727 Label& exit = registerBreak(fe, region.get(), 1, false)->m_label;
9728 Label& next = registerContinue(fe, region.get(), 1, false)->m_label;
9729 Label start;
9730 Offset bIterStart;
9731 Id itId = m_curFunc->allocIterator();
9732 ForeachIterGuard fig(*this, itId, strong ? KindOfMIter : KindOfIter);
9733 bool simpleCase = (!key || isNormalLocalVariable(key)) &&
9734 isNormalLocalVariable(val);
9735 bool listKey = key ? key->is(Expression::KindOfListAssignment) : false;
9736 bool listVal = val->is(Expression::KindOfListAssignment);
9738 if (simpleCase) {
9739 auto svVal = static_pointer_cast<SimpleVariable>(val);
9740 StringData* name = makeStaticString(svVal->getName());
9741 valTempLocal = m_curFunc->lookupVarId(name);
9742 if (key) {
9743 auto svKey = static_pointer_cast<SimpleVariable>(key);
9744 name = makeStaticString(svKey->getName());
9745 keyTempLocal = m_curFunc->lookupVarId(name);
9746 visit(key);
9747 // Meta info on the key local will confuse the translator (and
9748 // wouldn't be useful anyway)
9749 m_evalStack.cleanTopMeta();
9750 } else {
9751 // Make gcc happy
9752 keyTempLocal = -1;
9754 visit(val);
9755 // Meta info on the value local will confuse the translator (and
9756 // wouldn't be useful anyway)
9757 m_evalStack.cleanTopMeta();
9758 visit(ae);
9759 if (strong) {
9760 emitConvertToVar(e);
9761 if (key) {
9762 e.MIterInitK(itId, exit, valTempLocal, keyTempLocal);
9763 } else {
9764 e.MIterInit(itId, exit, valTempLocal);
9766 } else {
9767 emitConvertToCell(e);
9768 if (key) {
9769 e.IterInitK(itId, exit, valTempLocal, keyTempLocal);
9770 } else {
9771 e.IterInit(itId, exit, valTempLocal);
9775 start.set(e);
9776 bIterStart = m_ue.bcPos();
9777 } else {
9778 keyTempLocal = key ? m_curFunc->allocUnnamedLocal() : -1;
9779 valTempLocal = m_curFunc->allocUnnamedLocal();
9780 if (key) {
9781 emitVirtualLocal(keyTempLocal);
9783 emitVirtualLocal(valTempLocal);
9785 visit(ae);
9786 if (strong) {
9787 emitConvertToVar(e);
9788 } else {
9789 emitConvertToCell(e);
9792 if (strong) {
9793 if (key) {
9794 e.MIterInitK(itId, exit, valTempLocal, keyTempLocal);
9795 } else {
9796 e.MIterInit(itId, exit, valTempLocal);
9798 } else {
9799 if (key) {
9800 e.IterInitK(itId, exit, valTempLocal, keyTempLocal);
9801 } else {
9802 e.IterInit(itId, exit, valTempLocal);
9806 // At this point, valTempLocal and keyTempLocal if applicable, contain the
9807 // key and value for the iterator.
9808 start.set(e);
9809 bIterStart = m_ue.bcPos();
9810 if (key && !listKey) {
9811 visit(key);
9812 emitClsIfSPropBase(e);
9814 if (listVal) {
9815 emitForeachListAssignment(
9817 ListAssignmentPtr(static_pointer_cast<ListAssignment>(val)),
9818 [&] { emitVirtualLocal(valTempLocal); }
9820 } else {
9821 visit(val);
9822 emitClsIfSPropBase(e);
9823 emitVirtualLocal(valTempLocal);
9824 if (strong) {
9825 emitVGet(e);
9826 emitBind(e);
9827 } else {
9828 emitCGet(e);
9829 emitSet(e);
9831 emitPop(e);
9833 emitUnsetL(e, valTempLocal);
9834 newFaultRegionAndFunclet(bIterStart, m_ue.bcPos(),
9835 new UnsetUnnamedLocalThunklet(valTempLocal));
9836 if (key) {
9837 assert(keyTempLocal != -1);
9838 if (listKey) {
9839 emitForeachListAssignment(
9841 ListAssignmentPtr(static_pointer_cast<ListAssignment>(key)),
9842 [&] { emitVirtualLocal(keyTempLocal); }
9844 } else {
9845 emitVirtualLocal(keyTempLocal);
9846 emitCGet(e);
9847 emitSet(e);
9848 emitPop(e);
9850 emitUnsetL(e, keyTempLocal);
9851 newFaultRegionAndFunclet(bIterStart, m_ue.bcPos(),
9852 new UnsetUnnamedLocalThunklet(keyTempLocal));
9857 region->m_iterId = itId;
9858 region->m_iterKind = strong ? KindOfMIter : KindOfIter;
9859 enterRegion(region);
9860 SCOPE_EXIT { leaveRegion(region); };
9861 if (body) visit(body);
9863 if (next.isUsed()) {
9864 next.set(e);
9866 if (key) {
9867 emitVirtualLocal(keyTempLocal);
9868 // Meta info on the key local will confuse the translator (and
9869 // wouldn't be useful anyway)
9870 m_evalStack.cleanTopMeta();
9872 emitVirtualLocal(valTempLocal);
9873 // Meta info on the value local will confuse the translator (and
9874 // wouldn't be useful anyway)
9875 m_evalStack.cleanTopMeta();
9876 if (strong) {
9877 if (key) {
9878 e.MIterNextK(itId, start, valTempLocal, keyTempLocal);
9879 } else {
9880 e.MIterNext(itId, start, valTempLocal);
9882 } else {
9883 if (key) {
9884 e.IterNextK(itId, start, valTempLocal, keyTempLocal);
9885 } else {
9886 e.IterNext(itId, start, valTempLocal);
9889 newFaultRegionAndFunclet(bIterStart, m_ue.bcPos(),
9890 new IterFreeThunklet(itId, strong),
9891 { itId, strong ? KindOfMIter : KindOfIter });
9892 if (!simpleCase) {
9893 m_curFunc->freeUnnamedLocal(valTempLocal);
9894 if (key) {
9895 m_curFunc->freeUnnamedLocal(keyTempLocal);
9898 exit.set(e);
9899 m_curFunc->freeIterator(itId);
9902 void EmitterVisitor::emitForeachAwaitAs(Emitter& e,
9903 ForEachStatementPtr fe) {
9904 assert(!fe->isStrong());
9905 auto region = createRegion(fe, Region::Kind::LoopOrSwitch);
9906 Label& exit = registerBreak(fe, region.get(), 1, false)->m_label;
9907 Label& next = registerContinue(fe, region.get(), 1, false)->m_label;
9909 // Evaluate the AsyncIterator object and store it into unnamed local.
9910 auto const iterTempLocal = m_curFunc->allocUnnamedLocal();
9911 emitVirtualLocal(iterTempLocal);
9912 visit(fe->getArrayExp());
9913 emitConvertToCell(e);
9914 emitSet(e);
9915 auto const iterTempStartUse = m_ue.bcPos();
9917 // Make sure it actually is an AsyncIterator.
9918 e.InstanceOfD(makeStaticString("HH\\AsyncIterator"));
9919 e.JmpNZ(next);
9920 e.String(makeStaticString(
9921 "Unable to iterate non-AsyncIterator asynchronously"));
9922 e.Fatal(FatalOp::Runtime);
9924 // Start of the next iteration.
9925 next.set(e);
9927 // Await the next value.
9928 emitVirtualLocal(iterTempLocal);
9929 emitCGet(e);
9930 emitConstMethodCallNoParams(e, "next");
9931 e.Await();
9932 auto const resultTempLocal = emitSetUnnamedL(e);
9934 // Did we finish yet?
9935 emitVirtualLocal(resultTempLocal);
9936 emitIsType(e, IsTypeOp::Null);
9937 e.JmpNZ(exit);
9939 auto const populate = [&](ExpressionPtr target, int index) {
9940 auto const emitSrc = [&] {
9941 emitVirtualLocal(resultTempLocal);
9942 m_evalStack.push(StackSym::I);
9943 m_evalStack.setInt(index);
9944 markElem(e);
9947 if (target->is(Expression::KindOfListAssignment)) {
9948 emitForeachListAssignment(
9950 ListAssignmentPtr(static_pointer_cast<ListAssignment>(target)),
9951 emitSrc
9953 } else {
9954 // Obtain target to be set.
9955 visit(target);
9957 // Put $result[index] on the stack.
9958 emitSrc();
9959 emitCGet(e);
9961 // Set target.
9962 emitSet(e);
9963 emitPop(e);
9967 auto const resultTempStartUse = m_ue.bcPos();
9969 // Set the key.
9970 if (fe->getNameExp()) {
9971 populate(fe->getNameExp(), 0);
9974 // Set the value.
9975 populate(fe->getValueExp(), 1);
9977 newFaultRegionAndFunclet(resultTempStartUse, m_ue.bcPos(),
9978 new UnsetUnnamedLocalThunklet(resultTempLocal));
9979 emitUnsetL(e, resultTempLocal);
9981 // Run body.
9983 enterRegion(region);
9984 SCOPE_EXIT { leaveRegion(region); };
9985 if (fe->getBody()) visit(fe->getBody());
9988 // Continue iteration.
9989 e.Jmp(next);
9991 // Exit cleanup.
9992 exit.set(e);
9994 emitUnsetL(e, resultTempLocal);
9995 m_curFunc->freeUnnamedLocal(resultTempLocal);
9997 newFaultRegionAndFunclet(iterTempStartUse, m_ue.bcPos(),
9998 new UnsetUnnamedLocalThunklet(iterTempLocal));
9999 emitUnsetL(e, iterTempLocal);
10000 m_curFunc->freeUnnamedLocal(iterTempLocal);
10003 void EmitterVisitor::emitYieldFrom(Emitter& e, ExpressionPtr exp) {
10004 Id itId = m_curFunc->allocIterator();
10006 // Set the delegate to the result of visiting our expression
10007 visit(exp);
10008 emitConvertToCell(e);
10009 e.ContAssignDelegate(itId);
10011 Offset bDelegateAssigned = m_ue.bcPos();
10013 // Pass null to ContEnterDelegate initially.
10014 e.Null();
10016 Label loopBeginning(e);
10017 e.ContEnterDelegate();
10018 e.YieldFromDelegate(itId, loopBeginning);
10019 newFaultRegionAndFunclet(bDelegateAssigned, m_ue.bcPos(),
10020 new UnsetGeneratorDelegateThunklet(itId));
10022 // Now that we're done with it, remove the delegate. This lets us enforce
10023 // the invariant that if we have a delegate set, we should be using it.
10024 e.ContUnsetDelegate(false, itId);
10028 * Emits bytecode that restores the previous error reporting level after
10029 * evaluating a silenced (@) expression, or in the fault funclet protecting such
10030 * an expression. Requires a local variable id containing the previous error
10031 * reporting level. The whole silenced expression looks like this:
10032 * oldvalue = error_reporting(0)
10033 * ...evaluate silenced expression...
10034 * oldvalue = error_reporting(oldvalue)
10035 * if oldvalue != 0:
10036 * error_reporting(oldvalue)
10038 void EmitterVisitor::emitRestoreErrorReporting(Emitter& e, Id oldLevelLoc) {
10039 emitVirtualLocal(oldLevelLoc);
10040 auto idx = m_evalStack.size() - 1;
10041 e.Silence(m_evalStack.getLoc(idx), SilenceOp::End);
10044 void EmitterVisitor::emitMakeUnitFatal(Emitter& e,
10045 const char* msg,
10046 FatalOp k) {
10047 const StringData* sd = makeStaticString(msg);
10048 e.String(sd);
10049 e.Fatal(k);
10052 Funclet* EmitterVisitor::addFunclet(StatementPtr stmt, Thunklet* body) {
10053 Funclet* f = addFunclet(body);
10054 m_memoizedFunclets.insert(std::make_pair(stmt, f));
10055 return f;
10058 Funclet* EmitterVisitor::addFunclet(Thunklet* body) {
10059 m_funclets.push_back(new Funclet(body));
10060 return m_funclets.back();
10063 Funclet* EmitterVisitor::getFunclet(StatementPtr stmt) {
10064 if (m_memoizedFunclets.count(stmt)) {
10065 return m_memoizedFunclets[stmt];
10066 } else {
10067 return nullptr;
10071 void EmitterVisitor::emitFunclets(Emitter& e) {
10072 // TODO (#3271358): New fault funclets might appear while emitting
10073 // finally fault funclets. This is because we currently don't memoize
10074 // fault funclets other than finally fault fuclets. See task
10075 // description for more details.
10076 for (int i = 0; i < m_funclets.size(); ++i) {
10077 Funclet* f = m_funclets[i];
10078 f->m_entry.set(e);
10079 f->m_body->emit(e);
10080 delete f->m_body;
10081 f->m_body = nullptr;
10085 void EmitterVisitor::newFaultRegion(Offset start,
10086 Offset end,
10087 Label* entry,
10088 FaultIterInfo iter) {
10089 auto r = new FaultRegion(start, end, entry, iter.iterId, iter.kind);
10090 m_faultRegions.push_back(r);
10093 void EmitterVisitor::newFaultRegionAndFunclet(Offset start,
10094 Offset end,
10095 Thunklet* t,
10096 FaultIterInfo iter) {
10097 Funclet* f = addFunclet(t);
10098 newFaultRegion(start, end, &f->m_entry, iter);
10101 void EmitterVisitor::newFaultRegionAndFunclet(StatementPtr stmt,
10102 Offset start,
10103 Offset end,
10104 Thunklet* t,
10105 FaultIterInfo iter) {
10106 Funclet* f = addFunclet(stmt, t);
10107 newFaultRegion(start, end, &f->m_entry, iter);
10110 void EmitterVisitor::newFPIRegion(Offset start, Offset end, Offset fpOff) {
10111 FPIRegion* r = new FPIRegion(start, end, fpOff);
10112 m_fpiRegions.push_back(r);
10115 void EmitterVisitor::copyOverCatchAndFaultRegions(FuncEmitter* fe) {
10116 for (auto& eh : m_catchRegions) {
10117 auto& e = fe->addEHEnt();
10118 e.m_type = EHEnt::Type::Catch;
10119 e.m_base = eh->m_start;
10120 e.m_past = eh->m_end;
10121 assert(e.m_base != kInvalidOffset);
10122 assert(e.m_past != kInvalidOffset);
10123 e.m_iterId = -1;
10124 for (auto& c : eh->m_catchLabels) {
10125 Id id = m_ue.mergeLitstr(c.first);
10126 Offset off = c.second->getAbsoluteOffset();
10127 e.m_catches.push_back(std::pair<Id, Offset>(id, off));
10129 delete eh;
10131 m_catchRegions.clear();
10132 for (auto& fr : m_faultRegions) {
10133 auto& e = fe->addEHEnt();
10134 e.m_type = EHEnt::Type::Fault;
10135 e.m_base = fr->m_start;
10136 e.m_past = fr->m_end;
10137 assert(e.m_base != kInvalidOffset);
10138 assert(e.m_past != kInvalidOffset);
10139 e.m_iterId = fr->m_iterId;
10140 e.m_itRef = fr->m_iterKind == KindOfMIter;
10141 e.m_fault = fr->m_func->getAbsoluteOffset();
10142 assert(e.m_fault != kInvalidOffset);
10143 delete fr;
10145 m_faultRegions.clear();
10146 for (auto f : m_funclets) {
10147 delete f;
10149 m_funclets.clear();
10150 m_memoizedFunclets.clear();
10153 void EmitterVisitor::copyOverFPIRegions(FuncEmitter* fe) {
10154 for (std::deque<FPIRegion*>::iterator it = m_fpiRegions.begin();
10155 it != m_fpiRegions.end(); ++it) {
10156 FPIEnt& e = fe->addFPIEnt();
10157 e.m_fpushOff = (*it)->m_start;
10158 e.m_fcallOff = (*it)->m_end;
10159 e.m_fpOff = (*it)->m_fpOff;
10160 delete *it;
10162 m_fpiRegions.clear();
10165 void EmitterVisitor::saveMaxStackCells(FuncEmitter* fe, int32_t stackPad) {
10166 fe->maxStackCells +=
10167 fe->numIterators() * kNumIterCells +
10168 fe->numLocals() +
10169 stackPad;
10171 m_evalStack.m_actualStackHighWaterPtr = nullptr;
10174 // Are you sure you mean to be calling this directly? Would FuncFinisher
10175 // be more appropriate?
10176 void EmitterVisitor::finishFunc(Emitter& e, FuncEmitter* fe, int32_t stackPad) {
10177 emitFunclets(e);
10178 saveMaxStackCells(fe, stackPad);
10179 copyOverCatchAndFaultRegions(fe);
10180 copyOverFPIRegions(fe);
10181 m_staticEmitted.clear();
10182 Offset past = e.getUnitEmitter().bcPos();
10183 fe->finish(past, false);
10184 e.getUnitEmitter().recordFunction(fe);
10185 if (m_stateLocal >= 0) {
10186 m_stateLocal = -1;
10188 if (m_retLocal >= 0) {
10189 m_retLocal = -1;
10193 void EmitterVisitor::initScalar(TypedValue& tvVal, ExpressionPtr val,
10194 folly::Optional<HeaderKind> kind) {
10195 assert(val->isScalar());
10196 tvVal.m_type = KindOfUninit;
10197 // static array initilization
10198 auto initArray = [&](ExpressionPtr el, folly::Optional<HeaderKind> k) {
10199 if (k == HeaderKind::Dict) {
10200 m_staticArrays.push_back(Array::attach(MixedArray::MakeReserveDict(0)));
10201 } else if (k == HeaderKind::VecArray) {
10202 m_staticArrays.push_back(Array::attach(PackedArray::MakeReserveVec(0)));
10203 } else if (k == HeaderKind::Keyset) {
10204 m_staticArrays.push_back(Array::attach(SetArray::MakeReserveSet(0)));
10205 } else {
10206 m_staticArrays.push_back(Array::attach(PackedArray::MakeReserve(0)));
10208 m_staticColType.push_back(k);
10209 visit(el);
10210 tvVal = make_array_like_tv(
10211 ArrayData::GetScalarArray(m_staticArrays.back().get())
10213 m_staticArrays.pop_back();
10214 m_staticColType.pop_back();
10216 switch (val->getKindOf()) {
10217 case Expression::KindOfConstantExpression: {
10218 auto ce = static_pointer_cast<ConstantExpression>(val);
10219 if (ce->isNull()) {
10220 tvVal.m_data.num = 0;
10221 tvVal.m_type = KindOfNull;
10222 } else if (ce->isBoolean()) {
10223 tvVal = make_tv<KindOfBoolean>(ce->getBooleanValue());
10224 } else if (ce->isScalar()) {
10225 ce->getScalarValue(tvAsVariant(&tvVal));
10226 } else {
10227 not_implemented();
10229 break;
10231 case Expression::KindOfScalarExpression: {
10232 auto sval = static_pointer_cast<ScalarExpression>(val);
10233 const std::string* s;
10234 if (sval->getString(s)) {
10235 StringData* sd = makeStaticString(*s);
10236 tvVal = make_tv<KindOfString>(sd);
10237 break;
10239 int64_t i;
10240 if (sval->getInt(i)) {
10241 tvVal = make_tv<KindOfInt64>(i);
10242 break;
10244 double d;
10245 if (sval->getDouble(d)) {
10246 tvVal = make_tv<KindOfDouble>(d);
10247 break;
10249 assert(false);
10250 break;
10252 case Expression::KindOfExpressionList: {
10253 // Array, possibly for collection initialization.
10254 initArray(val, kind);
10255 break;
10257 case Expression::KindOfUnaryOpExpression: {
10258 auto u = static_pointer_cast<UnaryOpExpression>(val);
10259 if (u->getOp() == T_ARRAY) {
10260 initArray(u->getExpression(), folly::none);
10261 break;
10263 if (u->getOp() == T_DICT) {
10264 initArray(u->getExpression(), HeaderKind::Dict);
10265 break;
10267 if (u->getOp() == T_VEC) {
10268 initArray(u->getExpression(), HeaderKind::VecArray);
10269 break;
10271 if (u->getOp() == T_KEYSET) {
10272 initArray(u->getExpression(), HeaderKind::Keyset);
10273 break;
10275 // Fall through
10277 default: {
10278 if (val->getScalarValue(tvAsVariant(&tvVal))) {
10279 if (tvAsVariant(&tvVal).isArray()) {
10280 not_implemented();
10282 break;
10284 not_reached();
10289 void EmitterVisitor::emitArrayInit(Emitter& e, ExpressionListPtr el,
10290 folly::Optional<HeaderKind> kind) {
10291 assert(m_staticArrays.empty());
10292 auto const isDict = kind == HeaderKind::Dict;
10293 auto const isVec = kind == HeaderKind::VecArray;
10294 auto const isKeyset = kind == HeaderKind::Keyset;
10296 if (el == nullptr) {
10297 if (isDict) {
10298 e.Dict(staticEmptyDictArray());
10299 return;
10301 if (isVec) {
10302 e.Vec(staticEmptyVecArray());
10303 return;
10305 if (isKeyset) {
10306 e.Keyset(staticEmptyKeysetArray());
10307 return;
10309 e.Array(staticEmptyArray());
10310 return;
10313 auto const scalar =
10314 isDict ? isDictScalar(el) :
10315 isKeyset ? isKeysetScalar(el) :
10316 el->isScalar();
10317 if (scalar) {
10318 TypedValue tv;
10319 tvWriteUninit(&tv);
10320 initScalar(tv, el, kind);
10321 if (isDict) {
10322 assert(tv.m_data.parr->isDict());
10323 e.Dict(tv.m_data.parr);
10324 return;
10326 if (isVec) {
10327 assert(tv.m_data.parr->isVecArray());
10328 e.Vec(tv.m_data.parr);
10329 return;
10331 if (isKeyset) {
10332 assert(tv.m_data.parr->isKeyset());
10333 e.Keyset(tv.m_data.parr);
10334 return;
10336 assert(tv.m_data.parr->isPHPArray());
10337 e.Array(tv.m_data.parr);
10338 return;
10341 if (isVec || isKeyset) {
10342 auto const count = el->getCount();
10343 for (int i = 0; i < count; i++) {
10344 auto expr = static_pointer_cast<Expression>((*el)[i]);
10345 visit(expr);
10346 emitConvertToCell(e);
10348 if (isVec) {
10349 e.NewVecArray(count);
10350 } else {
10351 e.NewKeysetArray(count);
10353 return;
10356 auto const allowPacked = !kind || isVectorCollection((CollectionType)*kind);
10358 int nElms;
10359 if (allowPacked && isPackedInit(el, &nElms)) {
10360 for (int i = 0; i < nElms; ++i) {
10361 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10362 visit(ap->getValue());
10363 emitConvertToCell(e);
10365 e.NewPackedArray(nElms);
10366 return;
10369 auto const allowStruct = !kind;
10370 std::vector<std::string> keys;
10371 if (allowStruct && isStructInit(el, keys)) {
10372 for (int i = 0, n = keys.size(); i < n; i++) {
10373 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10374 visit(ap->getValue());
10375 emitConvertToCell(e);
10377 e.NewStructArray(keys);
10378 return;
10381 auto capacityHint = MixedArray::SmallSize;
10382 auto const capacity = el->getCount();
10383 if (capacity > 0) capacityHint = capacity;
10384 if (allowPacked && isPackedInit(el, &nElms, false /* ignore size */)) {
10385 e.NewArray(capacityHint);
10386 } else if (isDict) {
10387 e.NewDictArray(capacityHint);
10388 } else {
10389 e.NewMixedArray(capacityHint);
10391 visit(el);
10394 void EmitterVisitor::emitPairInit(Emitter& e, ExpressionListPtr el) {
10395 if (el->getCount() != 2) {
10396 throw IncludeTimeFatalException(el,
10397 "Pair objects must have exactly 2 elements");
10399 e.NewCol(static_cast<int>(CollectionType::Pair));
10400 for (int i = 0; i < 2; i++) {
10401 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10402 if (ap->getName() != nullptr) {
10403 throw IncludeTimeFatalException(ap,
10404 "Keys may not be specified for Pair initialization");
10406 visit(ap->getValue());
10407 emitConvertToCell(e);
10408 e.ColAddNewElemC();
10412 void EmitterVisitor::emitVectorInit(Emitter&e, CollectionType ct,
10413 ExpressionListPtr el) {
10414 // Do not allow specification of keys even if the resulting array is
10415 // packed. It doesn't make sense to specify keys for Vectors.
10416 for (int i = 0; i < el->getCount(); i++) {
10417 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10418 if (ap->getName() != nullptr) {
10419 throw IncludeTimeFatalException(ap,
10420 "Keys may not be specified for Vector initialization");
10423 emitArrayInit(e, el, (HeaderKind)ct);
10424 e.ColFromArray(static_cast<int>(ct));
10425 return;
10428 void EmitterVisitor::emitSetInit(Emitter&e, CollectionType ct,
10429 ExpressionListPtr el) {
10431 * Use an array to initialize the Set only if all the following conditional
10432 * are met:
10433 * 1. non-empty initializer;
10434 * 2. no integer-like string values (keys are the same as values for Set);
10435 * 3. !arr->isVectorData() to guarantee that we have a MixedArray.
10437 * Effectively, we use array for Set initialization only when it is a static
10438 * array for now.
10440 auto const nElms = el->getCount();
10441 auto useArray = !!nElms;
10442 auto hasVectorData = true;
10443 for (int i = 0; i < nElms; i++) {
10444 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10445 auto key = ap->getName();
10446 if ((bool)key) {
10447 throw IncludeTimeFatalException(ap,
10448 "Keys may not be specified for Set initialization");
10450 if (!useArray) continue;
10451 auto val = ap->getValue();
10452 Variant v;
10453 if (val->getScalarValue(v)) {
10454 if (v.isString()) {
10455 hasVectorData = false;
10456 int64_t intVal;
10457 if (v.getStringData()->isStrictlyInteger(intVal)) {
10458 useArray = false;
10460 } else if (v.isInteger()) {
10461 if (v.asInt64Val() != i) hasVectorData = false;
10462 } else {
10463 useArray = false;
10465 } else {
10466 useArray = false;
10469 if (hasVectorData) useArray = false;
10471 if (useArray) {
10472 emitArrayInit(e, el, (HeaderKind)ct);
10473 e.ColFromArray(static_cast<int>(ct));
10474 } else {
10475 if (nElms == 0) {
10476 // Will use the static empty mixed array to avoid allocation.
10477 e.NewCol(static_cast<int>(ct));
10478 return;
10480 e.NewMixedArray(nElms);
10481 e.ColFromArray(static_cast<int>(ct));
10482 for (int i = 0; i < nElms; i++) {
10483 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10484 visit(ap->getValue());
10485 emitConvertToCell(e);
10486 e.ColAddNewElemC();
10491 void EmitterVisitor::emitMapInit(Emitter&e, CollectionType ct,
10492 ExpressionListPtr el) {
10494 * Use an array to initialize the Map only when all the following conditional
10495 * are met:
10496 * 1. non-empty initializer;
10497 * 2. no integer-like string keys;
10498 * 3. !arr->isVectorData() to guarantee that we have a MixedArray.
10500 auto nElms = el->getCount();
10501 auto useArray = !!nElms;
10502 auto hasVectorData = true;
10503 int64_t max = 0;
10504 for (int i = 0; i < nElms; i++) {
10505 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10506 auto key = ap->getName();
10507 if (key == nullptr) {
10508 throw IncludeTimeFatalException(ap,
10509 "Keys must be specified for Map initialization");
10511 if (!useArray) continue;
10512 Variant vkey;
10513 if (key->getScalarValue(vkey)) {
10514 if (vkey.isString()) {
10515 hasVectorData = false;
10516 int64_t intKey;
10517 if (vkey.getStringData()->isStrictlyInteger(intKey)) {
10518 useArray = false;
10520 } else if (vkey.isInteger()) {
10521 auto val = vkey.asInt64Val();
10522 if (val > max || val < 0) {
10523 hasVectorData = false;
10524 } else if (val == max) {
10525 ++max;
10527 } else {
10528 useArray = false;
10530 } else {
10531 useArray = false;
10534 if (hasVectorData) useArray = false;
10536 if (useArray) {
10537 emitArrayInit(e, el, (HeaderKind)ct);
10538 e.ColFromArray(static_cast<int>(ct));
10539 } else {
10540 if (nElms == 0) {
10541 e.NewCol(static_cast<int>(ct));
10542 return;
10544 e.NewMixedArray(nElms);
10545 e.ColFromArray(static_cast<int>(ct));
10546 for (int i = 0; i < nElms; i++) {
10547 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10548 visit(ap->getName());
10549 emitConvertToCell(e);
10550 visit(ap->getValue());
10551 emitConvertToCell(e);
10552 e.MapAddElemC();
10557 void EmitterVisitor::emitCollectionInit(Emitter& e, BinaryOpExpressionPtr b) {
10558 auto cls = static_pointer_cast<ScalarExpression>(b->getExp1());
10559 const std::string* clsName = nullptr;
10560 cls->getString(clsName);
10561 auto ct = collections::stringToType(*clsName);
10562 if (!ct) {
10563 throw IncludeTimeFatalException(b,
10564 "Cannot use collection initialization for non-collection class");
10567 ExpressionListPtr el = nullptr;
10568 if (b->getExp2()) {
10569 el = static_pointer_cast<ExpressionList>(b->getExp2());
10570 } else {
10571 if (ct == CollectionType::Pair) {
10572 throw IncludeTimeFatalException(b, "Initializer needed for Pair object");
10574 e.NewCol(static_cast<int>(*ct));
10575 return;
10578 if (ct == CollectionType::Pair) {
10579 return emitPairInit(e, el);
10582 if (ct == CollectionType::Vector || ct == CollectionType::ImmVector) {
10583 return emitVectorInit(e, *ct, el);
10586 if (ct == CollectionType::Map || ct == CollectionType::ImmMap) {
10587 return emitMapInit(e, *ct, el);
10590 if (ct == CollectionType::Set || ct == CollectionType::ImmSet) {
10591 return emitSetInit(e, *ct, el);
10594 not_reached();
10597 bool EmitterVisitor::requiresDeepInit(ExpressionPtr initExpr) const {
10598 switch (initExpr->getKindOf()) {
10599 case Expression::KindOfScalarExpression:
10600 return false;
10601 case Expression::KindOfClassConstantExpression:
10602 case Expression::KindOfConstantExpression:
10603 return !initExpr->isScalar();
10604 case Expression::KindOfUnaryOpExpression: {
10605 auto u = static_pointer_cast<UnaryOpExpression>(initExpr);
10606 if (u->getOp() == T_ARRAY || u->getOp() == T_DICT) {
10607 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
10608 if (el) {
10609 int n = el->getCount();
10610 for (int i = 0; i < n; i++) {
10611 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10612 ExpressionPtr key = ap->getName();
10613 if (requiresDeepInit(ap->getValue()) ||
10614 (key && requiresDeepInit(key))) {
10615 return true;
10619 return false;
10620 } else if (u->getOp() == T_VEC || u->getOp() == T_KEYSET) {
10621 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
10622 if (el) {
10623 int n = el->getCount();
10624 for (int i = 0; i < n; i++) {
10625 auto expr = static_pointer_cast<Expression>((*el)[i]);
10626 if (requiresDeepInit(expr)) return true;
10629 return false;
10630 } else if (u->getOp() == '+' || u->getOp() == '-') {
10631 return requiresDeepInit(u->getExpression());
10632 } else if (u->getOp() == T_FILE || u->getOp() == T_DIR) {
10633 return false;
10635 return true;
10637 case Expression::KindOfBinaryOpExpression: {
10638 auto b = static_pointer_cast<BinaryOpExpression>(initExpr);
10639 return requiresDeepInit(b->getExp1()) || requiresDeepInit(b->getExp2());
10641 default:
10642 return true;
10646 Thunklet::~Thunklet() {}
10648 static ConstructPtr doOptimize(ConstructPtr c, AnalysisResultConstPtr ar) {
10649 for (int i = 0, n = c->getKidCount(); i < n; i++) {
10650 if (ConstructPtr k = c->getNthKid(i)) {
10651 if (ConstructPtr rep = doOptimize(k, ar)) {
10652 c->setNthKid(i, rep);
10656 if (auto e = dynamic_pointer_cast<Expression>(c)) {
10657 switch (e->getKindOf()) {
10658 case Expression::KindOfBinaryOpExpression:
10659 case Expression::KindOfUnaryOpExpression:
10660 case Expression::KindOfIncludeExpression:
10661 case Expression::KindOfSimpleFunctionCall:
10662 return e->preOptimize(ar);
10663 case Expression::KindOfClosureExpression: {
10664 auto cl = static_pointer_cast<ClosureExpression>(e);
10665 auto UNUSED exp = doOptimize(cl->getClosureFunction(), ar);
10666 assert(!exp);
10667 break;
10669 default: break;
10672 return ConstructPtr();
10675 static UnitEmitter* emitHHBCUnitEmitter(AnalysisResultPtr ar, FileScopePtr fsp,
10676 const MD5& md5) {
10677 if (fsp->getPseudoMain() && !Option::WholeProgram) {
10678 ar->setPhase(AnalysisResult::FirstPreOptimize);
10679 doOptimize(fsp->getPseudoMain()->getStmt(), ar);
10682 if (RuntimeOption::EvalDumpAst) {
10683 if (fsp->getPseudoMain()) {
10684 fsp->getPseudoMain()->getStmt()->dump(0, ar);
10688 auto msp =
10689 dynamic_pointer_cast<MethodStatement>(fsp->getPseudoMain()->getStmt());
10690 UnitEmitter* ue = new UnitEmitter(md5);
10691 ue->m_preloadPriority = fsp->preloadPriority();
10692 ue->initMain(msp->line0(), msp->line1());
10693 EmitterVisitor ev(*ue);
10694 try {
10695 ev.visit(fsp);
10696 } catch (EmitterVisitor::IncludeTimeFatalException& ex) {
10697 // Replace the unit with an empty one, but preserve its file path.
10698 UnitEmitter* nue = new UnitEmitter(md5);
10699 nue->initMain(msp->line0(), msp->line1());
10700 nue->m_filepath = ue->m_filepath;
10701 delete ue;
10702 ue = nue;
10704 EmitterVisitor fev(*ue);
10705 Emitter emitter(ex.m_node, *ue, fev);
10706 FuncFinisher ff(&fev, emitter, ue->getMain());
10707 auto kind = ex.m_parseFatal ? FatalOp::Parse : FatalOp::Runtime;
10708 fev.emitMakeUnitFatal(emitter, ex.getMessage().c_str(), kind);
10710 return ue;
10713 enum GeneratorMethod {
10714 METH_NEXT,
10715 METH_SEND,
10716 METH_RAISE,
10717 METH_VALID,
10718 METH_CURRENT,
10719 METH_KEY,
10720 METH_REWIND,
10721 METH_GETRETURN,
10724 typedef hphp_hash_map<const StringData*, GeneratorMethod,
10725 string_data_hash, string_data_same> ContMethMap;
10726 typedef std::map<StaticString, GeneratorMethod> ContMethMapT;
10728 namespace {
10729 StaticString s_next("next");
10730 StaticString s_send("send");
10731 StaticString s_raise("raise");
10732 StaticString s_valid("valid");
10733 StaticString s_current("current");
10734 StaticString s_key("key");
10735 StaticString s_throw("throw");
10736 StaticString s_rewind("rewind");
10737 StaticString s_getReturn("getReturn");
10739 StaticString genCls("Generator");
10740 StaticString asyncGenCls("HH\\AsyncGenerator");
10742 ContMethMapT s_asyncGenMethods = {
10743 {s_next, GeneratorMethod::METH_NEXT},
10744 {s_send, GeneratorMethod::METH_SEND},
10745 {s_raise, GeneratorMethod::METH_RAISE}
10747 ContMethMapT s_genMethods = {
10748 {s_next, GeneratorMethod::METH_NEXT},
10749 {s_send, GeneratorMethod::METH_SEND},
10750 {s_raise, GeneratorMethod::METH_RAISE},
10751 {s_valid, GeneratorMethod::METH_VALID},
10752 {s_current, GeneratorMethod::METH_CURRENT},
10753 {s_key, GeneratorMethod::METH_KEY},
10754 {s_throw, GeneratorMethod::METH_RAISE},
10755 {s_rewind, GeneratorMethod::METH_REWIND},
10756 {s_getReturn, GeneratorMethod::METH_GETRETURN}
10760 static int32_t emitGeneratorMethod(UnitEmitter& ue,
10761 FuncEmitter* fe,
10762 GeneratorMethod m,
10763 bool isAsync) {
10764 Attr attrs = (Attr)(AttrBuiltin | AttrPublic);
10765 fe->init(0, 0, ue.bcPos(), attrs, false, staticEmptyString());
10766 fe->isNative = false;
10767 if (!isAsync && RuntimeOption::AutoprimeGenerators) {
10768 // Create a dummy Emitter, so it's possible to emit jump instructions
10769 EmitterVisitor ev(ue);
10770 Emitter e(ConstructPtr(), ue, ev);
10771 Location::Range loc;
10772 if (ue.bcPos() > 0) loc.line0 = -1;
10773 e.setTempLocation(loc);
10775 // Check if the generator has started yet
10776 Label started;
10777 e.ContStarted();
10778 e.JmpNZ(started);
10780 // If it hasn't started, perform one "next" operation before
10781 // the actual operation (auto-priming)
10782 e.ContCheck(false);
10783 e.Null();
10784 e.ContEnter();
10785 e.PopC();
10786 started.set(e);
10789 if (!RuntimeOption::AutoprimeGenerators && m == METH_REWIND) {
10790 // In non-autopriming mode the rewind function will always call next, when
10791 // autopriming is enabled, rewind matches PHP behavior and will only advance
10792 // the generator when it has not yet been started.
10793 m = METH_NEXT;
10796 switch (m) {
10797 case METH_SEND:
10798 case METH_RAISE:
10799 case METH_NEXT: {
10800 // We always want these methods to be cloned with new funcids in
10801 // subclasses so we can burn Class*s and Func*s into the
10802 // translations
10803 fe->attrs |= AttrClone;
10805 // check generator status; send()/raise() also checks started
10806 ue.emitOp(OpContCheck);
10807 ue.emitIVA(m == METH_SEND || m == METH_RAISE);
10809 switch (m) {
10810 case METH_NEXT:
10811 ue.emitOp(OpNull);
10812 ue.emitOp(OpContEnter);
10813 break;
10814 case METH_SEND:
10815 ue.emitOp(OpPushL); ue.emitIVA(0);
10816 ue.emitOp(OpContEnter);
10817 break;
10818 case METH_RAISE:
10819 ue.emitOp(OpPushL); ue.emitIVA(0);
10820 ue.emitOp(OpContRaise);
10821 break;
10822 default:
10823 not_reached();
10826 // Backtrace has off-by-one bug when determining whether we are
10827 // in returning opcode; add Nop to avoid it
10828 ue.emitOp(OpNop);
10829 ue.emitOp(OpRetC);
10830 break;
10832 case METH_VALID: {
10833 ue.emitOp(OpContValid);
10834 ue.emitOp(OpRetC);
10835 break;
10837 case METH_CURRENT: {
10838 ue.emitOp(OpContCurrent);
10839 ue.emitOp(OpRetC);
10840 break;
10842 case METH_KEY: {
10843 ue.emitOp(OpContKey);
10844 ue.emitOp(OpRetC);
10845 break;
10847 case METH_REWIND: {
10848 ue.emitOp(OpNull);
10849 ue.emitOp(OpRetC);
10850 break;
10852 case METH_GETRETURN: {
10853 ue.emitOp(OpContGetReturn);
10854 ue.emitOp(OpRetC);
10855 break;
10857 default:
10858 not_reached();
10861 return 1; // Above cases push at most one stack cell.
10864 // Emit byte codes to implement methods. Return the maximum stack cell count.
10865 int32_t EmitterVisitor::emitNativeOpCodeImpl(MethodStatementPtr meth,
10866 const char* funcName,
10867 const char* className,
10868 FuncEmitter* fe) {
10869 GeneratorMethod* cmeth;
10870 StaticString s_func(funcName);
10871 StaticString s_class(className);
10873 if (genCls.same(s_class) &&
10874 (cmeth = folly::get_ptr(s_genMethods, s_func))) {
10875 return emitGeneratorMethod(m_ue, fe, *cmeth, false);
10876 } else if (asyncGenCls.same(s_class) &&
10877 (cmeth = folly::get_ptr(s_asyncGenMethods, s_func))) {
10878 return emitGeneratorMethod(m_ue, fe, *cmeth, true);
10881 throw IncludeTimeFatalException(meth,
10882 "OpCodeImpl attribute is not applicable to %s", funcName);
10885 namespace {
10887 std::atomic<uint64_t> lastHHBCUnitIndex;
10891 static UnitEmitter* emitHHBCVisitor(AnalysisResultPtr ar, FileScopeRawPtr fsp) {
10892 // If we're in whole program mode, we can just assign each Unit an increasing
10893 // counter, guaranteeing uniqueness.
10894 auto md5 = Option::WholeProgram ? MD5{++lastHHBCUnitIndex} : fsp->getMd5();
10896 if (!Option::WholeProgram) {
10897 // The passed-in ar is only useful in whole-program mode, so create a
10898 // distinct ar to be used only for emission of this unit, and perform
10899 // unit-level (non-global) optimization.
10900 ar = std::make_shared<AnalysisResult>();
10901 fsp->setOuterScope(ar);
10903 ar->setPhase(AnalysisResult::AnalyzeAll);
10904 fsp->analyzeProgram(ar);
10907 auto ue = emitHHBCUnitEmitter(ar, fsp, md5);
10908 assert(ue != nullptr);
10910 if (Option::GenerateTextHHBC) {
10911 // TODO(#2973538): Move HHBC text generation to after all the
10912 // units are created, and get rid of the LitstrTable locking,
10913 // since it won't be needed in that case.
10914 LitstrTable::get().mutex().lock();
10915 LitstrTable::get().setReading();
10916 std::unique_ptr<Unit> unit(ue->create());
10917 std::string fullPath = AnalysisResult::prepareFile(
10918 ar->getOutputPath().c_str(), Option::UserFilePrefix + fsp->getName(),
10919 true, false) + ".hhbc.txt";
10921 std::ofstream f(fullPath.c_str());
10922 if (!f) {
10923 Logger::Error("Unable to open %s for write", fullPath.c_str());
10924 } else {
10925 CodeGenerator cg(&f, CodeGenerator::TextHHBC);
10926 cg.printf("Hash: %" PRIx64 "%016" PRIx64 "\n", md5.q[0], md5.q[1]);
10927 cg.printRaw(unit->toString().c_str());
10928 f.close();
10930 LitstrTable::get().setWriting();
10931 LitstrTable::get().mutex().unlock();
10934 return ue;
10937 struct UEQ : Synchronizable {
10938 void push(UnitEmitter* ue) {
10939 assert(ue != nullptr);
10940 Lock lock(this);
10941 m_ues.push_back(ue);
10942 notify();
10944 UnitEmitter* tryPop(long sec, long long nsec) {
10945 Lock lock(this);
10946 if (m_ues.empty()) {
10947 // Check for empty() after wait(), in case of spurious wakeup.
10948 if (!wait(sec, nsec) || m_ues.empty()) {
10949 return nullptr;
10952 assert(m_ues.size() > 0);
10953 UnitEmitter* ue = m_ues.front();
10954 assert(ue != nullptr);
10955 m_ues.pop_front();
10956 return ue;
10958 private:
10959 std::deque<UnitEmitter*> m_ues;
10961 static UEQ s_ueq;
10963 class EmitterWorker
10964 : public JobQueueWorker<FileScopeRawPtr, void*, true, true> {
10965 public:
10966 EmitterWorker() : m_ret(true) {}
10967 void doJob(JobType job) override {
10968 try {
10969 AnalysisResultPtr ar = ((AnalysisResult*)m_context)->shared_from_this();
10970 UnitEmitter* ue = emitHHBCVisitor(ar, job);
10971 if (Option::GenerateBinaryHHBC) {
10972 s_ueq.push(ue);
10973 } else {
10974 delete ue;
10976 } catch (Exception &e) {
10977 Logger::Error("%s", e.getMessage().c_str());
10978 m_ret = false;
10979 } catch (...) {
10980 Logger::Error("Fatal: An unexpected exception was thrown");
10981 m_ret = false;
10984 void onThreadEnter() override {
10985 g_context.getCheck();
10987 void onThreadExit() override {
10988 hphp_memory_cleanup();
10990 private:
10991 bool m_ret;
10994 static void addEmitterWorker(AnalysisResultPtr ar, StatementPtr sp,
10995 void *data) {
10996 ((JobQueueDispatcher<EmitterWorker>*)data)->enqueue(sp->getFileScope());
10999 static void
11000 commitGlobalData(std::unique_ptr<ArrayTypeTable::Builder> arrTable) {
11001 auto gd = Repo::GlobalData{};
11002 gd.HardTypeHints = HHBBC::options.HardTypeHints;
11003 gd.HardReturnTypeHints = HHBBC::options.HardReturnTypeHints;
11004 gd.ElideAutoloadInvokes = HHBBC::options.ElideAutoloadInvokes;
11005 gd.UsedHHBBC = Option::UseHHBBC;
11006 gd.PHP7_IntSemantics = RuntimeOption::PHP7_IntSemantics;
11007 gd.PHP7_ScalarTypes = RuntimeOption::PHP7_ScalarTypes;
11008 gd.PHP7_Substr = RuntimeOption::PHP7_Substr;
11009 gd.AutoprimeGenerators = RuntimeOption::AutoprimeGenerators;
11010 gd.HardPrivatePropInference = true;
11011 gd.PromoteEmptyObject = RuntimeOption::EvalPromoteEmptyObject;
11013 for (auto a : Option::APCProfile) {
11014 gd.APCProfile.emplace_back(StringData::MakeStatic(folly::StringPiece(a)));
11016 if (arrTable) gd.arrayTypeTable.repopulate(*arrTable);
11017 Repo::get().saveGlobalData(gd);
11021 * This is the entry point for offline bytecode generation.
11023 void emitAllHHBC(AnalysisResultPtr&& ar) {
11024 unsigned int threadCount = Option::ParserThreadCount;
11025 unsigned int nFiles = ar->getAllFilesVector().size();
11026 if (threadCount > nFiles) {
11027 threadCount = nFiles;
11029 if (!threadCount) threadCount = 1;
11031 LitstrTable::get().setWriting();
11033 /* there is a race condition in the first call to
11034 makeStaticString. Make sure we dont hit it */
11036 makeStaticString("");
11037 /* same for TypeConstraint */
11038 TypeConstraint tc;
11041 Compiler::ClearErrors();
11043 JobQueueDispatcher<EmitterWorker>
11044 dispatcher(threadCount, 0, false, ar.get());
11046 auto setPreloadPriority = [&ar](const std::string& f, int p) {
11047 auto fs = ar->findFileScope(f);
11048 if (fs) fs->setPreloadPriority(p);
11052 * Mark files that are referenced from the autoload map
11053 * so they get preloaded via preloadRepo.
11054 * Higher priorities are preloaded first.
11055 * Classes, then functions, then constants mimics
11056 * the order of our existing warmup scripts
11058 for (const auto& ent : Option::AutoloadConstMap) {
11059 setPreloadPriority(ent.second, 1);
11061 for (const auto& ent : Option::AutoloadFuncMap) {
11062 setPreloadPriority(ent.second, 2);
11064 for (const auto& ent : Option::AutoloadClassMap) {
11065 setPreloadPriority(ent.second, 3);
11068 dispatcher.start();
11069 ar->visitFiles(addEmitterWorker, &dispatcher);
11071 auto ues = ar->getHhasFiles();
11072 if (!Option::UseHHBBC && ues.size()) {
11073 batchCommit(std::move(ues));
11076 if (Option::GenerateBinaryHHBC) {
11077 // kBatchSize needs to strike a balance between reducing transaction commit
11078 // overhead (bigger batches are better), and limiting the cost incurred by
11079 // failed commits due to identical units that require rollback and retry
11080 // (smaller batches have less to lose). Empirical results indicate that a
11081 // value in the 2-10 range is reasonable.
11082 static const unsigned kBatchSize = 8;
11084 // Gather up units created by the worker threads and commit them in
11085 // batches.
11086 bool didPop;
11087 bool inShutdown = false;
11088 while (true) {
11089 // Poll, but with a 100ms timeout so that this thread doesn't spin wildly
11090 // if it gets ahead of the workers.
11091 UnitEmitter* ue = s_ueq.tryPop(0, 100 * 1000 * 1000);
11092 if ((didPop = (ue != nullptr))) {
11093 ues.push_back(std::unique_ptr<UnitEmitter>{ue});
11095 if (!Option::UseHHBBC &&
11096 (ues.size() == kBatchSize ||
11097 (!didPop && inShutdown && ues.size() > 0))) {
11098 batchCommit(std::move(ues));
11100 if (!inShutdown) {
11101 inShutdown = dispatcher.pollEmpty();
11102 } else if (!didPop) {
11103 assert(Option::UseHHBBC || ues.size() == 0);
11104 break;
11108 if (!Option::UseHHBBC) {
11109 commitGlobalData(std::unique_ptr<ArrayTypeTable::Builder>{});
11111 } else {
11112 dispatcher.waitEmpty();
11115 assert(Option::UseHHBBC || ues.empty());
11117 ar->finish();
11118 ar.reset();
11120 if (!Option::UseHHBBC) {
11121 batchCommit(std::move(ues));
11122 return;
11125 RuntimeOption::EvalJit = false; // For HHBBC to invoke builtins.
11126 auto pair = HHBBC::whole_program(std::move(ues));
11127 batchCommit(std::move(pair.first));
11128 commitGlobalData(std::move(pair.second));
11131 namespace {
11133 bool startsWith(const char* big, const char* small) {
11134 return strncmp(big, small, strlen(small)) == 0;
11136 bool isFileHack(const char* code, int codeLen, bool allowPartial) {
11137 if (allowPartial) {
11138 return codeLen > strlen("<?hh") && startsWith(code, "<?hh");
11139 } else {
11140 return codeLen > strlen("<?hh // strict") &&
11141 (startsWith(code, "<?hh // strict") || startsWith(code, "<?hh //strict"));
11145 UnitEmitter* makeFatalUnit(const char* filename, const MD5& md5,
11146 const std::string& msg) {
11147 // basically duplicated from as.cpp but is maybe too janky to be
11148 // a common routine somewhere...
11150 // The line numbers we output are bogus, but it's not totally clear
11151 // what line numbers to put. It might be worth adding a mechanism for
11152 // the external emitter to emit a line number when it fails that we can
11153 // use when available.
11154 UnitEmitter* ue = new UnitEmitter(md5);
11155 ue->m_filepath = makeStaticString(filename);
11156 ue->initMain(1, 1);
11157 ue->emitOp(OpString);
11158 ue->emitInt32(ue->mergeLitstr(makeStaticString(msg)));
11159 ue->emitOp(OpFatal);
11160 ue->emitByte(static_cast<uint8_t>(FatalOp::Runtime));
11161 FuncEmitter* fe = ue->getMain();
11162 fe->maxStackCells = 1;
11163 fe->finish(ue->bcPos(), false);
11164 ue->recordFunction(fe);
11166 return ue;
11169 UnitEmitter* useExternalEmitter(const char* code, int codeLen,
11170 const char* filename, const MD5& md5) {
11171 #ifdef _MSC_VER
11172 Logger::Error("The external emitter is not supported under MSVC!");
11173 return nullptr;
11174 #else
11175 std::string hhas, errorOutput;
11177 try {
11178 std::vector<std::string> cmd({
11179 RuntimeOption::EvalUseExternalEmitter, "--stdin", filename});
11180 auto options = folly::Subprocess::pipeStdin().pipeStdout().pipeStderr();
11182 // Run the external emitter, sending the code to its stdin
11183 folly::Subprocess proc(cmd, options);
11184 std::tie(hhas, errorOutput) = proc.communicate(std::string(code, codeLen));
11185 proc.waitChecked();
11187 // External emitter succeeded; assemble its output
11188 // If assembly fails (probably because of malformed emitter
11189 // output), the assembler will return a unit that Fatals and we
11190 // won't do a fallback to the regular emitter. We may want to
11191 // revisit this.
11192 return assemble_string(hhas.data(), hhas.length(), filename, md5);
11194 } catch (const std::exception& e) {
11195 std::string errorMsg = e.what();
11196 if (!errorOutput.empty()) {
11197 // Add the stderr to the output
11198 errorMsg += folly::format(". Output: '{}'",
11199 folly::trimWhitespace(errorOutput)).str();
11202 // If we aren't going to fall back to the internal emitter, generate a
11203 // Fatal'ing unit.
11204 if (!RuntimeOption::EvalExternalEmitterFallback) {
11205 auto msg =
11206 folly::format("Failure running external emitter: {}", errorMsg);
11207 return makeFatalUnit(filename, md5, msg.str());
11210 // Unless we have fallback at the highest level, print a
11211 // diagnostic when we fail
11212 if (RuntimeOption::EvalExternalEmitterFallback < 2) {
11213 Logger::Warning("Failure running external emitter for %s: %s",
11214 filename,
11215 errorMsg.c_str());
11217 return nullptr;
11219 #endif
11224 extern "C" {
11227 * This is the entry point from the runtime; i.e. online bytecode generation.
11228 * The 'filename' parameter may be NULL if there is no file associated with
11229 * the source code.
11231 * Before being actually used, hphp_compiler_parse must be called with
11232 * a NULL `code' parameter to do initialization.
11235 Unit* hphp_compiler_parse(const char* code, int codeLen, const MD5& md5,
11236 const char* filename, Unit** releaseUnit) {
11237 if (UNLIKELY(!code)) {
11238 // Do initialization when code is null; see above.
11239 HHBBC::options.HardReturnTypeHints =
11240 RuntimeOption::EvalCheckReturnTypeHints >= 3;
11241 Option::RecordErrors = false;
11242 Option::ParseTimeOpts = false;
11243 Option::WholeProgram = false;
11244 BuiltinSymbols::LoadSuperGlobals();
11245 TypeConstraint tc;
11246 return nullptr;
11249 SCOPE_ASSERT_DETAIL("hphp_compiler_parse") { return filename; };
11250 std::unique_ptr<Unit> unit;
11251 SCOPE_EXIT {
11252 if (unit && releaseUnit) *releaseUnit = unit.release();
11255 // We don't want to invoke the JIT when trying to run PHP code.
11256 auto const prevFolding = RID().getJitFolding();
11257 RID().setJitFolding(true);
11258 SCOPE_EXIT { RID().setJitFolding(prevFolding); };
11260 try {
11261 UnitOrigin unitOrigin = UnitOrigin::File;
11262 if (!filename) {
11263 filename = "";
11264 unitOrigin = UnitOrigin::Eval;
11267 std::unique_ptr<UnitEmitter> ue;
11268 // Check if this file contains raw hip hop bytecode instead of
11269 // php. This is dictated by a special file extension.
11270 if (RuntimeOption::EvalAllowHhas) {
11271 if (const char* dot = strrchr(filename, '.')) {
11272 const char hhbc_ext[] = "hhas";
11273 if (!strcmp(dot + 1, hhbc_ext)) {
11274 ue.reset(assemble_string(code, codeLen, filename, md5));
11279 // If we are configured to use an external emitter and we are compiling
11280 // a strict mode hack file, try external emitting. Don't externally emit
11281 // systemlib because the external emitter can't handle that yet.
11282 if (!ue &&
11283 !RuntimeOption::EvalUseExternalEmitter.empty() &&
11284 isFileHack(code, codeLen,
11285 RuntimeOption::EvalExternalEmitterAllowPartial) &&
11286 SystemLib::s_inited) {
11287 ue.reset(useExternalEmitter(code, codeLen, filename, md5));
11290 if (!ue) {
11291 auto parseit = [=] (AnalysisResultPtr ar) {
11292 Scanner scanner(code, codeLen,
11293 RuntimeOption::GetScannerType(), filename);
11294 Parser parser(scanner, filename, ar, codeLen);
11295 parser.parse();
11296 return parser.getFileScope();
11299 if (BuiltinSymbols::s_systemAr) {
11300 parseit(BuiltinSymbols::s_systemAr)->setMd5(md5);
11303 auto ar = std::make_shared<AnalysisResult>();
11304 FileScopePtr fsp = parseit(ar);
11305 fsp->setOuterScope(ar);
11307 ar->setPhase(AnalysisResult::AnalyzeAll);
11308 fsp->analyzeProgram(ar);
11310 ue.reset(emitHHBCUnitEmitter(ar, fsp, md5));
11312 // NOTE: Repo errors are ignored!
11313 Repo::get().commitUnit(ue.get(), unitOrigin);
11315 unit = ue->create();
11316 ue.reset();
11318 if (unit->sn() == -1) {
11319 // the unit was not committed to the Repo, probably because
11320 // another thread did it first. Try to use the winner.
11321 auto u = Repo::get().loadUnit(filename ? filename : "", md5);
11322 if (u != nullptr) {
11323 return u.release();
11326 return unit.release();
11327 } catch (const std::exception&) {
11328 // extern "C" function should not be throwing exceptions...
11329 return nullptr;
11333 } // extern "C"
11335 ///////////////////////////////////////////////////////////////////////////////