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/array-common.h"
19 #include "hphp/runtime/base/array-data.h"
20 #include "hphp/runtime/base/array-data-defs.h"
21 #include "hphp/runtime/base/array-init.h"
22 #include "hphp/runtime/base/array-iterator.h"
23 #include "hphp/runtime/base/mixed-array-defs.h"
24 #include "hphp/runtime/base/object-data.h"
25 #include "hphp/runtime/base/set-array.h"
26 #include "hphp/runtime/base/packed-array.h"
27 #include "hphp/runtime/base/type-variant.h"
31 //////////////////////////////////////////////////////////////////////
33 ssize_t
ArrayCommon::ReturnInvalidIndex(const ArrayData
*) {
37 ArrayData
* ArrayCommon::Pop(ArrayData
* a
, Variant
&value
) {
39 auto const pos
= a
->iter_last();
40 value
= a
->getValue(pos
);
41 return a
->remove(a
->getKey(pos
));
43 value
= uninit_null();
47 ArrayData
* ArrayCommon::Dequeue(ArrayData
* a
, Variant
&value
) {
49 auto const pos
= a
->iter_begin();
50 value
= a
->getValue(pos
);
51 auto const ret
= a
->remove(a
->getKey(pos
));
52 // In PHP, array_shift() will cause all numerically key-ed values re-keyed
56 value
= uninit_null();
60 ArrayData
* ArrayCommon::ToVec(ArrayData
* a
, bool) {
61 auto const size
= a
->size();
62 if (!size
) return staticEmptyVecArray();
63 VecArrayInit init
{size
};
67 if (UNLIKELY(isRefType(v
.m_type
))) {
68 if (v
.m_data
.pref
->isReferenced()) {
69 throwRefInvalidArrayValueException(init
.toArray());
78 ArrayData
* ArrayCommon::ToDict(ArrayData
* a
, bool) {
79 auto const size
= a
->size();
80 if (!size
) return staticEmptyDictArray();
84 [&](Cell k
, TypedValue v
) {
85 if (UNLIKELY(isRefType(v
.m_type
))) {
86 if (v
.m_data
.pref
->isReferenced()) {
87 throwRefInvalidArrayValueException(init
.toArray());
90 init
.setValidKey(k
, v
);
96 ArrayData
* ArrayCommon::ToKeyset(ArrayData
* a
, bool) {
97 auto const size
= a
->size();
98 if (!size
) return staticEmptyKeysetArray();
99 KeysetInit init
{size
};
103 if (UNLIKELY(isRefType(v
.m_type
))) {
104 if (v
.m_data
.pref
->isReferenced()) {
105 throwRefInvalidArrayValueException(init
.toArray());
107 v
= *v
.m_data
.pref
->cell();
108 assertx(!isRefType(v
.m_type
));
111 if (LIKELY(isStringType(v
.m_type
))) {
112 init
.add(v
.m_data
.pstr
);
113 } else if (LIKELY(isIntType(v
.m_type
))) {
114 init
.add(v
.m_data
.num
);
116 throwInvalidArrayKeyException(&v
, init
.toArray().get());
120 return init
.create();
123 ArrayData
* ArrayCommon::ToVArray(ArrayData
* a
, bool) {
124 if (a
->isVArray()) return a
;
125 auto const size
= a
->size();
126 if (!size
) return staticEmptyVArray();
127 VArrayInit init
{size
};
128 IterateVNoInc(a
, [&](TypedValue v
) { init
.appendWithRef(v
); });
129 return init
.create();
132 ArrayData
* ArrayCommon::ToDArray(ArrayData
* a
, bool) {
133 if (a
->isDArray()) return a
;
134 auto const size
= a
->size();
135 if (!size
) return staticEmptyDArray();
136 DArrayInit init
{size
};
139 [&](Cell k
, TypedValue v
) {
140 init
.setUnknownKey(tvAsCVarRef(&k
), tvAsCVarRef(&v
));
143 return init
.create();
146 ArrayData
* ArrayCommon::ToShape(ArrayData
* a
, bool copy
) {
147 auto arr
= RuntimeOption::EvalHackArrDVArrs
148 ? ArrayCommon::ToDict(a
, copy
)
149 : ArrayCommon::ToDArray(a
, copy
);
150 arr
= arr
->toShapeInPlaceIfCompatible();
154 ArrayCommon::RefCheckResult
155 ArrayCommon::CheckForRefs(const ArrayData
* ad
) {
156 auto result
= RefCheckResult::Pass
;
160 if (UNLIKELY(isRefType(v
.m_type
))) {
161 auto const ref
= v
.m_data
.pref
;
162 if (ref
->isReferenced() || ref
->cell()->m_data
.parr
== ad
) {
163 result
= RefCheckResult::Fail
;
166 result
= RefCheckResult::Collapse
;
174 //////////////////////////////////////////////////////////////////////
178 template <typename E
, typename C
, typename A
>
180 ArrayData
* castObjToHackArrImpl(ObjectData
* obj
,
185 if (LIKELY(obj
->isCollection())) {
186 if (auto ad
= collections::asArray(obj
)) {
187 return cast(ArrNR
{ad
}.asArray()).detach();
189 return cast(collections::toArray(obj
)).detach();
192 // iterableObject can re-enter, so bump the ref-count to prevent it from
193 // possibly being freed.
195 SCOPE_EXIT
{ decRefObj(obj
); };
198 auto iterObj
= obj
->iterableObject(isIter
);
199 if (!isIter
) SystemLib::throwInvalidOperationExceptionObject(msg
);
202 for (ArrayIter
iter(iterObj
); iter
; ++iter
) add(arr
, iter
);
208 ArrayData
* castObjToVec(ObjectData
* obj
) {
209 return castObjToHackArrImpl(
212 [](const Array
& arr
) { return arr
.toVec(); },
213 [](Array
& arr
, ArrayIter
& iter
) { arr
.append(iter
.second()); },
214 "Non-iterable object to vec conversion"
218 ArrayData
* castObjToDict(ObjectData
* obj
) {
219 return castObjToHackArrImpl(
222 [](const Array
& arr
) { return arr
.toDict(); },
223 [](Array
& arr
, ArrayIter
& iter
) { arr
.set(iter
.first(), iter
.second()); },
224 "Non-iterable object to dict conversion"
228 ArrayData
* castObjToKeyset(ObjectData
* obj
) {
229 return castObjToHackArrImpl(
232 [](const Array
& arr
) { return arr
.toKeyset(); },
233 [](Array
& arr
, ArrayIter
& iter
) { arr
.append(iter
.second()); },
234 "Non-iterable object to keyset conversion"
238 ArrayData
* castObjToVArray(ObjectData
* obj
) {
239 assertx(!RuntimeOption::EvalHackArrDVArrs
);
240 return castObjToHackArrImpl(
243 [](const Array
& arr
) { return arr
.toVArray(); },
244 [](Array
& arr
, ArrayIter
& iter
) { arr
.append(iter
.second()); },
245 "Non-iterable object to varray conversion"
250 ArrayData
* castObjToDArray(ObjectData
* obj
) {
251 assertx(!RuntimeOption::EvalHackArrDVArrs
);
252 return castObjToHackArrImpl(
255 [](const Array
& arr
) { return arr
.toDArray(); },
256 [](Array
& arr
, ArrayIter
& iter
) { arr
.set(iter
.first(), iter
.second()); },
257 "Non-iterable object to darray conversion"
261 //////////////////////////////////////////////////////////////////////