Add new bytecode instructions for vec/dict/keyset
[hiphop-php.git] / hphp / compiler / analysis / emitter.cpp
blob3aa8bfcc09f13f1100e86b4ac28f0d7e064534c5
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2016 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/compiler/analysis/emitter.h"
19 #include <algorithm>
20 #include <deque>
21 #include <exception>
22 #include <fstream>
23 #include <iomanip>
24 #include <iostream>
25 #include <memory>
26 #include <set>
27 #include <utility>
28 #include <vector>
30 #include <boost/algorithm/string/predicate.hpp>
32 #include <folly/MapUtil.h>
33 #include <folly/Memory.h>
34 #include <folly/ScopeGuard.h>
35 #ifndef _MSC_VER
36 #include <folly/Subprocess.h>
37 #endif
38 #include <folly/String.h>
40 #include "hphp/compiler/builtin_symbols.h"
41 #include "hphp/compiler/analysis/class_scope.h"
42 #include "hphp/compiler/analysis/code_error.h"
43 #include "hphp/compiler/analysis/file_scope.h"
44 #include "hphp/compiler/analysis/function_scope.h"
45 #include "hphp/compiler/expression/array_element_expression.h"
46 #include "hphp/compiler/expression/array_pair_expression.h"
47 #include "hphp/compiler/expression/assignment_expression.h"
48 #include "hphp/compiler/expression/binary_op_expression.h"
49 #include "hphp/compiler/expression/class_constant_expression.h"
50 #include "hphp/compiler/expression/class_expression.h"
51 #include "hphp/compiler/expression/closure_expression.h"
52 #include "hphp/compiler/expression/constant_expression.h"
53 #include "hphp/compiler/expression/dynamic_variable.h"
54 #include "hphp/compiler/expression/encaps_list_expression.h"
55 #include "hphp/compiler/expression/expression_list.h"
56 #include "hphp/compiler/expression/include_expression.h"
57 #include "hphp/compiler/expression/list_assignment.h"
58 #include "hphp/compiler/expression/modifier_expression.h"
59 #include "hphp/compiler/expression/new_object_expression.h"
60 #include "hphp/compiler/expression/object_method_expression.h"
61 #include "hphp/compiler/expression/parameter_expression.h"
62 #include "hphp/compiler/expression/qop_expression.h"
63 #include "hphp/compiler/expression/null_coalesce_expression.h"
64 #include "hphp/compiler/expression/scalar_expression.h"
65 #include "hphp/compiler/expression/simple_variable.h"
66 #include "hphp/compiler/expression/simple_function_call.h"
67 #include "hphp/compiler/expression/static_member_expression.h"
68 #include "hphp/compiler/expression/unary_op_expression.h"
69 #include "hphp/compiler/expression/yield_expression.h"
70 #include "hphp/compiler/expression/yield_from_expression.h"
71 #include "hphp/compiler/expression/await_expression.h"
72 #include "hphp/compiler/statement/block_statement.h"
73 #include "hphp/compiler/statement/break_statement.h"
74 #include "hphp/compiler/statement/case_statement.h"
75 #include "hphp/compiler/statement/catch_statement.h"
76 #include "hphp/compiler/statement/class_constant.h"
77 #include "hphp/compiler/statement/class_variable.h"
78 #include "hphp/compiler/statement/do_statement.h"
79 #include "hphp/compiler/statement/echo_statement.h"
80 #include "hphp/compiler/statement/exp_statement.h"
81 #include "hphp/compiler/statement/for_statement.h"
82 #include "hphp/compiler/statement/foreach_statement.h"
83 #include "hphp/compiler/statement/finally_statement.h"
84 #include "hphp/compiler/statement/function_statement.h"
85 #include "hphp/compiler/statement/global_statement.h"
86 #include "hphp/compiler/statement/goto_statement.h"
87 #include "hphp/compiler/statement/if_branch_statement.h"
88 #include "hphp/compiler/statement/if_statement.h"
89 #include "hphp/compiler/statement/label_statement.h"
90 #include "hphp/compiler/statement/method_statement.h"
91 #include "hphp/compiler/statement/return_statement.h"
92 #include "hphp/compiler/statement/statement_list.h"
93 #include "hphp/compiler/statement/static_statement.h"
94 #include "hphp/compiler/statement/switch_statement.h"
95 #include "hphp/compiler/statement/try_statement.h"
96 #include "hphp/compiler/statement/unset_statement.h"
97 #include "hphp/compiler/statement/while_statement.h"
98 #include "hphp/compiler/statement/use_trait_statement.h"
99 #include "hphp/compiler/statement/class_require_statement.h"
100 #include "hphp/compiler/statement/trait_prec_statement.h"
101 #include "hphp/compiler/statement/trait_alias_statement.h"
102 #include "hphp/compiler/statement/typedef_statement.h"
103 #include "hphp/compiler/statement/declare_statement.h"
104 #include "hphp/compiler/parser/parser.h"
105 #include "hphp/hhbbc/hhbbc.h"
106 #include "hphp/hhbbc/parallel.h"
108 #include "hphp/util/trace.h"
109 #include "hphp/util/safe-cast.h"
110 #include "hphp/util/logger.h"
111 #include "hphp/util/job-queue.h"
112 #include "hphp/parser/hphp.tab.hpp"
113 #include "hphp/runtime/base/repo-auth-type.h"
114 #include "hphp/runtime/vm/bytecode.h"
115 #include "hphp/runtime/vm/native.h"
116 #include "hphp/runtime/vm/repo.h"
117 #include "hphp/runtime/vm/repo-global-data.h"
118 #include "hphp/runtime/vm/as.h"
119 #include "hphp/runtime/base/packed-array.h"
120 #include "hphp/runtime/base/stats.h"
121 #include "hphp/runtime/base/static-string-table.h"
122 #include "hphp/runtime/base/runtime-option.h"
123 #include "hphp/runtime/base/zend-string.h"
124 #include "hphp/runtime/base/zend-functions.h"
125 #include "hphp/runtime/base/type-conversions.h"
126 #include "hphp/runtime/base/builtin-functions.h"
127 #include "hphp/runtime/base/variable-serializer.h"
128 #include "hphp/runtime/base/program-functions.h"
129 #include "hphp/runtime/base/unit-cache.h"
130 #include "hphp/runtime/base/user-attributes.h"
131 #include "hphp/runtime/base/collections.h"
132 #include "hphp/runtime/vm/preclass-emitter.h"
133 #include "hphp/runtime/vm/runtime.h"
135 #include "hphp/system/systemlib.h"
137 namespace HPHP {
139 DECLARE_BOOST_TYPES(AwaitExpression);
140 DECLARE_BOOST_TYPES(ClosureExpression);
141 DECLARE_BOOST_TYPES(FileScope);
142 DECLARE_BOOST_TYPES(ForEachStatement);
143 DECLARE_BOOST_TYPES(FunctionCall);
144 DECLARE_BOOST_TYPES(FunctionScope);
145 DECLARE_BOOST_TYPES(InterfaceStatement);
146 DECLARE_BOOST_TYPES(ListAssignment);
147 DECLARE_BOOST_TYPES(MethodStatement);
148 DECLARE_BOOST_TYPES(SimpleFunctionCall);
149 DECLARE_BOOST_TYPES(SwitchStatement);
151 namespace Compiler {
152 ///////////////////////////////////////////////////////////////////////////////
154 TRACE_SET_MOD(emitter);
156 const StaticString
157 s_ini_get("ini_get"),
158 s_is_deprecated("deprecated function"),
159 s_trigger_error("trigger_error"),
160 s_trigger_sampled_error("trigger_sampled_error"),
161 s_zend_assertions("zend.assertions");
163 struct Label;
164 struct EmitterVisitor;
166 using OptLocation = folly::Optional<Location::Range>;
168 namespace StackSym {
169 static const char None = 0x00;
172 * We don't actually track the U flavor (we treat it as a C),
173 * because there's nothing important to do with it for emission.
174 * The verifier will check they are only created at the appropriate
175 * times.
177 static const char C = 0x01; // Cell symbolic flavor
178 static const char V = 0x02; // Ref symbolic flavor
179 static const char A = 0x03; // Classref symbolic flavor
180 static const char R = 0x04; // Return value symbolic flavor
181 static const char F = 0x05; // Function argument symbolic flavor
182 static const char L = 0x06; // Local symbolic flavor
183 static const char T = 0x07; // String literal symbolic flavor
184 static const char I = 0x08; // int literal symbolic flavor
185 static const char H = 0x09; // $this symbolic flavor
187 static const char N = 0x10; // Name marker
188 static const char G = 0x20; // Global name marker
189 static const char E = 0x30; // Element marker
190 static const char W = 0x40; // New element marker
191 static const char P = 0x50; // Property marker
192 static const char S = 0x60; // Static property marker
193 static const char M = 0x70; // Non elem/prop/W part of M-vector
194 static const char K = (char)0x80u; // Class base marker
195 static const char Q = (char)0x90u; // NullSafe Property marker
197 static const char CN = C | N;
198 static const char CG = C | G;
199 static const char CS = C | S;
200 static const char LN = L | N;
201 static const char LG = L | G;
202 static const char LS = L | S;
203 static const char AM = A | M;
205 char GetSymFlavor(char sym) { return (sym & 0x0F); }
206 char GetMarker(char sym) { return (sym & 0xF0); }
209 * Return whether or not sym represents a symbolic stack element, rather than
210 * an actual stack element. Symbolic stack elements do not have corresponding
211 * values on the real eval stack at runtime, and represent things like local
212 * variable ids or literal ints and strings.
214 bool IsSymbolic(char sym) {
215 auto const flavor = GetSymFlavor(sym);
216 if (flavor == L || flavor == T || flavor == I || flavor == H) return true;
218 auto const marker = GetMarker(sym);
219 if (marker == W || marker == K) return true;
221 return false;
224 std::string ToString(char sym) {
225 char symFlavor = StackSym::GetSymFlavor(sym);
226 std::string res;
227 switch (symFlavor) {
228 case StackSym::C: res = "C"; break;
229 case StackSym::V: res = "V"; break;
230 case StackSym::A: res = "A"; break;
231 case StackSym::R: res = "R"; break;
232 case StackSym::F: res = "F"; break;
233 case StackSym::L: res = "L"; break;
234 case StackSym::T: res = "T"; break;
235 case StackSym::I: res = "I"; break;
236 case StackSym::H: res = "H"; break;
237 default: break;
239 char marker = StackSym::GetMarker(sym);
240 switch (marker) {
241 case StackSym::N: res += "N"; break;
242 case StackSym::G: res += "G"; break;
243 case StackSym::E: res += "E"; break;
244 case StackSym::W: res += "W"; break;
245 case StackSym::P: res += "P"; break;
246 case StackSym::Q: res += "Q"; break;
247 case StackSym::S: res += "S"; break;
248 case StackSym::K: res += "K"; break;
249 default: break;
251 if (res == "") {
252 if (sym == StackSym::None) {
253 res = "None";
254 } else {
255 res = "?";
258 return res;
262 struct Emitter {
263 Emitter(ConstructPtr node, UnitEmitter& ue, EmitterVisitor& ev)
264 : m_node(node), m_ue(ue), m_ev(ev) {}
265 UnitEmitter& getUnitEmitter() { return m_ue; }
266 ConstructPtr getNode() { return m_node; }
267 EmitterVisitor& getEmitterVisitor() { return m_ev; }
268 void setTempLocation(const OptLocation& r) {
269 m_tempLoc = r;
271 const OptLocation& getTempLocation() { return m_tempLoc; }
272 void incStat(int counter, int value) {
273 if (RuntimeOption::EnableEmitterStats) {
274 IncStat(counter, value);
278 struct StrOff {
279 StrOff(Id s, Label* d) : str(s), dest(d) {}
280 Id str;
281 Label* dest;
284 struct IterPair {
285 IterPair(IterKind k, Id i) : kind(k), id(i) {}
286 IterKind kind;
287 Id id;
290 #define NA
291 #define ONE(typ) \
292 IMM_##typ
293 #define TWO(typ1, typ2) \
294 IMM_##typ1, IMM_##typ2
295 #define THREE(typ1, typ2, typ3) \
296 IMM_##typ1, IMM_##typ2, IMM_##typ3
297 #define FOUR(typ1, typ2, typ3, typ4) \
298 IMM_##typ1, IMM_##typ2, IMM_##typ3, IMM_##typ4
299 #define IMM_BLA std::vector<Label*>&
300 #define IMM_SLA std::vector<StrOff>&
301 #define IMM_ILA std::vector<IterPair>&
302 #define IMM_IVA int32_t
303 #define IMM_LA int32_t
304 #define IMM_IA int32_t
305 #define IMM_I64A int64_t
306 #define IMM_DA double
307 #define IMM_SA const StringData*
308 #define IMM_RATA RepoAuthType
309 #define IMM_AA ArrayData*
310 #define IMM_BA Label&
311 #define IMM_OA(type) type
312 #define IMM_VSA std::vector<std::string>&
313 #define IMM_KA MemberKey
314 #define O(name, imm, pop, push, flags) void name(imm);
315 OPCODES
316 #undef O
317 #undef NA
318 #undef ONE
319 #undef TWO
320 #undef THREE
321 #undef FOUR
322 #undef IMM_MA
323 #undef IMM_BLA
324 #undef IMM_SLA
325 #undef IMM_ILA
326 #undef IMM_IVA
327 #undef IMM_LA
328 #undef IMM_IA
329 #undef IMM_I64A
330 #undef IMM_DA
331 #undef IMM_SA
332 #undef IMM_RATA
333 #undef IMM_AA
334 #undef IMM_BA
335 #undef IMM_OA
336 #undef IMM_VSA
337 #undef IMM_KA
339 private:
340 ConstructPtr m_node;
341 UnitEmitter& m_ue;
342 EmitterVisitor& m_ev;
343 OptLocation m_tempLoc;
346 struct SymbolicStack {
347 enum ClassBaseType {
348 CLS_INVALID,
349 CLS_LATE_BOUND,
350 CLS_UNNAMED_LOCAL, // loc is an unnamed local
351 CLS_NAMED_LOCAL, // loc is a normal program local
352 CLS_STRING_NAME, // name is the string to use
353 CLS_SELF,
354 CLS_PARENT
357 private:
359 * Symbolic stack (m_symStack)
361 * The symbolic stack is used to keep track of the flavor descriptors
362 * of values along with other contextual information. Each position in
363 * the symbolic stack can encode a "symbolic flavor" and a "marker".
364 * Most symbolic flavors correspond with flavor descriptors in the HHBC
365 * spec, but some symbolic flavors used in the symbolic stack (ex. "L")
366 * do not correspond with a flavor descriptor from the spec. Markers
367 * provide contextual information and are used by the emitter in various
368 * situations to determine the appropriate bytecode instruction to use.
370 * Note that not all positions on the symbolic stack correspond to a
371 * value on the actual evaluation stack as described in the HHBC spec.
373 struct SymEntry {
374 explicit SymEntry(char s = 0)
375 : sym(s)
376 , name(nullptr)
377 , className(nullptr)
378 , intval(-1)
379 , unnamedLocalStart(InvalidAbsoluteOffset)
380 , clsBaseType(CLS_INVALID)
382 char sym;
383 const StringData* name;
384 const StringData* className;
385 int64_t intval; // used for L and I symbolic flavors
387 // If intval is an unnamed local temporary, this offset is the start
388 // of the region we are using it (which we will need to have a
389 // fault funclet for).
390 Offset unnamedLocalStart;
392 // When class bases are emitted, we need to delay class lookup for
393 // evaluation order reasons, but may have to do some evaluation
394 // early. How this works depends on the type of class base---see
395 // emitResolveClsBase for details.
396 ClassBaseType clsBaseType;
398 std::string pretty() const;
400 std::vector<SymEntry> m_symStack;
403 * Actual stack (m_actualStack)
405 * The actual stack represents the evaluation stack as described in the
406 * HHBC spec. Each position in m_actualStack contains the index of the
407 * corresponding symbolic value in m_symStack.
409 std::vector<int> m_actualStack;
411 // The number of Func descriptors (in HHVM terms, ActRecs) currently on the
412 // stack.
413 int m_fdescCount;
415 public:
416 int* m_actualStackHighWaterPtr;
418 SymbolicStack() : m_fdescCount(0) {}
420 std::string pretty() const;
422 void updateHighWater();
423 void push(char sym);
424 void setInt(int64_t v);
425 void setString(const StringData* s);
426 void setKnownCls(const StringData* s, bool nonNull);
427 void cleanTopMeta();
428 void setClsBaseType(ClassBaseType);
429 void setUnnamedLocal(int index, int localId, Offset startOffset);
430 void pop();
431 char top() const;
432 char get(int index) const;
433 const StringData* getName(int index) const;
434 const StringData* getClsName(int index) const;
435 bool isCls(int index) const;
436 bool isTypePredicted(int index = -1 /* stack top */) const;
437 void set(int index, char sym);
438 size_t size() const;
439 size_t actualSize() const;
440 size_t fdescSize() const { return m_fdescCount; }
441 bool empty() const;
442 void clear();
445 * Erase a stack element depth below the top. This is used for some
446 * instructions that pull elements out of the middle, and for our
447 * ClassBaseType virtual elements.
449 void consumeBelowTop(int depth);
451 int getActualPos(int vpos) const;
452 char getActual(int index) const;
453 void setActual(int index, char sym);
454 void insertAt(int depth, char sym);
455 int sizeActual() const;
457 ClassBaseType getClsBaseType(int index) const;
458 int getLoc(int index) const;
459 int64_t getInt(int index) const;
460 Offset getUnnamedLocStart(int index) const;
462 void pushFDesc();
463 void popFDesc();
466 struct Label {
467 enum class NoEntryNopFlag {};
468 Label() : m_off(InvalidAbsoluteOffset) {}
469 explicit Label(Emitter& e) : m_off(InvalidAbsoluteOffset) {
470 set(e, true);
472 Label(Emitter& e, NoEntryNopFlag) : m_off(InvalidAbsoluteOffset) {
473 set(e, false);
475 Offset getAbsoluteOffset() const { return m_off; }
476 // Sets the Label to the bytecode offset of given by e,
477 // fixes up any instructions that have already been
478 // emitted that reference this Label, and fixes up the
479 // EmitterVisitor's jump target info
480 void set(Emitter& e, bool emitNopAtEntry = true);
481 // If a Label is has not been set, it is the Emitter's
482 // resposibility to call bind on the Label each time it
483 // prepares to emit an instruction that uses the Label
484 void bind(EmitterVisitor& ev, Offset instrAddr, Offset offAddr);
485 bool isSet() { return m_off != InvalidAbsoluteOffset; }
486 bool isUsed();
487 private:
488 Offset m_off;
489 std::vector<std::pair<Offset, Offset> > m_emittedOffs;
490 // m_evalStack is used to store the eval stack of the
491 // first forward jump we see that references this label
492 SymbolicStack m_evalStack;
495 struct Thunklet {
496 virtual ~Thunklet();
497 virtual void emit(Emitter& e) = 0;
500 struct Funclet {
501 explicit Funclet(Thunklet* body)
502 : m_body(body) {
504 Thunklet* m_body;
505 Label m_entry;
508 DECLARE_BOOST_TYPES(ControlTarget);
510 * The structure represents a code path that potentially requires
511 * running finally blocks. A code path has an assigned state ID that
512 * is used inside switch statements emitted at the end of finally
513 * blocks. It also has an optional label (the destination to jump
514 * to after all the required finally blocks are run).
516 struct ControlTarget {
517 static const int k_unsetState;
518 explicit ControlTarget(EmitterVisitor* router);
519 ~ControlTarget();
520 // Manage state ID reuse.
521 bool isRegistered();
522 EmitterVisitor* m_visitor;
523 // The target to jump to once all the necessary finally blocks are run.
524 Label m_label;
525 // The state ID that identifies this control target inside finally
526 // epilogues. This ID assigned to the "state" unnamed local variable.
527 int m_state;
530 struct ControlTargetInfo {
531 ControlTargetInfo() : used(false) {}
532 ControlTargetInfo(ControlTargetPtr t, bool b) : target(t), used(b) {}
533 ControlTargetPtr target;
534 bool used;
537 DECLARE_BOOST_TYPES(Region);
540 * Region represents a single level of the unified stack
541 * of constructs that are meaningful from the point of view of finally
542 * implementation. The levels are used to keep track of the information
543 * such as the control targets that can be taken inside a block.
545 struct Region {
546 enum Kind {
547 // Top-level (global) context.
548 Global,
549 // Function body / method body entry.
550 FuncBody,
551 // Entry for finally fault funclets emitted after the body of
552 // a function
553 FaultFunclet,
554 // Region by a finally clause
555 TryFinally,
556 // Finally block entry (begins after catches ends after finally)
557 Finally,
558 // Loop or switch statement.
559 LoopOrSwitch,
562 typedef Emitter::IterPair IterPair;
563 typedef std::vector<IterPair> IterVec;
565 Region(Region::Kind kind, RegionPtr parent);
567 // Helper for establishing the maximal depth of break / continue
568 // control targets that are allocated.
569 int getBreakContinueDepth();
571 // Returns the maximal break / continue depth admissable (aka the
572 // number of nested loops).
573 int getMaxBreakContinueDepth();
575 int getMaxState();
577 // The number of cases to be emitted. This is a helper used in
578 // establishing whether one of the optimized cases can be used.
579 int getCaseCount();
581 bool isForeach() { return m_iterId != -1; }
582 bool isTryFinally() { return m_kind == Region::Kind::TryFinally; }
583 bool isFinally() { return m_kind == Region::Kind::Finally; }
585 bool isBreakUsed(int i) {
586 auto it = m_breakTargets.find(i);
587 if (it == m_breakTargets.end()) return false;
588 return it->second.used;
591 bool isContinueUsed(int i) {
592 auto it = m_continueTargets.find(i);
593 if (it == m_continueTargets.end()) return false;
594 return it->second.used;
597 Region::Kind m_kind;
598 // Only used for loop / break kind of entries.
599 Id m_iterId;
600 IterKind m_iterKind;
601 // Because of a bug in code emission, functions sometimes have
602 // inconsistent return flavors. Therefore instead of a single
603 // return control target, there need to be one return control
604 // target per flavor used. Once the bug is removed, this code
605 // can be simplified.
606 std::map<char, ControlTargetInfo> m_returnTargets;
607 // Break and continue control targets identified by their depth.
608 std::map<int, ControlTargetInfo> m_breakTargets;
609 std::map<int, ControlTargetInfo> m_continueTargets;
610 // Goto control targets. Each goto control target is identified
611 // by the name of the destination label.
612 std::map<StringData*, ControlTargetInfo, string_data_lt> m_gotoTargets;
613 // A set of goto labels occurrning inside the statement represented
614 // by this entry. This value is used for establishing whether
615 // a finally block needs to be executed when performing gotos.
616 std::set<StringData*, string_data_lt> m_gotoLabels;
617 // The label denoting the beginning of a finally block inside the
618 // current try. Only used when the entry kind is a try statement.
619 Label m_finallyLabel;
620 // The parent entry.
621 RegionPtr m_parent;
624 struct EmitterVisitor {
625 friend struct UnsetUnnamedLocalThunklet;
626 friend struct FuncFinisher;
627 public:
628 typedef std::vector<int> IndexChain;
629 typedef std::pair<ExpressionPtr, IndexChain> IndexPair;
630 typedef Emitter::IterPair IterPair;
631 typedef std::vector<IterPair> IterVec;
633 explicit EmitterVisitor(UnitEmitter& ue);
634 ~EmitterVisitor();
636 bool visit(ConstructPtr c);
637 void visitKids(ConstructPtr c);
638 void visit(FileScopePtr file);
639 void assignLocalVariableIds(FunctionScopePtr fs);
640 void assignFinallyVariableIds();
641 void fixReturnType(Emitter& e, FunctionCallPtr fn,
642 Func* builtinFunc = nullptr);
644 void listAssignmentVisitLHS(Emitter& e, ExpressionPtr exp,
645 IndexChain& indexChain,
646 std::vector<IndexPair>& chainList);
647 void listAssignmentAssignElements(Emitter& e,
648 std::vector<IndexPair>& indexChains,
649 std::function<void()> emitSrc);
651 void visitIfCondition(ExpressionPtr cond, Emitter& e, Label& tru, Label& fals,
652 bool truFallthrough);
653 const SymbolicStack& getEvalStack() const { return m_evalStack; }
654 SymbolicStack& getEvalStack() { return m_evalStack; }
655 void setEvalStack(const SymbolicStack& es) {
656 m_evalStack = es;
657 m_evalStackIsUnknown = false;
659 bool evalStackIsUnknown() { return m_evalStackIsUnknown; }
660 void popEvalStack(char symFlavor);
661 void popSymbolicLocal(Op opcode);
662 void popEvalStackMMany();
663 void popEvalStackMany(int len, char symFlavor);
664 void popEvalStackCVMany(int len);
665 void pushEvalStack(char symFlavor);
666 void peekEvalStack(char symFlavor, int depthActual);
667 void pokeEvalStack(char symFlavor, int depthActual);
668 void prepareEvalStack();
669 void recordJumpTarget(Offset target, const SymbolicStack& evalStack);
670 void recordJumpTarget(Offset target) {
671 recordJumpTarget(target, m_evalStack);
673 void restoreJumpTargetEvalStack();
674 void recordCall();
675 bool isJumpTarget(Offset target);
676 void setPrevOpcode(Op op) { m_prevOpcode = op; }
677 Op getPrevOpcode() const { return m_prevOpcode; }
678 bool currentPositionIsReachable() {
679 return (m_ue.bcPos() == m_curFunc->base
680 || isJumpTarget(m_ue.bcPos())
681 || (instrFlags(getPrevOpcode()) & TF) == 0);
683 FuncEmitter* getFuncEmitter() { return m_curFunc; }
684 Id getStateLocal() {
685 assert(m_stateLocal >= 0);
686 return m_stateLocal;
688 Id getRetLocal() {
689 assert(m_retLocal >= 0);
690 return m_retLocal;
693 struct IncludeTimeFatalException : Exception {
694 ConstructPtr m_node;
695 bool m_parseFatal;
696 IncludeTimeFatalException(ConstructPtr node, const char* fmt, ...)
697 : Exception(), m_node(node), m_parseFatal(false) {
698 va_list ap; va_start(ap, fmt); format(fmt, ap); va_end(ap);
700 EXCEPTION_COMMON_IMPL(IncludeTimeFatalException);
701 void setParseFatal(bool b = true) { m_parseFatal = b; }
704 void pushIterScope(Id id, IterKind kind) {
705 m_pendingIters.emplace_back(id, kind);
707 void popIterScope() { m_pendingIters.pop_back(); }
709 private:
710 typedef std::pair<StringData*, bool> ClosureUseVar; // (name, byRef)
711 typedef std::vector<ClosureUseVar> ClosureUseVarVec;
712 typedef std::vector<std::pair<Id,IterKind> > PendingIterVec;
713 typedef std::pair<StringData*, ExpressionPtr> NonScalarPair;
714 typedef std::vector<NonScalarPair> NonScalarVec;
715 typedef std::pair<Id, int> StrCase;
717 struct PostponedMeth {
718 PostponedMeth(MethodStatementPtr m, FuncEmitter* fe, bool top,
719 ClosureUseVarVec* useVars)
720 : m_meth(m), m_fe(fe), m_top(top), m_closureUseVars(useVars) {}
721 MethodStatementPtr m_meth;
722 FuncEmitter* m_fe;
723 bool m_top;
724 ClosureUseVarVec* m_closureUseVars;
727 struct PostponedCtor {
728 PostponedCtor(InterfaceStatementPtr is, FuncEmitter* fe)
729 : m_is(is), m_fe(fe) {}
730 InterfaceStatementPtr m_is;
731 FuncEmitter* m_fe;
734 struct PostponedNonScalars {
735 PostponedNonScalars(InterfaceStatementPtr is, FuncEmitter* fe,
736 NonScalarVec* v)
737 : m_is(is), m_fe(fe), m_vec(v) {}
738 void release() {
739 delete m_vec;
741 InterfaceStatementPtr m_is;
742 FuncEmitter* m_fe;
743 NonScalarVec* m_vec;
746 struct PostponedClosureCtor {
747 PostponedClosureCtor(ClosureUseVarVec& v, ClosureExpressionPtr e,
748 FuncEmitter* fe)
749 : m_useVars(v), m_expr(e), m_fe(fe) {}
750 ClosureUseVarVec m_useVars;
751 ClosureExpressionPtr m_expr;
752 FuncEmitter* m_fe;
755 struct CatchRegion {
756 CatchRegion(Offset start, Offset end) : m_start(start),
757 m_end(end) {}
758 ~CatchRegion() {
759 for (std::vector<std::pair<StringData*, Label*> >::const_iterator it =
760 m_catchLabels.begin(); it != m_catchLabels.end(); it++) {
761 delete it->second;
764 Offset m_start;
765 Offset m_end;
766 std::set<StringData*, string_data_lt> m_names;
767 std::vector<std::pair<StringData*, Label*> > m_catchLabels;
770 struct FaultRegion {
771 FaultRegion(Offset start,
772 Offset end,
773 Label* func,
774 Id iterId,
775 IterKind kind)
776 : m_start(start)
777 , m_end(end)
778 , m_func(func)
779 , m_iterId(iterId)
780 , m_iterKind(kind) {}
782 Offset m_start;
783 Offset m_end;
784 Label* m_func;
785 Id m_iterId;
786 IterKind m_iterKind;
789 struct FPIRegion {
790 FPIRegion(Offset start, Offset end, Offset fpOff)
791 : m_start(start), m_end(end), m_fpOff(fpOff) {}
792 Offset m_start;
793 Offset m_end;
794 Offset m_fpOff;
797 struct SwitchState {
798 SwitchState() : nonZeroI(-1), defI(-1) {}
800 SwitchState(const SwitchState&) = delete;
801 SwitchState& operator=(const SwitchState&) = delete;
803 std::map<int64_t, int> cases; // a map from int (or litstr id) to case index
804 std::vector<StrCase> caseOrder; // for string switches, a list of the
805 // <litstr id, case index> in the order
806 // they appear in the source
807 int nonZeroI;
808 int defI;
811 void allocPipeLocal(Id pipeVar) { m_pipeVars.emplace(pipeVar); }
812 void releasePipeLocal(Id pipeVar) {
813 assert(!m_pipeVars.empty() && m_pipeVars.top() == pipeVar);
814 m_pipeVars.pop();
816 folly::Optional<Id> getPipeLocal() {
817 if (m_pipeVars.empty()) return folly::none;
818 return m_pipeVars.top();
821 private:
822 static constexpr size_t kMinStringSwitchCases = 8;
824 UnitEmitter& m_ue;
825 FuncEmitter* m_curFunc;
826 FileScopePtr m_file;
828 Op m_prevOpcode;
830 std::deque<PostponedMeth> m_postponedMeths;
831 std::deque<PostponedCtor> m_postponedCtors;
832 std::deque<PostponedNonScalars> m_postponedPinits;
833 std::deque<PostponedNonScalars> m_postponedSinits;
834 std::deque<PostponedNonScalars> m_postponedCinits;
835 std::deque<PostponedClosureCtor> m_postponedClosureCtors;
836 PendingIterVec m_pendingIters;
837 hphp_hash_map<std::string, FuncEmitter*> m_topMethodEmitted;
838 SymbolicStack m_evalStack;
839 bool m_evalStackIsUnknown;
840 hphp_hash_map<Offset, SymbolicStack> m_jumpTargetEvalStacks;
841 typedef tbb::concurrent_hash_map<const StringData*, int,
842 StringDataHashICompare> EmittedClosures;
843 static EmittedClosures s_emittedClosures;
844 std::deque<Funclet*> m_funclets;
845 std::map<StatementPtr, Funclet*> m_memoizedFunclets;
846 std::deque<CatchRegion*> m_catchRegions;
847 std::deque<FaultRegion*> m_faultRegions;
848 std::deque<FPIRegion*> m_fpiRegions;
849 std::vector<Array> m_staticArrays;
850 std::vector<folly::Optional<HeaderKind>> m_staticColType;
851 std::set<std::string,stdltistr> m_hoistables;
852 OptLocation m_tempLoc;
853 std::unordered_set<std::string> m_staticEmitted;
855 // The stack of all Regions that this EmitterVisitor is currently inside
856 std::vector<RegionPtr> m_regions;
857 // The state IDs currently allocated for the "finally router" logic.
858 // See FIXME above the registerControlTarget() method.
859 std::set<int> m_states;
860 // Unnamed local variables used by the "finally router" logic
861 Id m_stateLocal;
862 Id m_retLocal;
863 // stack of nested unnamed pipe variables
864 std::stack<Id> m_pipeVars;
866 public:
867 bool checkIfStackEmpty(const char* forInstruction) const;
868 void unexpectedStackSym(char sym, const char* where) const;
870 int scanStackForLocation(int iLast);
873 * Emit bytecodes for the base and intermediate dims, returning the number of
874 * eval stack slots containing member keys that should be consumed by the
875 * final operation.
877 struct MInstrOpts {
878 explicit MInstrOpts(MOpFlags flags)
879 : allowW{flags & MOpFlags::Define}
880 , flags{flags}
883 explicit MInstrOpts(int32_t paramId)
884 : allowW{true}
885 , fpass{true}
886 , paramId{paramId}
889 MInstrOpts& rhs() {
890 rhsVal = true;
891 return *this;
894 bool allowW{false};
895 bool rhsVal{false};
896 bool fpass{false};
897 union {
898 MOpFlags flags;
899 int32_t paramId;
903 MemberKey symToMemberKey(Emitter& e, int i, bool allowW);
904 size_t emitMOp(int iFirst, int& iLast, Emitter& e, MInstrOpts opts);
905 void emitQueryMOp(int iFirst, int iLast, Emitter& e, QueryMOp op);
907 enum class PassByRefKind {
908 AllowCell,
909 WarnOnCell,
910 ErrorOnCell,
912 PassByRefKind getPassByRefKind(ExpressionPtr exp);
913 void emitCall(Emitter& e, FunctionCallPtr func,
914 ExpressionListPtr params, Offset fpiStart);
915 void emitAGet(Emitter& e);
916 void emitCGetL2(Emitter& e);
917 void emitCGetL3(Emitter& e);
918 void emitPushL(Emitter& e);
919 void emitCGet(Emitter& e);
920 void emitCGetQuiet(Emitter& e);
921 bool emitVGet(Emitter& e, bool skipCells = false);
922 void emitIsset(Emitter& e);
923 void emitIsType(Emitter& e, IsTypeOp op);
924 void emitEmpty(Emitter& e);
925 void emitUnset(Emitter& e, ExpressionPtr exp = ExpressionPtr());
926 void emitVisitAndUnset(Emitter& e, ExpressionPtr exp);
927 void emitSet(Emitter& e);
928 void emitSetOp(Emitter& e, int op);
929 void emitBind(Emitter& e);
930 void emitIncDec(Emitter& e, IncDecOp cop);
931 void emitPop(Emitter& e);
932 void emitConvertToCell(Emitter& e);
933 void emitConvertToCellIfVar(Emitter& e);
934 void emitConvertToCellOrLoc(Emitter& e);
935 void emitConvertSecondToCell(Emitter& e);
936 void emitConvertToVar(Emitter& e);
937 void emitFPass(Emitter& e, int paramID, PassByRefKind passByRefKind);
938 void emitVirtualLocal(int localId);
939 template<class Expr> void emitVirtualClassBase(Emitter&, Expr* node);
940 void emitResolveClsBase(Emitter& e, int pos);
941 void emitClsIfSPropBase(Emitter& e);
942 Id emitVisitAndSetUnnamedL(Emitter& e, ExpressionPtr exp);
943 Id emitSetUnnamedL(Emitter& e);
944 void emitPushAndFreeUnnamedL(Emitter& e, Id tempLocal, Offset start);
945 MaybeDataType analyzeSwitch(SwitchStatementPtr s, SwitchState& state);
946 void emitIntegerSwitch(Emitter& e, SwitchStatementPtr s,
947 std::vector<Label>& caseLabels, Label& done,
948 const SwitchState& state);
949 void emitStringSwitch(Emitter& e, SwitchStatementPtr s,
950 std::vector<Label>& caseLabels, Label& done,
951 const SwitchState& state);
952 void emitArrayInit(Emitter& e, ExpressionListPtr el,
953 folly::Optional<HeaderKind> ct = folly::none);
954 void emitPairInit(Emitter&e, ExpressionListPtr el);
955 void emitVectorInit(Emitter&e, CollectionType ct, ExpressionListPtr el);
956 void emitMapInit(Emitter&e, CollectionType ct, ExpressionListPtr el);
957 void emitSetInit(Emitter&e, CollectionType ct, ExpressionListPtr el);
958 void emitCollectionInit(Emitter& e, BinaryOpExpressionPtr exp);
959 void markElem(Emitter& e);
960 void markNewElem(Emitter& e);
961 void markProp(Emitter& e, PropAccessType propAccessType);
962 void markSProp(Emitter& e);
963 void markName(Emitter& e);
964 void markNameSecond(Emitter& e);
965 void markGlobalName(Emitter& e);
967 void emitNameString(Emitter& e, ExpressionPtr n, bool allowLiteral = false);
968 void emitAssignment(Emitter& e, ExpressionPtr c, int op, bool bind);
969 void emitListAssignment(Emitter& e, ListAssignmentPtr lst);
970 void postponeMeth(MethodStatementPtr m, FuncEmitter* fe, bool top,
971 ClosureUseVarVec* useVars = nullptr);
972 void postponeCtor(InterfaceStatementPtr m, FuncEmitter* fe);
973 void postponePinit(InterfaceStatementPtr m, FuncEmitter* fe, NonScalarVec* v);
974 void postponeSinit(InterfaceStatementPtr m, FuncEmitter* fe, NonScalarVec* v);
975 void postponeCinit(InterfaceStatementPtr m, FuncEmitter* fe, NonScalarVec* v);
976 void emitPostponedMeths();
977 void bindUserAttributes(MethodStatementPtr meth,
978 FuncEmitter *fe);
979 void bindNativeFunc(MethodStatementPtr meth, FuncEmitter *fe);
980 int32_t emitNativeOpCodeImpl(MethodStatementPtr meth,
981 const char* funcName,
982 const char* className,
983 FuncEmitter* fe);
984 void emitMethodMetadata(MethodStatementPtr meth,
985 ClosureUseVarVec* useVars,
986 bool top);
987 void fillFuncEmitterParams(FuncEmitter* fe,
988 ExpressionListPtr params,
989 bool coerce_params = false);
990 void emitMethodPrologue(Emitter& e, MethodStatementPtr meth);
991 void emitDeprecationWarning(Emitter& e, MethodStatementPtr meth);
992 void emitMethod(MethodStatementPtr meth);
993 void emitMemoizeProp(Emitter& e, MethodStatementPtr meth, Id localID,
994 const std::vector<Id>& paramIDs, uint32_t numParams);
995 void addMemoizeProp(MethodStatementPtr meth);
996 void emitMemoizeMethod(MethodStatementPtr meth, const StringData* methName);
997 void emitConstMethodCallNoParams(Emitter& e, const std::string& name);
998 bool emitInlineGen(Emitter& e, const ExpressionPtr&);
999 bool emitInlineGena(Emitter& e, const SimpleFunctionCallPtr& call);
1000 bool emitInlineGenva(Emitter& e, const SimpleFunctionCallPtr& call);
1001 bool emitInlineHHAS(Emitter& e, SimpleFunctionCallPtr);
1002 bool emitHHInvariant(Emitter& e, SimpleFunctionCallPtr);
1003 void emitMethodDVInitializers(Emitter& e,
1004 MethodStatementPtr& meth,
1005 Label& topOfBody);
1006 void emitPostponedCtors();
1007 void emitPostponedPSinit(PostponedNonScalars& p, bool pinit);
1008 void emitPostponedPinits();
1009 void emitPostponedSinits();
1010 void emitPostponedCinits();
1011 void emitPostponedClosureCtors();
1012 enum CallUserFuncFlags {
1013 CallUserFuncNone = -1,
1014 CallUserFuncPlain = 0,
1015 CallUserFuncArray = 1,
1016 CallUserFuncSafe = 2,
1017 CallUserFuncReturn = 4,
1018 CallUserFuncForward = 8,
1019 CallUserFuncSafeArray = CallUserFuncSafe | CallUserFuncArray,
1020 CallUserFuncSafeReturn = CallUserFuncSafe | CallUserFuncReturn,
1021 CallUserFuncForwardArray = CallUserFuncForward | CallUserFuncArray
1024 bool emitSystemLibVarEnvFunc(Emitter& e, SimpleFunctionCallPtr node);
1025 bool emitCallUserFunc(Emitter& e, SimpleFunctionCallPtr node);
1026 Func* canEmitBuiltinCall(const std::string& name, int numParams);
1027 void emitFuncCall(Emitter& e, FunctionCallPtr node,
1028 const char* nameOverride = nullptr,
1029 ExpressionListPtr paramsOverride = nullptr);
1030 bool emitConstantFuncCall(Emitter& e, SimpleFunctionCallPtr call);
1031 void emitFuncCallArg(Emitter& e, ExpressionPtr exp, int paramId,
1032 bool isUnpack);
1033 bool emitBuiltinCallArg(Emitter& e, ExpressionPtr exp, int paramId,
1034 bool byRef, bool mustBeRef);
1035 bool emitScalarValue(Emitter& e, const Variant& value);
1036 void emitLambdaCaptureArg(Emitter& e, ExpressionPtr exp);
1037 void emitBuiltinDefaultArg(Emitter& e, Variant& v,
1038 MaybeDataType t, int paramId);
1039 void emitClass(Emitter& e, ClassScopePtr cNode, bool topLevel);
1040 Id emitTypedef(Emitter& e, TypedefStatementPtr);
1041 void emitForeachListAssignment(Emitter& e,
1042 ListAssignmentPtr la,
1043 std::function<void()> emitSrc);
1044 void emitForeach(Emitter& e, ForEachStatementPtr fe);
1045 void emitForeachAwaitAs(Emitter& e, ForEachStatementPtr fe);
1046 void emitRestoreErrorReporting(Emitter& e, Id oldLevelLoc);
1047 void emitMakeUnitFatal(Emitter& e,
1048 const char* msg,
1049 FatalOp k = FatalOp::Runtime);
1051 // Emits a Jmp or IterBreak instruction to the specified target, freeing
1052 // the specified iterator variables. emitJump() cannot be used to leave a
1053 // try region, except if it jumps to the m_finallyLabel of the try region.
1054 void emitJump(Emitter& e, IterVec& iters, Label& target);
1056 // These methods handle the return, break, continue, and goto operations.
1057 // These methods are aware of try/finally blocks and foreach blocks and
1058 // will free iterators and jump to finally epilogues as appropriate.
1059 void emitReturn(Emitter& e, char sym, StatementPtr s);
1060 void emitBreak(Emitter& e, int depth, StatementPtr s);
1061 void emitContinue(Emitter& e, int depth, StatementPtr s);
1062 void emitGoto(Emitter& e, StringData* name, StatementPtr s);
1064 void emitYieldFrom(Emitter& e, ExpressionPtr exp);
1066 // Helper methods for emitting IterFree instructions
1067 void emitIterFree(Emitter& e, IterVec& iters);
1068 void emitIterFreeForReturn(Emitter& e);
1070 // A "finally epilogue" is a blob of bytecode that comes after an inline
1071 // copy of a "finally" clause body. Finally epilogues are used to ensure
1072 // that that the bodies of finally clauses are executed whenever a return,
1073 // break, continue, or goto operation jumps out of their corresponding
1074 // "try" blocks.
1075 void emitFinallyEpilogue(Emitter& e, Region* entry);
1076 void emitReturnTrampoline(Emitter& e, Region* entry,
1077 std::vector<Label*>& cases, char sym);
1078 void emitBreakTrampoline(Emitter& e, Region* entry,
1079 std::vector<Label*>& cases, int depth);
1080 void emitContinueTrampoline(Emitter& e, Region* entry,
1081 std::vector<Label*>& cases, int depth);
1082 void emitGotoTrampoline(Emitter& e, Region* entry,
1083 std::vector<Label*>& cases, StringData* name);
1085 // Returns true if VerifyRetType should be emitted before Ret for
1086 // the current function.
1087 bool shouldEmitVerifyRetType();
1089 Funclet* addFunclet(Thunklet* body);
1090 Funclet* addFunclet(StatementPtr stmt,
1091 Thunklet* body);
1092 Funclet* getFunclet(StatementPtr stmt);
1093 void emitFunclets(Emitter& e);
1095 struct FaultIterInfo {
1096 Id iterId;
1097 IterKind kind;
1100 void newFaultRegion(Offset start,
1101 Offset end,
1102 Label* entry,
1103 FaultIterInfo = FaultIterInfo { -1, KindOfIter });
1104 void newFaultRegion(StatementPtr stmt,
1105 Offset start,
1106 Offset end,
1107 Label* entry,
1108 FaultIterInfo = FaultIterInfo { -1, KindOfIter });
1109 void
1110 newFaultRegionAndFunclet(Offset start,
1111 Offset end,
1112 Thunklet* t,
1113 FaultIterInfo = FaultIterInfo { -1, KindOfIter });
1114 void
1115 newFaultRegionAndFunclet(StatementPtr stmt,
1116 Offset start,
1117 Offset end,
1118 Thunklet* t,
1119 FaultIterInfo = FaultIterInfo { -1, KindOfIter });
1121 void newFPIRegion(Offset start, Offset end, Offset fpOff);
1122 void copyOverCatchAndFaultRegions(FuncEmitter* fe);
1123 void copyOverFPIRegions(FuncEmitter* fe);
1124 void saveMaxStackCells(FuncEmitter* fe, int32_t stackPad);
1125 void finishFunc(Emitter& e, FuncEmitter* fe, int32_t stackPad);
1126 void initScalar(TypedValue& tvVal, ExpressionPtr val,
1127 folly::Optional<HeaderKind> ct = folly::none);
1128 bool requiresDeepInit(ExpressionPtr initExpr) const;
1130 void emitClassTraitPrecRule(PreClassEmitter* pce, TraitPrecStatementPtr rule);
1131 void emitClassTraitAliasRule(PreClassEmitter* pce,
1132 TraitAliasStatementPtr rule);
1133 void emitClassUseTrait(PreClassEmitter* pce, UseTraitStatementPtr useStmt);
1135 // Helper function for creating entries.
1136 RegionPtr createRegion(StatementPtr s, Region::Kind kind);
1137 // Enter/leave the passed in entry. Note that entries sometimes need be
1138 // to be constructed before they are entered, or need to be accessed
1139 // after they are left. This especially applies to constructs such
1140 // as loops and try blocks.
1141 void enterRegion(RegionPtr);
1142 void leaveRegion(RegionPtr);
1144 // Functions used for handling state IDs allocation.
1145 // FIXME (#3275259): This should be moved into global / func
1146 // body / fault funclet entries in order to optimize state
1147 // allocation. See the task description for more details.
1148 void registerControlTarget(ControlTarget* t);
1149 void unregisterControlTarget(ControlTarget* t);
1151 void registerReturn(StatementPtr s, Region* entry, char sym);
1152 void registerYieldAwait(ExpressionPtr e);
1153 ControlTargetPtr registerBreak(StatementPtr s, Region* entry, int depth,
1154 bool alloc);
1155 ControlTargetPtr registerContinue(StatementPtr s, Region* entry, int depth,
1156 bool alloc);
1157 ControlTargetPtr registerGoto(StatementPtr s, Region* entry,
1158 StringData* name, bool alloc);
1161 //=============================================================================
1162 // Emitter.
1164 #define InvariantViolation(...) do { \
1165 Logger::Warning(__VA_ARGS__); \
1166 Logger::Warning("Eval stack at the time of error: %s", \
1167 m_evalStack.pretty().c_str()); \
1168 assertx(false); \
1169 } while (0)
1172 * RAII guard for function creation.
1174 * This ensures that the eval stack's high water pointer is pointing
1175 * to the current function's maxStackCells (needed before we start
1176 * emitting bytecodes that manipulate the stack), and also that we
1177 * properly finish the function at the end of the scope.
1179 struct FuncFinisher {
1180 FuncFinisher(EmitterVisitor* ev, Emitter& e, FuncEmitter* fe,
1181 int32_t stackPad = 0)
1182 : m_ev(ev), m_e(e), m_fe(fe), m_stackPad(stackPad)
1184 assert(!ev->m_evalStack.m_actualStackHighWaterPtr ||
1185 ev->m_evalStack.m_actualStackHighWaterPtr == &fe->maxStackCells);
1186 ev->m_evalStack.m_actualStackHighWaterPtr = &fe->maxStackCells;
1189 ~FuncFinisher() {
1190 m_ev->finishFunc(m_e, m_fe, m_stackPad);
1193 void setStackPad(int32_t stackPad) {
1194 m_stackPad = stackPad;
1196 private:
1197 EmitterVisitor* m_ev;
1198 Emitter& m_e;
1199 FuncEmitter* m_fe;
1200 int32_t m_stackPad;
1203 // RAII guard for temporarily overriding an Emitter's location
1204 struct LocationGuard {
1205 LocationGuard(Emitter& e, const OptLocation& newLoc)
1206 : m_e(e), m_loc(e.getTempLocation()) {
1207 if (newLoc) m_e.setTempLocation(newLoc);
1209 ~LocationGuard() {
1210 m_e.setTempLocation(m_loc);
1213 private:
1214 Emitter& m_e;
1215 OptLocation m_loc;
1218 #define O(name, imm, pop, push, flags) \
1219 void Emitter::name(imm) { \
1220 auto const opcode = Op::name; \
1221 ITRACE(2, "{}\n", #name); \
1222 Trace::Indent indent; \
1223 ITRACE(3, "before: {}\n", m_ev.getEvalStack().pretty()); \
1224 /* Process opcode's effects on the EvalStack and emit it */ \
1225 Offset curPos UNUSED = getUnitEmitter().bcPos(); \
1227 Trace::Indent indent; \
1228 getEmitterVisitor().prepareEvalStack(); \
1229 char idxAPop UNUSED; \
1230 POP_##pop; \
1231 const int nIn UNUSED = COUNT_##pop; \
1232 POP_LA_##imm; \
1233 PUSH_##push; \
1234 getUnitEmitter().emitOp(Op##name); \
1235 IMPL_##imm; \
1237 ITRACE(3, "after: {}\n", m_ev.getEvalStack().pretty()); \
1238 auto& loc = m_tempLoc ? *m_tempLoc : m_node->getRange(); \
1239 auto UNUSED pc = m_ue.bc() + curPos; \
1240 ITRACE(2, "lines [{},{}] chars [{},{}]\n", \
1241 loc.line0, loc.line1, loc.char0, loc.char1); \
1242 /* Update various other metadata */ \
1243 getUnitEmitter().recordSourceLocation(loc, curPos); \
1244 if (flags & TF) { \
1245 getEmitterVisitor().restoreJumpTargetEvalStack(); \
1246 ITRACE(3, " jmp: {}\n", m_ev.getEvalStack().pretty()); \
1248 if (opcode == Op::FCall) getEmitterVisitor().recordCall(); \
1249 getEmitterVisitor().setPrevOpcode(opcode); \
1252 #define COUNT_NOV 0
1253 #define COUNT_ONE(t) 1
1254 #define COUNT_TWO(t1,t2) 2
1255 #define COUNT_THREE(t1,t2,t3) 3
1256 #define COUNT_FOUR(t1,t2,t3,t4) 4
1257 #define COUNT_MFINAL 0
1258 #define COUNT_F_MFINAL 0
1259 #define COUNT_C_MFINAL 0
1260 #define COUNT_V_MFINAL 0
1261 #define COUNT_FMANY 0
1262 #define COUNT_CVUMANY 0
1263 #define COUNT_CMANY 0
1264 #define COUNT_SMANY 0
1265 #define COUNT_IDX_A 0
1267 #define ONE(t) \
1268 DEC_##t a1
1269 #define TWO(t1, t2) \
1270 DEC_##t1 a1, DEC_##t2 a2
1271 #define THREE(t1, t2, t3) \
1272 DEC_##t1 a1, DEC_##t2 a2, DEC_##t3 a3
1273 #define FOUR(t1, t2, t3, t4) \
1274 DEC_##t1 a1, DEC_##t2 a2, DEC_##t3 a3, DEC_##t4 a4
1275 #define NA
1276 #define DEC_BLA std::vector<Label*>&
1277 #define DEC_SLA std::vector<StrOff>&
1278 #define DEC_ILA std::vector<IterPair>&
1279 #define DEC_IVA int32_t
1280 #define DEC_LA int32_t
1281 #define DEC_IA int32_t
1282 #define DEC_I64A int64_t
1283 #define DEC_DA double
1284 #define DEC_SA const StringData*
1285 #define DEC_RATA RepoAuthType
1286 #define DEC_AA ArrayData*
1287 #define DEC_BA Label&
1288 #define DEC_OA(type) type
1289 #define DEC_VSA std::vector<std::string>&
1290 #define DEC_KA MemberKey
1292 #define POP_NOV
1293 #define POP_ONE(t) \
1294 POP_##t(0)
1295 #define POP_TWO(t1, t2) \
1296 POP_##t1(0); \
1297 POP_##t2(1)
1298 #define POP_THREE(t1, t2, t3) \
1299 POP_##t1(0); \
1300 POP_##t2(1); \
1301 POP_##t3(2)
1302 #define POP_FOUR(t1, t2, t3, t4) \
1303 POP_##t1(0); \
1304 POP_##t2(1); \
1305 POP_##t3(2); \
1306 POP_##t4(3)
1307 #define POP_MFINAL \
1308 getEmitterVisitor().popEvalStackMMany()
1309 #define POP_F_MFINAL POP_MFINAL
1310 #define POP_C_MFINAL \
1311 getEmitterVisitor().popEvalStack(StackSym::C); \
1312 getEmitterVisitor().popEvalStackMMany()
1313 #define POP_V_MFINAL \
1314 getEmitterVisitor().popEvalStack(StackSym::V); \
1315 getEmitterVisitor().popEvalStackMMany()
1316 #define POP_FMANY \
1317 getEmitterVisitor().popEvalStackMany(a1, StackSym::F)
1318 #define POP_CVUMANY \
1319 getEmitterVisitor().popEvalStackCVMany(a1)
1320 #define POP_CMANY \
1321 getEmitterVisitor().popEvalStackMany(a1, StackSym::C)
1322 #define POP_SMANY \
1323 getEmitterVisitor().popEvalStackMany(a1.size(), StackSym::C)
1324 #define POP_IDX_A \
1325 idxAPop = getEmitterVisitor().getEvalStack().top(); \
1326 if (a2 == 1) getEmitterVisitor().popEvalStackCVMany(1); \
1327 getEmitterVisitor().popEvalStack(StackSym::A)
1329 #define POP_CV(i) getEmitterVisitor().popEvalStack(StackSym::C)
1330 #define POP_VV(i) getEmitterVisitor().popEvalStack(StackSym::V)
1331 #define POP_AV(i) getEmitterVisitor().popEvalStack(StackSym::A)
1332 #define POP_RV(i) getEmitterVisitor().popEvalStack(StackSym::R)
1333 #define POP_FV(i) getEmitterVisitor().popEvalStack(StackSym::F)
1335 // Pop of virtual "locs" on the stack that turn into immediates.
1336 #define POP_LA_ONE(t) \
1337 POP_LA_##t(nIn)
1338 #define POP_LA_TWO(t1, t2) \
1339 POP_LA_##t1(nIn); \
1340 POP_LA_##t2(nIn)
1341 #define POP_LA_THREE(t1, t2, t3) \
1342 POP_LA_##t1(nIn); \
1343 POP_LA_##t2(nIn); \
1344 POP_LA_##t3(nIn)
1345 #define POP_LA_FOUR(t1, t2, t3, t4) \
1346 POP_LA_##t1(nIn); \
1347 POP_LA_##t2(nIn); \
1348 POP_LA_##t3(nIn); \
1349 POP_LA_##t4(nIn)
1351 #define POP_LA_NA
1352 #define POP_LA_BLA(i)
1353 #define POP_LA_SLA(i)
1354 #define POP_LA_ILA(i)
1355 #define POP_LA_IVA(i)
1356 #define POP_LA_IA(i)
1357 #define POP_LA_I64A(i)
1358 #define POP_LA_DA(i)
1359 #define POP_LA_SA(i)
1360 #define POP_LA_RATA(i)
1361 #define POP_LA_AA(i)
1362 #define POP_LA_BA(i)
1363 #define POP_LA_IMPL(x)
1364 #define POP_LA_OA(i) POP_LA_IMPL
1365 #define POP_LA_VSA(i)
1366 #define POP_LA_KA(i)
1368 #define POP_LA_LA(i) \
1369 getEmitterVisitor().popSymbolicLocal(opcode)
1371 #define PUSH_NOV
1372 #define PUSH_ONE(t) \
1373 PUSH_##t
1374 #define PUSH_TWO(t1, t2) \
1375 PUSH_##t2; \
1376 PUSH_##t1
1377 #define PUSH_THREE(t1, t2, t3) \
1378 PUSH_##t3; \
1379 PUSH_##t2; \
1380 PUSH_##t1
1381 #define PUSH_FOUR(t1, t2, t3, t4) \
1382 PUSH_##t4; \
1383 PUSH_##t3; \
1384 PUSH_##t2; \
1385 PUSH_##t1
1386 #define PUSH_INS_1(t) PUSH_INS_1_##t
1387 #define PUSH_INS_2(t) PUSH_INS_2_##t
1389 #define PUSH_CV getEmitterVisitor().pushEvalStack(StackSym::C)
1390 #define PUSH_UV PUSH_CV
1391 #define PUSH_CUV PUSH_CV
1392 #define PUSH_VV getEmitterVisitor().pushEvalStack(StackSym::V)
1393 #define PUSH_AV getEmitterVisitor().pushEvalStack(StackSym::A)
1394 #define PUSH_RV getEmitterVisitor().pushEvalStack(StackSym::R)
1395 #define PUSH_FV getEmitterVisitor().pushEvalStack(StackSym::F)
1397 #define PUSH_INS_1_CV \
1398 getEmitterVisitor().getEvalStack().insertAt(1, StackSym::C);
1399 #define PUSH_INS_1_AV \
1400 getEmitterVisitor().getEvalStack().insertAt(1, StackSym::A);
1402 #define PUSH_INS_2_CV \
1403 getEmitterVisitor().getEvalStack().insertAt(2, StackSym::C);
1405 #define PUSH_IDX_A \
1406 if (a2 == 1) getEmitterVisitor().pushEvalStack(idxAPop);
1408 #define IMPL_NA
1409 #define IMPL_ONE(t) \
1410 IMPL1_##t
1411 #define IMPL_TWO(t1, t2) \
1412 IMPL1_##t1; \
1413 IMPL2_##t2
1414 #define IMPL_THREE(t1, t2, t3) \
1415 IMPL1_##t1; \
1416 IMPL2_##t2; \
1417 IMPL3_##t3
1418 #define IMPL_FOUR(t1, t2, t3, t4) \
1419 IMPL1_##t1; \
1420 IMPL2_##t2; \
1421 IMPL3_##t3; \
1422 IMPL4_##t4
1424 #define IMPL_BLA(var) do { \
1425 getUnitEmitter().emitInt32(var.size()); \
1426 for (unsigned int i = 0; i < var.size(); ++i) { \
1427 IMPL_BA(*var[i]); \
1429 } while (0)
1430 #define IMPL1_BLA IMPL_BLA(a1)
1431 #define IMPL2_BLA IMPL_BLA(a2)
1432 #define IMPL3_BLA IMPL_BLA(a3)
1433 #define IMPL4_BLA IMPL_BLA(a4)
1435 #define IMPL_ILA(var) do { \
1436 auto& ue = getUnitEmitter(); \
1437 ue.emitInt32(var.size()); \
1438 for (auto& i : var) { \
1439 ue.emitInt32(i.kind); \
1440 ue.emitInt32(i.id); \
1442 } while(0)
1443 #define IMPL1_ILA IMPL_ILA(a1)
1444 #define IMPL2_ILA IMPL_ILA(a2)
1445 #define IMPL3_ILA IMPL_ILA(a3)
1446 #define IMPL4_ILA IMPL_ILA(a4)
1448 #define IMPL_SLA(var) do { \
1449 auto& ue = getUnitEmitter(); \
1450 ue.emitInt32(var.size()); \
1451 for (auto& i : var) { \
1452 ue.emitInt32(i.str); \
1453 IMPL_BA(*i.dest); \
1455 } while (0)
1456 #define IMPL1_SLA IMPL_SLA(a1)
1457 #define IMPL2_SLA IMPL_SLA(a2)
1458 #define IMPL3_SLA IMPL_SLA(a3)
1460 #define IMPL_VSA(var) do { \
1461 auto n = var.size(); \
1462 getUnitEmitter().emitInt32(n); \
1463 for (size_t i = 0; i < n; ++i) { \
1464 IMPL_SA((HPHP::String(var[i])).get()); \
1466 } while (0)
1467 #define IMPL1_VSA IMPL_VSA(a1)
1468 #define IMPL2_VSA IMPL_VSA(a2)
1469 #define IMPL3_VSA IMPL_VSA(a3)
1470 #define IMPL4_VSA IMPL_VSA(a4)
1472 #define IMPL_IVA(var) do { \
1473 getUnitEmitter().emitIVA(var); \
1474 } while (0)
1475 #define IMPL1_IVA IMPL_IVA(a1)
1476 #define IMPL2_IVA IMPL_IVA(a2)
1477 #define IMPL3_IVA IMPL_IVA(a3)
1478 #define IMPL4_IVA IMPL_IVA(a4)
1480 #define IMPL1_LA IMPL_IVA(a1)
1481 #define IMPL2_LA IMPL_IVA(a2)
1482 #define IMPL3_LA IMPL_IVA(a3)
1483 #define IMPL4_LA IMPL_IVA(a4)
1485 #define IMPL1_IA IMPL_IVA(a1)
1486 #define IMPL2_IA IMPL_IVA(a2)
1487 #define IMPL3_IA IMPL_IVA(a3)
1488 #define IMPL4_IA IMPL_IVA(a4)
1490 #define IMPL_I64A(var) getUnitEmitter().emitInt64(var)
1491 #define IMPL1_I64A IMPL_I64A(a1)
1492 #define IMPL2_I64A IMPL_I64A(a2)
1493 #define IMPL3_I64A IMPL_I64A(a3)
1494 #define IMPL4_I64A IMPL_I64A(a4)
1496 #define IMPL_SA(var) \
1497 getUnitEmitter().emitInt32(getUnitEmitter().mergeLitstr(var))
1498 #define IMPL1_SA IMPL_SA(a1)
1499 #define IMPL2_SA IMPL_SA(a2)
1500 #define IMPL3_SA IMPL_SA(a3)
1501 #define IMPL4_SA IMPL_SA(a4)
1503 // Emitting RATAs isn't supported here right now. (They're only
1504 // created in hhbbc.)
1505 #define IMPL_RATA(var) not_reached()
1506 #define IMPL1_RATA IMPL_RATA(a1)
1507 #define IMPL2_RATA IMPL_RATA(a2)
1508 #define IMPL3_RATA IMPL_RATA(a3)
1509 #define IMPL4_RATA IMPL_RATA(a4)
1511 #define IMPL_AA(var) \
1512 getUnitEmitter().emitInt32(getUnitEmitter().mergeArray(var))
1513 #define IMPL1_AA IMPL_AA(a1)
1514 #define IMPL2_AA IMPL_AA(a2)
1515 #define IMPL3_AA IMPL_AA(a3)
1516 #define IMPL4_AA IMPL_AA(a4)
1518 #define IMPL_DA(var) getUnitEmitter().emitDouble(var)
1519 #define IMPL1_DA IMPL_DA(a1)
1520 #define IMPL2_DA IMPL_DA(a2)
1521 #define IMPL3_DA IMPL_DA(a3)
1522 #define IMPL4_DA IMPL_DA(a4)
1524 #define IMPL_BA(var) \
1525 if ((var).getAbsoluteOffset() == InvalidAbsoluteOffset) { \
1526 /* For forward jumps, we store information about the */ \
1527 /* current instruction in the Label. When the Label is */ \
1528 /* set, it will fix up any instructions that reference */ \
1529 /* it, and then it will call recordJumpTarget */ \
1530 (var).bind(getEmitterVisitor(), curPos, getUnitEmitter().bcPos()); \
1531 } else { \
1532 /* For backward jumps, we simply call recordJumpTarget */ \
1533 getEmitterVisitor().recordJumpTarget((var).getAbsoluteOffset()); \
1535 getUnitEmitter().emitInt32((var).getAbsoluteOffset() - curPos);
1536 #define IMPL1_BA IMPL_BA(a1)
1537 #define IMPL2_BA IMPL_BA(a2)
1538 #define IMPL3_BA IMPL_BA(a3)
1539 #define IMPL4_BA IMPL_BA(a4)
1541 #define IMPL_OA(var) getUnitEmitter().emitByte(static_cast<uint8_t>(var))
1542 #define IMPL1_OA(type) IMPL_OA(a1)
1543 #define IMPL2_OA(type) IMPL_OA(a2)
1544 #define IMPL3_OA(type) IMPL_OA(a3)
1545 #define IMPL4_OA(type) IMPL_OA(a4)
1547 #define IMPL_KA(var) encode_member_key(var, getUnitEmitter())
1548 #define IMPL1_KA IMPL_KA(a1)
1549 #define IMPL2_KA IMPL_KA(a2)
1550 #define IMPL3_KA IMPL_KA(a3)
1551 #define IMPL4_KA IMPL_KA(a4)
1553 OPCODES
1555 #undef O
1556 #undef ONE
1557 #undef TWO
1558 #undef THREE
1559 #undef FOUR
1560 #undef NA
1561 #undef DEC_IVA
1562 #undef DEC_LA
1563 #undef DEC_IA
1564 #undef DEC_I64A
1565 #undef DEC_DA
1566 #undef DEC_SA
1567 #undef DEC_RATA
1568 #undef DEC_AA
1569 #undef DEC_BA
1570 #undef DEC_OA
1571 #undef DEC_KA
1572 #undef POP_NOV
1573 #undef POP_ONE
1574 #undef POP_TWO
1575 #undef POP_THREE
1576 #undef POP_FOUR
1577 #undef POP_MFINAL
1578 #undef POP_F_MFINAL
1579 #undef POP_C_MFINAL
1580 #undef POP_V_MFINAL
1581 #undef POP_CV
1582 #undef POP_VV
1583 #undef POP_HV
1584 #undef POP_AV
1585 #undef POP_RV
1586 #undef POP_FV
1587 #undef POP_LREST
1588 #undef POP_FMANY
1589 #undef POP_CVUMANY
1590 #undef POP_CMANY
1591 #undef POP_SMANY
1592 #undef POP_IDX_A
1593 #undef POP_LA_ONE
1594 #undef POP_LA_TWO
1595 #undef POP_LA_THREE
1596 #undef POP_LA_FOUR
1597 #undef POP_LA_NA
1598 #undef POP_LA_IVA
1599 #undef POP_LA_IA
1600 #undef POP_LA_I64A
1601 #undef POP_LA_DA
1602 #undef POP_LA_SA
1603 #undef POP_LA_RATA
1604 #undef POP_LA_AA
1605 #undef POP_LA_BA
1606 #undef POP_LA_IMPL
1607 #undef POP_LA_OA
1608 #undef POP_LA_LA
1609 #undef POP_LA_KA
1610 #undef PUSH_NOV
1611 #undef PUSH_ONE
1612 #undef PUSH_TWO
1613 #undef PUSH_THREE
1614 #undef PUSH_FOUR
1615 #undef PUSH_CV
1616 #undef PUSH_UV
1617 #undef PUSH_CUV
1618 #undef PUSH_VV
1619 #undef PUSH_HV
1620 #undef PUSH_AV
1621 #undef PUSH_RV
1622 #undef PUSH_FV
1623 #undef PUSH_IDX_A
1624 #undef IMPL_ONE
1625 #undef IMPL_TWO
1626 #undef IMPL_THREE
1627 #undef IMPL_FOUR
1628 #undef IMPL_NA
1629 #undef IMPL_BLA
1630 #undef IMPL1_BLA
1631 #undef IMPL2_BLA
1632 #undef IMPL3_BLA
1633 #undef IMPL4_BLA
1634 #undef IMPL_SLA
1635 #undef IMPL1_SLA
1636 #undef IMPL2_SLA
1637 #undef IMPL3_SLA
1638 #undef IMPL4_SLA
1639 #undef IMPL_ILA
1640 #undef IMPL1_ILA
1641 #undef IMPL2_ILA
1642 #undef IMPL3_ILA
1643 #undef IMPL4_ILA
1644 #undef IMPL_IVA
1645 #undef IMPL1_IVA
1646 #undef IMPL2_IVA
1647 #undef IMPL3_IVA
1648 #undef IMPL4_IVA
1649 #undef IMPL1_LA
1650 #undef IMPL2_LA
1651 #undef IMPL3_LA
1652 #undef IMPL4_LA
1653 #undef IMPL1_IA
1654 #undef IMPL2_IA
1655 #undef IMPL3_IA
1656 #undef IMPL4_IA
1657 #undef IMPL_I64A
1658 #undef IMPL1_I64A
1659 #undef IMPL2_I64A
1660 #undef IMPL3_I64A
1661 #undef IMPL4_I64A
1662 #undef IMPL_DA
1663 #undef IMPL1_DA
1664 #undef IMPL2_DA
1665 #undef IMPL3_DA
1666 #undef IMPL4_DA
1667 #undef IMPL_SA
1668 #undef IMPL1_SA
1669 #undef IMPL2_SA
1670 #undef IMPL3_SA
1671 #undef IMPL4_SA
1672 #undef IMPL_RATA
1673 #undef IMPL1_RATA
1674 #undef IMPL2_RATA
1675 #undef IMPL3_RATA
1676 #undef IMPL4_RATA
1677 #undef IMPL_AA
1678 #undef IMPL1_AA
1679 #undef IMPL2_AA
1680 #undef IMPL3_AA
1681 #undef IMPL4_AA
1682 #undef IMPL_BA
1683 #undef IMPL1_BA
1684 #undef IMPL2_BA
1685 #undef IMPL3_BA
1686 #undef IMPL4_BA
1687 #undef IMPL_OA
1688 #undef IMPL1_OA
1689 #undef IMPL2_OA
1690 #undef IMPL3_OA
1691 #undef IMPL4_OA
1692 #undef IMPL_KA
1693 #undef IMPL1_KA
1694 #undef IMPL2_KA
1695 #undef IMPL3_KA
1696 #undef IMPL4_KA
1698 static void checkJmpTargetEvalStack(const SymbolicStack& source,
1699 const SymbolicStack& dest) {
1700 if (source.size() != dest.size()) {
1701 Logger::FWarning("Emitter detected a point in the bytecode where the "
1702 "depth of the stack is not the same for all possible "
1703 "control flow paths. source size: {}. dest size: {}",
1704 source.size(),
1705 dest.size());
1706 Logger::Warning("src stack : %s", source.pretty().c_str());
1707 Logger::Warning("dest stack: %s", dest.pretty().c_str());
1708 assertx(false);
1709 return;
1712 for (unsigned int i = 0; i < source.size(); ++i) {
1713 char flavor = StackSym::GetSymFlavor(source.get(i));
1714 bool matches = source.get(i) == dest.get(i) &&
1715 (flavor != StackSym::L || source.getLoc(i) == dest.getLoc(i)) &&
1716 (flavor != StackSym::T || source.getName(i) == dest.getName(i)) &&
1717 (flavor != StackSym::I || source.getInt(i) == dest.getInt(i));
1718 if (!matches) {
1719 Logger::Warning("Emitter detected a point in the bytecode where the "
1720 "symbolic flavor of a slot on the stack is not the same "
1721 "for all possible control flow paths");
1722 Logger::Warning("src stack : %s", source.pretty().c_str());
1723 Logger::Warning("dest stack: %s", dest.pretty().c_str());
1724 assert(false);
1725 return;
1730 std::string SymbolicStack::SymEntry::pretty() const {
1731 std::string ret;
1732 ret += StackSym::ToString(sym);
1733 char flavor = StackSym::GetSymFlavor(sym);
1734 if (flavor == StackSym::L || flavor == StackSym::I) {
1735 folly::toAppend(':', intval, &ret);
1736 } else if (flavor == StackSym::T && name) {
1737 folly::toAppend(':', name->data(), &ret);
1739 return ret;
1742 std::string SymbolicStack::pretty() const {
1743 std::ostringstream out;
1744 out << "[" << std::hex;
1745 size_t j = 0;
1746 auto sep = "";
1747 for (size_t i = 0; i < m_symStack.size(); ++i) {
1748 out << sep;
1749 sep = " ";
1750 while (j < m_actualStack.size() && m_actualStack[j] < int(i)) {
1751 ++j;
1753 if (j < m_actualStack.size() && m_actualStack[j] == int(i)) {
1754 out << "*";
1756 out << m_symStack[i].pretty();
1758 out << ']';
1759 return out.str();
1762 void SymbolicStack::updateHighWater() {
1763 *m_actualStackHighWaterPtr =
1764 std::max(*m_actualStackHighWaterPtr,
1765 static_cast<int>(m_actualStack.size() + m_fdescCount));
1768 void SymbolicStack::push(char sym) {
1769 if (!StackSym::IsSymbolic(sym)) {
1770 m_actualStack.push_back(m_symStack.size());
1771 updateHighWater();
1773 m_symStack.push_back(SymEntry(sym));
1774 ITRACE(4, "push: {}\n", m_symStack.back().pretty());
1777 void SymbolicStack::pop() {
1778 // TODO(drew): assert eval stack unknown is false?
1779 assert(!m_symStack.empty());
1780 char sym = m_symStack.back().sym;
1781 char flavor = StackSym::GetSymFlavor(sym);
1782 if (StackSym::GetMarker(sym) != StackSym::W &&
1783 flavor != StackSym::L && flavor != StackSym::T && flavor != StackSym::I &&
1784 flavor != StackSym::H) {
1785 assert(!m_actualStack.empty());
1786 m_actualStack.pop_back();
1788 ITRACE(4, "pop: {}\n", m_symStack.back().pretty());
1789 m_symStack.pop_back();
1792 char SymbolicStack::top() const {
1793 assert(!m_symStack.empty());
1794 return m_symStack.back().sym;
1797 char SymbolicStack::get(int index) const {
1798 assert(index >= 0 && index < (int)m_symStack.size());
1799 return m_symStack[index].sym;
1802 const StringData* SymbolicStack::getName(int index) const {
1803 assert(index >= 0 && index < (int)m_symStack.size());
1804 return m_symStack[index].name;
1807 const StringData* SymbolicStack::getClsName(int index) const {
1808 assert(index >= 0 && index < (int)m_symStack.size());
1809 return m_symStack[index].className;
1812 bool SymbolicStack::isCls(int index) const {
1813 assert(index >= 0 && index < (int)m_symStack.size());
1814 return m_symStack[index].className != nullptr;
1817 void SymbolicStack::setString(const StringData* s) {
1818 assert(m_symStack.size());
1819 SymEntry& se = m_symStack.back();
1820 assert(!se.name || se.name == s);
1821 se.name = s;
1824 void SymbolicStack::setKnownCls(const StringData* s, bool nonNull) {
1825 assert(m_symStack.size());
1826 SymEntry& se = m_symStack.back();
1827 assert(!se.className || se.className == s);
1828 se.className = s;
1831 void SymbolicStack::setInt(int64_t v) {
1832 assert(m_symStack.size());
1833 m_symStack.back().intval = v;
1836 void SymbolicStack::cleanTopMeta() {
1837 SymEntry& se = m_symStack.back();
1838 se.clsBaseType = CLS_INVALID;
1839 se.name = nullptr;
1842 void SymbolicStack::setClsBaseType(ClassBaseType type) {
1843 assert(!m_symStack.empty());
1844 m_symStack.back().clsBaseType = type;
1847 void SymbolicStack::setUnnamedLocal(int index,
1848 int localId,
1849 Offset startOff) {
1850 assert(size_t(index) < m_symStack.size());
1851 assert(m_symStack[index].sym == StackSym::K);
1852 assert(m_symStack[index].clsBaseType == CLS_UNNAMED_LOCAL);
1853 m_symStack[index].intval = localId;
1854 m_symStack[index].unnamedLocalStart = startOff;
1857 void SymbolicStack::set(int index, char sym) {
1858 assert(index >= 0 && index < (int)m_symStack.size());
1859 // XXX Add assert in debug build to make sure W is not getting
1860 // written or overwritten by something else
1861 m_symStack[index].sym = sym;
1862 ITRACE(4, " set: {} -> {}\n", index, m_symStack[index].pretty());
1865 size_t SymbolicStack::size() const {
1866 return m_symStack.size();
1869 size_t SymbolicStack::actualSize() const {
1870 return m_actualStack.size();
1873 bool SymbolicStack::empty() const {
1874 return m_symStack.empty();
1877 void SymbolicStack::clear() {
1878 m_symStack.clear();
1879 m_actualStack.clear();
1880 m_fdescCount = 0;
1883 void SymbolicStack::consumeBelowTop(int depth) {
1884 if (int(m_symStack.size()) < depth + 1) {
1885 Logger::Warning(
1886 "Emitter tried to consumeBelowTop() when the symbolic "
1887 "stack did not have enough elements in it.");
1888 assert(false);
1889 return;
1891 assert(int(m_symStack.size()) >= depth + 1);
1892 int index = m_symStack.size() - depth - 1;
1893 m_symStack.erase(m_symStack.begin() + index);
1896 * Update any indexes into the actual stack that pointed to or past
1897 * this element.
1899 * (In practice they should all be past---we don't currently ever
1900 * remove below the top for actual stack elements.)
1902 for (size_t i = 0; i < m_actualStack.size(); ++i) {
1903 if (m_actualStack[i] >= index) {
1904 --m_actualStack[i];
1909 int SymbolicStack::getActualPos(int vpos) const {
1910 assert(vpos >= 0 && vpos < int(m_symStack.size()));
1911 assert(!m_actualStack.empty());
1912 for (int j = int(m_actualStack.size()) - 1; j >= 0; --j) {
1913 if (m_actualStack[j] == vpos) {
1914 return j;
1917 not_reached();
1920 char SymbolicStack::getActual(int index) const {
1921 assert(index >= 0 && index < (int)m_actualStack.size());
1922 return get(m_actualStack[index]);
1925 void SymbolicStack::setActual(int index, char sym) {
1926 assert(index >= 0 && index < (int)m_actualStack.size());
1927 set(m_actualStack[index], sym);
1930 SymbolicStack::ClassBaseType
1931 SymbolicStack::getClsBaseType(int index) const {
1932 assert(m_symStack.size() > size_t(index));
1933 assert(m_symStack[index].sym == StackSym::K);
1934 assert(m_symStack[index].clsBaseType != CLS_INVALID);
1935 return m_symStack[index].clsBaseType;
1938 int SymbolicStack::getLoc(int index) const {
1939 assert(m_symStack.size() > size_t(index));
1940 assert(StackSym::GetSymFlavor(m_symStack[index].sym) == StackSym::L ||
1941 m_symStack[index].clsBaseType == CLS_NAMED_LOCAL ||
1942 m_symStack[index].clsBaseType == CLS_UNNAMED_LOCAL);
1943 assert(m_symStack[index].intval != -1);
1944 return m_symStack[index].intval;
1947 int64_t SymbolicStack::getInt(int index) const {
1948 assert(m_symStack.size() > size_t(index));
1949 assert(StackSym::GetSymFlavor(m_symStack[index].sym) == StackSym::I);
1950 return m_symStack[index].intval;
1953 Offset SymbolicStack::getUnnamedLocStart(int index) const {
1954 assert(m_symStack.size() > size_t(index));
1955 assert(m_symStack[index].sym == StackSym::K);
1956 assert(m_symStack[index].clsBaseType == CLS_UNNAMED_LOCAL);
1957 return m_symStack[index].unnamedLocalStart;
1960 // Insert an element in the actual stack at the specified depth of the
1961 // actual stack.
1962 void SymbolicStack::insertAt(int depth, char sym) {
1963 assert(depth <= sizeActual() && depth > 0);
1964 int virtIdx = m_actualStack[sizeActual() - depth];
1966 m_symStack.insert(m_symStack.begin() + virtIdx, SymEntry(sym));
1967 m_actualStack.insert(m_actualStack.end() - depth, virtIdx);
1969 for (size_t i = sizeActual() - depth + 1; i < m_actualStack.size(); ++i) {
1970 ++m_actualStack[i];
1974 int SymbolicStack::sizeActual() const {
1975 return m_actualStack.size();
1978 void SymbolicStack::pushFDesc() {
1979 m_fdescCount += kNumActRecCells;
1980 updateHighWater();
1983 void SymbolicStack::popFDesc() {
1984 m_fdescCount -= kNumActRecCells;
1987 void Label::set(Emitter& e, bool emitNopAtEntry /* = true */) {
1988 auto const off = e.getUnitEmitter().bcPos();
1989 if (isSet()) {
1990 InvariantViolation(
1991 "Label::set was called more than once on the same "
1992 "Label; originally set to %d; now %d",
1993 m_off,
1994 off);
1995 return;
1997 EmitterVisitor& ev = e.getEmitterVisitor();
1998 if (emitNopAtEntry && ev.getFuncEmitter()->base == off) {
1999 // Make sure there are no jumps to the base of functions.
2000 // The JIT relies on this in some analysis.
2001 e.getUnitEmitter().emitOp(OpEntryNop);
2003 m_off = e.getUnitEmitter().bcPos();
2004 // Fix up any forward jumps that reference to this Label
2005 for (std::vector<std::pair<Offset, Offset> >::const_iterator it =
2006 m_emittedOffs.begin(); it != m_emittedOffs.end(); ++it) {
2007 e.getUnitEmitter().emitInt32(m_off - it->first, it->second);
2009 if (!m_emittedOffs.empty()) {
2010 // If there were forward jumps that referenced this Label,
2011 // compare the the eval stack from the first foward jump we
2012 // saw with the current eval stack
2013 if (!ev.evalStackIsUnknown()) {
2014 checkJmpTargetEvalStack(m_evalStack, ev.getEvalStack());
2015 } else {
2016 // Assume the current eval stack matches that of the forward branch
2017 ITRACE(3, "bind: {}\n", m_evalStack.pretty());
2018 ev.setEvalStack(m_evalStack);
2020 // Fix up the EmitterVisitor's table of jump targets
2021 ev.recordJumpTarget(m_off, ev.getEvalStack());
2022 } else {
2023 // There were no forward jumps that referenced this label
2024 ev.prepareEvalStack();
2025 // Fix up the EmitterVisitor's table of jump targets
2026 ev.recordJumpTarget(m_off, ev.getEvalStack());
2030 bool Label::isUsed() {
2031 return (m_off != InvalidAbsoluteOffset || !m_emittedOffs.empty());
2034 void Label::bind(EmitterVisitor& ev, Offset instrAddr, Offset offAddr) {
2035 if (m_off != InvalidAbsoluteOffset) {
2036 InvariantViolation("Label::bind was called on a Label that has already "
2037 "been set to %d",
2038 m_off);
2039 return;
2041 bool labelHasEvalStack = !m_emittedOffs.empty();
2042 m_emittedOffs.push_back(std::pair<Offset, Offset>(instrAddr, offAddr));
2043 if (labelHasEvalStack) {
2044 checkJmpTargetEvalStack(m_evalStack, ev.getEvalStack());
2045 } else {
2046 m_evalStack = ev.getEvalStack();
2050 struct FPIRegionRecorder {
2051 FPIRegionRecorder(EmitterVisitor* ev, UnitEmitter& ue, SymbolicStack& stack,
2052 Offset start)
2053 : m_ev(ev), m_ue(ue), m_stack(stack), m_fpOff(m_stack.sizeActual()),
2054 m_start(start) {
2055 m_stack.pushFDesc();
2057 ~FPIRegionRecorder() {
2058 m_stack.popFDesc();
2059 m_ev->newFPIRegion(m_start, m_ue.bcPos(), m_fpOff);
2061 private:
2062 EmitterVisitor* m_ev;
2063 UnitEmitter& m_ue;
2064 SymbolicStack& m_stack;
2065 int m_fpOff;
2066 Offset m_start;
2069 //=============================================================================
2070 // ControlTarget.
2072 const int ControlTarget::k_unsetState = -1;
2074 ControlTarget::ControlTarget(EmitterVisitor* router)
2075 : m_visitor(router), m_label(), m_state(k_unsetState) {
2076 assert(m_visitor != nullptr);
2079 ControlTarget::~ControlTarget() {
2080 // The scope of states used in finally router is controled
2081 // using shared pointer refcounting. State numbers can be reused once
2082 // all the references are released.
2083 if (isRegistered()) {
2084 m_visitor->unregisterControlTarget(this);
2085 m_state = k_unsetState;
2089 bool ControlTarget::isRegistered() {
2090 return m_state != k_unsetState;
2093 //=============================================================================
2094 // Region.
2096 Region::Region(Region::Kind kind, RegionPtr parent)
2097 : m_kind(kind),
2098 m_iterId(-1),
2099 m_iterKind(KindOfIter),
2100 m_parent(parent) {
2103 void
2104 EmitterVisitor::registerReturn(StatementPtr s, Region* region, char sym) {
2105 ControlTargetPtr t;
2106 Region* r;
2107 for (r = region; true; r = r->m_parent.get()) {
2108 assert(r);
2109 if (r->isFinally()) {
2110 throw EmitterVisitor::IncludeTimeFatalException(s,
2111 "Return inside a finally block is not supported");
2113 if (r->m_returnTargets.count(sym)) {
2114 // We registered the control target before. Just return the existing one.
2115 t = r->m_returnTargets[sym].target;
2116 break;
2118 // Haven't registered the control target with region r yet.
2119 if (r->m_parent.get() == nullptr) {
2120 // Top of the region hierarchy - allocate a fresh control target.
2121 t = std::make_shared<ControlTarget>(this);
2122 r = r->m_parent.get();
2123 break;
2126 assert(t != nullptr);
2127 if (!t->isRegistered()) {
2128 registerControlTarget(t.get());
2130 // For all entries we visited that did not have this control target in
2131 // m_returnTargets, add this control target to these entries' m_returnTargets
2132 // fields as appropriate.
2133 Region* end = r;
2134 for (r = region; r != end; r = r->m_parent.get()) {
2135 r->m_returnTargets[sym] = ControlTargetInfo(t, r->isTryFinally());
2139 ControlTargetPtr
2140 EmitterVisitor::registerGoto(StatementPtr s, Region* region, StringData* name,
2141 bool alloc) {
2142 ControlTargetPtr t;
2143 Region* r;
2144 for (r = region; true; r = r->m_parent.get()) {
2145 assert(r);
2146 if (r->m_gotoTargets.count(name)) {
2147 // We registered the control target before. Just return the existing one.
2148 t = r->m_gotoTargets[name].target;
2149 if (alloc && r->isTryFinally()) {
2150 r->m_gotoTargets[name].used = true;
2152 break;
2154 // Haven't registered the control target in this region yet.
2155 if (r->m_parent.get() == nullptr) {
2156 // Top of the region hierarchy - allocate a fresh control target.
2157 t = std::make_shared<ControlTarget>(this);
2158 r = r->m_parent.get();
2159 break;
2162 assert(t != nullptr);
2163 if (alloc && !t->isRegistered()) {
2164 registerControlTarget(t.get());
2166 // For all entries we visited that did not have this control target in
2167 // m_gotoTargets, add this control target to these entries' m_gotoTargets
2168 // fields as appropriate.
2169 Region* end = r;
2170 for (r = region; r != end; r = r->m_parent.get()) {
2171 r->m_gotoTargets[name] = ControlTargetInfo(t, alloc && r->isTryFinally());
2173 return t;
2176 void EmitterVisitor::registerYieldAwait(ExpressionPtr e) {
2177 Region* region = m_regions.back().get();
2178 for (; region; region = region->m_parent.get()) {
2179 if (region->isFinally()) {
2180 throw EmitterVisitor::IncludeTimeFatalException(e,
2181 "Yield expression inside a finally block is not supported");
2186 ControlTargetPtr
2187 EmitterVisitor::registerBreak(StatementPtr s, Region* region, int depth,
2188 bool alloc) {
2189 ControlTargetPtr t;
2190 assert(depth >= 1);
2191 int d = depth;
2192 Region* r;
2193 for (r = region; true; r = r->m_parent.get()) {
2194 assert(r);
2195 if (r->isFinally()) {
2196 throw EmitterVisitor::IncludeTimeFatalException(s,
2197 "Break jump is not allowed to leave a finally block");
2199 if (r->m_breakTargets.count(d)) {
2200 // We registered the control target before. Just return the existing one.
2201 t = r->m_breakTargets[d].target;
2202 if (alloc && r->isTryFinally()) {
2203 r->m_breakTargets[d].used = true;
2205 break;
2207 if (r->m_kind != Region::Kind::LoopOrSwitch) {
2208 continue;
2210 if (d == 1) {
2211 // We should never reach this case if alloc == true, since the loop or
2212 // switch should have registered its break target in advance
2213 assert(!alloc);
2214 // If this is a loop, and depth is one, just allocate a fresh
2215 // control target, since there are no more entries to delegate to.
2216 t = std::make_shared<ControlTarget>(this);
2217 r = r->m_parent.get();
2218 break;
2220 // Otherwise, delegate to the parent. One break level has been
2221 // taken care of by this region.
2222 --d;
2224 assert(t != nullptr);
2225 if (alloc) {
2226 if (!t->isRegistered()) {
2227 registerControlTarget(t.get());
2230 // For all of the entries that did not have this control target in
2231 // m_breakTargets, add this control target to these entries' m_breakTargets
2232 // fields as appropriate.
2233 Region* end = r;
2234 for (r = region; r != end; r = r->m_parent.get()) {
2235 r->m_breakTargets[depth] =
2236 ControlTargetInfo(t, alloc && r->isTryFinally());
2237 if (r->m_kind == Region::Kind::LoopOrSwitch) {
2238 --depth;
2241 return t;
2244 ControlTargetPtr
2245 EmitterVisitor::registerContinue(StatementPtr s, Region* region, int depth,
2246 bool alloc) {
2247 ControlTargetPtr t;
2248 assert(depth >= 1);
2249 int d = depth;
2250 Region* r;
2251 for (r = region; true; r = r->m_parent.get()) {
2252 assert(r);
2253 if (r->isFinally()) {
2254 throw EmitterVisitor::IncludeTimeFatalException(s,
2255 "Continue jump is not allowed to leave a finally block");
2257 if (r->m_continueTargets.count(d)) {
2258 // We registered the control target before. Just return the existing one.
2259 t = r->m_continueTargets[d].target;
2260 if (alloc && r->isTryFinally()) {
2261 r->m_continueTargets[d].used = true;
2263 break;
2265 if (r->m_kind != Region::Kind::LoopOrSwitch) {
2266 continue;
2268 if (d == 1) {
2269 // We should never reach this case if alloc == true, since the loop or
2270 // switch should have registered its continue target in advance
2271 assert(!alloc);
2272 t = std::make_shared<ControlTarget>(this);
2273 r = r->m_parent.get();
2274 break;
2276 // Otherwise, delegate to the parent. One continue level has been
2277 // taken care of by this region.
2278 --d;
2280 assert(t != nullptr);
2281 if (alloc && !t->isRegistered()) {
2282 registerControlTarget(t.get());
2284 // For all of the entries that did not have this control target in
2285 // m_continueTargets, add this control target to these entries'
2286 // m_continueTargets fields as appropriate.
2287 Region* end = r;
2288 for (r = region; r != end; r = r->m_parent.get()) {
2289 r->m_continueTargets[depth] =
2290 ControlTargetInfo(t, alloc && r->isTryFinally());
2291 if (r->m_kind == Region::Kind::LoopOrSwitch) {
2292 --depth;
2295 return t;
2298 int Region::getCaseCount() {
2299 int count = 1; // The fall-through case.
2300 for (auto& t : m_returnTargets) {
2301 if (t.second.target->isRegistered()) ++count;
2303 for (auto& t : m_breakTargets) {
2304 if (t.second.target->isRegistered()) ++count;
2306 for (auto& t : m_continueTargets) {
2307 if (t.second.target->isRegistered()) ++count;
2309 for (auto& t : m_gotoTargets) {
2310 if (t.second.target->isRegistered()) ++count;
2312 return count;
2315 void EmitterVisitor::emitIterFree(Emitter& e, IterVec& iters) {
2316 for (auto& iter : iters) {
2317 assert(iter.id != -1);
2318 if (iter.kind == KindOfMIter) {
2319 e.MIterFree(iter.id);
2320 } else {
2321 assert(iter.kind == KindOfIter);
2322 e.IterFree(iter.id);
2327 void EmitterVisitor::emitJump(Emitter& e, IterVec& iters, Label& target) {
2328 if (!iters.empty()) {
2329 e.IterBreak(target, iters);
2330 iters.clear();
2331 } else {
2332 e.Jmp(target);
2336 void EmitterVisitor::emitReturn(Emitter& e, char sym, StatementPtr s) {
2337 Region* region = m_regions.back().get();
2338 registerReturn(s, region, sym);
2339 assert(getEvalStack().size() == 1);
2340 assert(region->m_returnTargets.count(sym));
2341 IterVec iters;
2342 for (Region* r = region; true; r = r->m_parent.get()) {
2343 auto& t = r->m_returnTargets[sym].target;
2344 if (r->m_parent == nullptr) {
2345 // At the top of the hierarchy, no more finally blocks to run.
2346 // Check return type, free pending iterators and actually return.
2347 if (sym == StackSym::C) {
2348 if (shouldEmitVerifyRetType()) {
2349 e.VerifyRetTypeC();
2351 // IterFree must come after VerifyRetType, because VerifyRetType may
2352 // throw, in which case any Iters will be freed by the fault funclet.
2353 emitIterFree(e, iters);
2354 e.RetC();
2355 } else {
2356 assert(sym == StackSym::V);
2357 if (shouldEmitVerifyRetType()) {
2358 e.VerifyRetTypeV();
2360 emitIterFree(e, iters);
2361 e.RetV();
2363 return;
2366 if (r->isTryFinally()) {
2367 // We encountered a try block - a finally needs to be run
2368 // before returning.
2369 Id stateLocal = getStateLocal();
2370 Id retLocal = getRetLocal();
2371 // Set the unnamed "state" local to the appropriate identifier
2372 emitVirtualLocal(retLocal);
2373 assert(t->isRegistered());
2374 e.Int(t->m_state);
2375 e.SetL(stateLocal);
2376 e.PopC();
2377 // Emit code stashing the current return value in the "ret" unnamed
2378 // local
2379 if (sym == StackSym::C) {
2380 // For legacy purposes, SetL expects its immediate argument to
2381 // be present on the symbolic stack. In reality, retLocal is
2382 // an immediate argument. The following pop and push instructions
2383 // ensure that the arguments are place on the symbolic stack
2384 // in a correct order. In reality the following three calls are
2385 // a no-op.
2386 popEvalStack(StackSym::C);
2387 emitVirtualLocal(retLocal);
2388 pushEvalStack(StackSym::C);
2389 e.SetL(retLocal);
2390 e.PopC();
2391 } else {
2392 assert(sym == StackSym::V);
2393 popEvalStack(StackSym::V);
2394 emitVirtualLocal(retLocal);
2395 pushEvalStack(StackSym::V);
2396 e.BindL(retLocal);
2397 e.PopV();
2399 emitJump(e, iters, r->m_finallyLabel);
2400 return;
2402 if (r->isForeach()) {
2403 iters.push_back(IterPair(r->m_iterKind, r->m_iterId));
2408 void EmitterVisitor::emitGoto(Emitter& e, StringData* name, StatementPtr s) {
2409 Region* region = m_regions.back().get();
2410 registerGoto(s, region, name, true);
2411 assert(region->m_gotoTargets.count(name));
2412 IterVec iters;
2413 for (Region* r = region; true; r = r->m_parent.get()) {
2414 auto t = r->m_gotoTargets[name].target;
2415 if (r->m_gotoLabels.count(name)) {
2416 // If only the destination label is within the statement
2417 // associated with the current statement, just perform a
2418 // direct jump. Free the pending iterators on the way.
2419 emitJump(e, iters, t->m_label);
2420 return;
2422 if (r->isFinally()) {
2423 throw EmitterVisitor::IncludeTimeFatalException(s,
2424 "Goto to a label outside a finally block is not supported");
2426 if (r->isTryFinally()) {
2427 // We came across a try region, need to run a finally block.
2428 // Store appropriate value inside the state local.
2429 Id stateLocal = getStateLocal();
2430 emitVirtualLocal(stateLocal);
2431 assert(t->isRegistered());
2432 e.Int(t->m_state);
2433 e.SetL(stateLocal);
2434 e.PopC();
2435 // Jump to the finally block and free any pending iterators on the
2436 // way.
2437 emitJump(e, iters, r->m_finallyLabel);
2438 return;
2440 if (r->isForeach()) {
2441 iters.push_back(IterPair(r->m_iterKind, r->m_iterId));
2446 void EmitterVisitor::emitBreak(Emitter& e, int depth, StatementPtr s) {
2447 Region* region = m_regions.back().get();
2448 registerBreak(s, region, depth, true);
2449 assert(depth >= 1);
2450 assert(!region->isFinally());
2451 assert(region->m_parent != nullptr);
2452 assert(region->m_breakTargets.count(depth));
2453 IterVec iters;
2455 for (Region* r = region; true; r = r->m_parent.get()) {
2456 auto t = r->m_breakTargets[depth].target;
2457 if (r->isTryFinally()) {
2458 // Encountered a try block, need to run finally.
2459 assert(r->m_breakTargets.count(depth));
2460 assert(t->isRegistered());
2461 Id stateLocal = getStateLocal();
2462 emitVirtualLocal(stateLocal);
2463 e.Int(t->m_state);
2464 e.SetL(stateLocal);
2465 e.PopC();
2466 emitJump(e, iters, r->m_finallyLabel);
2467 return;
2469 if (r->m_kind != Region::Kind::LoopOrSwitch) {
2470 continue;
2472 // Free iterator for the current loop whether or not
2473 // this is the last loop that we jump out of.
2474 if (r->isForeach()) {
2475 iters.push_back(IterPair(r->m_iterKind, r->m_iterId));
2477 if (depth == 1) {
2478 // Last loop to jumpt out of. Performa direct jump to the
2479 // break lable and free any pending iterators left.
2480 emitJump(e, iters, t->m_label);
2481 return;
2483 --depth;
2487 void EmitterVisitor::emitContinue(Emitter& e, int depth, StatementPtr s) {
2488 Region* region = m_regions.back().get();
2489 registerContinue(s, region, depth, true);
2490 assert(depth >= 1);
2491 assert(!region->isFinally());
2492 assert(region->m_parent != nullptr);
2493 assert(region->m_continueTargets.count(depth));
2494 IterVec iters;
2496 for (Region* r = region; true; r = r->m_parent.get()) {
2497 auto t = r->m_continueTargets[depth].target;
2498 if (r->isTryFinally()) {
2499 // Encountered a try block, need to run finally.
2500 assert(r->m_continueTargets.count(depth));
2501 Id stateLocal = getStateLocal();
2502 emitVirtualLocal(stateLocal);
2503 assert(t->isRegistered());
2504 e.Int(t->m_state);
2505 e.SetL(stateLocal);
2506 e.PopC();
2507 emitJump(e, iters, r->m_finallyLabel);
2508 return;
2510 if (r->m_kind != Region::Kind::LoopOrSwitch) {
2511 continue;
2513 if (depth == 1) {
2514 // Last level. Don't free the iterator for the current loop
2515 // however free any earlier pending iterators.
2516 emitJump(e, iters, t->m_label);
2517 return;
2519 // Only free the iterator for the current loop if this is
2520 // NOT the last level to continue out of.
2521 if (r->isForeach()) {
2522 iters.push_back(IterPair(r->m_iterKind, r->m_iterId));
2524 --depth;
2528 void EmitterVisitor::emitFinallyEpilogue(Emitter& e, Region* region) {
2529 assert(region != nullptr);
2530 assert(region->isTryFinally());
2531 assert(region->m_finallyLabel.isSet());
2532 int count = region->getCaseCount();
2533 assert(count >= 1);
2534 Label after;
2535 if (count == 1) {
2536 // If there is only one case (the fall-through case) then we're done
2537 after.set(e);
2538 return;
2540 // Otherwise, we need to emit some conditional jumps/switches to handle
2541 // the different cases. We start by builing up a vector of Label* that
2542 // we'll use for the Switch instruction and/or for conditional branches.
2543 int maxState = region->getMaxState();
2544 std::vector<Label*> cases;
2545 while (cases.size() <= maxState) {
2546 cases.push_back(new Label());
2548 // Now that we have our vector of Label*'s ready, we can emit a
2549 // Switch instruction and/or conditional branches, and we can
2550 // emit the body of each case.
2551 Id stateLocal = getStateLocal();
2552 emitVirtualLocal(stateLocal);
2553 e.IssetL(stateLocal);
2554 e.JmpZ(after);
2555 if (count >= 3) {
2556 // A switch is needed since there are more than two cases.
2557 emitVirtualLocal(stateLocal);
2558 e.CGetL(stateLocal);
2559 e.Switch(SwitchKind::Unbounded, 0, cases);
2561 for (auto& p : region->m_returnTargets) {
2562 if (p.second.used) emitReturnTrampoline(e, region, cases, p.first);
2564 assert(region->isTryFinally());
2565 int max_depth = region->getBreakContinueDepth();
2566 for (int i = 1; i <= max_depth; ++i) {
2567 if (region->isBreakUsed(i)) emitBreakTrampoline(e, region, cases, i);
2568 if (region->isContinueUsed(i)) emitContinueTrampoline(e, region, cases, i);
2570 for (auto& p : region->m_gotoTargets) {
2571 if (p.second.used) emitGotoTrampoline(e, region, cases, p.first);
2573 for (auto c : cases) {
2574 // Some cases might get assigned state numbers but not actually
2575 // occur in the try block. We need to set /some/ target for them,
2576 // so point them here.
2577 if (!c->isSet()) c->set(e);
2578 delete c;
2580 after.set(e);
2583 void EmitterVisitor::emitReturnTrampoline(Emitter& e,
2584 Region* region,
2585 std::vector<Label*>& cases,
2586 char sym) {
2587 assert(region->isTryFinally());
2588 assert(region->m_parent != nullptr);
2589 assert(region->m_returnTargets.count(sym));
2590 auto& t = region->m_returnTargets[sym].target;
2591 cases[t->m_state]->set(e);
2593 IterVec iters;
2594 // We are emitting a case in a finally epilogue, therefore skip
2595 // the current try region and start from its parent
2596 for (region = region->m_parent.get(); true; region = region->m_parent.get()) {
2597 assert(region->m_returnTargets.count(sym));
2598 assert(region->m_returnTargets[sym].target->isRegistered());
2599 // Add pending iterator if applicable
2600 if (region->isForeach()) {
2601 iters.push_back(IterPair(region->m_iterKind, region->m_iterId));
2603 if (region->m_parent == nullptr) {
2604 // At the bottom of the hierarchy. Restore the return value
2605 // and perform the actual return.
2606 Id retLocal = getRetLocal();
2607 emitVirtualLocal(retLocal);
2608 if (sym == StackSym::C) {
2609 e.CGetL(retLocal);
2610 if (shouldEmitVerifyRetType()) {
2611 e.VerifyRetTypeC();
2613 e.RetC();
2614 } else {
2615 assert(sym == StackSym::V);
2616 e.VGetL(retLocal);
2617 if (shouldEmitVerifyRetType()) {
2618 e.VerifyRetTypeV();
2620 e.RetV();
2622 return;
2624 if (region->isTryFinally()) {
2625 // Encountered another try block, jump to its finally and free
2626 // iterators on the way.
2627 emitJump(e, iters, region->m_finallyLabel);
2628 return;
2633 void EmitterVisitor::emitGotoTrampoline(Emitter& e,
2634 Region* region,
2635 std::vector<Label*>& cases,
2636 StringData* name) {
2637 assert(region->m_gotoTargets.count(name));
2638 auto t = region->m_gotoTargets[name].target;
2639 cases[t->m_state]->set(e);
2640 assert(region->m_parent != nullptr);
2641 IterVec iters;
2642 for (region = region->m_parent.get(); true; region = region->m_parent.get()) {
2643 assert(region->m_gotoTargets.count(name));
2644 auto t = region->m_gotoTargets[name].target;
2645 if (region->m_gotoLabels.count(name)) {
2646 // If only there is the appropriate label inside the current region
2647 // perform a jump.
2648 Id stateLocal = getStateLocal();
2649 emitVirtualLocal(stateLocal);
2650 // We need to unset the state unnamed local in order to correctly
2651 // fall through any future finally blocks.
2652 e.UnsetL(stateLocal);
2653 // Jump to the label and free any pending iterators.
2654 emitJump(e, iters, t->m_label);
2655 return;
2657 if (region->isTryFinally()) {
2658 // Encountered a finally block, jump and free any pending iterators
2659 emitJump(e, iters, region->m_finallyLabel);
2660 return;
2662 // Otherwise we will be jumping out of the current context,
2663 // therefore if we are in a loop, we need to free the iterator.
2664 if (region->isForeach()) {
2665 iters.push_back(IterPair(region->m_iterKind, region->m_iterId));
2667 // Error, because the label is crossing a finally
2668 if (region->isFinally()) {
2669 throw EmitterVisitor::IncludeTimeFatalException(e.getNode(),
2670 "jump out of a finally block is disallowed");
2672 // We should never break out of a function, therefore there
2673 // should always be a parent
2674 assert(region->m_parent != nullptr);
2678 void EmitterVisitor::emitBreakTrampoline(Emitter& e, Region* region,
2679 std::vector<Label*>& cases,
2680 int depth) {
2681 assert(depth >= 1);
2682 assert(region->isTryFinally());
2683 assert(region->m_breakTargets.count(depth));
2684 auto t = region->m_breakTargets[depth].target;
2685 cases[t->m_state]->set(e);
2686 assert(region->m_parent != nullptr);
2687 IterVec iters;
2688 for (region = region->m_parent.get(); true; region = region->m_parent.get()) {
2689 assert(depth >= 1);
2690 assert(!region->isFinally());
2691 assert(region->m_parent != nullptr);
2692 assert(region->m_breakTargets.count(depth));
2693 auto t = region->m_breakTargets[depth].target;
2694 if (region->isTryFinally()) {
2695 // We encountered another try block, jump to the corresponding
2696 // finally, freeing any iterators on the way.
2697 emitJump(e, iters, region->m_finallyLabel);
2698 return;
2700 if (region->m_kind != Region::Kind::LoopOrSwitch) {
2701 continue;
2703 // Whether or not this is the last loop to break out of, we
2704 // will be freeing the current iterator
2705 if (region->isForeach()) {
2706 iters.push_back(IterPair(region->m_iterKind, region->m_iterId));
2708 if (depth == 1) {
2709 // This is the last loop to break out of. Unset the state local in
2710 // order to correctly fall through any future finally blocks
2711 Id stateLocal = getStateLocal();
2712 emitVirtualLocal(stateLocal);
2713 e.UnsetL(stateLocal);
2714 // Jump to the break label and free any pending iterators on the
2715 // way.
2716 emitJump(e, iters, t->m_label);
2717 return;
2719 // Otherwise just delegate to the parent. One loop level has been
2720 // taken care of.
2721 --depth;
2725 void EmitterVisitor::emitContinueTrampoline(Emitter& e, Region* region,
2726 std::vector<Label*>& cases,
2727 int depth) {
2728 assert(depth >= 1);
2729 assert(region->isTryFinally());
2730 assert(region->m_continueTargets.count(depth));
2731 auto t = region->m_continueTargets[depth].target;
2732 cases[t->m_state]->set(e);
2733 assert(region->m_parent != nullptr);
2734 IterVec iters;
2735 for (region = region->m_parent.get(); true; region = region->m_parent.get()) {
2736 assert(depth >= 1);
2737 assert(region->m_parent != nullptr);
2738 assert(!region->isFinally());
2739 auto t = region->m_continueTargets[depth].target;
2740 if (region->isTryFinally()) {
2741 emitJump(e, iters, region->m_finallyLabel);
2742 return;
2744 if (region->m_kind != Region::Kind::LoopOrSwitch) {
2745 continue;
2747 if (depth == 1) {
2748 // This is the last loop level to continue out of. Don't free the
2749 // iterator for the current loop. We need to free the state unnamed
2750 // local in order to fall through any future finallies correctly
2751 Id stateLocal = getStateLocal();
2752 emitVirtualLocal(stateLocal);
2753 e.UnsetL(stateLocal);
2754 // Jump to the continue label and free any pending iterators
2755 emitJump(e, iters, t->m_label);
2756 return;
2758 // This is not the last loop level, therefore the current
2759 // iterator should be freed.
2760 if (region->isForeach()) {
2761 iters.push_back(IterPair(region->m_iterKind, region->m_iterId));
2763 --depth;
2767 bool EmitterVisitor::shouldEmitVerifyRetType() {
2768 return (m_curFunc->retTypeConstraint.hasConstraint() &&
2769 !m_curFunc->isGenerator);
2772 int Region::getMaxBreakContinueDepth() {
2773 if (m_parent == nullptr || isFinally()) {
2774 return 0;
2775 } else if (m_kind == Region::Kind::LoopOrSwitch) {
2776 return m_parent->getMaxBreakContinueDepth() + 1;
2777 } else {
2778 return m_parent->getMaxBreakContinueDepth();
2782 int Region::getBreakContinueDepth() {
2783 int depth = 0;
2784 for (auto& p : m_breakTargets) {
2785 depth = std::max(depth, p.first);
2787 for (auto& p : m_continueTargets) {
2788 depth = std::max(depth, p.first);
2790 return depth;
2793 int Region::getMaxState() {
2794 int maxState = -1;
2795 for (auto& p : m_returnTargets) {
2796 if (p.second.used) {
2797 maxState = std::max(maxState, p.second.target->m_state);
2800 int max_depth = getBreakContinueDepth();
2801 for (int i = 1; i <= max_depth; ++i) {
2802 if (isBreakUsed(i)) {
2803 maxState = std::max(maxState, m_breakTargets[i].target->m_state);
2805 if (isContinueUsed(i)) {
2806 maxState = std::max(maxState, m_continueTargets[i].target->m_state);
2809 for (auto& p : m_gotoTargets) {
2810 if (p.second.used) {
2811 maxState = std::max(maxState, p.second.target->m_state);
2814 return maxState;
2817 RegionPtr
2818 EmitterVisitor::createRegion(StatementPtr s, Region::Kind kind) {
2819 RegionPtr parent = nullptr;
2820 if (kind != Region::Kind::FuncBody && kind != Region::Kind::FaultFunclet &&
2821 kind != Region::Kind::Global && !m_regions.empty()) {
2822 parent = m_regions.back();
2824 auto region = std::make_shared<Region>(kind, parent);
2825 // We preregister all the labels occurring in the provided statement
2826 // ahead of the time. Therefore at the time of emitting the actual
2827 // goto instructions we can reliably tell which finally blocks to
2828 // run.
2829 for (auto& label : s->getLabelScope()->getLabels()) {
2830 StringData* nName = makeStaticString(label.getName().c_str());
2831 if (!region->m_gotoLabels.count(nName)) {
2832 region->m_gotoLabels.insert(nName);
2835 return region;
2838 void EmitterVisitor::enterRegion(RegionPtr region) {
2839 assert(region != nullptr);
2840 m_regions.push_back(region);
2843 void EmitterVisitor::leaveRegion(RegionPtr region) {
2844 assert(region != nullptr);
2845 assert(m_regions.size() > 0);
2846 assert(m_regions.back() == region);
2847 m_regions.pop_back();
2850 void EmitterVisitor::registerControlTarget(ControlTarget* t) {
2851 assert(!t->isRegistered());
2852 int state = 0;
2853 while (m_states.count(state)) {
2854 ++state;
2856 m_states.insert(state);
2857 t->m_state = state;
2860 void EmitterVisitor::unregisterControlTarget(ControlTarget* t) {
2861 assert(t->isRegistered());
2862 int state = t->m_state;
2863 assert(m_states.count(state));
2864 m_states.erase(state);
2865 t->m_state = ControlTarget::k_unsetState;
2868 //=============================================================================
2869 // EmitterVisitor.
2871 EmitterVisitor::EmittedClosures EmitterVisitor::s_emittedClosures;
2873 EmitterVisitor::EmitterVisitor(UnitEmitter& ue)
2874 : m_ue(ue),
2875 m_curFunc(ue.getMain()),
2876 m_evalStackIsUnknown(false),
2877 m_stateLocal(-1),
2878 m_retLocal(-1) {
2879 m_prevOpcode = OpLowInvalid;
2880 m_evalStack.m_actualStackHighWaterPtr = &m_curFunc->maxStackCells;
2883 EmitterVisitor::~EmitterVisitor() {
2884 // If a fatal occurs during emission, some extra cleanup is necessary.
2885 for (std::deque<CatchRegion*>::const_iterator it = m_catchRegions.begin();
2886 it != m_catchRegions.end(); ++it) {
2887 delete *it;
2891 bool EmitterVisitor::checkIfStackEmpty(const char* forInstruction) const {
2892 if (m_evalStack.empty()) {
2893 InvariantViolation("Emitter tried to emit a %s instruction when the "
2894 "evaluation stack is empty (at offset %d)",
2895 forInstruction,
2896 m_ue.bcPos());
2897 return true;
2899 return false;
2902 void EmitterVisitor::unexpectedStackSym(char sym, const char* where) const {
2903 InvariantViolation("Emitter encountered an unexpected StackSym \"%s\""
2904 " in %s() (at offset %d)",
2905 StackSym::ToString(sym).c_str(),
2906 where,
2907 m_ue.bcPos());
2910 void EmitterVisitor::popEvalStack(char expected) {
2911 // Pop a value off of the evaluation stack, and verify that it
2912 // matches the specified symbolic flavor
2913 if (m_evalStack.size() == 0) {
2914 InvariantViolation("Emitter emitted an instruction that tries to consume "
2915 "a value from the stack when the stack is empty "
2916 "(expected symbolic flavor \"%s\" at offset %d)",
2917 StackSym::ToString(expected).c_str(),
2918 m_ue.bcPos());
2919 return;
2922 char sym = m_evalStack.top();
2923 char actual = StackSym::GetSymFlavor(sym);
2924 m_evalStack.pop();
2925 if (actual != expected) {
2926 InvariantViolation(
2927 "Emitter emitted an instruction that tries to consume a "
2928 "value from the stack when the top of the stack does not "
2929 "match the symbolic flavor that the instruction expects "
2930 "(expected symbolic flavor \"%s\", actual symbolic flavor \"%s\" "
2931 "at offset %d)",
2932 StackSym::ToString(expected).c_str(),
2933 StackSym::ToString(actual).c_str(),
2934 m_ue.bcPos());
2938 void EmitterVisitor::popSymbolicLocal(Op op) {
2939 // A number of member instructions read locals without consuming an L from
2940 // the symbolic stack through the normal path.
2941 if (isMemberBaseOp(op) || isMemberDimOp(op) || isMemberFinalOp(op)) {
2942 return;
2945 int belowTop = -1;
2946 if (op == OpCGetL3) {
2947 belowTop = 3;
2948 } else if (op == OpCGetL2) {
2949 belowTop = 2;
2952 if (belowTop != -1) {
2953 char symFlavor = StackSym::GetSymFlavor(
2954 m_evalStack.get(m_evalStack.size() - belowTop));
2955 if (symFlavor != StackSym::L) {
2956 InvariantViolation("Operation tried to remove a local below the top of"
2957 " the symbolic stack but instead found \"%s\"",
2958 StackSym::ToString(symFlavor).c_str());
2960 m_evalStack.consumeBelowTop(belowTop - 1);
2961 } else {
2962 popEvalStack(StackSym::L);
2966 void EmitterVisitor::popEvalStackMMany() {
2967 ITRACE(3, "popEvalStackMMany()\n");
2968 Trace::Indent i;
2970 ITRACE(3, "popping member codes\n");
2971 while (!m_evalStack.empty()) {
2972 char sym = m_evalStack.top();
2973 char symFlavor = StackSym::GetSymFlavor(sym);
2974 char marker = StackSym::GetMarker(sym);
2975 if (marker == StackSym::E || marker == StackSym::P ||
2976 marker == StackSym::Q) {
2977 if (symFlavor != StackSym::C && symFlavor != StackSym::L &&
2978 symFlavor != StackSym::T && symFlavor != StackSym::I) {
2979 InvariantViolation(
2980 "Emitter emitted an instruction that tries to consume "
2981 "a value from the stack when the top of the stack "
2982 "does not match the symbolic flavor that the instruction "
2983 "expects (expected symbolic flavor \"C\", \"L\", \"T\", or \"I\", "
2984 "actual symbolic flavor \"%s\" at offset %d)",
2985 StackSym::ToString(symFlavor).c_str(),
2986 m_ue.bcPos());
2988 } else if (marker == StackSym::W) {
2989 if (symFlavor != StackSym::None) {
2990 InvariantViolation(
2991 "Emitter emitted an instruction that tries to consume "
2992 "a value from the stack when the top of the stack "
2993 "does not match the symbolic flavor that the instruction "
2994 "expects (expected symbolic flavor \"None\", actual "
2995 "symbolic flavor \"%s\" at offset %d)",
2996 StackSym::ToString(symFlavor).c_str(),
2997 m_ue.bcPos());
2999 } else if (marker == StackSym::M) {
3000 assert(symFlavor == StackSym::A);
3001 } else {
3002 break;
3004 m_evalStack.pop();
3007 if (m_evalStack.empty()) {
3008 InvariantViolation("Emitter emitted an instruction that tries to consume "
3009 "a value from the stack when the stack is empty "
3010 "(at offset %d)",
3011 m_ue.bcPos());
3012 return;
3015 ITRACE(3, "popping location\n");
3016 char sym = m_evalStack.top();
3017 char symFlavor = StackSym::GetSymFlavor(sym);
3018 m_evalStack.pop();
3019 if (symFlavor != StackSym::C && symFlavor != StackSym::L &&
3020 symFlavor != StackSym::R && symFlavor != StackSym::H) {
3021 InvariantViolation(
3022 "Emitter emitted an instruction that tries to consume a "
3023 "value from the stack when the top of the stack does not "
3024 "match the symbolic flavor that the instruction expects "
3025 "(expected symbolic flavor \"C\", \"L\", \"R\", or \"H\", actual "
3026 "symbolic flavor \"%s\" at offset %d)",
3027 StackSym::ToString(symFlavor).c_str(),
3028 m_ue.bcPos());
3032 void EmitterVisitor::popEvalStackMany(int len, char symFlavor) {
3033 for (int i = 0; i < len; ++i) {
3034 popEvalStack(symFlavor);
3038 void EmitterVisitor::popEvalStackCVMany(int len) {
3039 for (int i = 0; i < len; i++) {
3040 if (m_evalStack.size() == 0) {
3041 InvariantViolation("Emitter emitted an instruction that tries to consume "
3042 "a value from the stack when the stack is empty "
3043 "(expected symbolic flavor C or V at offset %d)",
3044 m_ue.bcPos());
3045 return;
3048 char sym = m_evalStack.top();
3049 char actual = StackSym::GetSymFlavor(sym);
3050 m_evalStack.pop();
3051 if (actual != StackSym::C && actual != StackSym::V) {
3052 InvariantViolation(
3053 "Emitter emitted an instruction that tries to consume a "
3054 "value from the stack when the top of the stack does not "
3055 "match the symbolic flavor that the instruction expects "
3056 "(expected symbolic flavor C or V, actual symbolic flavor \"%s\" "
3057 "at offset %d)",
3058 StackSym::ToString(actual).c_str(),
3059 m_ue.bcPos());
3064 void EmitterVisitor::pushEvalStack(char symFlavor) {
3065 // Push a value from the evaluation stack with the specified
3066 // symbolic flavor
3067 m_evalStack.push(symFlavor);
3070 void EmitterVisitor::peekEvalStack(char expected, int depthActual) {
3071 int posActual = (m_evalStack.sizeActual() - depthActual - 1);
3072 if (posActual >= 0 && posActual < (int)m_evalStack.sizeActual()) {
3073 char sym = m_evalStack.getActual(posActual);
3074 char actual = StackSym::GetSymFlavor(sym);
3075 if (actual != expected) {
3076 InvariantViolation(
3077 "Emitter emitted an instruction that tries to consume a "
3078 "value from the stack whose symbolic flavor does not match "
3079 "the symbolic flavor that the instruction expects (expected "
3080 "symbolic flavor \"%s\", actual symbolic flavor \"%s\" at "
3081 "offset %d)",
3082 StackSym::ToString(expected).c_str(),
3083 StackSym::ToString(actual).c_str(),
3084 m_ue.bcPos());
3089 void EmitterVisitor::pokeEvalStack(char symFlavor, int depthActual) {
3090 int sizeActual = m_evalStack.sizeActual();
3091 int posActual = sizeActual - depthActual - 1;
3092 if (posActual >= 0 && posActual < sizeActual) {
3093 m_evalStack.setActual(posActual, symFlavor);
3098 * Prior to making any changes to the evaluation stack in between
3099 * instructions, this function should be called.
3101 * What this handles is recording the evaluation stack state at
3102 * instruction boundaries so that jumps know what their stack state
3103 * will be at the destination.
3105 * When m_evalStackIsUnknown, it means we have hit a place in the
3106 * bytecode where the offset cannot be reached via fallthrough, but no
3107 * forward jumps targeted it either. In this case, the stack must be
3108 * empty, and we need to record that this is the case so that later
3109 * backward jumps can check this is the case at their jump site.
3111 void EmitterVisitor::prepareEvalStack() {
3112 if (m_evalStackIsUnknown) {
3113 if (!m_evalStack.empty()) {
3114 InvariantViolation("Emitter expected to have an empty evaluation "
3115 "stack because the eval stack was unknown, but "
3116 "it was non-empty.");
3117 return;
3119 // Record that we are assuming that the eval stack is empty
3120 recordJumpTarget(m_ue.bcPos(), m_evalStack);
3121 m_evalStackIsUnknown = false;
3125 void EmitterVisitor::recordJumpTarget(Offset target,
3126 const SymbolicStack& evalStack) {
3127 if (target == InvalidAbsoluteOffset) {
3128 InvariantViolation(
3129 "Offset passed to EmitterVisitor::recordJumpTarget was invalid");
3131 auto it = m_jumpTargetEvalStacks.find(target);
3132 if (it == m_jumpTargetEvalStacks.end()) {
3133 m_jumpTargetEvalStacks[target] = evalStack;
3134 return;
3136 checkJmpTargetEvalStack(evalStack, it->second);
3139 void EmitterVisitor::restoreJumpTargetEvalStack() {
3140 m_evalStack.clear();
3141 auto it = m_jumpTargetEvalStacks.find(m_ue.bcPos());
3142 if (it == m_jumpTargetEvalStacks.end()) {
3143 m_evalStackIsUnknown = true;
3144 return;
3146 m_evalStack = it->second;
3149 void EmitterVisitor::recordCall() {
3150 m_curFunc->containsCalls = true;
3153 bool EmitterVisitor::isJumpTarget(Offset target) {
3154 // Returns true iff one of the following conditions is true:
3155 // 1) We have seen an instruction that jumps to the specified offset
3156 // 2) We know of a Label that has been set to the specified offset
3157 // 3) We have seen a try region that ends at the specified offset
3158 auto it = m_jumpTargetEvalStacks.find(target);
3159 return (it != m_jumpTargetEvalStacks.end());
3162 struct IterFreeThunklet final : Thunklet {
3163 IterFreeThunklet(Id iterId, bool itRef)
3164 : m_id(iterId), m_itRef(itRef) {}
3165 void emit(Emitter& e) override {
3166 if (m_itRef) {
3167 e.MIterFree(m_id);
3168 } else {
3169 e.IterFree(m_id);
3171 e.Unwind();
3173 private:
3174 Id m_id;
3175 bool m_itRef;
3179 * A thunklet for the fault region protecting a silenced (@) expression.
3181 struct RestoreErrorReportingThunklet final : Thunklet {
3182 explicit RestoreErrorReportingThunklet(Id loc)
3183 : m_oldLevelLoc(loc) {}
3184 void emit(Emitter& e) override {
3185 e.getEmitterVisitor().emitRestoreErrorReporting(e, m_oldLevelLoc);
3186 e.Unwind();
3188 private:
3189 Id m_oldLevelLoc;
3192 struct UnsetUnnamedLocalThunklet final : Thunklet {
3193 explicit UnsetUnnamedLocalThunklet(Id loc)
3194 : m_loc(loc) {}
3195 void emit(Emitter& e) override {
3196 e.getEmitterVisitor().emitVirtualLocal(m_loc);
3197 e.getEmitterVisitor().emitUnset(e);
3198 e.Unwind();
3200 private:
3201 Id m_loc;
3204 struct UnsetUnnamedLocalsThunklet final : Thunklet {
3205 explicit UnsetUnnamedLocalsThunklet(std::vector<Id>&& locs)
3206 : m_locs(std::move(locs)) {}
3207 void emit(Emitter& e) override {
3208 auto& visitor = e.getEmitterVisitor();
3209 for (auto loc : m_locs) {
3210 visitor.emitVirtualLocal(loc);
3211 visitor.emitUnset(e);
3213 e.Unwind();
3215 private:
3216 const std::vector<Id> m_locs;
3219 struct UnsetGeneratorDelegateThunklet final : Thunklet {
3220 explicit UnsetGeneratorDelegateThunklet(Id iterId)
3221 : m_id(iterId) {}
3222 void emit(Emitter& e) override {
3223 e.ContUnsetDelegate(true, m_id);
3224 e.Unwind();
3226 private:
3227 Id m_id;
3230 struct FinallyThunklet final : Thunklet {
3231 explicit FinallyThunklet(FinallyStatementPtr finallyStatement,
3232 int numLiveIters)
3233 : m_finallyStatement(finallyStatement), m_numLiveIters(numLiveIters) {}
3234 void emit(Emitter& e) override {
3235 auto& visitor = e.getEmitterVisitor();
3236 auto region =
3237 visitor.createRegion(m_finallyStatement, Region::Kind::FaultFunclet);
3238 visitor.enterRegion(region);
3239 SCOPE_EXIT { visitor.leaveRegion(region); };
3240 Id stateLocal = visitor.getStateLocal();
3241 visitor.emitVirtualLocal(stateLocal);
3242 e.UnsetL(stateLocal);
3243 Id retLocal = visitor.getStateLocal();
3244 visitor.emitVirtualLocal(retLocal);
3245 e.UnsetL(retLocal);
3246 auto* func = visitor.getFuncEmitter();
3247 int oldNumLiveIters = func->numLiveIterators();
3248 func->setNumLiveIterators(m_numLiveIters);
3249 SCOPE_EXIT { func->setNumLiveIterators(oldNumLiveIters); };
3250 visitor.visit(m_finallyStatement);
3251 e.Unwind();
3253 private:
3254 FinallyStatementPtr m_finallyStatement;
3255 int m_numLiveIters;
3259 * Helper to deal with emitting list assignment and keeping track of some
3260 * associated info. A list assignment can be thought of as a list of "index
3261 * chains"; that is, sequences of indices that should be accessed for each
3262 * bottom-level expression in the list assignment. We recursively walk down the
3263 * LHS, building up index chains and copying them into the top-level list as we
3264 * reach the leaves of the tree.
3266 void EmitterVisitor::listAssignmentVisitLHS(Emitter& e, ExpressionPtr exp,
3267 IndexChain& indexChain,
3268 std::vector<IndexPair>& all) {
3269 if (!exp) {
3270 // Empty slot
3271 return;
3274 if (exp->is(Expression::KindOfListAssignment)) {
3275 // Nested assignment
3276 auto la = static_pointer_cast<ListAssignment>(exp);
3277 auto lhs = la->getVariables();
3278 int n = lhs->getCount();
3279 for (int i = 0; i < n; ++i) {
3280 indexChain.push_back(i);
3281 listAssignmentVisitLHS(e, (*lhs)[i], indexChain, all);
3282 indexChain.pop_back();
3284 } else {
3285 // Reached a "leaf". Lock in this index chain and deal with this exp.
3286 assert(!indexChain.empty());
3287 all.emplace_back(exp, IndexChain(indexChain));
3289 // First: the order we visit the LHS elements matters, as does whether we
3290 // do the RHS or LHS first, for things like:
3291 // list($a[$n++], $b[$n++]) = $c[$n++]
3293 // In PHP5 mode, we visit the LHS elements of the list() now. This does
3294 // two things: it causes their side effects to happen in LTR order, but
3295 // since they are pushed onto the m_evalStack they are actually asigned to
3296 // in LIFO order, e.g., RTL, in listAssignmentAssignElements below.
3298 // In PHP7 mode, we need to visit the elements in LTR order so their side
3299 // effects take place in that order, but we *also* need to assign them in
3300 // LTR order, so we can't push them onto the m_evalStack right now. Since
3301 // visit() does both of these things, we need to delay calling visit()
3302 // until listAssignmentAssignElements below. This also has the side effect
3303 // of making isRhsFirst() effectively always on in PHP7 mode when doing
3304 // list() assignment (since we delay the visit() until after the check
3305 // anyways), which turns out to be the right behavior.
3306 if (!RuntimeOption::PHP7_LTR_assign) {
3307 visit(exp);
3308 emitClsIfSPropBase(e);
3313 void EmitterVisitor::listAssignmentAssignElements(
3314 Emitter& e,
3315 std::vector<IndexPair>& indexPairs,
3316 std::function<void()> emitSrc
3319 // PHP5 does list() assignments RTL, PHP7 does them LTR, so this loop can go
3320 // either way and looks a little ugly. The assignment order normally isn't
3321 // visible, but it is if you do something like:
3322 // list($a[], $a[]) = $foo
3323 auto const ltr = RuntimeOption::PHP7_LTR_assign;
3324 for (int i = ltr ? 0 : (int)indexPairs.size() - 1;
3325 i >= 0 && i < (int)indexPairs.size();
3326 ltr ? i++ : i--) {
3327 if (ltr) {
3328 // Visit now, so we can both eval LTR and assign LTR. See comment in
3329 // listAssignmentVisitLHS.
3330 visit(indexPairs[i].first);
3331 emitClsIfSPropBase(e);
3334 IndexChain& currIndexChain = indexPairs[i].second;
3335 if (currIndexChain.empty()) {
3336 continue;
3339 if (emitSrc == nullptr) {
3340 e.Null();
3341 } else {
3342 emitSrc();
3343 for (int j = 0; j < (int)currIndexChain.size(); ++j) {
3344 m_evalStack.push(StackSym::I);
3345 m_evalStack.setInt(currIndexChain[j]);
3346 markElem(e);
3348 emitCGet(e);
3351 emitSet(e);
3352 emitPop(e);
3357 * visitIfCondition() serves as a helper method for visiting the condition
3358 * of an "if" statement. This function recursively visits each node in an
3359 * expression, keeping track of where to jump if an "and" or "or" expression
3360 * short circuits. If an expression other than "and", "or", or "not" is
3361 * encountered, this method calls EmitterVisitor::visit() to handle the
3362 * expression.
3364 void EmitterVisitor::visitIfCondition(
3365 ExpressionPtr cond, Emitter& e, Label& tru, Label& fals,
3366 bool truFallthrough) {
3368 auto binOpNode = dynamic_pointer_cast<BinaryOpExpression>(cond);
3370 if (binOpNode) {
3371 int op = binOpNode->getOp();
3372 // Short circuit && and ||
3373 if (op == T_LOGICAL_OR || op == T_LOGICAL_AND ||
3374 op == T_BOOLEAN_OR || op == T_BOOLEAN_AND) {
3375 bool isOr = (op == T_LOGICAL_OR || op == T_BOOLEAN_OR);
3376 Label localLabel;
3377 ExpressionPtr lhs = binOpNode->getExp1();
3378 if (isOr) {
3379 Emitter lhsEmitter(lhs, m_ue, *this);
3380 visitIfCondition(lhs, lhsEmitter, tru, localLabel, false);
3381 // falls through if that condition was false
3382 } else {
3383 Emitter lhsEmitter(lhs, m_ue, *this);
3384 visitIfCondition(lhs, lhsEmitter, localLabel, fals, true);
3385 // falls through if that condition was true
3387 if (localLabel.isUsed()) {
3388 localLabel.set(e);
3390 if (currentPositionIsReachable()) {
3391 ExpressionPtr rhs = binOpNode->getExp2();
3392 Emitter rhsEmitter(rhs, m_ue, *this);
3393 visitIfCondition(rhs, rhsEmitter, tru, fals, truFallthrough);
3395 return;
3399 auto unOpNode = dynamic_pointer_cast<UnaryOpExpression>(cond);
3400 if (unOpNode) {
3401 int op = unOpNode->getOp();
3402 // Logical not
3403 if (op == '!') {
3404 ExpressionPtr val = unOpNode->getExpression();
3405 Emitter valEmitter(val, m_ue, *this);
3406 visitIfCondition(val, valEmitter, fals, tru, !truFallthrough);
3407 return;
3411 Variant val;
3412 if (cond->getScalarValue(val)) {
3413 if (truFallthrough) {
3414 if (!val.toBoolean()) e.Jmp(fals);
3415 } else {
3416 if (val.toBoolean()) e.Jmp(tru);
3418 return;
3421 visit(cond);
3422 emitConvertToCell(e);
3423 if (truFallthrough) {
3424 e.JmpZ(fals);
3425 } else {
3426 e.JmpNZ(tru);
3430 // Assigns ids to all of the local variables eagerly. This gives us the
3431 // nice property that all named local variables will be assigned ids
3432 // 0 through k-1, while any unnamed local variable will have an id >= k.
3433 void EmitterVisitor::assignLocalVariableIds(FunctionScopePtr fs) {
3434 VariableTablePtr variables = fs->getVariables();
3435 std::vector<std::string> localNames;
3436 variables->getLocalVariableNames(localNames);
3437 for (int i = 0; i < (int)localNames.size(); ++i) {
3438 StringData* nLiteral = makeStaticString(localNames[i].c_str());
3439 m_curFunc->allocVarId(nLiteral);
3443 void EmitterVisitor::assignFinallyVariableIds() {
3444 assert(m_stateLocal < 0);
3445 m_stateLocal = m_curFunc->allocUnnamedLocal();
3446 assert(m_retLocal < 0);
3447 m_retLocal = m_curFunc->allocUnnamedLocal();
3450 void EmitterVisitor::visit(FileScopePtr file) {
3451 const std::string& filename = file->getName();
3452 m_ue.m_filepath = makeStaticString(filename);
3453 m_ue.m_isHHFile = file->isHHFile();
3454 m_ue.m_useStrictTypes = file->useStrictTypes();
3456 FunctionScopePtr func(file->getPseudoMain());
3457 if (!func) return;
3459 SCOPE_ASSERT_DETAIL("visit FileScope") { return m_evalStack.pretty(); };
3460 ITRACE(1, "Emitting file {}\n", file->getName());
3461 Trace::Indent indent;
3463 m_file = file;
3464 assignLocalVariableIds(func);
3466 AnalysisResultPtr ar(file->getContainingProgram());
3467 assert(ar);
3468 auto m = dynamic_pointer_cast<MethodStatement>(func->getStmt());
3469 if (!m) return;
3470 StatementListPtr stmts(m->getStmts());
3471 if (!stmts) return;
3473 Emitter e(m, m_ue, *this);
3475 int i, nk = stmts->getCount();
3476 for (i = 0; i < nk; i++) {
3477 StatementPtr s = (*stmts)[i];
3478 if (auto meth = dynamic_pointer_cast<MethodStatement>(s)) {
3479 // Emit afterwards
3480 postponeMeth(meth, nullptr, true);
3484 FunctionScopePtr fsp = m->getFunctionScope();
3485 if (fsp->needsLocalThis()) {
3486 static const StringData* thisStr = makeStaticString("this");
3487 Id thisId = m_curFunc->lookupVarId(thisStr);
3488 emitVirtualLocal(thisId);
3489 e.InitThisLoc(thisId);
3491 if (fsp->needsFinallyLocals()) {
3492 assignFinallyVariableIds();
3494 FuncFinisher ff(this, e, m_curFunc);
3495 TypedValue mainReturn;
3496 mainReturn.m_type = kInvalidDataType;
3497 bool notMergeOnly = false;
3499 if (Option::UseHHBBC && SystemLib::s_inited) notMergeOnly = true;
3501 auto region = createRegion(stmts, Region::Kind::Global);
3502 enterRegion(region);
3503 SCOPE_EXIT { leaveRegion(region); };
3505 for (auto cls : m_file->getAnonClasses()) {
3506 emitClass(e, cls->getClassScope(), true);
3509 for (i = 0; i < nk; i++) {
3510 StatementPtr s = (*stmts)[i];
3511 e.setTempLocation(s->getRange());
3512 switch (s->getKindOf()) {
3513 case Statement::KindOfMethodStatement:
3514 case Statement::KindOfFunctionStatement:
3515 break;
3516 case Statement::KindOfInterfaceStatement:
3517 case Statement::KindOfClassStatement: {
3518 // Handle classes directly here, since only top-level classes are
3519 // hoistable.
3520 ClassScopePtr cNode = s->getClassScope();
3521 emitClass(e, cNode, true);
3522 if (cNode->getFatalMessage()) {
3523 notMergeOnly = true;
3525 break;
3527 case Construct::KindOfDeclareStatement: {
3528 auto ds = static_pointer_cast<DeclareStatement>(s);
3529 for (auto& decl : ds->getDeclareMap()) {
3530 if (decl.first == "strict_types") {
3531 if (ds->getBlock()->getStmts()->getCount()) {
3532 emitMakeUnitFatal(e, "strict_types declaration must not use "
3533 "block mode");
3534 break;
3536 if (!RuntimeOption::PHP7_ScalarTypes) {
3537 emitMakeUnitFatal(e, "strict_types can only be used when "
3538 "hhvm.php7.scalar_types = true");
3539 break;
3544 visit(ds->getBlock());
3545 break;
3547 case Statement::KindOfTypedefStatement: {
3548 auto const id =
3549 emitTypedef(e, static_pointer_cast<TypedefStatement>(s));
3550 m_ue.pushMergeableTypeAlias(Unit::MergeKind::TypeAlias, id);
3551 break;
3553 case Statement::KindOfReturnStatement:
3554 if (mainReturn.m_type != kInvalidDataType) break;
3556 visit(s);
3557 if (notMergeOnly) {
3558 tvWriteUninit(&mainReturn);
3559 m_ue.m_returnSeen = true;
3560 continue;
3564 auto r = static_pointer_cast<ReturnStatement>(s);
3565 Variant v((Variant::NullInit()));
3566 if (r->getRetExp() &&
3567 !r->getRetExp()->getScalarValue(v)) {
3568 tvWriteUninit(&mainReturn);
3569 notMergeOnly = true;
3570 continue;
3572 if (v.isString()) {
3573 v = String(makeStaticString(v.asCStrRef().get()));
3574 } else if (v.isArray()) {
3575 v = Array(ArrayData::GetScalarArray(v.asCArrRef().get()));
3576 } else {
3577 assert(v.isInitialized());
3578 assert(!isRefcountedType(v.getType()));
3580 mainReturn = *v.asCell();
3581 m_ue.m_returnSeen = true;
3583 break;
3584 case Statement::KindOfExpStatement:
3585 if (mainReturn.m_type == kInvalidDataType) {
3586 auto e = static_pointer_cast<ExpStatement>(s)->getExpression();
3587 switch (e->getKindOf()) {
3588 case Expression::KindOfSimpleFunctionCall: {
3589 auto func = static_pointer_cast<SimpleFunctionCall>(e);
3590 StringData *name;
3591 TypedValue tv;
3592 if (func->isSimpleDefine(&name, &tv)) {
3593 auto k = func->isDefineWithoutImpl(ar)
3594 ? Unit::MergeKind::PersistentDefine
3595 : Unit::MergeKind::Define;
3596 if (tv.m_type == KindOfUninit) {
3597 tv.m_type = KindOfNull;
3599 m_ue.pushMergeableDef(k, name, tv);
3600 visit(s);
3601 continue;
3603 break;
3605 case Expression::KindOfAssignmentExpression: {
3606 auto ae = static_pointer_cast<AssignmentExpression>(e);
3607 StringData *name;
3608 TypedValue tv;
3609 if (ae->isSimpleGlobalAssign(&name, &tv)) {
3610 m_ue.pushMergeableDef(Unit::MergeKind::Global, name, tv);
3611 visit(s);
3612 continue;
3614 break;
3616 case Expression::KindOfIncludeExpression: {
3617 auto inc = static_pointer_cast<IncludeExpression>(e);
3618 if (inc->isReqLit()) {
3619 if (FileScopeRawPtr f = inc->getIncludedFile(ar)) {
3620 if (StatementListPtr sl = f->getStmt()) {
3621 FunctionScopeRawPtr ps DEBUG_ONLY =
3622 sl->getFunctionScope();
3623 assert(ps && ps->inPseudoMain());
3624 m_ue.pushMergeableInclude(
3625 Unit::MergeKind::ReqDoc,
3626 makeStaticString(inc->includePath()));
3627 visit(s);
3628 continue;
3632 break;
3634 default:
3635 break;
3637 } // fall through
3638 default:
3639 if (mainReturn.m_type != kInvalidDataType) break;
3640 notMergeOnly = true;
3641 visit(s);
3645 if (!notMergeOnly) {
3646 m_ue.m_mergeOnly = true;
3647 if (mainReturn.m_type == kInvalidDataType) {
3648 tvWriteUninit(&mainReturn);
3649 if (boost::algorithm::ends_with(filename, EVAL_FILENAME_SUFFIX)) {
3650 tvAsVariant(&mainReturn) = init_null();
3651 } else {
3652 tvAsVariant(&mainReturn) = 1;
3655 m_ue.m_mainReturn = mainReturn;
3658 // Pseudo-main returns the integer value 1 by default. If the
3659 // current position in the bytecode is reachable, emit code to
3660 // return 1.
3661 if (currentPositionIsReachable()) {
3662 Location::Range loc;
3663 if (m_ue.bcPos() > 0) loc.line0 = -1;
3664 e.setTempLocation(loc);
3665 if (boost::algorithm::ends_with(filename, EVAL_FILENAME_SUFFIX)) {
3666 e.Null();
3667 } else {
3668 e.Int(1);
3670 e.RetC();
3671 e.setTempLocation(OptLocation());
3675 if (!m_evalStack.empty()) {
3676 InvariantViolation("Eval stack was not empty as expected before "
3677 "emitPostponed* phase");
3680 // Method bodies
3681 emitPostponedMeths();
3682 emitPostponedCtors();
3683 emitPostponedPinits();
3684 emitPostponedSinits();
3685 emitPostponedCinits();
3688 static StringData* getClassName(ExpressionPtr e) {
3689 ClassScopeRawPtr cls;
3690 if (e->isThis()) {
3691 cls = e->getClassScope();
3693 if (cls && !cls->isTrait()) {
3694 return makeStaticString(cls->getScopeName());
3696 return nullptr;
3699 void EmitterVisitor::fixReturnType(Emitter& e, FunctionCallPtr fn,
3700 Func* builtinFunc) {
3701 int ref = -1;
3702 if (fn->hasAnyContext(Expression::RefValue |
3703 Expression::DeepReference |
3704 Expression::LValue |
3705 Expression::OprLValue |
3706 Expression::UnsetContext)) {
3707 return;
3709 if (builtinFunc) {
3710 ref = (builtinFunc->attrs() & AttrReference) != 0;
3711 } else if (fn->isValid() && fn->getFuncScope()) {
3712 ref = fn->getFuncScope()->isRefReturn();
3713 } else if (!fn->getOriginalName().empty()) {
3714 FunctionScope::FunctionInfoPtr fi =
3715 FunctionScope::GetFunctionInfo(fn->getOriginalName());
3716 if (!fi || !fi->getMaybeRefReturn()) ref = false;
3719 if (!fn->isUnused() &&
3720 ref >= 0 &&
3721 (!ref || !fn->hasAnyContext(Expression::AccessContext |
3722 Expression::ObjectContext))) {
3723 /* we dont support V in M-vectors, so leave it as an R in that
3724 case */
3725 assert(m_evalStack.get(m_evalStack.size() - 1) == StackSym::R);
3726 if (ref) {
3727 e.BoxRNop();
3728 } else {
3729 e.UnboxRNop();
3734 void EmitterVisitor::visitKids(ConstructPtr c) {
3735 for (int i = 0, nk = c->getKidCount(); i < nk; i++) {
3736 ConstructPtr kid(c->getNthKid(i));
3737 visit(kid);
3741 template<uint32_t MaxMakeSize, class Fun>
3742 bool checkKeys(ExpressionPtr init_expr, bool check_size, Fun fun) {
3743 if (init_expr->getKindOf() != Expression::KindOfExpressionList) {
3744 return false;
3747 auto el = static_pointer_cast<ExpressionList>(init_expr);
3748 int n = el->getCount();
3749 if (n < 1 || (check_size && n > MaxMakeSize)) {
3750 return false;
3753 for (int i = 0, n = el->getCount(); i < n; ++i) {
3754 ExpressionPtr ex = (*el)[i];
3755 if (ex->getKindOf() != Expression::KindOfArrayPairExpression) {
3756 return false;
3758 auto ap = static_pointer_cast<ArrayPairExpression>(ex);
3759 if (ap->isRef()) return false;
3760 if (!fun(ap)) return false;
3762 return true;
3766 * isPackedInit() returns true if this expression list looks like an
3767 * array with no keys and no ref values; e.g. array(x,y,z).
3769 * In this case we can NewPackedArray to create the array. The elements are
3770 * pushed on the stack, so we arbitrarily limit this to a small multiple of
3771 * MixedArray::SmallSize (12).
3773 bool isPackedInit(ExpressionPtr init_expr, int* size,
3774 bool check_size = true) {
3775 *size = 0;
3776 return checkKeys<MixedArray::MaxMakeSize>(init_expr, check_size,
3777 [&](ArrayPairExpressionPtr ap) {
3778 Variant key;
3780 // If we have a key...
3781 if (ap->getName() != nullptr) {
3782 // ...and it has no scalar value, bail.
3783 if (!ap->getName()->getScalarValue(key)) return false;
3785 if (key.isInteger()) {
3786 // If it's an integer key, check if it's the next packed index.
3787 if (key.asInt64Val() != *size) return false;
3788 } else if (key.isBoolean()) {
3789 // Bool to Int conversion
3790 if (static_cast<int>(key.asBooleanVal()) != *size) return false;
3791 } else {
3792 // Give up if it's not a string.
3793 if (!key.isString()) return false;
3795 int64_t i; double d;
3796 auto numtype = key.getStringData()->isNumericWithVal(i, d, false);
3798 // If it's a string of the next packed index,
3799 if (numtype != KindOfInt64 || i != *size) return false;
3803 (*size)++;
3804 return true;
3809 * isStructInit() is like isPackedInit(), but returns true if the keys are
3810 * all static strings with no duplicates.
3812 bool isStructInit(ExpressionPtr init_expr, std::vector<std::string>& keys) {
3813 return checkKeys<MixedArray::MaxStructMakeSize>(init_expr, true,
3814 [&](ArrayPairExpressionPtr ap) {
3815 auto key = ap->getName();
3816 if (key == nullptr || !key->isLiteralString()) return false;
3817 auto name = key->getLiteralString();
3818 int64_t ival;
3819 double dval;
3820 auto kind = is_numeric_string(name.data(), name.size(), &ival, &dval, 0);
3821 if (kind != KindOfNull) return false; // don't allow numeric keys
3822 if (std::find(keys.begin(), keys.end(), name) != keys.end()) return false;
3823 keys.push_back(name);
3824 return true;
3828 void EmitterVisitor::emitCall(Emitter& e,
3829 FunctionCallPtr func,
3830 ExpressionListPtr params,
3831 Offset fpiStart) {
3832 auto const numParams = params ? params->getCount() : 0;
3833 auto const unpack = func->hasUnpack();
3834 if (!func->checkUnpackParams()) {
3835 throw IncludeTimeFatalException(
3836 func, "Only the last parameter in a function call is allowed to use ...");
3839 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
3840 for (int i = 0; i < numParams; i++) {
3841 auto param = (*params)[i];
3842 emitFuncCallArg(e, param, i, param->isUnpack());
3846 if (unpack) {
3847 e.FCallUnpack(numParams);
3848 } else {
3849 e.FCall(numParams);
3853 bool EmitterVisitor::visit(ConstructPtr node) {
3854 if (!node) return false;
3856 SCOPE_ASSERT_DETAIL("visit Construct") { return node->getText(); };
3858 Emitter e(node, m_ue, *this);
3860 switch (node->getKindOf()) {
3861 case Construct::KindOfBlockStatement:
3862 case Construct::KindOfStatementList:
3863 visitKids(node);
3864 return false;
3866 case Construct::KindOfTypedefStatement: {
3867 emitMakeUnitFatal(e, "Type statements are currently only allowed at "
3868 "the top-level");
3869 return false;
3872 case Construct::KindOfDeclareStatement: {
3873 auto ds = static_pointer_cast<DeclareStatement>(node);
3874 for (auto& decl : ds->getDeclareMap()) {
3875 if (decl.first == "strict_types") {
3876 emitMakeUnitFatal(e, "strict_types declaration must not use "
3877 "block mode");
3881 visit(ds->getBlock());
3882 return false;
3885 case Construct::KindOfContinueStatement:
3886 case Construct::KindOfBreakStatement: {
3887 auto s = static_pointer_cast<Statement>(node);
3888 auto bs = static_pointer_cast<BreakStatement>(s);
3889 uint64_t destLevel = bs->getDepth();
3891 if (destLevel > m_regions.back()->getMaxBreakContinueDepth()) {
3892 std::ostringstream msg;
3893 msg << "Cannot break/continue " << destLevel << " level";
3894 if (destLevel > 1) {
3895 msg << "s";
3897 emitMakeUnitFatal(e, msg.str().c_str());
3898 return false;
3901 if (bs->is(Construct::KindOfBreakStatement)) {
3902 emitBreak(e, destLevel, bs);
3903 } else {
3904 emitContinue(e, destLevel, bs);
3907 return false;
3910 case Construct::KindOfDoStatement: {
3911 auto s = static_pointer_cast<Statement>(node);
3912 auto region = createRegion(s, Region::Kind::LoopOrSwitch);
3913 auto ds = static_pointer_cast<DoStatement>(s);
3915 Label top(e);
3916 Label& condition =
3917 registerContinue(ds, region.get(), 1, false)->m_label;
3918 Label& exit =
3919 registerBreak(ds, region.get(), 1, false)->m_label;
3921 enterRegion(region);
3922 SCOPE_EXIT { leaveRegion(region); };
3923 visit(ds->getBody());
3925 condition.set(e);
3927 ExpressionPtr c = ds->getCondExp();
3928 Emitter condEmitter(c, m_ue, *this);
3929 visitIfCondition(c, condEmitter, top, exit, false);
3932 if (exit.isUsed()) exit.set(e);
3933 return false;
3936 case Construct::KindOfCaseStatement: {
3937 // Should never be called. Handled in visitSwitch.
3938 not_reached();
3941 case Construct::KindOfCatchStatement: {
3942 // Store the current exception object in the appropriate local variable
3943 auto cs = static_pointer_cast<CatchStatement>(node);
3944 StringData* vName = makeStaticString(cs->getVariable()->getName());
3945 Id i = m_curFunc->lookupVarId(vName);
3946 emitVirtualLocal(i);
3947 e.Catch();
3948 emitSet(e);
3949 emitPop(e);
3950 visit(cs->getStmt());
3951 return false;
3954 case Construct::KindOfEchoStatement: {
3955 auto es = static_pointer_cast<EchoStatement>(node);
3956 auto exps = es->getExpressionList();
3957 int count = exps->getCount();
3958 for (int i = 0; i < count; i++) {
3959 visit((*exps)[i]);
3960 emitConvertToCell(e);
3961 e.Print();
3962 e.PopC();
3964 return false;
3967 case Construct::KindOfExpStatement: {
3968 auto s = static_pointer_cast<Statement>(node);
3969 auto es = static_pointer_cast<ExpStatement>(s);
3970 if (visit(es->getExpression())) {
3971 // reachability tracking isn't very sophisticated; emitting a pop
3972 // when we're unreachable will make the emitter think the next
3973 // position is reachable.
3974 // In that case, it will spit out Null;RetC at the end of the
3975 // function, which can cause issues if an asm expression has
3976 // already output fault funclets.
3977 if (currentPositionIsReachable()) {
3978 emitPop(e);
3979 } else {
3980 popEvalStack(StackSym::C);
3983 return false;
3986 case Construct::KindOfForStatement: {
3987 auto s = static_pointer_cast<Statement>(node);
3988 auto region = createRegion(s, Region::Kind::LoopOrSwitch);
3989 auto fs = static_pointer_cast<ForStatement>(s);
3991 if (visit(fs->getInitExp())) {
3992 emitPop(e);
3994 Label preCond(e);
3995 Label& preInc = registerContinue(fs, region.get(), 1, false)->m_label;
3996 Label& fail = registerBreak(fs, region.get(), 1, false)->m_label;
3997 ExpressionPtr condExp = fs->getCondExp();
3998 auto emit_cond = [&] (Label& tru, bool truFallthrough) {
3999 if (!condExp) return;
4000 Emitter condEmitter(condExp, m_ue, *this);
4001 visitIfCondition(condExp, condEmitter, tru, fail, truFallthrough);
4003 Label top;
4004 emit_cond(top, true);
4005 top.set(e);
4007 enterRegion(region);
4008 SCOPE_EXIT { leaveRegion(region); };
4009 visit(fs->getBody());
4011 preInc.set(e);
4012 if (visit(fs->getIncExp())) {
4013 emitPop(e);
4015 if (!condExp) {
4016 e.Jmp(top);
4017 } else {
4018 emit_cond(top, false);
4020 if (fail.isUsed()) fail.set(e);
4021 return false;
4024 case Construct::KindOfForEachStatement: {
4025 auto fe = static_pointer_cast<ForEachStatement>(node);
4026 if (fe->isAwaitAs()) {
4027 emitForeachAwaitAs(e, fe);
4028 } else {
4029 emitForeach(e, fe);
4031 return false;
4034 case Construct::KindOfGlobalStatement: {
4035 auto vars = static_pointer_cast<GlobalStatement>(node)->getVars();
4036 for (int i = 0, n = vars->getCount(); i < n; i++) {
4037 ExpressionPtr var((*vars)[i]);
4038 if (var->is(Construct::KindOfSimpleVariable)) {
4039 auto sv = static_pointer_cast<SimpleVariable>(var);
4040 if (sv->isSuperGlobal()) {
4041 continue;
4043 StringData* nLiteral = makeStaticString(sv->getName());
4044 Id i = m_curFunc->lookupVarId(nLiteral);
4045 emitVirtualLocal(i);
4046 e.String(nLiteral);
4047 markGlobalName(e);
4048 e.VGetG();
4049 emitBind(e);
4050 e.PopV();
4051 } else if (var->is(Construct::KindOfDynamicVariable)) {
4052 // global $<exp> =& $GLOBALS[<exp>]
4053 auto dv = static_pointer_cast<DynamicVariable>(var);
4054 // Get the variable name as a cell, for the LHS
4055 visit(dv->getSubExpression());
4056 emitConvertToCell(e);
4057 // Copy the variable name, for indexing into $GLOBALS
4058 e.Dup();
4059 markNameSecond(e);
4060 markGlobalName(e);
4061 e.VGetG();
4062 e.BindN();
4063 e.PopV();
4064 } else {
4065 not_implemented();
4068 return false;
4071 case Construct::KindOfIfStatement: {
4072 auto ifp = static_pointer_cast<IfStatement>(node);
4073 StatementListPtr branches(ifp->getIfBranches());
4074 int nb = branches->getCount();
4075 Label done;
4076 for (int i = 0; i < nb; i++) {
4077 auto branch = static_pointer_cast<IfBranchStatement>((*branches)[i]);
4078 Label fals;
4079 if (branch->getCondition()) {
4080 Label tru;
4081 Emitter condEmitter(branch->getCondition(), m_ue, *this);
4082 visitIfCondition(branch->getCondition(), condEmitter,
4083 tru, fals, true);
4084 if (tru.isUsed()) {
4085 tru.set(e);
4088 visit(branch->getStmt());
4089 if (currentPositionIsReachable() && i + 1 < nb) {
4090 e.Jmp(done);
4092 if (fals.isUsed()) {
4093 fals.set(e);
4096 if (done.isUsed()) {
4097 done.set(e);
4099 return false;
4102 case Construct::KindOfIfBranchStatement:
4103 not_reached(); // handled by KindOfIfStatement
4105 case Construct::KindOfReturnStatement: {
4106 auto r = static_pointer_cast<ReturnStatement>(node);
4108 char retSym = StackSym::C;
4109 if (visit(r->getRetExp())) {
4110 if (r->getRetExp()->getContext() & Expression::RefValue &&
4111 // Generators don't support returning by references
4112 !m_curFunc->isGenerator) {
4113 emitConvertToVar(e);
4114 retSym = StackSym::V;
4115 } else {
4116 emitConvertToCell(e);
4118 } else {
4119 e.Null();
4121 assert(m_evalStack.size() == 1);
4122 assert(IMPLIES(m_curFunc->isAsync || m_curFunc->isGenerator,
4123 retSym == StackSym::C));
4124 emitReturn(e, retSym, r);
4125 return false;
4128 case Construct::KindOfStaticStatement: {
4129 auto vars = static_pointer_cast<StaticStatement>(node)->getVars();
4130 for (int i = 0, n = vars->getCount(); i < n; i++) {
4131 ExpressionPtr se((*vars)[i]);
4132 assert(se->is(Construct::KindOfAssignmentExpression));
4133 auto ae = static_pointer_cast<AssignmentExpression>(se);
4134 ExpressionPtr var(ae->getVariable());
4135 ExpressionPtr value(ae->getValue());
4136 assert(var->is(Construct::KindOfSimpleVariable));
4137 auto sv = static_pointer_cast<SimpleVariable>(var);
4138 StringData* name = makeStaticString(sv->getName());
4139 Id local = m_curFunc->lookupVarId(name);
4141 if (m_staticEmitted.insert(sv->getName()).second) {
4142 Func::SVInfo svInfo;
4143 svInfo.name = name;
4144 std::ostringstream os;
4145 CodeGenerator cg(&os, CodeGenerator::PickledPHP);
4146 auto ar = std::make_shared<AnalysisResult>();
4147 value->outputPHP(cg, ar);
4148 svInfo.phpCode = makeStaticString(os.str());
4149 m_curFunc->staticVars.push_back(svInfo);
4152 if (value->isScalar()) {
4153 emitVirtualLocal(local);
4154 visit(value);
4155 emitConvertToCell(e);
4156 e.StaticLocInit(local, name);
4157 } else {
4158 Label done;
4159 emitVirtualLocal(local);
4160 e.StaticLoc(local, name);
4161 e.JmpNZ(done);
4163 emitVirtualLocal(local);
4164 visit(value);
4165 emitConvertToCell(e);
4166 emitSet(e);
4167 emitPop(e);
4169 done.set(e);
4172 return false;
4175 case Construct::KindOfSwitchStatement: {
4176 auto s = static_pointer_cast<Statement>(node);
4177 auto region = createRegion(s, Region::Kind::LoopOrSwitch);
4178 auto sw = static_pointer_cast<SwitchStatement>(node);
4180 auto cases = sw->getCases();
4181 if (!cases) {
4182 visit(sw->getExp());
4183 emitPop(e);
4184 return false;
4186 uint32_t ncase = cases->getCount();
4187 std::vector<Label> caseLabels(ncase);
4188 Label& brkTarget = registerBreak(sw, region.get(), 1, false)->m_label;
4189 Label& contTarget =
4190 registerContinue(sw, region.get(), 1, false)->m_label;
4191 // There are two different ways this can go. If the subject is a simple
4192 // variable, then we have to evaluate it every time we compare against a
4193 // case condition. Otherwise, we evaluate it once and store it in an
4194 // unnamed local. This is because (a) switch statements are equivalent
4195 // to a series of if-elses, and (b) Zend has some weird evaluation order
4196 // rules. For example, "$a == ++$a" is true but "$a[0] == ++$a[0]" is
4197 // false. In particular, if a case condition modifies the switch
4198 // subject, things behave differently depending on whether the subject
4199 // is a simple variable.
4200 auto subject = sw->getExp();
4201 bool simpleSubject = subject->is(Construct::KindOfSimpleVariable)
4202 && !static_pointer_cast<SimpleVariable>(subject)->getAlwaysStash();
4203 Id tempLocal = -1;
4204 Offset start = InvalidAbsoluteOffset;
4206 bool enabled = RuntimeOption::EvalEmitSwitch;
4207 auto call = dynamic_pointer_cast<SimpleFunctionCall>(subject);
4209 SwitchState state;
4210 bool didSwitch = false;
4211 if (enabled) {
4212 MaybeDataType stype = analyzeSwitch(sw, state);
4213 if (stype) {
4214 e.incStat(stype == KindOfInt64 ? Stats::Switch_Integer
4215 : Stats::Switch_String,
4217 if (state.cases.empty()) {
4218 // If there are no non-default cases, evaluate the subject for
4219 // side effects and fall through. If there's a default case it
4220 // will be emitted immediately after this.
4221 visit(sw->getExp());
4222 emitPop(e);
4223 } else if (stype == KindOfInt64) {
4224 emitIntegerSwitch(e, sw, caseLabels, brkTarget, state);
4225 } else {
4226 assert(isStringType(*stype));
4227 emitStringSwitch(e, sw, caseLabels, brkTarget, state);
4229 didSwitch = true;
4232 if (!didSwitch) {
4233 e.incStat(Stats::Switch_Generic, 1);
4234 if (!simpleSubject) {
4235 // Evaluate the subject once and stash it in a local
4236 tempLocal = m_curFunc->allocUnnamedLocal();
4237 emitVirtualLocal(tempLocal);
4238 visit(subject);
4239 emitConvertToCell(e);
4240 emitSet(e);
4241 emitPop(e);
4242 start = m_ue.bcPos();
4245 int defI = -1;
4246 for (uint32_t i = 0; i < ncase; i++) {
4247 auto c = static_pointer_cast<CaseStatement>((*cases)[i]);
4248 ExpressionPtr condition = c->getCondition();
4249 if (condition) {
4250 if (simpleSubject) {
4251 // Evaluate the subject every time.
4252 visit(subject);
4253 emitConvertToCellOrLoc(e);
4254 visit(condition);
4255 emitConvertToCell(e);
4256 emitConvertSecondToCell(e);
4257 } else {
4258 emitVirtualLocal(tempLocal);
4259 emitCGet(e);
4260 visit(condition);
4261 emitConvertToCell(e);
4263 e.Eq();
4264 e.JmpNZ(caseLabels[i]);
4265 } else if (LIKELY(defI == -1)) {
4266 // Default clause.
4267 defI = i;
4268 } else {
4269 throw IncludeTimeFatalException(
4270 c, "Switch statements may only contain one default: clause");
4273 if (defI != -1) {
4274 e.Jmp(caseLabels[defI]);
4275 } else {
4276 e.Jmp(brkTarget);
4279 for (uint32_t i = 0; i < ncase; i++) {
4280 caseLabels[i].set(e);
4281 auto c = static_pointer_cast<CaseStatement>((*cases)[i]);
4282 enterRegion(region);
4283 SCOPE_EXIT { leaveRegion(region); };
4284 visit(c->getStatement());
4286 if (brkTarget.isUsed()) brkTarget.set(e);
4287 if (contTarget.isUsed()) contTarget.set(e);
4288 if (!didSwitch && !simpleSubject) {
4289 // Null out temp local, to invoke any needed refcounting
4290 assert(tempLocal >= 0);
4291 assert(start != InvalidAbsoluteOffset);
4292 newFaultRegionAndFunclet(start, m_ue.bcPos(),
4293 new UnsetUnnamedLocalThunklet(tempLocal));
4294 emitVirtualLocal(tempLocal);
4295 emitUnset(e);
4296 m_curFunc->freeUnnamedLocal(tempLocal);
4298 return false;
4301 case Construct::KindOfThrowStatement: {
4302 visitKids(node);
4303 emitConvertToCell(e);
4304 e.Throw();
4305 return false;
4308 case Construct::KindOfFinallyStatement: {
4309 auto s = static_pointer_cast<Statement>(node);
4310 auto region = createRegion(s, Region::Kind::Finally);
4311 enterRegion(region);
4312 SCOPE_EXIT { leaveRegion(region); };
4314 auto fs = static_pointer_cast<FinallyStatement>(node);
4315 visit(fs->getBody());
4316 return false;
4319 case Construct::KindOfTryStatement: {
4320 auto s = static_pointer_cast<Statement>(node);
4321 auto region = createRegion(s, Region::Kind::TryFinally);
4322 if (!m_evalStack.empty()) {
4323 InvariantViolation(
4324 "Emitter detected that the evaluation stack is not empty "
4325 "at the beginning of a try region: %d", m_ue.bcPos());
4328 auto ts = static_pointer_cast<TryStatement>(node);
4329 auto f = static_pointer_cast<FinallyStatement>(ts->getFinally());
4331 Offset start = m_ue.bcPos();
4332 Offset end;
4333 Label after;
4336 if (f) {
4337 enterRegion(region);
4339 SCOPE_EXIT {
4340 if (f) {
4341 leaveRegion(region);
4345 visit(ts->getBody());
4347 StatementListPtr catches = ts->getCatches();
4348 int catch_count = catches->getCount();
4349 if (catch_count > 0) {
4350 // include the jump out of the try-catch block in the
4351 // exception handler address range
4352 e.Jmp(after);
4354 end = m_ue.bcPos();
4355 if (!m_evalStack.empty()) {
4356 InvariantViolation("Emitter detected that the evaluation stack "
4357 "is not empty at the end of a try region: %d",
4358 end);
4361 if (catch_count > 0) {
4362 CatchRegion* r = new CatchRegion(start, end);
4363 m_catchRegions.push_back(r);
4365 bool firstHandler = true;
4366 for (int i = 0; i < catch_count; i++) {
4367 auto c = static_pointer_cast<CatchStatement>((*catches)[i]);
4368 StringData* eName = makeStaticString(c->getOriginalClassName());
4370 // If there's already a catch of this class, skip;
4371 // the first one wins
4372 if (r->m_names.find(eName) == r->m_names.end()) {
4373 // Don't let execution of the try body, or the
4374 // previous catch body,
4375 // fall into here.
4376 if (!firstHandler) {
4377 e.Jmp(after);
4378 } else {
4379 firstHandler = false;
4382 Label* label = new Label(e);
4383 r->m_names.insert(eName);
4384 r->m_catchLabels.push_back(std::pair<StringData*, Label*>(eName,
4385 label));
4386 visit(c);
4392 Offset end_catches = m_ue.bcPos();
4393 if (after.isUsed()) after.set(e);
4395 if (f) {
4396 region->m_finallyLabel.set(e);
4397 visit(f);
4398 emitFinallyEpilogue(e, region.get());
4399 auto func = getFunclet(f);
4400 if (func == nullptr) {
4401 auto thunklet =
4402 new FinallyThunklet(f, m_curFunc->numLiveIterators());
4403 func = addFunclet(f, thunklet);
4405 newFaultRegion(start, end_catches, &func->m_entry);
4408 return false;
4411 case Construct::KindOfUnsetStatement: {
4412 auto exps = static_pointer_cast<UnsetStatement>(node)->getExps();
4413 for (int i = 0, n = exps->getCount(); i < n; i++) {
4414 emitVisitAndUnset(e, (*exps)[i]);
4416 return false;
4419 case Construct::KindOfWhileStatement: {
4420 auto s = static_pointer_cast<Statement>(node);
4421 auto region = createRegion(s, Region::Kind::LoopOrSwitch);
4422 auto ws = static_pointer_cast<WhileStatement>(s);
4423 ExpressionPtr condExp(ws->getCondExp());
4424 Label& lcontinue = registerContinue(ws, region.get(), 1,
4425 false)->m_label;
4426 Label& fail = registerBreak(ws, region.get(), 1, false)->m_label;
4427 Label top;
4428 auto emit_cond = [&] (Label& tru, bool truFallthrough) {
4429 Emitter condEmitter(condExp, m_ue, *this);
4430 visitIfCondition(condExp, condEmitter, tru, fail, truFallthrough);
4432 emit_cond(top, true);
4433 top.set(e);
4435 enterRegion(region);
4436 SCOPE_EXIT { leaveRegion(region); };
4437 visit(ws->getBody());
4439 if (lcontinue.isUsed()) lcontinue.set(e);
4440 emit_cond(top, false);
4441 if (fail.isUsed()) fail.set(e);
4442 return false;
4445 case Construct::KindOfInterfaceStatement:
4446 case Construct::KindOfClassStatement: {
4447 emitClass(e, node->getClassScope(), false);
4448 return false;
4451 case Construct::KindOfClassVariable:
4452 case Construct::KindOfClassConstant:
4453 case Construct::KindOfMethodStatement:
4454 // handled by emitClass
4455 not_reached();
4457 case Construct::KindOfFunctionStatement: {
4458 auto m = static_pointer_cast<MethodStatement>(node);
4459 // Only called for fn defs not on the top level
4460 assert(!node->getClassScope()); // Handled directly by emitClass().
4461 StringData* nName = makeStaticString(m->getOriginalName());
4462 FuncEmitter* fe = m_ue.newFuncEmitter(nName);
4463 e.DefFunc(fe->id());
4464 postponeMeth(m, fe, false);
4465 return false;
4468 case Construct::KindOfGotoStatement: {
4469 auto g = static_pointer_cast<GotoStatement>(node);
4470 StringData* nName = makeStaticString(g->label());
4471 emitGoto(e, nName, g);
4472 return false;
4475 case Construct::KindOfLabelStatement: {
4476 auto l = static_pointer_cast<LabelStatement>(node);
4477 StringData* nName = makeStaticString(l->label());
4478 registerGoto(l, m_regions.back().get(), nName, false)
4479 ->m_label.set(e);
4480 return false;
4482 case Construct::KindOfStatement:
4483 case Construct::KindOfUseTraitStatement:
4484 case Construct::KindOfClassRequireStatement:
4485 case Construct::KindOfTraitPrecStatement:
4486 case Construct::KindOfTraitAliasStatement: {
4487 not_implemented();
4489 case Construct::KindOfUnaryOpExpression: {
4490 auto u = static_pointer_cast<UnaryOpExpression>(node);
4491 int op = u->getOp();
4493 if (op == T_UNSET) {
4494 // php doesnt have an unset expression, but hphp's optimizations
4495 // sometimes introduce them
4496 auto exp = u->getExpression();
4497 if (exp->is(Construct::KindOfExpressionList)) {
4498 auto exps = static_pointer_cast<ExpressionList>(exp);
4499 if (exps->getListKind() == ExpressionList::ListKindParam) {
4500 for (int i = 0, n = exps->getCount(); i < n; i++) {
4501 emitVisitAndUnset(e, (*exps)[i]);
4503 e.Null();
4504 return true;
4507 emitVisitAndUnset(e, exp);
4508 e.Null();
4509 return true;
4512 if (op == T_ARRAY) {
4513 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
4514 emitArrayInit(e, el);
4515 return true;
4518 if (op == T_DICT) {
4519 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
4520 emitArrayInit(e, el, HeaderKind::Dict);
4521 return true;
4524 if (op == T_VEC) {
4525 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
4526 emitArrayInit(e, el, HeaderKind::VecArray);
4527 return true;
4530 if (op == T_KEYSET) {
4531 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
4532 emitArrayInit(e, el, HeaderKind::Keyset);
4533 return true;
4536 if (op == T_ISSET) {
4537 auto list = dynamic_pointer_cast<ExpressionList>(u->getExpression());
4538 if (list) {
4539 // isset($a, $b, ...) ==> isset($a) && isset($b) && ...
4540 Label done;
4541 int n = list->getCount();
4542 for (int i = 0; i < n - 1; ++i) {
4543 visit((*list)[i]);
4544 emitIsset(e);
4545 e.Dup();
4546 e.JmpZ(done);
4547 emitPop(e);
4549 // Treat the last one specially; let it fall through
4550 visit((*list)[n - 1]);
4551 emitIsset(e);
4552 done.set(e);
4553 } else {
4554 // Simple case
4555 visit(u->getExpression());
4556 emitIsset(e);
4558 return true;
4559 } else if (op == '+' || op == '-') {
4560 e.Int(0);
4563 Id oldErrorLevelLoc = -1;
4564 Offset start = InvalidAbsoluteOffset;
4565 if (op == '@') {
4566 oldErrorLevelLoc = m_curFunc->allocUnnamedLocal();
4567 emitVirtualLocal(oldErrorLevelLoc);
4568 auto idx = m_evalStack.size() - 1;
4569 e.Silence(m_evalStack.getLoc(idx), SilenceOp::Start);
4570 start = m_ue.bcPos();
4573 ExpressionPtr exp = u->getExpression();
4574 if (exp && visit(exp)) {
4575 if (op != T_EMPTY && op != T_INC && op != T_DEC) {
4576 emitConvertToCell(e);
4578 } else if (op == T_EXIT) {
4579 // exit without an expression is treated as exit(0)
4580 e.Int(0);
4581 } else {
4582 // __FILE__ and __DIR__ are special unary ops that don't
4583 // have expressions
4584 assert(op == T_FILE || op == T_DIR);
4586 switch (op) {
4587 case T_INC:
4588 case T_DEC: {
4589 // $this++ is a no-op
4590 if (auto var = dynamic_pointer_cast<SimpleVariable>(exp)) {
4591 if (var->isThis()) break;
4594 auto const cop = [&] {
4595 if (op == T_INC) {
4596 if (RuntimeOption::IntsOverflowToInts) {
4597 return u->getFront() ? IncDecOp::PreInc : IncDecOp::PostInc;
4599 return u->getFront() ? IncDecOp::PreIncO : IncDecOp::PostIncO;
4601 if (RuntimeOption::IntsOverflowToInts) {
4602 return u->getFront() ? IncDecOp::PreDec : IncDecOp::PostDec;
4604 return u->getFront() ? IncDecOp::PreDecO : IncDecOp::PostDecO;
4605 }();
4606 emitIncDec(e, cop);
4607 break;
4609 case T_EMPTY: emitEmpty(e); break;
4610 case T_CLONE: e.Clone(); break;
4611 case '+':
4612 RuntimeOption::IntsOverflowToInts ? e.Add() : e.AddO();
4613 break;
4614 case '-':
4615 RuntimeOption::IntsOverflowToInts ? e.Sub() : e.SubO();
4616 break;
4617 case '!': e.Not(); break;
4618 case '~': e.BitNot(); break;
4619 case '(': break;
4620 case T_INT_CAST: e.CastInt(); break;
4621 case T_DOUBLE_CAST: e.CastDouble(); break;
4622 case T_STRING_CAST: e.CastString(); break;
4623 case T_ARRAY_CAST: e.CastArray(); break;
4624 case T_OBJECT_CAST: e.CastObject(); break;
4625 case T_BOOL_CAST: e.CastBool(); break;
4626 case T_UNSET_CAST: emitPop(e); e.Null(); break;
4627 case T_EXIT: e.Exit(); break;
4628 case '@': {
4629 assert(oldErrorLevelLoc >= 0);
4630 assert(start != InvalidAbsoluteOffset);
4631 newFaultRegionAndFunclet(start, m_ue.bcPos(),
4632 new RestoreErrorReportingThunklet(oldErrorLevelLoc));
4633 emitRestoreErrorReporting(e, oldErrorLevelLoc);
4634 m_curFunc->freeUnnamedLocal(oldErrorLevelLoc);
4635 break;
4637 case T_PRINT: e.Print(); break;
4638 case T_EVAL: e.Eval(); break;
4639 case T_FILE: {
4640 e.File();
4641 break;
4643 case T_DIR: {
4644 e.Dir();
4645 break;
4647 default:
4648 assert(false);
4650 return true;
4653 case Construct::KindOfAssignmentExpression: {
4654 auto ae = static_pointer_cast<AssignmentExpression>(node);
4655 ExpressionPtr rhs = ae->getValue();
4656 Id tempLocal = -1;
4657 Offset start = InvalidAbsoluteOffset;
4659 if (ae->isRhsFirst()) {
4660 assert(!rhs->hasContext(Expression::RefValue));
4661 tempLocal = emitVisitAndSetUnnamedL(e, rhs);
4662 start = m_ue.bcPos();
4665 visit(ae->getVariable());
4666 emitClsIfSPropBase(e);
4668 if (ae->isRhsFirst()) {
4669 emitPushAndFreeUnnamedL(e, tempLocal, start);
4670 } else {
4671 visit(rhs);
4674 if (rhs->hasContext(Expression::RefValue)) {
4675 emitConvertToVar(e);
4676 emitBind(e);
4677 if (ae->hasAnyContext(Expression::AccessContext|
4678 Expression::ObjectContext|
4679 Expression::ExistContext)) {
4681 * hphpc optimizations can result in
4682 * ($x =& $y)->foo or ($x =& $y)['foo'] or empty($x =& $y)
4684 emitConvertToCellIfVar(e);
4686 } else {
4687 emitConvertToCell(e);
4688 emitSet(e);
4690 return true;
4693 case Construct::KindOfBinaryOpExpression: {
4694 auto b = static_pointer_cast<BinaryOpExpression>(node);
4695 int op = b->getOp();
4696 if (b->isAssignmentOp()) {
4697 visit(b->getExp1());
4698 emitClsIfSPropBase(e);
4699 visit(b->getExp2());
4700 emitConvertToCell(e);
4701 emitSetOp(e, op);
4702 return true;
4705 if (b->isShortCircuitOperator()) {
4706 Label tru, fls, done;
4707 visitIfCondition(b, e, tru, fls, false);
4708 if (fls.isUsed()) fls.set(e);
4709 if (currentPositionIsReachable()) {
4710 e.False();
4711 e.Jmp(done);
4713 if (tru.isUsed()) tru.set(e);
4714 if (currentPositionIsReachable()) {
4715 e.True();
4717 done.set(e);
4718 return true;
4721 if (op == T_INSTANCEOF) {
4722 visit(b->getExp1());
4723 emitConvertToCell(e);
4724 ExpressionPtr second = b->getExp2();
4725 if (second->isScalar()) {
4726 auto scalar = dynamic_pointer_cast<ScalarExpression>(second);
4727 bool notQuoted = scalar && !scalar->isQuoted();
4728 std::string s = second->getLiteralString();
4730 const auto isame =
4731 [](const std::string& a, const std::string& b) {
4732 return (a.size() == b.size()) &&
4733 !strncasecmp(a.c_str(), b.c_str(), a.size());
4736 if (notQuoted && isame(s, "static")) {
4737 // Can't resolve this to a literal name at emission time
4738 static const StringData* fname
4739 = makeStaticString("get_called_class");
4740 e.FCallBuiltin(0, 0, fname);
4741 e.UnboxRNop();
4742 e.InstanceOf();
4743 } else if (s != "") {
4744 ClassScopeRawPtr cls = second->getClassScope();
4745 bool isTrait = cls && cls->isTrait();
4746 bool isSelf = notQuoted && isame(s, "self");
4747 bool isParent = notQuoted && isame(s, "parent");
4749 if (isTrait && (isSelf || isParent)) {
4750 emitConvertToCell(e);
4751 if (isSelf) {
4752 e.Self();
4753 } else if (isParent) {
4754 e.Parent();
4757 e.NameA();
4758 e.InstanceOf();
4759 } else {
4760 if (cls) {
4761 if (isSelf) {
4762 s = cls->getScopeName();
4763 } else if (isParent) {
4764 s = cls->getOriginalParent();
4768 StringData* nLiteral = makeStaticString(s);
4769 e.InstanceOfD(nLiteral);
4771 } else {
4772 visit(b->getExp2());
4773 emitConvertToCell(e);
4774 e.InstanceOf();
4776 } else {
4777 visit(b->getExp2());
4778 emitConvertToCell(e);
4779 e.InstanceOf();
4781 return true;
4784 if (op == T_COLLECTION) {
4785 emitCollectionInit(e, b);
4786 return true;
4789 if (op == T_PIPE) {
4790 Id pipeVar = emitVisitAndSetUnnamedL(e, b->getExp1());
4791 allocPipeLocal(pipeVar);
4792 visit(b->getExp2());
4793 emitConvertToCell(e);
4794 releasePipeLocal(pipeVar);
4795 emitPushAndFreeUnnamedL(e, pipeVar, m_ue.bcPos());
4796 e.PopC();
4797 return true;
4800 visit(b->getExp1());
4801 emitConvertToCellOrLoc(e);
4802 visit(b->getExp2());
4803 emitConvertToCell(e);
4804 emitConvertSecondToCell(e);
4805 switch (op) {
4806 case T_LOGICAL_XOR: e.Xor(); break;
4807 case '|': e.BitOr(); break;
4808 case '&': e.BitAnd(); break;
4809 case '^': e.BitXor(); break;
4810 case '.': e.Concat(); break;
4811 case '+':
4812 RuntimeOption::IntsOverflowToInts ? e.Add() : e.AddO();
4813 break;
4814 case '-':
4815 RuntimeOption::IntsOverflowToInts ? e.Sub() : e.SubO();
4816 break;
4817 case '*':
4818 RuntimeOption::IntsOverflowToInts ? e.Mul() : e.MulO();
4819 break;
4820 case '/': e.Div(); break;
4821 case '%': e.Mod(); break;
4822 case T_SL: e.Shl(); break;
4823 case T_SR: e.Shr(); break;
4824 case T_IS_IDENTICAL: e.Same(); break;
4825 case T_IS_NOT_IDENTICAL: e.NSame(); break;
4826 case T_IS_EQUAL: e.Eq(); break;
4827 case T_IS_NOT_EQUAL: e.Neq(); break;
4828 case '<': e.Lt(); break;
4829 case T_IS_SMALLER_OR_EQUAL: e.Lte(); break;
4830 case '>': e.Gt(); break;
4831 case T_IS_GREATER_OR_EQUAL: e.Gte(); break;
4832 case T_SPACESHIP: e.Cmp(); break;
4833 case T_POW: e.Pow(); break;
4834 default: assert(false);
4836 return true;
4839 case Construct::KindOfClassConstantExpression: {
4840 auto cc = static_pointer_cast<ClassConstantExpression>(node);
4841 auto const nName = makeStaticString(cc->getConName());
4842 auto const getOriginalClassName = [&] {
4843 const std::string& clsName = cc->getOriginalClassName();
4844 return makeStaticString(clsName);
4847 // We treat ::class as a class constant in the AST and the
4848 // parser, but at the bytecode and runtime level it isn't
4849 // one.
4850 auto const emitClsCns = [&] {
4851 if (cc->isColonColonClass()) {
4852 e.NameA();
4853 return;
4855 e.ClsCns(nName);
4857 auto const noClassAllowed = [&] {
4858 auto const nCls = getOriginalClassName();
4859 std::ostringstream s;
4860 s << "Cannot access " << nCls->data() << "::" << nName->data() <<
4861 " when no class scope is active";
4862 throw IncludeTimeFatalException(cc, s.str().c_str());
4865 if (cc->isStatic()) {
4866 // static::Constant
4867 e.LateBoundCls();
4868 emitClsCns();
4869 } else if (cc->getClass()) {
4870 // $x::Constant
4871 ExpressionPtr cls(cc->getClass());
4872 visit(cls);
4873 emitAGet(e);
4874 emitClsCns();
4875 } else if (cc->getOriginalClassScope() &&
4876 !cc->getOriginalClassScope()->isTrait()) {
4877 // C::Constant inside a class
4878 auto nCls = getOriginalClassName();
4879 if (cc->isColonColonClass()) {
4880 e.String(nCls);
4881 } else {
4882 e.ClsCnsD(nName, nCls);
4884 } else if (cc->isSelf()) {
4885 // self::Constant inside trait or pseudomain
4886 e.Self();
4887 if (cc->isColonColonClass() &&
4888 cc->getFunctionScope()->inPseudoMain()) {
4889 noClassAllowed();
4891 emitClsCns();
4892 } else if (cc->isParent()) {
4893 // parent::Constant inside trait or pseudomain
4894 e.Parent();
4895 if (cc->isColonColonClass() &&
4896 cc->getFunctionScope()->inPseudoMain()) {
4897 noClassAllowed();
4899 emitClsCns();
4900 } else {
4901 // C::Constant inside a trait or pseudomain
4902 // Be careful to keep this case here after the isSelf and
4903 // isParent cases because StaticClassName::resolveClass()
4904 // will set cc->originalClassName to the trait's name for
4905 // the isSelf and isParent cases, but self and parent must
4906 // be resolved dynamically when used inside of traits.
4907 auto nCls = getOriginalClassName();
4908 if (cc->isColonColonClass()) noClassAllowed();
4909 e.ClsCnsD(nName, nCls);
4911 return true;
4914 case Construct::KindOfConstantExpression: {
4915 auto c = static_pointer_cast<ConstantExpression>(node);
4916 if (c->isNull()) {
4917 e.Null();
4918 } else if (c->isBoolean()) {
4919 if (c->getBooleanValue()) {
4920 e.True();
4921 } else {
4922 e.False();
4924 return true;
4925 } else {
4926 std::string nameStr = c->getOriginalName();
4927 StringData* nName = makeStaticString(nameStr);
4928 if (c->hadBackslash()) {
4929 e.CnsE(nName);
4930 } else {
4931 const std::string& nonNSName = c->getNonNSOriginalName();
4932 if (nonNSName != nameStr) {
4933 StringData* nsName = nName;
4934 nName = makeStaticString(nonNSName);
4935 e.CnsU(nsName, nName);
4936 } else {
4937 e.Cns(makeStaticString(c->getName()));
4941 return true;
4944 case Construct::KindOfEncapsListExpression: {
4945 auto el = static_pointer_cast<EncapsListExpression>(node);
4946 auto args = el->getExpressions();
4947 int n = args ? args->getCount() : 0;
4948 int i = 0;
4949 FPIRegionRecorder* fpi = nullptr;
4950 if (el->getType() == '`') {
4951 const static StringData* s_shell_exec =
4952 makeStaticString("shell_exec");
4953 Offset fpiStart = m_ue.bcPos();
4954 e.FPushFuncD(1, s_shell_exec);
4955 fpi = new FPIRegionRecorder(this, m_ue, m_evalStack, fpiStart);
4958 if (n) {
4959 visit((*args)[i++]);
4960 emitConvertToCellOrLoc(e);
4961 if (i == n) {
4962 emitConvertToCell(e);
4963 e.CastString();
4964 } else {
4965 while (i < n) {
4966 visit((*args)[i++]);
4967 emitConvertToCell(e);
4968 emitConvertSecondToCell(e);
4969 e.Concat();
4972 } else {
4973 e.String(staticEmptyString());
4976 if (el->getType() == '`') {
4977 emitConvertToCell(e);
4978 e.FPassC(0);
4979 delete fpi;
4980 e.FCall(1);
4982 return true;
4985 case Construct::KindOfArrayElementExpression: {
4986 auto ae = static_pointer_cast<ArrayElementExpression>(node);
4987 if (!ae->isSuperGlobal() || !ae->getOffset()) {
4988 visit(ae->getVariable());
4989 // XHP syntax allows for expressions like "($a =& $b)[0]". We
4990 // handle this by unboxing the var produced by "($a =& $b)".
4991 emitConvertToCellIfVar(e);
4994 ExpressionPtr offset = ae->getOffset();
4995 Variant v;
4996 if (!ae->isSuperGlobal() && offset &&
4997 offset->getScalarValue(v) && (v.isInteger() || v.isString())) {
4998 if (v.isString()) {
4999 m_evalStack.push(StackSym::T);
5000 m_evalStack.setString(
5001 makeStaticString(v.toCStrRef().get()));
5002 } else {
5003 m_evalStack.push(StackSym::I);
5004 m_evalStack.setInt(v.asInt64Val());
5006 markElem(e);
5007 } else if (visit(offset)) {
5008 emitConvertToCellOrLoc(e);
5009 if (ae->isSuperGlobal()) {
5010 markGlobalName(e);
5011 } else {
5012 markElem(e);
5014 } else {
5015 markNewElem(e);
5017 if (!ae->hasAnyContext(Expression::AccessContext|
5018 Expression::ObjectContext)) {
5019 m_tempLoc = ae->getRange();
5021 return true;
5024 case Construct::KindOfSimpleFunctionCall: {
5025 auto call = static_pointer_cast<SimpleFunctionCall>(node);
5026 auto params = call->getParams();
5028 if (call->isFatalFunction()) {
5029 if (params && params->getCount() == 1) {
5030 ExpressionPtr p = (*params)[0];
5031 Variant v;
5032 if (p->getScalarValue(v)) {
5033 assert(v.isString());
5034 StringData* msg = makeStaticString(v.toString());
5035 auto exn = IncludeTimeFatalException(call, "%s", msg->data());
5036 exn.setParseFatal(call->isParseFatalFunction());
5037 throw exn;
5039 not_reached();
5041 } else if (emitCallUserFunc(e, call)) {
5042 return true;
5043 } else if (call->isCallToFunction("array_key_exists")) {
5044 if (params && params->getCount() == 2) {
5045 visit((*params)[0]);
5046 emitConvertToCell(e);
5047 visit((*params)[1]);
5048 emitConvertToCell(e);
5049 call->changeToBytecode();
5050 e.AKExists();
5051 return true;
5053 } else if (call->isCallToFunction("hh\\asm")) {
5054 if (emitInlineHHAS(e, call)) return true;
5055 } else if (call->isCallToFunction("hh\\invariant")) {
5056 if (emitHHInvariant(e, call)) return true;
5057 } else if (call->isCallToFunction("hh\\idx") &&
5058 !Option::JitEnableRenameFunction) {
5059 if (params && (params->getCount() == 2 || params->getCount() == 3)) {
5060 visit((*params)[0]);
5061 emitConvertToCell(e);
5062 visit((*params)[1]);
5063 emitConvertToCell(e);
5064 if (params->getCount() == 2) {
5065 e.Null();
5066 } else {
5067 visit((*params)[2]);
5068 emitConvertToCell(e);
5070 call->changeToBytecode();
5071 e.Idx();
5072 return true;
5074 } else if (call->isCallToFunction("hphp_array_idx")) {
5075 if (params && params->getCount() == 3) {
5076 visit((*params)[0]);
5077 emitConvertToCell(e);
5078 visit((*params)[1]);
5079 emitConvertToCell(e);
5080 visit((*params)[2]);
5081 emitConvertToCell(e);
5082 call->changeToBytecode();
5083 e.ArrayIdx();
5084 return true;
5086 } else if (call->isCallToFunction("max")) {
5087 if (params && params->getCount() == 2) {
5088 emitFuncCall(e, call, "__SystemLib\\max2", params);
5089 return true;
5091 } else if (call->isCallToFunction("min")) {
5092 if (params && params->getCount() == 2) {
5093 emitFuncCall(e, call, "__SystemLib\\min2", params);
5094 return true;
5096 } else if (call->isCallToFunction("define")) {
5097 if (params && params->getCount() == 2) {
5098 ExpressionPtr p0 = (*params)[0];
5099 Variant v0;
5100 if (p0->getScalarValue(v0) && v0.isString()) {
5101 const StringData* cname =
5102 makeStaticString(v0.toString());
5103 visit((*params)[1]);
5104 emitConvertToCell(e);
5105 e.DefCns(cname);
5106 return true;
5109 } else if (call->isCallToFunction("assert")) {
5110 // Special-case some logic around emitting assert(), or jumping around
5111 // it. This all applies only for direct calls to assert() -- dynamic
5112 // calls don't get this special logic, and don't in PHP7 either.
5114 if (!RuntimeOption::AssertEmitted) {
5115 e.True();
5116 return true;
5119 // We need to emit an ini_get around all asserts to check if the
5120 // zend.assertions option is enabled -- you can switch between 0 and 1
5121 // at runtime, and having it set to 0 disables the assert from running,
5122 // including side effects of function arguments, so we need to jump
5123 // around it if so. (The -1 value of zend.assertions corresponds to
5124 // AssertEmitted being set to 0 above, and is not changeable at
5125 // runtime.)
5126 Label disabled, after;
5127 e.String(s_zend_assertions.get());
5128 e.FCallBuiltin(1, 1, s_ini_get.get());
5129 e.UnboxRNop();
5130 e.Int(0);
5131 e.Gt();
5132 e.JmpZ(disabled);
5134 emitFuncCall(e, call, "__SystemLib\\assert", call->getParams());
5135 emitConvertToCell(e);
5136 e.Jmp(after);
5138 disabled.set(e);
5139 e.True();
5141 after.set(e);
5142 return true;
5143 } else if (emitSystemLibVarEnvFunc(e, call)) {
5144 return true;
5145 } else if (call->isCallToFunction("array_slice") &&
5146 params && params->getCount() == 2 &&
5147 !Option::JitEnableRenameFunction) {
5148 ExpressionPtr p0 = (*params)[0];
5149 ExpressionPtr p1 = (*params)[1];
5150 Variant v1;
5151 if (p0->getKindOf() == Construct::KindOfSimpleFunctionCall &&
5152 p1->getScalarValue(v1) && v1.isInteger()) {
5153 auto innerCall = static_pointer_cast<SimpleFunctionCall>(p0);
5154 auto innerParams = innerCall->getParams();
5155 if (innerCall->isCallToFunction("func_get_args") &&
5156 (!innerParams || innerParams->getCount() == 0)) {
5157 params->removeElement(0);
5158 emitFuncCall(e, innerCall,
5159 "__SystemLib\\func_slice_args", params);
5160 return true;
5163 // fall through
5164 } else if ((call->isCallToFunction("class_exists") ||
5165 call->isCallToFunction("interface_exists") ||
5166 call->isCallToFunction("trait_exists"))
5167 && params
5168 && (params->getCount() == 1 || params->getCount() == 2)) {
5169 // Push name
5170 emitNameString(e, (*params)[0]);
5171 emitConvertToCell(e);
5172 e.CastString();
5174 // Push autoload, defaulting to true
5175 if (params->getCount() == 1) {
5176 e.True();
5177 } else {
5178 visit((*params)[1]);
5179 emitConvertToCell(e);
5180 e.CastBool();
5182 if (call->isCallToFunction("class_exists")) {
5183 e.OODeclExists(OODeclExistsOp::Class);
5184 } else if (call->isCallToFunction("interface_exists")) {
5185 e.OODeclExists(OODeclExistsOp::Interface);
5186 } else {
5187 assert(call->isCallToFunction("trait_exists"));
5188 e.OODeclExists(OODeclExistsOp::Trait);
5190 return true;
5191 } else if (call->isCallToFunction("get_class") &&
5192 !params &&
5193 call->getClassScope() &&
5194 !call->getClassScope()->isTrait()) {
5195 StringData* name =
5196 makeStaticString(call->getClassScope()->getScopeName());
5197 e.String(name);
5198 return true;
5199 } else if (((call->isCallToFunction("dict") &&
5200 (m_ue.m_isHHFile || Option::EnableHipHopSyntax)) ||
5201 call->isCallToFunction("HH\\dict")) &&
5202 params && params->getCount() == 1) {
5203 visit((*params)[0]);
5204 emitConvertToCell(e);
5205 e.CastDict();
5206 return true;
5207 } else if (((call->isCallToFunction("vec") &&
5208 (m_ue.m_isHHFile || Option::EnableHipHopSyntax)) ||
5209 call->isCallToFunction("HH\\vec")) &&
5210 params && params->getCount() == 1) {
5211 visit((*params)[0]);
5212 emitConvertToCell(e);
5213 e.CastVec();
5214 return true;
5215 } else if (((call->isCallToFunction("keyset") &&
5216 (m_ue.m_isHHFile || Option::EnableHipHopSyntax)) ||
5217 call->isCallToFunction("HH\\keyset")) &&
5218 params && params->getCount() == 1) {
5219 visit((*params)[0]);
5220 emitConvertToCell(e);
5221 e.CastKeyset();
5222 return true;
5223 } else if (((call->isCallToFunction("is_vec") &&
5224 (m_ue.m_isHHFile || Option::EnableHipHopSyntax)) ||
5225 call->isCallToFunction("HH\\is_vec")) &&
5226 params && params->getCount() == 1) {
5227 visit((*call->getParams())[0]);
5228 emitIsType(e, IsTypeOp::Vec);
5229 return true;
5230 } else if (((call->isCallToFunction("is_dict") &&
5231 (m_ue.m_isHHFile || Option::EnableHipHopSyntax)) ||
5232 call->isCallToFunction("HH\\is_dict")) &&
5233 params && params->getCount() == 1) {
5234 visit((*call->getParams())[0]);
5235 emitIsType(e, IsTypeOp::Dict);
5236 return true;
5237 } else if (((call->isCallToFunction("is_keyset") &&
5238 (m_ue.m_isHHFile || Option::EnableHipHopSyntax)) ||
5239 call->isCallToFunction("HH\\is_keyset")) &&
5240 params && params->getCount() == 1) {
5241 visit((*call->getParams())[0]);
5242 emitIsType(e, IsTypeOp::Keyset);
5243 return true;
5245 #define TYPE_CONVERT_INSTR(what, What) \
5246 else if (call->isCallToFunction(#what"val") && \
5247 params && params->getCount() == 1) { \
5248 visit((*params)[0]); \
5249 emitConvertToCell(e); \
5250 e.Cast ## What(); \
5251 return true; \
5253 TYPE_CONVERT_INSTR(bool, Bool)
5254 TYPE_CONVERT_INSTR(int, Int)
5255 TYPE_CONVERT_INSTR(double, Double)
5256 TYPE_CONVERT_INSTR(float, Double)
5257 TYPE_CONVERT_INSTR(str, String)
5258 #undef TYPE_CONVERT_INSTR
5260 #define TYPE_CHECK_INSTR(what, What) \
5261 else if (call->isCallToFunction("is_"#what) && \
5262 params && params->getCount() == 1) { \
5263 visit((*call->getParams())[0]); \
5264 emitIsType(e, IsTypeOp::What); \
5265 return true; \
5268 TYPE_CHECK_INSTR(null, Null)
5269 TYPE_CHECK_INSTR(object, Obj)
5270 TYPE_CHECK_INSTR(array, Arr)
5271 TYPE_CHECK_INSTR(string, Str)
5272 TYPE_CHECK_INSTR(int, Int)
5273 TYPE_CHECK_INSTR(integer, Int)
5274 TYPE_CHECK_INSTR(long, Int)
5275 TYPE_CHECK_INSTR(bool, Bool)
5276 TYPE_CHECK_INSTR(double, Dbl)
5277 TYPE_CHECK_INSTR(real, Dbl)
5278 TYPE_CHECK_INSTR(float, Dbl)
5279 TYPE_CHECK_INSTR(scalar, Scalar)
5280 #undef TYPE_CHECK_INSTR
5281 else if (emitConstantFuncCall(e, call)) {
5282 return true;
5285 // fall through
5286 case Construct::KindOfDynamicFunctionCall: {
5287 emitFuncCall(e, static_pointer_cast<FunctionCall>(node));
5288 return true;
5291 case Construct::KindOfIncludeExpression: {
5292 auto ie = static_pointer_cast<IncludeExpression>(node);
5293 if (ie->isReqLit()) {
5294 StringData* nValue = makeStaticString(ie->includePath());
5295 e.String(nValue);
5296 } else {
5297 visit(ie->getExpression());
5298 emitConvertToCell(e);
5300 switch (ie->getOp()) {
5301 case T_INCLUDE:
5302 e.Incl();
5303 break;
5304 case T_INCLUDE_ONCE:
5305 e.InclOnce();
5306 break;
5307 case T_REQUIRE:
5308 e.Req();
5309 break;
5310 case T_REQUIRE_ONCE:
5311 if (ie->isDocumentRoot()) {
5312 e.ReqDoc();
5313 } else {
5314 e.ReqOnce();
5316 break;
5318 return true;
5321 case Construct::KindOfListAssignment: {
5322 auto la = static_pointer_cast<ListAssignment>(node);
5323 auto rhs = la->getArray();
5325 // listAssignmentVisitLHS should have handled this
5326 assert(rhs);
5328 bool nullRHS = la->getRHSKind() == ListAssignment::Null;
5329 // If the RHS is not a simple variable, we need to evaluate it and assign
5330 // it to a temp local. If it is, whether or not we directly use it or copy
5331 // it into a temp local is visible in perverse statements like:
5332 // list($a, $b) = $a
5333 // The behavior of that changed between PHP5 and PHP7; in PHP5 we directly
5334 // use the temp local, in PHP7 we need to copy it.
5335 bool simpleRHS = rhs->is(Construct::KindOfSimpleVariable)
5336 && !static_pointer_cast<SimpleVariable>(rhs)->getAlwaysStash()
5337 && !RuntimeOption::PHP7_LTR_assign;
5338 Id tempLocal = -1;
5339 Offset start = InvalidAbsoluteOffset;
5341 if (!simpleRHS && la->isRhsFirst()) {
5342 tempLocal = emitVisitAndSetUnnamedL(e, rhs);
5343 start = m_ue.bcPos();
5346 // We use "index chains" to deal with nested list assignment. We will
5347 // end up with one index chain per expression we need to assign to.
5348 // The helper function will populate indexChains.
5350 // In PHP5 mode, this will also evaluate the LHS; in PHP7 mode, that is
5351 // always delayed until listAssignmentAssignElements below. This means
5352 // that isRhsFirst() has no effect in PHP7 mode. See comments in
5353 // listAssignmentVisitLHS and listAssignmentAssignElements for more
5354 // explanation.
5355 std::vector<IndexPair> indexPairs;
5356 IndexChain workingChain;
5357 listAssignmentVisitLHS(e, la, workingChain, indexPairs);
5359 if (!simpleRHS && !la->isRhsFirst()) {
5360 assert(tempLocal == -1);
5361 assert(start == InvalidAbsoluteOffset);
5362 tempLocal = emitVisitAndSetUnnamedL(e, rhs);
5363 start = m_ue.bcPos();
5366 // Assign elements.
5367 if (nullRHS) {
5368 listAssignmentAssignElements(e, indexPairs, nullptr);
5369 } else if (simpleRHS) {
5370 listAssignmentAssignElements(e, indexPairs, [&] { visit(rhs); });
5371 } else {
5372 listAssignmentAssignElements(
5373 e, indexPairs,
5374 [&] { emitVirtualLocal(tempLocal); }
5378 // Leave the RHS on the stack
5379 if (simpleRHS) {
5380 visit(rhs);
5381 } else {
5382 emitPushAndFreeUnnamedL(e, tempLocal, start);
5385 return true;
5388 case Construct::KindOfNewObjectExpression: {
5389 auto ne = static_pointer_cast<NewObjectExpression>(node);
5390 auto params = ne->getParams();
5391 int numParams = params ? params->getCount() : 0;
5392 ClassScopeRawPtr cls = ne->getClassScope();
5394 Offset fpiStart;
5395 if (ne->isStatic()) {
5396 // new static()
5397 e.LateBoundCls();
5398 fpiStart = m_ue.bcPos();
5399 e.FPushCtor(numParams);
5400 } else if (ne->getOriginalName().empty()) {
5401 // new $x()
5402 visit(ne->getNameExp());
5403 emitAGet(e);
5404 fpiStart = m_ue.bcPos();
5405 e.FPushCtor(numParams);
5406 } else if ((ne->isSelf() || ne->isParent()) &&
5407 (!cls || cls->isTrait() ||
5408 (ne->isParent() && cls->getOriginalParent().empty()))) {
5409 if (ne->isSelf()) {
5410 // new self() inside a trait or code statically not inside any class
5411 e.Self();
5412 } else {
5413 // new parent() inside a trait, code statically not inside any
5414 // class, or a class with no parent
5415 e.Parent();
5417 fpiStart = m_ue.bcPos();
5418 e.FPushCtor(numParams);
5419 } else {
5420 // new C() inside trait or pseudomain
5421 fpiStart = m_ue.bcPos();
5422 e.FPushCtorD(numParams,
5423 makeStaticString(ne->getOriginalClassName()));
5426 emitCall(e, ne, params, fpiStart);
5427 e.PopR();
5428 return true;
5431 case Construct::KindOfObjectMethodExpression: {
5432 auto om = static_pointer_cast<ObjectMethodExpression>(node);
5433 // $obj->name(...)
5434 // ^^^^
5435 visit(om->getObject());
5436 m_tempLoc = om->getRange();
5437 emitConvertToCell(e);
5438 ExpressionListPtr params(om->getParams());
5439 int numParams = params ? params->getCount() : 0;
5441 Offset fpiStart = 0;
5442 ExpressionPtr methName = om->getNameExp();
5443 bool useDirectForm = false;
5444 if (methName->is(Construct::KindOfScalarExpression)) {
5445 auto sval = static_pointer_cast<ScalarExpression>(methName);
5446 const std::string& methStr = sval->getOriginalLiteralString();
5447 if (!methStr.empty()) {
5448 // $obj->name(...)
5449 // ^^^^
5450 // Use getOriginalLiteralString(), which hasn't been
5451 // case-normalized, since __call() needs to preserve
5452 // the case.
5453 StringData* nameLiteral = makeStaticString(methStr);
5454 fpiStart = m_ue.bcPos();
5455 e.FPushObjMethodD(
5456 numParams,
5457 nameLiteral,
5458 om->isNullSafe() ? ObjMethodOp::NullSafe : ObjMethodOp::NullThrows
5460 useDirectForm = true;
5463 if (!useDirectForm) {
5464 // $obj->{...}(...)
5465 // ^^^^^
5466 visit(methName);
5467 emitConvertToCell(e);
5468 fpiStart = m_ue.bcPos();
5469 e.FPushObjMethod(
5470 numParams,
5471 om->isNullSafe() ? ObjMethodOp::NullSafe : ObjMethodOp::NullThrows
5474 // $obj->name(...)
5475 // ^^^^^
5476 emitCall(e, om, params, fpiStart);
5477 return true;
5480 case Construct::KindOfObjectPropertyExpression: {
5481 auto op = static_pointer_cast<ObjectPropertyExpression>(node);
5482 if (op->isNullSafe() &&
5483 op->hasAnyContext(
5484 Expression::RefValue
5485 | Expression::LValue
5486 | Expression::DeepReference
5487 ) && !op->hasContext(Expression::InvokeArgument)
5489 throw IncludeTimeFatalException(op,
5490 Strings::NULLSAFE_PROP_WRITE_ERROR);
5492 ExpressionPtr obj = op->getObject();
5493 auto sv = dynamic_pointer_cast<SimpleVariable>(obj);
5494 if (sv && sv->isThis() && sv->hasContext(Expression::ObjectContext)) {
5495 e.CheckThis();
5496 m_evalStack.push(StackSym::H);
5497 } else {
5498 visit(obj);
5500 StringData* clsName = getClassName(op->getObject());
5501 if (clsName) {
5502 m_evalStack.setKnownCls(clsName, false);
5504 emitNameString(e, op->getProperty(), true);
5505 if (!op->hasAnyContext(Expression::AccessContext|
5506 Expression::ObjectContext)) {
5507 m_tempLoc = op->getRange();
5509 markProp(
5511 op->isNullSafe()
5512 ? PropAccessType::NullSafe
5513 : PropAccessType::Normal
5515 return true;
5518 case Construct::KindOfQOpExpression: {
5519 auto q = static_pointer_cast<QOpExpression>(node);
5520 if (q->getYes()) {
5521 // <expr> ? <expr> : <expr>
5522 Label tru, fals, done;
5524 Emitter condEmitter(q->getCondition(), m_ue, *this);
5525 visitIfCondition(q->getCondition(), condEmitter,
5526 tru, fals, true);
5528 if (tru.isUsed()) {
5529 tru.set(e);
5531 if (currentPositionIsReachable()) {
5532 visit(q->getYes());
5533 emitConvertToCell(e);
5534 e.Jmp(done);
5536 if (fals.isUsed()) fals.set(e);
5537 if (currentPositionIsReachable()) {
5538 visit(q->getNo());
5539 emitConvertToCell(e);
5541 if (done.isUsed()) {
5542 done.set(e);
5543 m_evalStack.cleanTopMeta();
5545 } else {
5546 // <expr> ?: <expr>
5547 Label done;
5548 visit(q->getCondition());
5549 emitConvertToCell(e);
5550 e.Dup();
5551 e.JmpNZ(done);
5552 e.PopC();
5553 visit(q->getNo());
5554 emitConvertToCell(e);
5555 done.set(e);
5556 m_evalStack.cleanTopMeta();
5558 return true;
5561 case Construct::KindOfNullCoalesceExpression: {
5562 auto q = static_pointer_cast<NullCoalesceExpression>(node);
5564 Label done;
5565 visit(q->getFirst());
5566 emitCGetQuiet(e);
5567 e.Dup();
5568 emitIsset(e);
5569 e.JmpNZ(done);
5570 e.PopC();
5571 visit(q->getSecond());
5572 emitConvertToCell(e);
5573 done.set(e);
5574 m_evalStack.cleanTopMeta();
5576 return true;
5579 case Construct::KindOfScalarExpression: {
5580 auto ex = static_pointer_cast<Expression>(node);
5581 Variant v;
5582 ex->getScalarValue(v);
5583 auto const emitted = emitScalarValue(e, v);
5584 always_assert(emitted);
5585 return true;
5588 case Construct::KindOfPipeVariable: {
5589 if (auto pipeVar = getPipeLocal()) {
5590 emitVirtualLocal(*pipeVar);
5591 return true;
5594 throw IncludeTimeFatalException(
5595 node, "Pipe variables must occur only in the RHS of pipe expressions");
5598 case Construct::KindOfSimpleVariable: {
5599 auto sv = static_pointer_cast<SimpleVariable>(node);
5600 if (sv->isThis()) {
5601 if (sv->hasContext(Expression::ObjectContext)) {
5602 e.This();
5603 } else if (sv->getFunctionScope()->needsLocalThis()) {
5604 static const StringData* thisStr = makeStaticString("this");
5605 Id thisId = m_curFunc->lookupVarId(thisStr);
5606 emitVirtualLocal(thisId);
5607 } else {
5608 auto const subop = sv->hasContext(Expression::ExistContext)
5609 ? BareThisOp::NoNotice
5610 : BareThisOp::Notice;
5611 e.BareThis(subop);
5613 } else {
5614 StringData* nLiteral = makeStaticString(sv->getName());
5615 if (sv->isSuperGlobal()) {
5616 e.String(nLiteral);
5617 markGlobalName(e);
5618 return true;
5620 Id i = m_curFunc->lookupVarId(nLiteral);
5621 emitVirtualLocal(i);
5622 if (sv->getAlwaysStash() &&
5623 !sv->hasAnyContext(Expression::ExistContext |
5624 Expression::RefValue |
5625 Expression::LValue |
5626 Expression::RefParameter)) {
5627 emitConvertToCell(e);
5631 return true;
5634 case Construct::KindOfDynamicVariable: {
5635 auto dv = static_pointer_cast<DynamicVariable>(node);
5636 visit(dv->getSubExpression());
5637 emitConvertToCellOrLoc(e);
5638 markName(e);
5639 return true;
5642 case Construct::KindOfStaticMemberExpression: {
5643 auto sm = static_pointer_cast<StaticMemberExpression>(node);
5644 emitVirtualClassBase(e, sm.get());
5645 emitNameString(e, sm->getExp());
5646 markSProp(e);
5647 return true;
5650 case Construct::KindOfArrayPairExpression: {
5651 auto ap = static_pointer_cast<ArrayPairExpression>(node);
5653 auto key = ap->getName();
5654 if (!m_staticArrays.empty()) {
5655 auto val = ap->getValue();
5657 TypedValue tvVal;
5658 initScalar(tvVal, val);
5660 if (key != nullptr) {
5661 assert(key->isScalar());
5662 TypedValue tvKey = make_tv<KindOfNull>();
5663 if (!key->getScalarValue(tvAsVariant(&tvKey))) {
5664 InvariantViolation("Expected scalar value for array key\n");
5665 always_assert(0);
5667 m_staticArrays.back().set(tvAsCVarRef(&tvKey),
5668 tvAsVariant(&tvVal));
5669 } else {
5670 // Set/ImmSet, val is the key
5671 if (m_staticColType.back() == HeaderKind::Set ||
5672 m_staticColType.back() == HeaderKind::ImmSet) {
5673 m_staticArrays.back().set(tvAsVariant(&tvVal),
5674 tvAsVariant(&tvVal));
5675 } else {
5676 m_staticArrays.back().append(tvAsCVarRef(&tvVal));
5679 } else {
5680 // Assume new array is on top of stack
5681 bool hasKey = (bool)key;
5682 if (hasKey) {
5683 visit(key);
5684 emitConvertToCellOrLoc(e);
5686 visit(ap->getValue());
5687 if (ap->isRef()) {
5688 emitConvertToVar(e);
5689 if (hasKey) {
5690 emitConvertSecondToCell(e);
5691 e.AddElemV();
5692 } else {
5693 e.AddNewElemV();
5695 } else {
5696 emitConvertToCell(e);
5697 if (hasKey) {
5698 emitConvertSecondToCell(e);
5699 e.AddElemC();
5700 } else {
5701 e.AddNewElemC();
5705 return true;
5707 case Construct::KindOfExpressionList: {
5708 auto el = static_pointer_cast<ExpressionList>(node);
5709 if (!m_staticArrays.empty() &&
5710 (m_staticColType.back() == HeaderKind::VecArray ||
5711 m_staticColType.back() == HeaderKind::Keyset)) {
5712 auto const nelem = el->getCount();
5713 for (int i = 0; i < nelem; ++i) {
5714 auto const expr = (*el)[i];
5715 assert(expr->isScalar());
5716 TypedValue tvVal;
5717 initScalar(tvVal, expr);
5718 m_staticArrays.back().append(tvAsCVarRef(&tvVal));
5720 return true;
5721 } else {
5722 int nelem = el->getCount(), i;
5723 bool pop = el->getListKind() != ExpressionList::ListKindParam;
5724 int keep = el->getListKind() == ExpressionList::ListKindLeft ?
5725 0 : nelem - 1;
5726 int cnt = 0;
5727 for (i = 0; i < nelem; i++) {
5728 ExpressionPtr p((*el)[i]);
5729 if (visit(p)) {
5730 if (pop && i != keep) {
5731 emitPop(e);
5732 } else {
5733 cnt++;
5737 return cnt != 0;
5740 case Construct::KindOfParameterExpression: {
5741 not_implemented();
5743 case Construct::KindOfModifierExpression: {
5744 not_implemented();
5746 case Construct::KindOfUserAttribute: {
5747 not_implemented();
5749 case Construct::KindOfClosureExpression: {
5750 // Closures are implemented by anonymous classes that extend Closure.
5751 // There is one anonymous class per closure body.
5752 auto ce = static_pointer_cast<ClosureExpression>(node);
5754 // Build a convenient list of use-variables. Each one corresponds to:
5755 // (a) an instance variable, to store the value until call time
5756 // (b) a parameter of the generated constructor
5757 // (c) an argument to the constructor at the definition site
5758 // (d) a line of code in the generated constructor;
5759 // (e) a line of code in the generated prologue to the closure body
5760 auto useList = ce->getClosureVariables();
5761 ClosureUseVarVec useVars;
5762 int useCount = (useList ? useList->getCount() : 0);
5763 if (useList) {
5764 for (int i = 0; i < useCount; ++i) {
5765 auto var = static_pointer_cast<ParameterExpression>((*useList)[i]);
5766 StringData* varName = makeStaticString(var->getName());
5767 useVars.push_back(ClosureUseVar(varName, var->isRef()));
5771 // We're still at the closure definition site. Emit code to instantiate
5772 // the new anonymous class, with the use variables as arguments.
5773 ExpressionListPtr valuesList(ce->getClosureValues());
5774 for (int i = 0; i < useCount; ++i) {
5775 ce->type() == ClosureType::Short
5776 ? emitLambdaCaptureArg(e, (*valuesList)[i])
5777 : (void)emitBuiltinCallArg(e, (*valuesList)[i], i,
5778 useVars[i].second, false);
5781 // The parser generated a unique name for the function,
5782 // use that for the class
5783 std::string clsName = ce->getClosureFunction()->getOriginalName();
5785 if (m_curFunc->isPseudoMain()) {
5786 std::ostringstream oss;
5787 oss << clsName << '$' << std::hex <<
5788 m_curFunc->ue().md5().q[1] << m_curFunc->ue().md5().q[0] << '$';
5789 clsName = oss.str();
5792 if (Option::WholeProgram) {
5793 int my_id;
5795 EmittedClosures::accessor acc;
5796 s_emittedClosures.insert(acc, makeStaticString(clsName));
5797 my_id = ++acc->second;
5799 if (my_id > 1) {
5800 // The closure was from a trait, so we need a unique name in the
5801 // implementing class. _ is different from the #, which is used for
5802 // many closures in the same func in ParserBase::newClosureName
5803 folly::toAppend('_', my_id, &clsName);
5807 auto ssClsName = makeStaticString(clsName);
5808 e.CreateCl(useCount, ssClsName);
5810 // From here on out, we're creating a new class to hold the closure.
5811 const static StringData* parentName = makeStaticString("Closure");
5812 PreClassEmitter* pce = m_ue.newPreClassEmitter(
5813 ssClsName, PreClass::ClosureHoistable);
5815 auto const attrs = AttrNoOverride | AttrUnique | AttrPersistent;
5817 pce->init(ce->line0(), ce->line1(), m_ue.bcPos(),
5818 attrs, parentName, nullptr);
5820 // Instance properties---one for each use var, and one for
5821 // each static local.
5822 TypedValue uninit;
5823 tvWriteUninit(&uninit);
5824 for (auto& useVar : useVars) {
5825 pce->addProperty(useVar.first, AttrPrivate, nullptr, nullptr,
5826 &uninit, RepoAuthType{});
5829 // The __invoke method. This is the body of the closure, preceded by
5830 // code that pulls the object's instance variables into locals.
5831 static const StringData* invokeName = makeStaticString("__invoke");
5832 FuncEmitter* invoke = m_ue.newMethodEmitter(invokeName, pce);
5833 invoke->isClosureBody = true;
5834 pce->addMethod(invoke);
5835 auto body = static_pointer_cast<MethodStatement>(ce->getClosureFunction());
5836 postponeMeth(body, invoke, false, new ClosureUseVarVec(useVars));
5838 return true;
5840 case Construct::KindOfClassExpression: {
5841 auto ce = static_pointer_cast<ClassExpression>(node);
5842 // The parser generated a unique name for the class, use that
5843 std::string clsName = ce->getClass()->getOriginalName();
5844 auto ssClsName = makeStaticString(clsName);
5845 auto fpiStart = m_ue.bcPos();
5846 auto params = ce->getParams();
5847 int numParams = params ? params->getCount() : 0;
5848 e.FPushCtorD(numParams, ssClsName);
5849 emitCall(e, ce, ce->getParams(), fpiStart);
5850 e.PopR();
5851 return true;
5853 case Construct::KindOfYieldExpression: {
5854 auto y = static_pointer_cast<YieldExpression>(node);
5856 registerYieldAwait(y);
5857 assert(m_evalStack.size() == 0);
5859 // evaluate key passed to yield, if applicable
5860 ExpressionPtr keyExp = y->getKeyExpression();
5861 if (keyExp) {
5862 m_curFunc->isPairGenerator = true;
5863 visit(keyExp);
5864 emitConvertToCell(e);
5867 // evaluate value expression passed to yield
5868 visit(y->getValueExpression());
5869 emitConvertToCell(e);
5871 // suspend generator
5872 if (keyExp) {
5873 assert(m_evalStack.size() == 2);
5874 e.YieldK();
5875 } else {
5876 assert(m_evalStack.size() == 1);
5877 e.Yield();
5880 // continue with the received result on the stack
5881 assert(m_evalStack.size() == 1);
5882 return true;
5884 case Construct::KindOfYieldFromExpression: {
5885 auto yf = static_pointer_cast<YieldFromExpression>(node);
5887 registerYieldAwait(yf);
5888 assert(m_evalStack.size() == 0);
5890 emitYieldFrom(e, yf->getExpression());
5892 return true;
5894 case Construct::KindOfAwaitExpression: {
5895 auto await = static_pointer_cast<AwaitExpression>(node);
5897 registerYieldAwait(await);
5898 assert(m_evalStack.size() == 0);
5900 auto expression = await->getExpression();
5901 if (emitInlineGen(e, expression)) return true;
5903 Label resume;
5905 // evaluate expression passed to await
5906 visit(expression);
5907 emitConvertToCell(e);
5909 // if expr is null, just continue
5910 e.Dup();
5911 emitIsType(e, IsTypeOp::Null);
5912 e.JmpNZ(resume);
5914 assert(m_evalStack.size() == 1);
5916 e.Await();
5918 resume.set(e);
5919 return true;
5921 case Construct::KindOfUseDeclarationStatementFragment:
5922 case Construct::KindOfExpression: {
5923 not_reached();
5927 not_reached();
5930 bool EmitterVisitor::emitScalarValue(Emitter& e, const Variant& v) {
5931 switch (v.getRawType()) {
5932 case KindOfUninit:
5933 e.NullUninit();
5934 return true;
5936 case KindOfNull:
5937 e.Null();
5938 return true;
5940 case KindOfBoolean:
5941 v.asBooleanVal() ? e.True() : e.False();
5942 return true;
5944 case KindOfInt64:
5945 e.Int(v.getInt64());
5946 return true;
5948 case KindOfDouble:
5949 e.Double(v.getDouble());
5950 return true;
5952 case KindOfPersistentString:
5953 case KindOfString:
5954 e.String(makeStaticString(v.getStringData()));
5955 return true;
5957 case KindOfPersistentVec:
5958 case KindOfVec:
5959 assert(v.isVecArray());
5960 e.Vec(ArrayData::GetScalarArray(v.getArrayData()));
5961 return true;
5963 case KindOfPersistentDict:
5964 case KindOfDict:
5965 assert(v.isDict());
5966 e.Dict(ArrayData::GetScalarArray(v.getArrayData()));
5967 return true;
5969 case KindOfPersistentKeyset:
5970 case KindOfKeyset:
5971 assert(v.isKeyset());
5972 e.Keyset(ArrayData::GetScalarArray(v.getArrayData()));
5973 return true;
5975 case KindOfPersistentArray:
5976 case KindOfArray:
5977 assert(v.isPHPArray());
5978 e.Array(ArrayData::GetScalarArray(v.getArrayData()));
5979 return true;
5981 case KindOfObject:
5982 case KindOfResource:
5983 case KindOfRef:
5984 case KindOfClass:
5985 return false;
5987 not_reached();
5990 void EmitterVisitor::emitConstMethodCallNoParams(Emitter& e,
5991 const std::string& name) {
5992 auto const nameLit = makeStaticString(name);
5993 auto const fpiStart = m_ue.bcPos();
5994 e.FPushObjMethodD(0, nameLit, ObjMethodOp::NullThrows);
5996 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
5998 e.FCall(0);
5999 emitConvertToCell(e);
6002 namespace {
6003 const StaticString
6004 s_hh_invariant_violation("hh\\invariant_violation"),
6005 s_invariant_violation("invariant_violation"),
6006 s_gennull("HH\\Asio\\null"),
6007 s_fromArray("fromArray"),
6008 s_AwaitAllWaitHandle("HH\\AwaitAllWaitHandle")
6012 bool EmitterVisitor::emitInlineGenva(
6013 Emitter& e,
6014 const SimpleFunctionCallPtr& call
6016 assert(call->isCallToFunction("genva"));
6017 const auto params = call->getParams();
6018 if (!params) {
6019 e.Array(staticEmptyArray());
6020 return true;
6022 if (params->containsUnpack()) {
6023 throw IncludeTimeFatalException(params, "do not use ...$args with genva()");
6025 const auto num_params = params->getCount();
6026 assertx(num_params > 0);
6028 for (auto i = int{0}; i < num_params; i++) {
6029 Label gwh;
6031 visit((*params)[i]);
6032 emitConvertToCell(e);
6034 // ($_ !== null ? HH\Asio\null() : $_)->getWaitHandle()
6035 e.Dup();
6036 emitIsType(e, IsTypeOp::Null);
6037 e.JmpZ(gwh);
6038 emitPop(e);
6040 Offset fpiStart = m_ue.bcPos();
6041 e.FPushFuncD(0, s_gennull.get());
6043 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
6045 e.FCall(0);
6046 e.UnboxR();
6047 gwh.set(e);
6048 emitConstMethodCallNoParams(e, "getWaitHandle");
6051 std::vector<Id> waithandles(num_params);
6052 for (auto i = int{num_params - 1}; i >= 0; --i) {
6053 waithandles[i] = emitSetUnnamedL(e);
6055 assertx(waithandles.size() == num_params);
6057 // AwaitAllWaitHandle::fromArray() always returns a WaitHandle.
6058 Offset fpiStart = m_ue.bcPos();
6059 e.FPushClsMethodD(1, s_fromArray.get(), s_AwaitAllWaitHandle.get());
6061 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
6062 // create a packed array of the waithandles
6063 for (const auto wh : waithandles) {
6064 emitVirtualLocal(wh);
6065 emitCGet(e);
6067 e.NewPackedArray(num_params);
6068 emitFPass(e, 0, PassByRefKind::ErrorOnCell);
6070 e.FCall(1);
6071 e.UnboxR();
6073 e.Await();
6074 // result of AwaitAllWaitHandle does not matter
6075 emitPop(e);
6077 for (const auto wh : waithandles) {
6078 emitVirtualLocal(wh);
6079 emitPushL(e);
6080 e.WHResult();
6082 e.NewPackedArray(num_params);
6084 for (auto wh : waithandles) {
6085 m_curFunc->freeUnnamedLocal(wh);
6088 newFaultRegionAndFunclet(
6089 fpiStart, m_ue.bcPos(),
6090 new UnsetUnnamedLocalsThunklet(std::move(waithandles)));
6092 return true;
6095 bool EmitterVisitor::emitInlineGena(
6096 Emitter& e,
6097 const SimpleFunctionCallPtr& call
6099 assert(call->isCallToFunction("gena"));
6100 const auto params = call->getParams();
6102 if (params->getCount() != 1) return false;
6105 // Convert input into an array of WH (inline this?)
6106 // Two elements is the most common size.
6108 e.NewArray(2);
6109 const auto array = emitSetUnnamedL(e);
6110 const auto arrayStart = m_ue.bcPos();
6113 // Iterate over input and store wait handles for all elements in
6114 // a new array.
6116 const auto key = m_curFunc->allocUnnamedLocal();
6117 const auto item = m_curFunc->allocUnnamedLocal();
6119 emitVirtualLocal(key);
6120 emitVirtualLocal(item);
6122 visit((*params)[0]);
6123 emitConvertToCell(e);
6125 Label endloop;
6126 const auto initItId = m_curFunc->allocIterator();
6127 e.IterInitK(initItId, endloop, item, key);
6128 const auto iterStart = m_ue.bcPos();
6130 Label loop(e);
6132 emitVirtualLocal(array); // for Set below
6133 emitVirtualLocal(key); // for Set below
6134 markElem(e);
6136 emitVirtualLocal(item);
6137 emitCGet(e);
6138 emitConstMethodCallNoParams(e, "getWaitHandle");
6140 emitSet(e); // array[$key] = $item->getWaitHandle();
6141 emitPop(e);
6143 emitVirtualLocal(key);
6144 emitVirtualLocal(item);
6145 e.IterNextK(initItId, loop, item, key);
6146 endloop.set(e);
6148 // Clear item and key. Free iterator.
6149 emitVirtualLocal(item);
6150 emitUnset(e);
6151 emitVirtualLocal(key);
6152 emitUnset(e);
6153 m_curFunc->freeIterator(initItId);
6155 newFaultRegionAndFunclet(iterStart, m_ue.bcPos(),
6156 new UnsetUnnamedLocalsThunklet({item, key}));
6157 newFaultRegionAndFunclet(iterStart, m_ue.bcPos(),
6158 new IterFreeThunklet(initItId, false),
6159 { initItId, KindOfIter });
6163 // Construct an AAWH from the array.
6165 const auto fromArrayStart = m_ue.bcPos();
6166 e.FPushClsMethodD(1, s_fromArray.get(), s_AwaitAllWaitHandle.get());
6168 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fromArrayStart);
6169 emitVirtualLocal(array);
6170 e.FPassL(0, array);
6172 e.FCall(1);
6173 e.UnboxR();
6176 // Await on the AAWH. Note: the result of Await does not matter.
6178 e.Await();
6179 emitPop(e);
6182 // Iterate over results and store in array. Reuse same temporary array.
6185 emitVirtualLocal(key);
6186 emitVirtualLocal(item);
6188 emitVirtualLocal(array);
6189 emitCGet(e);
6191 Label endloop2;
6192 const auto resultItId = m_curFunc->allocIterator();
6193 e.IterInitK(resultItId, endloop2, item, key);
6194 const auto iterStart2 = m_ue.bcPos();
6196 Label loop2(e);
6198 emitVirtualLocal(array); // for Set below
6199 emitVirtualLocal(key); // for Set below
6200 markElem(e);
6202 emitVirtualLocal(item);
6203 emitCGet(e);
6204 e.WHResult();
6206 emitSet(e); // array[$key] = WHResult($item);
6207 emitPop(e);
6209 emitVirtualLocal(key);
6210 emitVirtualLocal(item);
6211 e.IterNextK(resultItId, loop2, item, key);
6212 endloop2.set(e);
6214 // Clear item and key. Free iterator.
6215 emitVirtualLocal(item);
6216 emitUnset(e);
6217 emitVirtualLocal(key);
6218 emitUnset(e);
6219 m_curFunc->freeIterator(resultItId);
6221 newFaultRegionAndFunclet(iterStart2, m_ue.bcPos(),
6222 new UnsetUnnamedLocalsThunklet({item, key}));
6224 newFaultRegionAndFunclet(iterStart2, m_ue.bcPos(),
6225 new IterFreeThunklet(resultItId, false),
6226 { resultItId, KindOfIter });
6229 // clean up locals
6230 m_curFunc->freeUnnamedLocal(item);
6231 m_curFunc->freeUnnamedLocal(key);
6233 // Leave result array on the stack.
6234 emitPushAndFreeUnnamedL(e, array, arrayStart);
6236 return true;
6239 bool EmitterVisitor::emitInlineGen(
6240 Emitter& e,
6241 const ExpressionPtr& expression
6243 if (!m_ue.m_isHHFile || !Option::EnableHipHopSyntax ||
6244 !expression->is(Expression::KindOfSimpleFunctionCall) ||
6245 Option::JitEnableRenameFunction) {
6246 return false;
6249 const auto call = static_pointer_cast<SimpleFunctionCall>(expression);
6250 if (call->isCallToFunction("genva")) {
6251 return emitInlineGenva(e, call);
6252 } else if (call->isCallToFunction("gena")) {
6253 return emitInlineGena(e, call);
6255 return false;
6258 // Compile a static string as HHAS
6260 // The hhas bytecodes should either leave the stack untouched, in
6261 // which case the result of the hh\asm() expression will be null; or
6262 // they should push exactly one cell, which will be the result of the
6263 // hh\asm() expression.
6264 bool EmitterVisitor::emitInlineHHAS(Emitter& e, SimpleFunctionCallPtr func) {
6265 if (SystemLib::s_inited &&
6266 !func->getFunctionScope()->isSystem() &&
6267 !RuntimeOption::EvalAllowHhas) {
6268 throw IncludeTimeFatalException(func,
6269 "Inline hhas only allowed in systemlib");
6271 auto const params = func->getParams();
6272 if (!params || params->getCount() != 1) {
6273 throw IncludeTimeFatalException(func,
6274 "Inline hhas expects exactly one argument");
6276 Variant v;
6277 if (!((*params)[0]->getScalarValue(v)) || !v.isString()) {
6278 throw IncludeTimeFatalException(func,
6279 "Inline hhas must be string literal");
6282 try {
6283 auto result =
6284 assemble_expression(m_ue, m_curFunc,
6285 m_evalStack.size() + m_evalStack.fdescSize(),
6286 v.toString().toCppString());
6287 switch (result) {
6288 case AsmResult::NoResult:
6289 e.Null();
6290 break;
6291 case AsmResult::ValuePushed:
6292 pushEvalStack(StackSym::C);
6293 break;
6294 case AsmResult::Unreachable:
6295 // PrevOpcode is only used to determine whether the current position
6296 // is reachable. Arbitrarily set it to Jmp to ensure that the emitter
6297 // knows the current position is not reachable
6298 setPrevOpcode(Op::Jmp);
6299 pushEvalStack(StackSym::C);
6300 break;
6302 } catch (const std::exception& ex) {
6303 throw IncludeTimeFatalException(func, ex.what());
6306 return true;
6309 bool EmitterVisitor::emitHHInvariant(Emitter& e, SimpleFunctionCallPtr call) {
6310 if (!m_ue.m_isHHFile && !RuntimeOption::EnableHipHopSyntax) return false;
6312 auto const params = call->getParams();
6313 if (!params || params->getCount() < 1) return false;
6315 Label ok;
6317 visit((*params)[0]);
6318 emitCGet(e);
6319 e.JmpNZ(ok);
6321 auto const fpiStart = m_ue.bcPos();
6322 e.FPushFuncD(params->getCount() - 1, s_hh_invariant_violation.get());
6324 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
6325 for (auto i = uint32_t{1}; i < params->getCount(); ++i) {
6326 emitFuncCallArg(e, (*params)[i], i - 1, false);
6329 e.FCall(params->getCount() - 1);
6330 emitPop(e);
6331 // The invariant_violation can't return; but bytecode invariants mandate an
6332 // opcode that can't fall through:
6333 e.String(s_invariant_violation.get());
6334 e.Fatal(FatalOp::Runtime);
6336 ok.set(e);
6337 e.Null(); // invariant returns null if used in an expression, void according
6338 // to the typechecker.
6339 return true;
6342 int EmitterVisitor::scanStackForLocation(int iLast) {
6343 assertx(iLast >= 0);
6344 assertx(iLast < (int)m_evalStack.size());
6345 for (int i = iLast; i >= 0; --i) {
6346 char marker = StackSym::GetMarker(m_evalStack.get(i));
6347 if (marker != StackSym::E && marker != StackSym::W &&
6348 marker != StackSym::P && marker != StackSym::M &&
6349 marker != StackSym::Q) {
6350 return i;
6353 InvariantViolation("Emitter expected a location on the stack but none "
6354 "was found (at offset %d)",
6355 m_ue.bcPos());
6356 return 0;
6359 static MOpFlags makeBaseFlags(MOpFlags f) {
6360 auto constexpr mask = uint8_t(MOpFlags::Warn) | uint8_t(MOpFlags::Define);
6361 return MOpFlags(uint8_t(f) & mask);
6364 size_t EmitterVisitor::emitMOp(
6365 int iFirst,
6366 int& iLast,
6367 Emitter& e,
6368 MInstrOpts opts
6370 auto stackIdx = [&](int i) {
6371 return m_evalStack.actualSize() - 1 - m_evalStack.getActualPos(i);
6374 auto const baseFlags = opts.fpass ? MOpFlags::None
6375 : makeBaseFlags(opts.flags);
6377 // Emit the base location operation.
6378 auto sym = m_evalStack.get(iFirst);
6379 auto flavor = StackSym::GetSymFlavor(sym);
6380 switch (StackSym::GetMarker(sym)) {
6381 case StackSym::N:
6382 switch (flavor) {
6383 case StackSym::C:
6384 if (opts.fpass) {
6385 e.FPassBaseNC(opts.paramId, stackIdx(iFirst));
6386 } else {
6387 e.BaseNC(stackIdx(iFirst), baseFlags);
6389 break;
6390 case StackSym::L:
6391 if (opts.fpass) {
6392 e.FPassBaseNL(opts.paramId, m_evalStack.getLoc(iFirst));
6393 } else {
6394 e.BaseNL(m_evalStack.getLoc(iFirst), baseFlags);
6396 break;
6397 default:
6398 always_assert(false);
6400 break;
6402 case StackSym::G:
6403 switch (flavor) {
6404 case StackSym::C:
6405 if (opts.fpass) {
6406 e.FPassBaseGC(opts.paramId, stackIdx(iFirst));
6407 } else {
6408 e.BaseGC(stackIdx(iFirst), baseFlags);
6410 break;
6411 case StackSym::L:
6412 if (opts.fpass) {
6413 e.FPassBaseGL(opts.paramId, m_evalStack.getLoc(iFirst));
6414 } else {
6415 e.BaseGL(m_evalStack.getLoc(iFirst), baseFlags);
6417 break;
6418 default:
6419 always_assert(false);
6421 break;
6423 case StackSym::S: {
6424 if (m_evalStack.get(iLast) != StackSym::AM) {
6425 unexpectedStackSym(sym, "S-vector base, class ref");
6428 auto const clsIdx = opts.rhsVal ? 1 : 0;
6429 switch (flavor) {
6430 case StackSym::C:
6431 e.BaseSC(stackIdx(iFirst), clsIdx);
6432 break;
6433 case StackSym::L:
6434 e.BaseSL(m_evalStack.getLoc(iFirst), clsIdx);
6435 break;
6436 default:
6437 unexpectedStackSym(sym, "S-vector base, prop name");
6438 break;
6440 // The BaseS* bytecodes consume the Class from the eval stack so the
6441 // final operations don't have to expect an A-flavored input. Adjust
6442 // iLast accordingly.
6443 --iLast;
6444 break;
6447 case StackSym::None:
6448 switch (flavor) {
6449 case StackSym::L:
6450 if (opts.fpass) {
6451 e.FPassBaseL(opts.paramId, m_evalStack.getLoc(iFirst));
6452 } else {
6453 e.BaseL(m_evalStack.getLoc(iFirst), baseFlags);
6455 break;
6456 case StackSym::C:
6457 e.BaseC(stackIdx(iFirst));
6458 break;
6459 case StackSym::R:
6460 e.BaseR(stackIdx(iFirst));
6461 break;
6462 case StackSym::H:
6463 e.BaseH();
6464 break;
6465 default:
6466 always_assert(false);
6468 break;
6470 default:
6471 always_assert(false && "Bad base marker");
6474 assert(StackSym::GetMarker(m_evalStack.get(iLast)) != StackSym::M);
6476 // Emit all intermediate operations, leaving the final operation up to our
6477 // caller.
6478 for (auto i = iFirst + 1; i < iLast; ++i) {
6479 if (opts.fpass) {
6480 e.FPassDim(opts.paramId, symToMemberKey(e, i, opts.allowW));
6481 } else {
6482 e.Dim(opts.flags, symToMemberKey(e, i, opts.allowW));
6486 size_t stackCount = 0;
6487 for (int i = iFirst; i <= iLast; ++i) {
6488 if (!StackSym::IsSymbolic(m_evalStack.get(i))) ++stackCount;
6490 return stackCount;
6493 MemberKey EmitterVisitor::symToMemberKey(Emitter& e, int i, bool allowW) {
6494 auto const sym = m_evalStack.get(i);
6495 auto const marker = StackSym::GetMarker(sym);
6496 if (marker == StackSym::W) {
6497 if (allowW) return MemberKey{};
6499 throw EmitterVisitor::IncludeTimeFatalException(
6500 e.getNode(), "Cannot use [] for reading"
6504 switch (StackSym::GetSymFlavor(sym)) {
6505 case StackSym::L: {
6506 auto const local = m_evalStack.getLoc(i);
6507 switch (marker) {
6508 case StackSym::E: return MemberKey{MEL, local};
6509 case StackSym::P: return MemberKey{MPL, local};
6510 default: always_assert(false);
6513 case StackSym::C: {
6514 auto const idx =
6515 int32_t(m_evalStack.actualSize() - 1 - m_evalStack.getActualPos(i));
6516 switch (marker) {
6517 case StackSym::E: return MemberKey{MEC, idx};
6518 case StackSym::P: return MemberKey{MPC, idx};
6519 default: always_assert(false);
6522 case StackSym::I: {
6523 auto const int64 = m_evalStack.getInt(i);
6524 switch (marker) {
6525 case StackSym::E: return MemberKey{MEI, int64};
6526 default: always_assert(false);
6529 case StackSym::T: {
6530 auto const str = m_evalStack.getName(i);
6531 switch (marker) {
6532 case StackSym::E: return MemberKey{MET, str};
6533 case StackSym::P: return MemberKey{MPT, str};
6534 case StackSym::Q: return MemberKey{MQT, str};
6535 default: always_assert(false);
6538 default:
6539 always_assert(false);
6543 void EmitterVisitor::emitPop(Emitter& e) {
6544 if (checkIfStackEmpty("Pop*")) return;
6545 LocationGuard loc(e, m_tempLoc);
6546 m_tempLoc.clear();
6548 emitClsIfSPropBase(e);
6549 int iLast = m_evalStack.size()-1;
6550 int i = scanStackForLocation(iLast);
6551 int sz = iLast - i;
6552 assert(sz >= 0);
6553 char sym = m_evalStack.get(i);
6554 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
6555 switch (sym) {
6556 case StackSym::L: e.CGetL(m_evalStack.getLoc(i)); // fall through
6557 case StackSym::C: e.PopC(); break;
6558 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
6559 case StackSym::CN: e.CGetN(); e.PopC(); break;
6560 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
6561 case StackSym::CG: e.CGetG(); e.PopC(); break;
6562 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
6563 case StackSym::CS: e.CGetS(); e.PopC(); break;
6564 case StackSym::V: e.PopV(); break;
6565 case StackSym::R: e.PopR(); break;
6566 default: {
6567 unexpectedStackSym(sym, "emitPop");
6568 break;
6571 } else {
6572 emitQueryMOp(i, iLast, e, QueryMOp::CGet);
6573 e.PopC();
6577 void EmitterVisitor::emitCGetL2(Emitter& e) {
6578 assert(m_evalStack.size() >= 2);
6579 assert(m_evalStack.sizeActual() >= 1);
6580 assert(StackSym::GetSymFlavor(m_evalStack.get(m_evalStack.size() - 2))
6581 == StackSym::L);
6582 int localIdx = m_evalStack.getLoc(m_evalStack.size() - 2);
6583 e.CGetL2(localIdx);
6586 void EmitterVisitor::emitCGetL3(Emitter& e) {
6587 assert(m_evalStack.size() >= 3);
6588 assert(m_evalStack.sizeActual() >= 2);
6589 assert(StackSym::GetSymFlavor(m_evalStack.get(m_evalStack.size() - 3))
6590 == StackSym::L);
6591 int localIdx = m_evalStack.getLoc(m_evalStack.size() - 3);
6592 e.CGetL3(localIdx);
6595 void EmitterVisitor::emitPushL(Emitter& e) {
6596 assert(m_evalStack.size() >= 1);
6597 auto const idx = m_evalStack.size() - 1;
6598 assert(StackSym::GetSymFlavor(m_evalStack.get(idx)) == StackSym::L);
6599 e.PushL(m_evalStack.getLoc(idx));
6602 void EmitterVisitor::emitAGet(Emitter& e) {
6603 if (checkIfStackEmpty("AGet*")) return;
6605 emitConvertToCellOrLoc(e);
6606 switch (char sym = m_evalStack.top()) {
6607 case StackSym::L:
6608 e.AGetL(m_evalStack.getLoc(m_evalStack.size() - 1));
6609 break;
6610 case StackSym::C:
6611 e.AGetC();
6612 break;
6613 default:
6614 unexpectedStackSym(sym, "emitAGet");
6618 void EmitterVisitor::emitQueryMOp(int iFirst, int iLast, Emitter& e,
6619 QueryMOp op) {
6620 auto const flags = getQueryMOpFlags(op);
6621 auto const stackCount = emitMOp(iFirst, iLast, e, MInstrOpts{flags});
6622 e.QueryM(stackCount, op, symToMemberKey(e, iLast, false /* allowW */));
6625 void EmitterVisitor::emitCGet(Emitter& e) {
6626 if (checkIfStackEmpty("CGet*")) return;
6627 LocationGuard loc(e, m_tempLoc);
6628 m_tempLoc.clear();
6630 emitClsIfSPropBase(e);
6631 int iLast = m_evalStack.size()-1;
6632 int i = scanStackForLocation(iLast);
6633 int sz = iLast - i;
6634 assert(sz >= 0);
6635 char sym = m_evalStack.get(i);
6636 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
6637 switch (sym) {
6638 case StackSym::L: e.CGetL(m_evalStack.getLoc(i)); break;
6639 case StackSym::C: /* nop */ break;
6640 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
6641 case StackSym::CN: e.CGetN(); break;
6642 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
6643 case StackSym::CG: e.CGetG(); break;
6644 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
6645 case StackSym::CS: e.CGetS(); break;
6646 case StackSym::V: e.Unbox(); break;
6647 case StackSym::R: e.UnboxR(); break;
6648 default: {
6649 unexpectedStackSym(sym, "emitCGet");
6650 break;
6653 } else {
6654 emitQueryMOp(i, iLast, e, QueryMOp::CGet);
6658 void EmitterVisitor::emitCGetQuiet(Emitter& e) {
6659 if (checkIfStackEmpty("CGetQuiet*")) return;
6660 LocationGuard loc(e, m_tempLoc);
6661 m_tempLoc.clear();
6663 emitClsIfSPropBase(e);
6664 int iLast = m_evalStack.size()-1;
6665 int i = scanStackForLocation(iLast);
6666 int sz = iLast - i;
6667 assert(sz >= 0);
6668 char sym = m_evalStack.get(i);
6669 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
6670 switch (sym) {
6671 case StackSym::L: e.CGetQuietL(m_evalStack.getLoc(i)); break;
6672 case StackSym::C: /* nop */ break;
6673 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
6674 case StackSym::CN: e.CGetQuietN(); break;
6675 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
6676 case StackSym::CG: e.CGetQuietG(); break;
6677 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
6678 case StackSym::CS: e.CGetS(); break;
6679 case StackSym::V: e.Unbox(); break;
6680 case StackSym::R: e.UnboxR(); break;
6681 default: {
6682 unexpectedStackSym(sym, "emitCGetQuiet");
6683 break;
6687 } else {
6688 emitQueryMOp(i, iLast, e, QueryMOp::CGetQuiet);
6692 bool EmitterVisitor::emitVGet(Emitter& e, bool skipCells) {
6693 if (checkIfStackEmpty("VGet*")) return false;
6694 LocationGuard loc(e, m_tempLoc);
6695 m_tempLoc.clear();
6697 emitClsIfSPropBase(e);
6698 int iLast = m_evalStack.size()-1;
6699 int i = scanStackForLocation(iLast);
6700 int sz = iLast - i;
6701 assert(sz >= 0);
6702 char sym = m_evalStack.get(i);
6703 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
6704 switch (sym) {
6705 case StackSym::L: e.VGetL(m_evalStack.getLoc(i)); break;
6706 case StackSym::C: if (skipCells) return true; e.Box(); break;
6707 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
6708 case StackSym::CN: e.VGetN(); break;
6709 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
6710 case StackSym::CG: e.VGetG(); break;
6711 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
6712 case StackSym::CS: e.VGetS(); break;
6713 case StackSym::V: /* nop */ break;
6714 case StackSym::R: e.BoxR(); break;
6715 default: {
6716 unexpectedStackSym(sym, "emitVGet");
6717 break;
6720 } else {
6721 auto const stackCount =
6722 emitMOp(i, iLast, e, MInstrOpts{MOpFlags::Define});
6723 e.VGetM(stackCount, symToMemberKey(e, iLast, true /* allowW */));
6725 return false;
6728 Id EmitterVisitor::emitVisitAndSetUnnamedL(Emitter& e, ExpressionPtr exp) {
6729 visit(exp);
6730 emitConvertToCell(e);
6732 return emitSetUnnamedL(e);
6735 Id EmitterVisitor::emitSetUnnamedL(Emitter& e) {
6736 // HACK: emitVirtualLocal would pollute m_evalStack before visiting exp,
6737 // YieldExpression won't be happy
6738 Id tempLocal = m_curFunc->allocUnnamedLocal();
6739 auto& ue = e.getUnitEmitter();
6740 ue.emitOp(OpSetL);
6741 ue.emitIVA(tempLocal);
6743 emitPop(e);
6744 return tempLocal;
6747 void EmitterVisitor::emitPushAndFreeUnnamedL(Emitter& e, Id tempLocal,
6748 Offset start) {
6749 assert(tempLocal >= 0);
6750 assert(start != InvalidAbsoluteOffset);
6751 newFaultRegionAndFunclet(start, m_ue.bcPos(),
6752 new UnsetUnnamedLocalThunklet(tempLocal));
6753 emitVirtualLocal(tempLocal);
6754 emitPushL(e);
6755 m_curFunc->freeUnnamedLocal(tempLocal);
6758 EmitterVisitor::PassByRefKind
6759 EmitterVisitor::getPassByRefKind(ExpressionPtr exp) {
6760 auto permissiveKind = PassByRefKind::AllowCell;
6762 // The PassByRefKind of a list assignment expression is determined
6763 // by the PassByRefKind of the RHS. This loop will repeatedly recurse
6764 // on the RHS until it encounters an expression other than a list
6765 // assignment expression.
6766 while (exp->is(Expression::KindOfListAssignment)) {
6767 exp = static_pointer_cast<ListAssignment>(exp)->getArray();
6768 permissiveKind = PassByRefKind::WarnOnCell;
6771 switch (exp->getKindOf()) {
6772 case Expression::KindOfSimpleFunctionCall: {
6773 auto sfc = static_pointer_cast<SimpleFunctionCall>(exp);
6774 // this only happens for calls that have been morphed into bytecode
6775 // e.g. idx(), abs(), strlen(), etc..
6776 // It is to allow the following code to work
6777 // function f(&$arg) {...}
6778 // f(idx($array, 'key')); <- this fails otherwise
6779 if (sfc->hasBeenChangedToBytecode()) {
6780 return PassByRefKind::AllowCell;
6782 } break;
6783 case Expression::KindOfNewObjectExpression:
6784 case Expression::KindOfIncludeExpression:
6785 case Expression::KindOfSimpleVariable:
6786 // New and include/require
6787 return PassByRefKind::AllowCell;
6788 case Expression::KindOfArrayElementExpression:
6789 // Allow if bare; warn if inside list assignment
6790 return permissiveKind;
6791 case Expression::KindOfAssignmentExpression:
6792 // Assignment (=) and binding assignment (=&)
6793 return PassByRefKind::WarnOnCell;
6794 case Expression::KindOfBinaryOpExpression: {
6795 auto b = static_pointer_cast<BinaryOpExpression>(exp);
6796 // Assignment op (+=, -=, *=, etc)
6797 if (b->isAssignmentOp()) return PassByRefKind::WarnOnCell;
6798 } break;
6799 case Expression::KindOfUnaryOpExpression: {
6800 auto u = static_pointer_cast<UnaryOpExpression>(exp);
6801 int op = u->getOp();
6802 if (op == T_CLONE) {
6803 // clone
6804 return PassByRefKind::AllowCell;
6805 } else if (op == '@' || op == T_EVAL ||
6806 ((op == T_INC || op == T_DEC) && u->getFront())) {
6807 // Silence operator, eval, preincrement, and predecrement
6808 return PassByRefKind::WarnOnCell;
6810 } break;
6811 case Expression::KindOfExpressionList: {
6812 auto el = static_pointer_cast<ExpressionList>(exp);
6813 if (el->getListKind() != ExpressionList::ListKindParam) {
6814 return PassByRefKind::WarnOnCell;
6816 } break;
6817 default:
6818 break;
6820 // All other cases
6821 return PassByRefKind::ErrorOnCell;
6824 bool EmitterVisitor::emitBuiltinCallArg(Emitter& e,
6825 ExpressionPtr exp,
6826 int paramId,
6827 bool byRef,
6828 bool mustBeRef) {
6829 visit(exp);
6830 if (checkIfStackEmpty("Builtin arg*")) return true;
6831 if (byRef) {
6832 auto wasCell = emitVGet(e, true);
6833 if (wasCell && mustBeRef) {
6834 auto kind = getPassByRefKind(exp);
6835 switch (kind) {
6836 case PassByRefKind::AllowCell:
6837 // nop
6838 break;
6839 case PassByRefKind::WarnOnCell:
6840 e.String(
6841 makeStaticString("Only variables should be passed by reference"));
6842 e.Int(k_E_STRICT);
6843 e.FCallBuiltin(2, 2, s_trigger_error.get());
6844 emitPop(e);
6845 break;
6846 case PassByRefKind::ErrorOnCell:
6847 auto save = m_evalStack;
6848 e.String(
6849 makeStaticString("Only variables can be passed by reference"));
6850 e.Fatal(FatalOp::Runtime);
6851 m_evalStackIsUnknown = false;
6852 m_evalStack = save;
6853 return false;
6856 } else {
6857 emitCGet(e);
6859 return true;
6862 static bool isNormalLocalVariable(const ExpressionPtr& expr) {
6863 SimpleVariable* sv = static_cast<SimpleVariable*>(expr.get());
6864 return (expr->is(Expression::KindOfSimpleVariable) &&
6865 !sv->isSuperGlobal() &&
6866 !sv->isThis());
6869 void EmitterVisitor::emitLambdaCaptureArg(Emitter& e, ExpressionPtr exp) {
6870 // Constant folding may lead this to be not a var anymore,
6871 // so we should not be emitting *GetL in this case.
6872 if (!isNormalLocalVariable(exp)) {
6873 visit(exp);
6874 return;
6876 auto const sv = static_cast<SimpleVariable*>(exp.get());
6877 Id locId = m_curFunc->lookupVarId(makeStaticString(sv->getName()));
6878 emitVirtualLocal(locId);
6879 e.CUGetL(locId);
6882 void EmitterVisitor::emitBuiltinDefaultArg(Emitter& e, Variant& v,
6883 MaybeDataType t, int paramId) {
6884 switch (v.getType()) {
6885 case KindOfNull:
6886 if (t) {
6887 [&] {
6888 switch (*t) {
6889 case KindOfPersistentString:
6890 case KindOfString:
6891 case KindOfPersistentVec:
6892 case KindOfVec:
6893 case KindOfPersistentDict:
6894 case KindOfDict:
6895 case KindOfPersistentKeyset:
6896 case KindOfKeyset:
6897 case KindOfPersistentArray:
6898 case KindOfArray:
6899 case KindOfObject:
6900 case KindOfResource:
6901 e.Int(0);
6902 return;
6903 case KindOfUninit:
6904 case KindOfNull:
6905 case KindOfBoolean:
6906 case KindOfInt64:
6907 case KindOfDouble:
6908 case KindOfRef:
6909 case KindOfClass:
6910 break;
6912 not_reached();
6913 }();
6914 } else {
6915 e.NullUninit();
6917 return;
6919 case KindOfBoolean:
6920 if (v.getBoolean()) {
6921 e.True();
6922 } else {
6923 e.False();
6925 return;
6927 case KindOfInt64:
6928 e.Int(v.getInt64());
6929 return;
6931 case KindOfDouble:
6932 e.Double(v.toDouble());
6933 return;
6935 case KindOfPersistentString:
6936 case KindOfString: {
6937 StringData *nValue = makeStaticString(v.getStringData());
6938 e.String(nValue);
6939 return;
6942 case KindOfPersistentVec:
6943 case KindOfVec:
6944 assert(v.isVecArray());
6945 e.Vec(v.getArrayData());
6946 return;
6948 case KindOfPersistentDict:
6949 case KindOfDict:
6950 assert(v.isDict());
6951 e.Dict(v.getArrayData());
6952 return;
6954 case KindOfPersistentKeyset:
6955 case KindOfKeyset:
6956 assert(v.isKeyset());
6957 e.Keyset(v.getArrayData());
6958 return;
6960 case KindOfPersistentArray:
6961 case KindOfArray:
6962 assert(v.isPHPArray());
6963 e.Array(v.getArrayData());
6964 return;
6966 case KindOfUninit:
6967 case KindOfObject:
6968 case KindOfResource:
6969 case KindOfRef:
6970 case KindOfClass:
6971 break;
6973 not_reached();
6976 void EmitterVisitor::emitFuncCallArg(Emitter& e,
6977 ExpressionPtr exp,
6978 int paramId,
6979 bool isUnpack) {
6980 visit(exp);
6981 if (checkIfStackEmpty("FPass*")) return;
6983 // TODO(4599379): if dealing with an unpack, here is where we'd want to
6984 // emit a bytecode to traverse any containers;
6986 auto kind = getPassByRefKind(exp);
6987 if (isUnpack) {
6988 // This deals with the case where the called function has a
6989 // by ref param at the index of the unpack (because we don't
6990 // want to box the unpack itself).
6991 // But note that unless the user created the array manually,
6992 // and added reference params at the correct places, we'll
6993 // still get warnings, and the array elements will not be
6994 // passed by reference.
6995 emitConvertToCell(e);
6996 kind = PassByRefKind::AllowCell;
6998 emitFPass(e, paramId, kind);
7001 void EmitterVisitor::emitFPass(Emitter& e, int paramId,
7002 PassByRefKind passByRefKind) {
7003 if (checkIfStackEmpty("FPass*")) return;
7004 LocationGuard locGuard(e, m_tempLoc);
7005 m_tempLoc.clear();
7007 emitClsIfSPropBase(e);
7008 int iLast = m_evalStack.size()-1;
7009 int i = scanStackForLocation(iLast);
7010 int sz = iLast - i;
7011 assert(sz >= 0);
7012 char sym = m_evalStack.get(i);
7013 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7014 switch (sym) {
7015 case StackSym::L: e.FPassL(paramId, m_evalStack.getLoc(i)); break;
7016 case StackSym::C:
7017 switch (passByRefKind) {
7018 case PassByRefKind::AllowCell: e.FPassC(paramId); break;
7019 case PassByRefKind::WarnOnCell: e.FPassCW(paramId); break;
7020 case PassByRefKind::ErrorOnCell: e.FPassCE(paramId); break;
7021 default: assert(false);
7023 break;
7024 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
7025 case StackSym::CN: e.FPassN(paramId); break;
7026 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
7027 case StackSym::CG: e.FPassG(paramId); break;
7028 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
7029 case StackSym::CS: e.FPassS(paramId); break;
7030 case StackSym::V: e.FPassV(paramId); break;
7031 case StackSym::R: e.FPassR(paramId); break;
7032 default: {
7033 unexpectedStackSym(sym, "emitFPass");
7034 break;
7037 } else {
7038 auto const stackCount = emitMOp(i, iLast, e, MInstrOpts{paramId});
7039 e.FPassM(paramId, stackCount, symToMemberKey(e, iLast, true /* allowW */));
7043 void EmitterVisitor::emitIsset(Emitter& e) {
7044 if (checkIfStackEmpty("Isset*")) return;
7046 emitClsIfSPropBase(e);
7047 int iLast = m_evalStack.size()-1;
7048 int i = scanStackForLocation(iLast);
7049 int sz = iLast - i;
7050 assert(sz >= 0);
7051 char sym = m_evalStack.get(i);
7052 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7053 switch (sym) {
7054 case StackSym::L: e.IssetL(m_evalStack.getLoc(i)); break;
7055 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
7056 case StackSym::CN: e.IssetN(); break;
7057 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
7058 case StackSym::CG: e.IssetG(); break;
7059 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
7060 case StackSym::CS: e.IssetS(); break;
7061 //XXX: Zend does not allow isset() on the result
7062 // of a function call. We allow it here so that emitted
7063 // code is valid. Once the parser handles this correctly,
7064 // the R and C cases can go.
7065 case StackSym::R: e.UnboxR(); // fall through
7066 case StackSym::C:
7067 e.IsTypeC(IsTypeOp::Null);
7068 e.Not();
7069 break;
7070 default: {
7071 unexpectedStackSym(sym, "emitIsset");
7072 break;
7075 } else {
7076 emitQueryMOp(i, iLast, e, QueryMOp::Isset);
7080 void EmitterVisitor::emitIsType(Emitter& e, IsTypeOp op) {
7081 if (checkIfStackEmpty("IsType")) return;
7083 emitConvertToCellOrLoc(e);
7084 switch (char sym = m_evalStack.top()) {
7085 case StackSym::L:
7086 e.IsTypeL(m_evalStack.getLoc(m_evalStack.size() - 1), op);
7087 break;
7088 case StackSym::C:
7089 e.IsTypeC(op);
7090 break;
7091 default:
7092 unexpectedStackSym(sym, "emitIsType");
7096 void EmitterVisitor::emitEmpty(Emitter& e) {
7097 if (checkIfStackEmpty("Empty*")) return;
7099 emitClsIfSPropBase(e);
7100 int iLast = m_evalStack.size()-1;
7101 int i = scanStackForLocation(iLast);
7102 int sz = iLast - i;
7103 assert(sz >= 0);
7104 char sym = m_evalStack.get(i);
7105 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7106 switch (sym) {
7107 case StackSym::L: e.EmptyL(m_evalStack.getLoc(i)); break;
7108 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
7109 case StackSym::CN: e.EmptyN(); break;
7110 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
7111 case StackSym::CG: e.EmptyG(); break;
7112 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
7113 case StackSym::CS: e.EmptyS(); break;
7114 case StackSym::R: e.UnboxR(); // fall through
7115 case StackSym::C: e.Not(); break;
7116 default: {
7117 unexpectedStackSym(sym, "emitEmpty");
7118 break;
7121 } else {
7122 emitQueryMOp(i, iLast, e, QueryMOp::Empty);
7126 void EmitterVisitor::emitUnset(Emitter& e,
7127 ExpressionPtr exp /* = ExpressionPtr() */) {
7128 if (checkIfStackEmpty("Unset*")) return;
7130 emitClsIfSPropBase(e);
7131 int iLast = m_evalStack.size()-1;
7132 int i = scanStackForLocation(iLast);
7133 int sz = iLast - i;
7134 assert(sz >= 0);
7135 char sym = m_evalStack.get(i);
7136 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7137 switch (sym) {
7138 case StackSym::L: e.UnsetL(m_evalStack.getLoc(i)); break;
7139 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
7140 case StackSym::CN: e.UnsetN(); break;
7141 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
7142 case StackSym::CG: e.UnsetG(); break;
7143 case StackSym::LS: // fall through
7144 case StackSym::CS: {
7145 assert(exp);
7147 std::ostringstream s;
7148 s << "Attempt to unset static property " << exp->getText();
7149 emitMakeUnitFatal(e, s.str().c_str());
7150 break;
7152 default: {
7153 unexpectedStackSym(sym, "emitUnset");
7154 break;
7157 } else {
7158 auto const stackCount = emitMOp(i, iLast, e, MInstrOpts{MOpFlags::Unset});
7159 e.UnsetM(stackCount, symToMemberKey(e, iLast, false /* allowW */));
7163 void EmitterVisitor::emitVisitAndUnset(Emitter& e, ExpressionPtr exp) {
7164 visit(exp);
7165 emitUnset(e, exp);
7168 void EmitterVisitor::emitSet(Emitter& e) {
7169 if (checkIfStackEmpty("Set*")) return;
7171 int iLast = m_evalStack.size()-2;
7172 int i = scanStackForLocation(iLast);
7173 int sz = iLast - i;
7174 assert(sz >= 0);
7175 char sym = m_evalStack.get(i);
7176 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7177 switch (sym) {
7178 case StackSym::L: e.SetL(m_evalStack.getLoc(i)); break;
7179 case StackSym::LN: emitCGetL2(e); // fall through
7180 case StackSym::CN: e.SetN(); break;
7181 case StackSym::LG: emitCGetL2(e); // fall through
7182 case StackSym::CG: e.SetG(); break;
7183 case StackSym::LS: emitCGetL3(e); // fall through
7184 case StackSym::CS: e.SetS(); break;
7185 default: {
7186 unexpectedStackSym(sym, "emitSet");
7187 break;
7190 } else {
7191 auto const stackCount =
7192 emitMOp(i, iLast, e, MInstrOpts{MOpFlags::Define}.rhs());
7193 return e.SetM(stackCount, symToMemberKey(e, iLast, true /* allowW */));
7197 void EmitterVisitor::emitSetOp(Emitter& e, int tokenOp) {
7198 if (checkIfStackEmpty("SetOp*")) return;
7200 auto ifIntOverflow = [](SetOpOp trueVal, SetOpOp falseVal) {
7201 return RuntimeOption::IntsOverflowToInts ? trueVal : falseVal;
7204 auto const op = [&] {
7205 switch (tokenOp) {
7206 case T_PLUS_EQUAL:
7207 return ifIntOverflow(SetOpOp::PlusEqual, SetOpOp::PlusEqualO);
7208 case T_MINUS_EQUAL:
7209 return ifIntOverflow(SetOpOp::MinusEqual, SetOpOp::MinusEqualO);
7210 case T_MUL_EQUAL:
7211 return ifIntOverflow(SetOpOp::MulEqual, SetOpOp::MulEqualO);
7212 case T_POW_EQUAL: return SetOpOp::PowEqual;
7213 case T_DIV_EQUAL: return SetOpOp::DivEqual;
7214 case T_CONCAT_EQUAL: return SetOpOp::ConcatEqual;
7215 case T_MOD_EQUAL: return SetOpOp::ModEqual;
7216 case T_AND_EQUAL: return SetOpOp::AndEqual;
7217 case T_OR_EQUAL: return SetOpOp::OrEqual;
7218 case T_XOR_EQUAL: return SetOpOp::XorEqual;
7219 case T_SL_EQUAL: return SetOpOp::SlEqual;
7220 case T_SR_EQUAL: return SetOpOp::SrEqual;
7221 default: break;
7223 not_reached();
7224 }();
7226 int iLast = m_evalStack.size()-2;
7227 int i = scanStackForLocation(iLast);
7228 int sz = iLast - i;
7229 assert(sz >= 0);
7230 char sym = m_evalStack.get(i);
7231 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7232 switch (sym) {
7233 case StackSym::L: e.SetOpL(m_evalStack.getLoc(i), op); break;
7234 case StackSym::LN: emitCGetL2(e); // fall through
7235 case StackSym::CN: e.SetOpN(op); break;
7236 case StackSym::LG: emitCGetL2(e); // fall through
7237 case StackSym::CG: e.SetOpG(op); break;
7238 case StackSym::LS: emitCGetL3(e); // fall through
7239 case StackSym::CS: e.SetOpS(op); break;
7240 default: {
7241 unexpectedStackSym(sym, "emitSetOp");
7242 break;
7245 } else {
7246 auto const stackCount =
7247 emitMOp(i, iLast, e, MInstrOpts{MOpFlags::Define}.rhs());
7248 e.SetOpM(stackCount, op, symToMemberKey(e, iLast, true /* allowW */));
7252 void EmitterVisitor::emitBind(Emitter& e) {
7253 if (checkIfStackEmpty("Bind*")) return;
7255 int iLast = m_evalStack.size()-2;
7256 int i = scanStackForLocation(iLast);
7257 int sz = iLast - i;
7258 assert(sz >= 0);
7259 char sym = m_evalStack.get(i);
7260 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7261 switch (sym) {
7262 case StackSym::L: e.BindL(m_evalStack.getLoc(i)); break;
7263 case StackSym::LN: emitCGetL2(e); // fall through
7264 case StackSym::CN: e.BindN(); break;
7265 case StackSym::LG: emitCGetL2(e); // fall through
7266 case StackSym::CG: e.BindG(); break;
7267 case StackSym::LS: emitCGetL3(e); // fall through
7268 case StackSym::CS: e.BindS(); break;
7269 default: {
7270 unexpectedStackSym(sym, "emitBind");
7271 break;
7274 } else {
7275 auto const stackCount =
7276 emitMOp(i, iLast, e, MInstrOpts{MOpFlags::Define}.rhs());
7277 e.BindM(stackCount, symToMemberKey(e, iLast, true /* allowW */));
7281 void EmitterVisitor::emitIncDec(Emitter& e, IncDecOp op) {
7282 if (checkIfStackEmpty("IncDec*")) return;
7284 emitClsIfSPropBase(e);
7285 int iLast = m_evalStack.size()-1;
7286 int i = scanStackForLocation(iLast);
7287 int sz = iLast - i;
7288 assert(sz >= 0);
7289 char sym = m_evalStack.get(i);
7290 if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
7291 switch (sym) {
7292 case StackSym::L: e.IncDecL(m_evalStack.getLoc(i), op); break;
7293 case StackSym::LN: e.CGetL(m_evalStack.getLoc(i)); // fall through
7294 case StackSym::CN: e.IncDecN(op); break;
7295 case StackSym::LG: e.CGetL(m_evalStack.getLoc(i)); // fall through
7296 case StackSym::CG: e.IncDecG(op); break;
7297 case StackSym::LS: e.CGetL2(m_evalStack.getLoc(i)); // fall through
7298 case StackSym::CS: e.IncDecS(op); break;
7299 default: {
7300 unexpectedStackSym(sym, "emitIncDec");
7301 break;
7304 } else {
7305 auto const stackCount =
7306 emitMOp(i, iLast, e, MInstrOpts{MOpFlags::Define});
7307 e.IncDecM(stackCount, op, symToMemberKey(e, iLast, true /* allowW */));
7311 void EmitterVisitor::emitConvertToCell(Emitter& e) {
7312 emitCGet(e);
7315 void EmitterVisitor::emitConvertSecondToCell(Emitter& e) {
7316 if (m_evalStack.size() <= 1) {
7317 InvariantViolation(
7318 "Emitter encounted an empty evaluation stack when inside "
7319 "the emitConvertSecondToCell() function (at offset %d)",
7320 m_ue.bcPos());
7321 return;
7323 char sym = m_evalStack.get(m_evalStack.size() - 2);
7324 char symFlavor = StackSym::GetSymFlavor(sym);
7325 if (symFlavor == StackSym::C) {
7326 // do nothing
7327 } else if (symFlavor == StackSym::L) {
7328 emitCGetL2(e);
7329 } else {
7330 // emitConvertSecondToCell() should never be used for symbolic flavors
7331 // other than C or L
7332 InvariantViolation(
7333 "Emitter encountered an unsupported StackSym \"%s\" on "
7334 "the evaluation stack inside the emitConvertSecondToCell()"
7335 " function (at offset %d)",
7336 StackSym::ToString(sym).c_str(),
7337 m_ue.bcPos());
7341 void EmitterVisitor::emitConvertToCellIfVar(Emitter& e) {
7342 if (!m_evalStack.empty()) {
7343 char sym = m_evalStack.top();
7344 if (sym == StackSym::V) {
7345 emitConvertToCell(e);
7350 void EmitterVisitor::emitConvertToCellOrLoc(Emitter& e) {
7351 if (m_evalStack.empty()) {
7352 InvariantViolation(
7353 "Emitter encounted an empty evaluation stack when inside "
7354 "the emitConvertToCellOrLoc() function (at offset %d)",
7355 m_ue.bcPos());
7356 return;
7358 char sym = m_evalStack.top();
7359 if (sym == StackSym::L) {
7360 // If the top of stack is a loc that is not marked, do nothing
7361 } else {
7362 // Otherwise, call emitCGet to convert the top of stack to cell
7363 emitCGet(e);
7367 void EmitterVisitor::emitConvertToVar(Emitter& e) {
7368 emitVGet(e);
7372 * Class bases are stored on the symbolic stack in a "virtual" way so
7373 * we can resolve them later (here) in order to properly handle php
7374 * evaluation order.
7376 * For example, in:
7378 * $cls = 'cls';
7379 * $cls::$x[0][f()] = g();
7381 * We need to evaluate f(), then resolve $cls to an A (possibly
7382 * invoking an autoload handler), then evaluate g(), then do the set.
7384 * Complex cases involve unnamed local temporaries. For example, in:
7386 * ${func()}::${f()} = g();
7388 * We'll emit code which calls func() and stashes the result in a
7389 * unnamed local. Then we call f(), then we turn the unnamed local
7390 * into an 'A' so that autoload handlers will run after f(). Then g()
7391 * is evaluated and then the set happens.
7393 void EmitterVisitor::emitResolveClsBase(Emitter& e, int pos) {
7394 switch (m_evalStack.getClsBaseType(pos)) {
7395 case SymbolicStack::CLS_STRING_NAME:
7396 e.String(m_evalStack.getName(pos));
7397 emitAGet(e);
7398 break;
7399 case SymbolicStack::CLS_LATE_BOUND:
7400 e.LateBoundCls();
7401 break;
7402 case SymbolicStack::CLS_SELF:
7403 e.Self();
7404 break;
7405 case SymbolicStack::CLS_PARENT:
7406 e.Parent();
7407 break;
7408 case SymbolicStack::CLS_NAMED_LOCAL: {
7409 int loc = m_evalStack.getLoc(pos);
7410 emitVirtualLocal(loc);
7411 emitAGet(e);
7412 break;
7414 case SymbolicStack::CLS_UNNAMED_LOCAL: {
7415 int loc = m_evalStack.getLoc(pos);
7416 emitVirtualLocal(loc);
7417 emitAGet(e);
7418 emitVirtualLocal(loc);
7419 emitUnset(e);
7420 newFaultRegionAndFunclet(m_evalStack.getUnnamedLocStart(pos),
7421 m_ue.bcPos(),
7422 new UnsetUnnamedLocalThunklet(loc));
7423 m_curFunc->freeUnnamedLocal(loc);
7424 break;
7426 case SymbolicStack::CLS_INVALID:
7427 default:
7428 assert(false);
7431 m_evalStack.consumeBelowTop(m_evalStack.size() - pos - 1);
7434 void EmitterVisitor::emitClsIfSPropBase(Emitter& e) {
7435 // If the eval stack is empty, then there is no work to do
7436 if (m_evalStack.empty()) return;
7438 // Scan past any values marked with the Elem, NewElem, or Prop markers
7439 int pos = m_evalStack.size() - 1;
7440 for (;;) {
7441 char marker = StackSym::GetMarker(m_evalStack.get(pos));
7442 if (marker != StackSym::E && marker != StackSym::W &&
7443 marker != StackSym::P && marker != StackSym::Q) {
7444 break;
7446 --pos;
7447 if (pos < 0) {
7448 InvariantViolation("Emitter expected a location on the stack but none "
7449 "was found (at offset %d)",
7450 m_ue.bcPos());
7451 return;
7454 // After scanning, if we did not find a value marked with the SProp
7455 // marker then there is no work to do
7456 if (StackSym::GetMarker(m_evalStack.get(pos)) != StackSym::S) {
7457 return;
7460 --pos;
7461 if (pos < 0) {
7462 InvariantViolation(
7463 "Emitter emitted an instruction that tries to consume "
7464 "a value from the stack when the stack is empty "
7465 "(expected symbolic flavor \"C\" or \"L\" at offset %d)",
7466 m_ue.bcPos());
7469 emitResolveClsBase(e, pos);
7470 m_evalStack.set(m_evalStack.size() - 1,
7471 m_evalStack.get(m_evalStack.size() - 1) | StackSym::M);
7474 MaybeDataType EmitterVisitor::analyzeSwitch(SwitchStatementPtr sw,
7475 SwitchState& state) {
7476 auto& caseMap = state.cases;
7477 DataType t = KindOfUninit;
7478 StatementListPtr cases(sw->getCases());
7479 const int ncase = cases->getCount();
7481 // Bail if the cases aren't homogeneous
7482 for (int i = 0; i < ncase; ++i) {
7483 auto c = static_pointer_cast<CaseStatement>((*cases)[i]);
7484 auto condition = c->getCondition();
7485 if (condition) {
7486 Variant cval;
7487 DataType caseType;
7488 if (condition->getScalarValue(cval)) {
7489 caseType = cval.getType();
7490 if (caseType == KindOfPersistentString) caseType = KindOfString;
7491 if ((caseType != KindOfInt64 && caseType != KindOfString) ||
7492 !IMPLIES(t != KindOfUninit, caseType == t)) {
7493 return folly::none;
7495 t = caseType;
7496 } else {
7497 return folly::none;
7499 int64_t n;
7500 bool isNonZero;
7501 if (t == KindOfInt64) {
7502 n = cval.asInt64Val();
7503 isNonZero = n;
7504 } else {
7505 always_assert(t == KindOfString);
7506 n = m_ue.mergeLitstr(cval.asStrRef().get());
7507 isNonZero = false; // not used for string switches
7509 if (!caseMap.count(n)) {
7510 // If 'case n:' appears multiple times, only the first will
7511 // ever match
7512 caseMap[n] = i;
7513 if (t == KindOfString) {
7514 // We have to preserve the original order of the cases for string
7515 // switches because of insane things like 0 being equal to any string
7516 // that is not a nonzero numeric string.
7517 state.caseOrder.push_back(StrCase(safe_cast<Id>(n), i));
7520 if (state.nonZeroI == -1 && isNonZero) {
7521 // true is equal to any non-zero integer, so to preserve php's
7522 // switch semantics we have to remember the first non-zero
7523 // case to appear in the source text
7524 state.nonZeroI = i;
7526 } else if (LIKELY(state.defI == -1)) {
7527 state.defI = i;
7528 } else {
7529 // Multiple defaults are not allowed
7530 throw IncludeTimeFatalException(
7531 c, "Switch statements may only contain one default: clause");
7535 if (t == KindOfInt64) {
7536 int64_t base = caseMap.begin()->first;
7537 int64_t nTargets = caseMap.rbegin()->first - base + 1;
7538 // Fail if the cases are too sparse. We emit Switch even for absurdly small
7539 // cases to allow the jit to decide when to lower back to comparisons.
7540 if ((float)caseMap.size() / nTargets < 0.5) {
7541 return folly::none;
7543 } else if (t == KindOfString) {
7544 if (caseMap.size() < kMinStringSwitchCases) {
7545 return folly::none;
7549 return t;
7552 void EmitterVisitor::emitIntegerSwitch(Emitter& e, SwitchStatementPtr sw,
7553 std::vector<Label>& caseLabels,
7554 Label& done, const SwitchState& state) {
7555 auto& caseMap = state.cases;
7556 int64_t base = caseMap.begin()->first;
7557 int64_t nTargets = caseMap.rbegin()->first - base + 1;
7559 // It's on. Map case values to Labels, filling in the blanks as
7560 // appropriate.
7561 Label* defLabel = state.defI == -1 ? &done : &caseLabels[state.defI];
7562 std::vector<Label*> labels(nTargets + 2);
7563 for (int i = 0; i < nTargets; ++i) {
7564 if (auto const caseIdx = folly::get_ptr(caseMap, base + i)) {
7565 labels[i] = &caseLabels[*caseIdx];
7566 } else {
7567 labels[i] = defLabel;
7571 // Fill in offsets for the first non-zero case and default
7572 labels[labels.size() - 2] =
7573 state.nonZeroI == -1 ? defLabel : &caseLabels[state.nonZeroI];
7574 labels[labels.size() - 1] = defLabel;
7576 visit(sw->getExp());
7577 emitConvertToCell(e);
7578 e.Switch(SwitchKind::Bounded, base, labels);
7581 void EmitterVisitor::emitStringSwitch(Emitter& e, SwitchStatementPtr sw,
7582 std::vector<Label>& caseLabels,
7583 Label& done, const SwitchState& state) {
7584 std::vector<Emitter::StrOff> labels;
7585 for (auto& pair : state.caseOrder) {
7586 labels.push_back(Emitter::StrOff(pair.first, &caseLabels[pair.second]));
7589 // Default case comes last
7590 Label* defLabel = state.defI == -1 ? &done : &caseLabels[state.defI];
7591 labels.push_back(Emitter::StrOff(-1, defLabel));
7593 visit(sw->getExp());
7594 emitConvertToCell(e);
7595 e.SSwitch(labels);
7598 void EmitterVisitor::markElem(Emitter& e) {
7599 if (m_evalStack.empty()) {
7600 InvariantViolation("Emitter encountered an empty evaluation stack inside"
7601 " the markElem function (at offset %d)",
7602 m_ue.bcPos());
7603 return;
7605 char sym = m_evalStack.top();
7606 if (sym == StackSym::C || sym == StackSym::L || sym == StackSym::T ||
7607 sym == StackSym::I) {
7608 m_evalStack.set(m_evalStack.size()-1, (sym | StackSym::E));
7609 } else {
7610 InvariantViolation(
7611 "Emitter encountered an unsupported StackSym \"%s\" on "
7612 "the evaluation stack inside the markElem function (at "
7613 "offset %d)",
7614 StackSym::ToString(sym).c_str(),
7615 m_ue.bcPos());
7619 void EmitterVisitor::markNewElem(Emitter& e) {
7620 m_evalStack.push(StackSym::W);
7623 void EmitterVisitor::markProp(Emitter& e, PropAccessType propAccessType) {
7624 if (m_evalStack.empty()) {
7625 InvariantViolation(
7626 "Emitter encountered an empty evaluation stack inside "
7627 "the markProp function (at offset %d)",
7628 m_ue.bcPos());
7629 return;
7631 char sym = m_evalStack.top();
7632 if (sym == StackSym::C || sym == StackSym::L || sym == StackSym::T) {
7633 m_evalStack.set(
7634 m_evalStack.size()-1,
7635 (sym | (propAccessType == PropAccessType::NullSafe
7636 ? StackSym::Q
7637 : StackSym::P
7640 } else {
7641 InvariantViolation(
7642 "Emitter encountered an unsupported StackSym \"%s\" on "
7643 "the evaluation stack inside the markProp function (at "
7644 "offset %d)",
7645 StackSym::ToString(sym).c_str(),
7646 m_ue.bcPos());
7650 void EmitterVisitor::markSProp(Emitter& e) {
7651 if (m_evalStack.empty()) {
7652 InvariantViolation(
7653 "Emitter encountered an empty evaluation stack inside "
7654 "the markSProp function (at offset %d)",
7655 m_ue.bcPos());
7656 return;
7658 char sym = m_evalStack.top();
7659 if (sym == StackSym::C || sym == StackSym::L) {
7660 m_evalStack.set(m_evalStack.size()-1, (sym | StackSym::S));
7661 } else {
7662 InvariantViolation(
7663 "Emitter encountered an unsupported StackSym \"%s\" on "
7664 "the evaluation stack inside the markSProp function "
7665 "(at offset %d)",
7666 StackSym::ToString(sym).c_str(),
7667 m_ue.bcPos());
7671 #define MARK_NAME_BODY(index, requiredStackSize) \
7672 if (m_evalStack.size() < requiredStackSize) { \
7673 InvariantViolation( \
7674 "Emitter encountered an evaluation stack with %lu" \
7675 " elements inside the %s function (at offset %d)", \
7676 (unsigned long)m_evalStack.size(), \
7677 __FUNCTION__, m_ue.bcPos()); \
7678 return; \
7680 char sym = m_evalStack.get(index); \
7681 if (sym == StackSym::C || sym == StackSym::L) { \
7682 m_evalStack.set(index, (sym | StackSym::N)); \
7683 } else { \
7684 InvariantViolation( \
7685 "Emitter encountered an unsupported StackSym \"%s\" " \
7686 "on the evaluation stack inside the %s function (at " \
7687 "offset %d)", \
7688 StackSym::ToString(sym).c_str(), __FUNCTION__, \
7689 m_ue.bcPos()); \
7692 void EmitterVisitor::markName(Emitter& e) {
7693 int index = m_evalStack.size() - 1;
7694 MARK_NAME_BODY(index, 1);
7697 void EmitterVisitor::markNameSecond(Emitter& e) {
7698 int index = m_evalStack.size() - 2;
7699 MARK_NAME_BODY(index, 2);
7702 #undef MARK_NAME_BODY
7704 void EmitterVisitor::markGlobalName(Emitter& e) {
7705 if (m_evalStack.empty()) {
7706 InvariantViolation(
7707 "Emitter encountered an empty evaluation stack inside "
7708 "the markGlobalName function (at offset %d)",
7709 m_ue.bcPos());
7710 return;
7712 char sym = m_evalStack.top();
7713 if (sym == StackSym::C || sym == StackSym::L) {
7714 m_evalStack.set(m_evalStack.size()-1, (sym | StackSym::G));
7715 } else {
7716 InvariantViolation(
7717 "Emitter encountered an unsupported StackSym \"%s\" on "
7718 "the evaluation stack inside the markGlobalName function "
7719 "(at offset %d)",
7720 StackSym::ToString(sym).c_str(),
7721 m_ue.bcPos());
7725 void EmitterVisitor::emitNameString(Emitter& e, ExpressionPtr n,
7726 bool allowLiteral) {
7727 Variant v;
7728 if (n->getScalarValue(v) && v.isString()) {
7729 StringData* nLiteral = makeStaticString(v.toCStrRef().get());
7730 if (allowLiteral) {
7731 m_evalStack.push(StackSym::T);
7732 } else {
7733 e.String(nLiteral);
7735 m_evalStack.setString(nLiteral);
7736 } else {
7737 visit(n);
7738 emitConvertToCellOrLoc(e);
7742 void EmitterVisitor::postponeMeth(MethodStatementPtr m, FuncEmitter* fe,
7743 bool top,
7744 ClosureUseVarVec* useVars /* = NULL */) {
7745 m_postponedMeths.push_back(PostponedMeth(m, fe, top, useVars));
7748 void EmitterVisitor::postponeCtor(InterfaceStatementPtr is, FuncEmitter* fe) {
7749 m_postponedCtors.push_back(PostponedCtor(is, fe));
7752 void EmitterVisitor::postponePinit(InterfaceStatementPtr is, FuncEmitter* fe,
7753 NonScalarVec* v) {
7754 m_postponedPinits.push_back(PostponedNonScalars(is, fe, v));
7757 void EmitterVisitor::postponeSinit(InterfaceStatementPtr is, FuncEmitter* fe,
7758 NonScalarVec* v) {
7759 m_postponedSinits.push_back(PostponedNonScalars(is, fe, v));
7762 void EmitterVisitor::postponeCinit(InterfaceStatementPtr is, FuncEmitter* fe,
7763 NonScalarVec* v) {
7764 m_postponedCinits.push_back(PostponedNonScalars(is, fe, v));
7767 static Attr buildAttrs(ModifierExpressionPtr mod, bool isRef = false) {
7768 int attrs = AttrNone;
7769 if (isRef) {
7770 attrs |= AttrReference;
7772 if (mod) {
7773 attrs |= mod->isPublic() ? AttrPublic :
7774 mod->isPrivate() ? AttrPrivate :
7775 mod->isProtected() ? AttrProtected : AttrNone;
7776 if (mod->isStatic()) {
7777 attrs |= AttrStatic;
7779 if (mod->isAbstract()) {
7780 attrs |= AttrAbstract;
7782 if (mod->isFinal()) {
7783 attrs |= AttrFinal;
7786 return Attr(attrs);
7790 * <<__HipHopSpecific>> user attribute marks funcs/methods as HipHop specific
7791 * for reflection.
7792 * <<__IsFoldable>> Function has no side-effects and may be called at
7793 * compile time with constant input to get deterministic output.
7795 const StaticString
7796 s_IsFoldable("__IsFoldable"),
7797 s_ParamCoerceModeNull("__ParamCoerceModeNull"),
7798 s_ParamCoerceModeFalse("__ParamCoerceModeFalse");
7800 static void parseUserAttributes(FuncEmitter* fe, Attr& attrs) {
7801 if (fe->userAttributes.count(s_IsFoldable.get())) {
7802 attrs = attrs | AttrIsFoldable;
7804 if (fe->userAttributes.count(s_ParamCoerceModeNull.get())) {
7805 attrs = attrs | AttrParamCoerceModeNull;
7806 } else if (fe->userAttributes.count(s_ParamCoerceModeFalse.get())) {
7807 attrs = attrs | AttrParamCoerceModeFalse;
7811 static Attr buildMethodAttrs(MethodStatementPtr meth, FuncEmitter* fe,
7812 bool top) {
7813 FunctionScopePtr funcScope = meth->getFunctionScope();
7814 ModifierExpressionPtr mod(meth->getModifiers());
7815 Attr attrs = buildAttrs(mod, meth->isRef());
7817 // if hasCallToGetArgs() or if mayUseVV
7818 if (meth->hasCallToGetArgs() || funcScope->mayUseVV()) {
7819 attrs = attrs | AttrMayUseVV;
7822 auto fullName = meth->getOriginalFullName();
7823 auto it = Option::FunctionSections.find(fullName);
7824 if ((it != Option::FunctionSections.end() && it->second == "hot") ||
7825 (RuntimeOption::EvalRandomHotFuncs &&
7826 (hash_string_i_unsafe(fullName.c_str(), fullName.size()) & 8))) {
7827 attrs = attrs | AttrHot;
7830 if (!SystemLib::s_inited) {
7831 // we're building systemlib. everything is unique
7832 attrs = attrs | AttrBuiltin | AttrUnique | AttrPersistent;
7833 } else if (Option::WholeProgram) {
7834 if (!funcScope->isRedeclaring()) {
7835 attrs = attrs | AttrUnique;
7836 if (top &&
7837 (!funcScope->isVolatile() ||
7838 funcScope->isPersistent())) {
7839 attrs = attrs | AttrPersistent;
7842 if (meth->getClassScope() && !funcScope->hasOverride()) {
7843 attrs = attrs | AttrNoOverride;
7845 if (funcScope->isSystem()) {
7846 assert((attrs & AttrPersistent) || meth->getClassScope());
7847 attrs = attrs | AttrBuiltin;
7851 // For closures, the MethodStatement didn't have real attributes; enforce
7852 // that the __invoke method is public here
7853 if (fe->isClosureBody) {
7854 assert(!(attrs & (AttrProtected | AttrPrivate)));
7855 attrs = attrs | AttrPublic;
7858 // Coerce memoized methods to private. This is needed for code that uses
7859 // parent:: to call through to the correct underlying function
7860 if (meth->is(Statement::KindOfMethodStatement) && fe->isMemoizeImpl) {
7861 attrs = static_cast<Attr>(attrs & ~(AttrPublic | AttrProtected));
7862 attrs = attrs | AttrPrivate;
7865 parseUserAttributes(fe, attrs);
7866 // Not supported except in __Native functions
7867 attrs = static_cast<Attr>(
7868 attrs & ~(AttrParamCoerceModeNull | AttrParamCoerceModeFalse));
7870 return attrs;
7874 * The code below is used for both, function/method parameter type as well as
7875 * for function/method return type.
7877 static TypeConstraint
7878 determine_type_constraint_from_annot(const TypeAnnotationPtr annot,
7879 bool is_return) {
7880 if (annot) {
7881 auto flags = TypeConstraint::ExtendedHint | TypeConstraint::HHType;
7883 // We only care about a subset of extended type constaints:
7884 // typevar, nullable, soft, return types.
7886 // For everything else, we return {}. We also return {} for annotations
7887 // we don't know how to handle.
7888 if (annot->isFunction() || annot->isMixed()) {
7889 return {};
7891 if (annot->isTypeAccess()) {
7892 flags = flags | TypeConstraint::TypeConstant;
7894 if (annot->isTypeVar()) {
7895 flags = flags | TypeConstraint::TypeVar;
7897 if (annot->isNullable()) {
7898 flags = flags | TypeConstraint::Nullable;
7900 if (annot->isSoft()) {
7901 flags = flags | TypeConstraint::Soft;
7903 if (!is_return &&
7904 (flags == (TypeConstraint::ExtendedHint | TypeConstraint::HHType))) {
7905 return {};
7908 auto strippedName = annot->stripNullable().stripSoft().vanillaName();
7910 return TypeConstraint{
7911 makeStaticString(strippedName),
7912 flags
7916 return {};
7919 static TypeConstraint
7920 determine_type_constraint(const ParameterExpressionPtr& par) {
7921 if (par->hasTypeHint()) {
7922 auto ce = dynamic_pointer_cast<ConstantExpression>(par->defaultValue());
7923 auto flags = TypeConstraint::NoFlags;
7924 if (ce && ce->isNull()) {
7925 flags = flags|TypeConstraint::Nullable;
7927 if (par->hhType()) {
7928 flags = flags|TypeConstraint::HHType;
7930 return TypeConstraint{
7931 makeStaticString(par->getOriginalTypeHint()),
7932 flags
7936 return determine_type_constraint_from_annot(par->annotation(), false);
7939 void EmitterVisitor::emitPostponedMeths() {
7940 std::vector<FuncEmitter*> top_fes;
7941 while (!m_postponedMeths.empty()) {
7942 assert(m_evalStack.m_actualStackHighWaterPtr == nullptr);
7943 PostponedMeth& p = m_postponedMeths.front();
7944 MethodStatementPtr meth = p.m_meth;
7945 FuncEmitter* fe = p.m_fe;
7947 ITRACE(1, "Emitting postponed method {}\n", meth->getOriginalFullName());
7948 Trace::Indent indent;
7950 if (!fe) {
7951 assert(p.m_top);
7952 const StringData* methName = makeStaticString(meth->getOriginalName());
7953 fe = new FuncEmitter(m_ue, -1, -1, methName);
7954 auto oldFunc = m_topMethodEmitted.find(meth->getOriginalName());
7955 if (oldFunc != m_topMethodEmitted.end()) {
7956 throw IncludeTimeFatalException(
7957 meth,
7958 "Cannot redeclare %s() (previously declared in %s:%d)",
7959 meth->getOriginalName().c_str(),
7960 oldFunc->second->ue().m_filepath->data(),
7961 oldFunc->second->getLocation().second);
7963 m_topMethodEmitted.emplace(meth->getOriginalName(), fe);
7965 p.m_fe = fe;
7966 top_fes.push_back(fe);
7969 auto funcScope = meth->getFunctionScope();
7970 m_curFunc = fe;
7971 fe->isAsync = funcScope->isAsync();
7972 fe->isGenerator = funcScope->isGenerator();
7974 if (fe->isAsync && !fe->isGenerator && meth->retTypeAnnotation()) {
7975 auto rta = meth->retTypeAnnotation();
7976 auto nTypeArgs = rta->numTypeArgs();
7977 if (!rta->isAwaitable() && !rta->isWaitHandle()) {
7978 if (fe->isClosureBody) {
7979 throw IncludeTimeFatalException(
7980 meth,
7981 "Return type hint for async closure must be awaitable"
7983 } else {
7984 throw IncludeTimeFatalException(
7985 meth,
7986 "Return type hint for async %s %s() must be awaitable",
7987 meth->getClassScope() ? "method" : "function",
7988 meth->getOriginalFullName().c_str()
7992 if (nTypeArgs >= 2) {
7993 throw IncludeTimeFatalException(
7994 meth,
7995 "Awaitable interface expects 1 type argument, %d given",
7996 nTypeArgs);
8000 if (funcScope->userAttributes().count("__Memoize") &&
8001 !funcScope->isAbstract()) {
8002 auto const originalName = fe->name;
8003 auto const rewrittenName = makeStaticString(
8004 folly::sformat("{}$memoize_impl", fe->name->data()));
8006 FuncEmitter* memoizeFe = nullptr;
8007 if (meth->is(Statement::KindOfFunctionStatement)) {
8008 if (!p.m_top) {
8009 throw IncludeTimeFatalException(meth,
8010 "<<__Memoize>> cannot be applied to closures and inline functions");
8013 memoizeFe = new FuncEmitter(m_ue, -1, -1, originalName);
8014 fe->name = rewrittenName;
8015 top_fes.push_back(memoizeFe);
8016 } else {
8017 // Rename the method and create a new method with the original name
8018 fe->pce()->renameMethod(originalName, rewrittenName);
8019 memoizeFe = m_ue.newMethodEmitter(originalName, fe->pce());
8020 bool added UNUSED = fe->pce()->addMethod(memoizeFe);
8021 assert(added);
8024 // Emit the new method that handles the memoization
8025 m_curFunc = memoizeFe;
8026 m_curFunc->isMemoizeWrapper = true;
8027 addMemoizeProp(meth);
8028 emitMethodMetadata(meth, p.m_closureUseVars, p.m_top);
8029 emitMemoizeMethod(meth, rewrittenName);
8031 // Switch back to the original method and mark it as a memoize
8032 // implementation
8033 m_curFunc = fe;
8034 m_curFunc->isMemoizeImpl = true;
8037 if (funcScope->isNative()) {
8038 bindNativeFunc(meth, fe);
8039 } else {
8040 emitMethodMetadata(meth, p.m_closureUseVars, p.m_top);
8041 emitMethod(meth);
8044 if (fe->isClosureBody) {
8045 TypedValue uninit;
8046 tvWriteUninit(&uninit);
8047 for (auto& sv : m_curFunc->staticVars) {
8048 auto const str = makeStaticString(
8049 folly::format("86static_{}", sv.name->data()).str());
8050 fe->pce()->addProperty(str, AttrPrivate, nullptr, nullptr,
8051 &uninit, RepoAuthType{});
8055 delete p.m_closureUseVars;
8056 m_postponedMeths.pop_front();
8059 for (size_t i = 0; i < top_fes.size(); i++) {
8060 m_ue.appendTopEmitter(top_fes[i]);
8064 void EmitterVisitor::bindUserAttributes(MethodStatementPtr meth,
8065 FuncEmitter *fe) {
8066 auto const& userAttrs = meth->getFunctionScope()->userAttributes();
8067 for (auto& attr : userAttrs) {
8068 const StringData* uaName = makeStaticString(attr.first);
8069 ExpressionPtr uaValue = attr.second;
8070 assert(uaValue);
8071 assert(uaValue->isScalar());
8072 TypedValue tv;
8073 initScalar(tv, uaValue);
8074 fe->userAttributes[uaName] = tv;
8078 const StaticString s_Void("HH\\void");
8079 const char* attr_Deprecated = "__Deprecated";
8080 const StaticString s_attr_Deprecated(attr_Deprecated);
8082 void EmitterVisitor::bindNativeFunc(MethodStatementPtr meth,
8083 FuncEmitter *fe) {
8084 if (SystemLib::s_inited &&
8085 !(Option::WholeProgram && meth->isSystem())) {
8086 throw IncludeTimeFatalException(meth,
8087 "Native functions/methods may only be defined in systemlib");
8090 auto modifiers = meth->getModifiers();
8091 bindUserAttributes(meth, fe);
8093 Attr attributes = AttrBuiltin | AttrUnique | AttrPersistent;
8094 if (meth->isRef()) {
8095 attributes = attributes | AttrReference;
8097 auto pce = fe->pce();
8098 if (pce) {
8099 if (modifiers->isStatic()) {
8100 attributes = attributes | AttrStatic;
8102 if (modifiers->isFinal()) {
8103 attributes = attributes | AttrFinal;
8105 if (modifiers->isAbstract()) {
8106 attributes = attributes | AttrAbstract;
8108 if (modifiers->isPrivate()) {
8109 attributes = attributes | AttrPrivate;
8110 } else {
8111 attributes = attributes | (modifiers->isProtected()
8112 ? AttrProtected : AttrPublic);
8115 parseUserAttributes(fe, attributes);
8116 if (!(attributes & (AttrParamCoerceModeFalse | AttrParamCoerceModeNull))) {
8117 attributes = attributes | AttrParamCoerceModeNull;
8120 fe->setLocation(meth->line0(), meth->line1());
8121 fe->docComment = makeStaticString(
8122 Option::GenerateDocComments ? meth->getDocComment().c_str() : ""
8124 auto retType = meth->retTypeAnnotation();
8125 assert(retType ||
8126 meth->isNamed("__construct") ||
8127 meth->isNamed("__destruct"));
8128 fe->returnType = retType ? retType->dataType() : KindOfNull;
8129 fe->retUserType = makeStaticString(meth->getReturnTypeConstraint());
8131 FunctionScopePtr funcScope = meth->getFunctionScope();
8132 const char *funcname = funcScope->getScopeName().c_str();
8133 const char *classname = pce ? pce->name()->data() : nullptr;
8134 auto const& info = Native::GetBuiltinFunction(funcname, classname,
8135 modifiers->isStatic());
8137 if (!classname && (
8138 !strcasecmp(funcname, "fb_call_user_func_safe") ||
8139 !strcasecmp(funcname, "fb_call_user_func_safe_return") ||
8140 !strcasecmp(funcname, "fb_call_user_func_array_safe"))) {
8141 // Legacy optimization functions
8142 funcScope->setOptFunction(hphp_opt_fb_call_user_func);
8145 int nativeAttrs = fe->parseNativeAttributes(attributes);
8146 BuiltinFunction bif = nullptr, nif = nullptr;
8147 Native::getFunctionPointers(info, nativeAttrs, bif, nif);
8148 if (nif && !(nativeAttrs & Native::AttrZendCompat)) {
8149 if (retType) {
8150 fe->retTypeConstraint =
8151 determine_type_constraint_from_annot(retType, true);
8152 } else {
8153 fe->retTypeConstraint = TypeConstraint {
8154 s_Void.get(),
8155 TypeConstraint::ExtendedHint | TypeConstraint::HHType
8160 Emitter e(meth, m_ue, *this);
8161 FuncFinisher ff(this, e, fe, 0);
8162 Label topOfBody(e, Label::NoEntryNopFlag{});
8164 Offset base = m_ue.bcPos();
8166 if (meth->getFunctionScope()->userAttributes().count(attr_Deprecated)) {
8167 emitDeprecationWarning(e, meth);
8170 fe->setBuiltinFunc(attributes, base);
8171 fillFuncEmitterParams(fe, meth->getParams(), true);
8172 if (nativeAttrs & Native::AttrOpCodeImpl) {
8173 ff.setStackPad(emitNativeOpCodeImpl(meth, funcname, classname, fe));
8174 } else {
8175 e.NativeImpl();
8177 emitMethodDVInitializers(e, meth, topOfBody);
8180 void EmitterVisitor::emitMethodMetadata(MethodStatementPtr meth,
8181 ClosureUseVarVec* useVars,
8182 bool top) {
8183 FuncEmitter* fe = m_curFunc;
8184 bindUserAttributes(meth, fe);
8186 // assign ids to parameters (all methods)
8187 int numParam = meth->getParams() ? meth->getParams()->getCount() : 0;
8188 for (int i = 0; i < numParam; i++) {
8189 auto par =
8190 static_pointer_cast<ParameterExpression>((*meth->getParams())[i]);
8191 fe->allocVarId(makeStaticString(par->getName()));
8194 // assign ids to 0Closure and use parameters (closures)
8195 if (fe->isClosureBody) {
8196 fe->allocVarId(makeStaticString("0Closure"));
8198 for (auto& useVar : *useVars) {
8199 fe->allocVarId(useVar.first);
8203 // assign id to 86metadata local representing frame metadata
8204 if (meth->mayCallSetFrameMetadata()) {
8205 fe->allocVarId(makeStaticString("86metadata"));
8208 // assign ids to local variables
8209 if (!fe->isMemoizeWrapper) {
8210 assignLocalVariableIds(meth->getFunctionScope());
8213 // add parameter info
8214 fillFuncEmitterParams(fe, meth->getParams(),
8215 meth->getFunctionScope()->isParamCoerceMode());
8217 // copy declared return type (hack)
8218 fe->retUserType = makeStaticString(meth->getReturnTypeConstraint());
8220 auto annot = meth->retTypeAnnotation();
8221 // For a non-generator async function with a return annotation of the form
8222 // "Awaitable<T>", we set m_retTypeConstraint to T. For all other async
8223 // functions, we leave m_retTypeConstraint empty.
8224 if (annot && fe->isAsync && !fe->isGenerator) {
8225 // Semantic checks ensure that the return annotation is "Awaitable" or
8226 // "WaitHandle" and that it has at most one type parameter
8227 assert(annot->isAwaitable() || annot->isWaitHandle());
8228 assert(annot->numTypeArgs() <= 1);
8229 bool isSoft = annot->isSoft();
8230 // If annot was "Awaitable" with no type args, getTypeArg() will return an
8231 // empty annotation
8232 annot = annot->getTypeArg(0);
8233 // If the original annotation was soft, make sure we preserve the softness
8234 if (annot && isSoft) annot->setSoft();
8236 // Ideally we should handle the void case in TypeConstraint::check. This
8237 // should however get done in a different diff, since it could impact
8238 // perf in a negative way (#3145038)
8239 if (annot && !annot->isVoid() && !annot->isThis()) {
8240 fe->retTypeConstraint = determine_type_constraint_from_annot(annot, true);
8243 // add the original filename for flattened traits
8244 auto const originalFilename = meth->getOriginalFilename();
8245 if (!originalFilename.empty()) {
8246 fe->originalFilename = makeStaticString(originalFilename);
8249 StringData* methDoc = Option::GenerateDocComments ?
8250 makeStaticString(meth->getDocComment()) : staticEmptyString();
8252 fe->init(meth->line0(),
8253 meth->line1(),
8254 m_ue.bcPos(),
8255 buildMethodAttrs(meth, fe, top),
8256 top,
8257 methDoc);
8259 if (meth->getFunctionScope()->needsFinallyLocals()) {
8260 assignFinallyVariableIds();
8264 void EmitterVisitor::fillFuncEmitterParams(FuncEmitter* fe,
8265 ExpressionListPtr params,
8266 bool coerce_params /*= false */) {
8267 int numParam = params ? params->getCount() : 0;
8268 for (int i = 0; i < numParam; i++) {
8269 auto par = static_pointer_cast<ParameterExpression>((*params)[i]);
8270 StringData* parName = makeStaticString(par->getName());
8272 FuncEmitter::ParamInfo pi;
8273 auto const typeConstraint = determine_type_constraint(par);
8274 if (typeConstraint.hasConstraint()) {
8275 pi.typeConstraint = typeConstraint;
8277 if (coerce_params) {
8278 if (auto const typeAnnotation = par->annotation()) {
8279 pi.builtinType = typeAnnotation->dataType();
8283 if (par->hasUserType()) {
8284 pi.userType = makeStaticString(par->getUserTypeHint());
8287 // Store info about the default value if there is one.
8288 if (par->isOptional()) {
8289 const StringData* phpCode;
8290 ExpressionPtr vNode = par->defaultValue();
8291 if (vNode->isScalar()) {
8292 TypedValue dv;
8293 initScalar(dv, vNode);
8294 pi.defaultValue = dv;
8296 std::string orig = vNode->getComment();
8297 if (orig.empty()) {
8298 // Simple case: it's a scalar value so we just serialize it
8299 VariableSerializer vs(VariableSerializer::Type::PHPOutput);
8300 String result = vs.serialize(tvAsCVarRef(&dv), true);
8301 phpCode = makeStaticString(result.get());
8302 } else {
8303 // This was optimized from a Constant, or ClassConstant
8304 // use the original string
8305 phpCode = makeStaticString(orig);
8307 } else {
8308 // Non-scalar, so we have to output PHP from the AST node
8309 std::ostringstream os;
8310 CodeGenerator cg(&os, CodeGenerator::PickledPHP);
8311 auto ar = std::make_shared<AnalysisResult>();
8312 vNode->outputPHP(cg, ar);
8313 phpCode = makeStaticString(os.str());
8315 pi.phpCode = phpCode;
8318 auto paramUserAttrs =
8319 dynamic_pointer_cast<ExpressionList>(par->userAttributeList());
8320 if (paramUserAttrs) {
8321 for (int j = 0; j < paramUserAttrs->getCount(); ++j) {
8322 auto a = dynamic_pointer_cast<UserAttribute>((*paramUserAttrs)[j]);
8323 StringData* uaName = makeStaticString(a->getName());
8324 ExpressionPtr uaValue = a->getExp();
8325 assert(uaValue);
8326 assert(uaValue->isScalar());
8327 TypedValue tv;
8328 initScalar(tv, uaValue);
8329 pi.userAttributes[uaName] = tv;
8333 pi.byRef = par->isRef();
8334 pi.variadic = par->isVariadic();
8335 fe->appendParam(parName, pi);
8339 void EmitterVisitor::emitMethodPrologue(Emitter& e, MethodStatementPtr meth) {
8340 FunctionScopePtr funcScope = meth->getFunctionScope();
8342 if (!m_curFunc->isMemoizeWrapper &&
8343 funcScope->needsLocalThis() && !funcScope->isStatic()) {
8344 assert(!m_curFunc->top);
8345 static const StringData* thisStr = makeStaticString("this");
8346 Id thisId = m_curFunc->lookupVarId(thisStr);
8347 emitVirtualLocal(thisId);
8348 e.InitThisLoc(thisId);
8351 if (!m_curFunc->isMemoizeImpl) {
8352 for (uint32_t i = 0; i < m_curFunc->params.size(); i++) {
8353 const TypeConstraint& tc = m_curFunc->params[i].typeConstraint;
8354 if (!tc.hasConstraint()) continue;
8355 emitVirtualLocal(i);
8356 e.VerifyParamType(i);
8360 if (funcScope->isAbstract()) {
8361 std::ostringstream s;
8362 s << "Cannot call abstract method " << meth->getOriginalFullName() << "()";
8363 emitMakeUnitFatal(e, s.str().c_str(), FatalOp::RuntimeOmitFrame);
8367 void EmitterVisitor::emitDeprecationWarning(Emitter& e,
8368 MethodStatementPtr meth) {
8369 auto funcScope = meth->getFunctionScope();
8371 auto userAttributes DEBUG_ONLY = funcScope->userAttributes();
8372 assert(userAttributes.find(attr_Deprecated) != userAttributes.end());
8374 // Include the message from <<__Deprecated('<message>')>> in the warning
8375 auto deprArgs = funcScope->getUserAttributeParams(attr_Deprecated);
8376 auto deprMessage = deprArgs.empty()
8377 ? s_is_deprecated.data()
8378 : deprArgs.front()->getString();
8380 // how often to display the warning (1 / rate)
8381 auto rate = deprArgs.size() > 1 ? deprArgs[1]->getLiteralInteger() : 1;
8382 if (rate <= 0) {
8383 // deprecation warnings disabled
8384 return;
8387 { // preface the message with the name of the offending function
8388 auto funcName = funcScope->getScopeName();
8389 BlockScopeRawPtr b = funcScope->getOuterScope();
8390 if (b && b->is(BlockScope::ClassScope)) {
8391 auto clsScope = dynamic_pointer_cast<ClassScope>(b);
8392 if (clsScope->isTrait()) {
8393 e.Self();
8394 e.NameA();
8395 e.String(makeStaticString("::" + funcName + ": " + deprMessage));
8396 e.Concat();
8397 } else {
8398 e.String(makeStaticString(
8399 clsScope->getScopeName() + "::" + funcName
8400 + ": " + deprMessage));
8402 } else {
8403 e.String(makeStaticString(funcName + ": " + deprMessage));
8407 e.Int(rate);
8408 e.Int((funcScope->isSystem() || funcScope->isNative())
8409 ? k_E_DEPRECATED : k_E_USER_DEPRECATED);
8410 e.FCallBuiltin(3, 3, s_trigger_sampled_error.get());
8411 emitPop(e);
8414 void EmitterVisitor::emitMethod(MethodStatementPtr meth) {
8415 auto region = createRegion(meth, Region::Kind::FuncBody);
8416 enterRegion(region);
8417 SCOPE_EXIT { leaveRegion(region); };
8419 Emitter e(meth, m_ue, *this);
8420 FuncFinisher ff(this, e, m_curFunc);
8421 Label topOfBody(e, Label::NoEntryNopFlag{});
8422 emitMethodPrologue(e, meth);
8424 if (meth->getFunctionScope()->userAttributes().count(attr_Deprecated)) {
8425 emitDeprecationWarning(e, meth);
8428 // emit code to create generator object
8429 if (m_curFunc->isGenerator) {
8430 e.CreateCont();
8431 e.PopC();
8434 // emit method body
8435 visit(meth->getStmts());
8436 assert(m_evalStack.size() == 0);
8438 // if the current position is reachable, emit code to return null
8439 if (currentPositionIsReachable()) {
8440 auto r = meth->getRange();
8441 r.line0 = r.line1;
8442 r.char0 = r.char1 - 1;
8443 e.setTempLocation(r);
8444 e.Null();
8445 if (shouldEmitVerifyRetType()) {
8446 e.VerifyRetTypeC();
8448 e.RetC();
8449 e.setTempLocation(OptLocation());
8452 if (!m_curFunc->isMemoizeImpl) {
8453 emitMethodDVInitializers(e, meth, topOfBody);
8457 void EmitterVisitor::emitMethodDVInitializers(Emitter& e,
8458 MethodStatementPtr& meth,
8459 Label& topOfBody) {
8460 bool hasOptional = false;
8461 ExpressionListPtr params = meth->getParams();
8462 int numParam = params ? params->getCount() : 0;
8463 for (int i = 0; i < numParam; i++) {
8464 auto par = static_pointer_cast<ParameterExpression>((*params)[i]);
8465 if (par->isOptional()) {
8466 hasOptional = true;
8467 Label entryPoint(e);
8468 emitVirtualLocal(i);
8469 visit(par->defaultValue());
8470 emitCGet(e);
8471 emitSet(e);
8472 e.PopC();
8473 m_curFunc->params[i].funcletOff = entryPoint.getAbsoluteOffset();
8476 if (hasOptional) e.JmpNS(topOfBody);
8479 void EmitterVisitor::addMemoizeProp(MethodStatementPtr meth) {
8480 assert(m_curFunc->isMemoizeWrapper);
8482 if (meth->is(Statement::KindOfFunctionStatement)) {
8483 // Functions use statics within themselves. So all we need to do here is
8484 // set the name
8485 m_curFunc->memoizePropName = makeStaticString("static$memoize_cache");
8486 return;
8489 auto pce = m_curFunc->pce();
8490 auto classScope = meth->getClassScope();
8491 auto funcScope = meth->getFunctionScope();
8492 bool useSharedProp = !funcScope->isStatic();
8494 std::string propNameBase;
8495 if (useSharedProp) {
8496 propNameBase = "$shared";
8497 m_curFunc->hasMemoizeSharedProp = true;
8498 m_curFunc->memoizeSharedPropIndex = pce->getNextMemoizeCacheKey();
8499 } else {
8500 propNameBase = toLower(funcScope->getScopeName());
8503 // The prop definition in traits conflicts with the definition in a class
8504 // so make a different prop for each trait
8505 std::string traitNamePart;
8506 if (classScope && classScope->isTrait()) {
8507 traitNamePart = toLower(classScope->getScopeName());
8508 // the backslash comes from namespaces. @jan thought that would cause
8509 // issues, so use $ instead
8510 for (char &c: traitNamePart) {
8511 c = (c == '\\' ? '$' : c);
8513 traitNamePart += "$";
8516 m_curFunc->memoizePropName = makeStaticString(
8517 folly::sformat("{}${}memoize_cache", propNameBase, traitNamePart));
8519 TypedValue tvProp;
8520 if (useSharedProp ||
8521 (meth->getParams() && meth->getParams()->getCount() > 0)) {
8522 tvProp = make_tv<KindOfPersistentArray>(staticEmptyArray());
8523 } else {
8524 tvWriteNull(&tvProp);
8527 Attr attrs = AttrPrivate | AttrNoSerialize;
8528 attrs = attrs | (funcScope->isStatic() ? AttrStatic : AttrNone);
8529 pce->addProperty(m_curFunc->memoizePropName, attrs, nullptr, nullptr, &tvProp,
8530 RepoAuthType{});
8533 void EmitterVisitor::emitMemoizeProp(Emitter& e,
8534 MethodStatementPtr meth,
8535 Id localID,
8536 const std::vector<Id>& paramIDs,
8537 uint32_t numParams) {
8538 assert(m_curFunc->isMemoizeWrapper);
8540 if (meth->is(Statement::KindOfFunctionStatement)) {
8541 emitVirtualLocal(localID);
8542 } else if (meth->getFunctionScope()->isStatic()) {
8543 m_evalStack.push(StackSym::K);
8544 m_evalStack.setClsBaseType(SymbolicStack::CLS_SELF);
8545 e.String(m_curFunc->memoizePropName);
8546 markSProp(e);
8547 } else {
8548 m_evalStack.push(StackSym::H);
8549 m_evalStack.setKnownCls(m_curFunc->pce()->name(), false);
8550 m_evalStack.push(StackSym::T);
8551 m_evalStack.setString(m_curFunc->memoizePropName);
8552 markProp(e, PropAccessType::Normal);
8555 assert(numParams <= paramIDs.size());
8556 for (uint32_t i = 0; i < numParams; i++) {
8557 if (i == 0 && m_curFunc->hasMemoizeSharedProp) {
8558 e.Int(m_curFunc->memoizeSharedPropIndex);
8559 } else {
8560 emitVirtualLocal(paramIDs[i]);
8562 markElem(e);
8566 void EmitterVisitor::emitMemoizeMethod(MethodStatementPtr meth,
8567 const StringData* methName) {
8568 assert(m_curFunc->isMemoizeWrapper);
8570 if (meth->getFunctionScope()->isRefReturn()) {
8571 throw IncludeTimeFatalException(meth,
8572 "<<__Memoize>> cannot be used on functions that return by reference");
8574 if (meth->getFunctionScope()->allowsVariableArguments()) {
8575 throw IncludeTimeFatalException(meth,
8576 "<<__Memoize>> cannot be used on functions with variable arguments");
8579 auto classScope = meth->getClassScope();
8580 if (classScope && classScope->isInterface()) {
8581 throw IncludeTimeFatalException(meth,
8582 "<<__Memoize>> cannot be used in interfaces");
8585 bool isFunc = meth->is(Statement::KindOfFunctionStatement);
8586 int numParams = m_curFunc->params.size();
8587 std::vector<Id> cacheLookup;
8589 auto region = createRegion(meth, Region::Kind::FuncBody);
8590 enterRegion(region);
8591 SCOPE_EXIT { leaveRegion(region); };
8593 Emitter e(meth, m_ue, *this);
8594 FuncFinisher ff(this, e, m_curFunc);
8596 Label topOfBody(e);
8597 Label cacheMiss;
8599 emitMethodPrologue(e, meth);
8601 // Function start
8602 int staticLocalID = 0;
8603 if (isFunc) {
8604 // static ${propName} = {numParams > 0 ? array() : null};
8605 staticLocalID = m_curFunc->allocUnnamedLocal();
8606 emitVirtualLocal(staticLocalID);
8607 if (numParams == 0) {
8608 e.Null();
8609 } else {
8610 e.Array(staticEmptyArray());
8612 e.StaticLocInit(staticLocalID, m_curFunc->memoizePropName);
8613 } else if (!meth->getFunctionScope()->isStatic()) {
8614 e.CheckThis();
8617 if (m_curFunc->hasMemoizeSharedProp) {
8618 // The code below depends on cacheLookup having the right number of elements
8619 // Push a dummy value even though we'll use the cacheID as an int instead
8620 // instead of emitting a local
8621 cacheLookup.push_back(0);
8624 if (numParams == 0 && cacheLookup.size() == 0) {
8625 // if (${propName} !== null)
8626 emitMemoizeProp(e, meth, staticLocalID, cacheLookup, 0);
8627 emitIsType(e, IsTypeOp::Null);
8628 e.JmpNZ(cacheMiss);
8629 } else {
8630 // Serialize all the params into something we can use for the key
8631 for (int i = 0; i < numParams; i++) {
8632 if (m_curFunc->params[i].byRef) {
8633 throw IncludeTimeFatalException(meth,
8634 "<<__Memoize>> cannot be used on functions with args passed by "
8635 "reference");
8638 // Translate the arg to a memoize key
8639 int serResultLocal = m_curFunc->allocUnnamedLocal();
8640 cacheLookup.push_back(serResultLocal);
8642 emitVirtualLocal(serResultLocal);
8643 emitVirtualLocal(i);
8644 emitCGet(e);
8645 e.GetMemoKey();
8646 emitSet(e);
8647 emitPop(e);
8650 // isset returns false for null values. Given that:
8651 // - If we know that we can't return null, we can do a single isset check
8652 // - If we could return null, but there's only one arg, we can do a single
8653 // array_key_exists() check
8654 // - Otherwise we need an isset check to make sure we can dereference the
8655 // first N - 1 args, and then an array_key_exists() check
8656 int cacheLookupLen = cacheLookup.size();
8657 bool noRetNull =
8658 meth->getFunctionScope()->isAsync() ||
8659 (m_curFunc->retTypeConstraint.hasConstraint() &&
8660 !m_curFunc->retTypeConstraint.isSoft() &&
8661 !m_curFunc->retTypeConstraint.isNullable());
8663 if (cacheLookupLen > 1 || noRetNull) {
8664 // if (isset(${propName}[$param1]...[noRetNull ? $paramN : $paramN-1]))
8665 emitMemoizeProp(e, meth, staticLocalID, cacheLookup,
8666 noRetNull ? cacheLookupLen : cacheLookupLen - 1);
8667 emitIsset(e);
8668 e.JmpZ(cacheMiss);
8671 if (!noRetNull) {
8672 // if (array_key_exists($paramN, ${propName}[$param1][...][$paramN-1]))
8673 if (cacheLookupLen == 1 && m_curFunc->hasMemoizeSharedProp) {
8674 e.Int(m_curFunc->memoizeSharedPropIndex);
8675 } else {
8676 emitVirtualLocal(cacheLookup[cacheLookupLen - 1]);
8677 emitCGet(e);
8679 emitMemoizeProp(e, meth, staticLocalID, cacheLookup, cacheLookupLen - 1);
8680 emitCGet(e);
8681 e.AKExists();
8682 e.JmpZ(cacheMiss);
8686 // return $<propName>[$param1][...][$paramN]
8687 int cacheLookupLen = cacheLookup.size();
8688 emitMemoizeProp(e, meth, staticLocalID, cacheLookup, cacheLookupLen);
8689 emitCGet(e);
8690 e.RetC();
8692 // Otherwise, call the memoized func, store the result, and return it
8693 cacheMiss.set(e);
8694 emitMemoizeProp(e, meth, staticLocalID, cacheLookup, cacheLookupLen);
8695 auto fpiStart = m_ue.bcPos();
8696 if (isFunc) {
8697 e.FPushFuncD(numParams, methName);
8698 } else if (meth->getFunctionScope()->isStatic()) {
8699 emitClsIfSPropBase(e);
8701 if (classScope && classScope->isTrait()) {
8702 e.String(methName);
8703 e.Self();
8704 fpiStart = m_ue.bcPos();
8705 e.FPushClsMethodF(numParams);
8706 } else {
8707 fpiStart = m_ue.bcPos();
8708 e.FPushClsMethodD(numParams, methName, m_curFunc->pce()->name());
8710 } else {
8711 e.This();
8712 fpiStart = m_ue.bcPos();
8713 e.FPushObjMethodD(numParams, methName, ObjMethodOp::NullThrows);
8716 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
8717 for (uint32_t i = 0; i < numParams; i++) {
8718 emitVirtualLocal(i);
8719 emitFPass(e, i, PassByRefKind::ErrorOnCell);
8722 e.FCall(numParams);
8723 emitConvertToCell(e);
8725 emitSet(e);
8726 e.RetC();
8728 assert(m_evalStack.size() == 0);
8730 emitMethodDVInitializers(e, meth, topOfBody);
8733 void EmitterVisitor::emitPostponedCtors() {
8734 while (!m_postponedCtors.empty()) {
8735 PostponedCtor& p = m_postponedCtors.front();
8737 Attr attrs = AttrPublic;
8738 if (!SystemLib::s_inited || p.m_is->getClassScope()->isSystem()) {
8739 attrs = attrs | AttrBuiltin;
8741 StringData* methDoc = staticEmptyString();
8742 p.m_fe->init(p.m_is->line0(), p.m_is->line1(),
8743 m_ue.bcPos(), attrs, false, methDoc);
8744 Emitter e(p.m_is, m_ue, *this);
8745 FuncFinisher ff(this, e, p.m_fe);
8746 e.Null();
8747 e.RetC();
8749 m_postponedCtors.pop_front();
8753 void EmitterVisitor::emitPostponedPSinit(PostponedNonScalars& p, bool pinit) {
8754 Attr attrs = (Attr)(AttrPrivate | AttrStatic);
8755 if (!SystemLib::s_inited || p.m_is->getClassScope()->isSystem()) {
8756 attrs = attrs | AttrBuiltin;
8758 StringData* methDoc = staticEmptyString();
8759 p.m_fe->init(p.m_is->line0(), p.m_is->line1(),
8760 m_ue.bcPos(), attrs, false, methDoc);
8762 Emitter e(p.m_is, m_ue, *this);
8763 FuncFinisher ff(this, e, p.m_fe);
8765 // Private instance and static properties are initialized using
8766 // InitProp.
8767 size_t nProps = p.m_vec->size();
8768 assert(nProps > 0);
8769 for (size_t i = 0; i < nProps; ++i) {
8770 const StringData* propName =
8771 makeStaticString(((*p.m_vec)[i]).first);
8773 Label isset;
8774 InitPropOp op = InitPropOp::NonStatic;
8775 const PreClassEmitter::Prop& preProp =
8776 p.m_fe->pce()->lookupProp(propName);
8777 if ((preProp.attrs() & AttrStatic) == AttrStatic) {
8778 op = InitPropOp::Static;
8779 } else if ((preProp.attrs() & (AttrPrivate|AttrStatic)) != AttrPrivate) {
8780 e.CheckProp(const_cast<StringData*>(propName));
8781 e.JmpNZ(isset);
8783 visit((*p.m_vec)[i].second);
8784 e.InitProp(const_cast<StringData*>(propName), op);
8785 isset.set(e);
8787 e.Null();
8788 e.RetC();
8791 void EmitterVisitor::emitPostponedPinits() {
8792 while (!m_postponedPinits.empty()) {
8793 PostponedNonScalars& p = m_postponedPinits.front();
8794 emitPostponedPSinit(p, true);
8795 p.release(); // Manually trigger memory cleanup.
8796 m_postponedPinits.pop_front();
8800 void EmitterVisitor::emitPostponedSinits() {
8801 while (!m_postponedSinits.empty()) {
8802 PostponedNonScalars& p = m_postponedSinits.front();
8803 emitPostponedPSinit(p, false);
8804 p.release(); // Manually trigger memory cleanup.
8805 m_postponedSinits.pop_front();
8809 void EmitterVisitor::emitPostponedCinits() {
8810 while (!m_postponedCinits.empty()) {
8811 PostponedNonScalars& p = m_postponedCinits.front();
8813 Attr attrs = (Attr)(AttrPrivate | AttrStatic);
8814 if (!SystemLib::s_inited || p.m_is->getClassScope()->isSystem()) {
8815 attrs = attrs | AttrBuiltin;
8817 StringData* methDoc = staticEmptyString();
8818 p.m_fe->init(p.m_is->line0(), p.m_is->line1(),
8819 m_ue.bcPos(), attrs, false, methDoc);
8820 static const StringData* s_constName = makeStaticString("constName");
8821 p.m_fe->appendParam(s_constName, FuncEmitter::ParamInfo());
8823 Emitter e(p.m_is, m_ue, *this);
8824 FuncFinisher ff(this, e, p.m_fe);
8826 // Generate HHBC of the structure:
8828 // private static function 86cinit(constName) {
8829 // if (constName == "FOO") {
8830 // return <expr for FOO>;
8831 // } else if (constName == "BAR") {
8832 // return <expr for BAR>;
8833 // } else { # (constName == "BAZ")
8834 // return <expr for BAZ>;
8835 // }
8836 // }
8837 size_t nConsts = p.m_vec->size();
8838 assert(nConsts > 0);
8839 Label retC;
8840 for (size_t i = 0; i < nConsts - 1; ++i) {
8841 Label mismatch;
8843 emitVirtualLocal(0);
8844 emitCGet(e);
8845 e.String((StringData*)makeStaticString(((*p.m_vec)[i]).first));
8846 e.Eq();
8847 e.JmpZ(mismatch);
8849 visit((*p.m_vec)[i].second);
8851 e.Jmp(retC);
8852 mismatch.set(e);
8854 visit((*p.m_vec)[nConsts-1].second);
8855 retC.set(e);
8856 e.RetC();
8858 p.release(); // Manually trigger memory cleanup.
8859 m_postponedCinits.pop_front();
8863 void EmitterVisitor::emitVirtualLocal(int localId) {
8864 prepareEvalStack();
8866 m_evalStack.push(StackSym::L);
8867 m_evalStack.setInt(localId);
8870 template<class Expr>
8871 void EmitterVisitor::emitVirtualClassBase(Emitter& e, Expr* node) {
8872 prepareEvalStack();
8874 m_evalStack.push(StackSym::K);
8875 auto const func = node->getFunctionScope();
8877 if (node->isStatic()) {
8878 m_evalStack.setClsBaseType(SymbolicStack::CLS_LATE_BOUND);
8879 } else if (node->getClass()) {
8880 const ExpressionPtr& expr = node->getClass();
8881 if (isNormalLocalVariable(expr)) {
8882 SimpleVariable* sv = static_cast<SimpleVariable*>(expr.get());
8883 StringData* name = makeStaticString(sv->getName());
8884 Id locId = m_curFunc->lookupVarId(name);
8885 m_evalStack.setClsBaseType(SymbolicStack::CLS_NAMED_LOCAL);
8886 m_evalStack.setInt(locId);
8887 } else {
8889 * More complex expressions get stashed into an unnamed local so
8890 * we can evaluate them at the proper time.
8892 * See emitResolveClsBase() for examples.
8894 int unnamedLoc = m_curFunc->allocUnnamedLocal();
8895 int clsBaseIdx = m_evalStack.size() - 1;
8896 m_evalStack.setClsBaseType(SymbolicStack::CLS_UNNAMED_LOCAL);
8897 emitVirtualLocal(unnamedLoc);
8898 visit(node->getClass());
8899 emitConvertToCell(e);
8900 emitSet(e);
8901 m_evalStack.setUnnamedLocal(clsBaseIdx, unnamedLoc, m_ue.bcPos());
8902 emitPop(e);
8904 } else if (!node->getClassScope() ||
8905 node->getClassScope()->isTrait() ||
8906 (func && func->isClosure())) {
8907 // In a trait, a potentially rebound closure or psuedo-main, we can't
8908 // resolve self:: or parent:: yet, so we emit special instructions that do
8909 // those lookups.
8910 if (node->isParent()) {
8911 m_evalStack.setClsBaseType(SymbolicStack::CLS_PARENT);
8912 } else if (node->isSelf()) {
8913 m_evalStack.setClsBaseType(SymbolicStack::CLS_SELF);
8914 } else {
8915 m_evalStack.setClsBaseType(SymbolicStack::CLS_STRING_NAME);
8916 m_evalStack.setString(
8917 makeStaticString(node->getOriginalClassName()));
8919 } else if (node->isParent() &&
8920 node->getClassScope()->getOriginalParent().empty()) {
8921 // parent:: in a class without a parent. We'll emit a Parent
8922 // opcode because it can handle this error case.
8923 m_evalStack.setClsBaseType(SymbolicStack::CLS_PARENT);
8924 } else {
8925 m_evalStack.setClsBaseType(SymbolicStack::CLS_STRING_NAME);
8926 m_evalStack.setString(
8927 makeStaticString(node->getOriginalClassName()));
8931 bool EmitterVisitor::emitSystemLibVarEnvFunc(Emitter& e,
8932 SimpleFunctionCallPtr call) {
8933 if (call->isCallToFunction("extract")) {
8934 emitFuncCall(e, call,
8935 "__SystemLib\\extract", call->getParams());
8936 return true;
8937 } else if (call->isCallToFunction("parse_str")) {
8938 emitFuncCall(e, call, "__SystemLib\\parse_str", call->getParams());
8939 return true;
8940 } else if (call->isCallToFunction("compact")) {
8941 emitFuncCall(e, call,
8942 "__SystemLib\\compact_sl", call->getParams());
8943 return true;
8944 } else if (call->isCallToFunction("get_defined_vars")) {
8945 emitFuncCall(e, call,
8946 "__SystemLib\\get_defined_vars", call->getParams());
8947 return true;
8948 } else if (call->isCallToFunction("func_get_args")) {
8949 emitFuncCall(e, call,
8950 "__SystemLib\\func_get_args_sl", call->getParams());
8951 return true;
8952 } else if (call->isCallToFunction("func_get_arg")) {
8953 emitFuncCall(e, call,
8954 "__SystemLib\\func_get_arg_sl", call->getParams());
8955 return true;
8956 } else if (call->isCallToFunction("func_num_args")) {
8957 emitFuncCall(e, call,
8958 "__SystemLib\\func_num_arg_", call->getParams());
8959 return true;
8961 return false;
8964 bool EmitterVisitor::emitCallUserFunc(Emitter& e, SimpleFunctionCallPtr func) {
8965 static struct {
8966 const char* name;
8967 int minParams, maxParams;
8968 CallUserFuncFlags flags;
8969 } cufTab[] = {
8970 { "call_user_func", 1, INT_MAX, CallUserFuncPlain },
8971 { "call_user_func_array", 2, 2, CallUserFuncArray },
8972 { "forward_static_call", 1, INT_MAX, CallUserFuncForward },
8973 { "forward_static_call_array", 2, 2, CallUserFuncForwardArray },
8974 { "fb_call_user_func_safe", 1, INT_MAX, CallUserFuncSafe },
8975 { "fb_call_user_func_array_safe", 2, 2, CallUserFuncSafeArray },
8976 { "fb_call_user_func_safe_return", 2, INT_MAX, CallUserFuncSafeReturn },
8979 ExpressionListPtr params = func->getParams();
8980 if (!params) return false;
8981 int nParams = params->getCount();
8982 if (!nParams) return false;
8983 CallUserFuncFlags flags = CallUserFuncNone;
8984 for (unsigned i = 0; i < sizeof(cufTab) / sizeof(cufTab[0]); i++) {
8985 if (func->isCallToFunction(cufTab[i].name) &&
8986 nParams >= cufTab[i].minParams &&
8987 nParams <= cufTab[i].maxParams) {
8988 flags = cufTab[i].flags;
8989 break;
8992 if (flags == CallUserFuncNone) return false;
8993 if (func->hasUnpack()) {
8994 throw EmitterVisitor::IncludeTimeFatalException(
8995 func,
8996 "Using argument unpacking for a call_user_func is not supported"
8999 int param = 1;
9000 ExpressionPtr callable = (*params)[0];
9001 visit(callable);
9002 emitConvertToCell(e);
9003 Offset fpiStart = m_ue.bcPos();
9004 if (flags & CallUserFuncForward) {
9005 e.FPushCufF(nParams - param);
9006 } else if (flags & CallUserFuncSafe) {
9007 if (flags & CallUserFuncReturn) {
9008 assert(nParams >= 2);
9009 visit((*params)[param++]);
9010 emitConvertToCell(e);
9011 } else {
9012 e.Null();
9014 fpiStart = m_ue.bcPos();
9015 e.FPushCufSafe(nParams - param);
9016 } else {
9017 e.FPushCuf(nParams - param);
9021 FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
9022 for (int i = param; i < nParams; i++) {
9023 visit((*params)[i]);
9024 emitConvertToCell(e);
9025 e.FPassC(i - param);
9029 if (flags & CallUserFuncArray) {
9030 e.FCallArray();
9031 } else {
9032 e.FCall(nParams - param);
9034 if (flags & CallUserFuncSafe) {
9035 if (flags & CallUserFuncReturn) {
9036 e.CufSafeReturn();
9037 } else {
9038 e.CufSafeArray();
9041 return true;
9044 Func* EmitterVisitor::canEmitBuiltinCall(const std::string& name,
9045 int numParams) {
9046 if (Option::JitEnableRenameFunction ||
9047 !RuntimeOption::EvalEnableCallBuiltin) {
9048 return nullptr;
9050 if (Option::DynamicInvokeFunctions.size()) {
9051 if (Option::DynamicInvokeFunctions.find(name) !=
9052 Option::DynamicInvokeFunctions.end()) {
9053 return nullptr;
9056 Func* f = Unit::lookupFunc(makeStaticString(name));
9057 if (!f ||
9058 (f->attrs() & AttrNoFCallBuiltin) ||
9059 !f->nativeFuncPtr() ||
9060 f->isMethod() ||
9061 (f->numParams() > Native::maxFCallBuiltinArgs()) ||
9062 (f->userAttributes().count(
9063 LowStringPtr(s_attr_Deprecated.get())))) return nullptr;
9065 auto variadic = f->hasVariadicCaptureParam();
9067 // Only allowed to overrun the signature if we have somewhere to put it
9068 if ((numParams > f->numParams()) && !variadic) return nullptr;
9070 if ((f->returnType() == KindOfDouble) &&
9071 !Native::allowFCallBuiltinDoubles()) return nullptr;
9073 bool allowDoubleArgs = Native::allowFCallBuiltinDoubles();
9074 auto concrete_params = f->numParams();
9075 if (variadic) {
9076 assertx(concrete_params > 0);
9077 --concrete_params;
9079 for (int i = 0; i < concrete_params; i++) {
9080 if ((!allowDoubleArgs) &&
9081 (f->params()[i].builtinType == KindOfDouble)) {
9082 return nullptr;
9084 if (i >= numParams) {
9085 auto &pi = f->params()[i];
9086 if (pi.isVariadic()) continue;
9087 if (!pi.hasDefaultValue()) {
9088 return nullptr;
9090 if (pi.defaultValue.m_type == KindOfUninit) {
9091 // TODO: Resolve persistent constants
9092 return nullptr;
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 StringData* nLiteral = nullptr;
9114 Offset fpiStart = 0;
9115 if (node->getClass() || node->hasStaticClass()) {
9116 bool isSelfOrParent = node->isSelf() || node->isParent();
9117 if (!node->isStatic() && !isSelfOrParent &&
9118 !node->getOriginalClassName().empty() && !nameStr.empty()) {
9119 // cls::foo()
9120 StringData* cLiteral =
9121 makeStaticString(node->getOriginalClassName());
9122 StringData* nLiteral = makeStaticString(nameStr);
9123 fpiStart = m_ue.bcPos();
9124 e.FPushClsMethodD(numParams, nLiteral, cLiteral);
9125 } else {
9126 emitVirtualClassBase(e, node.get());
9127 if (!nameStr.empty()) {
9128 // ...::foo()
9129 StringData* nLiteral = makeStaticString(nameStr);
9130 e.String(nLiteral);
9131 } else {
9132 // ...::$foo()
9133 visit(nameExp);
9134 emitConvertToCell(e);
9136 emitResolveClsBase(e, m_evalStack.size() - 2);
9137 fpiStart = m_ue.bcPos();
9138 if (isSelfOrParent) {
9139 // self and parent are "forwarding" calls, so we need to
9140 // use FPushClsMethodF instead
9141 e.FPushClsMethodF(numParams);
9142 } else {
9143 e.FPushClsMethod(numParams);
9146 } else if (!nameStr.empty()) {
9147 // foo()
9148 nLiteral = makeStaticString(nameStr);
9149 fcallBuiltin = canEmitBuiltinCall(nameStr, numParams);
9150 if (unpack &&
9151 fcallBuiltin &&
9152 (!fcallBuiltin->hasVariadicCaptureParam() ||
9153 numParams != fcallBuiltin->numParams())) {
9154 fcallBuiltin = nullptr;
9156 StringData* nsName = nullptr;
9157 if (!node->hadBackslash() && !nameOverride) {
9158 // nameOverride is to be used only when there's an exact function
9159 // to be called ... supporting a fallback doesn't make sense
9160 const std::string& nonNSName = node->getNonNSOriginalName();
9161 if (nonNSName != nameStr) {
9162 nsName = nLiteral;
9163 nLiteral = makeStaticString(nonNSName);
9164 fcallBuiltin = nullptr;
9168 if (!fcallBuiltin) {
9169 fpiStart = m_ue.bcPos();
9170 if (nsName == nullptr) {
9171 e.FPushFuncD(numParams, nLiteral);
9172 } else {
9173 assert(!nameOverride);
9174 e.FPushFuncU(numParams, nsName, nLiteral);
9177 } else {
9178 // $foo()
9179 visit(nameExp);
9180 emitConvertToCell(e);
9181 // FPushFunc consumes method name from stack
9182 fpiStart = m_ue.bcPos();
9183 e.FPushFunc(numParams);
9185 if (fcallBuiltin) {
9186 auto variadic = !unpack && fcallBuiltin->hasVariadicCaptureParam();
9187 assertx((numParams <= fcallBuiltin->numParams()) || variadic);
9189 auto concreteParams = fcallBuiltin->numParams();
9190 if (variadic) {
9191 assertx(concreteParams > 0);
9192 --concreteParams;
9195 int i = 0;
9196 for (; i < numParams; i++) {
9197 // for builtin calls, since we don't push the ActRec, we
9198 // must determine the reffiness statically
9199 bool byRef = fcallBuiltin->byRef(i);
9200 bool mustBeRef = fcallBuiltin->mustBeRef(i);
9201 if (!emitBuiltinCallArg(e, (*params)[i], i, byRef, mustBeRef)) {
9202 while (i--) emitPop(e);
9203 return;
9207 for (; i < concreteParams; i++) {
9208 auto &pi = fcallBuiltin->params()[i];
9209 assert(pi.hasDefaultValue());
9210 auto &def = pi.defaultValue;
9211 emitBuiltinDefaultArg(e, tvAsVariant(const_cast<TypedValue*>(&def)),
9212 pi.builtinType, i);
9215 if (variadic) {
9216 if (numParams <= concreteParams) {
9217 e.Array(staticEmptyArray());
9218 } else {
9219 e.NewPackedArray(numParams - concreteParams);
9222 e.FCallBuiltin(fcallBuiltin->numParams(),
9223 std::min<int32_t>(numParams, fcallBuiltin->numParams()),
9224 nLiteral);
9225 } else {
9226 emitCall(e, node, params, fpiStart);
9228 if (fcallBuiltin) {
9229 fixReturnType(e, node, fcallBuiltin);
9233 bool EmitterVisitor::emitConstantFuncCall(Emitter& e,
9234 SimpleFunctionCallPtr call) {
9235 if (!Option::WholeProgram || Option::ConstantFunctions.empty()) return false;
9237 if (call->getClass()) {
9238 // The class expression was either non-scalar or static, neither of which
9239 // we want to optimize.
9240 return false;
9243 auto const name = call->getFullName();
9244 auto const it = Option::ConstantFunctions.find(name);
9245 if (it == Option::ConstantFunctions.end()) return false;
9247 VariableUnserializer uns{
9248 it->second.data(), it->second.size(), VariableUnserializer::Type::Serialize,
9249 false, empty_array()
9252 try {
9253 return emitScalarValue(e, uns.unserialize());
9254 } catch (const Exception& e) {
9255 throw IncludeTimeFatalException(call,
9256 "Bad ConstantValue for %s: '%s'",
9257 name.c_str(), it->second.c_str());
9261 void EmitterVisitor::emitClassTraitPrecRule(PreClassEmitter* pce,
9262 TraitPrecStatementPtr stmt) {
9263 StringData* traitName = makeStaticString(stmt->getTraitName());
9264 StringData* methodName = makeStaticString(stmt->getMethodName());
9266 PreClass::TraitPrecRule rule(traitName, methodName);
9268 hphp_string_iset otherTraitNames;
9269 stmt->getOtherTraitNames(otherTraitNames);
9270 for (auto const& name : otherTraitNames) {
9271 rule.addOtherTraitName(makeStaticString(name));
9274 pce->addTraitPrecRule(rule);
9277 void EmitterVisitor::emitClassTraitAliasRule(PreClassEmitter* pce,
9278 TraitAliasStatementPtr stmt) {
9279 StringData* traitName = makeStaticString(stmt->getTraitName());
9280 StringData* origMethName = makeStaticString(stmt->getMethodName());
9281 StringData* newMethName = makeStaticString(stmt->getNewMethodName());
9282 // If there are no modifiers, buildAttrs() defaults to AttrPublic.
9283 // Here we don't want that. Instead, set AttrNone so that the modifiers of the
9284 // original method are preserved.
9285 Attr attr = (stmt->getModifiers()->getCount() == 0 ? AttrNone :
9286 buildAttrs(stmt->getModifiers()));
9288 PreClass::TraitAliasRule rule(traitName, origMethName, newMethName, attr);
9290 pce->addTraitAliasRule(rule);
9293 void EmitterVisitor::emitClassUseTrait(PreClassEmitter* pce,
9294 UseTraitStatementPtr useStmt) {
9295 auto rules = useStmt->getStmts();
9296 for (int r = 0; r < rules->getCount(); r++) {
9297 auto rule = (*rules)[r];
9298 auto precStmt = dynamic_pointer_cast<TraitPrecStatement>(rule);
9299 if (precStmt) {
9300 emitClassTraitPrecRule(pce, precStmt);
9301 } else {
9302 auto aliasStmt = dynamic_pointer_cast<TraitAliasStatement>(rule);
9303 assert(aliasStmt);
9304 emitClassTraitAliasRule(pce, aliasStmt);
9309 Id EmitterVisitor::emitTypedef(Emitter& e, TypedefStatementPtr td) {
9310 auto const nullable = td->annot->isNullable();
9311 auto const annot = td->annot->stripNullable();
9312 auto const valueStr = annot.vanillaName();
9314 // We have to merge the strings as litstrs to ensure namedentity
9315 // creation.
9316 auto const name = makeStaticString(td->name);
9317 auto const value = makeStaticString(valueStr);
9318 m_ue.mergeLitstr(name);
9319 m_ue.mergeLitstr(value);
9321 AnnotType type;
9322 if (annot.isFunction() || annot.isMixed()) {
9323 type = AnnotType::Mixed;
9324 } else {
9325 auto const at = nameToAnnotType(value);
9326 type = at ? *at : AnnotType::Object;
9327 // Type aliases are always defined at top-level scope, so
9328 // they're not allowed to reference "self" or "parent" (and
9329 // "static" is already disallowed by the parser, so we don't
9330 // need to worry about it here).
9331 if (UNLIKELY(type == AnnotType::Self || type == AnnotType::Parent)) {
9332 throw IncludeTimeFatalException(
9334 "Cannot access %s when no class scope is active",
9335 type == AnnotType::Self ? "self" : "parent");
9339 UserAttributeMap userAttrs;
9340 ExpressionListPtr attrList = td->attrList;
9341 if (attrList) {
9342 for (int i = 0; i < attrList->getCount(); ++i) {
9343 auto attr = dynamic_pointer_cast<UserAttribute>((*attrList)[i]);
9344 auto const uaName = makeStaticString(attr->getName());
9345 auto uaValue = attr->getExp();
9346 assert(uaValue);
9347 assert(uaValue->isScalar());
9348 TypedValue tv;
9349 initScalar(tv, uaValue);
9350 userAttrs[uaName] = tv;
9354 TypeAlias record;
9355 record.typeStructure = Array(td->annot->getScalarArrayRep());
9356 record.name = name;
9357 record.value = value;
9358 record.type = type;
9359 record.nullable = nullable;
9360 record.userAttrs = userAttrs;
9361 record.attrs = !SystemLib::s_inited ? AttrPersistent : AttrNone;
9362 Id id = m_ue.addTypeAlias(record);
9363 e.DefTypeAlias(id);
9365 return id;
9368 void EmitterVisitor::emitClass(Emitter& e,
9369 ClassScopePtr cNode,
9370 bool toplevel) {
9372 const StringData* fatal_msg = cNode->getFatalMessage();
9373 if (fatal_msg != nullptr) {
9374 e.String(fatal_msg);
9375 e.Fatal(FatalOp::Runtime);
9376 return;
9379 auto is = static_pointer_cast<InterfaceStatement>(cNode->getStmt());
9380 StringData* className = makeStaticString(cNode->getOriginalName());
9381 StringData* parentName = makeStaticString(cNode->getOriginalParent());
9382 StringData* classDoc = Option::GenerateDocComments ?
9383 makeStaticString(cNode->getDocComment()) : staticEmptyString();
9384 Attr attr = cNode->isInterface() ? AttrInterface :
9385 cNode->isTrait() ? AttrTrait :
9386 cNode->isAbstract() ? AttrAbstract :
9387 cNode->isEnum() ? (AttrEnum | AttrFinal) :
9388 AttrNone;
9389 if (cNode->isFinal()) {
9390 attr = attr | AttrFinal;
9392 if (Option::WholeProgram) {
9393 if (!cNode->isRedeclaring() &&
9394 cNode->derivesFromRedeclaring() == Derivation::Normal) {
9395 attr = attr | AttrUnique;
9396 if (!cNode->isVolatile()) {
9397 attr = attr | AttrPersistent;
9400 if (cNode->isSystem()) {
9401 assert(attr & AttrPersistent);
9402 attr = attr | AttrBuiltin;
9404 if (!cNode->getAttribute(ClassScope::NotFinal)) {
9405 attr = attr | AttrNoOverride;
9407 if (cNode->getUsedTraitNames().size()) {
9408 attr = attr | AttrNoExpandTrait;
9410 } else if (!SystemLib::s_inited) {
9411 // we're building systemlib. everything is unique
9412 attr = attr | AttrBuiltin | AttrUnique | AttrPersistent;
9415 const std::vector<std::string>& bases(cNode->getBases());
9416 int firstInterface = cNode->getOriginalParent().empty() ? 0 : 1;
9417 int nInterfaces = bases.size();
9418 PreClass::Hoistable hoistable = PreClass::NotHoistable;
9419 if (toplevel) {
9420 if (SystemLib::s_inited && !cNode->isSystem()) {
9421 if (nInterfaces > firstInterface
9422 || cNode->getUsedTraitNames().size()
9423 || cNode->getClassRequiredExtends().size()
9424 || cNode->getClassRequiredImplements().size()
9425 || cNode->isEnum()
9427 hoistable = PreClass::Mergeable;
9428 } else if (firstInterface &&
9429 !m_hoistables.count(cNode->getOriginalParent())) {
9430 hoistable = PreClass::MaybeHoistable;
9433 if (hoistable == PreClass::NotHoistable) {
9434 hoistable = attr & AttrUnique ?
9435 PreClass::AlwaysHoistable : PreClass::MaybeHoistable;
9436 m_hoistables.insert(cNode->getOriginalName());
9439 PreClassEmitter* pce = m_ue.newPreClassEmitter(className, hoistable);
9440 pce->init(is->line0(), is->line1(), m_ue.bcPos(), attr, parentName,
9441 classDoc);
9442 auto r = is->getRange();
9443 r.line1 = r.line0;
9444 r.char1 = r.char0;
9445 e.setTempLocation(r);
9446 if (hoistable != PreClass::AlwaysHoistable) {
9447 e.DefCls(pce->id());
9448 } else {
9449 // To attach the line number to for error reporting.
9450 e.DefClsNop(pce->id());
9452 e.setTempLocation(OptLocation());
9453 for (int i = firstInterface; i < nInterfaces; ++i) {
9454 pce->addInterface(makeStaticString(bases[i]));
9457 const std::vector<std::string>& usedTraits = cNode->getUsedTraitNames();
9458 for (size_t i = 0; i < usedTraits.size(); i++) {
9459 pce->addUsedTrait(makeStaticString(usedTraits[i]));
9461 pce->setNumDeclMethods(cNode->getNumDeclMethods());
9462 if (cNode->isTrait() || cNode->isInterface() || Option::WholeProgram) {
9463 for (auto& reqExtends : cNode->getClassRequiredExtends()) {
9464 pce->addClassRequirement(
9465 PreClass::ClassRequirement(makeStaticString(reqExtends), true));
9467 for (auto& reqImplements : cNode->getClassRequiredImplements()) {
9468 pce->addClassRequirement(
9469 PreClass::ClassRequirement(makeStaticString(reqImplements), false));
9472 auto const& userAttrs = cNode->userAttributes();
9473 for (auto it = userAttrs.begin(); it != userAttrs.end(); ++it) {
9474 const StringData* uaName = makeStaticString(it->first);
9475 ExpressionPtr uaValue = it->second;
9476 assert(uaValue);
9477 assert(uaValue->isScalar());
9478 TypedValue tv;
9479 initScalar(tv, uaValue);
9480 pce->addUserAttribute(uaName, tv);
9483 NonScalarVec* nonScalarPinitVec = nullptr;
9484 NonScalarVec* nonScalarSinitVec = nullptr;
9485 NonScalarVec* nonScalarConstVec = nullptr;
9486 if (StatementListPtr stmts = is->getStmts()) {
9487 int i, n = stmts->getCount();
9488 for (i = 0; i < n; i++) {
9489 if (auto meth = dynamic_pointer_cast<MethodStatement>((*stmts)[i])) {
9490 StringData* methName = makeStaticString(meth->getOriginalName());
9491 FuncEmitter* fe = m_ue.newMethodEmitter(methName, pce);
9492 bool added UNUSED = pce->addMethod(fe);
9493 assert(added);
9494 postponeMeth(meth, fe, false);
9495 } else if (auto cv = dynamic_pointer_cast<ClassVariable>((*stmts)[i])) {
9496 ModifierExpressionPtr mod(cv->getModifiers());
9497 ExpressionListPtr el(cv->getVarList());
9498 Attr declAttrs = buildAttrs(mod);
9499 StringData* typeConstraint = makeStaticString(
9500 cv->getTypeConstraint());
9501 int nVars = el->getCount();
9502 for (int ii = 0; ii < nVars; ii++) {
9503 ExpressionPtr exp((*el)[ii]);
9504 ExpressionPtr vNode;
9505 SimpleVariablePtr var;
9506 if (exp->is(Expression::KindOfAssignmentExpression)) {
9507 auto ae = static_pointer_cast<AssignmentExpression>(exp);
9508 var = static_pointer_cast<SimpleVariable>(ae->getVariable());
9509 vNode = ae->getValue();
9510 } else {
9511 var = static_pointer_cast<SimpleVariable>(exp);
9514 auto const propName = makeStaticString(var->getName());
9515 auto const propDoc = Option::GenerateDocComments ?
9516 makeStaticString(var->getDocComment()) : staticEmptyString();
9517 TypedValue tvVal;
9518 // Some properties may need to be marked with the AttrDeepInit
9519 // attribute, while other properties should not be marked with
9520 // this attrbiute. We copy declAttrs into propAttrs for each loop
9521 // iteration so that we can safely add AttrDeepInit to propAttrs
9522 // without mutating the original declAttrs.
9523 Attr propAttrs = declAttrs;
9524 if (vNode) {
9525 if (vNode->isScalar()) {
9526 initScalar(tvVal, vNode);
9527 } else {
9528 tvWriteUninit(&tvVal);
9529 if (!(declAttrs & AttrStatic)) {
9530 if (requiresDeepInit(vNode)) {
9531 propAttrs = propAttrs | AttrDeepInit;
9533 if (nonScalarPinitVec == nullptr) {
9534 nonScalarPinitVec = new NonScalarVec();
9536 nonScalarPinitVec->push_back(NonScalarPair(propName, vNode));
9537 } else {
9538 if (nonScalarSinitVec == nullptr) {
9539 nonScalarSinitVec = new NonScalarVec();
9541 nonScalarSinitVec->push_back(NonScalarPair(propName, vNode));
9544 } else {
9545 tvWriteNull(&tvVal);
9547 bool added UNUSED =
9548 pce->addProperty(propName, propAttrs, typeConstraint,
9549 propDoc, &tvVal, RepoAuthType{});
9550 assert(added);
9552 } else if (auto cc = dynamic_pointer_cast<ClassConstant>((*stmts)[i])) {
9554 ExpressionListPtr el(cc->getConList());
9555 StringData* typeConstraint =
9556 makeStaticString(cc->getTypeConstraint());
9557 int nCons = el->getCount();
9559 if (cc->isAbstract()) {
9560 for (int ii = 0; ii < nCons; ii++) {
9561 auto con = static_pointer_cast<ConstantExpression>((*el)[ii]);
9562 StringData* constName = makeStaticString(con->getName());
9563 bool added UNUSED =
9564 pce->addAbstractConstant(constName, typeConstraint,
9565 cc->isTypeconst());
9566 assert(added);
9568 } else {
9569 for (int ii = 0; ii < nCons; ii++) {
9570 auto ae = static_pointer_cast<AssignmentExpression>((*el)[ii]);
9571 auto con =
9572 static_pointer_cast<ConstantExpression>(ae->getVariable());
9573 auto vNode = ae->getValue();
9574 StringData* constName = makeStaticString(con->getName());
9575 assert(vNode);
9576 TypedValue tvVal;
9577 if (vNode->isArray()) {
9578 throw IncludeTimeFatalException(
9579 cc, "Arrays are not allowed in class constants");
9580 } else if (vNode->isCollection()) {
9581 throw IncludeTimeFatalException(
9582 cc, "Collections are not allowed in class constants");
9583 } else if (vNode->isScalar()) {
9584 initScalar(tvVal, vNode);
9585 } else {
9586 tvWriteUninit(&tvVal);
9587 if (nonScalarConstVec == nullptr) {
9588 nonScalarConstVec = new NonScalarVec();
9590 nonScalarConstVec->push_back(NonScalarPair(constName, vNode));
9592 // Store PHP source code for constant initializer.
9593 std::ostringstream os;
9594 CodeGenerator cg(&os, CodeGenerator::PickledPHP);
9595 auto ar = std::make_shared<AnalysisResult>();
9596 vNode->outputPHP(cg, ar);
9597 bool added UNUSED = pce->addConstant(
9598 constName, typeConstraint, &tvVal,
9599 makeStaticString(os.str()),
9600 cc->isTypeconst(),
9601 cc->getTypeStructure());
9602 assert(added);
9605 } else if (auto useStmt =
9606 dynamic_pointer_cast<UseTraitStatement>((*stmts)[i])) {
9607 emitClassUseTrait(pce, useStmt);
9612 if (!cNode->getAttribute(ClassScope::HasConstructor) &&
9613 !cNode->getAttribute(ClassScope::ClassNameConstructor)) {
9614 // cNode does not have a constructor; synthesize 86ctor() so that the class
9615 // will always have a method that can be called during construction.
9616 static const StringData* methName = makeStaticString("86ctor");
9617 FuncEmitter* fe = m_ue.newMethodEmitter(methName, pce);
9618 bool added UNUSED = pce->addMethod(fe);
9619 assert(added);
9620 postponeCtor(is, fe);
9623 if (nonScalarPinitVec != nullptr) {
9624 // Non-scalar property initializers require 86pinit() for run-time
9625 // initialization support.
9626 static const StringData* methName = makeStaticString("86pinit");
9627 FuncEmitter* fe = m_ue.newMethodEmitter(methName, pce);
9628 pce->addMethod(fe);
9629 postponePinit(is, fe, nonScalarPinitVec);
9632 if (nonScalarSinitVec != nullptr) {
9633 // Non-scalar property initializers require 86sinit() for run-time
9634 // initialization support.
9635 static const StringData* methName = makeStaticString("86sinit");
9636 FuncEmitter* fe = m_ue.newMethodEmitter(methName, pce);
9637 pce->addMethod(fe);
9638 postponeSinit(is, fe, nonScalarSinitVec);
9641 if (nonScalarConstVec != nullptr) {
9642 // Non-scalar constant initializers require 86cinit() for run-time
9643 // initialization support.
9644 static const StringData* methName = makeStaticString("86cinit");
9645 FuncEmitter* fe = m_ue.newMethodEmitter(methName, pce);
9646 assert(!(attr & AttrTrait));
9647 bool added UNUSED = pce->addMethod(fe);
9648 assert(added);
9649 postponeCinit(is, fe, nonScalarConstVec);
9652 // If this is an enum, get its type constraint.
9653 if (cNode->isEnum()) {
9654 auto cs = static_pointer_cast<ClassStatement>(is);
9655 auto const typeConstraint =
9656 determine_type_constraint_from_annot(cs->getEnumBaseTy(), true);
9657 pce->setEnumBaseTy(typeConstraint);
9661 namespace {
9663 struct ForeachIterGuard {
9664 ForeachIterGuard(EmitterVisitor& ev,
9665 Id iterId,
9666 IterKind kind)
9667 : m_ev(ev)
9669 m_ev.pushIterScope(iterId, kind);
9671 ~ForeachIterGuard() {
9672 m_ev.popIterScope();
9675 private:
9676 EmitterVisitor& m_ev;
9681 void EmitterVisitor::emitForeachListAssignment(Emitter& e,
9682 ListAssignmentPtr la,
9683 std::function<void()> emitSrc) {
9684 std::vector<IndexPair> indexPairs;
9685 IndexChain workingChain;
9686 listAssignmentVisitLHS(e, la, workingChain, indexPairs);
9688 if (indexPairs.size() == 0) {
9689 throw IncludeTimeFatalException(la, "Cannot use empty list");
9692 listAssignmentAssignElements(e, indexPairs, emitSrc);
9695 void EmitterVisitor::emitForeach(Emitter& e,
9696 ForEachStatementPtr fe) {
9697 auto region = createRegion(fe, Region::Kind::LoopOrSwitch);
9698 ExpressionPtr ae(fe->getArrayExp());
9699 ExpressionPtr val(fe->getValueExp());
9700 ExpressionPtr key(fe->getNameExp());
9701 StatementPtr body(fe->getBody());
9702 int keyTempLocal;
9703 int valTempLocal;
9704 bool strong = fe->isStrong();
9705 Label& exit = registerBreak(fe, region.get(), 1, false)->m_label;
9706 Label& next = registerContinue(fe, region.get(), 1, false)->m_label;
9707 Label start;
9708 Offset bIterStart;
9709 Id itId = m_curFunc->allocIterator();
9710 ForeachIterGuard fig(*this, itId, strong ? KindOfMIter : KindOfIter);
9711 bool simpleCase = (!key || isNormalLocalVariable(key)) &&
9712 isNormalLocalVariable(val);
9713 bool listKey = key ? key->is(Expression::KindOfListAssignment) : false;
9714 bool listVal = val->is(Expression::KindOfListAssignment);
9716 if (simpleCase) {
9717 auto svVal = static_pointer_cast<SimpleVariable>(val);
9718 StringData* name = makeStaticString(svVal->getName());
9719 valTempLocal = m_curFunc->lookupVarId(name);
9720 if (key) {
9721 auto svKey = static_pointer_cast<SimpleVariable>(key);
9722 name = makeStaticString(svKey->getName());
9723 keyTempLocal = m_curFunc->lookupVarId(name);
9724 visit(key);
9725 // Meta info on the key local will confuse the translator (and
9726 // wouldn't be useful anyway)
9727 m_evalStack.cleanTopMeta();
9728 } else {
9729 // Make gcc happy
9730 keyTempLocal = -1;
9732 visit(val);
9733 // Meta info on the value local will confuse the translator (and
9734 // wouldn't be useful anyway)
9735 m_evalStack.cleanTopMeta();
9736 visit(ae);
9737 if (strong) {
9738 emitConvertToVar(e);
9739 if (key) {
9740 e.MIterInitK(itId, exit, valTempLocal, keyTempLocal);
9741 } else {
9742 e.MIterInit(itId, exit, valTempLocal);
9744 } else {
9745 emitConvertToCell(e);
9746 if (key) {
9747 e.IterInitK(itId, exit, valTempLocal, keyTempLocal);
9748 } else {
9749 e.IterInit(itId, exit, valTempLocal);
9753 start.set(e);
9754 bIterStart = m_ue.bcPos();
9755 } else {
9756 keyTempLocal = key ? m_curFunc->allocUnnamedLocal() : -1;
9757 valTempLocal = m_curFunc->allocUnnamedLocal();
9758 if (key) {
9759 emitVirtualLocal(keyTempLocal);
9761 emitVirtualLocal(valTempLocal);
9763 visit(ae);
9764 if (strong) {
9765 emitConvertToVar(e);
9766 } else {
9767 emitConvertToCell(e);
9770 if (strong) {
9771 if (key) {
9772 e.MIterInitK(itId, exit, valTempLocal, keyTempLocal);
9773 } else {
9774 e.MIterInit(itId, exit, valTempLocal);
9776 } else {
9777 if (key) {
9778 e.IterInitK(itId, exit, valTempLocal, keyTempLocal);
9779 } else {
9780 e.IterInit(itId, exit, valTempLocal);
9784 // At this point, valTempLocal and keyTempLocal if applicable, contain the
9785 // key and value for the iterator.
9786 start.set(e);
9787 bIterStart = m_ue.bcPos();
9788 if (key && !listKey) {
9789 visit(key);
9790 emitClsIfSPropBase(e);
9792 if (listVal) {
9793 emitForeachListAssignment(
9795 ListAssignmentPtr(static_pointer_cast<ListAssignment>(val)),
9796 [&] { emitVirtualLocal(valTempLocal); }
9798 } else {
9799 visit(val);
9800 emitClsIfSPropBase(e);
9801 emitVirtualLocal(valTempLocal);
9802 if (strong) {
9803 emitVGet(e);
9804 emitBind(e);
9805 } else {
9806 emitCGet(e);
9807 emitSet(e);
9809 emitPop(e);
9811 emitVirtualLocal(valTempLocal);
9812 emitUnset(e);
9813 newFaultRegionAndFunclet(bIterStart, m_ue.bcPos(),
9814 new UnsetUnnamedLocalThunklet(valTempLocal));
9815 if (key) {
9816 assert(keyTempLocal != -1);
9817 if (listKey) {
9818 emitForeachListAssignment(
9820 ListAssignmentPtr(static_pointer_cast<ListAssignment>(key)),
9821 [&] { emitVirtualLocal(keyTempLocal); }
9823 } else {
9824 emitVirtualLocal(keyTempLocal);
9825 emitCGet(e);
9826 emitSet(e);
9827 emitPop(e);
9829 emitVirtualLocal(keyTempLocal);
9830 emitUnset(e);
9831 newFaultRegionAndFunclet(bIterStart, m_ue.bcPos(),
9832 new UnsetUnnamedLocalThunklet(keyTempLocal));
9837 region->m_iterId = itId;
9838 region->m_iterKind = strong ? KindOfMIter : KindOfIter;
9839 enterRegion(region);
9840 SCOPE_EXIT { leaveRegion(region); };
9841 if (body) visit(body);
9843 if (next.isUsed()) {
9844 next.set(e);
9846 if (key) {
9847 emitVirtualLocal(keyTempLocal);
9848 // Meta info on the key local will confuse the translator (and
9849 // wouldn't be useful anyway)
9850 m_evalStack.cleanTopMeta();
9852 emitVirtualLocal(valTempLocal);
9853 // Meta info on the value local will confuse the translator (and
9854 // wouldn't be useful anyway)
9855 m_evalStack.cleanTopMeta();
9856 if (strong) {
9857 if (key) {
9858 e.MIterNextK(itId, start, valTempLocal, keyTempLocal);
9859 } else {
9860 e.MIterNext(itId, start, valTempLocal);
9862 } else {
9863 if (key) {
9864 e.IterNextK(itId, start, valTempLocal, keyTempLocal);
9865 } else {
9866 e.IterNext(itId, start, valTempLocal);
9869 newFaultRegionAndFunclet(bIterStart, m_ue.bcPos(),
9870 new IterFreeThunklet(itId, strong),
9871 { itId, strong ? KindOfMIter : KindOfIter });
9872 if (!simpleCase) {
9873 m_curFunc->freeUnnamedLocal(valTempLocal);
9874 if (key) {
9875 m_curFunc->freeUnnamedLocal(keyTempLocal);
9878 exit.set(e);
9879 m_curFunc->freeIterator(itId);
9882 void EmitterVisitor::emitForeachAwaitAs(Emitter& e,
9883 ForEachStatementPtr fe) {
9884 assert(!fe->isStrong());
9885 auto region = createRegion(fe, Region::Kind::LoopOrSwitch);
9886 Label& exit = registerBreak(fe, region.get(), 1, false)->m_label;
9887 Label& next = registerContinue(fe, region.get(), 1, false)->m_label;
9889 // Evaluate the AsyncIterator object and store it into unnamed local.
9890 auto const iterTempLocal = m_curFunc->allocUnnamedLocal();
9891 emitVirtualLocal(iterTempLocal);
9892 visit(fe->getArrayExp());
9893 emitConvertToCell(e);
9894 emitSet(e);
9895 auto const iterTempStartUse = m_ue.bcPos();
9897 // Make sure it actually is an AsyncIterator.
9898 e.InstanceOfD(makeStaticString("HH\\AsyncIterator"));
9899 e.JmpNZ(next);
9900 e.String(makeStaticString(
9901 "Unable to iterate non-AsyncIterator asynchronously"));
9902 e.Fatal(FatalOp::Runtime);
9904 // Start of the next iteration.
9905 next.set(e);
9907 // Await the next value.
9908 emitVirtualLocal(iterTempLocal);
9909 emitCGet(e);
9910 emitConstMethodCallNoParams(e, "next");
9911 e.Await();
9912 auto const resultTempLocal = emitSetUnnamedL(e);
9914 // Did we finish yet?
9915 emitVirtualLocal(resultTempLocal);
9916 emitIsType(e, IsTypeOp::Null);
9917 e.JmpNZ(exit);
9919 auto const populate = [&](ExpressionPtr target, int index) {
9920 auto const emitSrc = [&] {
9921 emitVirtualLocal(resultTempLocal);
9922 m_evalStack.push(StackSym::I);
9923 m_evalStack.setInt(index);
9924 markElem(e);
9927 if (target->is(Expression::KindOfListAssignment)) {
9928 emitForeachListAssignment(
9930 ListAssignmentPtr(static_pointer_cast<ListAssignment>(target)),
9931 emitSrc
9933 } else {
9934 // Obtain target to be set.
9935 visit(target);
9937 // Put $result[index] on the stack.
9938 emitSrc();
9939 emitCGet(e);
9941 // Set target.
9942 emitSet(e);
9943 emitPop(e);
9947 auto const resultTempStartUse = m_ue.bcPos();
9949 // Set the key.
9950 if (fe->getNameExp()) {
9951 populate(fe->getNameExp(), 0);
9954 // Set the value.
9955 populate(fe->getValueExp(), 1);
9957 newFaultRegionAndFunclet(resultTempStartUse, m_ue.bcPos(),
9958 new UnsetUnnamedLocalThunklet(resultTempLocal));
9959 emitVirtualLocal(resultTempLocal);
9960 emitUnset(e);
9962 // Run body.
9964 enterRegion(region);
9965 SCOPE_EXIT { leaveRegion(region); };
9966 if (fe->getBody()) visit(fe->getBody());
9969 // Continue iteration.
9970 e.Jmp(next);
9972 // Exit cleanup.
9973 exit.set(e);
9975 emitVirtualLocal(resultTempLocal);
9976 emitUnset(e);
9977 m_curFunc->freeUnnamedLocal(resultTempLocal);
9979 newFaultRegionAndFunclet(iterTempStartUse, m_ue.bcPos(),
9980 new UnsetUnnamedLocalThunklet(iterTempLocal));
9981 emitVirtualLocal(iterTempLocal);
9982 emitUnset(e);
9983 m_curFunc->freeUnnamedLocal(iterTempLocal);
9986 void EmitterVisitor::emitYieldFrom(Emitter& e, ExpressionPtr exp) {
9987 Id itId = m_curFunc->allocIterator();
9989 // Set the delegate to the result of visiting our expression
9990 visit(exp);
9991 emitConvertToCell(e);
9992 e.ContAssignDelegate(itId);
9994 Offset bDelegateAssigned = m_ue.bcPos();
9996 // Pass null to ContEnterDelegate initially.
9997 e.Null();
9999 Label loopBeginning(e);
10000 e.ContEnterDelegate();
10001 e.YieldFromDelegate(itId, loopBeginning);
10002 newFaultRegionAndFunclet(bDelegateAssigned, m_ue.bcPos(),
10003 new UnsetGeneratorDelegateThunklet(itId));
10005 // Now that we're done with it, remove the delegate. This lets us enforce
10006 // the invariant that if we have a delegate set, we should be using it.
10007 e.ContUnsetDelegate(false, itId);
10011 * Emits bytecode that restores the previous error reporting level after
10012 * evaluating a silenced (@) expression, or in the fault funclet protecting such
10013 * an expression. Requires a local variable id containing the previous error
10014 * reporting level. The whole silenced expression looks like this:
10015 * oldvalue = error_reporting(0)
10016 * ...evaluate silenced expression...
10017 * oldvalue = error_reporting(oldvalue)
10018 * if oldvalue != 0:
10019 * error_reporting(oldvalue)
10021 void EmitterVisitor::emitRestoreErrorReporting(Emitter& e, Id oldLevelLoc) {
10022 emitVirtualLocal(oldLevelLoc);
10023 auto idx = m_evalStack.size() - 1;
10024 e.Silence(m_evalStack.getLoc(idx), SilenceOp::End);
10027 void EmitterVisitor::emitMakeUnitFatal(Emitter& e,
10028 const char* msg,
10029 FatalOp k) {
10030 const StringData* sd = makeStaticString(msg);
10031 e.String(sd);
10032 e.Fatal(k);
10035 Funclet* EmitterVisitor::addFunclet(StatementPtr stmt, Thunklet* body) {
10036 Funclet* f = addFunclet(body);
10037 m_memoizedFunclets.insert(std::make_pair(stmt, f));
10038 return f;
10041 Funclet* EmitterVisitor::addFunclet(Thunklet* body) {
10042 m_funclets.push_back(new Funclet(body));
10043 return m_funclets.back();
10046 Funclet* EmitterVisitor::getFunclet(StatementPtr stmt) {
10047 if (m_memoizedFunclets.count(stmt)) {
10048 return m_memoizedFunclets[stmt];
10049 } else {
10050 return nullptr;
10054 void EmitterVisitor::emitFunclets(Emitter& e) {
10055 // TODO (#3271358): New fault funclets might appear while emitting
10056 // finally fault funclets. This is because we currently don't memoize
10057 // fault funclets other than finally fault fuclets. See task
10058 // description for more details.
10059 for (int i = 0; i < m_funclets.size(); ++i) {
10060 Funclet* f = m_funclets[i];
10061 f->m_entry.set(e);
10062 f->m_body->emit(e);
10063 delete f->m_body;
10064 f->m_body = nullptr;
10068 void EmitterVisitor::newFaultRegion(Offset start,
10069 Offset end,
10070 Label* entry,
10071 FaultIterInfo iter) {
10072 auto r = new FaultRegion(start, end, entry, iter.iterId, iter.kind);
10073 m_faultRegions.push_back(r);
10076 void EmitterVisitor::newFaultRegionAndFunclet(Offset start,
10077 Offset end,
10078 Thunklet* t,
10079 FaultIterInfo iter) {
10080 Funclet* f = addFunclet(t);
10081 newFaultRegion(start, end, &f->m_entry, iter);
10084 void EmitterVisitor::newFaultRegionAndFunclet(StatementPtr stmt,
10085 Offset start,
10086 Offset end,
10087 Thunklet* t,
10088 FaultIterInfo iter) {
10089 Funclet* f = addFunclet(stmt, t);
10090 newFaultRegion(start, end, &f->m_entry, iter);
10093 void EmitterVisitor::newFPIRegion(Offset start, Offset end, Offset fpOff) {
10094 FPIRegion* r = new FPIRegion(start, end, fpOff);
10095 m_fpiRegions.push_back(r);
10098 void EmitterVisitor::copyOverCatchAndFaultRegions(FuncEmitter* fe) {
10099 for (auto& eh : m_catchRegions) {
10100 auto& e = fe->addEHEnt();
10101 e.m_type = EHEnt::Type::Catch;
10102 e.m_base = eh->m_start;
10103 e.m_past = eh->m_end;
10104 assert(e.m_base != kInvalidOffset);
10105 assert(e.m_past != kInvalidOffset);
10106 e.m_iterId = -1;
10107 for (auto& c : eh->m_catchLabels) {
10108 Id id = m_ue.mergeLitstr(c.first);
10109 Offset off = c.second->getAbsoluteOffset();
10110 e.m_catches.push_back(std::pair<Id, Offset>(id, off));
10112 delete eh;
10114 m_catchRegions.clear();
10115 for (auto& fr : m_faultRegions) {
10116 auto& e = fe->addEHEnt();
10117 e.m_type = EHEnt::Type::Fault;
10118 e.m_base = fr->m_start;
10119 e.m_past = fr->m_end;
10120 assert(e.m_base != kInvalidOffset);
10121 assert(e.m_past != kInvalidOffset);
10122 e.m_iterId = fr->m_iterId;
10123 e.m_itRef = fr->m_iterKind == KindOfMIter;
10124 e.m_fault = fr->m_func->getAbsoluteOffset();
10125 assert(e.m_fault != kInvalidOffset);
10126 delete fr;
10128 m_faultRegions.clear();
10129 for (auto f : m_funclets) {
10130 delete f;
10132 m_funclets.clear();
10133 m_memoizedFunclets.clear();
10136 void EmitterVisitor::copyOverFPIRegions(FuncEmitter* fe) {
10137 for (std::deque<FPIRegion*>::iterator it = m_fpiRegions.begin();
10138 it != m_fpiRegions.end(); ++it) {
10139 FPIEnt& e = fe->addFPIEnt();
10140 e.m_fpushOff = (*it)->m_start;
10141 e.m_fcallOff = (*it)->m_end;
10142 e.m_fpOff = (*it)->m_fpOff;
10143 delete *it;
10145 m_fpiRegions.clear();
10148 void EmitterVisitor::saveMaxStackCells(FuncEmitter* fe, int32_t stackPad) {
10149 fe->maxStackCells +=
10150 fe->numIterators() * kNumIterCells +
10151 fe->numLocals() +
10152 stackPad;
10154 m_evalStack.m_actualStackHighWaterPtr = nullptr;
10157 // Are you sure you mean to be calling this directly? Would FuncFinisher
10158 // be more appropriate?
10159 void EmitterVisitor::finishFunc(Emitter& e, FuncEmitter* fe, int32_t stackPad) {
10160 emitFunclets(e);
10161 saveMaxStackCells(fe, stackPad);
10162 copyOverCatchAndFaultRegions(fe);
10163 copyOverFPIRegions(fe);
10164 m_staticEmitted.clear();
10165 Offset past = e.getUnitEmitter().bcPos();
10166 fe->finish(past, false);
10167 e.getUnitEmitter().recordFunction(fe);
10168 if (m_stateLocal >= 0) {
10169 m_stateLocal = -1;
10171 if (m_retLocal >= 0) {
10172 m_retLocal = -1;
10176 void EmitterVisitor::initScalar(TypedValue& tvVal, ExpressionPtr val,
10177 folly::Optional<HeaderKind> kind) {
10178 assert(val->isScalar());
10179 tvVal.m_type = KindOfUninit;
10180 // static array initilization
10181 auto initArray = [&](ExpressionPtr el, folly::Optional<HeaderKind> k) {
10182 if (k == HeaderKind::Dict) {
10183 m_staticArrays.push_back(Array::attach(MixedArray::MakeReserveDict(0)));
10184 } else if (k == HeaderKind::VecArray) {
10185 m_staticArrays.push_back(Array::attach(PackedArray::MakeReserveVec(0)));
10186 } else if (k == HeaderKind::Keyset) {
10187 m_staticArrays.push_back(Array::attach(MixedArray::MakeReserveKeyset(0)));
10188 } else {
10189 m_staticArrays.push_back(Array::attach(PackedArray::MakeReserve(0)));
10191 m_staticColType.push_back(k);
10192 visit(el);
10193 tvVal = make_array_like_tv(
10194 ArrayData::GetScalarArray(m_staticArrays.back().get())
10196 m_staticArrays.pop_back();
10197 m_staticColType.pop_back();
10199 switch (val->getKindOf()) {
10200 case Expression::KindOfConstantExpression: {
10201 auto ce = static_pointer_cast<ConstantExpression>(val);
10202 if (ce->isNull()) {
10203 tvVal.m_data.num = 0;
10204 tvVal.m_type = KindOfNull;
10205 } else if (ce->isBoolean()) {
10206 tvVal = make_tv<KindOfBoolean>(ce->getBooleanValue());
10207 } else if (ce->isScalar()) {
10208 ce->getScalarValue(tvAsVariant(&tvVal));
10209 } else {
10210 not_implemented();
10212 break;
10214 case Expression::KindOfScalarExpression: {
10215 auto sval = static_pointer_cast<ScalarExpression>(val);
10216 const std::string* s;
10217 if (sval->getString(s)) {
10218 StringData* sd = makeStaticString(*s);
10219 tvVal = make_tv<KindOfString>(sd);
10220 break;
10222 int64_t i;
10223 if (sval->getInt(i)) {
10224 tvVal = make_tv<KindOfInt64>(i);
10225 break;
10227 double d;
10228 if (sval->getDouble(d)) {
10229 tvVal = make_tv<KindOfDouble>(d);
10230 break;
10232 assert(false);
10233 break;
10235 case Expression::KindOfExpressionList: {
10236 // Array, possibly for collection initialization.
10237 initArray(val, kind);
10238 break;
10240 case Expression::KindOfUnaryOpExpression: {
10241 auto u = static_pointer_cast<UnaryOpExpression>(val);
10242 if (u->getOp() == T_ARRAY) {
10243 initArray(u->getExpression(), folly::none);
10244 break;
10246 if (u->getOp() == T_DICT) {
10247 initArray(u->getExpression(), HeaderKind::Dict);
10248 break;
10250 if (u->getOp() == T_VEC) {
10251 initArray(u->getExpression(), HeaderKind::VecArray);
10252 break;
10254 if (u->getOp() == T_KEYSET) {
10255 initArray(u->getExpression(), HeaderKind::Keyset);
10256 break;
10258 // Fall through
10260 default: {
10261 if (val->getScalarValue(tvAsVariant(&tvVal))) {
10262 if (tvAsVariant(&tvVal).isArray()) {
10263 not_implemented();
10265 break;
10267 not_reached();
10272 void EmitterVisitor::emitArrayInit(Emitter& e, ExpressionListPtr el,
10273 folly::Optional<HeaderKind> kind) {
10274 assert(m_staticArrays.empty());
10275 auto const isDict = kind == HeaderKind::Dict;
10276 auto const isVec = kind == HeaderKind::VecArray;
10277 auto const isKeyset = kind == HeaderKind::Keyset;
10279 if (el == nullptr) {
10280 if (isDict) {
10281 e.Dict(staticEmptyDictArray());
10282 return;
10284 if (isVec) {
10285 e.Vec(staticEmptyVecArray());
10286 return;
10288 if (isKeyset) {
10289 e.Keyset(staticEmptyKeysetArray());
10290 return;
10292 e.Array(staticEmptyArray());
10293 return;
10296 auto const scalar =
10297 isDict ? isDictScalar(el) :
10298 isKeyset ? isKeysetScalar(el) :
10299 el->isScalar();
10300 if (scalar) {
10301 TypedValue tv;
10302 tvWriteUninit(&tv);
10303 initScalar(tv, el, kind);
10304 if (isDict) {
10305 assert(tv.m_data.parr->isDict());
10306 e.Dict(tv.m_data.parr);
10307 return;
10309 if (isVec) {
10310 assert(tv.m_data.parr->isVecArray());
10311 e.Vec(tv.m_data.parr);
10312 return;
10314 if (isKeyset) {
10315 assert(tv.m_data.parr->isKeyset());
10316 e.Keyset(tv.m_data.parr);
10317 return;
10319 assert(tv.m_data.parr->isPHPArray());
10320 e.Array(tv.m_data.parr);
10321 return;
10324 if (isVec || isKeyset) {
10325 auto const count = el->getCount();
10326 for (int i = 0; i < count; i++) {
10327 auto expr = static_pointer_cast<Expression>((*el)[i]);
10328 visit(expr);
10329 emitConvertToCell(e);
10331 if (isVec) {
10332 e.NewVecArray(count);
10333 } else {
10334 e.NewKeysetArray(count);
10336 return;
10339 auto const allowPacked = !kind || isVectorCollection((CollectionType)*kind);
10341 int nElms;
10342 if (allowPacked && isPackedInit(el, &nElms)) {
10343 for (int i = 0; i < nElms; ++i) {
10344 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10345 visit(ap->getValue());
10346 emitConvertToCell(e);
10348 e.NewPackedArray(nElms);
10349 return;
10352 auto const allowStruct = !kind;
10353 std::vector<std::string> keys;
10354 if (allowStruct && isStructInit(el, keys)) {
10355 for (int i = 0, n = keys.size(); i < n; i++) {
10356 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10357 visit(ap->getValue());
10358 emitConvertToCell(e);
10360 e.NewStructArray(keys);
10361 return;
10364 auto capacityHint = MixedArray::SmallSize;
10365 auto const capacity = el->getCount();
10366 if (capacity > 0) capacityHint = capacity;
10367 if (allowPacked && isPackedInit(el, &nElms, false /* ignore size */)) {
10368 e.NewArray(capacityHint);
10369 } else if (isDict) {
10370 e.NewDictArray(capacityHint);
10371 } else {
10372 e.NewMixedArray(capacityHint);
10374 visit(el);
10377 void EmitterVisitor::emitPairInit(Emitter& e, ExpressionListPtr el) {
10378 if (el->getCount() != 2) {
10379 throw IncludeTimeFatalException(el,
10380 "Pair objects must have exactly 2 elements");
10382 e.NewCol(static_cast<int>(CollectionType::Pair));
10383 for (int i = 0; i < 2; i++) {
10384 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10385 if (ap->getName() != nullptr) {
10386 throw IncludeTimeFatalException(ap,
10387 "Keys may not be specified for Pair initialization");
10389 visit(ap->getValue());
10390 emitConvertToCell(e);
10391 e.ColAddNewElemC();
10395 void EmitterVisitor::emitVectorInit(Emitter&e, CollectionType ct,
10396 ExpressionListPtr el) {
10397 // Do not allow specification of keys even if the resulting array is
10398 // packed. It doesn't make sense to specify keys for Vectors.
10399 for (int i = 0; i < el->getCount(); i++) {
10400 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10401 if (ap->getName() != nullptr) {
10402 throw IncludeTimeFatalException(ap,
10403 "Keys may not be specified for Vector initialization");
10406 emitArrayInit(e, el, (HeaderKind)ct);
10407 e.ColFromArray(static_cast<int>(ct));
10408 return;
10411 void EmitterVisitor::emitSetInit(Emitter&e, CollectionType ct,
10412 ExpressionListPtr el) {
10414 * Use an array to initialize the Set only if all the following conditional
10415 * are met:
10416 * 1. non-empty initializer;
10417 * 2. no integer-like string values (keys are the same as values for Set);
10418 * 3. !arr->isVectorData() to guarantee that we have a MixedArray.
10420 * Effectively, we use array for Set initialization only when it is a static
10421 * array for now.
10423 auto const nElms = el->getCount();
10424 auto useArray = !!nElms;
10425 auto hasVectorData = true;
10426 for (int i = 0; i < nElms; i++) {
10427 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10428 auto key = ap->getName();
10429 if ((bool)key) {
10430 throw IncludeTimeFatalException(ap,
10431 "Keys may not be specified for Set initialization");
10433 if (!useArray) continue;
10434 auto val = ap->getValue();
10435 Variant v;
10436 if (val->getScalarValue(v)) {
10437 if (v.isString()) {
10438 hasVectorData = false;
10439 int64_t intVal;
10440 if (v.getStringData()->isStrictlyInteger(intVal)) {
10441 useArray = false;
10443 } else if (v.isInteger()) {
10444 if (v.asInt64Val() != i) hasVectorData = false;
10445 } else {
10446 useArray = false;
10448 } else {
10449 useArray = false;
10452 if (hasVectorData) useArray = false;
10454 if (useArray) {
10455 emitArrayInit(e, el, (HeaderKind)ct);
10456 e.ColFromArray(static_cast<int>(ct));
10457 } else {
10458 if (nElms == 0) {
10459 // Will use the static empty mixed array to avoid allocation.
10460 e.NewCol(static_cast<int>(ct));
10461 return;
10463 e.NewMixedArray(nElms);
10464 e.ColFromArray(static_cast<int>(ct));
10465 for (int i = 0; i < nElms; i++) {
10466 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10467 visit(ap->getValue());
10468 emitConvertToCell(e);
10469 e.ColAddNewElemC();
10474 void EmitterVisitor::emitMapInit(Emitter&e, CollectionType ct,
10475 ExpressionListPtr el) {
10477 * Use an array to initialize the Map only when all the following conditional
10478 * are met:
10479 * 1. non-empty initializer;
10480 * 2. no integer-like string keys;
10481 * 3. !arr->isVectorData() to guarantee that we have a MixedArray.
10483 auto nElms = el->getCount();
10484 auto useArray = !!nElms;
10485 auto hasVectorData = true;
10486 int64_t max = 0;
10487 for (int i = 0; i < nElms; i++) {
10488 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10489 auto key = ap->getName();
10490 if (key == nullptr) {
10491 throw IncludeTimeFatalException(ap,
10492 "Keys must be specified for Map initialization");
10494 if (!useArray) continue;
10495 Variant vkey;
10496 if (key->getScalarValue(vkey)) {
10497 if (vkey.isString()) {
10498 hasVectorData = false;
10499 int64_t intKey;
10500 if (vkey.getStringData()->isStrictlyInteger(intKey)) {
10501 useArray = false;
10503 } else if (vkey.isInteger()) {
10504 auto val = vkey.asInt64Val();
10505 if (val > max || val < 0) {
10506 hasVectorData = false;
10507 } else if (val == max) {
10508 ++max;
10510 } else {
10511 useArray = false;
10513 } else {
10514 useArray = false;
10517 if (hasVectorData) useArray = false;
10519 if (useArray) {
10520 emitArrayInit(e, el, (HeaderKind)ct);
10521 e.ColFromArray(static_cast<int>(ct));
10522 } else {
10523 if (nElms == 0) {
10524 e.NewCol(static_cast<int>(ct));
10525 return;
10527 e.NewMixedArray(nElms);
10528 e.ColFromArray(static_cast<int>(ct));
10529 for (int i = 0; i < nElms; i++) {
10530 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10531 visit(ap->getName());
10532 emitConvertToCell(e);
10533 visit(ap->getValue());
10534 emitConvertToCell(e);
10535 e.MapAddElemC();
10540 void EmitterVisitor::emitCollectionInit(Emitter& e, BinaryOpExpressionPtr b) {
10541 auto cls = static_pointer_cast<ScalarExpression>(b->getExp1());
10542 const std::string* clsName = nullptr;
10543 cls->getString(clsName);
10544 auto ct = collections::stringToType(*clsName);
10545 if (!ct) {
10546 throw IncludeTimeFatalException(b,
10547 "Cannot use collection initialization for non-collection class");
10550 ExpressionListPtr el = nullptr;
10551 if (b->getExp2()) {
10552 el = static_pointer_cast<ExpressionList>(b->getExp2());
10553 } else {
10554 if (ct == CollectionType::Pair) {
10555 throw IncludeTimeFatalException(b, "Initializer needed for Pair object");
10557 e.NewCol(static_cast<int>(*ct));
10558 return;
10561 if (ct == CollectionType::Pair) {
10562 return emitPairInit(e, el);
10565 if (ct == CollectionType::Vector || ct == CollectionType::ImmVector) {
10566 return emitVectorInit(e, *ct, el);
10569 if (ct == CollectionType::Map || ct == CollectionType::ImmMap) {
10570 return emitMapInit(e, *ct, el);
10573 if (ct == CollectionType::Set || ct == CollectionType::ImmSet) {
10574 return emitSetInit(e, *ct, el);
10577 not_reached();
10580 bool EmitterVisitor::requiresDeepInit(ExpressionPtr initExpr) const {
10581 switch (initExpr->getKindOf()) {
10582 case Expression::KindOfScalarExpression:
10583 return false;
10584 case Expression::KindOfClassConstantExpression:
10585 case Expression::KindOfConstantExpression:
10586 return !initExpr->isScalar();
10587 case Expression::KindOfUnaryOpExpression: {
10588 auto u = static_pointer_cast<UnaryOpExpression>(initExpr);
10589 if (u->getOp() == T_ARRAY || u->getOp() == T_DICT) {
10590 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
10591 if (el) {
10592 int n = el->getCount();
10593 for (int i = 0; i < n; i++) {
10594 auto ap = static_pointer_cast<ArrayPairExpression>((*el)[i]);
10595 ExpressionPtr key = ap->getName();
10596 if (requiresDeepInit(ap->getValue()) ||
10597 (key && requiresDeepInit(key))) {
10598 return true;
10602 return false;
10603 } else if (u->getOp() == T_VEC || u->getOp() == T_KEYSET) {
10604 auto el = static_pointer_cast<ExpressionList>(u->getExpression());
10605 if (el) {
10606 int n = el->getCount();
10607 for (int i = 0; i < n; i++) {
10608 auto expr = static_pointer_cast<Expression>((*el)[i]);
10609 if (requiresDeepInit(expr)) return true;
10612 return false;
10613 } else if (u->getOp() == '+' || u->getOp() == '-') {
10614 return requiresDeepInit(u->getExpression());
10615 } else if (u->getOp() == T_FILE || u->getOp() == T_DIR) {
10616 return false;
10618 return true;
10620 case Expression::KindOfBinaryOpExpression: {
10621 auto b = static_pointer_cast<BinaryOpExpression>(initExpr);
10622 return requiresDeepInit(b->getExp1()) || requiresDeepInit(b->getExp2());
10624 default:
10625 return true;
10629 Thunklet::~Thunklet() {}
10631 static ConstructPtr doOptimize(ConstructPtr c, AnalysisResultConstPtr ar) {
10632 for (int i = 0, n = c->getKidCount(); i < n; i++) {
10633 if (ConstructPtr k = c->getNthKid(i)) {
10634 if (ConstructPtr rep = doOptimize(k, ar)) {
10635 c->setNthKid(i, rep);
10639 if (auto e = dynamic_pointer_cast<Expression>(c)) {
10640 switch (e->getKindOf()) {
10641 case Expression::KindOfBinaryOpExpression:
10642 case Expression::KindOfUnaryOpExpression:
10643 case Expression::KindOfIncludeExpression:
10644 case Expression::KindOfSimpleFunctionCall:
10645 return e->preOptimize(ar);
10646 case Expression::KindOfClosureExpression: {
10647 auto cl = static_pointer_cast<ClosureExpression>(e);
10648 auto UNUSED exp = doOptimize(cl->getClosureFunction(), ar);
10649 assert(!exp);
10650 break;
10652 default: break;
10655 return ConstructPtr();
10658 static UnitEmitter* emitHHBCUnitEmitter(AnalysisResultPtr ar, FileScopePtr fsp,
10659 const MD5& md5) {
10660 if (fsp->getPseudoMain() && !Option::WholeProgram) {
10661 ar->setPhase(AnalysisResult::FirstPreOptimize);
10662 doOptimize(fsp->getPseudoMain()->getStmt(), ar);
10665 if (RuntimeOption::EvalDumpAst) {
10666 if (fsp->getPseudoMain()) {
10667 fsp->getPseudoMain()->getStmt()->dump(0, ar);
10671 auto msp =
10672 dynamic_pointer_cast<MethodStatement>(fsp->getPseudoMain()->getStmt());
10673 UnitEmitter* ue = new UnitEmitter(md5);
10674 ue->m_preloadPriority = fsp->preloadPriority();
10675 ue->initMain(msp->line0(), msp->line1());
10676 EmitterVisitor ev(*ue);
10677 try {
10678 ev.visit(fsp);
10679 } catch (EmitterVisitor::IncludeTimeFatalException& ex) {
10680 // Replace the unit with an empty one, but preserve its file path.
10681 UnitEmitter* nue = new UnitEmitter(md5);
10682 nue->initMain(msp->line0(), msp->line1());
10683 nue->m_filepath = ue->m_filepath;
10684 delete ue;
10685 ue = nue;
10687 EmitterVisitor fev(*ue);
10688 Emitter emitter(ex.m_node, *ue, fev);
10689 FuncFinisher ff(&fev, emitter, ue->getMain());
10690 auto kind = ex.m_parseFatal ? FatalOp::Parse : FatalOp::Runtime;
10691 fev.emitMakeUnitFatal(emitter, ex.getMessage().c_str(), kind);
10693 return ue;
10696 enum GeneratorMethod {
10697 METH_NEXT,
10698 METH_SEND,
10699 METH_RAISE,
10700 METH_VALID,
10701 METH_CURRENT,
10702 METH_KEY,
10703 METH_REWIND,
10704 METH_GETRETURN,
10707 typedef hphp_hash_map<const StringData*, GeneratorMethod,
10708 string_data_hash, string_data_same> ContMethMap;
10709 typedef std::map<StaticString, GeneratorMethod> ContMethMapT;
10711 namespace {
10712 StaticString s_next("next");
10713 StaticString s_send("send");
10714 StaticString s_raise("raise");
10715 StaticString s_valid("valid");
10716 StaticString s_current("current");
10717 StaticString s_key("key");
10718 StaticString s_throw("throw");
10719 StaticString s_rewind("rewind");
10720 StaticString s_getReturn("getReturn");
10722 StaticString genCls("Generator");
10723 StaticString asyncGenCls("HH\\AsyncGenerator");
10725 ContMethMapT s_asyncGenMethods = {
10726 {s_next, GeneratorMethod::METH_NEXT},
10727 {s_send, GeneratorMethod::METH_SEND},
10728 {s_raise, GeneratorMethod::METH_RAISE}
10730 ContMethMapT s_genMethods = {
10731 {s_next, GeneratorMethod::METH_NEXT},
10732 {s_send, GeneratorMethod::METH_SEND},
10733 {s_raise, GeneratorMethod::METH_RAISE},
10734 {s_valid, GeneratorMethod::METH_VALID},
10735 {s_current, GeneratorMethod::METH_CURRENT},
10736 {s_key, GeneratorMethod::METH_KEY},
10737 {s_throw, GeneratorMethod::METH_RAISE},
10738 {s_rewind, GeneratorMethod::METH_REWIND},
10739 {s_getReturn, GeneratorMethod::METH_GETRETURN}
10743 static int32_t emitGeneratorMethod(UnitEmitter& ue,
10744 FuncEmitter* fe,
10745 GeneratorMethod m,
10746 bool isAsync) {
10747 Attr attrs = (Attr)(AttrBuiltin | AttrPublic);
10748 fe->init(0, 0, ue.bcPos(), attrs, false, staticEmptyString());
10749 fe->isNative = false;
10750 if (!isAsync && RuntimeOption::AutoprimeGenerators) {
10751 // Create a dummy Emitter, so it's possible to emit jump instructions
10752 EmitterVisitor ev(ue);
10753 Emitter e(ConstructPtr(), ue, ev);
10754 Location::Range loc;
10755 if (ue.bcPos() > 0) loc.line0 = -1;
10756 e.setTempLocation(loc);
10758 // Check if the generator has started yet
10759 Label started;
10760 e.ContStarted();
10761 e.JmpNZ(started);
10763 // If it hasn't started, perform one "next" operation before
10764 // the actual operation (auto-priming)
10765 e.ContCheck(false);
10766 e.Null();
10767 e.ContEnter();
10768 e.PopC();
10769 started.set(e);
10772 if (!RuntimeOption::AutoprimeGenerators && m == METH_REWIND) {
10773 // In non-autopriming mode the rewind function will always call next, when
10774 // autopriming is enabled, rewind matches PHP behavior and will only advance
10775 // the generator when it has not yet been started.
10776 m = METH_NEXT;
10779 switch (m) {
10780 case METH_SEND:
10781 case METH_RAISE:
10782 case METH_NEXT: {
10783 // We always want these methods to be cloned with new funcids in
10784 // subclasses so we can burn Class*s and Func*s into the
10785 // translations
10786 fe->attrs |= AttrClone;
10788 // check generator status; send()/raise() also checks started
10789 ue.emitOp(OpContCheck);
10790 ue.emitIVA(m == METH_SEND || m == METH_RAISE);
10792 switch (m) {
10793 case METH_NEXT:
10794 ue.emitOp(OpNull);
10795 ue.emitOp(OpContEnter);
10796 break;
10797 case METH_SEND:
10798 ue.emitOp(OpPushL); ue.emitIVA(0);
10799 ue.emitOp(OpContEnter);
10800 break;
10801 case METH_RAISE:
10802 ue.emitOp(OpPushL); ue.emitIVA(0);
10803 ue.emitOp(OpContRaise);
10804 break;
10805 default:
10806 not_reached();
10809 // Backtrace has off-by-one bug when determining whether we are
10810 // in returning opcode; add Nop to avoid it
10811 ue.emitOp(OpNop);
10812 ue.emitOp(OpRetC);
10813 break;
10815 case METH_VALID: {
10816 ue.emitOp(OpContValid);
10817 ue.emitOp(OpRetC);
10818 break;
10820 case METH_CURRENT: {
10821 ue.emitOp(OpContCurrent);
10822 ue.emitOp(OpRetC);
10823 break;
10825 case METH_KEY: {
10826 ue.emitOp(OpContKey);
10827 ue.emitOp(OpRetC);
10828 break;
10830 case METH_REWIND: {
10831 ue.emitOp(OpNull);
10832 ue.emitOp(OpRetC);
10833 break;
10835 case METH_GETRETURN: {
10836 ue.emitOp(OpContGetReturn);
10837 ue.emitOp(OpRetC);
10838 break;
10840 default:
10841 not_reached();
10844 return 1; // Above cases push at most one stack cell.
10847 // Emit byte codes to implement methods. Return the maximum stack cell count.
10848 int32_t EmitterVisitor::emitNativeOpCodeImpl(MethodStatementPtr meth,
10849 const char* funcName,
10850 const char* className,
10851 FuncEmitter* fe) {
10852 GeneratorMethod* cmeth;
10853 StaticString s_func(funcName);
10854 StaticString s_class(className);
10856 if (genCls.same(s_class) &&
10857 (cmeth = folly::get_ptr(s_genMethods, s_func))) {
10858 return emitGeneratorMethod(m_ue, fe, *cmeth, false);
10859 } else if (asyncGenCls.same(s_class) &&
10860 (cmeth = folly::get_ptr(s_asyncGenMethods, s_func))) {
10861 return emitGeneratorMethod(m_ue, fe, *cmeth, true);
10864 throw IncludeTimeFatalException(meth,
10865 "OpCodeImpl attribute is not applicable to %s", funcName);
10868 static UnitEmitter* emitHHBCVisitor(AnalysisResultPtr ar, FileScopeRawPtr fsp) {
10869 auto md5 = fsp->getMd5();
10871 if (!Option::WholeProgram) {
10872 // The passed-in ar is only useful in whole-program mode, so create a
10873 // distinct ar to be used only for emission of this unit, and perform
10874 // unit-level (non-global) optimization.
10875 ar = std::make_shared<AnalysisResult>();
10876 fsp->setOuterScope(ar);
10878 ar->setPhase(AnalysisResult::AnalyzeAll);
10879 fsp->analyzeProgram(ar);
10882 auto ue = emitHHBCUnitEmitter(ar, fsp, md5);
10883 assert(ue != nullptr);
10885 if (Option::GenerateTextHHBC) {
10886 // TODO(#2973538): Move HHBC text generation to after all the
10887 // units are created, and get rid of the LitstrTable locking,
10888 // since it won't be needed in that case.
10889 LitstrTable::get().mutex().lock();
10890 LitstrTable::get().setReading();
10891 std::unique_ptr<Unit> unit(ue->create());
10892 std::string fullPath = AnalysisResult::prepareFile(
10893 ar->getOutputPath().c_str(), Option::UserFilePrefix + fsp->getName(),
10894 true, false) + ".hhbc.txt";
10896 std::ofstream f(fullPath.c_str());
10897 if (!f) {
10898 Logger::Error("Unable to open %s for write", fullPath.c_str());
10899 } else {
10900 CodeGenerator cg(&f, CodeGenerator::TextHHBC);
10901 cg.printf("Hash: %" PRIx64 "%016" PRIx64 "\n", md5.q[0], md5.q[1]);
10902 cg.printRaw(unit->toString().c_str());
10903 f.close();
10905 LitstrTable::get().setWriting();
10906 LitstrTable::get().mutex().unlock();
10909 return ue;
10912 struct UEQ : Synchronizable {
10913 void push(UnitEmitter* ue) {
10914 assert(ue != nullptr);
10915 Lock lock(this);
10916 m_ues.push_back(ue);
10917 notify();
10919 UnitEmitter* tryPop(long sec, long long nsec) {
10920 Lock lock(this);
10921 if (m_ues.empty()) {
10922 // Check for empty() after wait(), in case of spurious wakeup.
10923 if (!wait(sec, nsec) || m_ues.empty()) {
10924 return nullptr;
10927 assert(m_ues.size() > 0);
10928 UnitEmitter* ue = m_ues.front();
10929 assert(ue != nullptr);
10930 m_ues.pop_front();
10931 return ue;
10933 private:
10934 std::deque<UnitEmitter*> m_ues;
10936 static UEQ s_ueq;
10938 class EmitterWorker
10939 : public JobQueueWorker<FileScopeRawPtr, void*, true, true> {
10940 public:
10941 EmitterWorker() : m_ret(true) {}
10942 void doJob(JobType job) override {
10943 try {
10944 AnalysisResultPtr ar = ((AnalysisResult*)m_context)->shared_from_this();
10945 UnitEmitter* ue = emitHHBCVisitor(ar, job);
10946 if (Option::GenerateBinaryHHBC) {
10947 s_ueq.push(ue);
10948 } else {
10949 delete ue;
10951 } catch (Exception &e) {
10952 Logger::Error("%s", e.getMessage().c_str());
10953 m_ret = false;
10954 } catch (...) {
10955 Logger::Error("Fatal: An unexpected exception was thrown");
10956 m_ret = false;
10959 void onThreadEnter() override {
10960 g_context.getCheck();
10962 void onThreadExit() override {
10963 hphp_memory_cleanup();
10965 private:
10966 bool m_ret;
10969 static void addEmitterWorker(AnalysisResultPtr ar, StatementPtr sp,
10970 void *data) {
10971 ((JobQueueDispatcher<EmitterWorker>*)data)->enqueue(sp->getFileScope());
10974 static void
10975 commitGlobalData(std::unique_ptr<ArrayTypeTable::Builder> arrTable) {
10976 auto gd = Repo::GlobalData{};
10977 gd.HardTypeHints = HHBBC::options.HardTypeHints;
10978 gd.HardReturnTypeHints = HHBBC::options.HardReturnTypeHints;
10979 gd.UsedHHBBC = Option::UseHHBBC;
10980 gd.PHP7_IntSemantics = RuntimeOption::PHP7_IntSemantics;
10981 gd.PHP7_ScalarTypes = RuntimeOption::PHP7_ScalarTypes;
10982 gd.PHP7_Substr = RuntimeOption::PHP7_Substr;
10983 gd.AutoprimeGenerators = RuntimeOption::AutoprimeGenerators;
10984 gd.HardPrivatePropInference = true;
10985 for (auto a : Option::APCProfile) {
10986 gd.APCProfile.emplace_back(StringData::MakeStatic(folly::StringPiece(a)));
10988 if (arrTable) gd.arrayTypeTable.repopulate(*arrTable);
10989 Repo::get().saveGlobalData(gd);
10993 * This is the entry point for offline bytecode generation.
10995 void emitAllHHBC(AnalysisResultPtr&& ar) {
10996 unsigned int threadCount = Option::ParserThreadCount;
10997 unsigned int nFiles = ar->getAllFilesVector().size();
10998 if (threadCount > nFiles) {
10999 threadCount = nFiles;
11001 if (!threadCount) threadCount = 1;
11003 LitstrTable::get().setWriting();
11005 /* there is a race condition in the first call to
11006 makeStaticString. Make sure we dont hit it */
11008 makeStaticString("");
11009 /* same for TypeConstraint */
11010 TypeConstraint tc;
11013 Compiler::ClearErrors();
11015 JobQueueDispatcher<EmitterWorker>
11016 dispatcher(threadCount, 0, false, ar.get());
11018 auto setPreloadPriority = [&ar](const std::string& f, int p) {
11019 auto fs = ar->findFileScope(f);
11020 if (fs) fs->setPreloadPriority(p);
11024 * Mark files that are referenced from the autoload map
11025 * so they get preloaded via preloadRepo.
11026 * Higher priorities are preloaded first.
11027 * Classes, then functions, then constants mimics
11028 * the order of our existing warmup scripts
11030 for (const auto& ent : Option::AutoloadConstMap) {
11031 setPreloadPriority(ent.second, 1);
11033 for (const auto& ent : Option::AutoloadFuncMap) {
11034 setPreloadPriority(ent.second, 2);
11036 for (const auto& ent : Option::AutoloadClassMap) {
11037 setPreloadPriority(ent.second, 3);
11040 dispatcher.start();
11041 ar->visitFiles(addEmitterWorker, &dispatcher);
11043 auto ues = ar->getHhasFiles();
11044 if (!Option::UseHHBBC && ues.size()) {
11045 batchCommit(std::move(ues));
11048 if (Option::GenerateBinaryHHBC) {
11049 // kBatchSize needs to strike a balance between reducing transaction commit
11050 // overhead (bigger batches are better), and limiting the cost incurred by
11051 // failed commits due to identical units that require rollback and retry
11052 // (smaller batches have less to lose). Empirical results indicate that a
11053 // value in the 2-10 range is reasonable.
11054 static const unsigned kBatchSize = 8;
11056 // Gather up units created by the worker threads and commit them in
11057 // batches.
11058 bool didPop;
11059 bool inShutdown = false;
11060 while (true) {
11061 // Poll, but with a 100ms timeout so that this thread doesn't spin wildly
11062 // if it gets ahead of the workers.
11063 UnitEmitter* ue = s_ueq.tryPop(0, 100 * 1000 * 1000);
11064 if ((didPop = (ue != nullptr))) {
11065 ues.push_back(std::unique_ptr<UnitEmitter>{ue});
11067 if (!Option::UseHHBBC &&
11068 (ues.size() == kBatchSize ||
11069 (!didPop && inShutdown && ues.size() > 0))) {
11070 batchCommit(std::move(ues));
11072 if (!inShutdown) {
11073 inShutdown = dispatcher.pollEmpty();
11074 } else if (!didPop) {
11075 assert(Option::UseHHBBC || ues.size() == 0);
11076 break;
11080 if (!Option::UseHHBBC) {
11081 commitGlobalData(std::unique_ptr<ArrayTypeTable::Builder>{});
11083 } else {
11084 dispatcher.waitEmpty();
11087 assert(Option::UseHHBBC || ues.empty());
11089 ar->finish();
11090 ar.reset();
11092 if (!Option::UseHHBBC) {
11093 batchCommit(std::move(ues));
11094 return;
11097 RuntimeOption::EvalJit = false; // For HHBBC to invoke builtins.
11098 auto pair = HHBBC::whole_program(std::move(ues));
11099 batchCommit(std::move(pair.first));
11100 commitGlobalData(std::move(pair.second));
11103 namespace {
11105 bool startsWith(const char* big, const char* small) {
11106 return strncmp(big, small, strlen(small)) == 0;
11108 bool isFileHack(const char* code, int codeLen, bool allowPartial) {
11109 if (allowPartial) {
11110 return codeLen > strlen("<?hh") && startsWith(code, "<?hh");
11111 } else {
11112 return codeLen > strlen("<?hh // strict") &&
11113 (startsWith(code, "<?hh // strict") || startsWith(code, "<?hh //strict"));
11117 UnitEmitter* makeFatalUnit(const char* filename, const MD5& md5,
11118 const std::string& msg) {
11119 // basically duplicated from as.cpp but is maybe too janky to be
11120 // a common routine somewhere...
11122 // The line numbers we output are bogus, but it's not totally clear
11123 // what line numbers to put. It might be worth adding a mechanism for
11124 // the external emitter to emit a line number when it fails that we can
11125 // use when available.
11126 UnitEmitter* ue = new UnitEmitter(md5);
11127 ue->m_filepath = makeStaticString(filename);
11128 ue->initMain(1, 1);
11129 ue->emitOp(OpString);
11130 ue->emitInt32(ue->mergeLitstr(makeStaticString(msg)));
11131 ue->emitOp(OpFatal);
11132 ue->emitByte(static_cast<uint8_t>(FatalOp::Runtime));
11133 FuncEmitter* fe = ue->getMain();
11134 fe->maxStackCells = 1;
11135 fe->finish(ue->bcPos(), false);
11136 ue->recordFunction(fe);
11138 return ue;
11141 UnitEmitter* useExternalEmitter(const char* code, int codeLen,
11142 const char* filename, const MD5& md5) {
11143 #ifdef _MSC_VER
11144 Logger::Error("The external emitter is not supported under MSVC!");
11145 return nullptr;
11146 #else
11147 std::string hhas, errorOutput;
11149 try {
11150 std::vector<std::string> cmd({
11151 RuntimeOption::EvalUseExternalEmitter, "--stdin", filename});
11152 auto options = folly::Subprocess::pipeStdin().pipeStdout().pipeStderr();
11154 // Run the external emitter, sending the code to its stdin
11155 folly::Subprocess proc(cmd, options);
11156 std::tie(hhas, errorOutput) = proc.communicate(std::string(code, codeLen));
11157 proc.waitChecked();
11159 // External emitter succeeded; assemble its output
11160 // If assembly fails (probably because of malformed emitter
11161 // output), the assembler will return a unit that Fatals and we
11162 // won't do a fallback to the regular emitter. We may want to
11163 // revisit this.
11164 return assemble_string(hhas.data(), hhas.length(), filename, md5);
11166 } catch (const std::exception& e) {
11167 std::string errorMsg = e.what();
11168 if (!errorOutput.empty()) {
11169 // Add the stderr to the output
11170 errorMsg += folly::format(". Output: '{}'",
11171 folly::trimWhitespace(errorOutput)).str();
11174 // If we aren't going to fall back to the internal emitter, generate a
11175 // Fatal'ing unit.
11176 if (!RuntimeOption::EvalExternalEmitterFallback) {
11177 auto msg =
11178 folly::format("Failure running external emitter: {}", errorMsg);
11179 return makeFatalUnit(filename, md5, msg.str());
11182 // Unless we have fallback at the highest level, print a
11183 // diagnostic when we fail
11184 if (RuntimeOption::EvalExternalEmitterFallback < 2) {
11185 Logger::Warning("Failure running external emitter for %s: %s",
11186 filename,
11187 errorMsg.c_str());
11189 return nullptr;
11191 #endif
11196 extern "C" {
11199 * This is the entry point from the runtime; i.e. online bytecode generation.
11200 * The 'filename' parameter may be NULL if there is no file associated with
11201 * the source code.
11203 * Before being actually used, hphp_compiler_parse must be called with
11204 * a NULL `code' parameter to do initialization.
11207 Unit* hphp_compiler_parse(const char* code, int codeLen, const MD5& md5,
11208 const char* filename) {
11209 if (UNLIKELY(!code)) {
11210 // Do initialization when code is null; see above.
11211 Option::EnableHipHopSyntax = RuntimeOption::EnableHipHopSyntax;
11212 HHBBC::options.HardReturnTypeHints =
11213 RuntimeOption::EvalCheckReturnTypeHints >= 3;
11214 Option::EnableZendCompat = RuntimeOption::EnableZendCompat;
11215 Option::JitEnableRenameFunction =
11216 RuntimeOption::EvalJitEnableRenameFunction;
11217 for (auto& i : RuntimeOption::DynamicInvokeFunctions) {
11218 Option::DynamicInvokeFunctions.insert(i);
11220 Option::IntsOverflowToInts = RuntimeOption::IntsOverflowToInts;
11221 Option::RecordErrors = false;
11222 Option::ParseTimeOpts = false;
11223 Option::WholeProgram = false;
11224 BuiltinSymbols::LoadSuperGlobals();
11225 TypeConstraint tc;
11226 return nullptr;
11229 SCOPE_ASSERT_DETAIL("hphp_compiler_parse") { return filename; };
11231 try {
11232 UnitOrigin unitOrigin = UnitOrigin::File;
11233 if (!filename) {
11234 filename = "";
11235 unitOrigin = UnitOrigin::Eval;
11238 std::unique_ptr<UnitEmitter> ue;
11239 // Check if this file contains raw hip hop bytecode instead of
11240 // php. This is dictated by a special file extension.
11241 if (RuntimeOption::EvalAllowHhas) {
11242 if (const char* dot = strrchr(filename, '.')) {
11243 const char hhbc_ext[] = "hhas";
11244 if (!strcmp(dot + 1, hhbc_ext)) {
11245 ue.reset(assemble_string(code, codeLen, filename, md5));
11250 // If we are configured to use an external emitter and we are compiling
11251 // a strict mode hack file, try external emitting. Don't externally emit
11252 // systemlib because the external emitter can't handle that yet.
11253 if (!ue &&
11254 !RuntimeOption::EvalUseExternalEmitter.empty() &&
11255 isFileHack(code, codeLen,
11256 RuntimeOption::EvalExternalEmitterAllowPartial) &&
11257 SystemLib::s_inited) {
11258 ue.reset(useExternalEmitter(code, codeLen, filename, md5));
11261 if (!ue) {
11262 auto parseit = [=] (AnalysisResultPtr ar) {
11263 Scanner scanner(code, codeLen,
11264 RuntimeOption::GetScannerType(), filename);
11265 Parser parser(scanner, filename, ar, codeLen);
11266 parser.parse();
11267 return parser.getFileScope();
11270 if (BuiltinSymbols::s_systemAr) {
11271 parseit(BuiltinSymbols::s_systemAr)->setMd5(md5);
11274 auto ar = std::make_shared<AnalysisResult>();
11275 FileScopePtr fsp = parseit(ar);
11276 fsp->setOuterScope(ar);
11278 ar->setPhase(AnalysisResult::AnalyzeAll);
11279 fsp->analyzeProgram(ar);
11281 ue.reset(emitHHBCUnitEmitter(ar, fsp, md5));
11283 // NOTE: Repo errors are ignored!
11284 Repo::get().commitUnit(ue.get(), unitOrigin);
11286 auto unit = ue->create();
11287 ue.reset();
11289 if (unit->sn() == -1) {
11290 // the unit was not committed to the Repo, probably because
11291 // another thread did it first. Try to use the winner.
11292 auto u = Repo::get().loadUnit(filename ? filename : "", md5);
11293 if (u != nullptr) {
11294 return u.release();
11297 return unit.release();
11298 } catch (const std::exception&) {
11299 // extern "C" function should not be throwing exceptions...
11300 return nullptr;
11304 } // extern "C"
11306 ///////////////////////////////////////////////////////////////////////////////