Allow naming and configuring order of type finders

Like for symbol finders, we want extra flexibility around configuring
type finders. The type finder callback signature also has a couple of
warts: it doesn't take the program since it was added before types
needed to be constructed from a program, and it is called separately for
each type kind since originally type lookups were for only one kind.
While we're adding a new interface, let's fix these warts: pass the
program and a set of type kinds. However, we need to keep the old
add_type_finder() interface for backwards compatibility.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
Omar Sandoval 2024-05-31 11:44:32 -07:00
parent 9b73b44908
commit 5c9797a633
11 changed files with 366 additions and 157 deletions

View File

@ -437,6 +437,49 @@ class Program:
another :ref:`buffer <python:binaryseq>` type.
"""
...
def register_type_finder(
self,
name: str,
fn: Callable[[Program, TypeKindSet, str, Optional[str]], Optional[Type]],
*,
enable_index: Optional[int] = None,
) -> None:
"""
Register a callback for finding types in the program.
This does not enable the finder unless *enable_index* is given.
:param name: Finder name.
:param fn: Callable taking the program, a :class:`TypeKindSet`, name,
and filename: ``(prog, kinds, name, filename)``. The filename
should be matched with :func:`filename_matches()`. This should
return a :class:`Type` or ``None`` if not found.
:param enable_index: Insert the finder into the list of enabled type
finders at the given index. If -1 or greater than the number of
enabled finders, insert it at the end. If ``None`` or not given,
don't enable the finder.
:raises ValueError: if there is already a finder with the given name
"""
...
def registered_type_finders(self) -> Set[str]:
"""Return the names of all registered type finders."""
...
def set_enabled_type_finders(self, names: Sequence[str]) -> None:
"""
Set the list of enabled type finders.
Finders are called in the same order as the list until a type is found.
Finders that are not in the list are not called.
:param names: Names of finders to enable, in order.
:raises ValueError: if no finder has a given name or the same name is
given more than once
"""
...
def enabled_type_finders(self) -> List[str]:
"""Return the names of enabled type finders, in order."""
...
def register_symbol_finder(
self,
name: str,
@ -499,16 +542,20 @@ class Program:
self, fn: Callable[[TypeKind, str, Optional[str]], Optional[Type]]
) -> None:
"""
Register a callback for finding types in the program.
Deprecated method to register and enable a callback for finding types
in the program.
Callbacks are called in reverse order of the order they were added
until the type is found. So, more recently added callbacks take
precedence.
.. deprecated:: 0.0.27
Use :meth:`register_type_finder()` instead.
:param fn: Callable taking a :class:`TypeKind`, name, and filename:
``(kind, name, filename)``. The filename should be matched with
:func:`filename_matches()`. This should return a :class:`Type`
or ``None`` if not found.
The differences from :meth:`register_type_finder()` are:
1. *fn* is not passed *prog*.
2. *fn* is passed a :class:`TypeKind` instead of a
:class:`TypeKindSet`. If multiple kinds are being searched for, *fn*
will be called multiple times.
3. A name for the finder is generated from *fn*.
4. The finder is always enabled before any existing finders.
"""
...
def add_object_finder(

View File

@ -82,7 +82,7 @@ program "memory":
print(drgn.Object(prog, 'struct btrfs_super_block', address=65536))
run_interactive(prog, banner_func=lambda _: "BTRFS debugger")
:meth:`drgn.Program.add_type_finder()` and
:meth:`drgn.Program.register_type_finder()` and
:meth:`drgn.Program.add_object_finder()` are the equivalent methods for
plugging in types and objects.

View File

@ -2181,8 +2181,12 @@ void drgn_debug_info_init(struct drgn_debug_info *dbinfo,
// unlikely to fail anwyays, so don't bother propagating an error up.
if (!dbinfo->dwfl)
abort();
drgn_program_add_type_finder_impl(prog, &dbinfo->type_finder,
drgn_debug_info_find_type, dbinfo);
const struct drgn_type_finder_ops type_finder_ops = {
.find = drgn_debug_info_find_type,
};
drgn_program_register_type_finder_impl(prog, &dbinfo->type_finder,
"dwarf", &type_finder_ops,
dbinfo, 0);
drgn_program_add_object_finder_impl(prog, &dbinfo->object_finder,
drgn_debug_info_find_object,
dbinfo);

View File

@ -587,41 +587,91 @@ enum {
DRGN_HANDLER_REGISTER_DONT_ENABLE = SIZE_MAX - 1,
};
/**
* Callback for finding a type.
*
* @param[in] kinds Kinds of types to find, as a bitmask of bits shifted by @ref
* drgn_type_kind. E.g., `(1 << DRGN_TYPE_STRUCT) | (1 << DRGN_TYPE_CLASS)`
* means to find a structure or class type.
* @param[in] name Name of type (or tag, for structs, unions, and enums). This
* is @em not null-terminated.
* @param[in] name_len Length of @p name.
* @param[in] filename Filename containing the type definition or @c NULL. This
* should be matched with @ref drgn_filename_matches().
* @param[in] arg Argument passed to @ref drgn_program_add_type_finder().
* @param[out] ret Returned type.
* @return @c NULL on success, non-@c NULL on error. In particular, if the type
* is not found, this should return &@ref drgn_not_found; any other errors are
* considered fatal.
*/
typedef struct drgn_error *
(*drgn_type_find_fn)(uint64_t kinds, const char *name, size_t name_len,
const char *filename, void *arg,
struct drgn_qualified_type *ret);
/** Type finder callback table. */
struct drgn_type_finder_ops {
/**
* Callback to destroy the type finder.
*
* This may be @c NULL.
*
* @param[in] arg Argument passed to @ref
* drgn_program_register_type_finder().
*/
void (*destroy)(void *arg);
/**
* Callback for finding a type.
*
* @param[in] kinds Kinds of types to find, as a bitmask of bits shifted
* by @ref drgn_type_kind. E.g., `(1 << DRGN_TYPE_STRUCT) | (1 <<
* DRGN_TYPE_CLASS)` means to find a structure or class type.
* @param[in] name Name of type (or tag, for structs, unions, and
* enums). This is @em not null-terminated.
* @param[in] name_len Length of @p name.
* @param[in] filename Filename containing the type definition or @c
* NULL. This should be matched with @ref drgn_filename_matches().
* @param[in] arg Argument passed to @ref
* drgn_program_register_type_finder().
* @param[out] ret Returned type.
* @return @c NULL on success, non-@c NULL on error. In particular, if
* the type is not found, this should return &@ref drgn_not_found; any
* other errors are considered fatal.
*/
struct drgn_error *(*find)(uint64_t kinds, const char *name,
size_t name_len, const char *filename,
void *arg, struct drgn_qualified_type *ret);
};
/**
* Register a type finding callback.
*
* Callbacks are called in reverse order of the order they were added until the
* type is found. So, more recently added callbacks take precedence.
*
* @param[in] fn The callback.
* @param[in] arg Argument to pass to @p fn.
* @return @c NULL on success, non-@c NULL on error.
* @param[in] name Finder name. This is copied.
* @param[in] ops Callback table. This is copied.
* @param[in] arg Argument to pass to callbacks.
* @param[in] enable_index Insert the finder into the list of enabled finders at
* the given index. If @ref DRGN_HANDLER_REGISTER_ENABLE_LAST or greater than
* the number of enabled finders, insert it at the end. If @ref
* DRGN_HANDLER_REGISTER_DONT_ENABLE, dont enable the finder.
*/
struct drgn_error *
drgn_program_add_type_finder(struct drgn_program *prog, drgn_type_find_fn fn,
void *arg);
drgn_program_register_type_finder(struct drgn_program *prog, const char *name,
const struct drgn_type_finder_ops *ops,
void *arg, size_t enable_index);
/**
* Get the names of all registered type finders.
*
* The order of the names is arbitrary.
*
* @param[out] names_ret Returned array of names.
* @param[out] count_ret Returned number of names in @p names_ret.
*/
struct drgn_error *
drgn_program_registered_type_finders(struct drgn_program *prog,
const char ***names_ret,
size_t *count_ret);
/**
* Set the list of enabled type finders.
*
* Finders are called in the same order as the list until a type is found.
*
* @param[in] names Names of finders to enable, in order.
* @param[in] count Number of names in @p names.
*/
struct drgn_error *
drgn_program_set_enabled_type_finders(struct drgn_program *prog,
const char * const *names,
size_t count);
/**
* Get the names of enabled type finders, in order.
*
* @param[out] names_ret Returned array of names.
* @param[out] count_ret Returned number of names in @p names_ret.
*/
struct drgn_error *drgn_program_enabled_type_finders(struct drgn_program *prog,
const char ***names_ret,
size_t *count_ret);
/** Flags for @ref drgn_program_find_object(). */
enum drgn_find_object_flags {

View File

@ -283,6 +283,7 @@ drgn_program_enabled_##which##_finders(struct drgn_program *prog, \
count_ret); \
}
DRGN_PROGRAM_FINDER(type)
DRGN_PROGRAM_FINDER(symbol)
#undef DRGN_PROGRAM_FINDER

View File

@ -32,6 +32,7 @@
#include "type.h"
#include "vector.h"
struct drgn_type_finder;
struct drgn_symbol_finder;
/**
@ -82,7 +83,7 @@ struct drgn_program {
* Types.
*/
/** Callbacks for finding types. */
struct drgn_type_finder *type_finders;
struct drgn_handler_list type_finders;
/** Void type for each language. */
struct drgn_type void_types[DRGN_NUM_LANGUAGES];
/** Cache of primitive types. */
@ -383,6 +384,13 @@ drgn_program_find_symbol_by_address_internal(struct drgn_program *prog,
uint64_t address,
struct drgn_symbol **ret);
struct drgn_error *
drgn_program_register_type_finder_impl(struct drgn_program *prog,
struct drgn_type_finder *finder,
const char *name,
const struct drgn_type_finder_ops *ops,
void *arg, size_t enable_index);
struct drgn_error *
drgn_program_register_symbol_finder_impl(struct drgn_program *prog,
struct drgn_symbol_finder *finder,

View File

@ -385,6 +385,29 @@ static PyObject *Program_add_memory_segment(Program *self, PyObject *args,
Py_RETURN_NONE;
}
static inline struct drgn_error *
py_type_find_fn_common(PyObject *type_obj, void *arg,
struct drgn_qualified_type *ret)
{
if (!PyObject_TypeCheck(type_obj, &DrgnType_type)) {
PyErr_SetString(PyExc_TypeError,
"type find callback must return Type or None");
return drgn_error_from_python();
}
// This check is also done in libdrgn, but we need it here because if
// the type isn't from this program, then there's no guarantee that it
// will remain valid after we decrement its reference count.
if (DrgnType_prog((DrgnType *)type_obj)
!= (Program *)PyTuple_GET_ITEM(arg, 0)) {
PyErr_SetString(PyExc_ValueError,
"type find callback returned type from wrong program");
return drgn_error_from_python();
}
ret->type = ((DrgnType *)type_obj)->type;
ret->qualifiers = ((DrgnType *)type_obj)->qualifiers;
return NULL;
}
static struct drgn_error *py_type_find_fn(uint64_t kinds, const char *name,
size_t name_len, const char *filename,
void *arg,
@ -392,6 +415,33 @@ static struct drgn_error *py_type_find_fn(uint64_t kinds, const char *name,
{
PyGILState_guard();
_cleanup_pydecref_ PyObject *name_obj =
PyUnicode_FromStringAndSize(name, name_len);
if (!name_obj)
return drgn_error_from_python();
_cleanup_pydecref_ PyObject *kinds_obj = TypeKindSet_wrap(kinds);
if (!kinds_obj)
return drgn_error_from_python();
_cleanup_pydecref_ PyObject *type_obj =
PyObject_CallFunction(PyTuple_GET_ITEM(arg, 1), "OOOs",
PyTuple_GET_ITEM(arg, 0), kinds_obj,
name_obj, filename);
if (!type_obj)
return drgn_error_from_python();
if (type_obj == Py_None)
return &drgn_not_found;
return py_type_find_fn_common(type_obj, arg, ret);
}
// Old version for add_type_finder().
static struct drgn_error *py_type_find_fn_old(uint64_t kinds, const char *name,
size_t name_len,
const char *filename, void *arg,
struct drgn_qualified_type *ret)
{
PyGILState_guard();
_cleanup_pydecref_ PyObject *name_obj =
PyUnicode_FromStringAndSize(name, name_len);
if (!name_obj)
@ -411,56 +461,11 @@ static struct drgn_error *py_type_find_fn(uint64_t kinds, const char *name,
return drgn_error_from_python();
if (type_obj == Py_None)
continue;
if (!PyObject_TypeCheck(type_obj, &DrgnType_type)) {
PyErr_SetString(PyExc_TypeError,
"type find callback must return Type or None");
return drgn_error_from_python();
}
// This check is also done in libdrgn, but we need it here
// because if the type isn't from this program, then there's no
// guarantee that it will remain valid after we decrement its
// reference count.
if (DrgnType_prog((DrgnType *)type_obj)
!= (Program *)PyTuple_GET_ITEM(arg, 0)) {
PyErr_SetString(PyExc_ValueError,
"type find callback returned type from wrong program");
return drgn_error_from_python();
}
ret->type = ((DrgnType *)type_obj)->type;
ret->qualifiers = ((DrgnType *)type_obj)->qualifiers;
return NULL;
return py_type_find_fn_common(type_obj, arg, ret);
}
return &drgn_not_found;
}
static PyObject *Program_add_type_finder(Program *self, PyObject *args,
PyObject *kwds)
{
struct drgn_error *err;
static char *keywords[] = {"fn", NULL};
PyObject *fn;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:add_type_finder",
keywords, &fn))
return NULL;
if (!PyCallable_Check(fn)) {
PyErr_SetString(PyExc_TypeError, "fn must be callable");
return NULL;
}
_cleanup_pydecref_ PyObject *arg = Py_BuildValue("OO", self, fn);
if (!arg)
return NULL;
if (Program_hold_object(self, arg))
return NULL;
err = drgn_program_add_type_finder(&self->prog, py_type_find_fn, arg);
if (err)
return set_drgn_error(err);
Py_RETURN_NONE;
}
static struct drgn_error *py_object_find_fn(const char *name, size_t name_len,
const char *filename,
enum drgn_find_object_flags flags,
@ -560,10 +565,11 @@ py_symbol_find_fn(const char *name, uint64_t addr,
return NULL;
}
#define symbol_finder_arg(self, fn) \
#define type_finder_arg(self, fn) \
_cleanup_pydecref_ PyObject *arg = Py_BuildValue("OO", self, fn); \
if (!arg) \
return NULL;
#define symbol_finder_arg type_finder_arg
#define DEFINE_PROGRAM_FINDER_METHODS(which) \
static PyObject *Program_register_##which##_finder(Program *self, \
@ -705,8 +711,62 @@ static PyObject *Program_enabled_##which##_finders(Program *self) \
return_ptr(res); \
}
DEFINE_PROGRAM_FINDER_METHODS(type)
DEFINE_PROGRAM_FINDER_METHODS(symbol)
static PyObject *deprecated_finder_name_obj(PyObject *fn)
{
_cleanup_pydecref_ PyObject *name_attr_obj =
PyObject_GetAttrString(fn, "__name__");
if (name_attr_obj) {
return PyUnicode_FromFormat("%S_%lu", name_attr_obj, random());
} else if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
return PyUnicode_FromFormat("%lu", random());
} else {
return NULL;
}
}
static PyObject *Program_add_type_finder(Program *self, PyObject *args,
PyObject *kwds)
{
struct drgn_error *err;
static char *keywords[] = {"fn", NULL};
PyObject *fn;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:add_type_finder",
keywords, &fn))
return NULL;
if (!PyCallable_Check(fn)) {
PyErr_SetString(PyExc_TypeError, "fn must be callable");
return NULL;
}
_cleanup_pydecref_ PyObject *arg = Py_BuildValue("OO", self, fn);
if (!arg)
return NULL;
_cleanup_pydecref_ PyObject *name_obj = deprecated_finder_name_obj(fn);
if (!name_obj)
return NULL;
const char *name = PyUnicode_AsUTF8(name_obj);
if (!name)
return NULL;
if (!Program_hold_reserve(self, 1))
return NULL;
const struct drgn_type_finder_ops ops = {
.find = py_type_find_fn_old,
};
err = drgn_program_register_type_finder(&self->prog, name, &ops, arg,
0);
if (err)
return set_drgn_error(err);
Program_hold_object(self, arg);
Py_RETURN_NONE;
}
static PyObject *Program_add_object_finder(Program *self, PyObject *args,
PyObject *kwds)
{
@ -1396,6 +1456,7 @@ static int Program_set_language(Program *self, PyObject *value, void *arg)
static PyMethodDef Program_methods[] = {
{"add_memory_segment", (PyCFunction)Program_add_memory_segment,
METH_VARARGS | METH_KEYWORDS, drgn_Program_add_memory_segment_DOC},
PROGRAM_FINDER_METHOD_DEFS(type),
PROGRAM_FINDER_METHOD_DEFS(symbol),
{"add_type_finder", (PyCFunction)Program_add_type_finder,
METH_VARARGS | METH_KEYWORDS, drgn_Program_add_type_finder_DOC},

View File

@ -1310,40 +1310,11 @@ void drgn_program_deinit_types(struct drgn_program *prog)
free(*it.entry);
drgn_dedupe_type_set_deinit(&prog->dedupe_types);
struct drgn_type_finder *finder = prog->type_finders;
while (finder) {
struct drgn_type_finder *next = finder->next;
if (finder->free)
free(finder);
finder = next;
}
}
struct drgn_error *
drgn_program_add_type_finder_impl(struct drgn_program *prog,
struct drgn_type_finder *finder,
drgn_type_find_fn fn, void *arg)
{
if (finder) {
finder->free = false;
} else {
finder = malloc(sizeof(*finder));
if (!finder)
return &drgn_enomem;
finder->free = true;
}
finder->fn = fn;
finder->arg = arg;
finder->next = prog->type_finders;
prog->type_finders = finder;
return NULL;
}
LIBDRGN_PUBLIC struct drgn_error *
drgn_program_add_type_finder(struct drgn_program *prog, drgn_type_find_fn fn,
void *arg)
{
return drgn_program_add_type_finder_impl(prog, NULL, fn, arg);
drgn_handler_list_deinit(struct drgn_type_finder, finder,
&prog->type_finders,
if (finder->ops.destroy)
finder->ops.destroy(finder->arg);
);
}
struct drgn_error *drgn_program_find_type_impl(struct drgn_program *prog,
@ -1352,11 +1323,11 @@ struct drgn_error *drgn_program_find_type_impl(struct drgn_program *prog,
const char *filename,
struct drgn_qualified_type *ret)
{
struct drgn_type_finder *finder = prog->type_finders;
while (finder) {
drgn_handler_list_for_each_enabled(struct drgn_type_finder, finder,
&prog->type_finders) {
struct drgn_error *err =
finder->fn(kinds, name, name_len, filename, finder->arg,
ret);
finder->ops.find(kinds, name, name_len, filename,
finder->arg, ret);
if (!err) {
if (drgn_type_program(ret->type) != prog) {
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
@ -1370,7 +1341,6 @@ struct drgn_error *drgn_program_find_type_impl(struct drgn_program *prog,
}
if (err != &drgn_not_found)
return err;
finder = finder->next;
}
return &drgn_not_found;
}

View File

@ -15,6 +15,7 @@
#include <assert.h>
#include "drgn.h"
#include "handler.h"
#include "hash_table.h"
#include "vector.h"
@ -50,21 +51,11 @@ drgn_byte_order_from_little_endian(bool little_endian)
/** Registered type finding callback in a @ref drgn_program. */
struct drgn_type_finder {
/** The callback. */
drgn_type_find_fn fn;
/** Argument to pass to @ref drgn_type_finder::fn. */
struct drgn_handler handler;
struct drgn_type_finder_ops ops;
void *arg;
/** Next callback to try. */
struct drgn_type_finder *next;
/** Whether this structure needs to be freed. */
bool free;
};
struct drgn_error *
drgn_program_add_type_finder_impl(struct drgn_program *prog,
struct drgn_type_finder *finder,
drgn_type_find_fn fn, void *arg);
DEFINE_HASH_SET_TYPE(drgn_dedupe_type_set, struct drgn_type *);
/** <tt>(type, member name)</tt> pair. */

View File

@ -68,11 +68,11 @@ class MockObject(NamedTuple):
def mock_program(platform=MOCK_PLATFORM, *, segments=None, types=None, objects=None):
def mock_find_type(kind, name, filename):
def mock_find_type(prog, kinds, name, filename):
if filename:
return None
for type in types:
if type.kind == kind:
if type.kind in kinds:
try:
type_name = type.name
except AttributeError:
@ -105,7 +105,7 @@ def mock_program(platform=MOCK_PLATFORM, *, segments=None, types=None, objects=N
if segments is not None:
add_mock_memory_segments(prog, segments)
if types is not None:
prog.add_type_finder(mock_find_type)
prog.register_type_finder("mock", mock_find_type, enable_index=0)
if objects is not None:
prog.add_object_finder(mock_object_find)
return prog

View File

@ -404,14 +404,73 @@ class TestMemory(TestCase):
)
class TestTypes(MockProgramTestCase):
def test_invalid_finder(self):
self.assertRaises(TypeError, self.prog.add_type_finder, "foo")
class TestTypeFinder(TestCase):
def test_register(self):
prog = Program(MOCK_PLATFORM)
self.prog.add_type_finder(lambda kind, name, filename: "foo")
self.assertRaises(TypeError, self.prog.type, "int")
# We don't test every corner case because the symbol finder tests cover
# the shared part.
self.assertEqual(prog.registered_type_finders(), {"dwarf"})
self.assertEqual(prog.enabled_type_finders(), ["dwarf"])
def test_finder_different_program(self):
prog.register_type_finder(
"foo", lambda prog, kinds, name, filename: None, enable_index=-1
)
self.assertEqual(prog.registered_type_finders(), {"dwarf", "foo"})
self.assertEqual(prog.enabled_type_finders(), ["dwarf", "foo"])
prog.set_enabled_type_finders(["foo"])
self.assertEqual(prog.registered_type_finders(), {"dwarf", "foo"})
self.assertEqual(prog.enabled_type_finders(), ["foo"])
def test_add_type_finder(self):
prog = Program(MOCK_PLATFORM)
def dummy(kind, name, filename):
if kind == TypeKind.TYPEDEF and name == "foo":
return prog.typedef_type("foo", prog.void_type())
else:
return None
prog.add_type_finder(dummy)
self.assertTrue(any("dummy" in name for name in prog.registered_type_finders()))
self.assertIn("dummy", prog.enabled_type_finders()[0])
self.assertIdentical(
prog.type("foo"), prog.typedef_type("foo", prog.void_type())
)
def test_register_invalid(self):
prog = Program(MOCK_PLATFORM)
self.assertRaises(TypeError, prog.register_type_finder, "foo", "foo")
prog.register_type_finder(
"foo", lambda prog, kinds, name, filename: "foo", enable_index=0
)
self.assertRaises(TypeError, prog.type, "int")
def test_add_invalid(self):
prog = Program(MOCK_PLATFORM)
self.assertRaises(TypeError, prog.add_type_finder, "foo")
prog.add_type_finder(lambda kind, name, filename: "foo")
self.assertRaises(TypeError, prog.type, "int")
def test_register_wrong_program(self):
def finder(prog, kinds, name, filename):
if TypeKind.TYPEDEF in kinds and name == "foo":
prog = Program()
return prog.typedef_type("foo", prog.void_type())
else:
return None
prog = Program(MOCK_PLATFORM)
prog.register_type_finder("foo", finder, enable_index=0)
self.assertRaisesRegex(
ValueError,
"type find callback returned type from wrong program",
prog.type,
"foo",
)
def test_add_wrong_program(self):
def finder(kind, name, filename):
if kind == TypeKind.TYPEDEF and name == "foo":
prog = Program()
@ -419,23 +478,41 @@ class TestTypes(MockProgramTestCase):
else:
return None
self.prog.add_type_finder(finder)
prog = Program(MOCK_PLATFORM)
prog.add_type_finder(finder)
self.assertRaisesRegex(
ValueError,
"type find callback returned type from wrong program",
self.prog.type,
prog.type,
"foo",
)
def test_wrong_kind(self):
self.prog.add_type_finder(lambda kind, name, filename: self.prog.void_type())
self.assertRaises(TypeError, self.prog.type, "int")
def test_register_wrong_kind(self):
prog = Program(MOCK_PLATFORM)
prog.register_type_finder(
"foo", lambda prog, kinds, name, filename: prog.void_type(), enable_index=0
)
self.assertRaises(TypeError, prog.type, "int")
def test_not_found(self):
self.assertRaises(LookupError, self.prog.type, "struct foo")
self.prog.add_type_finder(lambda kind, name, filename: None)
self.assertRaises(LookupError, self.prog.type, "struct foo")
def test_add_wrong_kind(self):
prog = Program(MOCK_PLATFORM)
prog.add_type_finder(lambda kind, name, filename: prog.void_type())
self.assertRaises(TypeError, prog.type, "int")
def test_register_not_found(self):
prog = Program(MOCK_PLATFORM)
prog.register_type_finder(
"foo", lambda prog, kinds, name, filename: None, enable_index=0
)
self.assertRaises(LookupError, prog.type, "struct foo")
def test_add_not_found(self):
prog = Program(MOCK_PLATFORM)
prog.add_type_finder(lambda kind, name, filename: None)
self.assertRaises(LookupError, prog.type, "struct foo")
class TestTypes(MockProgramTestCase):
def test_already_type(self):
self.assertIdentical(
self.prog.type(self.prog.pointer_type(self.prog.void_type())),