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 +----------------------------------------------------------------------+
17 #include <boost/dynamic_bitset.hpp>
22 #include <folly/gen/Base.h>
24 #include "hphp/runtime/vm/unit-util.h"
26 #include "hphp/hhbbc/representation.h"
27 #include "hphp/hhbbc/unit-util.h"
28 #include "hphp/hhbbc/cfg.h"
29 #include "hphp/hhbbc/class-util.h"
31 namespace HPHP
{ namespace HHBBC
{ namespace php
{
35 const StaticString
s_Closure("Closure");
36 const StaticString
s_invoke("__invoke");
38 //////////////////////////////////////////////////////////////////////
40 bool DEBUG_ONLY
checkBlock(const php::Func
& f
, const php::Block
& b
) {
42 // the block was deleted
45 assert(!b
.hhbcs
.empty());
47 // No instructions in the middle of a block should have taken edges,
48 // or be an unconditional Jmp.
49 for (auto it
= begin(b
.hhbcs
); it
!= end(b
.hhbcs
); ++it
) {
50 assert(it
->op
!= Op::Jmp
&& "unconditional Jmp mid-block");
51 if (std::next(it
) == end(b
.hhbcs
)) break;
52 forEachTakenEdge(*it
, [&](BlockId
/*blk*/) {
53 assert(!"Instruction in middle of block had a jump target");
57 // A block should either have a matching exnNodeId and throwExit, or neither
59 assert(b
.throwExit
== (b
.exnNodeId
!= NoExnNodeId
60 ? f
.exnNodes
[b
.exnNodeId
].region
.catchEntry
66 bool DEBUG_ONLY
checkParams(const php::Func
& f
) {
67 assert(f
.params
.size() <= f
.locals
.size());
68 for (uint32_t i
= 0; i
< f
.locals
.size(); ++i
) {
69 assert(f
.locals
[i
].id
== i
);
72 // dvInit pointers are consistent in the parameters vector and on
74 for (uint32_t i
= 0; i
< f
.params
.size(); ++i
) {
75 assert(f
.params
[i
].dvEntryPoint
== f
.dvEntries
[i
]);
81 // N is usually small ... ;)
82 template<class Container
>
83 bool has_edge_linear(const Container
& c
,
85 return std::find(begin(c
), end(c
), target
) != end(c
);
88 void checkExnTreeBasic(const php::Func
& f
,
89 boost::dynamic_bitset
<>& seenIds
,
91 ExnNodeId expectedParent
) {
92 // All exnNode ids must be unique.
93 if (seenIds
.size() < node
->idx
+ 1) {
94 seenIds
.resize(node
->idx
+ 1);
96 assert(!seenIds
[node
->idx
]);
97 seenIds
[node
->idx
] = true;
99 // Parent pointers should point to the node that has a given node as
101 assert(node
->parent
== expectedParent
);
103 for (auto& c
: node
->children
) {
104 checkExnTreeBasic(f
, seenIds
, &f
.exnNodes
[c
], node
->idx
);
108 bool DEBUG_ONLY
checkExnTree(const php::Func
& f
) {
109 boost::dynamic_bitset
<> seenIds
;
111 for (auto& n
: f
.exnNodes
) {
112 if (n
.parent
== NoExnNodeId
&& n
.idx
!= NoExnNodeId
) {
113 assertx(n
.idx
== idx
);
114 checkExnTreeBasic(f
, seenIds
, &n
, NoExnNodeId
);
119 // ExnNode ids are contiguous.
120 for (size_t i
= 0; i
< seenIds
.size(); ++i
) {
121 assert(seenIds
[i
] == true || f
.exnNodes
[i
].idx
== NoExnNodeId
);
127 bool DEBUG_ONLY
checkName(SString name
) {
128 return isNSNormalized(name
);
131 //////////////////////////////////////////////////////////////////////
135 bool check(const php::Func
& f
) {
136 assert(checkParams(f
));
137 assert(checkName(f
.name
));
138 for (DEBUG_ONLY
auto& block
: f
.blocks
) assert(checkBlock(f
, *block
));
141 * Some of these relationships may change as async/await
142 * implementation progresses. Asserting them now so they are
143 * revisited here if they aren't true anymore.
145 if (f
.isClosureBody
) assert(!f
.top
);
146 if (f
.isPairGenerator
) assert(f
.isGenerator
);
148 if (f
.isClosureBody
) {
151 f
.cls
->parentName
->isame(s_Closure
.get()));
154 assert(checkExnTree(f
));
158 bool check(const php::Class
& c
) {
159 assert(checkName(c
.name
));
160 for (DEBUG_ONLY
auto& m
: c
.methods
) assert(check(*m
));
162 // Some invariants about Closure classes.
163 auto const isClo
= is_closure(c
);
164 if (c
.closureContextCls
) {
165 assert(c
.closureContextCls
->unit
== c
.unit
);
169 assert(c
.methods
.size() == 1 || c
.methods
.size() == 2);
170 assert(c
.methods
[0]->name
->isame(s_invoke
.get()));
171 assert(c
.methods
[0]->isClosureBody
);
172 assert(c
.methods
.size() == 1 || (c
.methods
[1]->attrs
& AttrIsInOutWrapper
));
174 assert(!c
.closureContextCls
);
180 bool check(const php::Unit
& u
) {
181 assert(check(*u
.pseudomain
));
182 for (DEBUG_ONLY
auto& c
: u
.classes
) assert(check(*c
));
183 for (DEBUG_ONLY
auto& f
: u
.funcs
) assert(check(*f
));
187 bool check(const php::Program
& p
) {
188 for (DEBUG_ONLY
auto& u
: p
.units
) assert(check(*u
));
192 //////////////////////////////////////////////////////////////////////