Add sub-controls for Hack array compat runtime checks
[hiphop-php.git] / hphp / runtime / base / apc-object.cpp
blob1f19ab217be0f5a3c6f4fe2d2a8391dbd85f236a
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/apc-object.h"
19 #include <cstdlib>
21 #include "hphp/util/logger.h"
23 #include "hphp/runtime/base/apc-collection.h"
24 #include "hphp/runtime/base/apc-handle-defs.h"
25 #include "hphp/runtime/base/apc-handle.h"
26 #include "hphp/runtime/base/array-init.h"
27 #include "hphp/runtime/base/array-iterator.h"
28 #include "hphp/runtime/base/builtin-functions.h"
29 #include "hphp/runtime/base/data-walker.h"
30 #include "hphp/runtime/base/externals.h"
31 #include "hphp/runtime/base/tv-type.h"
32 #include "hphp/runtime/ext/apc/ext_apc.h"
34 namespace HPHP {
36 //////////////////////////////////////////////////////////////////////
38 namespace {
40 APCObject::ClassOrName make_class(const Class* c) {
41 if (classHasPersistentRDS(c)) return c;
42 return c->preClass()->name();
45 const StaticString s___wakeup("__wakeup");
49 //////////////////////////////////////////////////////////////////////
51 ALWAYS_INLINE
52 APCObject::APCObject(ClassOrName cls, uint32_t propCount)
53 : m_handle(APCKind::SharedObject)
54 , m_cls{cls}
55 , m_propCount{propCount}
56 , m_persistent{0}
57 , m_no_wakeup{0}
58 , m_fast_init{0}
61 APCHandle::Pair APCObject::Construct(ObjectData* objectData) {
62 // This function assumes the object and object/array down the tree have no
63 // internal references and do not implement the serializable interface.
64 assert(!objectData->instanceof(SystemLib::s_SerializableClass));
66 auto cls = objectData->getVMClass();
67 auto clsOrName = make_class(cls);
68 if (clsOrName.right()) return ConstructSlow(objectData, clsOrName);
70 // We have a persistent Class. Build an array of APCHandle* to mirror the
71 // declared properties in the object.
72 auto const propInfo = cls->declProperties();
73 auto const hasDynProps = objectData->hasDynProps();
74 auto const numRealProps = propInfo.size();
75 auto const numApcProps = numRealProps + hasDynProps;
76 auto size = sizeof(APCObject) + sizeof(APCHandle*) * numApcProps;
77 auto const apcObj = new (malloc_huge(size)) APCObject(clsOrName, numApcProps);
78 apcObj->m_persistent = 1;
80 // Set a few more flags for faster fetching: whether or not the object has a
81 // wakeup method, and whether or not we can use a fast path that avoids
82 // default-initializing properties before setting them to their APC values.
83 if (!cls->lookupMethod(s___wakeup.get())) apcObj->m_no_wakeup = 1;
84 if (!cls->instanceCtor()) {
85 apcObj->m_fast_init = 1;
88 auto const apcPropVec = apcObj->persistentProps();
89 auto const objPropVec = objectData->propVec();
90 const TypedValueAux* propInit = nullptr;
92 for (unsigned i = 0; i < numRealProps; ++i) {
93 auto const attrs = propInfo[i].attrs;
94 assert((attrs & AttrStatic) == 0);
96 const TypedValue* objProp;
97 if (attrs & AttrBuiltin) {
98 // Special properties like the Memoize cache should be set to their
99 // default value, not the current value.
100 if (propInit == nullptr) {
101 propInit = cls->pinitVec().empty() ? &cls->declPropInit()[0]
102 : &(*cls->getPropData())[0];
105 objProp = propInit + i;
106 } else {
107 objProp = objPropVec + i;
110 auto val = APCHandle::Create(tvAsCVarRef(objProp), false,
111 APCHandleLevel::Inner, true);
112 size += val.size;
113 apcPropVec[i] = val.handle;
116 if (UNLIKELY(hasDynProps)) {
117 auto val = APCHandle::Create(objectData->dynPropArray(), false,
118 APCHandleLevel::Inner, true);
119 size += val.size;
120 apcPropVec[numRealProps] = val.handle;
123 return {apcObj->getHandle(), size};
126 NEVER_INLINE
127 APCHandle::Pair APCObject::ConstructSlow(ObjectData* objectData,
128 ClassOrName name) {
129 Array odProps;
130 objectData->o_getArray(odProps);
131 auto const propCount = odProps.size();
133 auto size = sizeof(APCObject) + sizeof(Prop) * propCount;
134 auto const apcObj = new (malloc_huge(size)) APCObject(name, propCount);
135 if (!propCount) return {apcObj->getHandle(), size};
137 auto prop = apcObj->props();
138 for (ArrayIter it(odProps); !it.end(); it.next(), ++prop) {
139 Variant key(it.first());
140 assert(key.isString());
141 auto const rval = it.secondRval();
142 if (!isNullType(rval.unboxed().type())) {
143 auto val = APCHandle::Create(VarNR(rval.tv()), false,
144 APCHandleLevel::Inner, true);
145 prop->val = val.handle;
146 size += val.size;
147 } else {
148 prop->val = nullptr;
151 const String& keySD = key.asCStrRef();
153 if (!keySD.empty() && *keySD.data() == '\0') {
154 int32_t subLen = keySD.find('\0', 1) + 1;
155 String cls = keySD.substr(1, subLen - 2);
156 if (cls.size() == 1 && cls[0] == '*') {
157 // Protected.
158 prop->ctx = nullptr;
159 } else {
160 // Private.
161 auto* ctx = Unit::lookupClass(cls.get());
162 if (ctx && ctx->attrs() & AttrUnique) {
163 prop->ctx = ctx;
164 } else {
165 prop->ctx = makeStaticString(cls.get());
168 prop->name = makeStaticString(keySD.substr(subLen));
169 } else {
170 prop->ctx = nullptr;
171 prop->name = makeStaticString(keySD.get());
174 assert(prop == apcObj->props() + propCount);
176 return {apcObj->getHandle(), size};
179 ALWAYS_INLINE
180 APCObject::~APCObject() {
181 auto const numProps = m_propCount;
183 if (m_persistent) {
184 auto props = persistentProps();
185 for (unsigned i = 0; i < numProps; ++i) {
186 if (props[i]) props[i]->unreferenceRoot();
188 return;
191 for (auto i = uint32_t{0}; i < numProps; ++i) {
192 if (props()[i].val) props()[i].val->unreferenceRoot();
193 assert(props()[i].name->isStatic());
197 void APCObject::Delete(APCHandle* handle) {
198 auto const obj = fromHandle(handle);
199 obj->~APCObject();
200 // No need to run Prop destructors.
201 free_huge(obj);
204 //////////////////////////////////////////////////////////////////////
206 APCHandle::Pair APCObject::MakeAPCObject(APCHandle* obj, const Variant& value) {
207 if (!value.is(KindOfObject) || obj->objAttempted()) {
208 return {nullptr, 0};
210 obj->setObjAttempted();
211 ObjectData* o = value.getObjectData();
212 DataWalker walker(DataWalker::LookupFeature::DetectSerializable);
213 DataWalker::DataFeature features = walker.traverseData(o);
214 if (features.isCircular || features.hasSerializable) {
215 return {nullptr, 0};
217 auto tmp = APCHandle::Create(value, false, APCHandleLevel::Inner, true);
218 tmp.handle->setObjAttempted();
219 return tmp;
222 Variant APCObject::MakeLocalObject(const APCHandle* handle) {
223 auto apcObj = APCObject::fromHandle(handle);
224 return apcObj->m_persistent ? apcObj->createObject()
225 : apcObj->createObjectSlow();
228 Object APCObject::createObject() const {
229 auto cls = m_cls.left();
230 assert(cls != nullptr);
232 auto obj = Object::attach(
233 m_fast_init ? ObjectData::newInstanceNoPropInit(const_cast<Class*>(cls))
234 : ObjectData::newInstance(const_cast<Class*>(cls))
237 auto const numProps = cls->numDeclProperties();
238 auto const objProp = obj->propVecForConstruct();
239 auto const apcProp = persistentProps();
241 if (m_fast_init) {
242 // re-entry is possible while we're executing toLocal() on each
243 // property, so heap inspectors may see partially initid objects
244 // not yet exposed to PHP.
245 unsigned i = 0;
246 try {
247 for (; i < numProps; ++i) {
248 new (objProp + i) Variant(apcProp[i]->toLocal());
250 } catch (...) {
251 for (; i < numProps; ++i) {
252 new (objProp + i) Variant();
254 throw;
256 } else {
257 for (unsigned i = 0; i < numProps; ++i) {
258 tvAsVariant(&objProp[i]) = apcProp[i]->toLocal();
262 if (UNLIKELY(numProps < m_propCount)) {
263 auto dynProps = apcProp[numProps];
264 assert(dynProps->kind() == APCKind::StaticArray ||
265 dynProps->kind() == APCKind::UncountedArray ||
266 dynProps->kind() == APCKind::SharedArray);
267 obj->setDynPropArray(dynProps->toLocal().asCArrRef());
270 if (!m_no_wakeup) obj->invokeWakeup();
271 return obj;
274 Object APCObject::createObjectSlow() const {
275 const Class* klass;
276 if (auto const c = m_cls.left()) {
277 klass = c;
278 } else {
279 klass = Unit::loadClass(m_cls.right());
280 if (!klass) {
281 Logger::Error("APCObject::getObject(): Cannot find class %s",
282 m_cls.right()->data());
283 return Object{};
286 Object obj{const_cast<Class*>(klass)};
288 auto prop = props();
289 auto const propEnd = prop + m_propCount;
290 for (; prop != propEnd; ++prop) {
291 auto const key = prop->name;
293 const Class* ctx = nullptr;
294 if (prop->ctx.isNull()) {
295 ctx = klass;
296 } else {
297 if (auto const cls = prop->ctx.left()) {
298 ctx = cls;
299 } else {
300 ctx = Unit::lookupClass(prop->ctx.right());
301 if (!ctx) continue;
305 auto val = prop->val ? prop->val->toLocal() : init_null();
306 obj->setProp(const_cast<Class*>(ctx), key, *val.asCell());
309 obj->invokeWakeup();
310 return obj;
313 //////////////////////////////////////////////////////////////////////