mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-21 04:42:53 +08:00
0b233e34c8
Python 2 has a bit flag Py_TPFLAGS_HAVE_ITER which can be passed as part of the tp_flags field when defining a new object type. This flag is not defined in Python 3 and so we define it to 0 in python-internal.h (when IS_PY3K is defined). The meaning of this flag is that the object has the fields tp_iter and tp_iternext. Note the use of "has" here, the flag says nothing about the values in those fields, just that the type object has the fields. In early versions of Python 2 these fields were no part of the PyTypeObject struct, they were added in version 2.2 (see https://docs.python.org/release/2.3/api/type-structs.html). And so, there could be a some code compiled out there which has a PyTypeObject structure within it that doesn't even have the tp_iter and tp_iternext fields, attempting to access these fields would be undefined behaviour. And so Python added the Py_TPFLAGS_HAVE_ITER flag. If the flag is present then Python is free to access the tp_iter and tp_iternext fields. If we consider GDB then we always assume that the tp_iter and tp_iternext fields are part of PyTypeObject. If someone was crazy enough to try and compile GDB against Python 2.1 then we'd get lots of build errors saying that we were passing too many fields when initializing PyTypeObject structures. And so, I claim, we can be sure that GDB will always be compiled with a version of Python that has the tp_iter and tp_iternext fields in PyTypeObject. Next we can look at the Py_TPFLAGS_DEFAULT flag. In Python 2, each time additional fields are added to PyTypeObject a new Py_TPFLAGS_* flag would be defined to indicate whether those flags are present or not. And, those new flags would be added to Py_TPFLAGS_DEFAULT. And so, in the latest version of Python 2 the Py_TPFLAGS_DEFAULT flag includes Py_TPFLAGS_HAVE_ITER (see https://docs.python.org/2.7/c-api/typeobj.html). In GDB we pass Py_TPFLAGS_DEFAULT as part of the tp_flags for all objects we define. And so, in this commit, I propose to remove all uses of Py_TPFLAGS_HAVE_ITER from GDB, it's simply not needed. There should be no user visible changes after this commit.
593 lines
16 KiB
C
593 lines
16 KiB
C
/* Python interface to line tables.
|
||
|
||
Copyright (C) 2013-2021 Free Software Foundation, Inc.
|
||
|
||
This file is part of GDB.
|
||
|
||
This program is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||
|
||
#include "defs.h"
|
||
#include "python-internal.h"
|
||
|
||
struct linetable_entry_object {
|
||
PyObject_HEAD
|
||
/* The line table source line. */
|
||
int line;
|
||
/* The pc associated with the source line. */
|
||
CORE_ADDR pc;
|
||
};
|
||
|
||
extern PyTypeObject linetable_entry_object_type
|
||
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("linetable_entry_object");
|
||
|
||
struct linetable_object {
|
||
PyObject_HEAD
|
||
/* The symtab python object. We store the Python object here as the
|
||
underlying symtab can become invalid, and we have to run validity
|
||
checks on it. */
|
||
PyObject *symtab;
|
||
};
|
||
|
||
extern PyTypeObject linetable_object_type
|
||
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("linetable_object");
|
||
|
||
struct ltpy_iterator_object {
|
||
PyObject_HEAD
|
||
/* The current entry in the line table for the iterator */
|
||
int current_index;
|
||
/* Pointer back to the original source line table object. Needed to
|
||
check if the line table is still valid, and has not been invalidated
|
||
when an object file has been freed. */
|
||
PyObject *source;
|
||
};
|
||
|
||
extern PyTypeObject ltpy_iterator_object_type
|
||
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("ltpy_iterator_object");
|
||
|
||
/* Internal helper function to extract gdb.Symtab from a gdb.LineTable
|
||
object. */
|
||
|
||
static PyObject *
|
||
get_symtab (PyObject *linetable)
|
||
{
|
||
linetable_object *lt = (linetable_object *) linetable;
|
||
|
||
return lt->symtab;
|
||
}
|
||
|
||
#define LTPY_REQUIRE_VALID(lt_obj, symtab) \
|
||
do { \
|
||
symtab = symtab_object_to_symtab (get_symtab (lt_obj)); \
|
||
if (symtab == NULL) \
|
||
{ \
|
||
PyErr_SetString (PyExc_RuntimeError, \
|
||
_("Symbol Table in line table is invalid."));\
|
||
return NULL; \
|
||
} \
|
||
} while (0)
|
||
|
||
|
||
/* Helper function to create a line table object that wraps a
|
||
gdb.Symtab object. */
|
||
|
||
PyObject *
|
||
symtab_to_linetable_object (PyObject *symtab)
|
||
{
|
||
linetable_object *ltable;
|
||
|
||
ltable = PyObject_New (linetable_object, &linetable_object_type);
|
||
if (ltable != NULL)
|
||
{
|
||
ltable->symtab = symtab;
|
||
Py_INCREF (symtab);
|
||
}
|
||
return (PyObject *) ltable;
|
||
}
|
||
|
||
/* Internal helper function to build a line table object from a line
|
||
and an address. */
|
||
|
||
static PyObject *
|
||
build_linetable_entry (int line, CORE_ADDR address)
|
||
{
|
||
linetable_entry_object *obj;
|
||
|
||
obj = PyObject_New (linetable_entry_object,
|
||
&linetable_entry_object_type);
|
||
if (obj != NULL)
|
||
{
|
||
obj->line = line;
|
||
obj->pc = address;
|
||
}
|
||
|
||
return (PyObject *) obj;
|
||
}
|
||
|
||
/* Internal helper function to build a Python Tuple from a vector.
|
||
A line table entry can have multiple PCs for a given source line.
|
||
Construct a Tuple of all entries for the given source line, LINE
|
||
from the line table PCS. Construct one line table entry object per
|
||
address. */
|
||
|
||
static PyObject *
|
||
build_line_table_tuple_from_pcs (int line, const std::vector<CORE_ADDR> &pcs)
|
||
{
|
||
int i;
|
||
|
||
if (pcs.size () < 1)
|
||
Py_RETURN_NONE;
|
||
|
||
gdbpy_ref<> tuple (PyTuple_New (pcs.size ()));
|
||
|
||
if (tuple == NULL)
|
||
return NULL;
|
||
|
||
for (i = 0; i < pcs.size (); ++i)
|
||
{
|
||
CORE_ADDR pc = pcs[i];
|
||
gdbpy_ref<> obj (build_linetable_entry (line, pc));
|
||
|
||
if (obj == NULL)
|
||
return NULL;
|
||
else if (PyTuple_SetItem (tuple.get (), i, obj.release ()) != 0)
|
||
return NULL;
|
||
}
|
||
|
||
return tuple.release ();
|
||
}
|
||
|
||
/* Implementation of gdb.LineTable.line (self) -> Tuple. Returns a
|
||
tuple of LineTableEntry objects associated with this line from the
|
||
in the line table. */
|
||
|
||
static PyObject *
|
||
ltpy_get_pcs_for_line (PyObject *self, PyObject *args)
|
||
{
|
||
struct symtab *symtab;
|
||
gdb_py_longest py_line;
|
||
struct linetable_entry *best_entry = NULL;
|
||
std::vector<CORE_ADDR> pcs;
|
||
|
||
LTPY_REQUIRE_VALID (self, symtab);
|
||
|
||
if (! PyArg_ParseTuple (args, GDB_PY_LL_ARG, &py_line))
|
||
return NULL;
|
||
|
||
try
|
||
{
|
||
pcs = find_pcs_for_symtab_line (symtab, py_line, &best_entry);
|
||
}
|
||
catch (const gdb_exception &except)
|
||
{
|
||
GDB_PY_HANDLE_EXCEPTION (except);
|
||
}
|
||
|
||
return build_line_table_tuple_from_pcs (py_line, pcs);
|
||
}
|
||
|
||
/* Implementation of gdb.LineTable.has_line (self, line) -> Boolean.
|
||
Returns a Python Boolean indicating whether a source line has any
|
||
line table entries corresponding to it. */
|
||
|
||
static PyObject *
|
||
ltpy_has_line (PyObject *self, PyObject *args)
|
||
{
|
||
struct symtab *symtab;
|
||
gdb_py_longest py_line;
|
||
int index;
|
||
|
||
LTPY_REQUIRE_VALID (self, symtab);
|
||
|
||
if (! PyArg_ParseTuple (args, GDB_PY_LL_ARG, &py_line))
|
||
return NULL;
|
||
|
||
if (SYMTAB_LINETABLE (symtab) == NULL)
|
||
{
|
||
PyErr_SetString (PyExc_RuntimeError,
|
||
_("Linetable information not found in symbol table"));
|
||
return NULL;
|
||
}
|
||
|
||
for (index = 0; index < SYMTAB_LINETABLE (symtab)->nitems; index++)
|
||
{
|
||
struct linetable_entry *item = &(SYMTAB_LINETABLE (symtab)->item[index]);
|
||
if (item->line == py_line)
|
||
Py_RETURN_TRUE;
|
||
}
|
||
|
||
Py_RETURN_FALSE;
|
||
}
|
||
|
||
/* Implementation of gdb.LineTable.source_lines (self) -> List.
|
||
Returns a Python List that contains source line entries in the
|
||
line table. This function will just return the source lines
|
||
without corresponding addresses. */
|
||
|
||
static PyObject *
|
||
ltpy_get_all_source_lines (PyObject *self, PyObject *args)
|
||
{
|
||
struct symtab *symtab;
|
||
Py_ssize_t index;
|
||
struct linetable_entry *item;
|
||
|
||
LTPY_REQUIRE_VALID (self, symtab);
|
||
|
||
if (SYMTAB_LINETABLE (symtab) == NULL)
|
||
{
|
||
PyErr_SetString (PyExc_RuntimeError,
|
||
_("Linetable information not found in symbol table"));
|
||
return NULL;
|
||
}
|
||
|
||
gdbpy_ref<> source_dict (PyDict_New ());
|
||
if (source_dict == NULL)
|
||
return NULL;
|
||
|
||
for (index = 0; index < SYMTAB_LINETABLE (symtab)->nitems; index++)
|
||
{
|
||
item = &(SYMTAB_LINETABLE (symtab)->item[index]);
|
||
|
||
/* 0 is used to signify end of line table information. Do not
|
||
include in the source set. */
|
||
if (item->line > 0)
|
||
{
|
||
gdbpy_ref<> line = gdb_py_object_from_longest (item->line);
|
||
|
||
if (line == NULL)
|
||
return NULL;
|
||
|
||
if (PyDict_SetItem (source_dict.get (), line.get (), Py_None) == -1)
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
return PyDict_Keys (source_dict.get ());
|
||
}
|
||
|
||
/* Implementation of gdb.LineTable.is_valid (self) -> Boolean.
|
||
Returns True if this line table object still exists in GDB. */
|
||
|
||
static PyObject *
|
||
ltpy_is_valid (PyObject *self, PyObject *args)
|
||
{
|
||
struct symtab *symtab = NULL;
|
||
|
||
symtab = symtab_object_to_symtab (get_symtab (self));
|
||
|
||
if (symtab == NULL)
|
||
Py_RETURN_FALSE;
|
||
|
||
Py_RETURN_TRUE;
|
||
}
|
||
|
||
/* Deconstructor for the line table object. Decrement the reference
|
||
to the symbol table object before calling the default free. */
|
||
|
||
static void
|
||
ltpy_dealloc (PyObject *self)
|
||
{
|
||
linetable_object *obj = (linetable_object *) self;
|
||
|
||
Py_DECREF (obj->symtab);
|
||
Py_TYPE (self)->tp_free (self);
|
||
}
|
||
|
||
/* Initialize LineTable, LineTableEntry and LineTableIterator
|
||
objects. */
|
||
|
||
int
|
||
gdbpy_initialize_linetable (void)
|
||
{
|
||
if (PyType_Ready (&linetable_object_type) < 0)
|
||
return -1;
|
||
if (PyType_Ready (&linetable_entry_object_type) < 0)
|
||
return -1;
|
||
if (PyType_Ready (<py_iterator_object_type) < 0)
|
||
return -1;
|
||
|
||
Py_INCREF (&linetable_object_type);
|
||
Py_INCREF (&linetable_entry_object_type);
|
||
Py_INCREF (<py_iterator_object_type);
|
||
|
||
if (gdb_pymodule_addobject (gdb_module, "LineTable",
|
||
(PyObject *) &linetable_object_type) < 0)
|
||
return -1;
|
||
|
||
if (gdb_pymodule_addobject (gdb_module, "LineTableEntry",
|
||
(PyObject *) &linetable_entry_object_type) < 0)
|
||
return -1;
|
||
|
||
if (gdb_pymodule_addobject (gdb_module, "LineTableIterator",
|
||
(PyObject *) <py_iterator_object_type) < 0)
|
||
return -1;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* LineTable entry object get functions. */
|
||
|
||
/* Implementation of gdb.LineTableEntry.line (self) -> Long. Returns
|
||
a long integer associated with the line table entry. */
|
||
|
||
static PyObject *
|
||
ltpy_entry_get_line (PyObject *self, void *closure)
|
||
{
|
||
linetable_entry_object *obj = (linetable_entry_object *) self;
|
||
|
||
return gdb_py_object_from_longest (obj->line).release ();
|
||
}
|
||
|
||
/* Implementation of gdb.LineTableEntry.pc (self) -> Long. Returns a
|
||
a long integer associated with the PC of the line table entry. */
|
||
|
||
static PyObject *
|
||
ltpy_entry_get_pc (PyObject *self, void *closure)
|
||
{
|
||
linetable_entry_object *obj = (linetable_entry_object *) self;
|
||
|
||
return gdb_py_object_from_ulongest (obj->pc).release ();
|
||
}
|
||
|
||
/* LineTable iterator functions. */
|
||
|
||
/* Return a new line table iterator. */
|
||
|
||
static PyObject *
|
||
ltpy_iter (PyObject *self)
|
||
{
|
||
ltpy_iterator_object *ltpy_iter_obj;
|
||
struct symtab *symtab = NULL;
|
||
|
||
LTPY_REQUIRE_VALID (self, symtab);
|
||
|
||
ltpy_iter_obj = PyObject_New (ltpy_iterator_object,
|
||
<py_iterator_object_type);
|
||
if (ltpy_iter_obj == NULL)
|
||
return NULL;
|
||
|
||
ltpy_iter_obj->current_index = 0;
|
||
ltpy_iter_obj->source = self;
|
||
|
||
Py_INCREF (self);
|
||
return (PyObject *) ltpy_iter_obj;
|
||
}
|
||
|
||
static void
|
||
ltpy_iterator_dealloc (PyObject *obj)
|
||
{
|
||
ltpy_iterator_object *iter_obj = (ltpy_iterator_object *) obj;
|
||
|
||
Py_DECREF (iter_obj->source);
|
||
Py_TYPE (obj)->tp_free (obj);
|
||
}
|
||
|
||
/* Return a reference to the line table iterator. */
|
||
|
||
static PyObject *
|
||
ltpy_iterator (PyObject *self)
|
||
{
|
||
ltpy_iterator_object *iter_obj = (ltpy_iterator_object *) self;
|
||
struct symtab *symtab;
|
||
|
||
LTPY_REQUIRE_VALID (iter_obj->source, symtab);
|
||
|
||
Py_INCREF (self);
|
||
return self;
|
||
}
|
||
|
||
/* Return the next line table entry in the iteration through the line
|
||
table data structure. */
|
||
|
||
static PyObject *
|
||
ltpy_iternext (PyObject *self)
|
||
{
|
||
ltpy_iterator_object *iter_obj = (ltpy_iterator_object *) self;
|
||
struct symtab *symtab;
|
||
PyObject *obj;
|
||
struct linetable_entry *item;
|
||
|
||
LTPY_REQUIRE_VALID (iter_obj->source, symtab);
|
||
|
||
if (iter_obj->current_index >= SYMTAB_LINETABLE (symtab)->nitems)
|
||
{
|
||
PyErr_SetNone (PyExc_StopIteration);
|
||
return NULL;
|
||
}
|
||
|
||
item = &(SYMTAB_LINETABLE (symtab)->item[iter_obj->current_index]);
|
||
|
||
/* Skip over internal entries such as 0. 0 signifies the end of
|
||
line table data and is not useful to the API user. */
|
||
while (item->line < 1)
|
||
{
|
||
iter_obj->current_index++;
|
||
|
||
/* Exit if the internal value is the last item in the line table. */
|
||
if (iter_obj->current_index >= SYMTAB_LINETABLE (symtab)->nitems)
|
||
{
|
||
PyErr_SetNone (PyExc_StopIteration);
|
||
return NULL;
|
||
}
|
||
item = &(SYMTAB_LINETABLE (symtab)->item[iter_obj->current_index]);
|
||
}
|
||
|
||
obj = build_linetable_entry (item->line, item->pc);
|
||
iter_obj->current_index++;
|
||
|
||
return obj;
|
||
}
|
||
|
||
/* Implementation of gdb.LineTableIterator.is_valid (self) -> Boolean.
|
||
Returns True if this line table iterator object still exists in
|
||
GDB. */
|
||
|
||
static PyObject *
|
||
ltpy_iter_is_valid (PyObject *self, PyObject *args)
|
||
{
|
||
struct symtab *symtab = NULL;
|
||
ltpy_iterator_object *iter_obj = (ltpy_iterator_object *) self;
|
||
|
||
symtab = symtab_object_to_symtab (get_symtab (iter_obj->source));
|
||
|
||
if (symtab == NULL)
|
||
Py_RETURN_FALSE;
|
||
|
||
Py_RETURN_TRUE;
|
||
}
|
||
|
||
|
||
|
||
static PyMethodDef linetable_object_methods[] = {
|
||
{ "line", ltpy_get_pcs_for_line, METH_VARARGS,
|
||
"line (lineno) -> Tuple\n\
|
||
Return executable locations for a given source line." },
|
||
{ "has_line", ltpy_has_line, METH_VARARGS,
|
||
"has_line (lineno) -> Boolean\n\
|
||
Return TRUE if this line has executable information, FALSE if not." },
|
||
{ "source_lines", ltpy_get_all_source_lines, METH_NOARGS,
|
||
"source_lines () -> List\n\
|
||
Return a list of all executable source lines." },
|
||
{ "is_valid", ltpy_is_valid, METH_NOARGS,
|
||
"is_valid () -> Boolean.\n\
|
||
Return True if this LineTable is valid, False if not." },
|
||
{NULL} /* Sentinel */
|
||
};
|
||
|
||
PyTypeObject linetable_object_type = {
|
||
PyVarObject_HEAD_INIT (NULL, 0)
|
||
"gdb.LineTable", /*tp_name*/
|
||
sizeof (linetable_object), /*tp_basicsize*/
|
||
0, /*tp_itemsize*/
|
||
ltpy_dealloc, /*tp_dealloc*/
|
||
0, /*tp_print*/
|
||
0, /*tp_getattr*/
|
||
0, /*tp_setattr*/
|
||
0, /*tp_compare*/
|
||
0, /*tp_repr*/
|
||
0, /*tp_as_number*/
|
||
0, /*tp_as_sequence*/
|
||
0, /*tp_as_mapping*/
|
||
0, /*tp_hash */
|
||
0, /*tp_call*/
|
||
0, /*tp_str*/
|
||
0, /*tp_getattro*/
|
||
0, /*tp_setattro*/
|
||
0, /*tp_as_buffer*/
|
||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
||
"GDB line table object", /* tp_doc */
|
||
0, /* tp_traverse */
|
||
0, /* tp_clear */
|
||
0, /* tp_richcompare */
|
||
0, /* tp_weaklistoffset */
|
||
ltpy_iter, /* tp_iter */
|
||
0, /* tp_iternext */
|
||
linetable_object_methods, /* tp_methods */
|
||
0, /* tp_members */
|
||
0, /* tp_getset */
|
||
0, /* tp_base */
|
||
0, /* tp_dict */
|
||
0, /* tp_descr_get */
|
||
0, /* tp_descr_set */
|
||
0, /* tp_dictoffset */
|
||
0, /* tp_init */
|
||
0, /* tp_alloc */
|
||
};
|
||
|
||
static PyMethodDef ltpy_iterator_methods[] = {
|
||
{ "is_valid", ltpy_iter_is_valid, METH_NOARGS,
|
||
"is_valid () -> Boolean.\n\
|
||
Return True if this LineTable iterator is valid, False if not." },
|
||
{NULL} /* Sentinel */
|
||
};
|
||
|
||
PyTypeObject ltpy_iterator_object_type = {
|
||
PyVarObject_HEAD_INIT (NULL, 0)
|
||
"gdb.LineTableIterator", /*tp_name*/
|
||
sizeof (ltpy_iterator_object), /*tp_basicsize*/
|
||
0, /*tp_itemsize*/
|
||
ltpy_iterator_dealloc, /*tp_dealloc*/
|
||
0, /*tp_print*/
|
||
0, /*tp_getattr*/
|
||
0, /*tp_setattr*/
|
||
0, /*tp_compare*/
|
||
0, /*tp_repr*/
|
||
0, /*tp_as_number*/
|
||
0, /*tp_as_sequence*/
|
||
0, /*tp_as_mapping*/
|
||
0, /*tp_hash */
|
||
0, /*tp_call*/
|
||
0, /*tp_str*/
|
||
0, /*tp_getattro*/
|
||
0, /*tp_setattro*/
|
||
0, /*tp_as_buffer*/
|
||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
||
"GDB line table iterator object", /*tp_doc */
|
||
0, /*tp_traverse */
|
||
0, /*tp_clear */
|
||
0, /*tp_richcompare */
|
||
0, /*tp_weaklistoffset */
|
||
ltpy_iterator, /*tp_iter */
|
||
ltpy_iternext, /*tp_iternext */
|
||
ltpy_iterator_methods /*tp_methods */
|
||
};
|
||
|
||
|
||
static gdb_PyGetSetDef linetable_entry_object_getset[] = {
|
||
{ "line", ltpy_entry_get_line, NULL,
|
||
"The line number in the source file.", NULL },
|
||
{ "pc", ltpy_entry_get_pc, NULL,
|
||
"The memory address for this line number.", NULL },
|
||
{ NULL } /* Sentinel */
|
||
};
|
||
|
||
PyTypeObject linetable_entry_object_type = {
|
||
PyVarObject_HEAD_INIT (NULL, 0)
|
||
"gdb.LineTableEntry", /*tp_name*/
|
||
sizeof (linetable_entry_object), /*tp_basicsize*/
|
||
0, /*tp_itemsize*/
|
||
0, /*tp_dealloc*/
|
||
0, /*tp_print*/
|
||
0, /*tp_getattr*/
|
||
0, /*tp_setattr*/
|
||
0, /*tp_compare*/
|
||
0, /*tp_repr*/
|
||
0, /*tp_as_number*/
|
||
0, /*tp_as_sequence*/
|
||
0, /*tp_as_mapping*/
|
||
0, /*tp_hash */
|
||
0, /*tp_call*/
|
||
0, /*tp_str*/
|
||
0, /*tp_getattro*/
|
||
0, /*tp_setattro*/
|
||
0, /*tp_as_buffer*/
|
||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
||
"GDB line table entry object", /* tp_doc */
|
||
0, /* tp_traverse */
|
||
0, /* tp_clear */
|
||
0, /* tp_richcompare */
|
||
0, /* tp_weaklistoffset */
|
||
0, /* tp_iter */
|
||
0, /* tp_iternext */
|
||
0, /* tp_methods */
|
||
0, /* tp_members */
|
||
linetable_entry_object_getset, /* tp_getset */
|
||
0, /* tp_base */
|
||
0, /* tp_dict */
|
||
0, /* tp_descr_get */
|
||
0, /* tp_descr_set */
|
||
0, /* tp_dictoffset */
|
||
0, /* tp_init */
|
||
0, /* tp_alloc */
|
||
};
|