Add sub-controls for Hack array compat runtime checks
[hiphop-php.git] / hphp / runtime / base / apc-collection.cpp
blob93ced10b1695a8fe6192e568ae89443887901069
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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/apc-collection.h"
18 #include "hphp/runtime/base/apc-object.h"
19 #include "hphp/runtime/base/apc-array.h"
20 #include "hphp/runtime/base/apc-stats.h"
21 #include "hphp/runtime/base/object-data.h"
22 #include "hphp/runtime/base/type-object.h"
23 #include "hphp/runtime/ext/apc/ext_apc.h"
24 #include "hphp/runtime/base/collections.h"
25 #include "hphp/runtime/ext/collections/ext_collections-map.h"
26 #include "hphp/runtime/ext/collections/ext_collections-set.h"
27 #include "hphp/runtime/ext/collections/ext_collections-vector.h"
28 #include "hphp/runtime/base/data-walker.h"
30 namespace HPHP {
32 namespace {
34 //////////////////////////////////////////////////////////////////////
36 void fillMap(BaseMap* map, const APCArray* ar) {
37 for (auto i = uint32_t{0}; i < ar->size(); ++i) {
38 map->set(*ar->getKey(i).asTypedValue(),
39 *ar->getValue(i)->toLocal().asTypedValue());
43 template<class T>
44 void fillCollection(T* coll, const APCArray* ar) {
45 for (auto i = uint32_t{0}; i < ar->size(); ++i) {
46 coll->add(*ar->getValue(i)->toLocal().asTypedValue());
50 // Deserializing an array could give back a different ArrayKind than we need,
51 // so we have to go with the slow case of calling a collection constructor.
52 NEVER_INLINE
53 Object createFromSerialized(CollectionType colType, APCHandle* handle) {
54 auto const col = Object::attach(collections::alloc(colType));
55 auto const arr = handle->toLocal();
56 switch (colType) {
57 case CollectionType::ImmVector:
58 case CollectionType::Vector:
59 static_cast<BaseVector*>(col.get())->init(arr);
60 break;
61 case CollectionType::ImmSet:
62 case CollectionType::Set:
63 static_cast<BaseSet*>(col.get())->init(arr);
64 break;
65 case CollectionType::ImmMap:
66 case CollectionType::Map:
67 static_cast<BaseMap*>(col.get())->init(arr);
68 break;
69 case CollectionType::Pair:
70 not_reached();
71 break;
73 return col;
76 //////////////////////////////////////////////////////////////////////
80 APCHandle::Pair APCCollection::Make(const ObjectData* obj,
81 APCHandleLevel level,
82 bool unserializeObj) {
83 auto bail = [&] {
84 return APCString::MakeSerializedObject(
85 apc_serialize(Variant(const_cast<ObjectData*>(obj)))
89 auto const array = collections::asArray(obj);
90 if (!array) return bail();
93 * Create an uncounted array if we can.
95 * If this collection is an OuterHandle, then we need to do a full check on
96 * this array for things like circularity. If we're an InnerHandle, someone
97 * already checked that, but we want to check for whether it's uncounted to
98 * use a better representation. For the OuterHandle case, we just delegate
99 * to APCArray below (which will do the full DataWalker pass).
101 if (level == APCHandleLevel::Inner && apcExtension::UseUncounted &&
102 !array->empty()) {
103 DataWalker walker(DataWalker::LookupFeature::HasObjectOrResource);
104 auto const features = walker.traverseData(const_cast<ArrayData*>(array));
105 assert(!features.isCircular);
106 if (!features.hasObjectOrResource) {
107 auto const makeUncounted = [&] () {
108 if (isVectorCollection(obj->collectionType())) {
109 return APCArray::MakeUncountedVec(const_cast<ArrayData*>(array));
111 return APCArray::MakeUncountedDict(const_cast<ArrayData*>(array));
113 return WrapArray(
114 { makeUncounted(), getMemSize(array) + sizeof(APCTypedValue) },
115 obj->collectionType()
120 auto const makeShared = [&] () {
121 if (isVectorCollection(obj->collectionType())) {
122 return APCArray::MakeSharedVec(const_cast<ArrayData*>(array),
123 level,
124 unserializeObj);
126 return APCArray::MakeSharedDict(const_cast<ArrayData*>(array),
127 level,
128 unserializeObj);
130 return WrapArray(makeShared(), obj->collectionType());
133 void APCCollection::Delete(APCHandle* h) {
134 assert(offsetof(APCCollection, m_handle) == 0);
135 delete reinterpret_cast<APCCollection*>(h);
138 APCCollection::APCCollection()
139 : m_handle(APCKind::SharedCollection)
142 APCCollection::~APCCollection() {
143 // Zero for size is correct, because when this APCCollection was unreferenced
144 // it already included the size of the inner handle.
145 m_arrayHandle->unreferenceRoot(0);
148 APCHandle::Pair APCCollection::WrapArray(APCHandle::Pair inner,
149 CollectionType colType) {
150 auto const col = new APCCollection;
151 col->m_arrayHandle = inner.handle;
152 col->m_colType = colType;
153 return { &col->m_handle, inner.size + sizeof(APCCollection) };
156 Object APCCollection::createObject() const {
157 if (m_arrayHandle->isTypedValue()) {
158 Variant local(m_arrayHandle->toLocal());
159 assert(local.isArray());
160 return Object::attach(
161 collections::alloc(m_colType, local.getArrayData())
165 if (UNLIKELY(m_arrayHandle->kind() == APCKind::SerializedVec ||
166 m_arrayHandle->kind() == APCKind::SerializedDict)) {
167 return createFromSerialized(m_colType, m_arrayHandle);
170 // We had a counted inner array---we need to do an O(N) copy to get the
171 // collection into the request local heap.
172 auto const apcArr = APCArray::fromHandle(m_arrayHandle);
173 auto const col = Object::attach(collections::alloc(m_colType));
174 switch (m_colType) {
175 case CollectionType::ImmVector:
176 case CollectionType::Vector:
177 fillCollection(static_cast<BaseVector*>(col.get()), apcArr);
178 break;
179 case CollectionType::ImmSet:
180 case CollectionType::Set:
181 fillCollection(static_cast<BaseSet*>(col.get()), apcArr);
182 break;
183 case CollectionType::ImmMap:
184 case CollectionType::Map:
185 fillMap(static_cast<BaseMap*>(col.get()), apcArr);
186 break;
187 case CollectionType::Pair:
188 always_assert(0);
189 break;
191 return col;
194 //////////////////////////////////////////////////////////////////////