2 +----------------------------------------------------------------------+
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"
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"
28 //////////////////////////////////////////////////////////////////////
30 NameValueTable::NameValueTable()
36 allocate(folly::nextPowTwo(RuntimeOption::EvalVMInitialGlobalTableSize
));
39 NameValueTable::NameValueTable(ActRec
* fp
)
46 const auto func
= m_fp
->m_func
;
47 const Id numNames
= func
->numNamedLocals();
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
)
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
;
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
;
85 tvDupFlattenVars(&src
.m_tv
, &dst
.m_tv
);
91 NameValueTable::~NameValueTable() {
94 for (Elm
* elm
= &m_table
[m_tabMask
]; elm
!= &m_table
[-1]; --elm
) {
96 decRefStr(const_cast<StringData
*>(elm
->m_name
));
97 if (elm
->m_tv
.m_type
!= KindOfNamedLocal
) {
98 tvRefcountedDecRef(elm
->m_tv
);
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());
114 void NameValueTable::attach(ActRec
* fp
) {
115 assert(m_fp
== nullptr);
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
));
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
) {
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
);
155 void NameValueTable::leak() {
162 TypedValue
* NameValueTable::set(const StringData
* name
, const TypedValue
* val
) {
163 TypedValue
* target
= findTypedValue(name
);
164 tvSet(*tvToCell(val
), *target
);
168 TypedValue
* NameValueTable::bind(const StringData
* name
, TypedValue
* val
) {
169 TypedValue
* target
= findTypedValue(name
);
170 if (val
->m_type
!= KindOfRef
) {
177 void NameValueTable::unset(const StringData
* name
) {
178 Elm
* elm
= findElm(name
);
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
) {
198 void NameValueTable::reserve(size_t desiredSize
) {
200 * Reserve space for size * 4 / 3 because we limit our max load
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);
225 rehash(oldTab
, oldMask
);
230 TypedValue
* NameValueTable::derefNamedLocal(TypedValue
* tv
) const {
231 return tv
->m_type
== KindOfNamedLocal
232 ? frame_local(m_fp
, tv
->m_data
.num
)
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
);
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;
249 assert(numProbes
++ < m_tabMask
+ 1);
250 if (nullptr == elm
->m_name
) {
252 elm
->m_tv
.m_type
= KindOfInvalid
;
255 if (name
->same(elm
->m_name
)) {
258 if (UNLIKELY(++elm
== &m_table
[m_tabMask
+ 1])) {
264 NameValueTable::Elm
* NameValueTable::insert(const StringData
* name
) {
266 Elm
* elm
= insertImpl(name
);
267 if (elm
->m_tv
.m_type
== KindOfInvalid
) {
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;
290 assert(numProbes
++ < m_tabMask
+ 1);
291 if (UNLIKELY(nullptr == elm
->m_name
)) {
294 if (name
->same(elm
->m_name
)) return elm
;
295 if (UNLIKELY(++elm
== &m_table
[m_tabMask
+ 1])) {
301 //////////////////////////////////////////////////////////////////////
303 NameValueTable::Iterator::Iterator(const NameValueTable
* tab
)
307 if (!valid()) next();
310 NameValueTable::Iterator
311 NameValueTable::Iterator::getEnd(const NameValueTable
* tab
) {
314 it
.m_idx
= tab
->m_tabMask
+ 1;
319 NameValueTable::Iterator::Iterator(const NameValueTable
* tab
, ssize_t pos
)
324 if (!valid()) next();
327 NameValueTable::Iterator::Iterator(const NameValueTable
* tab
,
328 const StringData
* start
)
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 {
346 return m_tab
->m_table
[m_idx
].m_name
;
349 const TypedValue
* NameValueTable::Iterator::curVal() const {
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;
358 } while (size_t(m_idx
) < sz
&& atEmpty());
361 void NameValueTable::Iterator::prev() {
364 } while (m_idx
>= 0 && atEmpty());
367 bool NameValueTable::Iterator::atEmpty() const {
368 if (!m_tab
->m_table
[m_idx
].m_name
) {
371 const auto tv
= m_tab
->derefNamedLocal(&m_tab
->m_table
[m_idx
].m_tv
);
372 return tv
->m_type
== KindOfUninit
;
375 //////////////////////////////////////////////////////////////////////