2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2015 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/hhbbc/parse.h"
20 #include <unordered_map>
23 #include <boost/variant.hpp>
28 #include <unordered_set>
32 #include <folly/gen/Base.h>
33 #include <folly/gen/String.h>
34 #include <folly/ScopeGuard.h>
35 #include <folly/Memory.h>
37 #include "hphp/runtime/base/repo-auth-type-codec.h"
38 #include "hphp/runtime/base/repo-auth-type.h"
39 #include "hphp/runtime/vm/func-emitter.h"
40 #include "hphp/runtime/vm/hhbc-codec.h"
41 #include "hphp/runtime/vm/preclass-emitter.h"
42 #include "hphp/runtime/vm/unit-emitter.h"
44 #include "hphp/hhbbc/representation.h"
45 #include "hphp/hhbbc/cfg.h"
46 #include "hphp/hhbbc/unit-util.h"
48 namespace HPHP
{ namespace HHBBC
{
54 //////////////////////////////////////////////////////////////////////
56 const StaticString
s_Closure("Closure");
57 const StaticString
s_toString("__toString");
58 const StaticString
s_Stringish("Stringish");
60 //////////////////////////////////////////////////////////////////////
62 struct ParseUnitState
{
64 * This is computed once for each unit and stashed here. We support
65 * having either a SourceLocTable or a LineTable. If we're
66 * optimizing a repo that was already created by hphpc, it won't
67 * have the full SourceLocTable information in it, so we're limited
70 boost::variant
< SourceLocTable
75 * Map from class id to the function containing its DefCls
76 * instruction. We use this to compute whether classes are defined
79 * TODO_4: if we don't end up with a use for this, remove it.
81 std::vector
<borrowed_ptr
<php::Func
>> defClsMap
;
84 * Map from Closure names to the function(s) containing their
85 * associated CreateCl opcode(s).
89 std::unordered_set
<borrowed_ptr
<php::Func
>>,
95 //////////////////////////////////////////////////////////////////////
97 std::set
<Offset
> findBasicBlocks(const FuncEmitter
& fe
) {
98 std::set
<Offset
> blockStarts
;
99 auto markBlock
= [&] (Offset off
) { blockStarts
.insert(off
); };
101 // Each entry point for a DV funclet is the start of a basic
103 for (auto& param
: fe
.params
) {
104 if (param
.hasDefaultValue()) markBlock(param
.funcletOff
);
107 // The main entry point is also a basic block start.
111 * For each instruction, add it to the set if it must be the start
112 * of a block. It is the start of a block if it is:
116 * - Immediatelly following a control flow instruction, other than
119 auto offset
= fe
.base
;
121 auto const bc
= fe
.ue().bc();
122 auto const pc
= bc
+ offset
;
123 auto const nextOff
= offset
+ instrLen(pc
);
124 auto const atLast
= nextOff
== fe
.past
;
125 auto const op
= peek_op(pc
);
126 auto const breaksBB
= instrIsNonCallControlFlow(op
) || instrFlags(op
) & TF
;
128 if (breaksBB
&& !atLast
) {
133 foreachSwitchTarget(pc
, [&] (Offset delta
) {
134 markBlock(offset
+ delta
);
137 auto const target
= instrJumpTarget(bc
, offset
);
138 if (target
!= InvalidAbsoluteOffset
) markBlock(target
);
146 * Find blocks associated with exception handlers.
148 * - The start of each fault-protected region begins a block.
150 * - The instruction immediately after the end of any
151 * fault-protected region begins a block.
153 * - Each fault or catch entry point begins a block.
155 for (auto& eh
: fe
.ehtab
) {
156 markBlock(eh
.m_base
);
157 markBlock(eh
.m_past
);
159 case EHEnt::Type::Catch
:
160 for (auto& centry
: eh
.m_catches
) markBlock(centry
.second
);
162 case EHEnt::Type::Fault
:
163 markBlock(eh
.m_fault
);
168 // Now, each interval in blockStarts delinates a basic block.
169 blockStarts
.insert(fe
.past
);
175 * Map from EHEnt to the ExnNode that will represent exception
176 * behavior in that region.
178 std::map
<const EHEntEmitter
*,borrowed_ptr
<php::ExnNode
>> ehMap
;
181 * Fault funclets don't actually fall in the EHEnt region for all of
182 * their parent handlers in HHBC. There may be EHEnt regions
183 * covering the fault funclet, but if an exception occurs in the
184 * funclet it can also propagate to any EH region from the code that
185 * entered the funclet. We want factored exit edges from the fault
186 * funclets to any of these enclosing catch blocks (or other
187 * enclosing funclet blocks).
189 * Moreover, funclet offsets can be entered from multiple protected
190 * regions, so we need to keep a map of all the possible regions
191 * that could have entered a given funclet, so we can add exit edges
192 * to all their parent EHEnt handlers.
194 std::map
<borrowed_ptr
<php::Block
>,std::vector
<borrowed_ptr
<php::ExnNode
>>>
198 * Keep track of the start offsets for all fault funclets. This is
199 * used to find the extents of each handler for find_fault_funclets.
200 * It is assumed that each fault funclet handler extends from its
201 * entry offset until the next fault funclet entry offset (or end of
204 * This relies on the following bytecode invariants:
206 * - All fault funclets come after the primary function body.
208 * - Each fault funclet is a contiguous region of bytecode that
209 * does not jump into other fault funclets or into the primary
212 * - Nothing comes after the fault funclets.
214 std::set
<Offset
> faultFuncletStarts
;
217 template<class FindBlock
>
218 ExnTreeInfo
build_exn_tree(const FuncEmitter
& fe
,
220 FindBlock findBlock
) {
222 auto nextExnNode
= uint32_t{0};
224 for (auto& eh
: fe
.ehtab
) {
225 auto node
= folly::make_unique
<php::ExnNode
>();
226 node
->id
= nextExnNode
++;
227 node
->parent
= nullptr;
230 case EHEnt::Type::Fault
:
232 auto const fault
= findBlock(eh
.m_fault
);
233 ret
.funcletNodes
[fault
].push_back(borrow(node
));
234 ret
.faultFuncletStarts
.insert(eh
.m_fault
);
235 node
->info
= php::FaultRegion
{ fault
, eh
.m_iterId
, eh
.m_itRef
};
238 case EHEnt::Type::Catch
:
240 auto treg
= php::TryRegion
{};
241 for (auto& centry
: eh
.m_catches
) {
242 auto const catchBlk
= findBlock(centry
.second
);
243 treg
.catches
.emplace_back(
244 fe
.ue().lookupLitstr(centry
.first
),
253 ret
.ehMap
[&eh
] = borrow(node
);
255 if (eh
.m_parentIndex
!= -1) {
256 auto it
= ret
.ehMap
.find(&fe
.ehtab
[eh
.m_parentIndex
]);
257 assert(it
!= end(ret
.ehMap
));
258 node
->parent
= it
->second
;
259 it
->second
->children
.emplace_back(std::move(node
));
261 func
.exnNodes
.emplace_back(std::move(node
));
265 ret
.faultFuncletStarts
.insert(fe
.past
);
271 * Instead of breaking blocks on instructions that could throw, we
272 * represent the control flow edges for exception paths as a set of
273 * factored edges at the end of each block.
275 * When we initially add them here, no attempt is made to determine if
276 * the edge is actually possible to traverse.
278 void add_factored_exits(php::Block
& blk
,
279 borrowed_ptr
<const php::ExnNode
> node
) {
280 for (; node
; node
= node
->parent
) {
283 [&] (const php::TryRegion
& tr
) {
285 * Note: it seems like we should be able to stop adding edges
286 * when we see a catch handler for Exception; however, fatal
287 * errors don't stop there (and still run Fault handlers).
289 * For now we add all the edges, although we might be able to be
290 * less pessimistic later.
292 for (auto& c
: tr
.catches
) {
293 blk
.factoredExits
.push_back(c
.second
);
296 [&] (const php::FaultRegion
& fr
) {
297 blk
.factoredExits
.push_back(fr
.faultEntry
);
304 * Locate all the basic blocks associated with fault funclets, and
305 * mark them as such. Also, add factored exit edges for exceptional
306 * control flow through any parent protected regions of the region(s)
307 * that pointed at each fault handler.
309 template<class BlockStarts
, class FindBlock
>
310 void find_fault_funclets(ExnTreeInfo
& tinfo
,
311 const php::Func
& func
,
312 const BlockStarts
& blockStarts
,
313 FindBlock findBlock
) {
314 auto sectionId
= uint32_t{1};
316 for (auto funcletStartIt
= begin(tinfo
.faultFuncletStarts
);
317 std::next(funcletStartIt
) != end(tinfo
.faultFuncletStarts
);
318 ++funcletStartIt
, ++sectionId
) {
319 auto const nextFunclet
= *std::next(funcletStartIt
);
321 auto offIt
= blockStarts
.find(*funcletStartIt
);
322 assert(offIt
!= end(blockStarts
));
324 auto const firstBlk
= findBlock(*offIt
);
325 auto const funcletIt
= tinfo
.funcletNodes
.find(firstBlk
);
326 assert(funcletIt
!= end(tinfo
.funcletNodes
));
327 assert(!funcletIt
->second
.empty());
330 auto const blk
= findBlock(*offIt
);
331 blk
->section
= static_cast<php::Block::Section
>(sectionId
);
333 // Propagate the exit edges to the containing fault/try handlers,
334 // if there were any.
335 for (auto& node
: funcletIt
->second
) {
336 add_factored_exits(*blk
, node
->parent
);
339 // Fault funclets can have protected regions which may point to
340 // handlers that are also listed in parents of the EH-region that
341 // targets the funclet. This means we might have duplicate
342 // factored exits now, so we need to remove them.
343 std::sort(begin(blk
->factoredExits
), end(blk
->factoredExits
));
344 blk
->factoredExits
.erase(
345 std::unique(begin(blk
->factoredExits
), end(blk
->factoredExits
)),
346 end(blk
->factoredExits
)
350 } while (offIt
!= end(blockStarts
) && *offIt
< nextFunclet
);
354 template<class T
> T
decode(PC
& pc
) {
355 auto const ret
= *reinterpret_cast<const T
*>(pc
);
360 template<class T
> void decode(PC
& pc
, T
& val
) {
364 template<class FindBlock
>
365 void populate_block(ParseUnitState
& puState
,
366 const FuncEmitter
& fe
,
371 FindBlock findBlock
) {
372 auto const& ue
= fe
.ue();
374 auto decode_minstr
= [&] {
375 auto const immVec
= ImmVector::createFromStream(pc
);
376 pc
+= immVec
.size() + sizeof(int32_t) + sizeof(int32_t);
378 auto ret
= MVector
{};
379 auto vec
= immVec
.vec();
381 ret
.lcode
= static_cast<LocationCode
>(*vec
++);
382 if (numLocationCodeImms(ret
.lcode
)) {
383 assert(numLocationCodeImms(ret
.lcode
) == 1);
384 ret
.locBase
= borrow(func
.locals
[decodeVariableSizeImm(&vec
)]);
389 elm
.mcode
= static_cast<MemberCode
>(*vec
++);
390 switch (memberCodeImmType(elm
.mcode
)) {
391 case MCodeImm::None
: break;
392 case MCodeImm::Local
:
393 elm
.immLoc
= borrow(func
.locals
[decodeMemberCodeImm(&vec
, elm
.mcode
)]);
395 case MCodeImm::String
:
396 elm
.immStr
= ue
.lookupLitstr(decodeMemberCodeImm(&vec
, elm
.mcode
));
399 elm
.immInt
= decodeMemberCodeImm(&vec
, elm
.mcode
);
402 ret
.mcodes
.push_back(elm
);
409 auto decode_stringvec
= [&] {
410 auto const vecLen
= decode
<int32_t>(pc
);
411 std::vector
<SString
> keys
;
412 for (auto i
= size_t{0}; i
< vecLen
; ++i
) {
413 keys
.push_back(ue
.lookupLitstr(decode
<int32_t>(pc
)));
418 auto decode_switch
= [&] (PC opPC
) {
420 auto const vecLen
= decode
<int32_t>(pc
);
421 for (int32_t i
= 0; i
< vecLen
; ++i
) {
422 ret
.push_back(findBlock(
423 opPC
+ decode
<Offset
>(pc
) - ue
.bc()
429 auto decode_sswitch
= [&] (PC opPC
) {
432 auto const vecLen
= decode
<int32_t>(pc
);
433 for (int32_t i
= 0; i
< vecLen
- 1; ++i
) {
434 auto const id
= decode
<Id
>(pc
);
435 auto const offset
= decode
<Offset
>(pc
);
438 findBlock(opPC
+ offset
- ue
.bc())
442 // Final case is the default, and must have a litstr id of -1.
443 DEBUG_ONLY
auto const defId
= decode
<Id
>(pc
);
444 auto const defOff
= decode
<Offset
>(pc
);
446 ret
.emplace_back(nullptr, findBlock(opPC
+ defOff
- ue
.bc()));
450 auto decode_itertab
= [&] {
452 auto const vecLen
= decode
<int32_t>(pc
);
453 for (int32_t i
= 0; i
< vecLen
; ++i
) {
454 auto const kind
= static_cast<IterKind
>(decode
<int32_t>(pc
));
455 auto const id
= decode
<int32_t>(pc
);
456 ret
.emplace_back(kind
, borrow(func
.iters
[id
]));
461 auto defcls
= [&] (const Bytecode
& b
) {
462 puState
.defClsMap
[b
.DefCls
.arg1
] = &func
;
464 auto defclsnop
= [&] (const Bytecode
& b
) {
465 puState
.defClsMap
[b
.DefClsNop
.arg1
] = &func
;
467 auto createcl
= [&] (const Bytecode
& b
) {
468 puState
.createClMap
[b
.CreateCl
.str2
].insert(&func
);
471 #define IMM_MA(n) auto mvec = decode_minstr();
472 #define IMM_BLA(n) auto targets = decode_switch(opPC);
473 #define IMM_SLA(n) auto targets = decode_sswitch(opPC);
474 #define IMM_ILA(n) auto iterTab = decode_itertab();
475 #define IMM_IVA(n) auto arg##n = decodeVariableSizeImm(&pc);
476 #define IMM_I64A(n) auto arg##n = decode<int64_t>(pc);
477 #define IMM_LA(n) auto loc##n = [&] { \
478 auto id = decodeVariableSizeImm(&pc); \
479 always_assert(id < func.locals.size()); \
480 return borrow(func.locals[id]); \
482 #define IMM_IA(n) auto iter##n = [&] { \
483 auto id = decodeVariableSizeImm(&pc); \
484 always_assert(id < func.iters.size()); \
485 return borrow(func.iters[id]); \
487 #define IMM_DA(n) auto dbl##n = decode<double>(pc);
488 #define IMM_SA(n) auto str##n = ue.lookupLitstr(decode<Id>(pc));
489 #define IMM_RATA(n) auto rat = decodeRAT(ue, pc);
490 #define IMM_AA(n) auto arr##n = ue.lookupArray(decode<Id>(pc));
491 #define IMM_BA(n) assert(next == past); \
492 auto target = findBlock( \
493 opPC + decode<Offset>(pc) - ue.bc());
494 #define IMM_OA_IMPL(n) subop##n; decode(pc, subop##n);
495 #define IMM_OA(type) type IMM_OA_IMPL
496 #define IMM_VSA(n) auto keys = decode_stringvec();
499 #define IMM_ONE(x) IMM_##x(1)
500 #define IMM_TWO(x, y) IMM_##x(1) IMM_##y(2)
501 #define IMM_THREE(x, y, z) IMM_TWO(x, y) IMM_##z(3)
502 #define IMM_FOUR(x, y, z, n) IMM_THREE(x, y, z) IMM_##n(4)
504 #define IMM_ARG(which, n) IMM_NAME_##which(n)
506 #define IMM_ARG_ONE(x) IMM_ARG(x, 1)
507 #define IMM_ARG_TWO(x, y) IMM_ARG(x, 1), IMM_ARG(y, 2)
508 #define IMM_ARG_THREE(x, y, z) IMM_ARG(x, 1), IMM_ARG(y, 2), \
510 #define IMM_ARG_FOUR(x, y, z, l) IMM_ARG(x, 1), IMM_ARG(y, 2), \
511 IMM_ARG(z, 3), IMM_ARG(l, 4)
514 #define O(opcode, imms, inputs, outputs, flags) \
517 auto b = Bytecode {}; \
521 new (&b.opcode) bc::opcode { IMM_ARG_##imms }; \
522 if (Op::opcode == Op::DefCls) defcls(b); \
523 if (Op::opcode == Op::DefClsNop) defclsnop(b); \
524 if (Op::opcode == Op::CreateCl) createcl(b); \
525 blk.hhbcs.push_back(std::move(b)); \
526 assert(pc == next); \
532 auto const opPC
= pc
;
533 auto const next
= pc
+ instrLen(opPC
);
534 assert(next
<= past
);
536 auto const srcLoc
= match
<php::SrcLoc
>(
538 [&] (const SourceLocTable
& tab
) {
540 if (getSourceLoc(tab
, opPC
- ue
.bc(), sloc
)) {
542 { static_cast<uint32_t>(sloc
.line0
),
543 static_cast<uint32_t>(sloc
.char0
) },
544 { static_cast<uint32_t>(sloc
.line1
),
545 static_cast<uint32_t>(sloc
.char1
) }
548 return php::SrcLoc
{};
550 [&] (const LineTable
& tab
) {
551 auto const line
= getLineNumber(tab
, opPC
- ue
.bc());
554 { static_cast<uint32_t>(line
), 0 },
555 { static_cast<uint32_t>(line
), 0 },
558 return php::SrcLoc
{};
562 auto const op
= decode_op(pc
);
563 switch (op
) { OPCODES
}
566 if (instrAllowsFallThru(op
)) {
567 blk
.fallthrough
= findBlock(next
- ue
.bc());
572 } while (pc
!= past
);
607 * If a block ends with an unconditional jump, change it to a
610 * Just convert the opcode to a Nop, because this could create an
611 * empty block and we have an invariant that no blocks are empty.
614 auto make_fallthrough
= [&] {
615 blk
.fallthrough
= blk
.hhbcs
.back().Jmp
.target
;
616 blk
.hhbcs
.back() = bc_with_loc(blk
.hhbcs
.back().srcLoc
, bc::Nop
{});
619 switch (blk
.hhbcs
.back().op
) {
620 case Op::Jmp
: make_fallthrough(); break;
621 case Op::JmpNS
: make_fallthrough(); blk
.fallthroughNS
= true; break;
626 template<class FindBlk
>
627 void link_entry_points(php::Func
& func
,
628 const FuncEmitter
& fe
,
630 func
.dvEntries
.resize(fe
.params
.size());
631 for (size_t i
= 0, sz
= fe
.params
.size(); i
< sz
; ++i
) {
632 if (fe
.params
[i
].hasDefaultValue()) {
633 auto const dv
= findBlock(fe
.params
[i
].funcletOff
);
634 func
.params
[i
].dvEntryPoint
= dv
;
635 func
.dvEntries
[i
] = dv
;
638 func
.mainEntry
= findBlock(fe
.base
);
641 void build_cfg(ParseUnitState
& puState
,
643 const FuncEmitter
& fe
) {
644 auto const blockStarts
= findBasicBlocks(fe
);
646 FTRACE(3, " blocks are at: {}\n",
647 [&]() -> std::string
{
648 using namespace folly::gen
;
649 return from(blockStarts
)
650 | eachTo
<std::string
>()
651 | unsplit
<std::string
>(" ");
655 std::map
<Offset
,std::unique_ptr
<php::Block
>> blockMap
;
656 auto const bc
= fe
.ue().bc();
658 auto findBlock
= [&] (Offset off
) {
659 auto& ptr
= blockMap
[off
];
661 ptr
= folly::make_unique
<php::Block
>();
662 ptr
->id
= func
.nextBlockId
++;
663 ptr
->section
= php::Block::Section::Main
;
664 ptr
->exnNode
= nullptr;
669 auto exnTreeInfo
= build_exn_tree(fe
, func
, findBlock
);
671 for (auto it
= begin(blockStarts
);
672 std::next(it
) != end(blockStarts
);
674 auto const block
= findBlock(*it
);
675 auto const bcStart
= bc
+ *it
;
676 auto const bcStop
= bc
+ *std::next(it
);
678 if (auto const eh
= findEH(fe
.ehtab
, *it
)) {
679 auto it
= exnTreeInfo
.ehMap
.find(eh
);
680 assert(it
!= end(exnTreeInfo
.ehMap
));
681 block
->exnNode
= it
->second
;
682 add_factored_exits(*block
, block
->exnNode
);
685 populate_block(puState
, fe
, func
, *block
, bcStart
, bcStop
, findBlock
);
688 link_entry_points(func
, fe
, findBlock
);
689 find_fault_funclets(exnTreeInfo
, func
, blockStarts
, findBlock
);
691 for (auto& kv
: blockMap
) {
692 func
.blocks
.emplace_back(std::move(kv
.second
));
696 void add_frame_variables(php::Func
& func
, const FuncEmitter
& fe
) {
697 for (auto& param
: fe
.params
) {
698 func
.params
.push_back(
702 param
.typeConstraint
,
705 param
.userAttributes
,
713 func
.locals
.resize(fe
.numLocals());
714 for (size_t id
= 0; id
< func
.locals
.size(); ++id
) {
715 auto& loc
= func
.locals
[id
];
716 loc
= folly::make_unique
<php::Local
>();
720 for (auto& kv
: fe
.localNameMap()) {
721 func
.locals
[kv
.second
]->name
= kv
.first
;
724 func
.iters
.resize(fe
.numIterators());
725 for (uint32_t i
= 0; i
< func
.iters
.size(); ++i
) {
726 func
.iters
[i
] = folly::make_unique
<php::Iter
>();
727 func
.iters
[i
]->id
= i
;
730 func
.staticLocals
.reserve(fe
.staticVars
.size());
731 for (auto& sv
: fe
.staticVars
) {
732 func
.staticLocals
.push_back(
733 php::StaticLocalInfo
{ sv
.name
, sv
.phpCode
}
738 std::unique_ptr
<php::Func
> parse_func(ParseUnitState
& puState
,
739 borrowed_ptr
<php::Unit
> unit
,
740 borrowed_ptr
<php::Class
> cls
,
741 const FuncEmitter
& fe
) {
742 FTRACE(2, " func: {}\n",
743 fe
.name
->data() && *fe
.name
->data() ? fe
.name
->data() : "pseudomain");
745 auto ret
= folly::make_unique
<php::Func
>();
747 ret
->srcInfo
= php::SrcInfo
{ fe
.getLocation(),
751 ret
->nextBlockId
= 0;
753 ret
->attrs
= static_cast<Attr
>(fe
.attrs
& ~AttrNoOverride
);
754 ret
->userAttributes
= fe
.userAttributes
;
755 ret
->returnUserType
= fe
.retUserType
;
756 ret
->retTypeConstraint
= fe
.retTypeConstraint
;
757 ret
->originalFilename
= fe
.originalFilename
;
760 ret
->isClosureBody
= fe
.isClosureBody
;
761 ret
->isAsync
= fe
.isAsync
;
762 ret
->isGenerator
= fe
.isGenerator
;
763 ret
->isPairGenerator
= fe
.isPairGenerator
;
766 * Builtin functions get some extra information. The returnType flag is only
767 * non-folly::none for these, but note that something may be a builtin and
768 * still have a folly::none return type.
770 if (fe
.attrs
& AttrBuiltin
) {
771 ret
->nativeInfo
= folly::make_unique
<php::NativeInfo
>();
772 ret
->nativeInfo
->returnType
= fe
.returnType
;
775 add_frame_variables(*ret
, fe
);
776 build_cfg(puState
, *ret
, fe
);
781 void parse_methods(ParseUnitState
& puState
,
782 borrowed_ptr
<php::Class
> ret
,
783 borrowed_ptr
<php::Unit
> unit
,
784 const PreClassEmitter
& pce
) {
785 for (auto& me
: pce
.methods()) {
786 auto f
= parse_func(puState
, unit
, ret
, *me
);
787 ret
->methods
.push_back(std::move(f
));
791 void add_stringish(borrowed_ptr
<php::Class
> cls
) {
792 // The runtime adds Stringish to any class providing a __toString() function,
793 // so we mirror that here to make sure analysis of interfaces is correct.
794 if (cls
->attrs
& AttrInterface
&& cls
->name
->isame(s_Stringish
.get())) {
798 for (auto& iface
: cls
->interfaceNames
) {
799 if (iface
->isame(s_Stringish
.get())) return;
802 for (auto& func
: cls
->methods
) {
803 if (func
->name
->isame(s_toString
.get())) {
804 FTRACE(2, "Adding Stringish to {}\n", cls
->name
->data());
805 cls
->interfaceNames
.push_back(s_Stringish
.get());
811 std::unique_ptr
<php::Class
> parse_class(ParseUnitState
& puState
,
812 borrowed_ptr
<php::Unit
> unit
,
813 const PreClassEmitter
& pce
) {
814 FTRACE(2, " class: {}\n", pce
.name()->data());
816 auto ret
= folly::make_unique
<php::Class
>();
817 ret
->name
= pce
.name();
818 ret
->srcInfo
= php::SrcInfo
{ pce
.getLocation(),
821 ret
->closureContextCls
= nullptr;
822 ret
->parentName
= pce
.parentName()->empty() ? nullptr
824 ret
->attrs
= static_cast<Attr
>(pce
.attrs() & ~AttrNoOverride
);
825 ret
->hoistability
= pce
.hoistability();
826 ret
->userAttributes
= pce
.userAttributes();
828 for (auto& iface
: pce
.interfaces()) {
829 ret
->interfaceNames
.push_back(iface
);
832 ret
->usedTraitNames
= pce
.usedTraits();
833 ret
->traitPrecRules
= pce
.traitPrecRules();
834 ret
->traitAliasRules
= pce
.traitAliasRules();
835 ret
->requirements
= pce
.requirements();
836 ret
->numDeclMethods
= pce
.numDeclMethods();
838 parse_methods(puState
, borrow(ret
), unit
, pce
);
839 add_stringish(borrow(ret
));
841 auto& propMap
= pce
.propMap();
842 for (size_t idx
= 0; idx
< propMap
.size(); ++idx
) {
843 auto& prop
= propMap
[idx
];
844 ret
->properties
.push_back(
849 prop
.typeConstraint(),
855 auto& constMap
= pce
.constMap();
856 for (size_t idx
= 0; idx
< constMap
.size(); ++idx
) {
857 auto& cconst
= constMap
[idx
];
858 ret
->constants
.push_back(
864 cconst
.typeConstraint(),
870 ret
->enumBaseTy
= pce
.enumBaseTy();
875 //////////////////////////////////////////////////////////////////////
877 void assign_closure_context(const ParseUnitState
&, borrowed_ptr
<php::Class
>);
879 borrowed_ptr
<php::Class
>
880 find_closure_context(const ParseUnitState
& puState
,
881 borrowed_ptr
<php::Func
> createClFunc
) {
882 if (auto const cls
= createClFunc
->cls
) {
883 if (cls
->parentName
&&
884 cls
->parentName
->isame(s_Closure
.get())) {
885 // We have a closure created by a closure's invoke method, which
886 // means it should inherit the outer closure's context, so we
887 // have to know that first.
888 assign_closure_context(puState
, cls
);
889 return cls
->closureContextCls
;
896 void assign_closure_context(const ParseUnitState
& puState
,
897 borrowed_ptr
<php::Class
> clo
) {
898 if (clo
->closureContextCls
) return;
900 auto clIt
= puState
.createClMap
.find(clo
->name
);
901 if (clIt
== end(puState
.createClMap
)) {
902 // Unused closure class. Technically not prohibited by the spec.
907 * Any route to the closure context must yield the same class, or
908 * things downstream won't understand. We try every route and
909 * assert they are all the same here.
911 * See bytecode.specification for CreateCl for the relevant
914 always_assert(!clIt
->second
.empty());
915 auto it
= begin(clIt
->second
);
916 auto const representative
= find_closure_context(puState
, *it
);
919 for (; it
!= end(clIt
->second
); ++it
) {
920 assert(find_closure_context(puState
, *it
) == representative
);
923 clo
->closureContextCls
= representative
;
926 void find_additional_metadata(const ParseUnitState
& puState
,
927 borrowed_ptr
<php::Unit
> unit
) {
928 for (auto& c
: unit
->classes
) {
929 if (!c
->parentName
|| !c
->parentName
->isame(s_Closure
.get())) {
932 assign_closure_context(puState
, borrow(c
));
936 //////////////////////////////////////////////////////////////////////
940 std::unique_ptr
<php::Unit
> parse_unit(const UnitEmitter
& ue
) {
941 Trace::Bump bumper
{Trace::hhbbc
, kSystemLibBump
, ue
.isASystemLib()};
942 FTRACE(2, "parse_unit {}\n", ue
.m_filepath
->data());
944 auto ret
= folly::make_unique
<php::Unit
>();
946 ret
->filename
= ue
.m_filepath
;
947 ret
->preloadPriority
= ue
.m_preloadPriority
;
949 ParseUnitState puState
;
950 if (ue
.hasSourceLocInfo()) {
951 puState
.srcLocInfo
= ue
.createSourceLocTable();
953 puState
.srcLocInfo
= ue
.lineTable();
955 puState
.defClsMap
.resize(ue
.numPreClasses(), nullptr);
957 for (size_t i
= 0; i
< ue
.numPreClasses(); ++i
) {
958 auto cls
= parse_class(puState
, borrow(ret
), *ue
.pce(i
));
959 ret
->classes
.push_back(std::move(cls
));
962 for (auto& fe
: ue
.fevec()) {
963 auto func
= parse_func(puState
, borrow(ret
), nullptr, *fe
);
965 if (fe
->isPseudoMain()) {
966 ret
->pseudomain
= std::move(func
);
968 ret
->funcs
.push_back(std::move(func
));
972 for (auto& ta
: ue
.typeAliases()) {
973 ret
->typeAliases
.push_back(
974 folly::make_unique
<php::TypeAlias
>(ta
)
978 find_additional_metadata(puState
, borrow(ret
));
983 //////////////////////////////////////////////////////////////////////