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/apc-object.h"
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"
36 //////////////////////////////////////////////////////////////////////
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 //////////////////////////////////////////////////////////////////////
52 APCObject::APCObject(ClassOrName cls
, uint32_t propCount
)
53 : m_handle(APCKind::SharedObject
)
55 , m_propCount
{propCount
}
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
;
107 objProp
= objPropVec
+ i
;
110 auto val
= APCHandle::Create(tvAsCVarRef(objProp
), false,
111 APCHandleLevel::Inner
, true);
113 apcPropVec
[i
] = val
.handle
;
116 if (UNLIKELY(hasDynProps
)) {
117 auto val
= APCHandle::Create(objectData
->dynPropArray(), false,
118 APCHandleLevel::Inner
, true);
120 apcPropVec
[numRealProps
] = val
.handle
;
123 return {apcObj
->getHandle(), size
};
127 APCHandle::Pair
APCObject::ConstructSlow(ObjectData
* objectData
,
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
;
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] == '*') {
161 auto* ctx
= Unit::lookupClass(cls
.get());
162 if (ctx
&& ctx
->attrs() & AttrUnique
) {
165 prop
->ctx
= makeStaticString(cls
.get());
168 prop
->name
= makeStaticString(keySD
.substr(subLen
));
171 prop
->name
= makeStaticString(keySD
.get());
174 assert(prop
== apcObj
->props() + propCount
);
176 return {apcObj
->getHandle(), size
};
180 APCObject::~APCObject() {
181 auto const numProps
= m_propCount
;
184 auto props
= persistentProps();
185 for (unsigned i
= 0; i
< numProps
; ++i
) {
186 if (props
[i
]) props
[i
]->unreferenceRoot();
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
);
200 // No need to run Prop destructors.
204 //////////////////////////////////////////////////////////////////////
206 APCHandle::Pair
APCObject::MakeAPCObject(APCHandle
* obj
, const Variant
& value
) {
207 if (!value
.is(KindOfObject
) || obj
->objAttempted()) {
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
) {
217 auto tmp
= APCHandle::Create(value
, false, APCHandleLevel::Inner
, true);
218 tmp
.handle
->setObjAttempted();
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();
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.
247 for (; i
< numProps
; ++i
) {
248 new (objProp
+ i
) Variant(apcProp
[i
]->toLocal());
251 for (; i
< numProps
; ++i
) {
252 new (objProp
+ i
) Variant();
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();
274 Object
APCObject::createObjectSlow() const {
276 if (auto const c
= m_cls
.left()) {
279 klass
= Unit::loadClass(m_cls
.right());
281 Logger::Error("APCObject::getObject(): Cannot find class %s",
282 m_cls
.right()->data());
286 Object obj
{const_cast<Class
*>(klass
)};
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()) {
297 if (auto const cls
= prop
->ctx
.left()) {
300 ctx
= Unit::lookupClass(prop
->ctx
.right());
305 auto val
= prop
->val
? prop
->val
->toLocal() : init_null();
306 obj
->setProp(const_cast<Class
*>(ctx
), key
, *val
.asCell());
313 //////////////////////////////////////////////////////////////////////