Enable HHIRInliningUseReachableCost by default, tweak inlining constants
[hiphop-php.git] / hphp / runtime / base / array-common.cpp
blobd3b7c7d2cd9e43e0d472207e116aa187cd3de4e9
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
29 namespace HPHP {
31 //////////////////////////////////////////////////////////////////////
33 ssize_t ArrayCommon::ReturnInvalidIndex(const ArrayData*) {
34 return 0;
37 ArrayData* ArrayCommon::Pop(ArrayData* a, Variant &value) {
38 if (!a->empty()) {
39 auto const pos = a->iter_last();
40 value = a->getValue(pos);
41 return a->remove(a->getKey(pos));
43 value = uninit_null();
44 return a;
47 ArrayData* ArrayCommon::Dequeue(ArrayData* a, Variant &value) {
48 if (!a->empty()) {
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
53 ret->renumber();
54 return ret;
56 value = uninit_null();
57 return a;
60 ArrayData* ArrayCommon::ToVec(ArrayData* a, bool) {
61 auto const size = a->size();
62 if (!size) return staticEmptyVecArray();
63 VecArrayInit init{size};
64 IterateVNoInc(
66 [&](TypedValue v) {
67 if (UNLIKELY(isRefType(v.m_type))) {
68 if (v.m_data.pref->isReferenced()) {
69 throwRefInvalidArrayValueException(init.toArray());
72 init.append(v);
75 return init.create();
78 ArrayData* ArrayCommon::ToDict(ArrayData* a, bool) {
79 auto const size = a->size();
80 if (!size) return staticEmptyDictArray();
81 DictInit init{size};
82 IterateKVNoInc(
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);
93 return init.create();
96 ArrayData* ArrayCommon::ToKeyset(ArrayData* a, bool) {
97 auto const size = a->size();
98 if (!size) return staticEmptyKeysetArray();
99 KeysetInit init{size};
100 IterateVNoInc(
102 [&](TypedValue v) {
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);
115 } else {
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};
137 IterateKV(
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();
151 return arr;
154 ArrayCommon::RefCheckResult
155 ArrayCommon::CheckForRefs(const ArrayData* ad) {
156 auto result = RefCheckResult::Pass;
157 IterateVNoInc(
159 [&](TypedValue v) {
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;
164 return true;
166 result = RefCheckResult::Collapse;
168 return false;
171 return result;
174 //////////////////////////////////////////////////////////////////////
176 namespace {
178 template <typename E, typename C, typename A>
179 ALWAYS_INLINE
180 ArrayData* castObjToHackArrImpl(ObjectData* obj,
181 E empty,
182 C cast,
183 A add,
184 const char* msg) {
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.
194 obj->incRefCount();
195 SCOPE_EXIT { decRefObj(obj); };
197 bool isIter;
198 auto iterObj = obj->iterableObject(isIter);
199 if (!isIter) SystemLib::throwInvalidOperationExceptionObject(msg);
201 auto arr = empty();
202 for (ArrayIter iter(iterObj); iter; ++iter) add(arr, iter);
203 return arr.detach();
208 ArrayData* castObjToVec(ObjectData* obj) {
209 return castObjToHackArrImpl(
210 obj,
211 Array::CreateVec,
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(
220 obj,
221 Array::CreateDict,
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(
230 obj,
231 Array::CreateKeyset,
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(
241 obj,
242 Array::CreateVArray,
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(
253 obj,
254 Array::CreateDArray,
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 //////////////////////////////////////////////////////////////////////