Skip to content

Commit 9d8efcd

Browse files
committed
First commit.
Signed-off-by: Matthew A Johnson <matjoh@microsoft.com>
1 parent 0fb18b0 commit 9d8efcd

44 files changed

Lines changed: 2151 additions & 416 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,6 @@ Python/frozen_modules/MANIFEST
156156
# Ignore ./python binary on Unix but still look into ./Python/ directory.
157157
/python
158158
!/Python/
159+
160+
# Ignore the build directory.
161+
build*

Doc/c-api/typeobj.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2591,9 +2591,9 @@ Slot Type typedefs
25912591
The purpose of this function is to separate memory allocation from memory
25922592
initialization. It should return a pointer to a block of memory of adequate
25932593
length for the instance, suitably aligned, and initialized to zeros, but with
2594-
:c:member:`~PyObject.ob_refcnt` set to ``1`` and :c:member:`~PyObject.ob_type` set to the type argument. If
2594+
:c:member:`~PyObject.ob_refcnt` set to ``2`` and :c:member:`~PyObject.ob_type` set to the type argument. If
25952595
the type's :c:member:`~PyTypeObject.tp_itemsize` is non-zero, the object's :c:member:`~PyVarObject.ob_size` field
2596-
should be initialized to *nitems* and the length of the allocated memory block
2596+
should be initialized to *nitems * 2* and the length of the allocated memory block
25972597
should be ``tp_basicsize + nitems*tp_itemsize``, rounded up to a multiple of
25982598
``sizeof(void*)``; otherwise, *nitems* is not used and the length of the block
25992599
should be :c:member:`~PyTypeObject.tp_basicsize`.

Doc/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@
215215
('c:data', 'PyExc_UnicodeError'),
216216
('c:data', 'PyExc_UnicodeTranslateError'),
217217
('c:data', 'PyExc_ValueError'),
218+
('c:data', 'PyExc_NotWriteableError'),
218219
('c:data', 'PyExc_ZeroDivisionError'),
219220
# C API: Standard Python warning classes
220221
('c:data', 'PyExc_BytesWarning'),

Doc/data/stable_abi.dat

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/cpython/pyerrors.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,13 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalErrorFormat(
168168
const char *format,
169169
...);
170170

171+
PyAPI_FUNC(PyObject *) _PyErr_WriteToImmutable(const char* file, int line, PyObject *obj);
172+
#define PyErr_WriteToImmutable(obj) _PyErr_WriteToImmutable(__FILE__, __LINE__, _PyObject_CAST(obj))
173+
174+
PyAPI_FUNC(PyObject *) _PyErr_WriteToImmutableKey(const char* file, int line, PyObject *key);
175+
#define PyErr_WriteToImmutableKey(key) _PyErr_WriteToImmutableKey(__FILE__, __LINE__, _PyObject_CAST(key))
176+
177+
171178
extern PyObject *_PyErr_SetImportErrorWithNameFrom(
172179
PyObject *,
173180
PyObject *,

Include/internal/pycore_dict.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,24 @@ typedef struct {
2424
/* Cached hash code of me_key. */
2525
Py_hash_t me_hash;
2626
PyObject *me_key;
27-
PyObject *me_value; /* This field is only meaningful for combined tables */
27+
PyObject *_me_value; /* This field is only meaningful for combined tables */
2828
} PyDictKeyEntry;
2929

3030
typedef struct {
3131
PyObject *me_key; /* The key must be Unicode and have hash. */
32-
PyObject *me_value; /* This field is only meaningful for combined tables */
32+
PyObject *_me_value; /* This field is only meaningful for combined tables */
3333
} PyDictUnicodeEntry;
3434

35+
#define _PyDictEntry_IsImmutable(entry) (((uintptr_t)((entry)->_me_value)) & 0x1)
36+
#define _PyDictEntry_SetImmutable(entry) ((entry)->_me_value = (PyObject*)((uintptr_t)(entry)->_me_value | 0x1))
37+
#define _PyDictEntry_Hash(entry) ((entry)->me_hash)
38+
#define _PyDictEntry_Key(entry) ((entry)->me_key)
39+
#define _PyDictEntry_Value(entry) ((PyObject*)((((uintptr_t)((entry)->_me_value)) >> 1) << 1))
40+
#define _PyDictEntry_SetValue(entry, value) ((entry)->_me_value = value)
41+
#define _PyDictEntry_IsEmpty(entry) ((entry)->_me_value == NULL)
42+
43+
extern PyObject *_PyDict_IsKeyImmutable(PyObject* op, PyObject* key);
44+
3545
extern PyDictKeysObject *_PyDict_NewKeysForClass(void);
3646
extern PyObject *_PyDict_FromKeys(PyObject *, PyObject *, PyObject *);
3747

@@ -50,6 +60,7 @@ extern Py_ssize_t _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t has
5060
extern Py_ssize_t _PyDict_LookupIndex(PyDictObject *, PyObject *);
5161
extern Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject *key);
5262
extern PyObject *_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *);
63+
extern PyObject *_PyDict_SetKeyImmutable(PyDictObject *mp, PyObject *key);
5364

