mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-24 10:03:05 +00:00
dac4f9156a
Use functions instead of properties to make it clear that they're not free, and make it possible to construct a DwarfFile directly from an ElfFile.
747 lines
26 KiB
Python
747 lines
26 KiB
Python
from collections import OrderedDict
|
|
import ctypes
|
|
import os.path
|
|
import struct
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
from drgn.dwarf import DwarfFile, DwarfIndex
|
|
from drgn.type import (
|
|
ArrayType,
|
|
BitFieldType,
|
|
BoolType,
|
|
EnumType,
|
|
FloatType,
|
|
IntType,
|
|
PointerType,
|
|
StructType,
|
|
TypedefType,
|
|
TypeFactory,
|
|
UnionType,
|
|
VoidType,
|
|
)
|
|
|
|
|
|
point_type = StructType('point', 8, [
|
|
('x', 0, lambda: IntType('int', 4, True)),
|
|
('y', 4, lambda: IntType('int', 4, True)),
|
|
])
|
|
anonymous_point_type = StructType(None, 8, [
|
|
('x', 0, lambda: IntType('int', 4, True)),
|
|
('y', 4, lambda: IntType('int', 4, True)),
|
|
])
|
|
const_anonymous_point_type = StructType(None, 8, [
|
|
('x', 0, lambda: IntType('int', 4, True)),
|
|
('y', 4, lambda: IntType('int', 4, True)),
|
|
], {'const'})
|
|
line_segment_type = StructType('line_segment', 16, [
|
|
('a', 0, lambda: point_type),
|
|
('b', 8, lambda: point_type),
|
|
])
|
|
pointer_size = ctypes.sizeof(ctypes.c_void_p)
|
|
|
|
|
|
class TestType(unittest.TestCase):
|
|
def test_void(self):
|
|
type_ = VoidType()
|
|
self.assertEqual(str(type_), 'void')
|
|
self.assertRaises(ValueError, type_.sizeof)
|
|
self.assertRaises(ValueError, type_.read, b'')
|
|
self.assertRaises(ValueError, type_.format, b'')
|
|
|
|
def test_int(self):
|
|
type_ = IntType('int', 4, True)
|
|
self.assertEqual(str(type_), 'int')
|
|
self.assertEqual(type_.sizeof(), 4)
|
|
buffer = (99).to_bytes(4, sys.byteorder)
|
|
self.assertEqual(type_.read(buffer), 99)
|
|
self.assertEqual(type_.format(buffer), '(int)99')
|
|
self.assertEqual(type_.format(buffer, cast=False), '99')
|
|
buffer = b'\0\0' + (-1).to_bytes(4, sys.byteorder, signed=True)
|
|
self.assertEqual(type_.read(buffer, 2), -1)
|
|
self.assertRaises(ValueError, type_.read, buffer, 3)
|
|
|
|
type_ = IntType('unsigned long', 8, False)
|
|
buffer = b'\0' + (99).to_bytes(8, sys.byteorder)
|
|
self.assertEqual(type_.read(buffer, 1), 99)
|
|
buffer = b'\xff\xff\xff' + (0xffffffffffffffff).to_bytes(8, sys.byteorder)
|
|
self.assertEqual(type_.read(buffer, 3), 0xffffffffffffffff)
|
|
|
|
def test_float(self):
|
|
type_ = FloatType('double', 8)
|
|
self.assertEqual(str(type_), 'double')
|
|
self.assertEqual(type_.sizeof(), 8)
|
|
buffer = struct.pack('d', 3.14)
|
|
self.assertEqual(type_.read(buffer), 3.14)
|
|
self.assertEqual(type_.read(b'\0' + buffer, 1), 3.14)
|
|
self.assertRaises(ValueError, type_.read, buffer, 1)
|
|
|
|
type_ = FloatType('float', 4)
|
|
buffer = struct.pack('f', 1.5)
|
|
self.assertEqual(type_.read(buffer), 1.5)
|
|
self.assertEqual(type_.read(b'\0\0\0' + buffer, 3), 1.5)
|
|
self.assertRaises(ValueError, type_.read, b'')
|
|
|
|
def test_bool(self):
|
|
type_ = BoolType('_Bool', 1)
|
|
self.assertEqual(str(type_), '_Bool')
|
|
self.assertEqual(type_.sizeof(), 1)
|
|
self.assertEqual(type_.read(b'\0'), 0)
|
|
self.assertEqual(type_.format(b'\0'), '(_Bool)0')
|
|
self.assertEqual(type_.read(b'\0\x01', 1), 1)
|
|
self.assertEqual(type_.format(b'\x01'), '(_Bool)1')
|
|
self.assertRaises(ValueError, type_.read, b'')
|
|
self.assertRaises(ValueError, type_.read, b'\0', 1)
|
|
|
|
def test_qualifiers(self):
|
|
type_ = IntType('int', 4, True, {'const'})
|
|
self.assertEqual(str(type_), 'const int')
|
|
self.assertEqual(type_.sizeof(), 4)
|
|
self.assertEqual(type_.read(b'\0\0\0\0'), 0)
|
|
|
|
type_.qualifiers.add('volatile')
|
|
self.assertEqual(str(type_), 'const volatile int')
|
|
self.assertEqual(type_.sizeof(), 4)
|
|
|
|
def test_typedef(self):
|
|
type_ = TypedefType('INT', IntType('int', 4, True))
|
|
self.assertEqual(str(type_), 'typedef int INT')
|
|
self.assertEqual(type_.sizeof(), 4)
|
|
self.assertEqual(type_.read(b'\0\0\0\0'), 0)
|
|
self.assertEqual(type_.format(b'\0\0\0\0'), '(INT)0')
|
|
|
|
type_ = TypedefType('string', PointerType(pointer_size, IntType('char', 1, True)))
|
|
self.assertEqual(str(type_), 'typedef char *string')
|
|
self.assertEqual(type_.sizeof(), pointer_size)
|
|
|
|
type_ = TypedefType('CINT', IntType('int', 4, True, {'const'}))
|
|
self.assertEqual(str(type_), 'typedef const int CINT')
|
|
self.assertEqual(type_.sizeof(), 4)
|
|
self.assertEqual(type_.read(b'\0\0\0\0'), 0)
|
|
|
|
type_ = TypedefType('INT', IntType('int', 4, True), {'const'})
|
|
self.assertEqual(str(type_), 'const typedef int INT')
|
|
self.assertEqual(type_.sizeof(), 4)
|
|
self.assertEqual(type_.read(b'\0\0\0\0'), 0)
|
|
|
|
def test_struct(self):
|
|
self.assertEqual(str(point_type), """\
|
|
struct point {
|
|
int x;
|
|
int y;
|
|
}""")
|
|
self.assertEqual(point_type.sizeof(), 8)
|
|
self.assertEqual(point_type.members(), ['x', 'y'])
|
|
self.assertEqual(point_type.offsetof('x'), 0)
|
|
self.assertEqual(point_type.offsetof('y'), 4)
|
|
self.assertEqual(point_type.typeof('x'), IntType('int', 4, True))
|
|
self.assertEqual(point_type.typeof('y'), IntType('int', 4, True))
|
|
buffer = ((99).to_bytes(4, sys.byteorder, signed=True) +
|
|
(-1).to_bytes(4, sys.byteorder, signed=True))
|
|
self.assertEqual(point_type.read(buffer), OrderedDict([
|
|
('x', 99),
|
|
('y', -1),
|
|
]))
|
|
self.assertEqual(point_type.read(b'\0' + buffer, 1), OrderedDict([
|
|
('x', 99),
|
|
('y', -1),
|
|
]))
|
|
self.assertEqual(point_type.format(b'\0' + buffer, 1), """\
|
|
(struct point){
|
|
.x = (int)99,
|
|
.y = (int)-1,
|
|
}""")
|
|
self.assertRaises(ValueError, point_type.read, buffer[:7])
|
|
self.assertRaises(ValueError, point_type.read, buffer, 1)
|
|
|
|
self.assertEqual(str(line_segment_type), """\
|
|
struct line_segment {
|
|
struct point a;
|
|
struct point b;
|
|
}""")
|
|
|
|
self.assertEqual(str(anonymous_point_type), """\
|
|
struct {
|
|
int x;
|
|
int y;
|
|
}""")
|
|
|
|
type_ = StructType('line_segment', 16, [
|
|
(None, 0, lambda: const_anonymous_point_type),
|
|
('b', 8, lambda: const_anonymous_point_type),
|
|
], {'const', 'volatile'})
|
|
self.assertEqual(str(type_), """\
|
|
const volatile struct line_segment {
|
|
const struct {
|
|
int x;
|
|
int y;
|
|
};
|
|
const struct {
|
|
int x;
|
|
int y;
|
|
} b;
|
|
}""")
|
|
|
|
type_ = StructType('foo', None, None)
|
|
self.assertEqual(str(type_), 'struct foo')
|
|
self.assertRaises(ValueError, type_.sizeof)
|
|
|
|
type_ = StructType(None, 12, [
|
|
('x', 0, lambda: IntType('int', 4, True)),
|
|
(None, 4, lambda: StructType('point', 8, [
|
|
('y', 0, lambda: IntType('int', 4, True)),
|
|
('z', 4, lambda: IntType('int', 4, True)),
|
|
])),
|
|
])
|
|
self.assertEqual(type_.members(), ['x', 'y', 'z'])
|
|
self.assertEqual(type_.offsetof('x'), 0)
|
|
self.assertEqual(type_.offsetof('y'), 4)
|
|
self.assertEqual(type_.offsetof('z'), 8)
|
|
self.assertEqual(type_.typeof('x'), IntType('int', 4, True))
|
|
self.assertEqual(type_.typeof('y'), IntType('int', 4, True))
|
|
self.assertEqual(type_.typeof('z'), IntType('int', 4, True))
|
|
|
|
def test_bit_field(self):
|
|
type_ = StructType(None, 8, [
|
|
('x', 0, lambda: BitFieldType(IntType('int', 4, True), 0, 4)),
|
|
('y', 0, lambda: BitFieldType(IntType('int', 4, True, {'const'}), 4, 28)),
|
|
('z', 4, lambda: BitFieldType(IntType('int', 4, True), 0, 5)),
|
|
])
|
|
self.assertEqual(str(type_), """\
|
|
struct {
|
|
int x : 4;
|
|
const int y : 28;
|
|
int z : 5;
|
|
}""")
|
|
buffer = b'\x07\x10\x5e\x5f\x1f\0\0\0'
|
|
self.assertEqual(type_.typeof('x').read(buffer), 7)
|
|
self.assertEqual(type_.typeof('x').read(b'\0' + buffer, 1), 7)
|
|
self.assertRaises(ValueError, type_.typeof('x').read, b'')
|
|
self.assertEqual(type_.typeof('y').read(buffer), 100000000)
|
|
self.assertEqual(type_.typeof('y').read(b'\0\0\0' + buffer, 3),
|
|
100000000)
|
|
self.assertRaises(ValueError, type_.typeof('y').read, buffer[:3])
|
|
self.assertEqual(type_.typeof('z').read(buffer, 4), -1)
|
|
self.assertEqual(type_.typeof('z').read(b'\0\0' + buffer, 6), -1)
|
|
self.assertRaises(ValueError, type_.typeof('z').read, buffer, 8)
|
|
self.assertEqual(type_.read(buffer), OrderedDict([
|
|
('x', 7),
|
|
('y', 100000000),
|
|
('z', -1),
|
|
]))
|
|
self.assertEqual(type_.format(buffer), """\
|
|
{
|
|
.x = (int)7,
|
|
.y = (const int)100000000,
|
|
.z = (int)-1,
|
|
}""")
|
|
|
|
type_ = BitFieldType(IntType('int', 4, True), 0, 4)
|
|
self.assertEqual(str(type_), 'int : 4')
|
|
self.assertRaises(ValueError, type_.type_name)
|
|
|
|
def test_union(self):
|
|
type_ = UnionType('value', 4, [
|
|
('i', 0, lambda: IntType('int', 4, True)),
|
|
('f', 0, lambda: FloatType('float', 4)),
|
|
])
|
|
self.assertEqual(str(type_), """\
|
|
union value {
|
|
int i;
|
|
float f;
|
|
}""")
|
|
self.assertEqual(type_.sizeof(), 4)
|
|
buffer = b'\x00\x00\x80?'
|
|
self.assertEqual(type_.read(buffer), OrderedDict([
|
|
('i', 1065353216),
|
|
('f', 1.0),
|
|
]))
|
|
self.assertEqual(type_.read(b'\0' + buffer, 1), OrderedDict([
|
|
('i', 1065353216),
|
|
('f', 1.0),
|
|
]))
|
|
self.assertEqual(type_.format(buffer), """\
|
|
(union value){
|
|
.i = (int)1065353216,
|
|
.f = (float)1.0,
|
|
}""")
|
|
|
|
type_ = UnionType('value', 8, [
|
|
('i', 0, lambda: IntType('int', 4, True)),
|
|
('f', 0, lambda: FloatType('float', 4)),
|
|
('p', 0, lambda: point_type),
|
|
])
|
|
self.assertEqual(str(type_), """\
|
|
union value {
|
|
int i;
|
|
float f;
|
|
struct point p;
|
|
}""")
|
|
|
|
type_ = UnionType('foo', None, None)
|
|
self.assertEqual(str(type_), 'union foo')
|
|
self.assertRaises(ValueError, type_.sizeof)
|
|
|
|
def test_enum(self):
|
|
type_ = EnumType('color', 4, False, [
|
|
('RED', 0),
|
|
('GREEN', 1),
|
|
('BLUE', 2)
|
|
])
|
|
self.assertEqual(str(type_), """\
|
|
enum color {
|
|
RED = 0,
|
|
GREEN = 1,
|
|
BLUE = 2,
|
|
}""")
|
|
self.assertEqual(type_.sizeof(), 4)
|
|
buffer = (0).to_bytes(4, sys.byteorder)
|
|
self.assertEqual(type_.read(buffer), type_._enum.RED)
|
|
buffer = (1).to_bytes(4, sys.byteorder)
|
|
self.assertEqual(type_.read(b'\0' + buffer, 1), type_._enum.GREEN)
|
|
self.assertEqual(type_.format(b'\0' + buffer, 1), '(enum color)GREEN')
|
|
buffer = (4).to_bytes(4, sys.byteorder)
|
|
self.assertEqual(type_.read(b'\0\0\0' + buffer, 3), 4)
|
|
self.assertEqual(type_.format(b'\0\0\0' + buffer, 3), '(enum color)4')
|
|
self.assertRaises(ValueError, type_.read, buffer, 3)
|
|
self.assertRaises(ValueError, type_.read, b'')
|
|
|
|
type_.qualifiers.add('const')
|
|
self.assertEqual(str(type_), """\
|
|
const enum color {
|
|
RED = 0,
|
|
GREEN = 1,
|
|
BLUE = 2,
|
|
}""")
|
|
|
|
type_.qualifiers.add('volatile')
|
|
self.assertEqual(str(type_), """\
|
|
const volatile enum color {
|
|
RED = 0,
|
|
GREEN = 1,
|
|
BLUE = 2,
|
|
}""")
|
|
|
|
type_ = EnumType(None, 4, True, [
|
|
('RED', 10),
|
|
('GREEN', 11),
|
|
('BLUE', -1)
|
|
])
|
|
self.assertEqual(str(type_), """\
|
|
enum {
|
|
RED = 10,
|
|
GREEN = 11,
|
|
BLUE = -1,
|
|
}""")
|
|
buffer = (-1).to_bytes(4, sys.byteorder, signed=True)
|
|
self.assertEqual(type_.read(buffer), -1)
|
|
|
|
type_ = EnumType('foo', None, None, None)
|
|
self.assertEqual(str(type_), 'enum foo')
|
|
self.assertRaises(ValueError, type_.sizeof)
|
|
|
|
def test_pointer(self):
|
|
type_ = PointerType(pointer_size, IntType('int', 4, True))
|
|
self.assertEqual(str(type_), 'int *')
|
|
self.assertEqual(type_.sizeof(), pointer_size)
|
|
buffer = (0x7fffffff).to_bytes(pointer_size, sys.byteorder)
|
|
self.assertEqual(type_.read(buffer), 0x7fffffff)
|
|
self.assertEqual(type_.read(b'\0' + buffer, 1), 0x7fffffff)
|
|
self.assertEqual(type_.format(b'\0' + buffer, 1), '(int *)0x7fffffff')
|
|
|
|
type_ = PointerType(pointer_size, IntType('int', 4, True), {'const'})
|
|
self.assertEqual(str(type_), 'int * const')
|
|
|
|
type_ = PointerType(pointer_size, point_type)
|
|
self.assertEqual(str(type_), 'struct point *')
|
|
|
|
type_ = PointerType(pointer_size, PointerType(pointer_size, IntType('int', 4, True)))
|
|
self.assertEqual(str(type_), 'int **')
|
|
|
|
type_ = PointerType(pointer_size, VoidType())
|
|
self.assertEqual(str(type_), 'void *')
|
|
|
|
def test_array(self):
|
|
type_ = ArrayType(IntType('int', 4, True), 2)
|
|
self.assertEqual(str(type_), 'int [2]')
|
|
self.assertEqual(type_.sizeof(), 8)
|
|
buffer = ((99).to_bytes(4, sys.byteorder, signed=True) +
|
|
(-1).to_bytes(4, sys.byteorder, signed=True))
|
|
self.assertEqual(type_.read(buffer), [99, -1])
|
|
self.assertEqual(type_.read(b'\0\0\0' + buffer, 3), [99, -1])
|
|
self.assertEqual(type_.format(b'\0\0\0' + buffer, 3), """\
|
|
(int [2]){
|
|
99,
|
|
-1,
|
|
}""")
|
|
buffer = ((99).to_bytes(4, sys.byteorder, signed=True) +
|
|
(0).to_bytes(4, sys.byteorder, signed=True))
|
|
self.assertEqual(type_.format(b'\0\0\0' + buffer, 3), """\
|
|
(int [2]){
|
|
99,
|
|
}""")
|
|
buffer = ((0).to_bytes(4, sys.byteorder, signed=True) +
|
|
(-1).to_bytes(4, sys.byteorder, signed=True))
|
|
self.assertEqual(type_.format(b'\0\0\0' + buffer, 3), """\
|
|
(int [2]){
|
|
0,
|
|
-1,
|
|
}""")
|
|
self.assertRaises(ValueError, type_.read, buffer, 3)
|
|
self.assertRaises(ValueError, type_.format, buffer, 3)
|
|
|
|
type_ = ArrayType(ArrayType(IntType('int', 4, True), 3), 2)
|
|
self.assertEqual(str(type_), 'int [2][3]')
|
|
|
|
type_ = ArrayType(ArrayType(ArrayType(IntType('int', 4, True), 4), 3), 2)
|
|
self.assertEqual(str(type_), 'int [2][3][4]')
|
|
|
|
def test_array_with_empty_element(self):
|
|
type_ = ArrayType(StructType('empty', 0, []), 2)
|
|
self.assertEqual(str(type_), 'struct empty [2]')
|
|
self.assertEqual(type_.sizeof(), 0)
|
|
self.assertEqual(type_.read(b''), [OrderedDict(), OrderedDict()])
|
|
self.assertEqual(type_.format(b''), '(struct empty [2]){}')
|
|
self.assertRaises(ValueError, type_.format, b'', 1)
|
|
|
|
def test_incomplete_array(self):
|
|
type_ = ArrayType(IntType('int', 4, True), None)
|
|
self.assertEqual(str(type_), 'int []')
|
|
self.assertRaises(ValueError, type_.sizeof)
|
|
self.assertRaises(ValueError, type_.read, b'')
|
|
self.assertEqual(type_.format(b''), '(int []){}')
|
|
|
|
type_ = ArrayType(ArrayType(IntType('int', 4, True), 2), None)
|
|
self.assertEqual(str(type_), 'int [][2]')
|
|
|
|
|
|
class TestFromDwarfType(unittest.TestCase):
|
|
def setUp(self):
|
|
self.tmp_dir = tempfile.TemporaryDirectory()
|
|
|
|
def tearDown(self):
|
|
self.tmp_dir.cleanup()
|
|
|
|
def compile_type(self, decl):
|
|
program_path = os.path.join(self.tmp_dir.name, 'test')
|
|
source_path = program_path + '.c'
|
|
with open(source_path, 'w') as f:
|
|
f.write(decl)
|
|
f.write(';\nint main(void) { return 0; }\n')
|
|
subprocess.check_call(['gcc', '-g', '-o', program_path, source_path])
|
|
with open(program_path, 'rb') as program_file:
|
|
dwarf_file = DwarfFile.from_file(program_file)
|
|
dwarf_index = DwarfIndex()
|
|
for cu in dwarf_file.cu_headers():
|
|
dwarf_index.index_cu(cu)
|
|
type_factory = TypeFactory(dwarf_index)
|
|
dwarf_type = dwarf_index.find_variable('x').type()
|
|
return type_factory.from_dwarf_type(dwarf_type)
|
|
|
|
def test_char(self):
|
|
self.assertEqual(self.compile_type('char x'),
|
|
IntType('char', 1, True))
|
|
self.assertEqual(self.compile_type('signed char x'),
|
|
IntType('signed char', 1, True))
|
|
self.assertEqual(self.compile_type('unsigned char x'),
|
|
IntType('unsigned char', 1, False))
|
|
|
|
def test_short(self):
|
|
self.assertEqual(self.compile_type('short x'),
|
|
IntType('short int', 2, True))
|
|
self.assertEqual(self.compile_type('signed short x'),
|
|
IntType('short int', 2, True))
|
|
self.assertEqual(self.compile_type('unsigned short x'),
|
|
IntType('short unsigned int', 2, False))
|
|
|
|
def test_int(self):
|
|
self.assertEqual(self.compile_type('int x'),
|
|
IntType('int', 4, True))
|
|
self.assertEqual(self.compile_type('signed int x'),
|
|
IntType('int', 4, True))
|
|
self.assertEqual(self.compile_type('unsigned int x'),
|
|
IntType('unsigned int', 4, False))
|
|
|
|
def test_long(self):
|
|
self.assertEqual(self.compile_type('long x'),
|
|
IntType('long int', 8, True))
|
|
self.assertEqual(self.compile_type('signed long x'),
|
|
IntType('long int', 8, True))
|
|
self.assertEqual(self.compile_type('unsigned long x'),
|
|
IntType('long unsigned int', 8, False))
|
|
|
|
def test_long_long(self):
|
|
self.assertEqual(self.compile_type('long long x'),
|
|
IntType('long long int', 8, True))
|
|
self.assertEqual(self.compile_type('signed long long x'),
|
|
IntType('long long int', 8, True))
|
|
self.assertEqual(self.compile_type('unsigned long long x'),
|
|
IntType('long long unsigned int', 8, False))
|
|
|
|
def test_float(self):
|
|
self.assertEqual(self.compile_type('float x'),
|
|
FloatType('float', 4))
|
|
self.assertEqual(self.compile_type('double x'),
|
|
FloatType('double', 8))
|
|
self.assertEqual(self.compile_type('long double x'),
|
|
FloatType('long double', 16))
|
|
|
|
def test_bool(self):
|
|
self.assertEqual(self.compile_type('_Bool x'), BoolType('_Bool', 1))
|
|
|
|
def test_qualifiers(self):
|
|
# restrict is only valid in function parameters, and GCC doesn't seem
|
|
# to create a type for _Atomic.
|
|
self.assertEqual(self.compile_type('const int x'),
|
|
IntType('int', 4, True, {'const'}))
|
|
self.assertEqual(self.compile_type('volatile int x'),
|
|
IntType('int', 4, True, {'volatile'}))
|
|
self.assertEqual(self.compile_type('const volatile int x'),
|
|
IntType('int', 4, True, {'const', 'volatile'}))
|
|
|
|
def test_typedef(self):
|
|
self.assertEqual(self.compile_type('typedef int INT; INT x'),
|
|
TypedefType('INT', IntType('int', 4, True)))
|
|
self.assertEqual(self.compile_type('typedef char *string; string x'),
|
|
TypedefType('string', PointerType(pointer_size, IntType('char', 1, True))))
|
|
self.assertEqual(self.compile_type('typedef const int CINT; CINT x'),
|
|
TypedefType('CINT', IntType('int', 4, True, {'const'})))
|
|
self.assertEqual(self.compile_type('typedef int INT; const INT x'),
|
|
TypedefType('INT', IntType('int', 4, True), {'const'}))
|
|
|
|
def test_struct(self):
|
|
self.assertEqual(self.compile_type("""\
|
|
struct point {
|
|
int x;
|
|
int y;
|
|
} x;"""), point_type)
|
|
|
|
self.assertEqual(self.compile_type("""\
|
|
struct point {
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
struct line_segment {
|
|
struct point a;
|
|
struct point b;
|
|
} x;"""), line_segment_type)
|
|
|
|
self.assertEqual(self.compile_type("""\
|
|
struct {
|
|
int x;
|
|
int y;
|
|
} x;"""), anonymous_point_type)
|
|
|
|
self.assertEqual(self.compile_type("""\
|
|
const struct line_segment {
|
|
const struct {
|
|
int x;
|
|
int y;
|
|
};
|
|
const struct {
|
|
int x;
|
|
int y;
|
|
} b;
|
|
} x;"""), StructType('line_segment', 16, [
|
|
(None, 0, lambda: const_anonymous_point_type),
|
|
('b', 8, lambda: const_anonymous_point_type),
|
|
], {'const'}))
|
|
|
|
def test_incomplete_struct(self):
|
|
self.assertEqual(self.compile_type('struct foo; extern struct foo x'),
|
|
StructType('foo', None, None))
|
|
|
|
def test_bit_field(self):
|
|
self.assertEqual(self.compile_type("""\
|
|
struct {
|
|
int x : 4;
|
|
const int y : 28;
|
|
int z : 5;
|
|
} x;"""), StructType(None, 8, [
|
|
('x', 0, lambda: BitFieldType(IntType('int', 4, True), 0, 4)),
|
|
('y', 0, lambda: BitFieldType(IntType('int', 4, True, {'const'}), 4, 28)),
|
|
('z', 4, lambda: BitFieldType(IntType('int', 4, True), 0, 5)),
|
|
]))
|
|
|
|
def test_union(self):
|
|
self.assertEqual(self.compile_type("""\
|
|
union value {
|
|
int i;
|
|
float f;
|
|
} x;"""), UnionType('value', 4, [
|
|
('i', 0, lambda: IntType('int', 4, True)),
|
|
('f', 0, lambda: FloatType('float', 4)),
|
|
]))
|
|
|
|
self.assertEqual(self.compile_type("""\
|
|
struct point {
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
union value {
|
|
int i;
|
|
float f;
|
|
struct point p;
|
|
} x;"""), UnionType('value', 8, [
|
|
('i', 0, lambda: IntType('int', 4, True)),
|
|
('f', 0, lambda: FloatType('float', 4)),
|
|
('p', 0, lambda: point_type),
|
|
]))
|
|
|
|
def test_incomplete_union(self):
|
|
self.assertEqual(self.compile_type('union foo; extern union foo x'),
|
|
UnionType('foo', None, None))
|
|
|
|
def test_enum(self):
|
|
self.assertEqual(self.compile_type("""\
|
|
enum color {
|
|
RED,
|
|
GREEN,
|
|
BLUE,
|
|
} x;"""), EnumType('color', 4, False, [('RED', 0), ('GREEN', 1), ('BLUE', 2)]))
|
|
|
|
self.assertEqual(self.compile_type("""\
|
|
enum {
|
|
RED = 10,
|
|
GREEN,
|
|
BLUE = -1,
|
|
} x;"""), EnumType(None, 4, True, [('RED', 10), ('GREEN', 11), ('BLUE', -1)]))
|
|
|
|
def test_incomplete_enum(self):
|
|
self.assertEqual(self.compile_type('enum foo; extern enum foo x'),
|
|
EnumType('foo', None, None, None))
|
|
|
|
def test_pointer(self):
|
|
self.assertEqual(self.compile_type('int *x'),
|
|
PointerType(pointer_size, IntType('int', 4, True)))
|
|
|
|
self.assertEqual(self.compile_type('int * const x'),
|
|
PointerType(pointer_size, IntType('int', 4, True), {'const'}))
|
|
|
|
self.assertEqual(self.compile_type("""\
|
|
struct point {
|
|
int x;
|
|
int y;
|
|
} *x;"""), PointerType(pointer_size, point_type))
|
|
|
|
self.assertEqual(self.compile_type('int **x'),
|
|
PointerType(pointer_size, PointerType(pointer_size, IntType('int', 4, True))))
|
|
|
|
self.assertEqual(self.compile_type('void *x'),
|
|
PointerType(pointer_size, VoidType()))
|
|
|
|
def test_array(self):
|
|
self.assertEqual(self.compile_type('int x[2]'),
|
|
ArrayType(IntType('int', 4, True), 2))
|
|
self.assertEqual(self.compile_type('int x[2][3]'),
|
|
ArrayType(ArrayType(IntType('int', 4, True), 3), 2))
|
|
self.assertEqual(self.compile_type('int x[2][3][4]'),
|
|
ArrayType(ArrayType(ArrayType(IntType('int', 4, True), 4), 3), 2))
|
|
|
|
def test_incomplete_array(self):
|
|
self.assertEqual(self.compile_type('extern int x[]'),
|
|
ArrayType(IntType('int', 4, True), None))
|
|
self.assertEqual(self.compile_type('extern int x[][2]'),
|
|
ArrayType(ArrayType(IntType('int', 4, True), 2), None))
|
|
|
|
|
|
class TestFromTypeString(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
program_path = os.path.join(tmp_dir, 'test')
|
|
source_path = program_path + '.c'
|
|
with open(source_path, 'w') as f:
|
|
f.write("""\
|
|
int i;
|
|
|
|
struct point {
|
|
int x, y;
|
|
} u;
|
|
|
|
union value {
|
|
int i;
|
|
float f;
|
|
} v;
|
|
|
|
enum color {
|
|
RED,
|
|
GREEN,
|
|
BLUE,
|
|
} e;
|
|
|
|
typedef struct point point;
|
|
|
|
point t;
|
|
|
|
int main(void)
|
|
{
|
|
return 0;
|
|
}
|
|
""")
|
|
subprocess.check_call(['gcc', '-g', '-o', program_path, source_path])
|
|
cls.program_file = open(program_path, 'rb')
|
|
dwarf_file = DwarfFile.from_file(cls.program_file)
|
|
dwarf_index = DwarfIndex()
|
|
for cu in dwarf_file.cu_headers():
|
|
dwarf_index.index_cu(cu)
|
|
cls.type_factory = TypeFactory(dwarf_index)
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
if hasattr(cls, 'program_file'):
|
|
cls.program_file.close()
|
|
|
|
def test_void_type(self):
|
|
self.assertEqual(self.type_factory.from_type_string('void'),
|
|
VoidType())
|
|
self.assertEqual(self.type_factory.from_type_string('const void'),
|
|
VoidType({'const'}))
|
|
|
|
def test_base_type(self):
|
|
self.assertEqual(self.type_factory.from_type_string('int'),
|
|
IntType('int', 4, True))
|
|
self.assertEqual(self.type_factory.from_type_string('volatile int'),
|
|
IntType('int', 4, True, {'volatile'}))
|
|
|
|
def test_struct_type(self):
|
|
self.assertEqual(self.type_factory.from_type_string('struct point'),
|
|
point_type)
|
|
|
|
def test_union_type(self):
|
|
self.assertEqual(self.type_factory.from_type_string('union value'),
|
|
UnionType('value', 4, [
|
|
('i', 0, lambda: IntType('int', 4, True)),
|
|
('f', 0, lambda: FloatType('float', 4)),
|
|
]))
|
|
|
|
def test_enum_type(self):
|
|
self.assertEqual(self.type_factory.from_type_string('enum color'),
|
|
EnumType('color', 4, False, [
|
|
('RED', 0),
|
|
('GREEN', 1),
|
|
('BLUE', 2)
|
|
]))
|
|
|
|
def test_typedef_type(self):
|
|
self.assertEqual(self.type_factory.from_type_string('point'),
|
|
TypedefType('point', point_type))
|
|
self.assertEqual(self.type_factory.from_type_string('const point'),
|
|
TypedefType('point', point_type, {'const'}))
|
|
|
|
def test_pointer_type(self):
|
|
self.assertEqual(self.type_factory.from_type_string('int *'),
|
|
PointerType(pointer_size, IntType('int', 4, True)))
|
|
self.assertEqual(self.type_factory.from_type_string('int * const'),
|
|
PointerType(pointer_size, IntType('int', 4, True), {'const'}))
|
|
|
|
def test_array_type(self):
|
|
self.assertEqual(self.type_factory.from_type_string('int [4]'),
|
|
ArrayType(IntType('int', 4, True), 4))
|
|
self.assertEqual(self.type_factory.from_type_string('int []'),
|
|
ArrayType(IntType('int', 4, True), None))
|