Add sub-controls for Hack array compat runtime checks
[hiphop-php.git] / hphp / runtime / base / resource-data.h
blobc21066eb6754f2e1390e47b82bad1a6cf8318e5a
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 #ifndef incl_HPHP_RESOURCE_DATA_H_
18 #define incl_HPHP_RESOURCE_DATA_H_
20 #include <iostream>
21 #include "hphp/runtime/base/countable.h"
22 #include "hphp/runtime/base/sweepable.h"
23 #include "hphp/runtime/base/classname-is.h"
24 #include "hphp/runtime/base/req-ptr.h"
25 #include "hphp/runtime/base/memory-manager.h"
26 #include "hphp/util/thread-local.h"
28 namespace HPHP {
30 struct Array;
31 struct String;
32 struct ResourceData;
34 namespace req {
35 template<class T, class... Args>
36 typename std::enable_if<std::is_convertible<T*,ResourceData*>::value,
37 req::ptr<T>>::type
38 make(Args&&... args);
42 * De-virtualized header for Resource objects. The memory layout is:
44 * [ResourceHdr] { HeapObject, m_id; }
45 * [ResourceData] { vtbl, subclass fields; }
47 * Historically, we only had ResourceData. To ease refactoring, we have
48 * pointer conversion utilities:
49 * ResourceHdr* ResourceData::hdr()
50 * ResourceData* ResourceHdr::data()
52 * ResourceData explicitly declares inc/decref functions that
53 * delegate to ResourceHdr, which allows req::ptr<T> in user code to
54 * continue doing transparent refcounting.
56 * Type-agnostic header access requires TypedValue (and Variant) to have a
57 * ResourceHdr* ptr in the m_data union. We also still need to cast &m_data
58 * to a Resource**, so Resource owns a req::ptr<Resourcebase>.
60 * Runtime and extension code typically will use req::ptr<T> where T extends
61 * ResourceData; these are interior pointers, but allow code to continue
62 * using ResourceData as the base of the virtual class hierarchy.
64 * In the JIT, SSATmps of type Res are ResourceHdr pointers.
66 struct ResourceHdr final : Countable, // aux stores heap size
67 type_scan::MarkCollectable<ResourceHdr> {
68 static void resetMaxId();
70 ALWAYS_INLINE void decRefAndRelease() {
71 assert(kindIsValid());
72 if (decReleaseCheck()) release();
74 bool kindIsValid() const { return m_kind == HeaderKind::Resource; }
75 void release() noexcept;
77 void init(uint16_t size, type_scan::Index tyindex) {
78 assert(type_scan::isKnownType(tyindex));
79 initHeader_16(HeaderKind::Resource, OneReference, size);
80 m_type_index = tyindex;
83 ResourceData* data() {
84 assert(kindIsValid());
85 return reinterpret_cast<ResourceData*>(this + 1);
87 const ResourceData* data() const {
88 assert(kindIsValid());
89 return reinterpret_cast<const ResourceData*>(this + 1);
92 size_t heapSize() const {
93 assert(kindIsValid());
94 assert(m_aux16 != 0);
95 return m_aux16;
98 type_scan::Index typeIndex() const { return m_type_index; }
100 int32_t getId() const { return m_id; }
101 void setRawId(int32_t id) { m_id = id; }
102 void setId(int32_t id); // only for BuiltinFiles
104 private:
105 static_assert(sizeof(type_scan::Index) <= 4,
106 "type_scan::Index cannot be greater than 32-bits");
108 int32_t m_id;
109 type_scan::Index m_type_index;
113 * Base class of all PHP resources.
115 struct ResourceData : type_scan::MarkCollectable<ResourceData> {
116 ResourceData();
118 ResourceData(const ResourceData&) = delete;
119 ResourceData& operator=(const ResourceData&) = delete;
121 const ResourceHdr* hdr() const {
122 auto h = reinterpret_cast<const ResourceHdr*>(this) - 1;
123 assert(h->kindIsValid());
124 return h;
126 ResourceHdr* hdr() {
127 auto h = reinterpret_cast<ResourceHdr*>(this) - 1;
128 assert(h->kindIsValid());
129 return h;
132 // delegate refcount operations to base.
133 void incRefCount() const { hdr()->incRefCount(); }
134 void decRefAndRelease() { hdr()->decRefAndRelease(); }
135 bool hasExactlyOneRef() const { return hdr()->hasExactlyOneRef(); }
136 bool hasMultipleRefs() const { return hdr()->hasMultipleRefs(); }
137 int32_t getId() const { return hdr()->getId(); }
138 void setId(int32_t id) { hdr()->setId(id); }
140 virtual ~ResourceData(); // all PHP resources need vtables
142 void operator delete(void* /*p*/) { always_assert(false); }
144 const String& o_getClassName() const;
145 virtual const String& o_getClassNameHook() const;
146 virtual const String& o_getResourceName() const;
147 virtual bool isInvalid() const { return false; }
149 template <typename T>
150 bool instanceof() const { return dynamic_cast<const T*>(this) != nullptr; }
152 bool o_toBoolean() const { return true; }
153 int64_t o_toInt64() const { return hdr()->getId(); }
154 double o_toDouble() const { return hdr()->getId(); }
155 String o_toString() const;
156 Array o_toArray() const;
158 private:
159 template<class T, class... Args> friend
160 typename std::enable_if<std::is_convertible<T*,ResourceData*>::value,
161 req::ptr<T>>::type req::make(Args&&... args);
164 inline void ResourceHdr::release() noexcept {
165 assert(kindIsValid());
166 delete data();
167 AARCH64_WALKABLE_FRAME();
170 inline ResourceData* safedata(ResourceHdr* hdr) {
171 return hdr ? hdr->data() : nullptr;
173 inline const ResourceData* safedata(const ResourceHdr* hdr) {
174 return hdr ? hdr->data() : nullptr;
176 inline ResourceHdr* safehdr(ResourceData* data) {
177 return data ? data->hdr() : nullptr;
179 inline const ResourceHdr* safehdr(const ResourceData* data) {
180 return data ? data->hdr() : nullptr;
184 * Rules to avoid memory problems/leaks from ResourceData classes
185 * ==============================================================
187 * 1. If a ResourceData is entirely request-allocated, for example,
189 * struct EntirelyRequestAllocated : ResourceData {
190 * int number; // primitives are allocated together with "this"
191 * String str; // request-allocated objects are fine
192 * };
194 * Then, the best choice is to use this macro to make sure the object
195 * is always collected during request shutdown time:
197 * DECLARE_RESOURCE_ALLOCATION_NO_SWEEP(T);
199 * This object doesn't participate in sweep(), as object allocator doesn't
200 * have any callback installed.
202 * 2. If a ResourceData is entirely not request allocated, for example,
204 * struct NonRequestAllocated : SweepableResourceData {
205 * int number; // primitives are always not in consideration
206 * std::string str; // this has malloc() in its own
207 * std::vector<int> vec; // all STL collection classes belong here
208 * HANDLE ptr; // raw pointers that need to be free-d somehow
209 * };
211 * Then it has to derive from SweepableResourceData, so sweep() will be
212 * called. By default, it will call this object's destructor automatically,
213 * so everything will be free-d.
215 * When deriving from SweepableResourceData, either "new" or "NEW" can
216 * be used, but we prefer people use NEW with these macros:
218 * DECLARE_RESOURCE_ALLOCATION(T);
219 * IMPLEMENT_RESOURCE_ALLOCATION(T);
221 * 3. If a ResourceData is a mix of request allocated data members and globally
222 * allocated data members, sweep() has to be overwritten to only free
223 * the globally allocated members. This is because request-allocated
224 * data members may have their own sweep() defined to destruct, and another
225 * destruction from this ResourceData's default sweep() will cause double-
226 * free problems on these request-allocated data members.
228 * This means, std::vector<String> is almost always wrong, because there is
229 * no way to free up vector's memory without touching String, which is
230 * request-allocated.
232 * struct MixedRequestAllocated : SweepableResourceData {
233 * int number; // primitives are always not in consideration
235 * // STL classes need to new/delete to have clean sweep
236 * std::string *stdstr;
237 * std::vector<int> *vec;
239 * HANDLE ptr; // raw pointers that need to be free-d somehow
240 * String str; // request-allocated objects are fine
242 * DECLARE_RESOURCE_ALLOCATION(T);
243 * };
244 * void MixedRequestAllocated::sweep() {
245 * delete stdstr;
246 * delete vec;
247 * close_handle(ptr);
248 * // without doing anything with Strings, Arrays, or Objects
252 struct SweepableResourceData : ResourceData, Sweepable {
253 protected:
254 void* owner() override {
255 return static_cast<ResourceData*>(this)->hdr();
259 ///////////////////////////////////////////////////////////////////////////////
261 ALWAYS_INLINE void decRefRes(ResourceData* res) {
262 res->hdr()->decRefAndRelease();
264 ALWAYS_INLINE void decRefRes(ResourceHdr* res) {
265 res->decRefAndRelease();
268 #define DECLARE_RESOURCE_ALLOCATION_NO_SWEEP(T) \
269 public: \
270 ALWAYS_INLINE void operator delete(void* p) { \
271 static_assert(std::is_base_of<ResourceData,T>::value, ""); \
272 constexpr auto size = sizeof(ResourceHdr) + sizeof(T); \
273 auto h = static_cast<ResourceData*>(p)->hdr(); \
274 assert(h->heapSize() == size); \
275 tl_heap->objFree(h, size); \
278 #define DECLARE_RESOURCE_ALLOCATION(T) \
279 DECLARE_RESOURCE_ALLOCATION_NO_SWEEP(T) \
280 void sweep() override;
282 #define IMPLEMENT_RESOURCE_ALLOCATION_NS(NS, T) \
283 static_assert(std::is_base_of<HPHP::ResourceData,NS::T>::value, ""); \
284 void NS::T::sweep() { this->~T(); }
286 #define IMPLEMENT_RESOURCE_ALLOCATION(T) \
287 IMPLEMENT_RESOURCE_ALLOCATION_NS(HPHP, T)
289 namespace req {
290 // allocate and construct a resource subclass type T,
291 // wrapped in a req::ptr<T>
292 template<class T, class... Args>
293 typename std::enable_if<
294 std::is_convertible<T*, ResourceData*>::value,
295 req::ptr<T>
296 >::type make(Args&&... args) {
297 constexpr auto size = sizeof(ResourceHdr) + sizeof(T);
298 static_assert(uint16_t(size) == size, "size must fit in 16 bits");
299 static_assert(std::is_convertible<T*,ResourceData*>::value, "");
300 auto const b = static_cast<ResourceHdr*>(tl_heap->objMalloc(size));
301 // initialize HeapObject
302 b->init(size, type_scan::getIndexForMalloc<T>());
303 try {
304 auto r = new (b->data()) T(std::forward<Args>(args)...);
305 assert(r->hasExactlyOneRef());
306 return req::ptr<T>::attach(r);
307 } catch (...) {
308 tl_heap->objFree(b, size);
309 throw;
312 } // namespace req
314 ///////////////////////////////////////////////////////////////////////////////
317 #endif // incl_HPHP_RESOURCE_DATA_H_