2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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"
19 #include <unordered_map>
22 #include <boost/variant.hpp>
27 #include <unordered_set>
31 #include <folly/gen/Base.h>
32 #include <folly/gen/String.h>
33 #include <folly/Memory.h>
34 #include <folly/ScopeGuard.h>
35 #include <folly/sorted_vector_types.h>
37 #include "hphp/runtime/base/repo-auth-type-codec.h"
38 #include "hphp/runtime/base/repo-auth-type.h"
39 #include "hphp/runtime/ext/std/ext_std_misc.h"
40 #include "hphp/runtime/vm/func-emitter.h"
41 #include "hphp/runtime/vm/hhbc-codec.h"
42 #include "hphp/runtime/vm/preclass-emitter.h"
43 #include "hphp/runtime/vm/unit-emitter.h"
45 #include "hphp/hhbbc/cfg.h"
46 #include "hphp/hhbbc/eval-cell.h"
47 #include "hphp/hhbbc/func-util.h"
48 #include "hphp/hhbbc/optimize.h"
49 #include "hphp/hhbbc/representation.h"
50 #include "hphp/hhbbc/unit-util.h"
52 namespace HPHP
{ namespace HHBBC
{
54 TRACE_SET_MOD(hhbbc_parse
);
58 //////////////////////////////////////////////////////////////////////
60 const StaticString
s_Closure("Closure");
61 const StaticString
s_toString("__toString");
62 const StaticString
s_Stringish("Stringish");
63 const StaticString
s_XHPChild("XHPChild");
64 const StaticString
s_attr_Deprecated("__Deprecated");
65 const StaticString
s_class_alias("class_alias");
66 const StaticString
s___NoContextSensitiveAnalysis(
67 "__NoContextSensitiveAnalysis");
69 //////////////////////////////////////////////////////////////////////
71 void record_const_init(php::Program
& prog
, php::Func
* func
) {
72 auto id
= prog
.nextConstInit
.fetch_add(1, std::memory_order_relaxed
);
73 prog
.constInits
.ensureSize(id
+ 1);
75 DEBUG_ONLY
auto const oldVal
= prog
.constInits
.exchange(id
, func
);
79 //////////////////////////////////////////////////////////////////////
81 struct ParseUnitState
{
82 std::atomic
<uint32_t>& nextFuncId
;
85 * This is computed once for each unit and stashed here. We support
86 * having either a SourceLocTable or a LineTable. If we're
87 * optimizing a repo that was already created by hphpc, it won't
88 * have the full SourceLocTable information in it, so we're limited
91 boost::variant
< SourceLocTable
96 * Map from class id to the function containing its DefCls
97 * instruction. We use this to compute whether classes are defined
100 * TODO_4: if we don't end up with a use for this, remove it.
102 std::vector
<php::Func
*> defClsMap
;
105 * Map from Closure index to the function(s) containing their
106 * associated CreateCl opcode(s).
110 hphp_fast_set
<php::Func
*>
114 size_t operator()(const php::SrcLoc
& sl
) const {
115 auto const h1
= ((size_t)sl
.start
.col
<< 32) | sl
.start
.line
;
116 auto const h2
= ((size_t)sl
.past
.col
<< 32) | sl
.past
.line
;
117 return hash_int64_pair(h1
, h2
);
120 hphp_fast_map
<php::SrcLoc
, int32_t, SrcLocHash
> srcLocs
;
123 * Set of functions that should be processed in the constant
126 * Must include every function with a DefCns for correctness; cinit,
127 * pinit and sinit functions are added to improve overall
130 hphp_fast_set
<php::Func
*> constPassFuncs
;
133 * List of class aliases defined by this unit
135 CompactVector
<std::pair
<SString
,SString
>> classAliases
;
138 //////////////////////////////////////////////////////////////////////
140 std::set
<Offset
> findBasicBlocks(const FuncEmitter
& fe
) {
141 std::set
<Offset
> blockStarts
;
142 auto markBlock
= [&] (Offset off
) { blockStarts
.insert(off
); };
144 // Each entry point for a DV funclet is the start of a basic
146 for (auto& param
: fe
.params
) {
147 if (param
.hasDefaultValue()) markBlock(param
.funcletOff
);
150 // The main entry point is also a basic block start.
153 bool traceBc
= false;
156 * For each instruction, add it to the set if it must be the start
157 * of a block. It is the start of a block if it is:
161 * - Immediatelly following a control flow instruction, other than
164 auto offset
= fe
.base
;
166 auto const bc
= fe
.ue().bc();
167 auto const pc
= bc
+ offset
;
168 auto const nextOff
= offset
+ instrLen(pc
);
169 auto const atLast
= nextOff
== fe
.past
;
170 auto const op
= peek_op(pc
);
171 auto const breaksBB
=
172 instrIsNonCallControlFlow(op
) ||
173 instrFlags(op
) & TF
||
174 (hasFCallEffects(op
) && !instrJumpOffsets(pc
).empty());
176 if (options
.TraceBytecodes
.count(op
)) traceBc
= true;
178 if (breaksBB
&& !atLast
) {
182 auto const targets
= instrJumpTargets(bc
, offset
);
183 for (auto const& target
: targets
) markBlock(target
);
190 * Find blocks associated with exception handlers.
192 * - The start of each EH protected region begins a block.
194 * - The instruction immediately after the end of any
195 * EH protected region begins a block.
197 * - Each catch entry point begins a block.
199 * - The instruction immediately after the end of any
200 * catch region begins a block.
202 for (auto& eh
: fe
.ehtab
) {
203 markBlock(eh
.m_base
);
204 markBlock(eh
.m_past
);
205 markBlock(eh
.m_handler
);
206 if (eh
.m_end
!= kInvalidOffset
) {
211 // Now, each interval in blockStarts delinates a basic block.
212 blockStarts
.insert(fe
.past
);
215 FTRACE(0, "TraceBytecode (parse): {}::{} in {}\n",
216 fe
.pce() ? fe
.pce()->name()->data() : "",
217 fe
.name
, fe
.ue().m_filepath
);
225 * Map from EHEnt to the ExnNode that will represent exception
226 * behavior in that region.
228 hphp_fast_map
<const EHEnt
*,ExnNodeId
> ehMap
;
231 template<class FindBlock
>
232 ExnTreeInfo
build_exn_tree(const FuncEmitter
& fe
,
234 FindBlock findBlock
) {
236 func
.exnNodes
.reserve(fe
.ehtab
.size());
237 for (auto& eh
: fe
.ehtab
) {
238 auto const catchBlk
= findBlock(eh
.m_handler
, true);
239 auto node
= php::ExnNode
{};
240 node
.idx
= func
.exnNodes
.size();
241 node
.parent
= NoExnNodeId
;
242 node
.depth
= 1; // 0 depth means no ExnNode
243 node
.region
= php::CatchRegion
{ catchBlk
, eh
.m_iterId
};
244 ret
.ehMap
[&eh
] = node
.idx
;
246 if (eh
.m_parentIndex
!= -1) {
247 auto it
= ret
.ehMap
.find(&fe
.ehtab
[eh
.m_parentIndex
]);
248 assert(it
!= end(ret
.ehMap
));
249 assert(it
->second
< node
.idx
);
250 node
.parent
= it
->second
;
251 auto& parent
= func
.exnNodes
[node
.parent
];
252 node
.depth
= parent
.depth
+ 1;
253 parent
.children
.emplace_back(node
.idx
);
255 func
.exnNodes
.emplace_back(std::move(node
));
261 template<class T
> T
decode(PC
& pc
) {
262 auto const ret
= *reinterpret_cast<const T
*>(pc
);
267 template<class T
> void decode(PC
& pc
, T
& val
) {
271 MKey
make_mkey(const php::Func
& /*func*/, MemberKey mk
) {
274 return MKey
{mk
.mcode
, static_cast<LocalId
>(mk
.iva
)};
276 return MKey
{mk
.mcode
, mk
.iva
};
277 case MET
: case MPT
: case MQT
:
278 return MKey
{mk
.mcode
, mk
.litstr
};
280 return MKey
{mk
.mcode
, mk
.int64
};
287 template<class FindBlock
>
288 void populate_block(ParseUnitState
& puState
,
289 const FuncEmitter
& fe
,
294 FindBlock findBlock
) {
295 auto const& ue
= fe
.ue();
297 auto decode_stringvec
= [&] {
298 auto const vecLen
= decode_iva(pc
);
299 CompactVector
<LSString
> keys
;
300 for (auto i
= size_t{0}; i
< vecLen
; ++i
) {
301 keys
.push_back(ue
.lookupLitstr(decode
<int32_t>(pc
)));
306 auto decode_switch
= [&] (PC opPC
) {
308 auto const vecLen
= decode_iva(pc
);
309 for (int32_t i
= 0; i
< vecLen
; ++i
) {
310 ret
.push_back(findBlock(
311 opPC
+ decode
<Offset
>(pc
) - ue
.bc()
317 auto decode_sswitch
= [&] (PC opPC
) {
320 auto const vecLen
= decode_iva(pc
);
321 for (int32_t i
= 0; i
< vecLen
- 1; ++i
) {
322 auto const id
= decode
<Id
>(pc
);
323 auto const offset
= decode
<Offset
>(pc
);
324 ret
.emplace_back(ue
.lookupLitstr(id
),
325 findBlock(opPC
+ offset
- ue
.bc()));
328 // Final case is the default, and must have a litstr id of -1.
329 DEBUG_ONLY
auto const defId
= decode
<Id
>(pc
);
330 auto const defOff
= decode
<Offset
>(pc
);
332 ret
.emplace_back(nullptr, findBlock(opPC
+ defOff
- ue
.bc()));
336 auto decode_itertab
= [&] {
338 auto const vecLen
= decode_iva(pc
);
339 for (int32_t i
= 0; i
< vecLen
; ++i
) {
340 auto const kind
= static_cast<IterKind
>(decode_iva(pc
));
341 auto const id
= decode_iva(pc
);
342 auto const local
= [&]{
343 if (kind
!= KindOfLIter
) return NoLocalId
;
344 auto const loc
= decode_iva(pc
);
345 always_assert(loc
< func
.locals
.size());
348 ret
.push_back(IterTabEnt
{kind
, static_cast<IterId
>(id
), local
});
353 auto decode_argv32
= [&] {
354 CompactVector
<uint32_t> ret
;
355 auto const vecLen
= decode_iva(pc
);
356 for (uint32_t i
= 0; i
< vecLen
; ++i
) {
357 ret
.emplace_back(decode
<uint32_t>(pc
));
362 auto defcns
= [&] () {
363 puState
.constPassFuncs
.insert(&func
);
365 auto defcls
= [&] (const Bytecode
& b
) {
366 puState
.defClsMap
[b
.DefCls
.arg1
] = &func
;
368 auto defclsnop
= [&] (const Bytecode
& b
) {
369 puState
.defClsMap
[b
.DefClsNop
.arg1
] = &func
;
371 auto aliascls
= [&] (const Bytecode
& b
) {
372 puState
.classAliases
.emplace_back(b
.AliasCls
.str1
, b
.AliasCls
.str2
);
374 auto createcl
= [&] (const Bytecode
& b
) {
375 puState
.createClMap
[b
.CreateCl
.arg2
].insert(&func
);
377 auto fpushfuncd
= [&] (const Bytecode
& b
) {
378 if (b
.FPushFuncD
.str2
== s_class_alias
.get()) {
379 puState
.constPassFuncs
.insert(&func
);
382 auto has_call_unpack
= [&] {
383 auto const fpi
= Func::findFPI(&*fe
.fpitab
.begin(),
384 &*fe
.fpitab
.end(), pc
- ue
.bc());
385 auto pc
= ue
.bc() + fpi
->m_fpiEndOff
;
386 auto const op
= decode_op(pc
);
387 if (!isLegacyFCall(op
)) return false;
388 return decodeFCallArgs(pc
).hasUnpack();
391 #define IMM_BLA(n) auto targets = decode_switch(opPC);
392 #define IMM_SLA(n) auto targets = decode_sswitch(opPC);
393 #define IMM_ILA(n) auto iterTab = decode_itertab();
394 #define IMM_I32LA(n) auto argv = decode_argv32();
395 #define IMM_IVA(n) auto arg##n = decode_iva(pc);
396 #define IMM_I64A(n) auto arg##n = decode<int64_t>(pc);
397 #define IMM_LA(n) auto loc##n = [&] { \
398 LocalId id = decode_iva(pc); \
399 always_assert(id < func.locals.size()); \
402 #define IMM_IA(n) auto iter##n = [&] { \
403 IterId id = decode_iva(pc); \
404 always_assert(id < func.numIters); \
407 #define IMM_CAR(n) auto slot = [&] { \
408 ClsRefSlotId id = decode_iva(pc); \
409 always_assert(id >= 0 && id < func.numClsRefSlots); \
412 #define IMM_CAW(n) auto slot = [&] { \
413 ClsRefSlotId id = decode_iva(pc); \
414 always_assert(id >= 0 && id < func.numClsRefSlots); \
417 #define IMM_DA(n) auto dbl##n = decode<double>(pc);
418 #define IMM_SA(n) auto str##n = ue.lookupLitstr(decode<Id>(pc));
419 #define IMM_RATA(n) auto rat = decodeRAT(ue, pc);
420 #define IMM_AA(n) auto arr##n = ue.lookupArray(decode<Id>(pc));
421 #define IMM_BA(n) assert(next == past); \
422 auto target##n = findBlock( \
423 opPC + decode<Offset>(pc) - ue.bc());
424 #define IMM_OA_IMPL(n) subop##n; decode(pc, subop##n);
425 #define IMM_OA(type) type IMM_OA_IMPL
426 #define IMM_VSA(n) auto keys = decode_stringvec();
427 #define IMM_KA(n) auto mkey = make_mkey(func, decode_member_key(pc, &ue));
428 #define IMM_LAR(n) auto locrange = [&] { \
429 auto const range = decodeLocalRange(pc); \
430 always_assert(range.first + range.count \
431 <= func.locals.size()); \
432 return LocalRange { range.first, range.count }; \
434 #define IMM_FCA(n) auto fca = [&] { \
435 auto const fca = decodeFCallArgs(pc); \
436 auto const numBytes = (fca.numArgs + 7) / 8; \
437 auto byRefs = fca.enforceReffiness() \
438 ? std::make_unique<uint8_t[]>(numBytes) \
441 memcpy(byRefs.get(), fca.byRefs, numBytes); \
443 auto const aeOffset = fca.asyncEagerOffset; \
444 auto const aeTarget = aeOffset != kInvalidOffset \
445 ? findBlock(opPC + aeOffset - ue.bc()) \
447 assertx(aeTarget == NoBlockId || next == past); \
448 return FCallArgs(fca.flags, fca.numArgs, \
449 fca.numRets, std::move(byRefs), \
454 #define IMM_ONE(x) IMM_##x(1)
455 #define IMM_TWO(x, y) IMM_##x(1) IMM_##y(2)
456 #define IMM_THREE(x, y, z) IMM_TWO(x, y) IMM_##z(3)
457 #define IMM_FOUR(x, y, z, n) IMM_THREE(x, y, z) IMM_##n(4)
458 #define IMM_FIVE(x, y, z, n, m) IMM_FOUR(x, y, z, n) IMM_##m(5)
460 #define IMM_ARG(which, n) IMM_NAME_##which(n)
462 #define IMM_ARG_ONE(x) IMM_ARG(x, 1)
463 #define IMM_ARG_TWO(x, y) IMM_ARG(x, 1), IMM_ARG(y, 2)
464 #define IMM_ARG_THREE(x, y, z) IMM_ARG(x, 1), IMM_ARG(y, 2), \
466 #define IMM_ARG_FOUR(x, y, z, l) IMM_ARG(x, 1), IMM_ARG(y, 2), \
467 IMM_ARG(z, 3), IMM_ARG(l, 4)
468 #define IMM_ARG_FIVE(x, y, z, l, m) IMM_ARG(x, 1), IMM_ARG(y, 2), \
469 IMM_ARG(z, 3), IMM_ARG(l, 4), \
476 #define FLAGS_PF auto hu = has_call_unpack();
484 #define FLAGS_ARG_PF ,hu
485 #define FLAGS_ARG_CF_TF
486 #define FLAGS_ARG_CF_FF
488 #define O(opcode, imms, inputs, outputs, flags) \
491 auto b = [&] () -> Bytecode { \
492 IMM_##imms /*these two macros advance the pc as required*/ \
494 if (isTypeAssert(op)) return bc::Nop {}; \
495 return bc::opcode { IMM_ARG_##imms FLAGS_ARG_##flags }; \
497 b.srcLoc = srcLocIx; \
498 if (Op::opcode == Op::DefCns) defcns(); \
499 if (Op::opcode == Op::DefCls) defcls(b); \
500 if (Op::opcode == Op::DefClsNop) defclsnop(b); \
501 if (Op::opcode == Op::AliasCls) aliascls(b); \
502 if (Op::opcode == Op::CreateCl) createcl(b); \
503 if (Op::opcode == Op::FPushFuncD) fpushfuncd(b); \
504 blk.hhbcs.push_back(std::move(b)); \
505 assert(pc == next); \
511 auto const opPC
= pc
;
512 auto const next
= pc
+ instrLen(opPC
);
513 assert(next
<= past
);
515 auto const srcLoc
= match
<php::SrcLoc
>(
517 [&] (const SourceLocTable
& tab
) {
519 if (getSourceLoc(tab
, opPC
- ue
.bc(), sloc
)) {
521 { static_cast<uint32_t>(sloc
.line0
),
522 static_cast<uint32_t>(sloc
.char0
) },
523 { static_cast<uint32_t>(sloc
.line1
),
524 static_cast<uint32_t>(sloc
.char1
) }
527 return php::SrcLoc
{};
529 [&] (const LineTable
& tab
) {
530 auto const line
= getLineNumber(tab
, opPC
- ue
.bc());
533 { static_cast<uint32_t>(line
), 0 },
534 { static_cast<uint32_t>(line
), 0 },
537 return php::SrcLoc
{};
541 auto const srcLocIx
= puState
.srcLocs
.emplace(
542 srcLoc
, puState
.srcLocs
.size()).first
->second
;
544 auto const op
= decode_op(pc
);
545 switch (op
) { OPCODES
}
548 if (instrAllowsFallThru(op
)) {
549 blk
.fallthrough
= findBlock(next
- ue
.bc());
554 } while (pc
!= past
);
571 #undef FLAGS_ARG_CF_TF
572 #undef FLAGS_ARG_CF_FF
611 * If a block ends with an unconditional jump, change it to a
614 * If the jmp is the only instruction, convert it to a Nop, to avoid
615 * creating an empty block (we have an invariant that no blocks are
619 auto make_fallthrough
= [&] {
620 blk
.fallthrough
= blk
.hhbcs
.back().Jmp
.target1
;
621 if (blk
.hhbcs
.size() == 1) {
622 blk
.hhbcs
.back() = bc_with_loc(blk
.hhbcs
.back().srcLoc
, bc::Nop
{});
624 blk
.hhbcs
.pop_back();
628 switch (blk
.hhbcs
.back().op
) {
629 case Op::Jmp
: make_fallthrough(); break;
630 case Op::JmpNS
: make_fallthrough(); blk
.fallthroughNS
= true; break;
635 template<class FindBlk
>
636 void link_entry_points(php::Func
& func
,
637 const FuncEmitter
& fe
,
639 func
.dvEntries
.resize(fe
.params
.size(), NoBlockId
);
640 for (size_t i
= 0, sz
= fe
.params
.size(); i
< sz
; ++i
) {
641 if (fe
.params
[i
].hasDefaultValue()) {
642 auto const dv
= findBlock(fe
.params
[i
].funcletOff
);
643 func
.params
[i
].dvEntryPoint
= dv
;
644 func
.dvEntries
[i
] = dv
;
647 func
.mainEntry
= findBlock(fe
.base
);
650 void build_cfg(ParseUnitState
& puState
,
652 const FuncEmitter
& fe
) {
653 auto const blockStarts
= findBasicBlocks(fe
);
655 FTRACE(3, " blocks are at: {}\n",
656 [&]() -> std::string
{
657 using namespace folly::gen
;
658 return from(blockStarts
)
659 | eachTo
<std::string
>()
660 | unsplit
<std::string
>(" ");
664 std::map
<Offset
,std::pair
<BlockId
, copy_ptr
<php::Block
>>> blockMap
;
665 auto const bc
= fe
.ue().bc();
667 auto findBlock
= [&] (Offset off
, bool catchEntry
= false) {
668 auto& ent
= blockMap
[off
];
670 auto blk
= php::Block
{};
671 ent
.first
= blockMap
.size() - 1;
672 blk
.exnNodeId
= NoExnNodeId
;
673 blk
.catchEntry
= catchEntry
;
674 ent
.second
.emplace(std::move(blk
));
675 } else if (catchEntry
) {
676 ent
.second
.mutate()->catchEntry
= true;
681 auto exnTreeInfo
= build_exn_tree(fe
, func
, findBlock
);
683 hphp_fast_map
<BlockId
, std::pair
<int, int>> predSuccCounts
;
685 for (auto it
= begin(blockStarts
);
686 std::next(it
) != end(blockStarts
);
688 auto const bid
= findBlock(*it
);
689 auto const block
= blockMap
[*it
].second
.mutate();
690 auto const bcStart
= bc
+ *it
;
691 auto const bcStop
= bc
+ *std::next(it
);
693 if (auto const eh
= Func::findEH(fe
.ehtab
, *it
)) {
694 auto it
= exnTreeInfo
.ehMap
.find(eh
);
695 assert(it
!= end(exnTreeInfo
.ehMap
));
696 block
->exnNodeId
= it
->second
;
697 block
->throwExit
= func
.exnNodes
[it
->second
].region
.catchEntry
;
700 populate_block(puState
, fe
, func
, *block
, bcStart
, bcStop
, findBlock
);
701 forEachNonThrowSuccessor(*block
, [&] (BlockId blkId
) {
702 predSuccCounts
[blkId
].first
++;
703 predSuccCounts
[bid
].second
++;
707 link_entry_points(func
, fe
, findBlock
);
709 func
.blocks
.resize(blockMap
.size());
710 for (auto& kv
: blockMap
) {
711 auto const blk
= kv
.second
.second
.mutate();
712 auto const id
= kv
.second
.first
;
713 blk
->multiSucc
= predSuccCounts
[id
].second
> 1;
714 blk
->multiPred
= predSuccCounts
[id
].first
> 1;
715 func
.blocks
[id
] = std::move(kv
.second
.second
);
719 void add_frame_variables(php::Func
& func
, const FuncEmitter
& fe
) {
720 for (auto& param
: fe
.params
) {
721 func
.params
.push_back(
725 param
.typeConstraint
,
728 param
.userAttributes
,
737 func
.locals
.reserve(fe
.numLocals());
738 for (LocalId id
= 0; id
< fe
.numLocals(); ++id
) {
739 func
.locals
.push_back({nullptr, id
, false});
741 for (auto& kv
: fe
.localNameMap()) {
742 func
.locals
[kv
.second
].name
= kv
.first
;
745 func
.numIters
= fe
.numIterators();
746 func
.numClsRefSlots
= fe
.numClsRefSlots();
749 std::unique_ptr
<php::Func
> parse_func(ParseUnitState
& puState
,
752 const FuncEmitter
& fe
) {
753 FTRACE(2, " func: {}\n",
754 fe
.name
->data() && *fe
.name
->data() ? fe
.name
->data() : "pseudomain");
756 auto ret
= std::make_unique
<php::Func
>();
757 ret
->idx
= puState
.nextFuncId
.fetch_add(1, std::memory_order_relaxed
);
759 ret
->srcInfo
= php::SrcInfo
{ fe
.getLocation(),
764 ret
->attrs
= static_cast<Attr
>(fe
.attrs
& ~AttrNoOverride
);
765 ret
->userAttributes
= fe
.userAttributes
;
766 ret
->returnUserType
= fe
.retUserType
;
767 ret
->retTypeConstraint
= fe
.retTypeConstraint
;
768 ret
->originalFilename
= fe
.originalFilename
;
771 ret
->isClosureBody
= fe
.isClosureBody
;
772 ret
->isAsync
= fe
.isAsync
;
773 ret
->isGenerator
= fe
.isGenerator
;
774 ret
->isPairGenerator
= fe
.isPairGenerator
;
775 ret
->isMemoizeWrapper
= fe
.isMemoizeWrapper
;
776 ret
->isMemoizeWrapperLSB
= fe
.isMemoizeWrapperLSB
;
777 ret
->isMemoizeImpl
= Func::isMemoizeImplName(fe
.name
);
778 ret
->isReified
= fe
.userAttributes
.find(s___Reified
.get()) !=
779 fe
.userAttributes
.end();
780 ret
->isRxDisabled
= fe
.isRxDisabled
;
781 ret
->noContextSensitiveAnalysis
= fe
.userAttributes
.find(
782 s___NoContextSensitiveAnalysis
.get()) != fe
.userAttributes
.end();
784 add_frame_variables(*ret
, fe
);
786 if (!RuntimeOption::ConstantFunctions
.empty()) {
787 auto const name
= [&] {
788 if (!cls
) return fe
.name
->toCppString();
789 return folly::sformat("{}::{}", cls
->name
, ret
->name
);
791 auto const it
= RuntimeOption::ConstantFunctions
.find(name
);
792 if (it
!= RuntimeOption::ConstantFunctions
.end()) {
793 ret
->locals
.resize(fe
.params
.size());
795 ret
->numClsRefSlots
= 0;
796 ret
->attrs
|= AttrIsFoldable
;
798 auto const mainEntry
= BlockId
{0};
800 auto blk
= php::Block
{};
801 blk
.exnNodeId
= NoExnNodeId
;
803 blk
.hhbcs
.push_back(gen_constant(it
->second
));
804 blk
.hhbcs
.push_back(bc::RetC
{});
805 ret
->blocks
.emplace_back(std::move(blk
));
807 ret
->dvEntries
.resize(fe
.params
.size(), NoBlockId
);
808 ret
->mainEntry
= mainEntry
;
810 for (size_t i
= 0, sz
= fe
.params
.size(); i
< sz
; ++i
) {
811 if (fe
.params
[i
].hasDefaultValue()) {
812 ret
->params
[i
].dvEntryPoint
= mainEntry
;
813 ret
->dvEntries
[i
] = mainEntry
;
821 * Builtin functions get some extra information. The returnType flag is only
822 * non-folly::none for these, but note that something may be a builtin and
823 * still have a folly::none return type.
826 auto const f
= [&] () -> HPHP::Func
* {
828 auto const cls
= Unit::lookupClass(ret
->cls
->name
);
829 return cls
? cls
->lookupMethod(ret
->name
) : nullptr;
831 return Unit::lookupBuiltin(ret
->name
);
835 ret
->nativeInfo
= std::make_unique
<php::NativeInfo
>();
836 ret
->nativeInfo
->returnType
= fe
.hniReturnType
;
837 if (f
&& ret
->params
.size()) {
838 for (auto i
= 0; i
< ret
->params
.size(); i
++) {
839 auto& pi
= ret
->params
[i
];
840 if (pi
.isVariadic
|| !f
->params()[i
].hasDefaultValue()) continue;
841 if (pi
.defaultValue
.m_type
== KindOfUninit
&&
842 pi
.phpCode
!= nullptr) {
843 auto res
= eval_cell_value([&] {
844 auto val
= f_constant(StrNR(pi
.phpCode
));
846 return *val
.asTypedValue();
849 FTRACE(4, "Argument {} to {}: Failed to evaluate {}\n",
850 i
, f
->fullName(), pi
.phpCode
);
853 pi
.defaultValue
= *res
;
857 if (!f
|| !f
->nativeFuncPtr() ||
858 (f
->userAttributes().count(
859 LowStringPtr(s_attr_Deprecated
.get())))) {
860 ret
->attrs
|= AttrNoFCallBuiltin
;
864 build_cfg(puState
, *ret
, fe
);
869 void parse_methods(ParseUnitState
& puState
,
872 const PreClassEmitter
& pce
) {
873 std::unique_ptr
<php::Func
> cinit
;
874 for (auto& me
: pce
.methods()) {
875 auto f
= parse_func(puState
, unit
, ret
, *me
);
876 if (f
->name
== s_86cinit
.get()) {
877 puState
.constPassFuncs
.insert(f
.get());
878 cinit
= std::move(f
);
880 if (f
->name
== s_86pinit
.get() ||
881 f
->name
== s_86sinit
.get() ||
882 f
->name
== s_86linit
.get()) {
883 puState
.constPassFuncs
.insert(f
.get());
885 ret
->methods
.push_back(std::move(f
));
888 if (cinit
) ret
->methods
.push_back(std::move(cinit
));
891 void add_stringish(php::Class
* cls
) {
892 // The runtime adds Stringish to any class providing a __toString() function,
893 // so we mirror that here to make sure analysis of interfaces is correct.
894 // All Stringish are also XHPChild, so handle it here as well.
895 if (cls
->attrs
& AttrInterface
&& cls
->name
->isame(s_Stringish
.get())) {
900 for (auto& iface
: cls
->interfaceNames
) {
901 if (iface
->isame(s_Stringish
.get())) return;
902 if (iface
->isame(s_XHPChild
.get())) { hasXHP
= true; }
905 for (auto& func
: cls
->methods
) {
906 if (func
->name
->isame(s_toString
.get())) {
907 FTRACE(2, "Adding Stringish and XHPChild to {}\n", cls
->name
->data());
908 cls
->interfaceNames
.push_back(s_Stringish
.get());
909 if (!hasXHP
&& !cls
->name
->isame(s_XHPChild
.get())) {
910 cls
->interfaceNames
.push_back(s_XHPChild
.get());
917 std::unique_ptr
<php::Record
> parse_record(php::Unit
* unit
,
918 const RecordEmitter
& re
) {
919 FTRACE(2, " record: {}\n", re
.name()->data());
921 auto ret
= std::make_unique
<php::Record
>();
923 ret
->srcInfo
= php::SrcInfo
{re
.getLocation(), re
.docComment()};
924 ret
->name
= re
.name();
925 ret
->attrs
= static_cast<Attr
>(re
.attrs() & ~AttrNoOverride
);
926 ret
->parentName
= re
.parentName();
928 ret
->userAttributes
= re
.userAttributes();
930 auto& fieldMap
= re
.fieldMap();
931 for (size_t idx
= 0; idx
< fieldMap
.size(); ++idx
) {
932 auto& field
= fieldMap
[idx
];
933 ret
->fields
.push_back(
940 field
.typeConstraint(),
941 field
.userAttributes()
948 std::unique_ptr
<php::Class
> parse_class(ParseUnitState
& puState
,
950 const PreClassEmitter
& pce
) {
951 FTRACE(2, " class: {}\n", pce
.name()->data());
953 auto ret
= std::make_unique
<php::Class
>();
954 ret
->name
= pce
.name();
955 ret
->srcInfo
= php::SrcInfo
{ pce
.getLocation(),
958 ret
->closureContextCls
= nullptr;
959 ret
->parentName
= pce
.parentName()->empty() ? nullptr
961 ret
->attrs
= static_cast<Attr
>(pce
.attrs() & ~AttrNoOverride
);
962 ret
->hoistability
= pce
.hoistability();
963 ret
->userAttributes
= pce
.userAttributes();
965 ret
->hasReifiedGenerics
= ret
->userAttributes
.find(s___Reified
.get()) !=
966 ret
->userAttributes
.end();
968 for (auto& iface
: pce
.interfaces()) {
969 ret
->interfaceNames
.push_back(iface
);
972 copy(ret
->usedTraitNames
, pce
.usedTraits());
973 copy(ret
->traitPrecRules
, pce
.traitPrecRules());
974 copy(ret
->traitAliasRules
, pce
.traitAliasRules());
975 copy(ret
->requirements
, pce
.requirements());
977 parse_methods(puState
, ret
.get(), unit
, pce
);
978 add_stringish(ret
.get());
980 auto& propMap
= pce
.propMap();
981 for (size_t idx
= 0; idx
< propMap
.size(); ++idx
) {
982 auto& prop
= propMap
[idx
];
983 ret
->properties
.push_back(
987 prop
.userAttributes(),
990 prop
.typeConstraint(),
996 auto& constMap
= pce
.constMap();
997 for (size_t idx
= 0; idx
< constMap
.size(); ++idx
) {
998 auto& cconst
= constMap
[idx
];
999 // Set all constants as NoOverride, we'll clear this while building
1001 ret
->constants
.push_back(
1007 cconst
.typeConstraint(),
1008 cconst
.isTypeconst(),
1014 if (ret
->attrs
& AttrBuiltin
) {
1015 if (auto nativeConsts
= Native::getClassConstants(ret
->name
)) {
1016 for (auto const& cnsMap
: *nativeConsts
) {
1017 TypedValueAux tvaux
;
1018 tvCopy(cnsMap
.second
, tvaux
);
1019 tvaux
.constModifiers() = {};
1020 ret
->constants
.push_back(
1025 staticEmptyString(),
1026 staticEmptyString(),
1034 ret
->enumBaseTy
= pce
.enumBaseTy();
1039 //////////////////////////////////////////////////////////////////////
1041 void assign_closure_context(const ParseUnitState
&, php::Class
*);
1044 find_closure_context(const ParseUnitState
& puState
,
1045 php::Func
* createClFunc
) {
1046 if (auto const cls
= createClFunc
->cls
) {
1047 if (cls
->parentName
&&
1048 cls
->parentName
->isame(s_Closure
.get())) {
1049 // We have a closure created by a closure's invoke method, which
1050 // means it should inherit the outer closure's context, so we
1051 // have to know that first.
1052 assign_closure_context(puState
, cls
);
1053 return cls
->closureContextCls
;
1060 void assign_closure_context(const ParseUnitState
& puState
,
1062 if (clo
->closureContextCls
) return;
1064 auto clIt
= puState
.createClMap
.find(clo
->id
);
1065 if (clIt
== end(puState
.createClMap
)) {
1066 // Unused closure class. Technically not prohibited by the spec.
1071 * Any route to the closure context must yield the same class, or
1072 * things downstream won't understand. We try every route and
1073 * assert they are all the same here.
1075 * See bytecode.specification for CreateCl for the relevant
1078 always_assert(!clIt
->second
.empty());
1079 auto it
= begin(clIt
->second
);
1080 auto const representative
= find_closure_context(puState
, *it
);
1082 for (++it
; it
!= end(clIt
->second
); ++it
) {
1083 assert(find_closure_context(puState
, *it
) == representative
);
1086 clo
->closureContextCls
= representative
;
1089 void find_additional_metadata(const ParseUnitState
& puState
,
1091 for (auto& c
: unit
->classes
) {
1092 if (!c
->parentName
|| !c
->parentName
->isame(s_Closure
.get())) {
1095 assign_closure_context(puState
, c
.get());
1099 //////////////////////////////////////////////////////////////////////
1103 std::unique_ptr
<php::Unit
> parse_unit(php::Program
& prog
,
1104 std::unique_ptr
<UnitEmitter
> uep
) {
1105 Trace::Bump bumper
{Trace::hhbbc_parse
, kSystemLibBump
, uep
->isASystemLib()};
1106 FTRACE(2, "parse_unit {}\n", uep
->m_filepath
->data());
1108 if (RuntimeOption::EvalAbortBuildOnVerifyError
&& !uep
->check(false)) {
1111 "The unoptimized unit for %s did not pass verification, "
1112 "bailing because Eval.AbortBuildOnVerifyError is set\n",
1113 uep
->m_filepath
->data()
1118 auto const& ue
= *uep
;
1120 auto ret
= std::make_unique
<php::Unit
>();
1121 ret
->sha1
= ue
.sha1();
1122 ret
->filename
= ue
.m_filepath
;
1123 ret
->isHHFile
= ue
.m_isHHFile
;
1124 ret
->metaData
= ue
.m_metaData
;
1125 ret
->fileAttributes
= ue
.m_fileAttributes
;
1127 ParseUnitState puState
{ prog
.nextFuncId
};
1128 if (ue
.hasSourceLocInfo()) {
1129 puState
.srcLocInfo
= ue
.createSourceLocTable();
1131 puState
.srcLocInfo
= ue
.lineTable();
1133 puState
.defClsMap
.resize(ue
.numPreClasses(), nullptr);
1135 for (size_t i
= 0; i
< ue
.numPreClasses(); ++i
) {
1136 auto cls
= parse_class(puState
, ret
.get(), *ue
.pce(i
));
1137 ret
->classes
.push_back(std::move(cls
));
1140 for (size_t i
= 0; i
< ue
.numRecords(); ++i
) {
1141 auto rec
= parse_record(ret
.get(), *ue
.re(i
));
1142 ret
->records
.push_back(std::move(rec
));
1145 for (auto& fe
: ue
.fevec()) {
1146 auto func
= parse_func(puState
, ret
.get(), nullptr, *fe
);
1148 if (fe
->isPseudoMain()) {
1149 ret
->pseudomain
= std::move(func
);
1151 ret
->funcs
.push_back(std::move(func
));
1155 ret
->srcLocs
.resize(puState
.srcLocs
.size());
1156 for (auto& srcInfo
: puState
.srcLocs
) {
1157 ret
->srcLocs
[srcInfo
.second
] = srcInfo
.first
;
1160 for (auto& ta
: ue
.typeAliases()) {
1161 ret
->typeAliases
.push_back(
1162 std::make_unique
<php::TypeAlias
>(ta
)
1166 ret
->classAliases
= std::move(puState
.classAliases
);
1168 find_additional_metadata(puState
, ret
.get());
1170 for (auto const item
: puState
.constPassFuncs
) {
1171 record_const_init(prog
, item
);
1174 assert(check(*ret
));
1178 //////////////////////////////////////////////////////////////////////