Skip to content

Commit 89a3946

Browse files
committed
Wrote down the invariants of some common objects whose structure is
exposed in header files. Fixed a few comments in these headers. As we might have expected, writing down invariants systematically exposed a (minor) bug. In this case, function objects have a writeable func_code attribute, which could be set to code objects with the wrong number of free variables. Calling the resulting function segfaulted the interpreter. Added a corresponding test.
1 parent 063e1e8 commit 89a3946

File tree

12 files changed

+98
-25
lines changed

12 files changed

+98
-25
lines changed

Include/cellobject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ extern "C" {
88

99
typedef struct {
1010
PyObject_HEAD
11-
PyObject *ob_ref;
11+
PyObject *ob_ref; /* Content of the cell or NULL when empty */
1212
} PyCellObject;
1313

1414
PyAPI_DATA(PyTypeObject) PyCell_Type;

Include/funcobject.h

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,34 @@
77
extern "C" {
88
#endif
99

10+
/* Function objects and code objects should not be confused with each other:
11+
*
12+
* Function objects are created by the execution of the 'def' statement.
13+
* They reference a code object in their func_code attribute, which is a
14+
* purely syntactic object, i.e. nothing more than a compiled version of some
15+
* source code lines. There is one code object per source code "fragment",
16+
* but each code object can be referenced by zero or many function objects
17+
* depending only on how many times the 'def' statement in the source was
18+
* executed so far.
19+
*/
20+
1021
typedef struct {
1122
PyObject_HEAD
12-
PyObject *func_code;
13-
PyObject *func_globals;
14-
PyObject *func_defaults;
15-
PyObject *func_closure;
16-
PyObject *func_doc;
17-
PyObject *func_name;
18-
PyObject *func_dict;
19-
PyObject *func_weakreflist;
20-
PyObject *func_module;
23+
PyObject *func_code; /* A code object */
24+
PyObject *func_globals; /* A dictionary (other mappings won't do) */
25+
PyObject *func_defaults; /* NULL or a tuple */
26+
PyObject *func_closure; /* NULL or a tuple of cell objects */
27+
PyObject *func_doc; /* The __doc__ attribute, can be anything */
28+
PyObject *func_name; /* The __name__ attribute, a string object */
29+
PyObject *func_dict; /* The __dict__ attribute, a dict or NULL */
30+
PyObject *func_weakreflist; /* List of weak references */
31+
PyObject *func_module; /* The __module__ attribute, can be anything */
32+
33+
/* Invariant:
34+
* func_closure contains the bindings for func_code->co_freevars, so
35+
* PyTuple_Size(func_closure) == PyCode_GetNumFree(func_code)
36+
* (func_closure may be NULL if PyCode_GetNumFree(func_code) == 0).
37+
*/
2138
} PyFunctionObject;
2239

2340
PyAPI_DATA(PyTypeObject) PyFunction_Type;

Include/intobject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ returns -1 and sets errno to EBADF if the object is not an PyIntObject.
1111
None of the functions should be applied to nil objects.
1212
1313
The type PyIntObject is (unfortunately) exposed here so we can declare
14-
_Py_TrueStruct and _Py_ZeroStruct below; don't use this.
14+
_Py_TrueStruct and _Py_ZeroStruct in boolobject.h; don't use this.
1515
*/
1616

1717
#ifndef Py_INTOBJECT_H

Include/listobject.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ typedef struct {
3131
* len(list) == ob_size
3232
* ob_item == NULL implies ob_size == allocated == 0
3333
* list.sort() temporarily sets allocated to -1 to detect mutations.
34+
*
35+
* Items must normally not be NULL, except during construction when
36+
* the list is not yet visible outside the function that builds it.
3437
*/
3538
int allocated;
3639
} PyListObject;

Include/methodobject.h

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
extern "C" {
88
#endif
99

10+
/* This is about the type 'builtin_function_or_method',
11+
not Python methods in user-defined classes. See classobject.h
12+
for the latter. */
13+
1014
PyAPI_DATA(PyTypeObject) PyCFunction_Type;
1115

1216
#define PyCFunction_Check(op) ((op)->ob_type == &PyCFunction_Type)
@@ -31,10 +35,11 @@ PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *);
3135
PyAPI_FUNC(PyObject *) PyCFunction_Call(PyObject *, PyObject *, PyObject *);
3236

3337
struct PyMethodDef {
34-
char *ml_name;
35-
PyCFunction ml_meth;
36-
int ml_flags;
37-
char *ml_doc;
38+
char *ml_name; /* The name of the built-in function/method */
39+
PyCFunction ml_meth; /* The C function that implements it */
40+
int ml_flags; /* Combination of METH_xxx flags, which mostly
41+
describe the args expected by the C func */
42+
char *ml_doc; /* The __doc__ attribute, or NULL */
3843
};
3944
typedef struct PyMethodDef PyMethodDef;
4045

@@ -75,9 +80,9 @@ PyAPI_FUNC(PyObject *) Py_FindMethodInChain(PyMethodChain *, PyObject *,
7580

7681
typedef struct {
7782
PyObject_HEAD
78-
PyMethodDef *m_ml;
79-
PyObject *m_self;
80-
PyObject *m_module;
83+
PyMethodDef *m_ml; /* Description of the C function to call */
84+
PyObject *m_self; /* Passed as 'self' arg to the C func, can be NULL */
85+
PyObject *m_module; /* The __module__ attribute, can be anything */
8186
} PyCFunctionObject;
8287

8388
#ifdef __cplusplus

Include/rangeobject.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
extern "C" {
88
#endif
99

10+
/* This is about the type 'xrange', not the built-in function range(), which
11+
returns regular lists. */
12+
1013
/*
1114
A range object represents an integer range. This is an immutable object;
1215
a range cannot change its value after creation.

Include/setobject.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ typedef struct {
1616
PyObject *data;
1717
long hash; /* only used by frozenset objects */
1818
PyObject *weakreflist; /* List of weak references */
19+
20+
/* Invariants:
21+
* data is a dictionary whose values are all True.
22+
* data points to the same dict for the whole life of the set.
23+
* For frozensets only:
24+
* data is immutable.
25+
* hash is the hash of the frozenset or -1 if not computed yet.
26+
*/
1927
} PySetObject;
2028

2129
PyAPI_DATA(PyTypeObject) PySet_Type;

Include/sliceobject.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ PyAPI_DATA(PyObject) _Py_EllipsisObject; /* Don't use this directly */
1616
1717
A slice object containing start, stop, and step data members (the
1818
names are from range). After much talk with Guido, it was decided to
19-
let these be any arbitrary python type.
19+
let these be any arbitrary python type. Py_None stands for omitted values.
2020
*/
2121

2222
typedef struct {
2323
PyObject_HEAD
24-
PyObject *start, *stop, *step;
24+
PyObject *start, *stop, *step; /* not NULL */
2525
} PySliceObject;
2626

2727
PyAPI_DATA(PyTypeObject) PySlice_Type;

Include/stringobject.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ typedef struct {
3737
long ob_shash;
3838
int ob_sstate;
3939
char ob_sval[1];
40+
41+
/* Invariants:
42+
* ob_sval contains space for 'ob_size+1' elements.
43+
* ob_sval[ob_size] == 0.
44+
* ob_shash is the hash of the string or -1 if not computed yet.
45+
* ob_sstate != 0 iff the string object is in stringobject.c's
46+
* 'interned' dictionary; in this case the two references
47+
* from 'interned' to this object are *not counted* in ob_refcnt.
48+
*/
4049
} PyStringObject;
4150

4251
#define SSTATE_NOT_INTERNED 0

Include/tupleobject.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ extern "C" {
88
#endif
99

1010
/*
11-
Another generally useful object type is an tuple of object pointers.
12-
This is a mutable type: the tuple items can be changed (but not their
13-
number). Out-of-range indices or non-tuple objects are ignored.
11+
Another generally useful object type is a tuple of object pointers.
12+
For Python, this is an immutable type. C code can change the tuple items
13+
(but not their number), and even use tuples are general-purpose arrays of
14+
object references, but in general only brand new tuples should be mutated,
15+
not ones that might already have been exposed to Python code.
1416
1517
*** WARNING *** PyTuple_SetItem does not increment the new item's reference
1618
count, but does decrement the reference count of the item it replaces,
@@ -22,6 +24,11 @@ returned item's reference count.
2224
typedef struct {
2325
PyObject_VAR_HEAD
2426
PyObject *ob_item[1];
27+
28+
/* ob_item contains space for 'ob_size' elements.
29+
* Items must normally not be NULL, except during construction when
30+
* the tuple is not yet visible outside the function that builds it.
31+
*/
2532
} PyTupleObject;
2633

2734
PyAPI_DATA(PyTypeObject) PyTuple_Type;

0 commit comments

Comments
 (0)