2 +----------------------------------------------------------------------+
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 "hphp/runtime/base/empty-array.h"
19 #include <type_traits>
21 #include "hphp/util/assertions.h"
23 #include "hphp/runtime/base/array-data.h"
24 #include "hphp/runtime/base/array-init.h"
25 #include "hphp/runtime/base/tv-val.h"
26 #include "hphp/runtime/base/mixed-array-defs.h"
27 #include "hphp/runtime/base/mixed-array.h"
28 #include "hphp/runtime/base/packed-array-defs.h"
29 #include "hphp/runtime/base/set-array.h"
30 #include "hphp/runtime/base/tv-mutate.h"
31 #include "hphp/runtime/base/tv-type.h"
32 #include "hphp/runtime/base/tv-variant.h"
33 #include "hphp/runtime/base/type-variant.h"
37 //////////////////////////////////////////////////////////////////////
39 std::aligned_storage
<sizeof(ArrayData
), 16>::type s_theEmptyArray
;
41 struct EmptyArray::Initializer
{
43 auto const ad
= reinterpret_cast<ArrayData
*>(&s_theEmptyArray
);
45 ad
->initHeader(HeaderKind::Empty
, StaticValue
);
48 EmptyArray::Initializer
EmptyArray::s_initializer
;
50 //////////////////////////////////////////////////////////////////////
52 void EmptyArray::Release(ArrayData
*) {
53 always_assert(!"never try to free the empty array");
56 TypedValue
EmptyArray::NvGetKey(const ArrayData
*, ssize_t
/*pos*/) {
57 // We have no valid positions---no one should call this function.
61 size_t EmptyArray::Vsize(const ArrayData
*) { not_reached(); }
63 tv_rval
EmptyArray::RvalPos(const ArrayData
* /*ad*/, ssize_t
/*pos*/) {
64 // We have no valid positions---no one should call this function.
68 // EmptyArray::IterAdvance() is reachable; see ArrayData::next() for details
69 ssize_t
EmptyArray::IterAdvance(const ArrayData
*, ssize_t
/*prev*/) {
73 // EmptyArray::IterRewind() is NOT reachable; see ArrayData::prev() for details
74 ssize_t
EmptyArray::IterRewind(const ArrayData
*, ssize_t
/*prev*/) {
78 // We're always already a static array.
79 void EmptyArray::OnSetEvalScalar(ArrayData
*) { not_reached(); }
80 ArrayData
* EmptyArray::CopyStatic(const ArrayData
* /*ad*/) {
84 //////////////////////////////////////////////////////////////////////
87 ArrayData
* EmptyArray::Copy(const ArrayData
*) { return ArrayData::Create(); }
89 //////////////////////////////////////////////////////////////////////
92 * Note: if you try to tail-call these helper routines, gcc will
93 * unfortunately still generate functions with frames and makes a
94 * call instead of a jump. It's because of std::pair (and is still
95 * the case if you return a custom struct).
97 * For now we're leaving this, because it's essentially free for these
98 * routines to leave the lval pointer in the second return register,
99 * and it seems questionable to clone the whole function just to avoid
100 * the frame creation in these callers. (It works to reinterpret_cast
101 * these functions to one that returns ArrayData* instead of a pair in
102 * the cases we don't need the second value, but this seems a tad too
103 * sketchy for probably-unmeasurable benefits. I'll admit I didn't
104 * try to measure it though... ;)
108 * Helper for empty array -> packed transitions. Creates an array
109 * with one element. The element is transferred into the array (should
110 * already be incref'd).
113 arr_lval
EmptyArray::MakePackedInl(TypedValue tv
) {
114 auto const ad
= static_cast<ArrayData
*>(
115 tl_heap
->objMallocIndex(PackedArray::SmallSizeIndex
)
120 PackedArray::packSizeIndexAndAuxBits(
121 PackedArray::SmallSizeIndex
,
122 ArrayData::kNotDVArray
125 ad
->m_sizeAndPos
= 1; // size=1, pos=0
127 auto elem
= PackedArray::LvalUncheckedInt(ad
, 0);
130 assertx(ad
->kind() == ArrayData::kPackedKind
);
131 assertx(ad
->dvArray() == ArrayData::kNotDVArray
);
132 assertx(ad
->m_size
== 1);
133 assertx(ad
->m_pos
== 0);
134 assertx(ad
->hasExactlyOneRef());
135 assertx(PackedArray::checkInvariants(ad
));
136 return arr_lval
{ ad
, elem
};
140 arr_lval
EmptyArray::MakePacked(TypedValue tv
) {
141 return MakePackedInl(tv
);
145 * Helper for creating a single-element mixed array with a string key.
147 * Note: the key is not already incref'd, but the value must be.
150 arr_lval
EmptyArray::MakeMixed(StringData
* key
, TypedValue val
) {
151 auto const ad
= MixedArray::reqAlloc(MixedArray::SmallScale
);
152 MixedArray::InitSmall(ad
, 1/*size*/, 0/*nextIntKey*/);
153 auto const data
= ad
->data();
154 auto const hash
= reinterpret_cast<int32_t*>(data
+ MixedArray::SmallSize
);
155 auto const khash
= key
->hash();
156 auto const mask
= MixedArray::SmallMask
;
157 hash
[khash
& mask
] = 0;
158 data
[0].setStrKey(key
, khash
);
159 ad
->mutableKeyTypes()->recordStr(key
);
161 auto& elem
= data
[0].data
;
162 elem
.m_data
= val
.m_data
;
163 elem
.m_type
= val
.m_type
;
165 assertx(ad
->m_size
== 1);
166 assertx(ad
->m_pos
== 0);
167 assertx(ad
->m_scale
== MixedArray::SmallScale
);
168 assertx(ad
->kind() == ArrayData::kMixedKind
);
169 assertx(ad
->hasExactlyOneRef());
170 assertx(ad
->m_used
== 1);
171 assertx(ad
->checkInvariants());
172 return arr_lval
{ ad
, &elem
};
176 * Creating a single-element mixed array with a integer key. The
177 * value is already incref'd.
179 arr_lval
EmptyArray::MakeMixed(int64_t key
, TypedValue val
) {
180 auto const ad
= MixedArray::reqAlloc(MixedArray::SmallScale
);
181 MixedArray::InitSmall(ad
, 1/*size*/, (key
>= 0) ? key
+ uint64_t{1} : 0);
182 auto const data
= ad
->data();
183 auto const hash
= reinterpret_cast<int32_t*>(data
+ MixedArray::SmallSize
);
185 auto const mask
= MixedArray::SmallMask
;
186 auto h
= hash_int64(key
);
188 data
[0].setIntKey(key
, h
);
189 ad
->mutableKeyTypes()->recordInt();
191 auto& elem
= data
[0].data
;
192 elem
.m_data
= val
.m_data
;
193 elem
.m_type
= val
.m_type
;
195 assertx(ad
->kind() == ArrayData::kMixedKind
);
196 assertx(ad
->m_size
== 1);
197 assertx(ad
->m_pos
== 0);
198 assertx(ad
->hasExactlyOneRef());
199 assertx(ad
->m_scale
== MixedArray::SmallScale
);
200 assertx(ad
->m_used
== 1);
201 assertx(ad
->checkInvariants());
202 return arr_lval
{ ad
, &elem
};
205 //////////////////////////////////////////////////////////////////////
207 template<bool enforce
> ALWAYS_INLINE
208 arr_lval
EmptyArray::LvalIntImpl(ArrayData
*, int64_t k
, bool) {
209 if (enforce
) throwMissingElementException("Lval");
210 return k
== 0 ? EmptyArray::MakePacked(make_tv
<KindOfNull
>())
211 : EmptyArray::MakeMixed(k
, make_tv
<KindOfNull
>());
214 template<bool enforce
> ALWAYS_INLINE
215 arr_lval
EmptyArray::LvalStrImpl(ArrayData
*, StringData
* k
, bool) {
216 if (enforce
) throwMissingElementException("Lval");
217 return EmptyArray::MakeMixed(k
, make_tv
<KindOfNull
>());
220 ArrayData
* EmptyArray::SetInt(ArrayData
* ad
, int64_t k
, TypedValue v
) {
222 return SetIntMove(ad
, k
, v
);
225 ArrayData
* EmptyArray::SetIntMove(ArrayData
*, int64_t k
, TypedValue v
) {
226 // TODO(#3888164): we should make it so we don't need KindOfUninit checks
227 if (v
.m_type
== KindOfUninit
) v
.m_type
= KindOfNull
;
228 return k
== 0 ? EmptyArray::MakePacked(v
).arr
229 : EmptyArray::MakeMixed(k
, v
).arr
;
232 ArrayData
* EmptyArray::SetStr(ArrayData
* ad
, StringData
* k
, TypedValue v
) {
234 return SetStrMove(ad
, k
, v
);
237 ArrayData
* EmptyArray::SetStrMove(ArrayData
*, StringData
* k
, TypedValue v
) {
238 // TODO(#3888164): we should make it so we don't need KindOfUninit checks
239 if (v
.m_type
== KindOfUninit
) v
.m_type
= KindOfNull
;
240 return EmptyArray::MakeMixed(k
, v
).arr
;
243 ArrayData
* EmptyArray::RemoveInt(ArrayData
* ad
, int64_t) {
247 ArrayData
* EmptyArray::RemoveStr(ArrayData
* ad
, const StringData
*) {
251 arr_lval
EmptyArray::LvalInt(ArrayData
* ad
, int64_t k
, bool copy
) {
252 return LvalIntImpl
<true>(ad
, k
, copy
);
255 arr_lval
EmptyArray::LvalStr(ArrayData
* ad
, StringData
* k
, bool copy
) {
256 return LvalStrImpl
<true>(ad
, k
, copy
);
259 arr_lval
EmptyArray::LvalSilentInt(ArrayData
* ad
, int64_t k
, bool copy
) {
260 return arr_lval
{ ad
, nullptr };
263 arr_lval
EmptyArray::LvalSilentStr(ArrayData
* ad
, StringData
* k
, bool copy
) {
264 return arr_lval
{ ad
, nullptr };
267 arr_lval
EmptyArray::LvalForceNew(ArrayData
*, bool) {
268 return EmptyArray::MakePacked(make_tv
<KindOfNull
>());
271 ArrayData
* EmptyArray::Append(ArrayData
*, TypedValue v
) {
273 return EmptyArray::MakePackedInl(v
).arr
;
276 //////////////////////////////////////////////////////////////////////
278 ArrayData
* EmptyArray::PlusEq(ArrayData
*, const ArrayData
* elems
) {
279 if (!elems
->isPHPArrayType()) throwInvalidAdditionException(elems
);
280 elems
->incRefCount();
281 return const_cast<ArrayData
*>(elems
);
284 ArrayData
* EmptyArray::Merge(ArrayData
*, const ArrayData
* elems
) {
285 if (elems
->isNotDVArray()) {
286 // Packed arrays don't need renumbering, so don't make a copy.
287 if (elems
->isPackedKind()) {
288 elems
->incRefCount();
289 return const_cast<ArrayData
*>(elems
);
291 // Fast path the common case that elems is mixed.
292 if (elems
->isMixedKind()) {
293 auto const copy
= MixedArray::Copy(elems
);
294 assertx(copy
!= elems
);
295 MixedArray::Renumber(copy
);
299 auto copy
= const_cast<ArrayData
*>(elems
)->toPHPArray(true);
300 copy
= copy
== elems
? elems
->copy() : copy
;
301 assertx(copy
!= elems
);
306 ArrayData
* EmptyArray::PopOrDequeue(ArrayData
* ad
, Variant
& value
) {
307 value
= uninit_null();
311 ArrayData
* EmptyArray::Prepend(ArrayData
*, TypedValue v
) {
313 return EmptyArray::MakePacked(v
).arr
;
316 ArrayData
* EmptyArray::ToDict(ArrayData
*, bool) {
317 return ArrayData::CreateDict();
320 ArrayData
* EmptyArray::ToVec(ArrayData
*, bool) {
321 return ArrayData::CreateVec();
324 ArrayData
* EmptyArray::ToKeyset(ArrayData
*, bool) {
325 return ArrayData::CreateKeyset();
328 //////////////////////////////////////////////////////////////////////