Rewrite AllFuncs as an iterator function.
[hiphop-php.git] / hphp / runtime / vm / verifier / check-unit.cpp
blob48dc9218f5750779f8e8c38fd044419ca1e314b6
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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 +----------------------------------------------------------------------+
17 #include <stdio.h>
19 #include "hphp/runtime/vm/verifier/check.h"
20 #include "hphp/runtime/vm/verifier/cfg.h"
21 #include "hphp/runtime/vm/verifier/util.h"
22 #include "hphp/runtime/vm/verifier/pretty.h"
24 namespace HPHP {
25 namespace Verifier {
27 class UnitChecker {
28 public:
29 UnitChecker(const Unit*, bool verbose);
30 ~UnitChecker() {}
31 bool verify();
33 private:
34 template<class T> bool checkLiteral(size_t, const T*, const char*);
35 bool checkStrings();
36 bool checkArrays();
37 bool checkSourceLocs();
38 bool checkPreClasses();
39 bool checkFuncs();
40 bool checkBytecode();
41 bool checkMetadata();
43 private:
44 template<class... Args>
45 void error(const char* const fmt, Args&&... args) {
46 verify_error(m_unit, nullptr, fmt, std::forward<Args>(args)...);
49 private:
50 const Unit* m_unit;
51 bool m_verbose;
54 bool checkUnit(const Unit* unit, bool verbose) {
55 if (verbose) {
56 printf("verifying unit from %s\n", unit->filepath()->data());
58 return UnitChecker(unit, verbose).verify();
61 // Unit contents to check:
62 // o bc
63 // o lines
64 // o UnitLitStr table
65 // o UnitArray table
66 // o UnitSourceLoc table
67 // o Classes
68 // o Functions
70 UnitChecker::UnitChecker(const Unit* unit, bool verbose)
71 : m_unit(unit), m_verbose(verbose) {
74 bool UnitChecker::verify() {
75 return checkStrings() &&
76 checkArrays() &&
77 //checkSourceLocs() &&
78 //checkPreClasses() &&
79 checkBytecode() &&
80 //checkMetadata() &&
81 checkFuncs();
84 template<class LitType>
85 bool UnitChecker::checkLiteral(size_t id,
86 const LitType* lt,
87 const char* what) {
88 bool ok = true;
89 if (!lt) {
90 error("null %s id %zu in unit %s\n", what, id,
91 m_unit->md5().toString().c_str());
92 ok = false;
94 if (!lt->isStatic()) {
95 error("non-static %s id %zu in unit %s\n", what, id,
96 m_unit->md5().toString().c_str());
97 ok = false;
99 return ok;
102 bool UnitChecker::checkStrings() {
103 bool ok = true;
104 for (size_t i = 0, n = m_unit->numLitstrs(); i < n; ++i) {
105 ok &= checkLiteral(i, m_unit->lookupLitstrId(i), "string");
107 return ok;
108 // Notes
109 // * Any string in repo can be null. repo litstrId is checked on load
110 // then discarded. Each Litstr entry becomes a static string.
111 // Strings are hash-commoned so presumably only one can be null per unit.
112 // string_data_hash and string_data_same both crash/assert on null.
113 // * If DB has dups then UnitEmitter commons them - spec should outlaw
114 // dups because UE will assert if db has dups with different ids.
115 // StringData statically keeps a map of loaded static strings
116 // * UE keeps a (String->id) mapping and assigns dups the same id, plus
117 // a table of litstrs indexed by id. Unit stores them as
118 // m_namedInfo, a vector<StringData,NamedEntity=null> of pairs.
119 // * are null characters allowed inside the string?
120 // * are strings utf8-encoded?
123 bool UnitChecker::checkArrays() {
124 bool ok = true;
125 for (size_t i = 0, n = m_unit->numArrays(); i < n; ++i) {
126 ok &= checkLiteral(i, m_unit->lookupArrayId(i), "array");
128 return ok;
132 * Check that every byte in the unit's bytecode is inside exactly one
133 * function's code region.
135 bool UnitChecker::checkBytecode() {
136 bool ok = true;
137 typedef std::map<Offset, const Func*> FuncMap; // ordered!
138 FuncMap funcs;
139 m_unit->forEachFunc([&](const Func* f) {
140 if (f->past() <= f->base()) {
141 if (!f->isAbstract() || f->past() < f->base()) {
142 error("func size <= 0 [%d:%d] in unit %s\n",
143 f->base(), f->past(), m_unit->md5().toString().c_str());
144 ok = false;
145 return; // continue
148 if (f->base() < 0 || f->past() > m_unit->bclen()) {
149 error("function region [%d:%d] out of unit %s bounds [%d:%d]\n",
150 f->base(), f->past(), m_unit->md5().toString().c_str(),
151 0, m_unit->bclen());
152 ok = false;
153 return; // continue
155 if (funcs.find(f->base()) != funcs.end()) {
156 error("duplicate function-base at %d in unit %s\n",
157 f->base(), m_unit->md5().toString().c_str());
158 ok = false;
159 return; // continue
161 funcs.insert(FuncMap::value_type(f->base(), f));
163 // iterate funcs in offset order, checking for holes and overlap
164 if (funcs.empty()) {
165 error("unit %s must have at least one func\n",
166 m_unit->md5().toString().c_str());
167 return false;
169 Offset last_past = 0;
170 for (FuncMap::iterator i = funcs.begin(), e = funcs.end(); i != e; ) {
171 const Func* f = (*i).second; ++i;
172 if (f->base() < last_past) {
173 error("function overlap [%d:%d] in unit %s\n",
174 f->base(), last_past, m_unit->md5().toString().c_str());
175 ok = false;
176 } else if (f->base() > last_past) {
177 error("dead bytecode space [%d:%d] in unit %s\n",
178 last_past, f->base(), m_unit->md5().toString().c_str());
179 ok = false;
181 last_past = f->past();
182 if (i == e && last_past != m_unit->bclen()) {
183 error("dead bytecode [%d:%d] at end of unit %s\n",
184 last_past, m_unit->bclen(), m_unit->md5().toString().c_str());
185 ok = false;
188 return ok;
189 // Notes
190 // 1. Bytecode regions for every function must not overlap and must exactly
191 // divide up the bytecode of the whole unit.
192 // 2. Its not an error for an abstract function to have zero size.
195 bool UnitChecker::checkFuncs() {
196 const Func* pseudo = nullptr;
197 bool multi = false;
198 bool ok = true;
200 m_unit->forEachFunc([&](const Func* func) {
201 if (func->isPseudoMain()) {
202 if (pseudo) {
203 multi = true;
204 error("%s", "unit should have exactly one pseudo-main\n");
205 ok = false;
207 pseudo = func;
210 if (func->attrs() & AttrNative) {
211 ok &= checkNativeFunc(func, m_verbose);
214 ok &= checkFunc(func, m_verbose);
217 if (!multi && m_unit->getMain() != pseudo) {
218 error("%s", "funcs and unit disagree on what is the pseudo-main\n");
219 ok = false;
222 return ok;
225 }} // HPHP::Verifier