Use makePseudoMainExit in more places
[hiphop-php.git] / hphp / runtime / vm / name-value-table.cpp
blob0670f93db5a753ed1386d392fd01bca3c9be8e4d
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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 "hphp/runtime/vm/name-value-table.h"
19 #include <limits>
20 #include "folly/Bits.h"
22 #include "hphp/runtime/base/complex-types.h"
23 #include "hphp/runtime/vm/bytecode.h"
24 #include "hphp/runtime/vm/runtime.h"
26 namespace HPHP {
28 //////////////////////////////////////////////////////////////////////
30 NameValueTable::NameValueTable()
31 : m_fp(nullptr)
32 , m_table(nullptr)
33 , m_tabMask(0)
34 , m_elms(0)
36 allocate(folly::nextPowTwo(RuntimeOption::EvalVMInitialGlobalTableSize));
39 NameValueTable::NameValueTable(ActRec* fp)
40 : m_fp(fp)
41 , m_table(nullptr)
42 , m_tabMask(0)
43 , m_elms(0)
45 assert(m_fp);
46 const auto func = m_fp->m_func;
47 const Id numNames = func->numNamedLocals();
49 /**
50 * Reserve space for all named locals plus one additional variable
51 * to avoid reallocations if one extra dynamic variable is used.
53 reserve(numNames + 1);
55 for (Id i = 0; i < numNames; ++i) {
56 assert(func->lookupVarId(func->localVarName(i)) == i);
58 auto elm = insert(func->localVarName(i));
59 assert(elm && elm->m_tv.m_type == KindOfInvalid);
60 elm->m_tv.m_type = KindOfNamedLocal;
61 elm->m_tv.m_data.num = i;
65 NameValueTable::NameValueTable(const NameValueTable& nvTable, ActRec* fp)
66 : m_fp(fp)
67 , m_table(nullptr)
68 , m_tabMask(0)
69 , m_elms(nvTable.m_elms)
71 allocate(nvTable.m_tabMask + 1);
72 assert(m_tabMask == nvTable.m_tabMask);
74 for (int i = 0; i <= m_tabMask; ++i) {
75 Elm& src = nvTable.m_table[i];
76 Elm& dst = m_table[i];
78 dst.m_name = src.m_name;
79 if (dst.m_name) {
80 dst.m_name->incRefCount();
81 if (src.m_tv.m_type == KindOfNamedLocal) {
82 dst.m_tv.m_type = KindOfNamedLocal;
83 dst.m_tv.m_data.num = src.m_tv.m_data.num;
84 } else {
85 tvDupFlattenVars(&src.m_tv, &dst.m_tv);
91 NameValueTable::~NameValueTable() {
92 if (!m_table) return;
94 for (Elm* elm = &m_table[m_tabMask]; elm != &m_table[-1]; --elm) {
95 if (elm->m_name) {
96 decRefStr(const_cast<StringData*>(elm->m_name));
97 if (elm->m_tv.m_type != KindOfNamedLocal) {
98 tvRefcountedDecRef(elm->m_tv);
102 free(m_table);
105 void NameValueTable::suspend(ActRec* oldFP, ActRec* newFP) {
106 assert(m_fp == oldFP);
107 assert(oldFP->func() == newFP->func());
108 assert(!oldFP->resumed());
109 assert(newFP->resumed());
111 m_fp = newFP;
114 void NameValueTable::attach(ActRec* fp) {
115 assert(m_fp == nullptr);
116 m_fp = fp;
118 const auto func = fp->m_func;
119 const Id numNames = func->numNamedLocals();
120 TypedValue* loc = frame_local(fp, 0);
122 for (Id i = 0; i < numNames; ++i, --loc) {
123 assert(func->lookupVarId(func->localVarName(i)) == i);
125 auto elm = insert(func->localVarName(i));
126 assert(elm);
127 if (elm->m_tv.m_type != KindOfInvalid) {
128 assert(elm->m_tv.m_type != KindOfNamedLocal);
129 tvCopy(elm->m_tv, *loc);
132 elm->m_tv.m_type = KindOfNamedLocal;
133 elm->m_tv.m_data.num = i;
137 void NameValueTable::detach(ActRec* fp) {
138 assert(m_fp == fp);
139 m_fp = nullptr;
141 const auto func = fp->m_func;
142 const Id numNames = func->numNamedLocals();
143 TypedValue* loc = frame_local(fp, 0);
145 for (Id i = 0; i < numNames; ++i, --loc) {
146 assert(func->lookupVarId(func->localVarName(i)) == i);
148 auto elm = findElm(func->localVarName(i));
149 assert(elm && elm->m_tv.m_type == KindOfNamedLocal);
150 tvCopy(*loc, elm->m_tv);
151 tvDebugTrash(loc);
155 void NameValueTable::leak() {
156 m_elms = 0;
157 m_tabMask = 0;
158 free(m_table);
159 m_table = nullptr;
162 TypedValue* NameValueTable::set(const StringData* name, const TypedValue* val) {
163 TypedValue* target = findTypedValue(name);
164 tvSet(*tvToCell(val), *target);
165 return target;
168 TypedValue* NameValueTable::bind(const StringData* name, TypedValue* val) {
169 TypedValue* target = findTypedValue(name);
170 if (val->m_type != KindOfRef) {
171 tvBox(val);
173 tvBind(val, target);
174 return target;
177 void NameValueTable::unset(const StringData* name) {
178 Elm* elm = findElm(name);
179 if (!elm) return;
180 tvUnset(derefNamedLocal(&elm->m_tv));
183 TypedValue* NameValueTable::lookup(const StringData* name) {
184 Elm* elm = findElm(name);
185 if (!elm) return nullptr;
186 TypedValue* tv = derefNamedLocal(&elm->m_tv);
187 return tv->m_type == KindOfUninit ? nullptr : tv;
190 TypedValue* NameValueTable::lookupAdd(const StringData* name) {
191 auto tv = findTypedValue(name);
192 if (tv->m_type == KindOfUninit) {
193 tvWriteNull(tv);
195 return tv;
198 void NameValueTable::reserve(size_t desiredSize) {
200 * Reserve space for size * 4 / 3 because we limit our max load
201 * factor to .75.
203 * Add one because the way we compute whether there is enough
204 * space on lookupAdd will look at m_elms + 1.
206 const size_t reqCapac = desiredSize * 4 / 3 + 1;
207 const size_t curCapac = m_tabMask + 1;
209 if (reqCapac > curCapac) {
210 allocate(folly::nextPowTwo(reqCapac));
214 void NameValueTable::allocate(const size_t newCapac) {
215 assert(folly::isPowTwo(newCapac));
216 assert(newCapac - 1 <= std::numeric_limits<uint32_t>::max());
218 Elm* oldTab = m_table;
219 const size_t oldMask = m_tabMask;
221 m_table = static_cast<Elm*>(calloc(sizeof(Elm), newCapac));
222 m_tabMask = uint32_t(newCapac - 1);
224 if (oldTab) {
225 rehash(oldTab, oldMask);
226 free(oldTab);
230 TypedValue* NameValueTable::derefNamedLocal(TypedValue* tv) const {
231 return tv->m_type == KindOfNamedLocal
232 ? frame_local(m_fp, tv->m_data.num)
233 : tv;
236 TypedValue* NameValueTable::findTypedValue(const StringData* name) {
237 Elm* elm = insert(name);
238 if (elm->m_tv.m_type == KindOfInvalid) {
239 tvWriteNull(&elm->m_tv);
240 return &elm->m_tv;
242 return derefNamedLocal(&elm->m_tv);
245 NameValueTable::Elm* NameValueTable::insertImpl(const StringData* name) {
246 Elm* elm = &m_table[name->hash() & m_tabMask];
247 UNUSED size_t numProbes = 0;
248 for (;;) {
249 assert(numProbes++ < m_tabMask + 1);
250 if (nullptr == elm->m_name) {
251 elm->m_name = name;
252 elm->m_tv.m_type = KindOfInvalid;
253 return elm;
255 if (name->same(elm->m_name)) {
256 return elm;
258 if (UNLIKELY(++elm == &m_table[m_tabMask + 1])) {
259 elm = m_table;
264 NameValueTable::Elm* NameValueTable::insert(const StringData* name) {
265 reserve(m_elms + 1);
266 Elm* elm = insertImpl(name);
267 if (elm->m_tv.m_type == KindOfInvalid) {
268 ++m_elms;
269 name->incRefCount();
271 return elm;
274 void NameValueTable::rehash(Elm* const oldTab, const size_t oldMask) {
275 for (Elm* srcElm = &oldTab[oldMask]; srcElm != &oldTab[-1]; --srcElm) {
276 if (srcElm->m_name) {
277 assert(srcElm->m_tv.m_type == KindOfNamedLocal ||
278 tvIsPlausible(srcElm->m_tv));
279 Elm* dstElm = insertImpl(srcElm->m_name);
280 dstElm->m_name = srcElm->m_name;
281 dstElm->m_tv = srcElm->m_tv;
286 NameValueTable::Elm* NameValueTable::findElm(const StringData* name) const {
287 Elm* elm = &m_table[name->hash() & m_tabMask];
288 UNUSED size_t numProbes = 0;
289 for (;;) {
290 assert(numProbes++ < m_tabMask + 1);
291 if (UNLIKELY(nullptr == elm->m_name)) {
292 return nullptr;
294 if (name->same(elm->m_name)) return elm;
295 if (UNLIKELY(++elm == &m_table[m_tabMask + 1])) {
296 elm = m_table;
301 //////////////////////////////////////////////////////////////////////
303 NameValueTable::Iterator::Iterator(const NameValueTable* tab)
304 : m_tab(tab)
305 , m_idx(0)
307 if (!valid()) next();
310 NameValueTable::Iterator
311 NameValueTable::Iterator::getEnd(const NameValueTable* tab) {
312 Iterator it;
313 it.m_tab = tab;
314 it.m_idx = tab->m_tabMask + 1;
315 it.prev();
316 return it;
319 NameValueTable::Iterator::Iterator(const NameValueTable* tab, ssize_t pos)
320 : m_tab(tab)
321 , m_idx(pos)
323 assert(pos >= 0);
324 if (!valid()) next();
327 NameValueTable::Iterator::Iterator(const NameValueTable* tab,
328 const StringData* start)
329 : m_tab(tab)
331 Elm* e = m_tab->findElm(start);
332 m_idx = e ? e - m_tab->m_table : (m_tab->m_tabMask + 1);
335 ssize_t NameValueTable::Iterator::toInteger() const {
336 const ssize_t invalid = ArrayData::invalid_index;
337 return valid() ? m_idx : invalid;
340 bool NameValueTable::Iterator::valid() const {
341 return m_idx >= 0 && size_t(m_idx) < m_tab->m_tabMask + 1 && !atEmpty();
344 const StringData* NameValueTable::Iterator::curKey() const {
345 assert(valid());
346 return m_tab->m_table[m_idx].m_name;
349 const TypedValue* NameValueTable::Iterator::curVal() const {
350 assert(valid());
351 return m_tab->derefNamedLocal(&m_tab->m_table[m_idx].m_tv);
354 void NameValueTable::Iterator::next() {
355 size_t const sz = m_tab->m_tabMask + 1;
356 do {
357 ++m_idx;
358 } while (size_t(m_idx) < sz && atEmpty());
361 void NameValueTable::Iterator::prev() {
362 do {
363 --m_idx;
364 } while (m_idx >= 0 && atEmpty());
367 bool NameValueTable::Iterator::atEmpty() const {
368 if (!m_tab->m_table[m_idx].m_name) {
369 return true;
371 const auto tv = m_tab->derefNamedLocal(&m_tab->m_table[m_idx].m_tv);
372 return tv->m_type == KindOfUninit;
375 //////////////////////////////////////////////////////////////////////