naming 2/2 - use naming_db_path provider
[hiphop-php.git] / hphp / hhbbc / check.cpp
blob5d720c870f8ba8e9a4bcf1f62d801b5976e68600
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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>
18 #include <algorithm>
19 #include <iterator>
20 #include <set>
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 {
33 namespace {
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) {
41 if (b.dead) {
42 // the block was deleted
43 return true;
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");
54 });
57 // A block should either have a matching exnNodeId and throwExit, or neither
58 // should be set.
59 assert(b.throwExit == (b.exnNodeId != NoExnNodeId
60 ? f.exnNodes[b.exnNodeId].region.catchEntry
61 : NoBlockId));
63 return true;
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
73 // the func.
74 for (uint32_t i = 0; i < f.params.size(); ++i) {
75 assert(f.params[i].dvEntryPoint == f.dvEntries[i]);
78 return true;
81 // N is usually small ... ;)
82 template<class Container>
83 bool has_edge_linear(const Container& c,
84 BlockId target) {
85 return std::find(begin(c), end(c), target) != end(c);
88 void checkExnTreeBasic(const php::Func& f,
89 boost::dynamic_bitset<>& seenIds,
90 const ExnNode* node,
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
100 // a child.
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;
110 ExnNodeId idx{0};
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);
116 idx++;
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);
124 return true;
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) {
149 assert(f.cls &&
150 f.cls->parentName &&
151 f.cls->parentName->isame(s_Closure.get()));
154 assert(checkExnTree(f));
155 return true;
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);
166 assert(isClo);
168 if (isClo) {
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);
173 } else {
174 assert(!c.closureContextCls);
177 return true;
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));
184 return true;
187 bool check(const php::Program& p) {
188 for (DEBUG_ONLY auto& u : p.units) assert(check(*u));
189 return true;
192 //////////////////////////////////////////////////////////////////////