2 +----------------------------------------------------------------------+
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 "hphp/runtime/vm/name-value-table.h"
20 #include <folly/Bits.h>
22 #include "hphp/runtime/vm/bytecode.h"
23 #include "hphp/runtime/vm/runtime.h"
27 //////////////////////////////////////////////////////////////////////
29 NameValueTable::NameValueTable() {
30 allocate(folly::nextPowTwo(RuntimeOption::EvalVMInitialGlobalTableSize
));
33 NameValueTable::NameValueTable(ActRec
* fp
)
37 const auto func
= m_fp
->m_func
;
38 const Id numNames
= func
->numNamedLocals();
41 * Reserve space for all named locals plus one additional variable
42 * to avoid reallocations if one extra dynamic variable is used.
44 reserve(numNames
+ 1);
46 for (Id i
= 0; i
< numNames
; ++i
) {
47 assert(func
->lookupVarId(func
->localVarName(i
)) == i
);
49 auto elm
= insert(func
->localVarName(i
));
50 assert(elm
&& elm
->m_tv
.m_type
== kInvalidDataType
);
51 elm
->m_tv
.m_type
= kNamedLocalDataType
;
52 elm
->m_tv
.m_data
.num
= i
;
56 NameValueTable::NameValueTable(const NameValueTable
& nvTable
, ActRec
* fp
)
58 , m_elms(nvTable
.m_elms
)
60 allocate(nvTable
.m_tabMask
+ 1);
61 assert(m_tabMask
== nvTable
.m_tabMask
);
63 for (int i
= 0; i
<= m_tabMask
; ++i
) {
64 Elm
& src
= nvTable
.m_table
[i
];
65 Elm
& dst
= m_table
[i
];
67 dst
.m_name
= src
.m_name
;
69 dst
.m_name
->incRefCount();
70 if (src
.m_tv
.m_type
== kNamedLocalDataType
) {
71 dst
.m_tv
.m_type
= kNamedLocalDataType
;
72 dst
.m_tv
.m_data
.num
= src
.m_tv
.m_data
.num
;
74 tvDupFlattenVars(&src
.m_tv
, &dst
.m_tv
);
80 NameValueTable::~NameValueTable() {
82 for (Elm
* elm
= &m_table
[m_tabMask
]; elm
!= &m_table
[-1]; --elm
) {
84 decRefStr(const_cast<StringData
*>(elm
->m_name
));
85 if (elm
->m_tv
.m_type
!= kNamedLocalDataType
) {
86 tvRefcountedDecRef(elm
->m_tv
);
93 void NameValueTable::suspend(const ActRec
* oldFP
, ActRec
* newFP
) {
94 assert(m_fp
== oldFP
);
95 assert(oldFP
->func() == newFP
->func());
96 assert(!oldFP
->resumed());
97 assert(newFP
->resumed());
102 void NameValueTable::attach(ActRec
* fp
) {
103 assert(m_fp
== nullptr);
106 const auto func
= fp
->m_func
;
107 const Id numNames
= func
->numNamedLocals();
108 TypedValue
* loc
= frame_local(fp
, 0);
110 for (Id i
= 0; i
< numNames
; ++i
, --loc
) {
111 assert(func
->lookupVarId(func
->localVarName(i
)) == i
);
113 auto elm
= insert(func
->localVarName(i
));
115 if (elm
->m_tv
.m_type
!= kInvalidDataType
) {
116 assert(elm
->m_tv
.m_type
!= kNamedLocalDataType
);
117 tvCopy(elm
->m_tv
, *loc
);
120 elm
->m_tv
.m_type
= kNamedLocalDataType
;
121 elm
->m_tv
.m_data
.num
= i
;
125 void NameValueTable::detach(ActRec
* fp
) {
129 const auto func
= fp
->m_func
;
130 const Id numNames
= func
->numNamedLocals();
131 TypedValue
* loc
= frame_local(fp
, 0);
133 for (Id i
= 0; i
< numNames
; ++i
, --loc
) {
134 assert(func
->lookupVarId(func
->localVarName(i
)) == i
);
136 auto elm
= findElm(func
->localVarName(i
));
137 assert(elm
&& elm
->m_tv
.m_type
== kNamedLocalDataType
);
138 tvCopy(*loc
, elm
->m_tv
);
143 void NameValueTable::leak() {
151 TypedValue
* NameValueTable::set(const StringData
* name
, const TypedValue
* val
) {
152 TypedValue
* target
= findTypedValue(name
);
153 tvSet(*tvToCell(val
), *target
);
157 TypedValue
* NameValueTable::bind(const StringData
* name
, TypedValue
* val
) {
158 TypedValue
* target
= findTypedValue(name
);
159 if (val
->m_type
!= KindOfRef
) {
166 void NameValueTable::unset(const StringData
* name
) {
167 Elm
* elm
= findElm(name
);
169 tvUnset(derefNamedLocal(&elm
->m_tv
));
172 TypedValue
* NameValueTable::lookup(const StringData
* name
) {
173 Elm
* elm
= findElm(name
);
174 if (!elm
) return nullptr;
175 TypedValue
* tv
= derefNamedLocal(&elm
->m_tv
);
176 return tv
->m_type
== KindOfUninit
? nullptr : tv
;
179 TypedValue
* NameValueTable::lookupAdd(const StringData
* name
) {
180 auto tv
= findTypedValue(name
);
181 if (tv
->m_type
== KindOfUninit
) {
187 void NameValueTable::reserve(size_t desiredSize
) {
189 * Reserve space for size * 4 / 3 because we limit our max load
192 * Add one because the way we compute whether there is enough
193 * space on lookupAdd will look at m_elms + 1.
195 const size_t reqCapac
= desiredSize
* 4 / 3 + 1;
196 const size_t curCapac
= m_tabMask
+ 1;
198 if (reqCapac
> curCapac
) {
199 allocate(folly::nextPowTwo(reqCapac
));
203 void NameValueTable::allocate(const size_t newCapac
) {
204 assert(folly::isPowTwo(newCapac
));
205 assert(newCapac
- 1 <= std::numeric_limits
<uint32_t>::max());
207 Elm
* oldTab
= m_table
;
208 const size_t oldMask
= m_tabMask
;
210 m_table
= static_cast<Elm
*>(req::calloc(sizeof(Elm
), newCapac
));
211 m_tabMask
= uint32_t(newCapac
- 1);
214 rehash(oldTab
, oldMask
);
219 TypedValue
* NameValueTable::derefNamedLocal(TypedValue
* tv
) const {
220 return tv
->m_type
== kNamedLocalDataType
221 ? frame_local(m_fp
, tv
->m_data
.num
)
225 TypedValue
* NameValueTable::findTypedValue(const StringData
* name
) {
226 Elm
* elm
= insert(name
);
227 if (elm
->m_tv
.m_type
== kInvalidDataType
) {
228 tvWriteNull(&elm
->m_tv
);
231 return derefNamedLocal(&elm
->m_tv
);
234 NameValueTable::Elm
* NameValueTable::insertImpl(const StringData
* name
) {
235 Elm
* elm
= &m_table
[name
->hash() & m_tabMask
];
236 UNUSED
size_t numProbes
= 0;
238 assert(numProbes
++ < m_tabMask
+ 1);
239 if (nullptr == elm
->m_name
) {
241 elm
->m_tv
.m_type
= kInvalidDataType
;
244 if (name
->same(elm
->m_name
)) {
247 if (UNLIKELY(++elm
== &m_table
[m_tabMask
+ 1])) {
253 NameValueTable::Elm
* NameValueTable::insert(const StringData
* name
) {
255 Elm
* elm
= insertImpl(name
);
256 if (elm
->m_tv
.m_type
== kInvalidDataType
) {
263 void NameValueTable::rehash(Elm
* const oldTab
, const size_t oldMask
) {
264 for (Elm
* srcElm
= &oldTab
[oldMask
]; srcElm
!= &oldTab
[-1]; --srcElm
) {
265 if (srcElm
->m_name
) {
266 assert(srcElm
->m_tv
.m_type
== kNamedLocalDataType
||
267 tvIsPlausible(srcElm
->m_tv
));
268 Elm
* dstElm
= insertImpl(srcElm
->m_name
);
269 dstElm
->m_name
= srcElm
->m_name
;
270 dstElm
->m_tv
= srcElm
->m_tv
;
275 NameValueTable::Elm
* NameValueTable::findElm(const StringData
* name
) const {
276 Elm
* elm
= &m_table
[name
->hash() & m_tabMask
];
277 UNUSED
size_t numProbes
= 0;
279 assert(numProbes
++ < m_tabMask
+ 1);
280 if (UNLIKELY(nullptr == elm
->m_name
)) {
283 if (name
->same(elm
->m_name
)) return elm
;
284 if (UNLIKELY(++elm
== &m_table
[m_tabMask
+ 1])) {
290 //////////////////////////////////////////////////////////////////////
292 NameValueTable::Iterator::Iterator(const NameValueTable
* tab
)
296 if (!valid()) next();
299 NameValueTable::Iterator
300 NameValueTable::Iterator::getLast(const NameValueTable
* tab
) {
303 it
.m_idx
= tab
->m_tabMask
+ 1;
308 NameValueTable::Iterator
309 NameValueTable::Iterator::getEnd(const NameValueTable
* tab
) {
312 it
.m_idx
= tab
->m_tabMask
+ 1;
316 NameValueTable::Iterator::Iterator(const NameValueTable
* tab
, ssize_t pos
)
321 if (!valid()) next();
324 NameValueTable::Iterator::Iterator(const NameValueTable
* tab
,
325 const StringData
* start
)
328 Elm
* e
= m_tab
->findElm(start
);
329 m_idx
= e
? e
- m_tab
->m_table
: (m_tab
->m_tabMask
+ 1);
332 ssize_t
NameValueTable::Iterator::toInteger() const {
333 const ssize_t invalid
= m_tab
->m_tabMask
+ 1;
334 return valid() ? m_idx
: invalid
;
337 bool NameValueTable::Iterator::valid() const {
338 return m_idx
>= 0 && size_t(m_idx
) < m_tab
->m_tabMask
+ 1 && !atEmpty();
341 const StringData
* NameValueTable::Iterator::curKey() const {
343 return m_tab
->m_table
[m_idx
].m_name
;
346 const TypedValue
* NameValueTable::Iterator::curVal() const {
348 return m_tab
->derefNamedLocal(&m_tab
->m_table
[m_idx
].m_tv
);
351 void NameValueTable::Iterator::next() {
352 size_t const sz
= m_tab
->m_tabMask
+ 1;
353 if (m_idx
+ 1 >= sz
) {
358 if (LIKELY(!atEmpty())) {
363 } while (size_t(m_idx
) < sz
&& atEmpty());
366 void NameValueTable::Iterator::prev() {
369 } while (m_idx
>= 0 && atEmpty());
372 bool NameValueTable::Iterator::atEmpty() const {
373 if (!m_tab
->m_table
[m_idx
].m_name
) {
376 const auto tv
= m_tab
->derefNamedLocal(&m_tab
->m_table
[m_idx
].m_tv
);
377 return tv
->m_type
== KindOfUninit
;
380 //////////////////////////////////////////////////////////////////////