2 +----------------------------------------------------------------------+
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"
30 #include <boost/algorithm/string/predicate.hpp>
32 #include <folly/MapUtil.h>
33 #include <folly/Memory.h>
34 #include <folly/ScopeGuard.h>
36 #include <folly/Subprocess.h>
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"
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
);
152 ///////////////////////////////////////////////////////////////////////////////
154 TRACE_SET_MOD(emitter
);
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");
164 struct EmitterVisitor
;
166 using OptLocation
= folly::Optional
<Location::Range
>;
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
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;
224 std::string
ToString(char sym
) {
225 char symFlavor
= StackSym::GetSymFlavor(sym
);
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;
239 char marker
= StackSym::GetMarker(sym
);
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;
252 if (sym
== StackSym::None
) {
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
) {
271 const OptLocation
& getTempLocation() { return m_tempLoc
; }
272 void incStat(int counter
, int value
) {
273 if (RuntimeOption::EnableEmitterStats
) {
274 IncStat(counter
, value
);
279 StrOff(Id s
, Label
* d
) : str(s
), dest(d
) {}
285 IterPair(IterKind k
, Id i
) : kind(k
), id(i
) {}
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);
342 EmitterVisitor
& m_ev
;
343 OptLocation m_tempLoc
;
346 struct SymbolicStack
{
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
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.
374 explicit SymEntry(char s
= 0)
379 , unnamedLocalStart(InvalidAbsoluteOffset
)
380 , clsBaseType(CLS_INVALID
)
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
416 int* m_actualStackHighWaterPtr
;
418 SymbolicStack() : m_fdescCount(0) {}
420 std::string
pretty() const;
422 void updateHighWater();
424 void setInt(int64_t v
);
425 void setString(const StringData
* s
);
426 void setKnownCls(const StringData
* s
, bool nonNull
);
428 void setClsBaseType(ClassBaseType
);
429 void setUnnamedLocal(int index
, int localId
, Offset startOffset
);
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
);
439 size_t actualSize() const;
440 size_t fdescSize() const { return m_fdescCount
; }
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;
467 enum class NoEntryNopFlag
{};
468 Label() : m_off(InvalidAbsoluteOffset
) {}
469 explicit Label(Emitter
& e
) : m_off(InvalidAbsoluteOffset
) {
472 Label(Emitter
& e
, NoEntryNopFlag
) : m_off(InvalidAbsoluteOffset
) {
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
; }
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
;
497 virtual void emit(Emitter
& e
) = 0;
501 explicit Funclet(Thunklet
* body
)
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
);
520 // Manage state ID reuse.
522 EmitterVisitor
* m_visitor
;
523 // The target to jump to once all the necessary finally blocks are run.
525 // The state ID that identifies this control target inside finally
526 // epilogues. This ID assigned to the "state" unnamed local variable.
530 struct ControlTargetInfo
{
531 ControlTargetInfo() : used(false) {}
532 ControlTargetInfo(ControlTargetPtr t
, bool b
) : target(t
), used(b
) {}
533 ControlTargetPtr target
;
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.
547 // Top-level (global) context.
549 // Function body / method body entry.
551 // Entry for finally fault funclets emitted after the body of
554 // Region by a finally clause
556 // Finally block entry (begins after catches ends after finally)
558 // Loop or switch statement.
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();
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.
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
;
598 // Only used for loop / break kind of entries.
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
;
624 struct EmitterVisitor
{
625 friend struct UnsetUnnamedLocalThunklet
;
626 friend struct FuncFinisher
;
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
);
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
) {
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();
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
; }
685 assert(m_stateLocal
>= 0);
689 assert(m_retLocal
>= 0);
693 struct IncludeTimeFatalException
: Exception
{
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(); }
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
;
724 ClosureUseVarVec
* m_closureUseVars
;
727 struct PostponedCtor
{
728 PostponedCtor(InterfaceStatementPtr is
, FuncEmitter
* fe
)
729 : m_is(is
), m_fe(fe
) {}
730 InterfaceStatementPtr m_is
;
734 struct PostponedNonScalars
{
735 PostponedNonScalars(InterfaceStatementPtr is
, FuncEmitter
* fe
,
737 : m_is(is
), m_fe(fe
), m_vec(v
) {}
741 InterfaceStatementPtr m_is
;
746 struct PostponedClosureCtor
{
747 PostponedClosureCtor(ClosureUseVarVec
& v
, ClosureExpressionPtr e
,
749 : m_useVars(v
), m_expr(e
), m_fe(fe
) {}
750 ClosureUseVarVec m_useVars
;
751 ClosureExpressionPtr m_expr
;
756 CatchRegion(Offset start
, Offset end
) : m_start(start
),
759 for (std::vector
<std::pair
<StringData
*, Label
*> >::const_iterator it
=
760 m_catchLabels
.begin(); it
!= m_catchLabels
.end(); it
++) {
766 std::set
<StringData
*, string_data_lt
> m_names
;
767 std::vector
<std::pair
<StringData
*, Label
*> > m_catchLabels
;
771 FaultRegion(Offset start
,
780 , m_iterKind(kind
) {}
790 FPIRegion(Offset start
, Offset end
, Offset fpOff
)
791 : m_start(start
), m_end(end
), m_fpOff(fpOff
) {}
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
811 void allocPipeLocal(Id pipeVar
) { m_pipeVars
.emplace(pipeVar
); }
812 void releasePipeLocal(Id pipeVar
) {
813 assert(!m_pipeVars
.empty() && m_pipeVars
.top() == pipeVar
);
816 folly::Optional
<Id
> getPipeLocal() {
817 if (m_pipeVars
.empty()) return folly::none
;
818 return m_pipeVars
.top();
822 static constexpr size_t kMinStringSwitchCases
= 8;
825 FuncEmitter
* m_curFunc
;
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
863 // stack of nested unnamed pipe variables
864 std::stack
<Id
> m_pipeVars
;
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
878 explicit MInstrOpts(MOpFlags flags
)
879 : allowW
{flags
& MOpFlags::Define
}
883 explicit MInstrOpts(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
{
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
,
979 void bindNativeFunc(MethodStatementPtr meth
, FuncEmitter
*fe
);
980 int32_t emitNativeOpCodeImpl(MethodStatementPtr meth
,
981 const char* funcName
,
982 const char* className
,
984 void emitMethodMetadata(MethodStatementPtr meth
,
985 ClosureUseVarVec
* useVars
,
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
,
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
,
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
,
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
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
,
1092 Funclet
* getFunclet(StatementPtr stmt
);
1093 void emitFunclets(Emitter
& e
);
1095 struct FaultIterInfo
{
1100 void newFaultRegion(Offset start
,
1103 FaultIterInfo
= FaultIterInfo
{ -1, KindOfIter
});
1104 void newFaultRegion(StatementPtr stmt
,
1108 FaultIterInfo
= FaultIterInfo
{ -1, KindOfIter
});
1110 newFaultRegionAndFunclet(Offset start
,
1113 FaultIterInfo
= FaultIterInfo
{ -1, KindOfIter
});
1115 newFaultRegionAndFunclet(StatementPtr stmt
,
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
,
1155 ControlTargetPtr
registerContinue(StatementPtr s
, Region
* entry
, int depth
,
1157 ControlTargetPtr
registerGoto(StatementPtr s
, Region
* entry
,
1158 StringData
* name
, bool alloc
);
1161 //=============================================================================
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()); \
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
;
1190 m_ev
->finishFunc(m_e
, m_fe
, m_stackPad
);
1193 void setStackPad(int32_t stackPad
) {
1194 m_stackPad
= stackPad
;
1197 EmitterVisitor
* m_ev
;
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
);
1210 m_e
.setTempLocation(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; \
1231 const int nIn UNUSED = COUNT_##pop; \
1234 getUnitEmitter().emitOp(Op##name); \
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); \
1245 getEmitterVisitor().restoreJumpTargetEvalStack(); \
1246 ITRACE(3, " jmp: {}\n", m_ev.getEvalStack().pretty()); \
1248 if (opcode == Op::FCall) getEmitterVisitor().recordCall(); \
1249 getEmitterVisitor().setPrevOpcode(opcode); \
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
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
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
1293 #define POP_ONE(t) \
1295 #define POP_TWO(t1, t2) \
1298 #define POP_THREE(t1, t2, t3) \
1302 #define POP_FOUR(t1, t2, t3, t4) \
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()
1317 getEmitterVisitor().popEvalStackMany(a1, StackSym::F)
1318 #define POP_CVUMANY \
1319 getEmitterVisitor().popEvalStackCVMany(a1)
1321 getEmitterVisitor().popEvalStackMany(a1, StackSym::C)
1323 getEmitterVisitor().popEvalStackMany(a1.size(), StackSym::C)
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) \
1338 #define POP_LA_TWO(t1, t2) \
1341 #define POP_LA_THREE(t1, t2, t3) \
1345 #define POP_LA_FOUR(t1, t2, t3, t4) \
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)
1372 #define PUSH_ONE(t) \
1374 #define PUSH_TWO(t1, t2) \
1377 #define PUSH_THREE(t1, t2, t3) \
1381 #define PUSH_FOUR(t1, t2, t3, t4) \
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);
1409 #define IMPL_ONE(t) \
1411 #define IMPL_TWO(t1, t2) \
1414 #define IMPL_THREE(t1, t2, t3) \
1418 #define IMPL_FOUR(t1, t2, t3, t4) \
1424 #define IMPL_BLA(var) do { \
1425 getUnitEmitter().emitInt32(var.size()); \
1426 for (unsigned int i = 0; i < var.size(); ++i) { \
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); \
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); \
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()); \
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); \
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()); \
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)
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: {}",
1706 Logger::Warning("src stack : %s", source
.pretty().c_str());
1707 Logger::Warning("dest stack: %s", dest
.pretty().c_str());
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
));
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());
1730 std::string
SymbolicStack::SymEntry::pretty() const {
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
);
1742 std::string
SymbolicStack::pretty() const {
1743 std::ostringstream out
;
1744 out
<< "[" << std::hex
;
1747 for (size_t i
= 0; i
< m_symStack
.size(); ++i
) {
1750 while (j
< m_actualStack
.size() && m_actualStack
[j
] < int(i
)) {
1753 if (j
< m_actualStack
.size() && m_actualStack
[j
] == int(i
)) {
1756 out
<< m_symStack
[i
].pretty();
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());
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
);
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
);
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
;
1842 void SymbolicStack::setClsBaseType(ClassBaseType type
) {
1843 assert(!m_symStack
.empty());
1844 m_symStack
.back().clsBaseType
= type
;
1847 void SymbolicStack::setUnnamedLocal(int index
,
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() {
1879 m_actualStack
.clear();
1883 void SymbolicStack::consumeBelowTop(int depth
) {
1884 if (int(m_symStack
.size()) < depth
+ 1) {
1886 "Emitter tried to consumeBelowTop() when the symbolic "
1887 "stack did not have enough elements in it.");
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
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
) {
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
) {
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
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
) {
1974 int SymbolicStack::sizeActual() const {
1975 return m_actualStack
.size();
1978 void SymbolicStack::pushFDesc() {
1979 m_fdescCount
+= kNumActRecCells
;
1983 void SymbolicStack::popFDesc() {
1984 m_fdescCount
-= kNumActRecCells
;
1987 void Label::set(Emitter
& e
, bool emitNopAtEntry
/* = true */) {
1988 auto const off
= e
.getUnitEmitter().bcPos();
1991 "Label::set was called more than once on the same "
1992 "Label; originally set to %d; now %d",
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());
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());
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 "
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());
2046 m_evalStack
= ev
.getEvalStack();
2050 struct FPIRegionRecorder
{
2051 FPIRegionRecorder(EmitterVisitor
* ev
, UnitEmitter
& ue
, SymbolicStack
& stack
,
2053 : m_ev(ev
), m_ue(ue
), m_stack(stack
), m_fpOff(m_stack
.sizeActual()),
2055 m_stack
.pushFDesc();
2057 ~FPIRegionRecorder() {
2059 m_ev
->newFPIRegion(m_start
, m_ue
.bcPos(), m_fpOff
);
2062 EmitterVisitor
* m_ev
;
2064 SymbolicStack
& m_stack
;
2069 //=============================================================================
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 //=============================================================================
2096 Region::Region(Region::Kind kind
, RegionPtr parent
)
2099 m_iterKind(KindOfIter
),
2104 EmitterVisitor::registerReturn(StatementPtr s
, Region
* region
, char sym
) {
2107 for (r
= region
; true; r
= r
->m_parent
.get()) {
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
;
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();
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.
2134 for (r
= region
; r
!= end
; r
= r
->m_parent
.get()) {
2135 r
->m_returnTargets
[sym
] = ControlTargetInfo(t
, r
->isTryFinally());
2140 EmitterVisitor::registerGoto(StatementPtr s
, Region
* region
, StringData
* name
,
2144 for (r
= region
; true; r
= r
->m_parent
.get()) {
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;
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();
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.
2170 for (r
= region
; r
!= end
; r
= r
->m_parent
.get()) {
2171 r
->m_gotoTargets
[name
] = ControlTargetInfo(t
, alloc
&& r
->isTryFinally());
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");
2187 EmitterVisitor::registerBreak(StatementPtr s
, Region
* region
, int depth
,
2193 for (r
= region
; true; r
= r
->m_parent
.get()) {
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;
2207 if (r
->m_kind
!= Region::Kind::LoopOrSwitch
) {
2211 // We should never reach this case if alloc == true, since the loop or
2212 // switch should have registered its break target in advance
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();
2220 // Otherwise, delegate to the parent. One break level has been
2221 // taken care of by this region.
2224 assert(t
!= nullptr);
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.
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
) {
2245 EmitterVisitor::registerContinue(StatementPtr s
, Region
* region
, int depth
,
2251 for (r
= region
; true; r
= r
->m_parent
.get()) {
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;
2265 if (r
->m_kind
!= Region::Kind::LoopOrSwitch
) {
2269 // We should never reach this case if alloc == true, since the loop or
2270 // switch should have registered its continue target in advance
2272 t
= std::make_shared
<ControlTarget
>(this);
2273 r
= r
->m_parent
.get();
2276 // Otherwise, delegate to the parent. One continue level has been
2277 // taken care of by this region.
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.
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
) {
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
;
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
);
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
);
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
));
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()) {
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
);
2356 assert(sym
== StackSym::V
);
2357 if (shouldEmitVerifyRetType()) {
2360 emitIterFree(e
, iters
);
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());
2377 // Emit code stashing the current return value in the "ret" unnamed
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
2386 popEvalStack(StackSym::C
);
2387 emitVirtualLocal(retLocal
);
2388 pushEvalStack(StackSym::C
);
2392 assert(sym
== StackSym::V
);
2393 popEvalStack(StackSym::V
);
2394 emitVirtualLocal(retLocal
);
2395 pushEvalStack(StackSym::V
);
2399 emitJump(e
, iters
, r
->m_finallyLabel
);
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
));
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
);
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());
2435 // Jump to the finally block and free any pending iterators on the
2437 emitJump(e
, iters
, r
->m_finallyLabel
);
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);
2450 assert(!region
->isFinally());
2451 assert(region
->m_parent
!= nullptr);
2452 assert(region
->m_breakTargets
.count(depth
));
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
);
2466 emitJump(e
, iters
, r
->m_finallyLabel
);
2469 if (r
->m_kind
!= Region::Kind::LoopOrSwitch
) {
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
));
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
);
2487 void EmitterVisitor::emitContinue(Emitter
& e
, int depth
, StatementPtr s
) {
2488 Region
* region
= m_regions
.back().get();
2489 registerContinue(s
, region
, depth
, true);
2491 assert(!region
->isFinally());
2492 assert(region
->m_parent
!= nullptr);
2493 assert(region
->m_continueTargets
.count(depth
));
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());
2507 emitJump(e
, iters
, r
->m_finallyLabel
);
2510 if (r
->m_kind
!= Region::Kind::LoopOrSwitch
) {
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
);
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
));
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();
2536 // If there is only one case (the fall-through case) then we're done
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
);
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
);
2583 void EmitterVisitor::emitReturnTrampoline(Emitter
& e
,
2585 std::vector
<Label
*>& cases
,
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
);
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
) {
2610 if (shouldEmitVerifyRetType()) {
2615 assert(sym
== StackSym::V
);
2617 if (shouldEmitVerifyRetType()) {
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
);
2633 void EmitterVisitor::emitGotoTrampoline(Emitter
& e
,
2635 std::vector
<Label
*>& cases
,
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);
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
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
);
2657 if (region
->isTryFinally()) {
2658 // Encountered a finally block, jump and free any pending iterators
2659 emitJump(e
, iters
, region
->m_finallyLabel
);
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
,
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);
2688 for (region
= region
->m_parent
.get(); true; region
= region
->m_parent
.get()) {
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
);
2700 if (region
->m_kind
!= Region::Kind::LoopOrSwitch
) {
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
));
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
2716 emitJump(e
, iters
, t
->m_label
);
2719 // Otherwise just delegate to the parent. One loop level has been
2725 void EmitterVisitor::emitContinueTrampoline(Emitter
& e
, Region
* region
,
2726 std::vector
<Label
*>& cases
,
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);
2735 for (region
= region
->m_parent
.get(); true; region
= region
->m_parent
.get()) {
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
);
2744 if (region
->m_kind
!= Region::Kind::LoopOrSwitch
) {
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
);
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
));
2767 bool EmitterVisitor::shouldEmitVerifyRetType() {
2768 return (m_curFunc
->retTypeConstraint
.hasConstraint() &&
2769 !m_curFunc
->isGenerator
);
2772 int Region::getMaxBreakContinueDepth() {
2773 if (m_parent
== nullptr || isFinally()) {
2775 } else if (m_kind
== Region::Kind::LoopOrSwitch
) {
2776 return m_parent
->getMaxBreakContinueDepth() + 1;
2778 return m_parent
->getMaxBreakContinueDepth();
2782 int Region::getBreakContinueDepth() {
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
);
2793 int Region::getMaxState() {
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
);
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
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
);
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());
2853 while (m_states
.count(state
)) {
2856 m_states
.insert(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 //=============================================================================
2871 EmitterVisitor::EmittedClosures
EmitterVisitor::s_emittedClosures
;
2873 EmitterVisitor::EmitterVisitor(UnitEmitter
& ue
)
2875 m_curFunc(ue
.getMain()),
2876 m_evalStackIsUnknown(false),
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
) {
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)",
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(),
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(),
2922 char sym
= m_evalStack
.top();
2923 char actual
= StackSym::GetSymFlavor(sym
);
2925 if (actual
!= expected
) {
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\" "
2932 StackSym::ToString(expected
).c_str(),
2933 StackSym::ToString(actual
).c_str(),
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
)) {
2946 if (op
== OpCGetL3
) {
2948 } else if (op
== OpCGetL2
) {
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);
2962 popEvalStack(StackSym::L
);
2966 void EmitterVisitor::popEvalStackMMany() {
2967 ITRACE(3, "popEvalStackMMany()\n");
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
) {
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(),
2988 } else if (marker
== StackSym::W
) {
2989 if (symFlavor
!= StackSym::None
) {
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(),
2999 } else if (marker
== StackSym::M
) {
3000 assert(symFlavor
== StackSym::A
);
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 "
3015 ITRACE(3, "popping location\n");
3016 char sym
= m_evalStack
.top();
3017 char symFlavor
= StackSym::GetSymFlavor(sym
);
3019 if (symFlavor
!= StackSym::C
&& symFlavor
!= StackSym::L
&&
3020 symFlavor
!= StackSym::R
&& symFlavor
!= StackSym::H
) {
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(),
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)",
3048 char sym
= m_evalStack
.top();
3049 char actual
= StackSym::GetSymFlavor(sym
);
3051 if (actual
!= StackSym::C
&& actual
!= StackSym::V
) {
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\" "
3058 StackSym::ToString(actual
).c_str(),
3064 void EmitterVisitor::pushEvalStack(char symFlavor
) {
3065 // Push a value from the evaluation stack with the specified
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
) {
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 "
3082 StackSym::ToString(expected
).c_str(),
3083 StackSym::ToString(actual
).c_str(),
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.");
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
) {
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
;
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;
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
{
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
);
3192 struct UnsetUnnamedLocalThunklet final
: Thunklet
{
3193 explicit UnsetUnnamedLocalThunklet(Id loc
)
3195 void emit(Emitter
& e
) override
{
3196 e
.getEmitterVisitor().emitVirtualLocal(m_loc
);
3197 e
.getEmitterVisitor().emitUnset(e
);
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
);
3216 const std::vector
<Id
> m_locs
;
3219 struct UnsetGeneratorDelegateThunklet final
: Thunklet
{
3220 explicit UnsetGeneratorDelegateThunklet(Id iterId
)
3222 void emit(Emitter
& e
) override
{
3223 e
.ContUnsetDelegate(true, m_id
);
3230 struct FinallyThunklet final
: Thunklet
{
3231 explicit FinallyThunklet(FinallyStatementPtr finallyStatement
,
3233 : m_finallyStatement(finallyStatement
), m_numLiveIters(numLiveIters
) {}
3234 void emit(Emitter
& e
) override
{
3235 auto& visitor
= e
.getEmitterVisitor();
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
);
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
);
3254 FinallyStatementPtr m_finallyStatement
;
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
) {
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();
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
) {
3308 emitClsIfSPropBase(e
);
3313 void EmitterVisitor::listAssignmentAssignElements(
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();
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()) {
3339 if (emitSrc
== nullptr) {
3343 for (int j
= 0; j
< (int)currIndexChain
.size(); ++j
) {
3344 m_evalStack
.push(StackSym::I
);
3345 m_evalStack
.setInt(currIndexChain
[j
]);
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
3364 void EmitterVisitor::visitIfCondition(
3365 ExpressionPtr cond
, Emitter
& e
, Label
& tru
, Label
& fals
,
3366 bool truFallthrough
) {
3368 auto binOpNode
= dynamic_pointer_cast
<BinaryOpExpression
>(cond
);
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
);
3377 ExpressionPtr lhs
= binOpNode
->getExp1();
3379 Emitter
lhsEmitter(lhs
, m_ue
, *this);
3380 visitIfCondition(lhs
, lhsEmitter
, tru
, localLabel
, false);
3381 // falls through if that condition was false
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()) {
3390 if (currentPositionIsReachable()) {
3391 ExpressionPtr rhs
= binOpNode
->getExp2();
3392 Emitter
rhsEmitter(rhs
, m_ue
, *this);
3393 visitIfCondition(rhs
, rhsEmitter
, tru
, fals
, truFallthrough
);
3399 auto unOpNode
= dynamic_pointer_cast
<UnaryOpExpression
>(cond
);
3401 int op
= unOpNode
->getOp();
3404 ExpressionPtr val
= unOpNode
->getExpression();
3405 Emitter
valEmitter(val
, m_ue
, *this);
3406 visitIfCondition(val
, valEmitter
, fals
, tru
, !truFallthrough
);
3412 if (cond
->getScalarValue(val
)) {
3413 if (truFallthrough
) {
3414 if (!val
.toBoolean()) e
.Jmp(fals
);
3416 if (val
.toBoolean()) e
.Jmp(tru
);
3422 emitConvertToCell(e
);
3423 if (truFallthrough
) {
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());
3459 SCOPE_ASSERT_DETAIL("visit FileScope") { return m_evalStack
.pretty(); };
3460 ITRACE(1, "Emitting file {}\n", file
->getName());
3461 Trace::Indent indent
;
3464 assignLocalVariableIds(func
);
3466 AnalysisResultPtr
ar(file
->getContainingProgram());
3468 auto m
= dynamic_pointer_cast
<MethodStatement
>(func
->getStmt());
3470 StatementListPtr
stmts(m
->getStmts());
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
)) {
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
:
3516 case Statement::KindOfInterfaceStatement
:
3517 case Statement::KindOfClassStatement
: {
3518 // Handle classes directly here, since only top-level classes are
3520 ClassScopePtr cNode
= s
->getClassScope();
3521 emitClass(e
, cNode
, true);
3522 if (cNode
->getFatalMessage()) {
3523 notMergeOnly
= true;
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 "
3536 if (!RuntimeOption::PHP7_ScalarTypes
) {
3537 emitMakeUnitFatal(e
, "strict_types can only be used when "
3538 "hhvm.php7.scalar_types = true");
3544 visit(ds
->getBlock());
3547 case Statement::KindOfTypedefStatement
: {
3549 emitTypedef(e
, static_pointer_cast
<TypedefStatement
>(s
));
3550 m_ue
.pushMergeableTypeAlias(Unit::MergeKind::TypeAlias
, id
);
3553 case Statement::KindOfReturnStatement
:
3554 if (mainReturn
.m_type
!= kInvalidDataType
) break;
3558 tvWriteUninit(&mainReturn
);
3559 m_ue
.m_returnSeen
= true;
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;
3573 v
= String(makeStaticString(v
.asCStrRef().get()));
3574 } else if (v
.isArray()) {
3575 v
= Array(ArrayData::GetScalarArray(v
.asCArrRef().get()));
3577 assert(v
.isInitialized());
3578 assert(!isRefcountedType(v
.getType()));
3580 mainReturn
= *v
.asCell();
3581 m_ue
.m_returnSeen
= true;
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
);
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
);
3605 case Expression::KindOfAssignmentExpression
: {
3606 auto ae
= static_pointer_cast
<AssignmentExpression
>(e
);
3609 if (ae
->isSimpleGlobalAssign(&name
, &tv
)) {
3610 m_ue
.pushMergeableDef(Unit::MergeKind::Global
, name
, tv
);
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()));
3639 if (mainReturn
.m_type
!= kInvalidDataType
) break;
3640 notMergeOnly
= true;
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();
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
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
)) {
3671 e
.setTempLocation(OptLocation());
3675 if (!m_evalStack
.empty()) {
3676 InvariantViolation("Eval stack was not empty as expected before "
3677 "emitPostponed* phase");
3681 emitPostponedMeths();
3682 emitPostponedCtors();
3683 emitPostponedPinits();
3684 emitPostponedSinits();
3685 emitPostponedCinits();
3688 static StringData
* getClassName(ExpressionPtr e
) {
3689 ClassScopeRawPtr cls
;
3691 cls
= e
->getClassScope();
3693 if (cls
&& !cls
->isTrait()) {
3694 return makeStaticString(cls
->getScopeName());
3699 void EmitterVisitor::fixReturnType(Emitter
& e
, FunctionCallPtr fn
,
3700 Func
* builtinFunc
) {
3702 if (fn
->hasAnyContext(Expression::RefValue
|
3703 Expression::DeepReference
|
3704 Expression::LValue
|
3705 Expression::OprLValue
|
3706 Expression::UnsetContext
)) {
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() &&
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
3725 assert(m_evalStack
.get(m_evalStack
.size() - 1) == StackSym::R
);
3734 void EmitterVisitor::visitKids(ConstructPtr c
) {
3735 for (int i
= 0, nk
= c
->getKidCount(); i
< nk
; i
++) {
3736 ConstructPtr
kid(c
->getNthKid(i
));
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
) {
3747 auto el
= static_pointer_cast
<ExpressionList
>(init_expr
);
3748 int n
= el
->getCount();
3749 if (n
< 1 || (check_size
&& n
> MaxMakeSize
)) {
3753 for (int i
= 0, n
= el
->getCount(); i
< n
; ++i
) {
3754 ExpressionPtr ex
= (*el
)[i
];
3755 if (ex
->getKindOf() != Expression::KindOfArrayPairExpression
) {
3758 auto ap
= static_pointer_cast
<ArrayPairExpression
>(ex
);
3759 if (ap
->isRef()) return false;
3760 if (!fun(ap
)) return false;
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) {
3776 return checkKeys
<MixedArray::MaxMakeSize
>(init_expr
, check_size
,
3777 [&](ArrayPairExpressionPtr ap
) {
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;
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;
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();
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
);
3828 void EmitterVisitor::emitCall(Emitter
& e
,
3829 FunctionCallPtr func
,
3830 ExpressionListPtr params
,
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());
3847 e
.FCallUnpack(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
:
3866 case Construct::KindOfTypedefStatement
: {
3867 emitMakeUnitFatal(e
, "Type statements are currently only allowed at "
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 "
3881 visit(ds
->getBlock());
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) {
3897 emitMakeUnitFatal(e
, msg
.str().c_str());
3901 if (bs
->is(Construct::KindOfBreakStatement
)) {
3902 emitBreak(e
, destLevel
, bs
);
3904 emitContinue(e
, destLevel
, bs
);
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
);
3917 registerContinue(ds
, region
.get(), 1, false)->m_label
;
3919 registerBreak(ds
, region
.get(), 1, false)->m_label
;
3921 enterRegion(region
);
3922 SCOPE_EXIT
{ leaveRegion(region
); };
3923 visit(ds
->getBody());
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
);
3936 case Construct::KindOfCaseStatement
: {
3937 // Should never be called. Handled in visitSwitch.
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
);
3950 visit(cs
->getStmt());
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
++) {
3960 emitConvertToCell(e
);
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()) {
3980 popEvalStack(StackSym::C
);
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())) {
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
);
4004 emit_cond(top
, true);
4007 enterRegion(region
);
4008 SCOPE_EXIT
{ leaveRegion(region
); };
4009 visit(fs
->getBody());
4012 if (visit(fs
->getIncExp())) {
4018 emit_cond(top
, false);
4020 if (fail
.isUsed()) fail
.set(e
);
4024 case Construct::KindOfForEachStatement
: {
4025 auto fe
= static_pointer_cast
<ForEachStatement
>(node
);
4026 if (fe
->isAwaitAs()) {
4027 emitForeachAwaitAs(e
, fe
);
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()) {
4043 StringData
* nLiteral
= makeStaticString(sv
->getName());
4044 Id i
= m_curFunc
->lookupVarId(nLiteral
);
4045 emitVirtualLocal(i
);
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
4071 case Construct::KindOfIfStatement
: {
4072 auto ifp
= static_pointer_cast
<IfStatement
>(node
);
4073 StatementListPtr
branches(ifp
->getIfBranches());
4074 int nb
= branches
->getCount();
4076 for (int i
= 0; i
< nb
; i
++) {
4077 auto branch
= static_pointer_cast
<IfBranchStatement
>((*branches
)[i
]);
4079 if (branch
->getCondition()) {
4081 Emitter
condEmitter(branch
->getCondition(), m_ue
, *this);
4082 visitIfCondition(branch
->getCondition(), condEmitter
,
4088 visit(branch
->getStmt());
4089 if (currentPositionIsReachable() && i
+ 1 < nb
) {
4092 if (fals
.isUsed()) {
4096 if (done
.isUsed()) {
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
;
4116 emitConvertToCell(e
);
4121 assert(m_evalStack
.size() == 1);
4122 assert(IMPLIES(m_curFunc
->isAsync
|| m_curFunc
->isGenerator
,
4123 retSym
== StackSym::C
));
4124 emitReturn(e
, retSym
, r
);
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
;
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
);
4155 emitConvertToCell(e
);
4156 e
.StaticLocInit(local
, name
);
4159 emitVirtualLocal(local
);
4160 e
.StaticLoc(local
, name
);
4163 emitVirtualLocal(local
);
4165 emitConvertToCell(e
);
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();
4182 visit(sw
->getExp());
4186 uint32_t ncase
= cases
->getCount();
4187 std::vector
<Label
> caseLabels(ncase
);
4188 Label
& brkTarget
= registerBreak(sw
, region
.get(), 1, false)->m_label
;
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();
4204 Offset start
= InvalidAbsoluteOffset
;
4206 bool enabled
= RuntimeOption::EvalEmitSwitch
;
4207 auto call
= dynamic_pointer_cast
<SimpleFunctionCall
>(subject
);
4210 bool didSwitch
= false;
4212 MaybeDataType stype
= analyzeSwitch(sw
, state
);
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());
4223 } else if (stype
== KindOfInt64
) {
4224 emitIntegerSwitch(e
, sw
, caseLabels
, brkTarget
, state
);
4226 assert(isStringType(*stype
));
4227 emitStringSwitch(e
, sw
, caseLabels
, brkTarget
, state
);
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
);
4239 emitConvertToCell(e
);
4242 start
= m_ue
.bcPos();
4246 for (uint32_t i
= 0; i
< ncase
; i
++) {
4247 auto c
= static_pointer_cast
<CaseStatement
>((*cases
)[i
]);
4248 ExpressionPtr condition
= c
->getCondition();
4250 if (simpleSubject
) {
4251 // Evaluate the subject every time.
4253 emitConvertToCellOrLoc(e
);
4255 emitConvertToCell(e
);
4256 emitConvertSecondToCell(e
);
4258 emitVirtualLocal(tempLocal
);
4261 emitConvertToCell(e
);
4264 e
.JmpNZ(caseLabels
[i
]);
4265 } else if (LIKELY(defI
== -1)) {
4269 throw IncludeTimeFatalException(
4270 c
, "Switch statements may only contain one default: clause");
4274 e
.Jmp(caseLabels
[defI
]);
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
);
4296 m_curFunc
->freeUnnamedLocal(tempLocal
);
4301 case Construct::KindOfThrowStatement
: {
4303 emitConvertToCell(e
);
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());
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()) {
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();
4337 enterRegion(region
);
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
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",
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,
4376 if (!firstHandler
) {
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
,
4392 Offset end_catches
= m_ue
.bcPos();
4393 if (after
.isUsed()) after
.set(e
);
4396 region
->m_finallyLabel
.set(e
);
4398 emitFinallyEpilogue(e
, region
.get());
4399 auto func
= getFunclet(f
);
4400 if (func
== nullptr) {
4402 new FinallyThunklet(f
, m_curFunc
->numLiveIterators());
4403 func
= addFunclet(f
, thunklet
);
4405 newFaultRegion(start
, end_catches
, &func
->m_entry
);
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
]);
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,
4426 Label
& fail
= registerBreak(ws
, region
.get(), 1, false)->m_label
;
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);
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
);
4445 case Construct::KindOfInterfaceStatement
:
4446 case Construct::KindOfClassStatement
: {
4447 emitClass(e
, node
->getClassScope(), false);
4451 case Construct::KindOfClassVariable
:
4452 case Construct::KindOfClassConstant
:
4453 case Construct::KindOfMethodStatement
:
4454 // handled by emitClass
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);
4468 case Construct::KindOfGotoStatement
: {
4469 auto g
= static_pointer_cast
<GotoStatement
>(node
);
4470 StringData
* nName
= makeStaticString(g
->label());
4471 emitGoto(e
, nName
, g
);
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)
4482 case Construct::KindOfStatement
:
4483 case Construct::KindOfUseTraitStatement
:
4484 case Construct::KindOfClassRequireStatement
:
4485 case Construct::KindOfTraitPrecStatement
:
4486 case Construct::KindOfTraitAliasStatement
: {
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
]);
4507 emitVisitAndUnset(e
, exp
);
4512 if (op
== T_ARRAY
) {
4513 auto el
= static_pointer_cast
<ExpressionList
>(u
->getExpression());
4514 emitArrayInit(e
, el
);
4519 auto el
= static_pointer_cast
<ExpressionList
>(u
->getExpression());
4520 emitArrayInit(e
, el
, HeaderKind::Dict
);
4525 auto el
= static_pointer_cast
<ExpressionList
>(u
->getExpression());
4526 emitArrayInit(e
, el
, HeaderKind::VecArray
);
4530 if (op
== T_KEYSET
) {
4531 auto el
= static_pointer_cast
<ExpressionList
>(u
->getExpression());
4532 emitArrayInit(e
, el
, HeaderKind::Keyset
);
4536 if (op
== T_ISSET
) {
4537 auto list
= dynamic_pointer_cast
<ExpressionList
>(u
->getExpression());
4539 // isset($a, $b, ...) ==> isset($a) && isset($b) && ...
4541 int n
= list
->getCount();
4542 for (int i
= 0; i
< n
- 1; ++i
) {
4549 // Treat the last one specially; let it fall through
4550 visit((*list
)[n
- 1]);
4555 visit(u
->getExpression());
4559 } else if (op
== '+' || op
== '-') {
4563 Id oldErrorLevelLoc
= -1;
4564 Offset start
= InvalidAbsoluteOffset
;
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)
4582 // __FILE__ and __DIR__ are special unary ops that don't
4584 assert(op
== T_FILE
|| op
== T_DIR
);
4589 // $this++ is a no-op
4590 if (auto var
= dynamic_pointer_cast
<SimpleVariable
>(exp
)) {
4591 if (var
->isThis()) break;
4594 auto const cop
= [&] {
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
;
4609 case T_EMPTY
: emitEmpty(e
); break;
4610 case T_CLONE
: e
.Clone(); break;
4612 RuntimeOption::IntsOverflowToInts
? e
.Add() : e
.AddO();
4615 RuntimeOption::IntsOverflowToInts
? e
.Sub() : e
.SubO();
4617 case '!': e
.Not(); break;
4618 case '~': e
.BitNot(); 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;
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
);
4637 case T_PRINT
: e
.Print(); break;
4638 case T_EVAL
: e
.Eval(); break;
4653 case Construct::KindOfAssignmentExpression
: {
4654 auto ae
= static_pointer_cast
<AssignmentExpression
>(node
);
4655 ExpressionPtr rhs
= ae
->getValue();
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
);
4674 if (rhs
->hasContext(Expression::RefValue
)) {
4675 emitConvertToVar(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
);
4687 emitConvertToCell(e
);
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
);
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()) {
4713 if (tru
.isUsed()) tru
.set(e
);
4714 if (currentPositionIsReachable()) {
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();
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
);
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
);
4753 } else if (isParent
) {
4762 s
= cls
->getScopeName();
4763 } else if (isParent
) {
4764 s
= cls
->getOriginalParent();
4768 StringData
* nLiteral
= makeStaticString(s
);
4769 e
.InstanceOfD(nLiteral
);
4772 visit(b
->getExp2());
4773 emitConvertToCell(e
);
4777 visit(b
->getExp2());
4778 emitConvertToCell(e
);
4784 if (op
== T_COLLECTION
) {
4785 emitCollectionInit(e
, b
);
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());
4800 visit(b
->getExp1());
4801 emitConvertToCellOrLoc(e
);
4802 visit(b
->getExp2());
4803 emitConvertToCell(e
);
4804 emitConvertSecondToCell(e
);
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;
4812 RuntimeOption::IntsOverflowToInts
? e
.Add() : e
.AddO();
4815 RuntimeOption::IntsOverflowToInts
? e
.Sub() : e
.SubO();
4818 RuntimeOption::IntsOverflowToInts
? e
.Mul() : e
.MulO();
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);
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
4850 auto const emitClsCns
= [&] {
4851 if (cc
->isColonColonClass()) {
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()) {
4869 } else if (cc
->getClass()) {
4871 ExpressionPtr
cls(cc
->getClass());
4875 } else if (cc
->getOriginalClassScope() &&
4876 !cc
->getOriginalClassScope()->isTrait()) {
4877 // C::Constant inside a class
4878 auto nCls
= getOriginalClassName();
4879 if (cc
->isColonColonClass()) {
4882 e
.ClsCnsD(nName
, nCls
);
4884 } else if (cc
->isSelf()) {
4885 // self::Constant inside trait or pseudomain
4887 if (cc
->isColonColonClass() &&
4888 cc
->getFunctionScope()->inPseudoMain()) {
4892 } else if (cc
->isParent()) {
4893 // parent::Constant inside trait or pseudomain
4895 if (cc
->isColonColonClass() &&
4896 cc
->getFunctionScope()->inPseudoMain()) {
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
);
4914 case Construct::KindOfConstantExpression
: {
4915 auto c
= static_pointer_cast
<ConstantExpression
>(node
);
4918 } else if (c
->isBoolean()) {
4919 if (c
->getBooleanValue()) {
4926 std::string nameStr
= c
->getOriginalName();
4927 StringData
* nName
= makeStaticString(nameStr
);
4928 if (c
->hadBackslash()) {
4931 const std::string
& nonNSName
= c
->getNonNSOriginalName();
4932 if (nonNSName
!= nameStr
) {
4933 StringData
* nsName
= nName
;
4934 nName
= makeStaticString(nonNSName
);
4935 e
.CnsU(nsName
, nName
);
4937 e
.Cns(makeStaticString(c
->getName()));
4944 case Construct::KindOfEncapsListExpression
: {
4945 auto el
= static_pointer_cast
<EncapsListExpression
>(node
);
4946 auto args
= el
->getExpressions();
4947 int n
= args
? args
->getCount() : 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
);
4959 visit((*args
)[i
++]);
4960 emitConvertToCellOrLoc(e
);
4962 emitConvertToCell(e
);
4966 visit((*args
)[i
++]);
4967 emitConvertToCell(e
);
4968 emitConvertSecondToCell(e
);
4973 e
.String(staticEmptyString());
4976 if (el
->getType() == '`') {
4977 emitConvertToCell(e
);
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();
4996 if (!ae
->isSuperGlobal() && offset
&&
4997 offset
->getScalarValue(v
) && (v
.isInteger() || v
.isString())) {
4999 m_evalStack
.push(StackSym::T
);
5000 m_evalStack
.setString(
5001 makeStaticString(v
.toCStrRef().get()));
5003 m_evalStack
.push(StackSym::I
);
5004 m_evalStack
.setInt(v
.asInt64Val());
5007 } else if (visit(offset
)) {
5008 emitConvertToCellOrLoc(e
);
5009 if (ae
->isSuperGlobal()) {
5017 if (!ae
->hasAnyContext(Expression::AccessContext
|
5018 Expression::ObjectContext
)) {
5019 m_tempLoc
= ae
->getRange();
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];
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());
5041 } else if (emitCallUserFunc(e
, call
)) {
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();
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) {
5067 visit((*params
)[2]);
5068 emitConvertToCell(e
);
5070 call
->changeToBytecode();
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();
5086 } else if (call
->isCallToFunction("max")) {
5087 if (params
&& params
->getCount() == 2) {
5088 emitFuncCall(e
, call
, "__SystemLib\\max2", params
);
5091 } else if (call
->isCallToFunction("min")) {
5092 if (params
&& params
->getCount() == 2) {
5093 emitFuncCall(e
, call
, "__SystemLib\\min2", params
);
5096 } else if (call
->isCallToFunction("define")) {
5097 if (params
&& params
->getCount() == 2) {
5098 ExpressionPtr p0
= (*params
)[0];
5100 if (p0
->getScalarValue(v0
) && v0
.isString()) {
5101 const StringData
* cname
=
5102 makeStaticString(v0
.toString());
5103 visit((*params
)[1]);
5104 emitConvertToCell(e
);
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
) {
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
5126 Label disabled
, after
;
5127 e
.String(s_zend_assertions
.get());
5128 e
.FCallBuiltin(1, 1, s_ini_get
.get());
5134 emitFuncCall(e
, call
, "__SystemLib\\assert", call
->getParams());
5135 emitConvertToCell(e
);
5143 } else if (emitSystemLibVarEnvFunc(e
, call
)) {
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];
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
);
5164 } else if ((call
->isCallToFunction("class_exists") ||
5165 call
->isCallToFunction("interface_exists") ||
5166 call
->isCallToFunction("trait_exists"))
5168 && (params
->getCount() == 1 || params
->getCount() == 2)) {
5170 emitNameString(e
, (*params
)[0]);
5171 emitConvertToCell(e
);
5174 // Push autoload, defaulting to true
5175 if (params
->getCount() == 1) {
5178 visit((*params
)[1]);
5179 emitConvertToCell(e
);
5182 if (call
->isCallToFunction("class_exists")) {
5183 e
.OODeclExists(OODeclExistsOp::Class
);
5184 } else if (call
->isCallToFunction("interface_exists")) {
5185 e
.OODeclExists(OODeclExistsOp::Interface
);
5187 assert(call
->isCallToFunction("trait_exists"));
5188 e
.OODeclExists(OODeclExistsOp::Trait
);
5191 } else if (call
->isCallToFunction("get_class") &&
5193 call
->getClassScope() &&
5194 !call
->getClassScope()->isTrait()) {
5196 makeStaticString(call
->getClassScope()->getScopeName());
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
);
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
);
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
);
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
);
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
);
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
);
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); \
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); \
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
)) {
5286 case Construct::KindOfDynamicFunctionCall
: {
5287 emitFuncCall(e
, static_pointer_cast
<FunctionCall
>(node
));
5291 case Construct::KindOfIncludeExpression
: {
5292 auto ie
= static_pointer_cast
<IncludeExpression
>(node
);
5293 if (ie
->isReqLit()) {
5294 StringData
* nValue
= makeStaticString(ie
->includePath());
5297 visit(ie
->getExpression());
5298 emitConvertToCell(e
);
5300 switch (ie
->getOp()) {
5304 case T_INCLUDE_ONCE
:
5310 case T_REQUIRE_ONCE
:
5311 if (ie
->isDocumentRoot()) {
5321 case Construct::KindOfListAssignment
: {
5322 auto la
= static_pointer_cast
<ListAssignment
>(node
);
5323 auto rhs
= la
->getArray();
5325 // listAssignmentVisitLHS should have handled this
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
;
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
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();
5368 listAssignmentAssignElements(e
, indexPairs
, nullptr);
5369 } else if (simpleRHS
) {
5370 listAssignmentAssignElements(e
, indexPairs
, [&] { visit(rhs
); });
5372 listAssignmentAssignElements(
5374 [&] { emitVirtualLocal(tempLocal
); }
5378 // Leave the RHS on the stack
5382 emitPushAndFreeUnnamedL(e
, tempLocal
, start
);
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();
5395 if (ne
->isStatic()) {
5398 fpiStart
= m_ue
.bcPos();
5399 e
.FPushCtor(numParams
);
5400 } else if (ne
->getOriginalName().empty()) {
5402 visit(ne
->getNameExp());
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()))) {
5410 // new self() inside a trait or code statically not inside any class
5413 // new parent() inside a trait, code statically not inside any
5414 // class, or a class with no parent
5417 fpiStart
= m_ue
.bcPos();
5418 e
.FPushCtor(numParams
);
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
);
5431 case Construct::KindOfObjectMethodExpression
: {
5432 auto om
= static_pointer_cast
<ObjectMethodExpression
>(node
);
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()) {
5450 // Use getOriginalLiteralString(), which hasn't been
5451 // case-normalized, since __call() needs to preserve
5453 StringData
* nameLiteral
= makeStaticString(methStr
);
5454 fpiStart
= m_ue
.bcPos();
5458 om
->isNullSafe() ? ObjMethodOp::NullSafe
: ObjMethodOp::NullThrows
5460 useDirectForm
= true;
5463 if (!useDirectForm
) {
5467 emitConvertToCell(e
);
5468 fpiStart
= m_ue
.bcPos();
5471 om
->isNullSafe() ? ObjMethodOp::NullSafe
: ObjMethodOp::NullThrows
5476 emitCall(e
, om
, params
, fpiStart
);
5480 case Construct::KindOfObjectPropertyExpression
: {
5481 auto op
= static_pointer_cast
<ObjectPropertyExpression
>(node
);
5482 if (op
->isNullSafe() &&
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
)) {
5496 m_evalStack
.push(StackSym::H
);
5500 StringData
* clsName
= getClassName(op
->getObject());
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();
5512 ? PropAccessType::NullSafe
5513 : PropAccessType::Normal
5518 case Construct::KindOfQOpExpression
: {
5519 auto q
= static_pointer_cast
<QOpExpression
>(node
);
5521 // <expr> ? <expr> : <expr>
5522 Label tru
, fals
, done
;
5524 Emitter
condEmitter(q
->getCondition(), m_ue
, *this);
5525 visitIfCondition(q
->getCondition(), condEmitter
,
5531 if (currentPositionIsReachable()) {
5533 emitConvertToCell(e
);
5536 if (fals
.isUsed()) fals
.set(e
);
5537 if (currentPositionIsReachable()) {
5539 emitConvertToCell(e
);
5541 if (done
.isUsed()) {
5543 m_evalStack
.cleanTopMeta();
5548 visit(q
->getCondition());
5549 emitConvertToCell(e
);
5554 emitConvertToCell(e
);
5556 m_evalStack
.cleanTopMeta();
5561 case Construct::KindOfNullCoalesceExpression
: {
5562 auto q
= static_pointer_cast
<NullCoalesceExpression
>(node
);
5565 visit(q
->getFirst());
5571 visit(q
->getSecond());
5572 emitConvertToCell(e
);
5574 m_evalStack
.cleanTopMeta();
5579 case Construct::KindOfScalarExpression
: {
5580 auto ex
= static_pointer_cast
<Expression
>(node
);
5582 ex
->getScalarValue(v
);
5583 auto const emitted
= emitScalarValue(e
, v
);
5584 always_assert(emitted
);
5588 case Construct::KindOfPipeVariable
: {
5589 if (auto pipeVar
= getPipeLocal()) {
5590 emitVirtualLocal(*pipeVar
);
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
);
5601 if (sv
->hasContext(Expression::ObjectContext
)) {
5603 } else if (sv
->getFunctionScope()->needsLocalThis()) {
5604 static const StringData
* thisStr
= makeStaticString("this");
5605 Id thisId
= m_curFunc
->lookupVarId(thisStr
);
5606 emitVirtualLocal(thisId
);
5608 auto const subop
= sv
->hasContext(Expression::ExistContext
)
5609 ? BareThisOp::NoNotice
5610 : BareThisOp::Notice
;
5614 StringData
* nLiteral
= makeStaticString(sv
->getName());
5615 if (sv
->isSuperGlobal()) {
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
);
5634 case Construct::KindOfDynamicVariable
: {
5635 auto dv
= static_pointer_cast
<DynamicVariable
>(node
);
5636 visit(dv
->getSubExpression());
5637 emitConvertToCellOrLoc(e
);
5642 case Construct::KindOfStaticMemberExpression
: {
5643 auto sm
= static_pointer_cast
<StaticMemberExpression
>(node
);
5644 emitVirtualClassBase(e
, sm
.get());
5645 emitNameString(e
, sm
->getExp());
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();
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");
5667 m_staticArrays
.back().set(tvAsCVarRef(&tvKey
),
5668 tvAsVariant(&tvVal
));
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
));
5676 m_staticArrays
.back().append(tvAsCVarRef(&tvVal
));
5680 // Assume new array is on top of stack
5681 bool hasKey
= (bool)key
;
5684 emitConvertToCellOrLoc(e
);
5686 visit(ap
->getValue());
5688 emitConvertToVar(e
);
5690 emitConvertSecondToCell(e
);
5696 emitConvertToCell(e
);
5698 emitConvertSecondToCell(e
);
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());
5717 initScalar(tvVal
, expr
);
5718 m_staticArrays
.back().append(tvAsCVarRef(&tvVal
));
5722 int nelem
= el
->getCount(), i
;
5723 bool pop
= el
->getListKind() != ExpressionList::ListKindParam
;
5724 int keep
= el
->getListKind() == ExpressionList::ListKindLeft
?
5727 for (i
= 0; i
< nelem
; i
++) {
5728 ExpressionPtr
p((*el
)[i
]);
5730 if (pop
&& i
!= keep
) {
5740 case Construct::KindOfParameterExpression
: {
5743 case Construct::KindOfModifierExpression
: {
5746 case Construct::KindOfUserAttribute
: {
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);
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
) {
5795 EmittedClosures::accessor acc
;
5796 s_emittedClosures
.insert(acc
, makeStaticString(clsName
));
5797 my_id
= ++acc
->second
;
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.
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
));
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
);
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();
5862 m_curFunc
->isPairGenerator
= true;
5864 emitConvertToCell(e
);
5867 // evaluate value expression passed to yield
5868 visit(y
->getValueExpression());
5869 emitConvertToCell(e
);
5871 // suspend generator
5873 assert(m_evalStack
.size() == 2);
5876 assert(m_evalStack
.size() == 1);
5880 // continue with the received result on the stack
5881 assert(m_evalStack
.size() == 1);
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());
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;
5905 // evaluate expression passed to await
5907 emitConvertToCell(e
);
5909 // if expr is null, just continue
5911 emitIsType(e
, IsTypeOp::Null
);
5914 assert(m_evalStack
.size() == 1);
5921 case Construct::KindOfUseDeclarationStatementFragment
:
5922 case Construct::KindOfExpression
: {
5930 bool EmitterVisitor::emitScalarValue(Emitter
& e
, const Variant
& v
) {
5931 switch (v
.getRawType()) {
5941 v
.asBooleanVal() ? e
.True() : e
.False();
5945 e
.Int(v
.getInt64());
5949 e
.Double(v
.getDouble());
5952 case KindOfPersistentString
:
5954 e
.String(makeStaticString(v
.getStringData()));
5957 case KindOfPersistentVec
:
5959 assert(v
.isVecArray());
5960 e
.Vec(ArrayData::GetScalarArray(v
.getArrayData()));
5963 case KindOfPersistentDict
:
5966 e
.Dict(ArrayData::GetScalarArray(v
.getArrayData()));
5969 case KindOfPersistentKeyset
:
5971 assert(v
.isKeyset());
5972 e
.Keyset(ArrayData::GetScalarArray(v
.getArrayData()));
5975 case KindOfPersistentArray
:
5977 assert(v
.isPHPArray());
5978 e
.Array(ArrayData::GetScalarArray(v
.getArrayData()));
5982 case KindOfResource
:
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
);
5999 emitConvertToCell(e
);
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(
6014 const SimpleFunctionCallPtr
& call
6016 assert(call
->isCallToFunction("genva"));
6017 const auto params
= call
->getParams();
6019 e
.Array(staticEmptyArray());
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
++) {
6031 visit((*params
)[i
]);
6032 emitConvertToCell(e
);
6034 // ($_ !== null ? HH\Asio\null() : $_)->getWaitHandle()
6036 emitIsType(e
, IsTypeOp::Null
);
6040 Offset fpiStart
= m_ue
.bcPos();
6041 e
.FPushFuncD(0, s_gennull
.get());
6043 FPIRegionRecorder
fpi(this, m_ue
, m_evalStack
, fpiStart
);
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
);
6067 e
.NewPackedArray(num_params
);
6068 emitFPass(e
, 0, PassByRefKind::ErrorOnCell
);
6074 // result of AwaitAllWaitHandle does not matter
6077 for (const auto wh
: waithandles
) {
6078 emitVirtualLocal(wh
);
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
)));
6095 bool EmitterVisitor::emitInlineGena(
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.
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
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
);
6126 const auto initItId
= m_curFunc
->allocIterator();
6127 e
.IterInitK(initItId
, endloop
, item
, key
);
6128 const auto iterStart
= m_ue
.bcPos();
6132 emitVirtualLocal(array
); // for Set below
6133 emitVirtualLocal(key
); // for Set below
6136 emitVirtualLocal(item
);
6138 emitConstMethodCallNoParams(e
, "getWaitHandle");
6140 emitSet(e
); // array[$key] = $item->getWaitHandle();
6143 emitVirtualLocal(key
);
6144 emitVirtualLocal(item
);
6145 e
.IterNextK(initItId
, loop
, item
, key
);
6148 // Clear item and key. Free iterator.
6149 emitVirtualLocal(item
);
6151 emitVirtualLocal(key
);
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
);
6176 // Await on the AAWH. Note: the result of Await does not matter.
6182 // Iterate over results and store in array. Reuse same temporary array.
6185 emitVirtualLocal(key
);
6186 emitVirtualLocal(item
);
6188 emitVirtualLocal(array
);
6192 const auto resultItId
= m_curFunc
->allocIterator();
6193 e
.IterInitK(resultItId
, endloop2
, item
, key
);
6194 const auto iterStart2
= m_ue
.bcPos();
6198 emitVirtualLocal(array
); // for Set below
6199 emitVirtualLocal(key
); // for Set below
6202 emitVirtualLocal(item
);
6206 emitSet(e
); // array[$key] = WHResult($item);
6209 emitVirtualLocal(key
);
6210 emitVirtualLocal(item
);
6211 e
.IterNextK(resultItId
, loop2
, item
, key
);
6214 // Clear item and key. Free iterator.
6215 emitVirtualLocal(item
);
6217 emitVirtualLocal(key
);
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
});
6230 m_curFunc
->freeUnnamedLocal(item
);
6231 m_curFunc
->freeUnnamedLocal(key
);
6233 // Leave result array on the stack.
6234 emitPushAndFreeUnnamedL(e
, array
, arrayStart
);
6239 bool EmitterVisitor::emitInlineGen(
6241 const ExpressionPtr
& expression
6243 if (!m_ue
.m_isHHFile
|| !Option::EnableHipHopSyntax
||
6244 !expression
->is(Expression::KindOfSimpleFunctionCall
) ||
6245 Option::JitEnableRenameFunction
) {
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
);
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");
6277 if (!((*params
)[0]->getScalarValue(v
)) || !v
.isString()) {
6278 throw IncludeTimeFatalException(func
,
6279 "Inline hhas must be string literal");
6284 assemble_expression(m_ue
, m_curFunc
,
6285 m_evalStack
.size() + m_evalStack
.fdescSize(),
6286 v
.toString().toCppString());
6288 case AsmResult::NoResult
:
6291 case AsmResult::ValuePushed
:
6292 pushEvalStack(StackSym::C
);
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
);
6302 } catch (const std::exception
& ex
) {
6303 throw IncludeTimeFatalException(func
, ex
.what());
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;
6317 visit((*params
)[0]);
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);
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
);
6337 e
.Null(); // invariant returns null if used in an expression, void according
6338 // to the typechecker.
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
) {
6353 InvariantViolation("Emitter expected a location on the stack but none "
6354 "was found (at offset %d)",
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(
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
)) {
6385 e
.FPassBaseNC(opts
.paramId
, stackIdx(iFirst
));
6387 e
.BaseNC(stackIdx(iFirst
), baseFlags
);
6392 e
.FPassBaseNL(opts
.paramId
, m_evalStack
.getLoc(iFirst
));
6394 e
.BaseNL(m_evalStack
.getLoc(iFirst
), baseFlags
);
6398 always_assert(false);
6406 e
.FPassBaseGC(opts
.paramId
, stackIdx(iFirst
));
6408 e
.BaseGC(stackIdx(iFirst
), baseFlags
);
6413 e
.FPassBaseGL(opts
.paramId
, m_evalStack
.getLoc(iFirst
));
6415 e
.BaseGL(m_evalStack
.getLoc(iFirst
), baseFlags
);
6419 always_assert(false);
6424 if (m_evalStack
.get(iLast
) != StackSym::AM
) {
6425 unexpectedStackSym(sym
, "S-vector base, class ref");
6428 auto const clsIdx
= opts
.rhsVal
? 1 : 0;
6431 e
.BaseSC(stackIdx(iFirst
), clsIdx
);
6434 e
.BaseSL(m_evalStack
.getLoc(iFirst
), clsIdx
);
6437 unexpectedStackSym(sym
, "S-vector base, prop name");
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.
6447 case StackSym::None
:
6451 e
.FPassBaseL(opts
.paramId
, m_evalStack
.getLoc(iFirst
));
6453 e
.BaseL(m_evalStack
.getLoc(iFirst
), baseFlags
);
6457 e
.BaseC(stackIdx(iFirst
));
6460 e
.BaseR(stackIdx(iFirst
));
6466 always_assert(false);
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
6478 for (auto i
= iFirst
+ 1; i
< iLast
; ++i
) {
6480 e
.FPassDim(opts
.paramId
, symToMemberKey(e
, i
, opts
.allowW
));
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
;
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
)) {
6506 auto const local
= m_evalStack
.getLoc(i
);
6508 case StackSym::E
: return MemberKey
{MEL
, local
};
6509 case StackSym::P
: return MemberKey
{MPL
, local
};
6510 default: always_assert(false);
6515 int32_t(m_evalStack
.actualSize() - 1 - m_evalStack
.getActualPos(i
));
6517 case StackSym::E
: return MemberKey
{MEC
, idx
};
6518 case StackSym::P
: return MemberKey
{MPC
, idx
};
6519 default: always_assert(false);
6523 auto const int64
= m_evalStack
.getInt(i
);
6525 case StackSym::E
: return MemberKey
{MEI
, int64
};
6526 default: always_assert(false);
6530 auto const str
= m_evalStack
.getName(i
);
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);
6539 always_assert(false);
6543 void EmitterVisitor::emitPop(Emitter
& e
) {
6544 if (checkIfStackEmpty("Pop*")) return;
6545 LocationGuard
loc(e
, m_tempLoc
);
6548 emitClsIfSPropBase(e
);
6549 int iLast
= m_evalStack
.size()-1;
6550 int i
= scanStackForLocation(iLast
);
6553 char sym
= m_evalStack
.get(i
);
6554 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
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;
6567 unexpectedStackSym(sym
, "emitPop");
6572 emitQueryMOp(i
, iLast
, e
, QueryMOp::CGet
);
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))
6582 int localIdx
= m_evalStack
.getLoc(m_evalStack
.size() - 2);
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))
6591 int localIdx
= m_evalStack
.getLoc(m_evalStack
.size() - 3);
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()) {
6608 e
.AGetL(m_evalStack
.getLoc(m_evalStack
.size() - 1));
6614 unexpectedStackSym(sym
, "emitAGet");
6618 void EmitterVisitor::emitQueryMOp(int iFirst
, int iLast
, Emitter
& e
,
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
);
6630 emitClsIfSPropBase(e
);
6631 int iLast
= m_evalStack
.size()-1;
6632 int i
= scanStackForLocation(iLast
);
6635 char sym
= m_evalStack
.get(i
);
6636 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
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;
6649 unexpectedStackSym(sym
, "emitCGet");
6654 emitQueryMOp(i
, iLast
, e
, QueryMOp::CGet
);
6658 void EmitterVisitor::emitCGetQuiet(Emitter
& e
) {
6659 if (checkIfStackEmpty("CGetQuiet*")) return;
6660 LocationGuard
loc(e
, m_tempLoc
);
6663 emitClsIfSPropBase(e
);
6664 int iLast
= m_evalStack
.size()-1;
6665 int i
= scanStackForLocation(iLast
);
6668 char sym
= m_evalStack
.get(i
);
6669 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
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;
6682 unexpectedStackSym(sym
, "emitCGetQuiet");
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
);
6697 emitClsIfSPropBase(e
);
6698 int iLast
= m_evalStack
.size()-1;
6699 int i
= scanStackForLocation(iLast
);
6702 char sym
= m_evalStack
.get(i
);
6703 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
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;
6716 unexpectedStackSym(sym
, "emitVGet");
6721 auto const stackCount
=
6722 emitMOp(i
, iLast
, e
, MInstrOpts
{MOpFlags::Define
});
6723 e
.VGetM(stackCount
, symToMemberKey(e
, iLast
, true /* allowW */));
6728 Id
EmitterVisitor::emitVisitAndSetUnnamedL(Emitter
& e
, ExpressionPtr 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();
6741 ue
.emitIVA(tempLocal
);
6747 void EmitterVisitor::emitPushAndFreeUnnamedL(Emitter
& e
, Id tempLocal
,
6749 assert(tempLocal
>= 0);
6750 assert(start
!= InvalidAbsoluteOffset
);
6751 newFaultRegionAndFunclet(start
, m_ue
.bcPos(),
6752 new UnsetUnnamedLocalThunklet(tempLocal
));
6753 emitVirtualLocal(tempLocal
);
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
;
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
;
6799 case Expression::KindOfUnaryOpExpression
: {
6800 auto u
= static_pointer_cast
<UnaryOpExpression
>(exp
);
6801 int op
= u
->getOp();
6802 if (op
== T_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
;
6811 case Expression::KindOfExpressionList
: {
6812 auto el
= static_pointer_cast
<ExpressionList
>(exp
);
6813 if (el
->getListKind() != ExpressionList::ListKindParam
) {
6814 return PassByRefKind::WarnOnCell
;
6821 return PassByRefKind::ErrorOnCell
;
6824 bool EmitterVisitor::emitBuiltinCallArg(Emitter
& e
,
6830 if (checkIfStackEmpty("Builtin arg*")) return true;
6832 auto wasCell
= emitVGet(e
, true);
6833 if (wasCell
&& mustBeRef
) {
6834 auto kind
= getPassByRefKind(exp
);
6836 case PassByRefKind::AllowCell
:
6839 case PassByRefKind::WarnOnCell
:
6841 makeStaticString("Only variables should be passed by reference"));
6843 e
.FCallBuiltin(2, 2, s_trigger_error
.get());
6846 case PassByRefKind::ErrorOnCell
:
6847 auto save
= m_evalStack
;
6849 makeStaticString("Only variables can be passed by reference"));
6850 e
.Fatal(FatalOp::Runtime
);
6851 m_evalStackIsUnknown
= false;
6862 static bool isNormalLocalVariable(const ExpressionPtr
& expr
) {
6863 SimpleVariable
* sv
= static_cast<SimpleVariable
*>(expr
.get());
6864 return (expr
->is(Expression::KindOfSimpleVariable
) &&
6865 !sv
->isSuperGlobal() &&
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
)) {
6876 auto const sv
= static_cast<SimpleVariable
*>(exp
.get());
6877 Id locId
= m_curFunc
->lookupVarId(makeStaticString(sv
->getName()));
6878 emitVirtualLocal(locId
);
6882 void EmitterVisitor::emitBuiltinDefaultArg(Emitter
& e
, Variant
& v
,
6883 MaybeDataType t
, int paramId
) {
6884 switch (v
.getType()) {
6889 case KindOfPersistentString
:
6891 case KindOfPersistentVec
:
6893 case KindOfPersistentDict
:
6895 case KindOfPersistentKeyset
:
6897 case KindOfPersistentArray
:
6900 case KindOfResource
:
6920 if (v
.getBoolean()) {
6928 e
.Int(v
.getInt64());
6932 e
.Double(v
.toDouble());
6935 case KindOfPersistentString
:
6936 case KindOfString
: {
6937 StringData
*nValue
= makeStaticString(v
.getStringData());
6942 case KindOfPersistentVec
:
6944 assert(v
.isVecArray());
6945 e
.Vec(v
.getArrayData());
6948 case KindOfPersistentDict
:
6951 e
.Dict(v
.getArrayData());
6954 case KindOfPersistentKeyset
:
6956 assert(v
.isKeyset());
6957 e
.Keyset(v
.getArrayData());
6960 case KindOfPersistentArray
:
6962 assert(v
.isPHPArray());
6963 e
.Array(v
.getArrayData());
6968 case KindOfResource
:
6976 void EmitterVisitor::emitFuncCallArg(Emitter
& e
,
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
);
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
);
7007 emitClsIfSPropBase(e
);
7008 int iLast
= m_evalStack
.size()-1;
7009 int i
= scanStackForLocation(iLast
);
7012 char sym
= m_evalStack
.get(i
);
7013 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
7015 case StackSym::L
: e
.FPassL(paramId
, m_evalStack
.getLoc(i
)); break;
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);
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;
7033 unexpectedStackSym(sym
, "emitFPass");
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
);
7051 char sym
= m_evalStack
.get(i
);
7052 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
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
7067 e
.IsTypeC(IsTypeOp::Null
);
7071 unexpectedStackSym(sym
, "emitIsset");
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()) {
7086 e
.IsTypeL(m_evalStack
.getLoc(m_evalStack
.size() - 1), op
);
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
);
7104 char sym
= m_evalStack
.get(i
);
7105 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
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;
7117 unexpectedStackSym(sym
, "emitEmpty");
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
);
7135 char sym
= m_evalStack
.get(i
);
7136 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
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
: {
7147 std::ostringstream s
;
7148 s
<< "Attempt to unset static property " << exp
->getText();
7149 emitMakeUnitFatal(e
, s
.str().c_str());
7153 unexpectedStackSym(sym
, "emitUnset");
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
) {
7168 void EmitterVisitor::emitSet(Emitter
& e
) {
7169 if (checkIfStackEmpty("Set*")) return;
7171 int iLast
= m_evalStack
.size()-2;
7172 int i
= scanStackForLocation(iLast
);
7175 char sym
= m_evalStack
.get(i
);
7176 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
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;
7186 unexpectedStackSym(sym
, "emitSet");
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
= [&] {
7207 return ifIntOverflow(SetOpOp::PlusEqual
, SetOpOp::PlusEqualO
);
7209 return ifIntOverflow(SetOpOp::MinusEqual
, SetOpOp::MinusEqualO
);
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
;
7226 int iLast
= m_evalStack
.size()-2;
7227 int i
= scanStackForLocation(iLast
);
7230 char sym
= m_evalStack
.get(i
);
7231 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
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;
7241 unexpectedStackSym(sym
, "emitSetOp");
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
);
7259 char sym
= m_evalStack
.get(i
);
7260 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
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;
7270 unexpectedStackSym(sym
, "emitBind");
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
);
7289 char sym
= m_evalStack
.get(i
);
7290 if (sz
== 0 || (sz
== 1 && StackSym::GetMarker(sym
) == StackSym::S
)) {
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;
7300 unexpectedStackSym(sym
, "emitIncDec");
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
) {
7315 void EmitterVisitor::emitConvertSecondToCell(Emitter
& e
) {
7316 if (m_evalStack
.size() <= 1) {
7318 "Emitter encounted an empty evaluation stack when inside "
7319 "the emitConvertSecondToCell() function (at offset %d)",
7323 char sym
= m_evalStack
.get(m_evalStack
.size() - 2);
7324 char symFlavor
= StackSym::GetSymFlavor(sym
);
7325 if (symFlavor
== StackSym::C
) {
7327 } else if (symFlavor
== StackSym::L
) {
7330 // emitConvertSecondToCell() should never be used for symbolic flavors
7331 // other than C or L
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(),
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()) {
7353 "Emitter encounted an empty evaluation stack when inside "
7354 "the emitConvertToCellOrLoc() function (at offset %d)",
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
7362 // Otherwise, call emitCGet to convert the top of stack to cell
7367 void EmitterVisitor::emitConvertToVar(Emitter
& 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
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
));
7399 case SymbolicStack::CLS_LATE_BOUND
:
7402 case SymbolicStack::CLS_SELF
:
7405 case SymbolicStack::CLS_PARENT
:
7408 case SymbolicStack::CLS_NAMED_LOCAL
: {
7409 int loc
= m_evalStack
.getLoc(pos
);
7410 emitVirtualLocal(loc
);
7414 case SymbolicStack::CLS_UNNAMED_LOCAL
: {
7415 int loc
= m_evalStack
.getLoc(pos
);
7416 emitVirtualLocal(loc
);
7418 emitVirtualLocal(loc
);
7420 newFaultRegionAndFunclet(m_evalStack
.getUnnamedLocStart(pos
),
7422 new UnsetUnnamedLocalThunklet(loc
));
7423 m_curFunc
->freeUnnamedLocal(loc
);
7426 case SymbolicStack::CLS_INVALID
:
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;
7441 char marker
= StackSym::GetMarker(m_evalStack
.get(pos
));
7442 if (marker
!= StackSym::E
&& marker
!= StackSym::W
&&
7443 marker
!= StackSym::P
&& marker
!= StackSym::Q
) {
7448 InvariantViolation("Emitter expected a location on the stack but none "
7449 "was found (at offset %d)",
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
) {
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)",
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();
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
)) {
7501 if (t
== KindOfInt64
) {
7502 n
= cval
.asInt64Val();
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
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
7526 } else if (LIKELY(state
.defI
== -1)) {
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) {
7543 } else if (t
== KindOfString
) {
7544 if (caseMap
.size() < kMinStringSwitchCases
) {
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
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
];
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
);
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)",
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
));
7611 "Emitter encountered an unsupported StackSym \"%s\" on "
7612 "the evaluation stack inside the markElem function (at "
7614 StackSym::ToString(sym
).c_str(),
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()) {
7626 "Emitter encountered an empty evaluation stack inside "
7627 "the markProp function (at offset %d)",
7631 char sym
= m_evalStack
.top();
7632 if (sym
== StackSym::C
|| sym
== StackSym::L
|| sym
== StackSym::T
) {
7634 m_evalStack
.size()-1,
7635 (sym
| (propAccessType
== PropAccessType::NullSafe
7642 "Emitter encountered an unsupported StackSym \"%s\" on "
7643 "the evaluation stack inside the markProp function (at "
7645 StackSym::ToString(sym
).c_str(),
7650 void EmitterVisitor::markSProp(Emitter
& e
) {
7651 if (m_evalStack
.empty()) {
7653 "Emitter encountered an empty evaluation stack inside "
7654 "the markSProp function (at offset %d)",
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
));
7663 "Emitter encountered an unsupported StackSym \"%s\" on "
7664 "the evaluation stack inside the markSProp function "
7666 StackSym::ToString(sym
).c_str(),
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()); \
7680 char sym = m_evalStack.get(index); \
7681 if (sym == StackSym::C || sym == StackSym::L) { \
7682 m_evalStack.set(index, (sym | StackSym::N)); \
7684 InvariantViolation( \
7685 "Emitter encountered an unsupported StackSym \"%s\" " \
7686 "on the evaluation stack inside the %s function (at " \
7688 StackSym::ToString(sym).c_str(), __FUNCTION__, \
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()) {
7707 "Emitter encountered an empty evaluation stack inside "
7708 "the markGlobalName function (at offset %d)",
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
));
7717 "Emitter encountered an unsupported StackSym \"%s\" on "
7718 "the evaluation stack inside the markGlobalName function "
7720 StackSym::ToString(sym
).c_str(),
7725 void EmitterVisitor::emitNameString(Emitter
& e
, ExpressionPtr n
,
7726 bool allowLiteral
) {
7728 if (n
->getScalarValue(v
) && v
.isString()) {
7729 StringData
* nLiteral
= makeStaticString(v
.toCStrRef().get());
7731 m_evalStack
.push(StackSym::T
);
7735 m_evalStack
.setString(nLiteral
);
7738 emitConvertToCellOrLoc(e
);
7742 void EmitterVisitor::postponeMeth(MethodStatementPtr m
, FuncEmitter
* fe
,
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
,
7754 m_postponedPinits
.push_back(PostponedNonScalars(is
, fe
, v
));
7757 void EmitterVisitor::postponeSinit(InterfaceStatementPtr is
, FuncEmitter
* fe
,
7759 m_postponedSinits
.push_back(PostponedNonScalars(is
, fe
, v
));
7762 void EmitterVisitor::postponeCinit(InterfaceStatementPtr is
, FuncEmitter
* fe
,
7764 m_postponedCinits
.push_back(PostponedNonScalars(is
, fe
, v
));
7767 static Attr
buildAttrs(ModifierExpressionPtr mod
, bool isRef
= false) {
7768 int attrs
= AttrNone
;
7770 attrs
|= AttrReference
;
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()) {
7790 * <<__HipHopSpecific>> user attribute marks funcs/methods as HipHop specific
7792 * <<__IsFoldable>> Function has no side-effects and may be called at
7793 * compile time with constant input to get deterministic output.
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
,
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
;
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
));
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
,
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()) {
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
;
7904 (flags
== (TypeConstraint::ExtendedHint
| TypeConstraint::HHType
))) {
7908 auto strippedName
= annot
->stripNullable().stripSoft().vanillaName();
7910 return TypeConstraint
{
7911 makeStaticString(strippedName
),
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()),
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
;
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(
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
);
7966 top_fes
.push_back(fe
);
7969 auto funcScope
= meth
->getFunctionScope();
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(
7981 "Return type hint for async closure must be awaitable"
7984 throw IncludeTimeFatalException(
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(
7995 "Awaitable interface expects 1 type argument, %d given",
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
)) {
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
);
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
);
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
8034 m_curFunc
->isMemoizeImpl
= true;
8037 if (funcScope
->isNative()) {
8038 bindNativeFunc(meth
, fe
);
8040 emitMethodMetadata(meth
, p
.m_closureUseVars
, p
.m_top
);
8044 if (fe
->isClosureBody
) {
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
,
8066 auto const& userAttrs
= meth
->getFunctionScope()->userAttributes();
8067 for (auto& attr
: userAttrs
) {
8068 const StringData
* uaName
= makeStaticString(attr
.first
);
8069 ExpressionPtr uaValue
= attr
.second
;
8071 assert(uaValue
->isScalar());
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
,
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();
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
;
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();
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());
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
)) {
8150 fe
->retTypeConstraint
=
8151 determine_type_constraint_from_annot(retType
, true);
8153 fe
->retTypeConstraint
= TypeConstraint
{
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
));
8177 emitMethodDVInitializers(e
, meth
, topOfBody
);
8180 void EmitterVisitor::emitMethodMetadata(MethodStatementPtr meth
,
8181 ClosureUseVarVec
* useVars
,
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
++) {
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
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(),
8255 buildMethodAttrs(meth
, fe
, top
),
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()) {
8293 initScalar(dv
, vNode
);
8294 pi
.defaultValue
= dv
;
8296 std::string orig
= vNode
->getComment();
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());
8303 // This was optimized from a Constant, or ClassConstant
8304 // use the original string
8305 phpCode
= makeStaticString(orig
);
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();
8326 assert(uaValue
->isScalar());
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;
8383 // deprecation warnings disabled
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()) {
8395 e
.String(makeStaticString("::" + funcName
+ ": " + deprMessage
));
8398 e
.String(makeStaticString(
8399 clsScope
->getScopeName() + "::" + funcName
8400 + ": " + deprMessage
));
8403 e
.String(makeStaticString(funcName
+ ": " + deprMessage
));
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());
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
) {
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();
8442 r
.char0
= r
.char1
- 1;
8443 e
.setTempLocation(r
);
8445 if (shouldEmitVerifyRetType()) {
8449 e
.setTempLocation(OptLocation());
8452 if (!m_curFunc
->isMemoizeImpl
) {
8453 emitMethodDVInitializers(e
, meth
, topOfBody
);
8457 void EmitterVisitor::emitMethodDVInitializers(Emitter
& e
,
8458 MethodStatementPtr
& meth
,
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()) {
8467 Label
entryPoint(e
);
8468 emitVirtualLocal(i
);
8469 visit(par
->defaultValue());
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
8485 m_curFunc
->memoizePropName
= makeStaticString("static$memoize_cache");
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();
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
));
8520 if (useSharedProp
||
8521 (meth
->getParams() && meth
->getParams()->getCount() > 0)) {
8522 tvProp
= make_tv
<KindOfPersistentArray
>(staticEmptyArray());
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
,
8533 void EmitterVisitor::emitMemoizeProp(Emitter
& e
,
8534 MethodStatementPtr meth
,
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
);
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
);
8560 emitVirtualLocal(paramIDs
[i
]);
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
);
8599 emitMethodPrologue(e
, meth
);
8602 int staticLocalID
= 0;
8604 // static ${propName} = {numParams > 0 ? array() : null};
8605 staticLocalID
= m_curFunc
->allocUnnamedLocal();
8606 emitVirtualLocal(staticLocalID
);
8607 if (numParams
== 0) {
8610 e
.Array(staticEmptyArray());
8612 e
.StaticLocInit(staticLocalID
, m_curFunc
->memoizePropName
);
8613 } else if (!meth
->getFunctionScope()->isStatic()) {
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
);
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 "
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
);
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();
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);
8672 // if (array_key_exists($paramN, ${propName}[$param1][...][$paramN-1]))
8673 if (cacheLookupLen
== 1 && m_curFunc
->hasMemoizeSharedProp
) {
8674 e
.Int(m_curFunc
->memoizeSharedPropIndex
);
8676 emitVirtualLocal(cacheLookup
[cacheLookupLen
- 1]);
8679 emitMemoizeProp(e
, meth
, staticLocalID
, cacheLookup
, cacheLookupLen
- 1);
8686 // return $<propName>[$param1][...][$paramN]
8687 int cacheLookupLen
= cacheLookup
.size();
8688 emitMemoizeProp(e
, meth
, staticLocalID
, cacheLookup
, cacheLookupLen
);
8692 // Otherwise, call the memoized func, store the result, and return it
8694 emitMemoizeProp(e
, meth
, staticLocalID
, cacheLookup
, cacheLookupLen
);
8695 auto fpiStart
= m_ue
.bcPos();
8697 e
.FPushFuncD(numParams
, methName
);
8698 } else if (meth
->getFunctionScope()->isStatic()) {
8699 emitClsIfSPropBase(e
);
8701 if (classScope
&& classScope
->isTrait()) {
8704 fpiStart
= m_ue
.bcPos();
8705 e
.FPushClsMethodF(numParams
);
8707 fpiStart
= m_ue
.bcPos();
8708 e
.FPushClsMethodD(numParams
, methName
, m_curFunc
->pce()->name());
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
);
8723 emitConvertToCell(e
);
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
);
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
8767 size_t nProps
= p
.m_vec
->size();
8769 for (size_t i
= 0; i
< nProps
; ++i
) {
8770 const StringData
* propName
=
8771 makeStaticString(((*p
.m_vec
)[i
]).first
);
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
));
8783 visit((*p
.m_vec
)[i
].second
);
8784 e
.InitProp(const_cast<StringData
*>(propName
), op
);
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>;
8837 size_t nConsts
= p
.m_vec
->size();
8838 assert(nConsts
> 0);
8840 for (size_t i
= 0; i
< nConsts
- 1; ++i
) {
8843 emitVirtualLocal(0);
8845 e
.String((StringData
*)makeStaticString(((*p
.m_vec
)[i
]).first
));
8849 visit((*p
.m_vec
)[i
].second
);
8854 visit((*p
.m_vec
)[nConsts
-1].second
);
8858 p
.release(); // Manually trigger memory cleanup.
8859 m_postponedCinits
.pop_front();
8863 void EmitterVisitor::emitVirtualLocal(int localId
) {
8866 m_evalStack
.push(StackSym::L
);
8867 m_evalStack
.setInt(localId
);
8870 template<class Expr
>
8871 void EmitterVisitor::emitVirtualClassBase(Emitter
& e
, Expr
* node
) {
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
);
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
);
8901 m_evalStack
.setUnnamedLocal(clsBaseIdx
, unnamedLoc
, m_ue
.bcPos());
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
8910 if (node
->isParent()) {
8911 m_evalStack
.setClsBaseType(SymbolicStack::CLS_PARENT
);
8912 } else if (node
->isSelf()) {
8913 m_evalStack
.setClsBaseType(SymbolicStack::CLS_SELF
);
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
);
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());
8937 } else if (call
->isCallToFunction("parse_str")) {
8938 emitFuncCall(e
, call
, "__SystemLib\\parse_str", call
->getParams());
8940 } else if (call
->isCallToFunction("compact")) {
8941 emitFuncCall(e
, call
,
8942 "__SystemLib\\compact_sl", call
->getParams());
8944 } else if (call
->isCallToFunction("get_defined_vars")) {
8945 emitFuncCall(e
, call
,
8946 "__SystemLib\\get_defined_vars", call
->getParams());
8948 } else if (call
->isCallToFunction("func_get_args")) {
8949 emitFuncCall(e
, call
,
8950 "__SystemLib\\func_get_args_sl", call
->getParams());
8952 } else if (call
->isCallToFunction("func_get_arg")) {
8953 emitFuncCall(e
, call
,
8954 "__SystemLib\\func_get_arg_sl", call
->getParams());
8956 } else if (call
->isCallToFunction("func_num_args")) {
8957 emitFuncCall(e
, call
,
8958 "__SystemLib\\func_num_arg_", call
->getParams());
8964 bool EmitterVisitor::emitCallUserFunc(Emitter
& e
, SimpleFunctionCallPtr func
) {
8967 int minParams
, maxParams
;
8968 CallUserFuncFlags flags
;
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
;
8992 if (flags
== CallUserFuncNone
) return false;
8993 if (func
->hasUnpack()) {
8994 throw EmitterVisitor::IncludeTimeFatalException(
8996 "Using argument unpacking for a call_user_func is not supported"
9000 ExpressionPtr callable
= (*params
)[0];
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
);
9014 fpiStart
= m_ue
.bcPos();
9015 e
.FPushCufSafe(nParams
- param
);
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
) {
9032 e
.FCall(nParams
- param
);
9034 if (flags
& CallUserFuncSafe
) {
9035 if (flags
& CallUserFuncReturn
) {
9044 Func
* EmitterVisitor::canEmitBuiltinCall(const std::string
& name
,
9046 if (Option::JitEnableRenameFunction
||
9047 !RuntimeOption::EvalEnableCallBuiltin
) {
9050 if (Option::DynamicInvokeFunctions
.size()) {
9051 if (Option::DynamicInvokeFunctions
.find(name
) !=
9052 Option::DynamicInvokeFunctions
.end()) {
9056 Func
* f
= Unit::lookupFunc(makeStaticString(name
));
9058 (f
->attrs() & AttrNoFCallBuiltin
) ||
9059 !f
->nativeFuncPtr() ||
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();
9076 assertx(concrete_params
> 0);
9079 for (int i
= 0; i
< concrete_params
; i
++) {
9080 if ((!allowDoubleArgs
) &&
9081 (f
->params()[i
].builtinType
== KindOfDouble
)) {
9084 if (i
>= numParams
) {
9085 auto &pi
= f
->params()[i
];
9086 if (pi
.isVariadic()) continue;
9087 if (!pi
.hasDefaultValue()) {
9090 if (pi
.defaultValue
.m_type
== KindOfUninit
) {
9091 // TODO: Resolve persistent constants
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
:
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()) {
9120 StringData
* cLiteral
=
9121 makeStaticString(node
->getOriginalClassName());
9122 StringData
* nLiteral
= makeStaticString(nameStr
);
9123 fpiStart
= m_ue
.bcPos();
9124 e
.FPushClsMethodD(numParams
, nLiteral
, cLiteral
);
9126 emitVirtualClassBase(e
, node
.get());
9127 if (!nameStr
.empty()) {
9129 StringData
* nLiteral
= makeStaticString(nameStr
);
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
);
9143 e
.FPushClsMethod(numParams
);
9146 } else if (!nameStr
.empty()) {
9148 nLiteral
= makeStaticString(nameStr
);
9149 fcallBuiltin
= canEmitBuiltinCall(nameStr
, numParams
);
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
) {
9163 nLiteral
= makeStaticString(nonNSName
);
9164 fcallBuiltin
= nullptr;
9168 if (!fcallBuiltin
) {
9169 fpiStart
= m_ue
.bcPos();
9170 if (nsName
== nullptr) {
9171 e
.FPushFuncD(numParams
, nLiteral
);
9173 assert(!nameOverride
);
9174 e
.FPushFuncU(numParams
, nsName
, nLiteral
);
9180 emitConvertToCell(e
);
9181 // FPushFunc consumes method name from stack
9182 fpiStart
= m_ue
.bcPos();
9183 e
.FPushFunc(numParams
);
9186 auto variadic
= !unpack
&& fcallBuiltin
->hasVariadicCaptureParam();
9187 assertx((numParams
<= fcallBuiltin
->numParams()) || variadic
);
9189 auto concreteParams
= fcallBuiltin
->numParams();
9191 assertx(concreteParams
> 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
);
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
)),
9216 if (numParams
<= concreteParams
) {
9217 e
.Array(staticEmptyArray());
9219 e
.NewPackedArray(numParams
- concreteParams
);
9222 e
.FCallBuiltin(fcallBuiltin
->numParams(),
9223 std::min
<int32_t>(numParams
, fcallBuiltin
->numParams()),
9226 emitCall(e
, node
, params
, fpiStart
);
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.
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()
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
);
9300 emitClassTraitPrecRule(pce
, precStmt
);
9302 auto aliasStmt
= dynamic_pointer_cast
<TraitAliasStatement
>(rule
);
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
9316 auto const name
= makeStaticString(td
->name
);
9317 auto const value
= makeStaticString(valueStr
);
9318 m_ue
.mergeLitstr(name
);
9319 m_ue
.mergeLitstr(value
);
9322 if (annot
.isFunction() || annot
.isMixed()) {
9323 type
= AnnotType::Mixed
;
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
;
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();
9347 assert(uaValue
->isScalar());
9349 initScalar(tv
, uaValue
);
9350 userAttrs
[uaName
] = tv
;
9355 record
.typeStructure
= Array(td
->annot
->getScalarArrayRep());
9357 record
.value
= value
;
9359 record
.nullable
= nullable
;
9360 record
.userAttrs
= userAttrs
;
9361 record
.attrs
= !SystemLib::s_inited
? AttrPersistent
: AttrNone
;
9362 Id id
= m_ue
.addTypeAlias(record
);
9368 void EmitterVisitor::emitClass(Emitter
& e
,
9369 ClassScopePtr cNode
,
9372 const StringData
* fatal_msg
= cNode
->getFatalMessage();
9373 if (fatal_msg
!= nullptr) {
9374 e
.String(fatal_msg
);
9375 e
.Fatal(FatalOp::Runtime
);
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
) :
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
;
9420 if (SystemLib::s_inited
&& !cNode
->isSystem()) {
9421 if (nInterfaces
> firstInterface
9422 || cNode
->getUsedTraitNames().size()
9423 || cNode
->getClassRequiredExtends().size()
9424 || cNode
->getClassRequiredImplements().size()
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
,
9442 auto r
= is
->getRange();
9445 e
.setTempLocation(r
);
9446 if (hoistable
!= PreClass::AlwaysHoistable
) {
9447 e
.DefCls(pce
->id());
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
;
9477 assert(uaValue
->isScalar());
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
);
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();
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();
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
;
9525 if (vNode
->isScalar()) {
9526 initScalar(tvVal
, vNode
);
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
));
9538 if (nonScalarSinitVec
== nullptr) {
9539 nonScalarSinitVec
= new NonScalarVec();
9541 nonScalarSinitVec
->push_back(NonScalarPair(propName
, vNode
));
9545 tvWriteNull(&tvVal
);
9548 pce
->addProperty(propName
, propAttrs
, typeConstraint
,
9549 propDoc
, &tvVal
, RepoAuthType
{});
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());
9564 pce
->addAbstractConstant(constName
, typeConstraint
,
9569 for (int ii
= 0; ii
< nCons
; ii
++) {
9570 auto ae
= static_pointer_cast
<AssignmentExpression
>((*el
)[ii
]);
9572 static_pointer_cast
<ConstantExpression
>(ae
->getVariable());
9573 auto vNode
= ae
->getValue();
9574 StringData
* constName
= makeStaticString(con
->getName());
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
);
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()),
9601 cc
->getTypeStructure());
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
);
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
);
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
);
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
);
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
);
9663 struct ForeachIterGuard
{
9664 ForeachIterGuard(EmitterVisitor
& ev
,
9669 m_ev
.pushIterScope(iterId
, kind
);
9671 ~ForeachIterGuard() {
9672 m_ev
.popIterScope();
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());
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
;
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
);
9717 auto svVal
= static_pointer_cast
<SimpleVariable
>(val
);
9718 StringData
* name
= makeStaticString(svVal
->getName());
9719 valTempLocal
= m_curFunc
->lookupVarId(name
);
9721 auto svKey
= static_pointer_cast
<SimpleVariable
>(key
);
9722 name
= makeStaticString(svKey
->getName());
9723 keyTempLocal
= m_curFunc
->lookupVarId(name
);
9725 // Meta info on the key local will confuse the translator (and
9726 // wouldn't be useful anyway)
9727 m_evalStack
.cleanTopMeta();
9733 // Meta info on the value local will confuse the translator (and
9734 // wouldn't be useful anyway)
9735 m_evalStack
.cleanTopMeta();
9738 emitConvertToVar(e
);
9740 e
.MIterInitK(itId
, exit
, valTempLocal
, keyTempLocal
);
9742 e
.MIterInit(itId
, exit
, valTempLocal
);
9745 emitConvertToCell(e
);
9747 e
.IterInitK(itId
, exit
, valTempLocal
, keyTempLocal
);
9749 e
.IterInit(itId
, exit
, valTempLocal
);
9754 bIterStart
= m_ue
.bcPos();
9756 keyTempLocal
= key
? m_curFunc
->allocUnnamedLocal() : -1;
9757 valTempLocal
= m_curFunc
->allocUnnamedLocal();
9759 emitVirtualLocal(keyTempLocal
);
9761 emitVirtualLocal(valTempLocal
);
9765 emitConvertToVar(e
);
9767 emitConvertToCell(e
);
9772 e
.MIterInitK(itId
, exit
, valTempLocal
, keyTempLocal
);
9774 e
.MIterInit(itId
, exit
, valTempLocal
);
9778 e
.IterInitK(itId
, exit
, valTempLocal
, keyTempLocal
);
9780 e
.IterInit(itId
, exit
, valTempLocal
);
9784 // At this point, valTempLocal and keyTempLocal if applicable, contain the
9785 // key and value for the iterator.
9787 bIterStart
= m_ue
.bcPos();
9788 if (key
&& !listKey
) {
9790 emitClsIfSPropBase(e
);
9793 emitForeachListAssignment(
9795 ListAssignmentPtr(static_pointer_cast
<ListAssignment
>(val
)),
9796 [&] { emitVirtualLocal(valTempLocal
); }
9800 emitClsIfSPropBase(e
);
9801 emitVirtualLocal(valTempLocal
);
9811 emitVirtualLocal(valTempLocal
);
9813 newFaultRegionAndFunclet(bIterStart
, m_ue
.bcPos(),
9814 new UnsetUnnamedLocalThunklet(valTempLocal
));
9816 assert(keyTempLocal
!= -1);
9818 emitForeachListAssignment(
9820 ListAssignmentPtr(static_pointer_cast
<ListAssignment
>(key
)),
9821 [&] { emitVirtualLocal(keyTempLocal
); }
9824 emitVirtualLocal(keyTempLocal
);
9829 emitVirtualLocal(keyTempLocal
);
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()) {
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();
9858 e
.MIterNextK(itId
, start
, valTempLocal
, keyTempLocal
);
9860 e
.MIterNext(itId
, start
, valTempLocal
);
9864 e
.IterNextK(itId
, start
, valTempLocal
, keyTempLocal
);
9866 e
.IterNext(itId
, start
, valTempLocal
);
9869 newFaultRegionAndFunclet(bIterStart
, m_ue
.bcPos(),
9870 new IterFreeThunklet(itId
, strong
),
9871 { itId
, strong
? KindOfMIter
: KindOfIter
});
9873 m_curFunc
->freeUnnamedLocal(valTempLocal
);
9875 m_curFunc
->freeUnnamedLocal(keyTempLocal
);
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
);
9895 auto const iterTempStartUse
= m_ue
.bcPos();
9897 // Make sure it actually is an AsyncIterator.
9898 e
.InstanceOfD(makeStaticString("HH\\AsyncIterator"));
9900 e
.String(makeStaticString(
9901 "Unable to iterate non-AsyncIterator asynchronously"));
9902 e
.Fatal(FatalOp::Runtime
);
9904 // Start of the next iteration.
9907 // Await the next value.
9908 emitVirtualLocal(iterTempLocal
);
9910 emitConstMethodCallNoParams(e
, "next");
9912 auto const resultTempLocal
= emitSetUnnamedL(e
);
9914 // Did we finish yet?
9915 emitVirtualLocal(resultTempLocal
);
9916 emitIsType(e
, IsTypeOp::Null
);
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
);
9927 if (target
->is(Expression::KindOfListAssignment
)) {
9928 emitForeachListAssignment(
9930 ListAssignmentPtr(static_pointer_cast
<ListAssignment
>(target
)),
9934 // Obtain target to be set.
9937 // Put $result[index] on the stack.
9947 auto const resultTempStartUse
= m_ue
.bcPos();
9950 if (fe
->getNameExp()) {
9951 populate(fe
->getNameExp(), 0);
9955 populate(fe
->getValueExp(), 1);
9957 newFaultRegionAndFunclet(resultTempStartUse
, m_ue
.bcPos(),
9958 new UnsetUnnamedLocalThunklet(resultTempLocal
));
9959 emitVirtualLocal(resultTempLocal
);
9964 enterRegion(region
);
9965 SCOPE_EXIT
{ leaveRegion(region
); };
9966 if (fe
->getBody()) visit(fe
->getBody());
9969 // Continue iteration.
9975 emitVirtualLocal(resultTempLocal
);
9977 m_curFunc
->freeUnnamedLocal(resultTempLocal
);
9979 newFaultRegionAndFunclet(iterTempStartUse
, m_ue
.bcPos(),
9980 new UnsetUnnamedLocalThunklet(iterTempLocal
));
9981 emitVirtualLocal(iterTempLocal
);
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
9991 emitConvertToCell(e
);
9992 e
.ContAssignDelegate(itId
);
9994 Offset bDelegateAssigned
= m_ue
.bcPos();
9996 // Pass null to ContEnterDelegate initially.
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
,
10030 const StringData
* sd
= makeStaticString(msg
);
10035 Funclet
* EmitterVisitor::addFunclet(StatementPtr stmt
, Thunklet
* body
) {
10036 Funclet
* f
= addFunclet(body
);
10037 m_memoizedFunclets
.insert(std::make_pair(stmt
, 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
];
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
];
10062 f
->m_body
->emit(e
);
10064 f
->m_body
= nullptr;
10068 void EmitterVisitor::newFaultRegion(Offset start
,
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
,
10079 FaultIterInfo iter
) {
10080 Funclet
* f
= addFunclet(t
);
10081 newFaultRegion(start
, end
, &f
->m_entry
, iter
);
10084 void EmitterVisitor::newFaultRegionAndFunclet(StatementPtr stmt
,
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
);
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
));
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
);
10128 m_faultRegions
.clear();
10129 for (auto f
: m_funclets
) {
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
;
10145 m_fpiRegions
.clear();
10148 void EmitterVisitor::saveMaxStackCells(FuncEmitter
* fe
, int32_t stackPad
) {
10149 fe
->maxStackCells
+=
10150 fe
->numIterators() * kNumIterCells
+
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
) {
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) {
10171 if (m_retLocal
>= 0) {
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)));
10189 m_staticArrays
.push_back(Array::attach(PackedArray::MakeReserve(0)));
10191 m_staticColType
.push_back(k
);
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
));
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
);
10223 if (sval
->getInt(i
)) {
10224 tvVal
= make_tv
<KindOfInt64
>(i
);
10228 if (sval
->getDouble(d
)) {
10229 tvVal
= make_tv
<KindOfDouble
>(d
);
10235 case Expression::KindOfExpressionList
: {
10236 // Array, possibly for collection initialization.
10237 initArray(val
, kind
);
10240 case Expression::KindOfUnaryOpExpression
: {
10241 auto u
= static_pointer_cast
<UnaryOpExpression
>(val
);
10242 if (u
->getOp() == T_ARRAY
) {
10243 initArray(u
->getExpression(), folly::none
);
10246 if (u
->getOp() == T_DICT
) {
10247 initArray(u
->getExpression(), HeaderKind::Dict
);
10250 if (u
->getOp() == T_VEC
) {
10251 initArray(u
->getExpression(), HeaderKind::VecArray
);
10254 if (u
->getOp() == T_KEYSET
) {
10255 initArray(u
->getExpression(), HeaderKind::Keyset
);
10261 if (val
->getScalarValue(tvAsVariant(&tvVal
))) {
10262 if (tvAsVariant(&tvVal
).isArray()) {
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) {
10281 e
.Dict(staticEmptyDictArray());
10285 e
.Vec(staticEmptyVecArray());
10289 e
.Keyset(staticEmptyKeysetArray());
10292 e
.Array(staticEmptyArray());
10296 auto const scalar
=
10297 isDict
? isDictScalar(el
) :
10298 isKeyset
? isKeysetScalar(el
) :
10302 tvWriteUninit(&tv
);
10303 initScalar(tv
, el
, kind
);
10305 assert(tv
.m_data
.parr
->isDict());
10306 e
.Dict(tv
.m_data
.parr
);
10310 assert(tv
.m_data
.parr
->isVecArray());
10311 e
.Vec(tv
.m_data
.parr
);
10315 assert(tv
.m_data
.parr
->isKeyset());
10316 e
.Keyset(tv
.m_data
.parr
);
10319 assert(tv
.m_data
.parr
->isPHPArray());
10320 e
.Array(tv
.m_data
.parr
);
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
]);
10329 emitConvertToCell(e
);
10332 e
.NewVecArray(count
);
10334 e
.NewKeysetArray(count
);
10339 auto const allowPacked
= !kind
|| isVectorCollection((CollectionType
)*kind
);
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
);
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
);
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
);
10372 e
.NewMixedArray(capacityHint
);
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
));
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
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
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();
10430 throw IncludeTimeFatalException(ap
,
10431 "Keys may not be specified for Set initialization");
10433 if (!useArray
) continue;
10434 auto val
= ap
->getValue();
10436 if (val
->getScalarValue(v
)) {
10437 if (v
.isString()) {
10438 hasVectorData
= false;
10440 if (v
.getStringData()->isStrictlyInteger(intVal
)) {
10443 } else if (v
.isInteger()) {
10444 if (v
.asInt64Val() != i
) hasVectorData
= false;
10452 if (hasVectorData
) useArray
= false;
10455 emitArrayInit(e
, el
, (HeaderKind
)ct
);
10456 e
.ColFromArray(static_cast<int>(ct
));
10459 // Will use the static empty mixed array to avoid allocation.
10460 e
.NewCol(static_cast<int>(ct
));
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
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;
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;
10496 if (key
->getScalarValue(vkey
)) {
10497 if (vkey
.isString()) {
10498 hasVectorData
= false;
10500 if (vkey
.getStringData()->isStrictlyInteger(intKey
)) {
10503 } else if (vkey
.isInteger()) {
10504 auto val
= vkey
.asInt64Val();
10505 if (val
> max
|| val
< 0) {
10506 hasVectorData
= false;
10507 } else if (val
== max
) {
10517 if (hasVectorData
) useArray
= false;
10520 emitArrayInit(e
, el
, (HeaderKind
)ct
);
10521 e
.ColFromArray(static_cast<int>(ct
));
10524 e
.NewCol(static_cast<int>(ct
));
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
);
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
);
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());
10554 if (ct
== CollectionType::Pair
) {
10555 throw IncludeTimeFatalException(b
, "Initializer needed for Pair object");
10557 e
.NewCol(static_cast<int>(*ct
));
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
);
10580 bool EmitterVisitor::requiresDeepInit(ExpressionPtr initExpr
) const {
10581 switch (initExpr
->getKindOf()) {
10582 case Expression::KindOfScalarExpression
:
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());
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
))) {
10603 } else if (u
->getOp() == T_VEC
|| u
->getOp() == T_KEYSET
) {
10604 auto el
= static_pointer_cast
<ExpressionList
>(u
->getExpression());
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;
10613 } else if (u
->getOp() == '+' || u
->getOp() == '-') {
10614 return requiresDeepInit(u
->getExpression());
10615 } else if (u
->getOp() == T_FILE
|| u
->getOp() == T_DIR
) {
10620 case Expression::KindOfBinaryOpExpression
: {
10621 auto b
= static_pointer_cast
<BinaryOpExpression
>(initExpr
);
10622 return requiresDeepInit(b
->getExp1()) || requiresDeepInit(b
->getExp2());
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
);
10655 return ConstructPtr();
10658 static UnitEmitter
* emitHHBCUnitEmitter(AnalysisResultPtr ar
, FileScopePtr fsp
,
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
);
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
);
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
;
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
);
10696 enum GeneratorMethod
{
10707 typedef hphp_hash_map
<const StringData
*, GeneratorMethod
,
10708 string_data_hash
, string_data_same
> ContMethMap
;
10709 typedef std::map
<StaticString
, GeneratorMethod
> ContMethMapT
;
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
,
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
10763 // If it hasn't started, perform one "next" operation before
10764 // the actual operation (auto-priming)
10765 e
.ContCheck(false);
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.
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
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
);
10795 ue
.emitOp(OpContEnter
);
10798 ue
.emitOp(OpPushL
); ue
.emitIVA(0);
10799 ue
.emitOp(OpContEnter
);
10802 ue
.emitOp(OpPushL
); ue
.emitIVA(0);
10803 ue
.emitOp(OpContRaise
);
10809 // Backtrace has off-by-one bug when determining whether we are
10810 // in returning opcode; add Nop to avoid it
10816 ue
.emitOp(OpContValid
);
10820 case METH_CURRENT
: {
10821 ue
.emitOp(OpContCurrent
);
10826 ue
.emitOp(OpContKey
);
10830 case METH_REWIND
: {
10835 case METH_GETRETURN
: {
10836 ue
.emitOp(OpContGetReturn
);
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
,
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());
10898 Logger::Error("Unable to open %s for write", fullPath
.c_str());
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());
10905 LitstrTable::get().setWriting();
10906 LitstrTable::get().mutex().unlock();
10912 struct UEQ
: Synchronizable
{
10913 void push(UnitEmitter
* ue
) {
10914 assert(ue
!= nullptr);
10916 m_ues
.push_back(ue
);
10919 UnitEmitter
* tryPop(long sec
, long long nsec
) {
10921 if (m_ues
.empty()) {
10922 // Check for empty() after wait(), in case of spurious wakeup.
10923 if (!wait(sec
, nsec
) || m_ues
.empty()) {
10927 assert(m_ues
.size() > 0);
10928 UnitEmitter
* ue
= m_ues
.front();
10929 assert(ue
!= nullptr);
10934 std::deque
<UnitEmitter
*> m_ues
;
10938 class EmitterWorker
10939 : public JobQueueWorker
<FileScopeRawPtr
, void*, true, true> {
10941 EmitterWorker() : m_ret(true) {}
10942 void doJob(JobType job
) override
{
10944 AnalysisResultPtr ar
= ((AnalysisResult
*)m_context
)->shared_from_this();
10945 UnitEmitter
* ue
= emitHHBCVisitor(ar
, job
);
10946 if (Option::GenerateBinaryHHBC
) {
10951 } catch (Exception
&e
) {
10952 Logger::Error("%s", e
.getMessage().c_str());
10955 Logger::Error("Fatal: An unexpected exception was thrown");
10959 void onThreadEnter() override
{
10960 g_context
.getCheck();
10962 void onThreadExit() override
{
10963 hphp_memory_cleanup();
10969 static void addEmitterWorker(AnalysisResultPtr ar
, StatementPtr sp
,
10971 ((JobQueueDispatcher
<EmitterWorker
>*)data
)->enqueue(sp
->getFileScope());
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 */
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
11059 bool inShutdown
= false;
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
));
11073 inShutdown
= dispatcher
.pollEmpty();
11074 } else if (!didPop
) {
11075 assert(Option::UseHHBBC
|| ues
.size() == 0);
11080 if (!Option::UseHHBBC
) {
11081 commitGlobalData(std::unique_ptr
<ArrayTypeTable::Builder
>{});
11084 dispatcher
.waitEmpty();
11087 assert(Option::UseHHBBC
|| ues
.empty());
11092 if (!Option::UseHHBBC
) {
11093 batchCommit(std::move(ues
));
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
));
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");
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
);
11141 UnitEmitter
* useExternalEmitter(const char* code
, int codeLen
,
11142 const char* filename
, const MD5
& md5
) {
11144 Logger::Error("The external emitter is not supported under MSVC!");
11147 std::string hhas
, errorOutput
;
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
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
11176 if (!RuntimeOption::EvalExternalEmitterFallback
) {
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",
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
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();
11229 SCOPE_ASSERT_DETAIL("hphp_compiler_parse") { return filename
; };
11232 UnitOrigin unitOrigin
= UnitOrigin::File
;
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.
11254 !RuntimeOption::EvalUseExternalEmitter
.empty() &&
11255 isFileHack(code
, codeLen
,
11256 RuntimeOption::EvalExternalEmitterAllowPartial
) &&
11257 SystemLib::s_inited
) {
11258 ue
.reset(useExternalEmitter(code
, codeLen
, filename
, md5
));
11262 auto parseit
= [=] (AnalysisResultPtr ar
) {
11263 Scanner
scanner(code
, codeLen
,
11264 RuntimeOption::GetScannerType(), filename
);
11265 Parser
parser(scanner
, filename
, ar
, codeLen
);
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();
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...
11306 ///////////////////////////////////////////////////////////////////////////////