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 bool ArrayCommon::ValidMArrayIter(const ArrayData
* ad
, const MArrayIter
& fp
) {
38 assert(fp
.getContainer() == ad
);
39 if (fp
.getResetFlag()) return false;
40 if (ad
->hasPackedLayout()) {
41 assert(PackedArray::checkInvariants(ad
));
42 return fp
.m_pos
!= ad
->getSize();
43 } else if (ad
->isKeyset()) {
46 assert(MixedArray::asMixed(ad
));
47 return fp
.m_pos
!= MixedArray::asMixed(ad
)->iterLimit();
51 ArrayData
* ArrayCommon::Pop(ArrayData
* a
, Variant
&value
) {
53 auto const pos
= a
->iter_last();
54 value
= a
->getValue(pos
);
55 return a
->remove(a
->getKey(pos
), a
->cowCheck());
57 value
= uninit_null();
61 ArrayData
* ArrayCommon::Dequeue(ArrayData
* a
, Variant
&value
) {
63 auto const pos
= a
->iter_begin();
64 value
= a
->getValue(pos
);
65 auto const ret
= a
->remove(a
->getKey(pos
), a
->cowCheck());
66 // In PHP, array_shift() will cause all numerically key-ed values re-keyed
70 value
= uninit_null();
74 ArrayData
* ArrayCommon::ToVec(ArrayData
* a
, bool) {
75 auto const size
= a
->size();
76 if (!size
) return staticEmptyVecArray();
77 VecArrayInit init
{size
};
81 if (UNLIKELY(v
.m_type
== KindOfRef
)) {
82 if (v
.m_data
.pref
->isReferenced()) {
83 throwRefInvalidArrayValueException(init
.toArray());
92 ArrayData
* ArrayCommon::ToDict(ArrayData
* a
, bool) {
93 auto const size
= a
->size();
94 if (!size
) return staticEmptyDictArray();
98 [&](Cell k
, TypedValue v
) {
99 if (UNLIKELY(v
.m_type
== KindOfRef
)) {
100 if (v
.m_data
.pref
->isReferenced()) {
101 throwRefInvalidArrayValueException(init
.toArray());
104 init
.setValidKey(k
, v
);
107 return init
.create();
110 ArrayData
* ArrayCommon::ToKeyset(ArrayData
* a
, bool) {
111 auto const size
= a
->size();
112 if (!size
) return staticEmptyKeysetArray();
113 KeysetInit init
{size
};
117 if (UNLIKELY(v
.m_type
== KindOfRef
)) {
118 if (v
.m_data
.pref
->isReferenced()) {
119 throwRefInvalidArrayValueException(init
.toArray());
121 v
= *v
.m_data
.pref
->tv();
122 assertx(v
.m_type
!= KindOfRef
);
125 if (LIKELY(isStringType(v
.m_type
))) {
126 init
.add(v
.m_data
.pstr
);
127 } else if (LIKELY(isIntType(v
.m_type
))) {
128 init
.add(v
.m_data
.num
);
130 throwInvalidArrayKeyException(&v
, init
.toArray().get());
134 return init
.create();
137 ArrayData
* ArrayCommon::ToVArray(ArrayData
* a
, bool) {
138 if (a
->isVArray()) return a
;
139 auto const size
= a
->size();
140 if (!size
) return staticEmptyVArray();
141 VArrayInit init
{size
};
142 IterateV(a
, [&](TypedValue v
) { init
.appendWithRef(v
); });
143 return init
.create();
146 ArrayData
* ArrayCommon::ToDArray(ArrayData
* a
, bool) {
147 if (a
->isDArray()) return a
;
148 auto const size
= a
->size();
149 if (!size
) return staticEmptyDArray();
150 DArrayInit init
{size
};
153 [&](Cell k
, TypedValue v
) {
154 init
.setUnknownKey(tvAsCVarRef(&k
), tvAsCVarRef(&v
));
157 return init
.create();
160 ArrayCommon::RefCheckResult
161 ArrayCommon::CheckForRefs(const ArrayData
* ad
) {
162 auto result
= RefCheckResult::Pass
;
166 if (UNLIKELY(v
.m_type
== KindOfRef
)) {
167 auto const ref
= v
.m_data
.pref
;
168 if (ref
->isReferenced() || ref
->tv()->m_data
.parr
== ad
) {
169 result
= RefCheckResult::Fail
;
172 result
= RefCheckResult::Collapse
;
180 //////////////////////////////////////////////////////////////////////
184 template <typename E
, typename C
, typename A
>
186 ArrayData
* castObjToHackArrImpl(ObjectData
* obj
,
191 if (LIKELY(obj
->isCollection())) {
192 if (auto ad
= collections::asArray(obj
)) {
193 return cast(ArrNR
{ad
}.asArray()).detach();
195 return cast(collections::toArray(obj
)).detach();
198 // iterableObject can re-enter, so bump the ref-count to prevent it from
199 // possibly being freed.
201 SCOPE_EXIT
{ decRefObj(obj
); };
204 auto iterObj
= obj
->iterableObject(isIter
);
205 if (!isIter
) SystemLib::throwInvalidOperationExceptionObject(msg
);
208 for (ArrayIter
iter(iterObj
); iter
; ++iter
) add(arr
, iter
);
214 ArrayData
* castObjToVec(ObjectData
* obj
) {
215 return castObjToHackArrImpl(
218 [](const Array
& arr
) { return arr
.toVec(); },
219 [](Array
& arr
, ArrayIter
& iter
) { arr
.append(iter
.second()); },
220 "Non-iterable object to vec conversion"
224 ArrayData
* castObjToDict(ObjectData
* obj
) {
225 return castObjToHackArrImpl(
228 [](const Array
& arr
) { return arr
.toDict(); },
229 [](Array
& arr
, ArrayIter
& iter
) { arr
.set(iter
.first(), iter
.second()); },
230 "Non-iterable object to dict conversion"
234 ArrayData
* castObjToKeyset(ObjectData
* obj
) {
235 return castObjToHackArrImpl(
238 [](const Array
& arr
) { return arr
.toKeyset(); },
239 [](Array
& arr
, ArrayIter
& iter
) { arr
.append(iter
.second()); },
240 "Non-iterable object to keyset conversion"
244 ArrayData
* castObjToVArray(ObjectData
* obj
) {
245 assertx(!RuntimeOption::EvalHackArrDVArrs
);
246 return castObjToHackArrImpl(
249 [](const Array
& arr
) { return arr
.toVArray(); },
250 [](Array
& arr
, ArrayIter
& iter
) { arr
.append(iter
.second()); },
251 "Non-iterable object to varray conversion"
256 ArrayData
* castObjToDArray(ObjectData
* obj
) {
257 assertx(!RuntimeOption::EvalHackArrDVArrs
);
258 return castObjToHackArrImpl(
261 [](const Array
& arr
) { return arr
.toDArray(); },
262 [](Array
& arr
, ArrayIter
& iter
) { arr
.set(iter
.first(), iter
.second()); },
263 "Non-iterable object to darray conversion"
267 //////////////////////////////////////////////////////////////////////