this repo has no description
1/* Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) */
2#pragma once
3
4#include <cstdint>
5
6#include "cpython-types.h"
7
8#include "handles.h"
9#include "objects.h"
10
11namespace py {
12
13class PointerVisitor;
14
15static const Py_ssize_t kImmediateRefcnt = Py_ssize_t{1} << 63;
16
17class ApiHandle : public PyObject {
18 public:
19 // Returns a handle for a managed object. Increments the reference count of
20 // the handle.
21 static ApiHandle* newReference(Runtime* runtime, RawObject obj);
22
23 // Returns a handle for a managed object. This must not be called with an
24 // extension object or an object for which `isEncodeableAsImmediate` is true.
25 static ApiHandle* newReferenceWithManaged(Runtime* runtime, RawObject obj);
26
27 // Returns a handle for a managed object. Does not affect the reference count
28 // of the handle.
29 static ApiHandle* borrowedReference(Runtime* runtime, RawObject obj);
30
31 static ApiHandle* handleFromImmediate(RawObject obj);
32
33 // Returns the managed object associated with the handle. Decrements the
34 // reference count of handle.
35 static RawObject stealReference(PyObject* py_obj);
36
37 // Returns the managed object associated with the handle checking for
38 static RawObject checkFunctionResult(Thread* thread, PyObject* result);
39
40 static ApiHandle* fromPyObject(PyObject* py_obj);
41
42 static ApiHandle* fromPyTypeObject(PyTypeObject* type);
43
44 // Get the object from the handle's reference field.
45 static RawObject asObject(ApiHandle* handle);
46
47 static RawObject asObjectImmediate(ApiHandle* handle);
48
49 static RawObject asObjectNoImmediate(ApiHandle* handle);
50
51 // Return native proxy belonging to an extension object.
52 static RawNativeProxy asNativeProxy(ApiHandle* handle);
53
54 // Each ApiHandle can have one pointer to cached data, which will be freed
55 // when the handle is destroyed.
56 static void* cache(Runtime* runtime, ApiHandle* handle);
57 static void setCache(Runtime* runtime, ApiHandle* handle, void* value);
58
59 // Decrements the reference count of the handle to signal the removal of a
60 // reference count from extension code.
61 static void decref(ApiHandle* handle);
62
63 static void decrefNoImmediate(ApiHandle* handle);
64
65 // Remove the ApiHandle from the dictionary and free its memory
66 static void dispose(ApiHandle* handle);
67 static void disposeWithRuntime(Runtime* runtime, ApiHandle* handle);
68
69 static bool isImmediate(ApiHandle* handle);
70
71 // Increments the reference count of the handle to signal the addition of a
72 // reference from extension code.
73 static void incref(ApiHandle* handle);
74
75 static void increfNoImmediate(ApiHandle* handle);
76
77 // Returns the number of references to this handle from extension code.
78 static Py_ssize_t refcnt(ApiHandle* handle);
79
80 static Py_ssize_t refcntNoImmediate(ApiHandle* handle);
81
82 static void setRefcnt(ApiHandle* handle, Py_ssize_t count);
83
84 static void setBorrowedNoImmediate(ApiHandle* handle);
85 static bool isBorrowedNoImmediate(ApiHandle* handle);
86
87 private:
88 static bool isEncodeableAsImmediate(RawObject obj);
89
90 static const Py_ssize_t kBorrowedBit = Py_ssize_t{1} << 63;
91
92 static const long kImmediateTag = 0x1;
93 static const long kImmediateMask = 0x7;
94
95 static_assert(kBorrowedBit == kImmediateRefcnt,
96 "keep kBorrowedBit and kImmediateRefcnt in sync");
97 static_assert(kImmediateMask < alignof(PyObject*),
98 "Stronger alignment guarantees are required for immediate "
99 "tagged PyObject* to work.");
100
101 DISALLOW_IMPLICIT_CONSTRUCTORS(ApiHandle);
102};
103
104static_assert(sizeof(ApiHandle) == sizeof(PyObject),
105 "ApiHandle must not add members to PyObject");
106
107struct FreeListNode {
108 FreeListNode* next;
109};
110
111static_assert(sizeof(FreeListNode) <= sizeof(ApiHandle),
112 "Free ApiHandle should be usable as a FreeListNode");
113
114inline RawObject ApiHandle::asObject(ApiHandle* handle) {
115 if (isImmediate(handle)) return asObjectImmediate(handle);
116 return asObjectNoImmediate(handle);
117}
118
119inline RawObject ApiHandle::asObjectImmediate(ApiHandle* handle) {
120 DCHECK(isImmediate(handle), "expected immediate");
121 return RawObject{reinterpret_cast<uword>(handle) ^ kImmediateTag};
122}
123
124inline RawObject ApiHandle::asObjectNoImmediate(ApiHandle* handle) {
125 DCHECK(!isImmediate(handle), "must not be called with immediate object");
126 return RawObject{handle->reference_};
127}
128
129inline void ApiHandle::decref(ApiHandle* handle) {
130 if (isImmediate(handle)) return;
131 decrefNoImmediate(handle);
132}
133
134inline void ApiHandle::decrefNoImmediate(ApiHandle* handle) {
135 DCHECK(!isImmediate(handle), "must not be called with immediate object");
136 DCHECK((handle->ob_refcnt & ~kBorrowedBit) > 0, "reference count underflow");
137 --handle->ob_refcnt;
138 // Dispose `ApiHandle`s without `kBorrowedBit` when they reach refcount zero.
139 if (handle->ob_refcnt == 0) {
140 dispose(handle);
141 }
142}
143
144inline ApiHandle* ApiHandle::fromPyObject(PyObject* py_obj) {
145 return static_cast<ApiHandle*>(py_obj);
146}
147
148inline ApiHandle* ApiHandle::fromPyTypeObject(PyTypeObject* type) {
149 return fromPyObject(reinterpret_cast<PyObject*>(type));
150}
151
152inline ApiHandle* ApiHandle::handleFromImmediate(RawObject obj) {
153 DCHECK(isEncodeableAsImmediate(obj), "expected immediate");
154 return reinterpret_cast<ApiHandle*>(obj.raw() ^ kImmediateTag);
155}
156
157inline void ApiHandle::incref(ApiHandle* handle) {
158 if (isImmediate(handle)) return;
159 // fprintf(stderr, "incref(%p)\n", (void*)this);
160 increfNoImmediate(handle);
161}
162
163inline void ApiHandle::increfNoImmediate(ApiHandle* handle) {
164 DCHECK(!isImmediate(handle), "must not be called with immediate object");
165 DCHECK((handle->ob_refcnt & ~kBorrowedBit) <
166 (std::numeric_limits<Py_ssize_t>::max() & ~kBorrowedBit),
167 "Reference count overflowed");
168 ++handle->ob_refcnt;
169}
170
171inline bool ApiHandle::isImmediate(ApiHandle* handle) {
172 return (reinterpret_cast<uword>(handle) & kImmediateMask) != 0;
173}
174
175inline Py_ssize_t ApiHandle::refcnt(ApiHandle* handle) {
176 if (isImmediate(handle)) return kImmediateRefcnt;
177 return refcntNoImmediate(handle);
178}
179
180inline Py_ssize_t ApiHandle::refcntNoImmediate(ApiHandle* handle) {
181 DCHECK(!isImmediate(handle), "must not be called with immediate object");
182 return handle->ob_refcnt & ~kBorrowedBit;
183}
184
185inline void ApiHandle::setBorrowedNoImmediate(ApiHandle* handle) {
186 DCHECK(!isImmediate(handle), "must not be called with immediate object");
187 handle->ob_refcnt |= kBorrowedBit;
188}
189
190inline bool ApiHandle::isBorrowedNoImmediate(ApiHandle* handle) {
191 DCHECK(!isImmediate(handle), "must not be called with immediate object");
192 return (handle->ob_refcnt & kBorrowedBit) != 0;
193}
194
195inline RawObject ApiHandle::stealReference(PyObject* py_obj) {
196 ApiHandle* handle = ApiHandle::fromPyObject(py_obj);
197 if (isImmediate(handle)) return asObjectImmediate(handle);
198 DCHECK((handle->ob_refcnt & ~kBorrowedBit) > 0, "refcount underflow");
199 // Mark stolen reference as borrowed. This is to support code like this that
200 // increases refcount after the fact:
201 // PyModule_AddObject(..., x);
202 // Py_INCREF(x);
203 handle->ob_refcnt |= kBorrowedBit;
204 handle->ob_refcnt--;
205 return asObjectNoImmediate(handle);
206}
207
208} // namespace py