from functools import reduce import operator import unittest from drgn import ( Qualifiers, array_type, bool_type, class_type, complex_type, enum_type, float_type, function_type, int_type, pointer_type, struct_type, typedef_type, union_type, void_type, ) from tests import coord_type, point_type from tests.libdrgn import C_TOKEN, drgn_lexer_c, Lexer class TestPrettyPrintTypeName(unittest.TestCase): def assertTypeName(self, type, expected, same_as_definition=False): self.assertEqual(type.type_name(), expected) if same_as_definition: self.assertEqual(str(type), expected) def assertQualifiedTypeName(self, expected, same_as_definition, constructor, *args): self.assertEqual(constructor(*args).type_name(), expected) qualifiers = [ (Qualifiers.CONST, 'const'), (Qualifiers.VOLATILE, 'volatile'), (Qualifiers.RESTRICT, 'restrict'), (Qualifiers.ATOMIC, '_Atomic'), ] for qualifier in qualifiers: t = constructor(*args, qualifiers=qualifier[0]) self.assertTypeName(t, qualifier[1] + ' ' + expected, same_as_definition) # All qualifiers. t = constructor( *args, qualifiers=reduce(operator.or_, (qualifier[0] for qualifier in qualifiers))) self.assertTypeName(t, ' '.join(qualifier[1] for qualifier in qualifiers) + ' ' + expected, same_as_definition) def test_void(self): self.assertQualifiedTypeName('void', True, void_type) def test_int(self): self.assertQualifiedTypeName('int', True, int_type, 'int', 4, True) def test_bool(self): self.assertQualifiedTypeName('_Bool', True, bool_type, '_Bool', 1) def test_float(self): self.assertQualifiedTypeName('float', True, float_type, 'float', 4) def test_complex(self): self.assertQualifiedTypeName('double _Complex', True, complex_type, 'double _Complex', 16, float_type('double', 8)) def test_struct(self): self.assertQualifiedTypeName('struct point', True, struct_type, 'point') self.assertQualifiedTypeName('struct ', False, struct_type, None) def test_union(self): self.assertQualifiedTypeName('union option', True, union_type, 'option'), self.assertQualifiedTypeName('union ', False, union_type, None) def test_class(self): self.assertQualifiedTypeName('class coord', True, class_type, 'coord') self.assertQualifiedTypeName('class ', False, class_type, None) def test_enum(self): self.assertQualifiedTypeName('enum color', True, enum_type, 'color', None, None), self.assertQualifiedTypeName('enum ', False, enum_type, None, None, None) def test_typedef(self): self.assertQualifiedTypeName('bool', False, typedef_type, 'bool', bool_type('_Bool', 1)) def test_pointer(self): self.assertTypeName(pointer_type(8, void_type()), 'void *', True) t = pointer_type(8, void_type(Qualifiers.VOLATILE)) self.assertTypeName(t, 'volatile void *', True) t = pointer_type(8, void_type(Qualifiers.VOLATILE), Qualifiers.CONST) self.assertTypeName(t, 'volatile void * const', True) t = pointer_type(8, t) self.assertTypeName(t, 'volatile void * const *', True) def test_array(self): i = int_type('int', 4, True) self.assertTypeName(array_type(None, i), 'int []', True) self.assertTypeName(array_type(2, i), 'int [2]', True) self.assertTypeName(array_type(2, array_type(3, i)), 'int [2][3]', True) self.assertTypeName(array_type(2, array_type(3, array_type(4, i))), 'int [2][3][4]', True) def test_array_of_pointers(self): self.assertTypeName(array_type(2, array_type(3, pointer_type(8, int_type('int', 4, True)))), 'int *[2][3]', True) def test_pointer_to_array(self): self.assertTypeName(pointer_type(8, array_type(2, int_type('int', 4, True))), 'int (*)[2]', True) def test_pointer_to_pointer_to_array(self): self.assertTypeName(pointer_type(8, pointer_type(8, array_type(2, int_type('int', 4, True)))), 'int (**)[2]', True) def test_pointer_to_array_of_pointers(self): self.assertTypeName(pointer_type(8, array_type(2, pointer_type(8, int_type('int', 4, True)))), 'int *(*)[2]', True) def test_array_of_pointers_to_array(self): self.assertTypeName(array_type(2, pointer_type(8, array_type(3, int_type('int', 4, True)))), 'int (*[2])[3]', True) def test_pointer_to_function(self): i = int_type('int', 4, True) self.assertTypeName(pointer_type(8, function_type(i, ((i,),), False)), 'int (*)(int)', True) self.assertTypeName(pointer_type(8, function_type(i, ((i, 'x'),), False)), 'int (*)(int x)', True) self.assertTypeName(pointer_type(8, function_type(i, ((i,), (float_type('float', 4),)), False)), 'int (*)(int, float)', True) def test_pointer_to_function_returning_pointer(self): i = int_type('int', 4, True) self.assertTypeName(pointer_type(8, function_type(pointer_type(8, i), ((i,),), False)), 'int *(*)(int)', True) self.assertTypeName(pointer_type(8, function_type(pointer_type(8, i), ((pointer_type(8, i),),), False)), 'int *(*)(int *)', True) def test_pointer_to_function_returning_pointer_to_const(self): i = int_type('int', 4, True) self.assertTypeName(pointer_type(8, function_type(pointer_type(8, int_type('int', 4, True, Qualifiers.CONST)), ((i,),), False)), 'const int *(*)(int)', True) def test_pointer_to_function_returning_const_pointer(self): i = int_type('int', 4, True) self.assertTypeName(pointer_type(8, function_type(pointer_type(8, i, Qualifiers.CONST), ((i,),), False)), 'int * const (*)(int)', True) def test_const_pointer_to_function_returning_pointer(self): i = int_type('int', 4, True) self.assertTypeName(pointer_type(8, function_type(pointer_type(8, i), ((i,),), False), Qualifiers.CONST), 'int *(* const)(int)', True) def test_array_of_pointers_to_functions(self): i = int_type('int', 4, True) self.assertTypeName(array_type(4, pointer_type(8, function_type(i, ((i,),), False))), 'int (*[4])(int)', True) def test_array_of_const_pointers_to_functions(self): i = int_type('int', 4, True) self.assertTypeName(array_type(None, pointer_type(8, function_type(i, ((i,),), False), Qualifiers.CONST)), 'int (* const [])(int)', True) def test_pointer_to_variadic_function(self): i = int_type('int', 4, True) self.assertTypeName(pointer_type(8, function_type(i, ((i,),), True)), 'int (*)(int, ...)', True) def test_pointer_to_function_with_no_parameters(self): self.assertTypeName(pointer_type(8, function_type(int_type('int', 4, True), (), False)), 'int (*)(void)', True) def test_pointer_to_function_with_no_parameter_specification(self): self.assertTypeName(pointer_type(8, function_type(int_type('int', 4, True), (), True)), 'int (*)()', True) def test_function(self): self.assertTypeName(function_type(int_type('int', 4, True), (), False), 'int (void)') class TestPrettyPrintType(unittest.TestCase): def assertPrettyPrint(self, type, expected): self.assertEqual(str(type), expected) def test_struct(self): self.assertPrettyPrint(point_type, """\ struct point { int x; int y; }""") line_segment = struct_type('line_segment', 16, ( (point_type, 'a', 0), (point_type, 'b', 8), )) self.assertPrettyPrint(line_segment, """\ struct line_segment { struct point a; struct point b; }""") anonymous_point = struct_type(None, 8, ( (int_type('int', 4, True), 'x', 0), (int_type('int', 4, True), 'y', 4), )) self.assertPrettyPrint(anonymous_point, """\ struct { int x; int y; }""") # Member with anonymous struct type. line_segment = struct_type('line_segment', 16, ( (anonymous_point, 'a', 0), (anonymous_point, 'b', 8), )) self.assertPrettyPrint(line_segment, """\ struct line_segment { struct { int x; int y; } a; struct { int x; int y; } b; }""") # Unnamed member. point3 = struct_type('point3', 0, ( (anonymous_point, None, 0), (int_type('int', 4, True), 'z', 8), )) self.assertPrettyPrint(point3, """\ struct point3 { struct { int x; int y; }; int z; }""") def test_bit_field(self): point = struct_type('point', 4, ( (int_type('int', 4, True), 'x', 0, 4), (int_type('int', 4, True), 'y', 4, 8), )) self.assertPrettyPrint(point, """\ struct point { int x : 4; int y : 8; }""") def test_union(self): t = union_type('foo', 4, ( (int_type('int', 4, True), 'i'), (array_type(4, int_type('unsigned char', 1, False)), 'a'), )) self.assertPrettyPrint(t, """\ union foo { int i; unsigned char a[4]; }""") t = union_type('foo', 4, ( (int_type('int', 4, True), 'i'), (array_type(4, int_type('unsigned char', 1, False)), 'a'), ), Qualifiers.CONST) self.assertPrettyPrint(t, """\ const union foo { int i; unsigned char a[4]; }""") def test_class(self): self.assertPrettyPrint(coord_type, """\ class coord { int x; int y; int z; }""") def test_enum(self): t = enum_type('color', int_type('unsigned int', 4, False), ( ('RED', 0), ('GREEN', 1), ('BLUE', 2), )) self.assertPrettyPrint(t, """\ enum color { RED = 0, GREEN = 1, BLUE = 2, }""") t = enum_type('color', int_type('unsigned int', 4, False), ( ('RED', 0), ('GREEN', 1), ('BLUE', 2), ), Qualifiers.CONST) self.assertPrettyPrint(t, """\ const enum color { RED = 0, GREEN = 1, BLUE = 2, }""") t = enum_type(None, int_type('int', 4, True), ( ('RED', 0), ('GREEN', -1), ('BLUE', -2), )) self.assertPrettyPrint(t, """\ enum { RED = 0, GREEN = -1, BLUE = -2, }""") def test_typedef(self): self.assertPrettyPrint(typedef_type('INT', int_type('int', 4, True)), 'typedef int INT') self.assertPrettyPrint(typedef_type('CINT', int_type('int', 4, True, Qualifiers.CONST)), 'typedef const int CINT') self.assertPrettyPrint(typedef_type('INT', int_type('int', 4, True), Qualifiers.CONST), 'const typedef int INT') self.assertPrettyPrint(typedef_type('string', pointer_type(8, int_type('char', 1, True))), 'typedef char *string') t = typedef_type('Point', struct_type(None, 8, ( (int_type('int', 4, True), 'x', 0), (int_type('int', 4, True), 'y', 4), ))) self.assertPrettyPrint(t, """\ typedef struct { int x; int y; } Point""") def test_function_typedef(self): self.assertPrettyPrint(typedef_type('fn', function_type(int_type('int', 4, True), (), False)), 'typedef int fn(void)') def test_function_no_name(self): self.assertRaisesRegex(ValueError, 'function must have name', str, struct_type('foo', 8, ( (function_type(int_type('int', 4, True), (), False), None), ))) class TestLexer(unittest.TestCase): def lex(self, s): lexer = Lexer(drgn_lexer_c, s) while True: token = lexer.pop() if token.kind == C_TOKEN.EOF: break yield token def test_empty(self): lexer = Lexer(drgn_lexer_c, '') for i in range(64): self.assertEqual(lexer.pop().kind, C_TOKEN.EOF) def test_symbols(self): s = '()[]*.' tokens = [ C_TOKEN.LPAREN, C_TOKEN.RPAREN, C_TOKEN.LBRACKET, C_TOKEN.RBRACKET, C_TOKEN.ASTERISK, C_TOKEN.DOT, ] self.assertEqual([token.kind for token in self.lex(s)], tokens) def test_keywords(self): s = """void char short int long signed unsigned _Bool float double _Complex const restrict volatile _Atomic struct union enum""" tokens = [ C_TOKEN.VOID, C_TOKEN.CHAR, C_TOKEN.SHORT, C_TOKEN.INT, C_TOKEN.LONG, C_TOKEN.SIGNED, C_TOKEN.UNSIGNED, C_TOKEN.BOOL, C_TOKEN.FLOAT, C_TOKEN.DOUBLE, C_TOKEN.COMPLEX, C_TOKEN.CONST, C_TOKEN.RESTRICT, C_TOKEN.VOLATILE, C_TOKEN.ATOMIC, C_TOKEN.STRUCT, C_TOKEN.UNION, C_TOKEN.ENUM, ] self.assertEqual([token.kind for token in self.lex(s)], tokens) def test_identifiers(self): s = '_ x foo _bar baz1' tokens = s.split() self.assertEqual([(token.kind, token.value) for token in self.lex(s)], [(C_TOKEN.IDENTIFIER, value) for value in tokens]) def test_number(self): s = '0 1234 0xdeadbeef' tokens = s.split() self.assertEqual([(token.kind, token.value) for token in self.lex(s)], [(C_TOKEN.NUMBER, value) for value in tokens]) def test_invalid_number(self): for s in ['0x', '1234y']: self.assertRaisesRegex(SyntaxError, 'invalid number', list, self.lex(s)) def test_invalid_character(self): self.assertRaisesRegex(SyntaxError, 'invalid character', list, self.lex('@'))