Skip to content

Commit dd6a77c

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

Some content is hidden

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

46 files changed

+2143
-419
lines changed

.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
{

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
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: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
6464
#ifdef Py_REF_DEBUG
6565
_Py_AddRefTotal(_PyInterpreterState_GET(), n);
6666
#endif
67-
op->ob_refcnt += n;
67+
op->ob_refcnt += n * 2;
68+
assert(op->ob_refcnt % 2 == 0);
6869
}
6970
#define _Py_RefcntAdd(op, n) _Py_RefcntAdd(_PyObject_CAST(op), n)
7071

@@ -81,7 +82,7 @@ static inline void _Py_ClearImmortal(PyObject *op)
8182
{
8283
if (op) {
8384
assert(op->ob_refcnt == _Py_IMMORTAL_REFCNT);
84-
op->ob_refcnt = 1;
85+
op->ob_refcnt = 2;
8586
Py_DECREF(op);
8687
}
8788
}
@@ -91,6 +92,19 @@ static inline void _Py_ClearImmortal(PyObject *op)
9192
op = NULL; \
9293
} while (0)
9394

95+
#define _Py_IMMUTABLE_FLAG 0x1
96+
97+
static inline void _Py_SetImmutable(PyObject *op)
98+
{
99+
if(op) {
100+
op->ob_refcnt |= _Py_IMMUTABLE_FLAG;
101+
}
102+
}
103+
#define _Py_SetImmutable(op) _Py_SetImmutable(_PyObject_CAST(op))
104+
105+
#define Py_CHECKWRITE(op) ((op) && (!_Py_IsImmutable(op)))
106+
#define Py_REQUIREWRITE(op, msg) {if (Py_CHECKWRITE(op)) { _PyObject_ASSERT_FAILED_MSG(op, msg); }}
107+
94108
static inline void
95109
_Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
96110
{
@@ -101,7 +115,8 @@ _Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
101115
#ifdef Py_REF_DEBUG
102116
_Py_DEC_REFTOTAL(_PyInterpreterState_GET());
103117
#endif
104-
if (--op->ob_refcnt != 0) {
118+
op->ob_refcnt -= 2;
119+
if (op->ob_refcnt != 0) {
105120
assert(op->ob_refcnt > 0);
106121
}
107122
else {
@@ -122,7 +137,7 @@ _Py_DECREF_NO_DEALLOC(PyObject *op)
122137
#ifdef Py_REF_DEBUG
123138
_Py_DEC_REFTOTAL(_PyInterpreterState_GET());
124139
#endif
125-
op->ob_refcnt--;
140+
op->ob_refcnt -= 2;
126141
#ifdef Py_DEBUG
127142
if (op->ob_refcnt <= 0) {
128143
_Py_FatalRefcountError("Expected a positive remaining refcount");

0 commit comments

Comments
 (0)