5465
/* Consumes references to key and value */
5566
extern int _PyDict_SetItem_Take2(PyDictObject *op, PyObject *key, PyObject *value);

Include/internal/pycore_frame.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ _PyFrame_MakeAndSetFrameObject(_PyInterpreterFrame *frame);
198198

199199
/* Gets the PyFrameObject for this frame, lazily
200200
* creating it if necessary.
201-
* Returns a borrowed referennce */
201+
* Returns a borrowed reference */
202202
static inline PyFrameObject *
203203
_PyFrame_GetFrameObject(_PyInterpreterFrame *frame)
204204
{
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef Py_FREEZE_H
2+
#define Py_FREEZE_H
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
8+
#ifndef Py_BUILD_CORE
9+
# error "this header requires Py_BUILD_CORE define"
10+
#endif
11+
12+
PyObject* _Py_Freeze(PyObject*);
13+
#define Py_Freeze(op) _Py_Freeze(_PyObject_CAST(op))
14+
15+
PyObject* _Py_FreezeGlobals(void);
16+
17+
bool _Py_GlobalsImmutable_Check(void);
18+
19+
#ifdef __cplusplus
20+
}
21+
#endif
22+
#endif /* !Py_FREEZE_H */

Include/internal/pycore_object.h

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
7171
static inline void _Py_SetImmortal(PyObject *op)
7272
{
7373
if (op) {
74-
op->ob_refcnt = _Py_IMMORTAL_REFCNT;
74+
op->ob_refcnt = (op->ob_refcnt & _Py_IMMUTABLE_MASK) | _Py_IMMORTAL_REFCNT;
7575
}
7676
}
7777
#define _Py_SetImmortal(op) _Py_SetImmortal(_PyObject_CAST(op))
@@ -80,7 +80,8 @@ static inline void _Py_SetImmortal(PyObject *op)
8080
static inline void _Py_ClearImmortal(PyObject *op)
8181
{
8282
if (op) {
83-
assert(op->ob_refcnt == _Py_IMMORTAL_REFCNT);
83+
assert((op->ob_refcnt & _Py_REFCNT_MASK) == _Py_IMMORTAL_REFCNT);
84+
// note this also clears the _Py_IMMUTABLE_FLAG, if set
8485
op->ob_refcnt = 1;
8586
Py_DECREF(op);
8687
}
@@ -91,6 +92,17 @@ static inline void _Py_ClearImmortal(PyObject *op)
9192
op = NULL; \
9293
} while (0)
9394

95+
static inline void _Py_SetImmutable(PyObject *op)
96+
{
97+
if(op) {
98+
op->ob_refcnt |= _Py_IMMUTABLE_FLAG;
99+
}
100+
}
101+
#define _Py_SetImmutable(op) _Py_SetImmutable(_PyObject_CAST(op))
102+
103+
#define Py_CHECKWRITE(op) ((op) && (!_Py_IsImmutable(op)))
104+
#define Py_REQUIREWRITE(op, msg) {if (Py_CHECKWRITE(op)) { _PyObject_ASSERT_FAILED_MSG(op, msg); }}
105+
94106
static inline void
95107
_Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
96108
{
@@ -101,10 +113,10 @@ _Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
101113
#ifdef Py_REF_DEBUG
102114
_Py_DEC_REFTOTAL(_PyInterpreterState_GET());
103115
#endif
104-
if (--op->ob_refcnt != 0) {
105-
assert(op->ob_refcnt > 0);
106-
}
107-
else {
116+
assert((op->ob_refcnt & _Py_REFCNT_MASK) > 0);
117+
op->ob_refcnt -= 1;
118+
119+
if((op->ob_refcnt & _Py_REFCNT_MASK) == 0) {
108120
#ifdef Py_TRACE_REFS
109121
_Py_ForgetReference(op);
110122
#endif
@@ -122,12 +134,12 @@ _Py_DECREF_NO_DEALLOC(PyObject *op)
122134
#ifdef Py_REF_DEBUG
123135
_Py_DEC_REFTOTAL(_PyInterpreterState_GET());
124136
#endif
125-
op->ob_refcnt--;
126137
#ifdef Py_DEBUG
127-
if (op->ob_refcnt <= 0) {
138+
if ((op->ob_refcnt & _Py_REFCNT_MASK) == 0) {
128139
_Py_FatalRefcountError("Expected a positive remaining refcount");
129140
}
130141
#endif
142+
op->ob_refcnt -= 1;
131143
}
132144

133145
#ifdef Py_REF_DEBUG

Include/object.h

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,25 @@ whose size is determined when the object is allocated.
7878
/* PyObject_HEAD defines the initial segment of every PyObject. */
7979
#define PyObject_HEAD PyObject ob_base;
8080

81+
/*
82+
Immutability:
83+
84+
Immutability is tracked in the top bit of the reference count. The immutability
85+
system also uses the second-to-top bit for managing immutable graphs.
86+
*/
87+
88+
#if SIZEOF_VOID_P > 4
89+
#define _Py_REFCNT_MASK 0xFFFFFFFF
90+
#define _Py_IMMUTABLE_MASK 0xC000000000000000
91+
#define _Py_IMMUTABLE_FLAG 0x8000000000000000
92+
#define _Py_IMMUTABLE_SCC_FLAG 0x4000000000000000
93+
#else
94+
#define _Py_REFCNT_MASK 0x3FFFFFFF
95+
#define _Py_IMMUTABLE_MASK 0xC0000000
96+
#define _Py_IMMUTABLE_FLAG 0x80000000
97+
#define _Py_IMMUTABLE_SCC_FLAG 0x40000000
98+
#endif
99+
81100
/*
82101
Immortalization:
83102
@@ -112,17 +131,17 @@ be done by checking the bit sign flag in the lower 32 bits.
112131
#else
113132
/*
114133
In 32 bit systems, an object will be marked as immortal by setting all of the
115-
lower 30 bits of the reference count field, which is equal to: 0x3FFFFFFF
134+
lower 28 bits of the reference count field, which is equal to: 0x0FFFFFFF.
116135
117-
Using the lower 30 bits makes the value backwards compatible by allowing
136+
Using the lower 28 bits makes the value backwards compatible by allowing
118137
C-Extensions without the updated checks in Py_INCREF and Py_DECREF to safely
119138
increase and decrease the objects reference count. The object would lose its
120139
immortality, but the execution would still be correct.
121140
122141
Reference count increases and decreases will first go through an immortality
123142
check by comparing the reference count field to the immortality reference count.
124143
*/
125-
#define _Py_IMMORTAL_REFCNT (UINT_MAX >> 2)
144+
#define _Py_IMMORTAL_REFCNT (UINT_MAX >> 4)
126145
#endif
127146

128147
// Make all internal uses of PyObject_HEAD_INIT immortal while preserving the
@@ -208,7 +227,7 @@ PyAPI_FUNC(int) Py_Is(PyObject *x, PyObject *y);
208227

209228

210229
static inline Py_ssize_t Py_REFCNT(PyObject *ob) {
211-
return ob->ob_refcnt;
230+
return ob->ob_refcnt & _Py_REFCNT_MASK;
212231
}
213232
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
214233
# define Py_REFCNT(ob) Py_REFCNT(_PyObject_CAST(ob))
@@ -242,7 +261,7 @@ static inline Py_ALWAYS_INLINE int _Py_IsImmortal(PyObject *op)
242261
#if SIZEOF_VOID_P > 4
243262
return _Py_CAST(PY_INT32_T, op->ob_refcnt) < 0;
244263
#else
245-
return op->ob_refcnt == _Py_IMMORTAL_REFCNT;
264+
return (op->ob_refcnt & _Py_REFCNT_MASK) == _Py_IMMORTAL_REFCNT;
246265
#endif
247266
}
248267
#define _Py_IsImmortal(op) _Py_IsImmortal(_PyObject_CAST(op))
@@ -254,6 +273,15 @@ static inline int Py_IS_TYPE(PyObject *ob, PyTypeObject *type) {
254273
# define Py_IS_TYPE(ob, type) Py_IS_TYPE(_PyObject_CAST(ob), (type))
255274
#endif
256275

276+
static inline Py_ALWAYS_INLINE int _Py_IsImmutable(PyObject *op)
277+
{
278+
#if SIZEOF_VOID_P > 4
279+
return _Py_CAST(PY_INT64_T, op->ob_refcnt) < 0;
280+
#else
281+
return _Py_CAST(PY_INT32_T, op->ob_refcnt) < 0;
282+
#endif
283+
}
284+
#define _Py_IsImmutable(op) _Py_IsImmutable(_PyObject_CAST(op))
257285

258286
static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) {
259287
// This immortal check is for code that is unaware of immortal objects.
@@ -263,7 +291,7 @@ static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) {
263291
if (_Py_IsImmortal(ob)) {
264292
return;
265293
}
266-
ob->ob_refcnt = refcnt;
294+
ob->ob_refcnt = (ob->ob_refcnt & _Py_IMMUTABLE_MASK) | (refcnt & _Py_REFCNT_MASK);
267295
}
268296
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
269297
# define Py_SET_REFCNT(ob, refcnt) Py_SET_REFCNT(_PyObject_CAST(ob), (refcnt))
@@ -687,7 +715,8 @@ static inline void Py_DECREF(const char *filename, int lineno, PyObject *op)
687715
}
688716
_Py_DECREF_STAT_INC();
689717
_Py_DECREF_DecRefTotal();
690-
if (--op->ob_refcnt == 0) {
718+
op->ob_refcnt -= 1;
719+
if ((op->ob_refcnt & _Py_REFCNT_MASK) == 0) {
691720
_Py_Dealloc(op);
692721
}
693722
}
@@ -702,7 +731,8 @@ static inline Py_ALWAYS_INLINE void Py_DECREF(PyObject *op)
702731
return;
703732
}
704733
_Py_DECREF_STAT_INC();
705-
if (--op->ob_refcnt == 0) {
734+
op->ob_refcnt -= 1;
735+
if ((op->ob_refcnt & _Py_REFCNT_MASK) == 0) {
706736
_Py_Dealloc(op);
707737
}
708738
}

0 commit comments

Comments
 (0)