drgn/tests/test_language_c.py
Omar Sandoval 243f6fb7d5 libdrgn: support value objects with >64-bit integer types
The Linux kernel's struct task_struct on AArch64 contains an array of
__uint128_t:

  >>> task = find_task(prog, 1)
  >>> task.type_
  struct task_struct *
  >>> task.thread.type_
  struct thread_struct {
          struct cpu_context cpu_context;
          struct {
                  unsigned long tp_value;
                  unsigned long tp2_value;
                  struct user_fpsimd_state fpsimd_state;
          } uw;
          enum fp_type fp_type;
          unsigned int fpsimd_cpu;
          void *sve_state;
          void *sme_state;
          unsigned int vl[2];
          unsigned int vl_onexec[2];
          unsigned long fault_address;
          unsigned long fault_code;
          struct debug_info debug;
          struct ptrauth_keys_user keys_user;
          struct ptrauth_keys_kernel keys_kernel;
          u64 mte_ctrl;
          u64 sctlr_user;
          u64 svcr;
          u64 tpidr2_el0;
  }
  >>> task.thread.uw.fpsimd_state.type_
  struct user_fpsimd_state {
          __int128 unsigned vregs[32];
          __u32 fpsr;
          __u32 fpcr;
          __u32 __reserved[2];
  }

As a result, printing a task_struct fails:

  >>> task
  Traceback (most recent call last):
    File "<console>", line 1, in <module>
    File "/host/home/osandov/repos/drgn3/drgn/cli.py", line 140, in _displayhook
      text = value.format_(columns=shutil.get_terminal_size((0, 0)).columns)
  NotImplementedError: integer values larger than 64 bits are not yet supported

PR #311 suggested treating >64-bit integers as byte arrays for now; I
tried an alternate hack of handling >64-bit integers only in the
pretty-printing code. Both of these had issues, though.

Instead, let's push >64-bit integer support a little further and allow
storing "big integer" value objects. We still don't support any
operations on them, so this still doesn't complete #170. We store the
raw bytes of the value for now, but we'll probably change this if we add
support for operations (e.g., to store the value as an mp_limb_t array
for GMP). We also print >64-bit integer types in hexadecimal for
simplicity. This is inconsistent with the existing behavior of printing
in decimal, but more readable. In the future, we might want to add
heuristics to decide when to print in decimal vs hexadecimal for all
sizes.

Closes #311.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
2023-08-02 14:21:46 -07:00

2532 lines
80 KiB
Python

# Copyright (c) Meta Platforms, Inc. and affiliates.
# SPDX-License-Identifier: LGPL-2.1-or-later
from functools import reduce
from itertools import chain
import operator
from drgn import (
Object,
Qualifiers,
Type,
TypeEnumerator,
TypeMember,
TypeParameter,
cast,
container_of,
)
from tests import MockProgramTestCase, TestCase
from tests.libdrgn import C_TOKEN, Lexer, drgn_c_family_lexer_func
class TestPrettyPrintTypeName(MockProgramTestCase):
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, self.prog.void_type)
def test_int(self):
self.assertQualifiedTypeName("int", True, self.prog.int_type, "int", 4, True)
def test_bool(self):
self.assertQualifiedTypeName("_Bool", True, self.prog.bool_type, "_Bool", 1)
def test_float(self):
self.assertQualifiedTypeName("float", True, self.prog.float_type, "float", 4)
def test_struct(self):
self.assertQualifiedTypeName(
"struct point", True, self.prog.struct_type, "point"
)
self.assertQualifiedTypeName(
"struct <anonymous>", False, self.prog.struct_type, None
)
def test_union(self):
self.assertQualifiedTypeName(
"union option", True, self.prog.union_type, "option"
),
self.assertQualifiedTypeName(
"union <anonymous>", False, self.prog.union_type, None
)
def test_class(self):
self.assertQualifiedTypeName("class coord", True, self.prog.class_type, "coord")
self.assertQualifiedTypeName(
"class <anonymous>", False, self.prog.class_type, None
)
def test_enum(self):
self.assertQualifiedTypeName(
"enum color", True, self.prog.enum_type, "color", None, None
),
self.assertQualifiedTypeName(
"enum <anonymous>", False, self.prog.enum_type, None, None, None
)
def test_typedef(self):
self.assertQualifiedTypeName(
"bool",
False,
self.prog.typedef_type,
"bool",
self.prog.bool_type("_Bool", 1),
)
def test_pointer(self):
self.assertTypeName(
self.prog.pointer_type(self.prog.void_type()), "void *", True
)
t = self.prog.pointer_type(self.prog.void_type(qualifiers=Qualifiers.VOLATILE))
self.assertTypeName(t, "volatile void *", True)
t = self.prog.pointer_type(
self.prog.void_type(qualifiers=Qualifiers.VOLATILE),
qualifiers=Qualifiers.CONST,
)
self.assertTypeName(t, "volatile void * const", True)
t = self.prog.pointer_type(t)
self.assertTypeName(t, "volatile void * const *", True)
def test_array(self):
i = self.prog.int_type("int", 4, True)
self.assertTypeName(self.prog.array_type(i), "int []", True)
self.assertTypeName(self.prog.array_type(i, 2), "int [2]", True)
self.assertTypeName(
self.prog.array_type(self.prog.array_type(i, 3), 2), "int [2][3]", True
)
self.assertTypeName(
self.prog.array_type(
self.prog.array_type(self.prog.array_type(i, 4), 3), 2
),
"int [2][3][4]",
True,
)
def test_array_of_pointers(self):
self.assertTypeName(
self.prog.array_type(
self.prog.array_type(
self.prog.pointer_type(self.prog.int_type("int", 4, True)), 3
),
2,
),
"int *[2][3]",
True,
)
def test_pointer_to_array(self):
self.assertTypeName(
self.prog.pointer_type(
self.prog.array_type(self.prog.int_type("int", 4, True), 2)
),
"int (*)[2]",
True,
)
def test_pointer_to_pointer_to_array(self):
self.assertTypeName(
self.prog.pointer_type(
self.prog.pointer_type(
self.prog.array_type(self.prog.int_type("int", 4, True), 2)
)
),
"int (**)[2]",
True,
)
def test_pointer_to_array_of_pointers(self):
self.assertTypeName(
self.prog.pointer_type(
self.prog.array_type(
self.prog.pointer_type(self.prog.int_type("int", 4, True)), 2
)
),
"int *(*)[2]",
True,
)
def test_array_of_pointers_to_array(self):
self.assertTypeName(
self.prog.array_type(
self.prog.pointer_type(
self.prog.array_type(self.prog.int_type("int", 4, True), 3)
),
2,
),
"int (*[2])[3]",
True,
)
def test_pointer_to_function(self):
i = self.prog.int_type("int", 4, True)
self.assertTypeName(
self.prog.pointer_type(
self.prog.function_type(i, (TypeParameter(i),), False)
),
"int (*)(int)",
True,
)
self.assertTypeName(
self.prog.pointer_type(
self.prog.function_type(i, (TypeParameter(i, "x"),), False)
),
"int (*)(int x)",
True,
)
self.assertTypeName(
self.prog.pointer_type(
self.prog.function_type(
i,
(
TypeParameter(i),
TypeParameter(self.prog.float_type("float", 4)),
),
False,
),
),
"int (*)(int, float)",
True,
)
def test_pointer_to_function_returning_pointer(self):
i = self.prog.int_type("int", 4, True)
self.assertTypeName(
self.prog.pointer_type(
self.prog.function_type(
self.prog.pointer_type(i), (TypeParameter(i),), False
)
),
"int *(*)(int)",
True,
)
self.assertTypeName(
self.prog.pointer_type(
self.prog.function_type(
self.prog.pointer_type(i),
(TypeParameter(self.prog.pointer_type(i)),),
False,
),
),
"int *(*)(int *)",
True,
)
def test_pointer_to_function_returning_pointer_to_const(self):
i = self.prog.int_type("int", 4, True)
self.assertTypeName(
self.prog.pointer_type(
self.prog.function_type(
self.prog.pointer_type(
self.prog.int_type("int", 4, True, qualifiers=Qualifiers.CONST)
),
(TypeParameter(i),),
False,
),
),
"const int *(*)(int)",
True,
)
def test_pointer_to_function_returning_const_pointer(self):
i = self.prog.int_type("int", 4, True)
self.assertTypeName(
self.prog.pointer_type(
self.prog.function_type(
self.prog.pointer_type(i, qualifiers=Qualifiers.CONST),
(TypeParameter(i),),
False,
),
),
"int * const (*)(int)",
True,
)
def test_const_pointer_to_function_returning_pointer(self):
i = self.prog.int_type("int", 4, True)
self.assertTypeName(
self.prog.pointer_type(
self.prog.function_type(
self.prog.pointer_type(i), (TypeParameter(i),), False
),
qualifiers=Qualifiers.CONST,
),
"int *(* const)(int)",
True,
)
def test_array_of_pointers_to_functions(self):
i = self.prog.int_type("int", 4, True)
self.assertTypeName(
self.prog.array_type(
self.prog.pointer_type(
self.prog.function_type(i, (TypeParameter(i),), False)
),
4,
),
"int (*[4])(int)",
True,
)
def test_array_of_const_pointers_to_functions(self):
i = self.prog.int_type("int", 4, True)
self.assertTypeName(
self.prog.array_type(
self.prog.pointer_type(
self.prog.function_type(i, (TypeParameter(i),), False),
qualifiers=Qualifiers.CONST,
),
),
"int (* const [])(int)",
True,
)
def test_pointer_to_variadic_function(self):
i = self.prog.int_type("int", 4, True)
self.assertTypeName(
self.prog.pointer_type(
self.prog.function_type(i, (TypeParameter(i),), True)
),
"int (*)(int, ...)",
True,
)
def test_pointer_to_function_with_no_parameters(self):
self.assertTypeName(
self.prog.pointer_type(
self.prog.function_type(self.prog.int_type("int", 4, True), (), False)
),
"int (*)(void)",
True,
)
def test_pointer_to_function_with_no_parameter_specification(self):
self.assertTypeName(
self.prog.pointer_type(
self.prog.function_type(self.prog.int_type("int", 4, True), (), True)
),
"int (*)()",
True,
)
def test_function(self):
self.assertTypeName(
self.prog.function_type(self.prog.int_type("int", 4, True), (), False),
"int (void)",
)
def test_pointer_to_anonymous_struct(self):
self.assertTypeName(
self.prog.pointer_type(
self.prog.struct_type(
None, 8, (TypeMember(self.prog.int_type("int", 4, True), "x", 0),)
)
),
"struct <anonymous> *",
)
def test_array_of_anonymous_struct(self):
self.assertTypeName(
self.prog.array_type(
self.prog.struct_type(
None, 8, (TypeMember(self.prog.int_type("int", 4, True), "x", 0),)
),
2,
),
"struct <anonymous> [2]",
)
def test_function_returning_anonymous_struct(self):
self.assertTypeName(
self.prog.function_type(
self.prog.struct_type(
None, 8, (TypeMember(self.prog.int_type("int", 4, True), "x", 0),)
),
(),
),
"struct <anonymous> (void)",
)
def test_function_of_anonymous_struct(self):
self.assertTypeName(
self.prog.function_type(
self.prog.int_type("int", 4, True),
(
TypeParameter(
self.prog.struct_type(
None,
8,
(TypeMember(self.prog.int_type("int", 4, True), "x", 0),),
),
"x",
),
),
),
"int (struct <anonymous> x)",
)
def test_typedef_of_anonymous_struct(self):
self.assertTypeName(
self.prog.typedef_type(
"onymous",
self.prog.struct_type(
None, 8, (TypeMember(self.prog.int_type("int", 4, True), "x", 0),)
),
),
"onymous",
)
class TestPrettyPrintType(MockProgramTestCase):
def assertPrettyPrint(self, type, expected):
self.assertEqual(str(type), expected)
def test_struct(self):
self.assertPrettyPrint(
self.point_type,
"""\
struct point {
int x;
int y;
}""",
)
def test_struct_member(self):
self.assertPrettyPrint(
self.line_segment_type,
"""\
struct line_segment {
struct point a;
struct point b;
}""",
)
def test_anonymous_struct(self):
self.assertPrettyPrint(
self.prog.struct_type(
None,
8,
(
TypeMember(self.prog.int_type("int", 4, True), "x", 0),
TypeMember(self.prog.int_type("int", 4, True), "y", 32),
),
),
"""\
struct {
int x;
int y;
}""",
)
def test_anonymous_struct_member(self):
# Member with anonymous struct type.
anonymous_struct = self.prog.struct_type(
None,
8,
(
TypeMember(self.prog.int_type("int", 4, True), "x", 0),
TypeMember(self.prog.int_type("int", 4, True), "y", 32),
),
)
self.assertPrettyPrint(
self.prog.struct_type(
"line_segment",
16,
(
TypeMember(anonymous_struct, "a", 0),
TypeMember(anonymous_struct, "b", 64),
),
),
"""\
struct line_segment {
struct {
int x;
int y;
} a;
struct {
int x;
int y;
} b;
}""",
)
def test_struct_unnamed_member(self):
self.assertPrettyPrint(
self.prog.struct_type(
"point3",
0,
(
TypeMember(
self.prog.struct_type(
None,
8,
(
TypeMember(self.prog.int_type("int", 4, True), "x"),
TypeMember(self.prog.int_type("int", 4, True), "y", 32),
),
)
),
TypeMember(self.prog.int_type("int", 4, True), "z", 64),
),
),
"""\
struct point3 {
struct {
int x;
int y;
};
int z;
}""",
)
def test_bit_field(self):
self.assertPrettyPrint(
self.prog.struct_type(
"point",
4,
(
TypeMember(
Object(
self.prog,
self.prog.int_type("int", 4, True),
bit_field_size=4,
),
"x",
0,
),
TypeMember(
Object(
self.prog,
self.prog.int_type("int", 4, True),
bit_field_size=8,
),
"y",
4,
),
),
),
"""\
struct point {
int x : 4;
int y : 8;
}""",
)
def test_union(self):
self.assertPrettyPrint(
self.prog.union_type(
"foo",
4,
(
TypeMember(self.prog.int_type("int", 4, True), "i"),
TypeMember(
self.prog.array_type(
self.prog.int_type("unsigned char", 1, False), 4
),
"a",
),
),
),
"""\
union foo {
int i;
unsigned char a[4];
}""",
)
def test_union_qualified(self):
self.assertPrettyPrint(
self.prog.union_type(
"foo",
4,
(
TypeMember(self.prog.int_type("int", 4, True), "i"),
TypeMember(
self.prog.array_type(
self.prog.int_type("unsigned char", 1, False), 4
),
"a",
),
),
qualifiers=Qualifiers.CONST,
),
"""\
const union foo {
int i;
unsigned char a[4];
}""",
)
def test_class(self):
self.assertPrettyPrint(
self.coord_type,
"""\
class coord {
int x;
int y;
int z;
}""",
)
def test_enum(self):
self.assertPrettyPrint(
self.color_type,
"""\
enum color {
RED = 0,
GREEN = 1,
BLUE = 2,
}""",
)
def test_enum_qualified(self):
self.assertPrettyPrint(
self.color_type.qualified(Qualifiers.CONST),
"""\
const enum color {
RED = 0,
GREEN = 1,
BLUE = 2,
}""",
)
def test_enum_anonymous(self):
self.assertPrettyPrint(
self.prog.enum_type(
None,
self.prog.int_type("int", 4, True),
(
TypeEnumerator("RED", 0),
TypeEnumerator("GREEN", -1),
TypeEnumerator("BLUE", -2),
),
),
"""\
enum {
RED = 0,
GREEN = -1,
BLUE = -2,
}""",
)
def test_typedef(self):
self.assertPrettyPrint(
self.prog.typedef_type("INT", self.prog.int_type("int", 4, True)),
"typedef int INT",
)
def test_typedef_const(self):
self.assertPrettyPrint(
self.prog.typedef_type(
"CINT", self.prog.int_type("int", 4, True, qualifiers=Qualifiers.CONST)
),
"typedef const int CINT",
)
def test_const_typedef(self):
self.assertPrettyPrint(
self.prog.typedef_type(
"INT", self.prog.int_type("int", 4, True), qualifiers=Qualifiers.CONST
),
"const typedef int INT",
)
def test_typedef_pointer(self):
self.assertPrettyPrint(
self.prog.typedef_type(
"string", self.prog.pointer_type(self.prog.int_type("char", 1, True))
),
"typedef char *string",
)
def test_typedef_struct(self):
self.assertPrettyPrint(
self.prog.typedef_type(
"Point",
self.prog.struct_type(
None,
8,
(
TypeMember(self.prog.int_type("int", 4, True), "x", 0),
TypeMember(self.prog.int_type("int", 4, True), "y", 32),
),
),
),
"""\
typedef struct {
int x;
int y;
} Point""",
)
def test_typedef_function(self):
self.assertPrettyPrint(
self.prog.typedef_type(
"fn",
self.prog.function_type(self.prog.int_type("int", 4, True), (), False),
),
"typedef int fn(void)",
)
def test_function_no_name(self):
self.assertRaisesRegex(
ValueError,
"function must have name",
str,
self.prog.struct_type(
"foo",
8,
(
TypeMember(
self.prog.function_type(
self.prog.int_type("int", 4, True), (), False
)
),
),
),
)
class TestLexer(TestCase):
def lex(self, s, cpp=False):
lexer = Lexer(drgn_c_family_lexer_func, s, cpp)
while True:
token = lexer.pop()
if token.kind == C_TOKEN.EOF:
break
yield token
def test_empty(self):
lexer = Lexer(drgn_c_family_lexer_func, "")
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 class 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.IDENTIFIER,
C_TOKEN.ENUM,
]
self.assertEqual([token.kind for token in self.lex(s)], tokens)
def test_cpp_keywords(self):
s = """void char short int long signed unsigned _Bool float double
_Complex const restrict volatile _Atomic struct union class 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.CLASS,
C_TOKEN.ENUM,
]
self.assertEqual([token.kind for token in self.lex(s, cpp=True)], tokens)
def test_cpp_identifiers_with_template_parameters(self):
token_pairs = (
("vector", "<int>"),
("pair", "<int, double>"),
("unordered_map", "<std::string, std::vector<pair<short, bool>>>"),
("IntLiteral", "<123>"),
("IntLiteralWithSuffix", "<123UL>"),
("FloatLiteral", "<1.987>"),
("FloatLiteralWithExponent", "<1.23423e+1f>"),
("PointerLiteral", "<&asdf>"),
("ParenthesizedPointerLiteral", "<(&asdf)>"),
("CharLiteral", "<'a'>"),
("CharLiteralEdgeCase1", "<'<'>"),
("CharLiteralEdgeCase2", "<'>'>"),
("CharLiteralEdgeCase3", r"<'\''>"),
)
self.assertEqual(
[
(token.kind, token.value)
for token in self.lex(
" ".join("".join(pair) for pair in token_pairs), cpp=True
)
],
[
(C_TOKEN.TEMPLATE_ARGUMENTS if i % 2 else C_TOKEN.IDENTIFIER, value)
for i, value in enumerate(chain.from_iterable(token_pairs))
],
)
def test_cpp_identifiers_with_invalid_template_parameters(self):
for s in [
"vector<int",
"pair<<int, double>",
"unordered_map<string, vector<pair<short, bool>>",
]:
self.assertRaisesRegex(
SyntaxError,
"invalid template arguments",
list,
self.lex(s, cpp=True),
)
for s in [
"vectorint>",
"pair<int, double>>",
]:
self.assertRaisesRegex(
SyntaxError,
"invalid character",
list,
self.lex(s, cpp=True),
)
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_almost_keywords(self):
s = """voi cha shor in lon signe unsigne _Boo floa doubl
_Comple cons restric volatil _Atomi struc unio enu"""
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("@"))
class TestLiteral(MockProgramTestCase):
def test_int(self):
self.assertIdentical(
Object(self.prog, value=1), Object(self.prog, "int", value=1)
)
self.assertIdentical(
Object(self.prog, value=-1), Object(self.prog, "int", value=-1)
)
self.assertIdentical(
Object(self.prog, value=2**31 - 1),
Object(self.prog, "int", value=2**31 - 1),
)
self.assertIdentical(
Object(self.prog, value=2**31), Object(self.prog, "long", value=2**31)
)
# Not int, because this is treated as the negation operator applied to
# 2**31.
self.assertIdentical(
Object(self.prog, value=-(2**31)),
Object(self.prog, "long", value=-(2**31)),
)
self.assertIdentical(
Object(self.prog, value=2**63),
Object(self.prog, "unsigned long long", value=2**63),
)
self.assertIdentical(
Object(self.prog, value=2**64 - 1),
Object(self.prog, "unsigned long long", value=2**64 - 1),
)
self.assertIdentical(
Object(self.prog, value=-(2**64 - 1)),
Object(self.prog, "unsigned long long", value=1),
)
def test_bool(self):
self.assertIdentical(
Object(self.prog, value=True), Object(self.prog, "int", value=1)
)
self.assertIdentical(
Object(self.prog, value=False), Object(self.prog, "int", value=0)
)
def test_float(self):
self.assertIdentical(
Object(self.prog, value=3.14), Object(self.prog, "double", value=3.14)
)
def test_invalid(self):
class Foo:
pass
self.assertRaisesRegex(
TypeError, "cannot create Foo literal", Object, self.prog, value=Foo()
)
class TestIntegerPromotion(MockProgramTestCase):
def test_conversion_rank_less_than_int(self):
self.assertIdentical(+self.bool(False), self.int(0))
self.assertIdentical(
+Object(self.prog, "char", value=1), Object(self.prog, "int", value=1)
)
self.assertIdentical(
+Object(self.prog, "signed char", value=2),
Object(self.prog, "int", value=2),
)
self.assertIdentical(
+Object(self.prog, "unsigned char", value=3),
Object(self.prog, "int", value=3),
)
self.assertIdentical(
+Object(self.prog, "short", value=1), Object(self.prog, "int", value=1)
)
self.assertIdentical(
+Object(self.prog, "unsigned short", value=2),
Object(self.prog, "int", value=2),
)
# If short is the same size as int, then int can't represent all of the
# values of unsigned short.
self.assertIdentical(
+Object(self.prog, self.prog.int_type("short", 4, True), value=1),
Object(self.prog, "int", value=1),
)
self.assertIdentical(
+Object(self.prog, self.prog.int_type("unsigned short", 4, False), value=2),
Object(self.prog, "unsigned int", value=2),
)
def test_int(self):
self.assertIdentical(
+Object(self.prog, "int", value=-1), Object(self.prog, "int", value=-1)
)
self.assertIdentical(
+Object(self.prog, "unsigned int", value=-1),
Object(self.prog, "unsigned int", value=-1),
)
def test_conversion_rank_greater_than_int(self):
self.assertIdentical(
+Object(self.prog, "long", value=-1), Object(self.prog, "long", value=-1)
)
self.assertIdentical(
+Object(self.prog, "unsigned long", value=-1),
Object(self.prog, "unsigned long", value=-1),
)
self.assertIdentical(
+Object(self.prog, "long long", value=-1),
Object(self.prog, "long long", value=-1),
)
self.assertIdentical(
+Object(self.prog, "unsigned long long", value=-1),
Object(self.prog, "unsigned long long", value=-1),
)
def test_extended_integer(self):
self.assertIdentical(
+Object(self.prog, self.prog.int_type("byte", 1, True), value=1),
Object(self.prog, "int", value=1),
)
self.assertIdentical(
+Object(self.prog, self.prog.int_type("ubyte", 1, False), value=-1),
Object(self.prog, "int", value=0xFF),
)
self.assertIdentical(
+Object(self.prog, self.prog.int_type("qword", 8, True), value=1),
Object(self.prog, self.prog.int_type("qword", 8, True), value=1),
)
self.assertIdentical(
+Object(self.prog, self.prog.int_type("qword", 8, False), value=1),
Object(self.prog, self.prog.int_type("qword", 8, False), value=1),
)
def test_bit_field(self):
# Bit fields which can be represented by int or unsigned int should be
# promoted.
self.assertIdentical(
+Object(self.prog, "int", value=1, bit_field_size=4),
Object(self.prog, "int", value=1),
)
self.assertIdentical(
+Object(self.prog, "long", value=1, bit_field_size=4),
Object(self.prog, "int", value=1),
)
self.assertIdentical(
+Object(self.prog, "int", value=1, bit_field_size=32),
Object(self.prog, "int", value=1),
)
self.assertIdentical(
+Object(self.prog, "long", value=1, bit_field_size=32),
Object(self.prog, "int", value=1),
)
self.assertIdentical(
+Object(self.prog, "unsigned int", value=1, bit_field_size=4),
Object(self.prog, "int", value=1),
)
self.assertIdentical(
+Object(self.prog, "unsigned long", value=1, bit_field_size=4),
Object(self.prog, "int", value=1),
)
self.assertIdentical(
+Object(self.prog, "unsigned int", value=1, bit_field_size=32),
Object(self.prog, "unsigned int", value=1),
)
self.assertIdentical(
+Object(self.prog, "unsigned long", value=1, bit_field_size=32),
Object(self.prog, "unsigned int", value=1),
)
# Bit fields which cannot be represented by int or unsigned int should
# be preserved.
self.assertIdentical(
+Object(self.prog, "long", value=1, bit_field_size=40),
Object(self.prog, "long", value=1, bit_field_size=40),
)
self.assertIdentical(
+Object(self.prog, "unsigned long", value=1, bit_field_size=40),
Object(self.prog, "unsigned long", value=1, bit_field_size=40),
)
def test_enum(self):
# Enums should be converted to their compatible type and then promoted.
self.assertIdentical(
+Object(self.prog, self.color_type, value=1),
Object(self.prog, "unsigned int", value=1),
)
type_ = self.prog.enum_type(
"color",
self.prog.type("unsigned long long"),
(
TypeEnumerator("RED", 0),
TypeEnumerator("GREEN", 1),
TypeEnumerator("BLUE", 2),
),
)
self.assertIdentical(
+Object(self.prog, type_, value=1),
Object(self.prog, "unsigned long long", value=1),
)
type_ = self.prog.enum_type(
"color",
self.prog.type("char"),
(
TypeEnumerator("RED", 0),
TypeEnumerator("GREEN", 1),
TypeEnumerator("BLUE", 2),
),
)
self.assertIdentical(
+Object(self.prog, type_, value=1), Object(self.prog, "int", value=1)
)
def test_typedef(self):
type_ = self.prog.typedef_type("SHORT", self.prog.type("short"))
self.assertIdentical(
+Object(self.prog, type_, value=5), Object(self.prog, "int", value=5)
)
# Typedef should be preserved if the type wasn't promoted.
type_ = self.prog.typedef_type("CINT", self.prog.type("int"))
self.assertIdentical(
+Object(self.prog, type_, value=5), Object(self.prog, type_, value=5)
)
def test_non_integer(self):
# Non-integer types should not be affected.
self.assertIdentical(
+Object(self.prog, "double", value=3.14),
Object(self.prog, "double", value=3.14),
)
def test_byte_order(self):
# Types in the opposite byte order should converted to the program's
# byte order.
self.assertIdentical(
+Object(self.prog, self.prog.int_type("int", 4, True, "big"), value=5),
Object(self.prog, self.prog.int_type("int", 4, True, "little"), value=5),
)
def test_byte_order_typedef(self):
self.assertIdentical(
+Object(
self.prog,
self.prog.typedef_type(
"CINT", self.prog.int_type("int", 4, True, "big")
),
value=5,
),
Object(
self.prog,
self.prog.typedef_type(
"CINT", self.prog.int_type("int", 4, True, "little")
),
value=5,
),
)
def test_byte_order_enum(self):
self.assertIdentical(
+Object(
self.prog,
self.prog.enum_type(
"ENUM", self.prog.int_type("int", 4, True, "big"), ()
),
value=5,
),
Object(self.prog, self.prog.int_type("int", 4, True, "little"), value=5),
)
class TestCommonRealType(MockProgramTestCase):
def assertCommonRealType(self, lhs, rhs, expected, commutative=True):
if isinstance(lhs, (str, Type)):
obj1 = Object(self.prog, lhs, value=1)
else:
obj1 = Object(self.prog, lhs[0], value=1, bit_field_size=lhs[1])
if isinstance(rhs, (str, Type)):
obj2 = Object(self.prog, rhs, value=1)
else:
obj2 = Object(self.prog, rhs[0], value=1, bit_field_size=rhs[1])
if isinstance(expected, (str, Type)):
expected_obj = Object(self.prog, expected, value=1)
else:
expected_obj = Object(
self.prog, expected[0], value=1, bit_field_size=expected[1]
)
self.assertIdentical(obj1 * obj2, expected_obj)
if commutative:
self.assertIdentical(obj2 * obj1, expected_obj)
def test_float(self):
self.assertCommonRealType("float", "long long", "float")
self.assertCommonRealType("float", "float", "float")
self.assertCommonRealType("double", "long long", "double")
self.assertCommonRealType("double", "float", "double")
self.assertCommonRealType("double", "double", "double")
# Floating type not in the standard.
float64 = self.prog.float_type("float64", 8)
self.assertCommonRealType(float64, "long long", float64)
self.assertCommonRealType(float64, "float", float64)
self.assertCommonRealType(float64, "double", float64)
self.assertCommonRealType(float64, float64, float64)
def test_bit_field(self):
# Same width and sign.
self.assertCommonRealType(
("long long", 33), ("long long", 33), ("long long", 33)
)
self.assertCommonRealType(
("long long", 33), ("long", 33), ("long", 33), commutative=False
)
self.assertCommonRealType(
("long", 33), ("long long", 33), ("long long", 33), commutative=False
)
# Same width, different sign.
self.assertCommonRealType(
("long long", 33), ("unsigned long long", 33), ("unsigned long long", 33)
)
# Different width, same sign.
self.assertCommonRealType(
("long long", 34), ("long long", 33), ("long long", 34)
)
# Different width, different sign.
self.assertCommonRealType(
("long long", 34), ("unsigned long long", 33), ("long long", 34)
)
def test_same(self):
self.assertCommonRealType("_Bool", "_Bool", "int")
self.assertCommonRealType("int", "int", "int")
self.assertCommonRealType("long", "long", "long")
def test_same_sign(self):
self.assertCommonRealType("long", "int", "long")
self.assertCommonRealType("long long", "int", "long long")
self.assertCommonRealType("long long", "long", "long long")
self.assertCommonRealType("unsigned long", "unsigned int", "unsigned long")
self.assertCommonRealType(
"unsigned long long", "unsigned int", "unsigned long long"
)
self.assertCommonRealType(
"unsigned long long", "unsigned long", "unsigned long long"
)
int64 = self.prog.int_type("int64", 8, True)
qword = self.prog.int_type("qword", 8, True)
self.assertCommonRealType("long", int64, "long")
self.assertCommonRealType(int64, qword, qword, commutative=False)
self.assertCommonRealType(qword, int64, int64, commutative=False)
self.assertCommonRealType("int", int64, int64)
def test_unsigned_greater_rank(self):
self.assertCommonRealType("unsigned long", "int", "unsigned long")
self.assertCommonRealType("unsigned long long", "long", "unsigned long long")
self.assertCommonRealType("unsigned long long", "int", "unsigned long long")
int64 = self.prog.int_type("int64", 8, True)
uint64 = self.prog.int_type("uint64", 8, False)
self.assertCommonRealType(uint64, "int", uint64)
self.assertCommonRealType("unsigned long", int64, "unsigned long")
def test_signed_can_represent_unsigned(self):
self.assertCommonRealType("long", "unsigned int", "long")
self.assertCommonRealType("long long", "unsigned int", "long long")
int64 = self.prog.int_type("int64", 8, True)
weirduint = self.prog.int_type("weirduint", 6, False)
self.assertCommonRealType(int64, "unsigned int", int64)
self.assertCommonRealType("long", weirduint, "long")
def test_corresponding_unsigned(self):
self.assertCommonRealType("long", "unsigned long", "unsigned long")
self.assertCommonRealType("long long", "unsigned long", "unsigned long long")
def test_enum(self):
self.assertCommonRealType(self.color_type, self.color_type, "unsigned int")
def test_typedef(self):
type_ = self.prog.typedef_type("INT", self.prog.type("int"))
self.assertCommonRealType(type_, type_, type_)
self.assertCommonRealType("int", type_, type_, commutative=False)
self.assertCommonRealType(type_, "int", "int", commutative=False)
type_ = self.prog.typedef_type("LONG", self.prog.type("long"))
self.assertCommonRealType(type_, "int", type_)
class TestOperators(MockProgramTestCase):
def test_cast_array(self):
obj = Object(self.prog, "int []", address=0xFFFF0000)
self.assertIdentical(
cast("int *", obj), Object(self.prog, "int *", value=0xFFFF0000)
)
self.assertIdentical(
cast("void *", obj), Object(self.prog, "void *", value=0xFFFF0000)
)
self.assertIdentical(
cast("unsigned long", obj),
Object(self.prog, "unsigned long", value=0xFFFF0000),
)
self.assertRaisesRegex(
TypeError, r"cannot cast to 'int \[2]'", cast, "int [2]", obj
)
def test_cast_function(self):
func = Object(
self.prog,
self.prog.function_type(self.prog.void_type(), (), False),
address=0xFFFF0000,
)
self.assertIdentical(
cast("void *", func), Object(self.prog, "void *", value=0xFFFF0000)
)
def _test_arithmetic(
self, op, lhs, rhs, result, integral=True, floating_point=False
):
if integral:
self.assertIdentical(op(self.int(lhs), self.int(rhs)), self.int(result))
self.assertIdentical(op(self.int(lhs), self.long(rhs)), self.long(result))
self.assertIdentical(op(self.long(lhs), self.int(rhs)), self.long(result))
self.assertIdentical(op(self.long(lhs), self.long(rhs)), self.long(result))
self.assertIdentical(op(self.int(lhs), rhs), self.int(result))
self.assertIdentical(op(self.long(lhs), rhs), self.long(result))
self.assertIdentical(op(lhs, self.int(rhs)), self.int(result))
self.assertIdentical(op(lhs, self.long(rhs)), self.long(result))
if floating_point:
self.assertIdentical(
op(self.double(lhs), self.double(rhs)), self.double(result)
)
self.assertIdentical(
op(self.double(lhs), self.int(rhs)), self.double(result)
)
self.assertIdentical(
op(self.int(lhs), self.double(rhs)), self.double(result)
)
self.assertIdentical(op(self.double(lhs), float(rhs)), self.double(result))
self.assertIdentical(op(float(lhs), self.double(rhs)), self.double(result))
self.assertIdentical(op(float(lhs), self.int(rhs)), self.double(result))
self.assertIdentical(op(self.int(lhs), float(rhs)), self.double(result))
def _test_shift(self, op, lhs, rhs, result):
self.assertIdentical(op(self.int(lhs), self.int(rhs)), self.int(result))
self.assertIdentical(op(self.int(lhs), self.long(rhs)), self.int(result))
self.assertIdentical(op(self.long(lhs), self.int(rhs)), self.long(result))
self.assertIdentical(op(self.long(lhs), self.long(rhs)), self.long(result))
self.assertIdentical(op(self.int(lhs), rhs), self.int(result))
self.assertIdentical(op(self.long(lhs), rhs), self.long(result))
self.assertIdentical(op(lhs, self.int(rhs)), self.int(result))
self.assertIdentical(op(lhs, self.long(rhs)), self.int(result))
self._test_pointer_type_errors(op)
self._test_floating_type_errors(op)
def _test_pointer_type_errors(self, op):
def pointer(value):
return Object(self.prog, "int *", value=value)
self.assertRaisesRegex(
TypeError, "invalid operands to binary", op, self.int(1), pointer(1)
)
self.assertRaisesRegex(
TypeError, "invalid operands to binary", op, pointer(1), self.int(1)
)
self.assertRaisesRegex(
TypeError, "invalid operands to binary", op, pointer(1), pointer(1)
)
def _test_floating_type_errors(self, op):
self.assertRaises(TypeError, op, self.int(1), self.double(1))
self.assertRaises(TypeError, op, self.double(1), self.int(1))
self.assertRaises(TypeError, op, self.double(1), self.double(1))
def test_relational(self):
one = self.int(1)
two = self.int(2)
three = self.int(3)
self.assertTrue(one < two)
self.assertFalse(two < two)
self.assertFalse(three < two)
self.assertTrue(one <= two)
self.assertTrue(two <= two)
self.assertFalse(three <= two)
self.assertTrue(one == one)
self.assertFalse(one == two)
self.assertFalse(one != one)
self.assertTrue(one != two)
self.assertFalse(one > two)
self.assertFalse(two > two)
self.assertTrue(three > two)
self.assertFalse(one >= two)
self.assertTrue(two >= two)
self.assertTrue(three >= two)
# The usual arithmetic conversions convert -1 to an unsigned int.
self.assertFalse(self.int(-1) < self.unsigned_int(0))
self.assertTrue(self.int(1) == self.bool(1))
def test_ptr_relational(self):
ptr0 = Object(self.prog, "int *", value=0xFFFF0000)
ptr1 = Object(self.prog, "int *", value=0xFFFF0004)
fptr1 = Object(self.prog, "float *", value=0xFFFF0004)
self.assertTrue(ptr0 < ptr1)
self.assertTrue(ptr0 < fptr1)
self.assertFalse(ptr1 < fptr1)
self.assertTrue(ptr0 <= ptr1)
self.assertTrue(ptr0 <= fptr1)
self.assertTrue(ptr1 <= fptr1)
self.assertFalse(ptr0 == ptr1)
self.assertFalse(ptr0 == fptr1)
self.assertTrue(ptr1 == fptr1)
self.assertTrue(ptr0 != ptr1)
self.assertTrue(ptr0 != fptr1)
self.assertFalse(ptr1 != fptr1)
self.assertFalse(ptr0 > ptr1)
self.assertFalse(ptr0 > fptr1)
self.assertFalse(ptr1 > fptr1)
self.assertFalse(ptr0 >= ptr1)
self.assertFalse(ptr0 >= fptr1)
self.assertTrue(ptr1 >= fptr1)
self.assertRaises(TypeError, operator.lt, ptr0, self.int(1))
func = Object(
self.prog,
self.prog.function_type(self.prog.void_type(), (), False),
address=0xFFFF0000,
)
self.assertTrue(func == func)
self.assertTrue(func == ptr0)
array = Object(self.prog, "int [8]", address=0xFFFF0000)
self.assertTrue(array == array)
self.assertTrue(array != ptr1)
incomplete = Object(self.prog, "int []", address=0xFFFF0000)
self.assertTrue(incomplete == incomplete)
self.assertTrue(incomplete == ptr0)
self.assertRaises(
TypeError,
operator.eq,
Object(
self.prog, self.prog.struct_type("foo", None, None), address=0xFFFF0000
),
ptr0,
)
def test_add(self):
self._test_arithmetic(operator.add, 1, 2, 3, floating_point=True)
ptr = Object(self.prog, "int *", value=0xFFFF0000)
arr = Object(self.prog, "int [2]", address=0xFFFF0000)
ptr1 = Object(self.prog, "int *", value=0xFFFF0004)
self.assertIdentical(ptr + self.int(1), ptr1)
self.assertIdentical(self.unsigned_int(1) + ptr, ptr1)
self.assertIdentical(arr + self.int(1), ptr1)
self.assertIdentical(ptr1 + self.int(-1), ptr)
self.assertIdentical(self.int(-1) + ptr1, ptr)
self.assertIdentical(ptr + 1, ptr1)
self.assertIdentical(1 + ptr, ptr1)
self.assertRaises(TypeError, operator.add, ptr, ptr)
self.assertRaises(TypeError, operator.add, ptr, 2.0)
self.assertRaises(TypeError, operator.add, 2.0, ptr)
void_ptr = Object(self.prog, "void *", value=0xFFFF0000)
void_ptr1 = Object(self.prog, "void *", value=0xFFFF0001)
self.assertIdentical(void_ptr + self.int(1), void_ptr1)
self.assertIdentical(self.unsigned_int(1) + void_ptr, void_ptr1)
self.assertIdentical(void_ptr + 1, void_ptr1)
self.assertIdentical(1 + void_ptr, void_ptr1)
def test_sub(self):
self._test_arithmetic(operator.sub, 4, 2, 2, floating_point=True)
ptr = Object(self.prog, "int *", value=0xFFFF0000)
arr = Object(self.prog, "int [2]", address=0xFFFF0004)
ptr1 = Object(self.prog, "int *", value=0xFFFF0004)
self.assertIdentical(ptr1 - ptr, Object(self.prog, "ptrdiff_t", value=1))
self.assertIdentical(ptr - ptr1, Object(self.prog, "ptrdiff_t", value=-1))
self.assertIdentical(ptr - self.int(0), ptr)
self.assertIdentical(ptr1 - self.int(1), ptr)
self.assertIdentical(arr - self.int(1), ptr)
self.assertRaises(TypeError, operator.sub, self.int(1), ptr)
self.assertRaises(TypeError, operator.sub, ptr, 1.0)
void_ptr = Object(self.prog, "void *", value=0xFFFF0000)
void_ptr1 = Object(self.prog, "void *", value=0xFFFF0001)
self.assertIdentical(
void_ptr1 - void_ptr, Object(self.prog, "ptrdiff_t", value=1)
)
self.assertIdentical(
void_ptr - void_ptr1, Object(self.prog, "ptrdiff_t", value=-1)
)
self.assertIdentical(void_ptr - self.int(0), void_ptr)
self.assertIdentical(void_ptr1 - self.int(1), void_ptr)
def test_mul(self):
self._test_arithmetic(operator.mul, 2, 3, 6, floating_point=True)
self._test_pointer_type_errors(operator.mul)
# Negative numbers.
self.assertIdentical(self.int(2) * self.int(-3), self.int(-6))
self.assertIdentical(self.int(-2) * self.int(3), self.int(-6))
self.assertIdentical(self.int(-2) * self.int(-3), self.int(6))
# Integer overflow.
self.assertIdentical(self.int(0x8000) * self.int(0x10000), self.int(-(2**31)))
self.assertIdentical(
self.unsigned_int(0x8000) * self.int(0x10000), self.unsigned_int(2**31)
)
self.assertIdentical(
self.unsigned_int(0xFFFFFFFF) * self.unsigned_int(0xFFFFFFFF),
self.unsigned_int(1),
)
self.assertIdentical(
self.unsigned_int(0xFFFFFFFF) * self.int(-1), self.unsigned_int(1)
)
def test_div(self):
self._test_arithmetic(operator.truediv, 6, 3, 2, floating_point=True)
# Make sure we do integer division for integer operands.
self._test_arithmetic(operator.truediv, 3, 2, 1)
# Make sure we truncate towards zero (Python truncates towards negative
# infinity).
self._test_arithmetic(operator.truediv, -1, 2, 0)
self._test_arithmetic(operator.truediv, 1, -2, 0)
self.assertRaises(ZeroDivisionError, operator.truediv, self.int(1), self.int(0))
self.assertRaises(
ZeroDivisionError,
operator.truediv,
self.unsigned_int(1),
self.unsigned_int(0),
)
self.assertRaises(
ZeroDivisionError, operator.truediv, self.double(1), self.double(0)
)
self._test_pointer_type_errors(operator.truediv)
def test_mod(self):
self._test_arithmetic(operator.mod, 4, 2, 0)
# Make sure the modulo result has the sign of the dividend (Python uses
# the sign of the divisor).
self._test_arithmetic(operator.mod, 1, 26, 1)
self._test_arithmetic(operator.mod, 1, -26, 1)
self._test_arithmetic(operator.mod, -1, 26, -1)
self._test_arithmetic(operator.mod, -1, -26, -1)
self.assertRaises(ZeroDivisionError, operator.mod, self.int(1), self.int(0))
self.assertRaises(
ZeroDivisionError, operator.mod, self.unsigned_int(1), self.unsigned_int(0)
)
self._test_pointer_type_errors(operator.mod)
self._test_floating_type_errors(operator.mod)
def test_lshift(self):
self._test_shift(operator.lshift, 2, 3, 16)
self.assertIdentical(self.bool(True) << self.bool(True), self.int(2))
self.assertIdentical(self.int(1) << self.int(32), self.int(0))
def test_rshift(self):
self._test_shift(operator.rshift, 16, 3, 2)
self.assertIdentical(self.int(-2) >> self.int(1), self.int(-1))
self.assertIdentical(self.int(1) >> self.int(32), self.int(0))
self.assertIdentical(self.int(-1) >> self.int(32), self.int(-1))
def test_and(self):
self._test_arithmetic(operator.and_, 1, 3, 1)
self.assertIdentical(self.int(-1) & self.int(2**31), self.int(2**31))
self._test_pointer_type_errors(operator.and_)
self._test_floating_type_errors(operator.and_)
def test_xor(self):
self._test_arithmetic(operator.xor, 1, 3, 2)
self.assertIdentical(self.int(-1) ^ self.int(-(2**31)), self.int(2**31 - 1))
self._test_pointer_type_errors(operator.xor)
self._test_floating_type_errors(operator.xor)
def test_or(self):
self._test_arithmetic(operator.or_, 1, 3, 3)
self.assertIdentical(self.int(-(2**31)) | self.int(2**31 - 1), self.int(-1))
self._test_pointer_type_errors(operator.or_)
self._test_floating_type_errors(operator.or_)
def test_pos(self):
# TestIntegerPromotion covers the other cases.
self.assertRaisesRegex(
TypeError,
r"invalid operand to unary \+",
operator.pos,
Object(self.prog, "int *", value=0),
)
def test_neg(self):
self.assertIdentical(-Object(self.prog, "unsigned char", value=1), self.int(-1))
self.assertIdentical(-self.int(-1), self.int(1))
self.assertIdentical(-self.unsigned_int(1), self.unsigned_int(0xFFFFFFFF))
self.assertIdentical(
-Object(self.prog, "long", value=-0x8000000000000000),
Object(self.prog, "long", value=-0x8000000000000000),
)
self.assertIdentical(-self.double(2.0), self.double(-2.0))
self.assertRaisesRegex(
TypeError,
"invalid operand to unary -",
operator.neg,
Object(self.prog, "int *", value=0),
)
def test_not(self):
self.assertIdentical(~self.int(1), self.int(-2))
self.assertIdentical(
~Object(self.prog, "unsigned long long", value=-1),
Object(self.prog, "unsigned long long", value=0),
)
self.assertIdentical(
~Object(self.prog, "unsigned char", value=255), self.int(-256)
)
for type_ in ["int *", "double"]:
self.assertRaisesRegex(
TypeError,
"invalid operand to unary ~",
operator.invert,
Object(self.prog, type_, value=0),
)
def test_container_of(self):
obj = Object(self.prog, "int *", value=0xFFFF000C)
container_of(obj, self.point_type, "x")
self.assertIdentical(
container_of(obj, self.point_type, "x"),
Object(
self.prog, self.prog.pointer_type(self.point_type), value=0xFFFF000C
),
)
self.assertIdentical(
container_of(obj, self.point_type, "y"),
Object(
self.prog, self.prog.pointer_type(self.point_type), value=0xFFFF0008
),
)
self.assertIdentical(
container_of(obj, self.line_segment_type, "a.x"),
Object(
self.prog,
self.prog.pointer_type(self.line_segment_type),
value=0xFFFF000C,
),
)
self.assertIdentical(
container_of(obj, self.line_segment_type, "b.x"),
Object(
self.prog,
self.prog.pointer_type(self.line_segment_type),
value=0xFFFF0004,
),
)
self.assertRaisesRegex(
LookupError,
"'struct line_segment' has no member 'c'",
container_of,
obj,
self.line_segment_type,
"c.x",
)
polygon_type = self.prog.struct_type(
"polygon", 0, (TypeMember(self.prog.array_type(self.point_type), "points"),)
)
self.assertIdentical(
container_of(obj, polygon_type, "points[3].x"),
Object(self.prog, self.prog.pointer_type(polygon_type), value=0xFFFEFFF4),
)
small_point_type = self.prog.struct_type(
"small_point",
1,
(
TypeMember(
Object(
self.prog, self.prog.int_type("int", 4, True), bit_field_size=4
),
"x",
0,
),
TypeMember(
Object(
self.prog, self.prog.int_type("int", 4, True), bit_field_size=4
),
"y",
4,
),
),
)
self.assertRaisesRegex(
ValueError,
"member is not byte-aligned",
container_of,
obj,
small_point_type,
"y",
)
self.assertRaisesRegex(
TypeError,
r"container_of\(\) argument must be a pointer",
container_of,
obj[0],
self.point_type,
"x",
)
self.assertRaisesRegex(
TypeError,
"not a structure, union, or class",
container_of,
obj,
obj.type_,
"x",
),
type_ = self.prog.struct_type(
"foo",
16,
(
TypeMember(
self.prog.array_type(self.prog.int_type("int", 4, True), 8), "arr"
),
TypeMember(self.point_type, "point", 256),
),
)
syntax_errors = [
("", r"^expected identifier$"),
("[1]", r"^expected identifier$"),
("point.", r"^expected identifier after '\.'$"),
("point(", r"^expected '\.' or '\[' after identifier$"),
("arr[1](", r"^expected '\.' or '\[' after ']'$"),
("arr[]", r"^expected number after '\['$"),
("arr[1)", r"^expected ']' after number$"),
]
for member_designator, error in syntax_errors:
self.assertRaisesRegex(
SyntaxError, error, container_of, obj, type_, member_designator
)
class TestPrettyPrintObject(MockProgramTestCase):
def test_int(self):
obj = Object(self.prog, "int", value=99)
self.assertEqual(str(obj), "(int)99")
self.assertEqual(obj.format_(type_name=False), "99")
self.assertEqual(
str(Object(self.prog, "const int", value=-99)), "(const int)-99"
)
def test_char(self):
obj = Object(self.prog, "char", value=65)
self.assertEqual(str(obj), "(char)65")
self.assertEqual(obj.format_(char=True), "(char)'A'")
self.assertEqual(
Object(self.prog, "signed char", value=65).format_(char=True),
"(signed char)'A'",
)
self.assertEqual(
Object(self.prog, "unsigned char", value=65).format_(char=True),
"(unsigned char)'A'",
)
self.assertEqual(
Object(
self.prog,
self.prog.typedef_type("uint8_t", self.prog.type("unsigned char")),
value=65,
).format_(char=True),
"(uint8_t)65",
)
def test_bool(self):
self.assertEqual(str(Object(self.prog, "_Bool", value=False)), "(_Bool)0")
self.assertEqual(
str(Object(self.prog, "const _Bool", value=True)), "(const _Bool)1"
)
def test_float(self):
self.assertEqual(str(Object(self.prog, "double", value=2.0)), "(double)2.0")
self.assertEqual(str(Object(self.prog, "float", value=0.5)), "(float)0.5")
def test_typedef(self):
type_ = self.prog.typedef_type("INT", self.prog.int_type("int", 4, True))
self.assertEqual(str(Object(self.prog, type_, value=99)), "(INT)99")
type_ = self.prog.typedef_type(
"INT", self.prog.int_type("int", 4, True), qualifiers=Qualifiers.CONST
)
self.assertEqual(str(Object(self.prog, type_, value=99)), "(const INT)99")
type_ = self.prog.typedef_type(
"CINT", self.prog.int_type("int", 4, True, qualifiers=Qualifiers.CONST)
)
self.assertEqual(str(Object(self.prog, type_, value=99)), "(CINT)99")
def test_struct(self):
segment = (
(99).to_bytes(4, "little")
+ (-1).to_bytes(4, "little", signed=True)
+ (12345).to_bytes(4, "little", signed=True)
+ (0).to_bytes(4, "little", signed=True)
)
self.add_memory_segment(segment, virt_addr=0xFFFF0000)
self.types.append(self.point_type)
obj = Object(self.prog, "struct point", address=0xFFFF0000)
self.assertEqual(
str(obj),
"""\
(struct point){
.x = (int)99,
.y = (int)-1,
}""",
)
self.assertEqual(
obj.format_(member_type_names=False),
"""\
(struct point){
.x = 99,
.y = -1,
}""",
)
self.assertEqual(
obj.format_(members_same_line=True),
"(struct point){ .x = (int)99, .y = (int)-1 }",
)
self.assertEqual(
obj.format_(member_names=False),
"""\
(struct point){
(int)99,
(int)-1,
}""",
)
self.assertEqual(
obj.format_(members_same_line=True, member_names=False),
"(struct point){ (int)99, (int)-1 }",
)
type_ = self.prog.struct_type(
"foo",
16,
(
TypeMember(self.point_type, "point"),
TypeMember(
self.prog.struct_type(
None,
8,
(
TypeMember(self.prog.int_type("int", 4, True), "bar"),
TypeMember(self.prog.int_type("int", 4, True), "baz", 32),
),
),
None,
64,
),
),
)
obj = Object(self.prog, type_, address=0xFFFF0000)
expected = """\
(struct foo){
.point = (struct point){
.x = (int)99,
.y = (int)-1,
},
.bar = (int)12345,
.baz = (int)0,
}"""
self.assertEqual(str(obj), expected)
self.assertEqual(str(obj.read_()), expected)
self.add_memory_segment(
(
(99).to_bytes(8, "little")
+ (-1).to_bytes(8, "little", signed=True)
+ (12345).to_bytes(8, "little", signed=True)
+ (0).to_bytes(8, "little", signed=True)
),
virt_addr=0xFFFF8000,
)
type_ = self.prog.struct_type(
"foo",
32,
(
TypeMember(
self.prog.struct_type(
"long_point",
16,
(
TypeMember(self.prog.int_type("long", 8, True), "x"),
TypeMember(self.prog.int_type("long", 8, True), "y", 64),
),
),
"point",
),
TypeMember(self.prog.int_type("long", 8, True), "bar", 128),
TypeMember(self.prog.int_type("long", 8, True), "baz", 192),
),
)
obj = Object(self.prog, type_, address=0xFFFF8000)
expected = """\
(struct foo){
.point = (struct long_point){
.x = (long)99,
.y = (long)-1,
},
.bar = (long)12345,
.baz = (long)0,
}"""
self.assertEqual(str(obj), expected)
self.assertEqual(str(obj.read_()), expected)
type_ = self.prog.struct_type("foo", 0, ())
self.assertEqual(str(Object(self.prog, type_, address=0)), "(struct foo){}")
obj = Object(self.prog, self.point_type, value={"x": 1})
self.assertEqual(
obj.format_(implicit_members=False),
"""\
(struct point){
.x = (int)1,
}""",
)
self.assertEqual(
obj.format_(member_names=False, implicit_members=False),
"""\
(struct point){
(int)1,
}""",
)
obj = Object(self.prog, self.point_type, value={"y": 1})
self.assertEqual(
obj.format_(implicit_members=False),
"""\
(struct point){
.y = (int)1,
}""",
)
self.assertEqual(
obj.format_(member_names=False, implicit_members=False),
"""\
(struct point){
(int)0,
(int)1,
}""",
)
def test_bit_field(self):
self.add_memory_segment(b"\x07\x10\x5e\x5f\x1f\0\0\0", virt_addr=0xFFFF0000)
type_ = self.prog.struct_type(
"bits",
8,
(
TypeMember(
Object(
self.prog, self.prog.int_type("int", 4, True), bit_field_size=4
),
"x",
0,
),
TypeMember(
Object(
self.prog,
self.prog.int_type("int", 4, True, qualifiers=Qualifiers.CONST),
bit_field_size=28,
),
"y",
4,
),
TypeMember(
Object(
self.prog, self.prog.int_type("int", 4, True), bit_field_size=5
),
"z",
32,
),
),
)
obj = Object(self.prog, type_, address=0xFFFF0000)
self.assertEqual(
str(obj),
"""\
(struct bits){
.x = (int)7,
.y = (const int)100000000,
.z = (int)-1,
}""",
)
self.assertEqual(str(obj.x), "(int)7")
self.assertEqual(str(obj.y), "(const int)100000000")
self.assertEqual(str(obj.z), "(int)-1")
def test_union(self):
self.add_memory_segment(b"\0\0\x80?", virt_addr=0xFFFF0000)
self.types.append(self.option_type)
self.assertEqual(
str(Object(self.prog, "union option", address=0xFFFF0000)),
"""\
(union option){
.i = (int)1065353216,
.f = (float)1.0,
}""",
)
def test_enum(self):
self.assertEqual(
str(Object(self.prog, self.color_type, value=0)), "(enum color)RED"
)
self.assertEqual(
str(Object(self.prog, self.color_type, value=1)), "(enum color)GREEN"
)
self.assertEqual(
str(Object(self.prog, self.color_type, value=4)), "(enum color)4"
)
obj = Object(self.prog, self.prog.enum_type("color"), address=0)
self.assertRaisesRegex(TypeError, "cannot format incomplete enum", str, obj)
def test_pointer(self):
self.add_memory_segment((99).to_bytes(4, "little"), virt_addr=0xFFFF0000)
obj = Object(self.prog, "int *", value=0xFFFF0000)
self.assertEqual(str(obj), "*(int *)0xffff0000 = 99")
self.assertEqual(obj.format_(dereference=False), "(int *)0xffff0000")
self.assertEqual(
str(Object(self.prog, "int *", value=0x7FFFFFFF)), "(int *)0x7fffffff"
)
def test_void_pointer(self):
self.add_memory_segment((99).to_bytes(4, "little"), virt_addr=0xFFFF0000)
self.assertEqual(
str(Object(self.prog, "void *", value=0xFFFF0000)), "(void *)0xffff0000"
)
def test_pointer_typedef(self):
self.add_memory_segment(
(0xFFFF00F0).to_bytes(8, "little"), virt_addr=0xFFFF0000
)
type_ = self.prog.typedef_type(
"HANDLE",
self.prog.pointer_type(self.prog.pointer_type(self.prog.void_type())),
)
self.assertEqual(
str(Object(self.prog, type_, value=0xFFFF0000)),
"*(HANDLE)0xffff0000 = 0xffff00f0",
)
# TODO: test symbolize.
def test_c_string(self):
self.add_memory_segment(b"hello\0", virt_addr=0xFFFF0000)
self.add_memory_segment(b"unterminated", virt_addr=0xFFFF0010)
self.add_memory_segment(b'"escape\tme\\\0', virt_addr=0xFFFF0020)
obj = Object(self.prog, "char *", value=0xFFFF0000)
self.assertEqual(str(obj), '(char *)0xffff0000 = "hello"')
self.assertEqual(obj.format_(string=False), "*(char *)0xffff0000 = 104")
self.assertEqual(str(Object(self.prog, "char *", value=0x0)), "(char *)0x0")
self.assertEqual(
str(Object(self.prog, "char *", value=0xFFFF0010)), "(char *)0xffff0010"
)
self.assertEqual(
str(Object(self.prog, "char *", value=0xFFFF0020)),
r'(char *)0xffff0020 = "\"escape\tme\\"',
)
def test_basic_array(self):
segment = bytearray()
for i in range(5):
segment.extend(i.to_bytes(4, "little"))
self.add_memory_segment(segment, virt_addr=0xFFFF0000)
obj = Object(self.prog, "int [5]", address=0xFFFF0000)
self.assertEqual(str(obj), "(int [5]){ 0, 1, 2, 3, 4 }")
self.assertEqual(
obj.format_(type_name=False, element_type_names=True),
"{ (int)0, (int)1, (int)2, (int)3, (int)4 }",
)
self.assertEqual(
obj.format_(element_indices=True),
"(int [5]){ [1] = 1, [2] = 2, [3] = 3, [4] = 4 }",
)
self.assertEqual(
obj.format_(element_indices=True, implicit_elements=True),
"(int [5]){ [0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4 }",
)
self.assertEqual(obj.format_(columns=27), str(obj))
for columns in range(22, 26):
self.assertEqual(
obj.format_(columns=columns),
"""\
(int [5]){
0, 1, 2, 3, 4,
}""",
)
for columns in range(19, 22):
self.assertEqual(
obj.format_(columns=columns),
"""\
(int [5]){
0, 1, 2, 3,
4,
}""",
)
for columns in range(16, 19):
self.assertEqual(
obj.format_(columns=columns),
"""\
(int [5]){
0, 1, 2,
3, 4,
}""",
)
for columns in range(13, 16):
self.assertEqual(
obj.format_(columns=columns),
"""\
(int [5]){
0, 1,
2, 3,
4,
}""",
)
for columns in range(13):
self.assertEqual(
obj.format_(columns=columns),
"""\
(int [5]){
0,
1,
2,
3,
4,
}""",
)
self.assertEqual(
obj.format_(elements_same_line=False),
"""\
(int [5]){
0,
1,
2,
3,
4,
}""",
)
def test_nested_array(self):
segment = bytearray()
for i in range(10):
segment.extend(i.to_bytes(4, "little"))
self.add_memory_segment(segment, virt_addr=0xFFFF0000)
obj = Object(self.prog, "int [2][5]", address=0xFFFF0000)
self.assertEqual(
str(obj), "(int [2][5]){ { 0, 1, 2, 3, 4 }, { 5, 6, 7, 8, 9 } }"
)
self.assertEqual(obj.format_(columns=52), str(obj))
for columns in range(45, 52):
self.assertEqual(
obj.format_(columns=columns),
"""\
(int [2][5]){
{ 0, 1, 2, 3, 4 }, { 5, 6, 7, 8, 9 },
}""",
)
for columns in range(26, 45):
self.assertEqual(
obj.format_(columns=columns),
"""\
(int [2][5]){
{ 0, 1, 2, 3, 4 },
{ 5, 6, 7, 8, 9 },
}""",
)
for columns in range(24, 26):
self.assertEqual(
obj.format_(columns=columns),
"""\
(int [2][5]){
{
0, 1, 2,
3, 4,
},
{
5, 6, 7,
8, 9,
},
}""",
)
for columns in range(21, 24):
self.assertEqual(
obj.format_(columns=columns),
"""\
(int [2][5]){
{
0, 1,
2, 3,
4,
},
{
5, 6,
7, 8,
9,
},
}""",
)
for columns in range(21):
self.assertEqual(
obj.format_(columns=columns),
"""\
(int [2][5]){
{
0,
1,
2,
3,
4,
},
{
5,
6,
7,
8,
9,
},
}""",
)
def test_array_member(self):
segment = bytearray()
for i in range(5):
segment.extend(i.to_bytes(4, "little"))
self.add_memory_segment(segment, virt_addr=0xFFFF0000)
type_ = self.prog.struct_type(
None,
20,
(
TypeMember(
self.prog.array_type(self.prog.int_type("int", 4, True), 5), "arr"
),
),
)
obj = Object(self.prog, type_, address=0xFFFF0000)
self.assertEqual(
str(obj),
"""\
(struct <anonymous>){
.arr = (int [5]){ 0, 1, 2, 3, 4 },
}""",
)
self.assertEqual(obj.format_(columns=42), str(obj))
self.assertEqual(
obj.format_(columns=41),
"""\
(struct <anonymous>){
.arr = (int [5]){
0, 1, 2, 3, 4,
},
}""",
)
self.assertEqual(
obj.format_(columns=18),
"""\
(struct <anonymous>){
.arr = (int [5]){
0,
1,
2,
3,
4,
},
}""",
)
def test_array_of_struct(self):
segment = bytearray()
for i in range(1, 5):
segment.extend(i.to_bytes(4, "little"))
self.add_memory_segment(segment, virt_addr=0xFFFF0000)
self.types.append(self.point_type)
obj = Object(self.prog, "struct point [2]", address=0xFFFF0000)
self.assertEqual(
str(obj),
"""\
(struct point [2]){
{
.x = (int)1,
.y = (int)2,
},
{
.x = (int)3,
.y = (int)4,
},
}""",
)
def test_zero_length_array(self):
self.assertEqual(str(Object(self.prog, "int []", address=0)), "(int []){}")
self.assertEqual(str(Object(self.prog, "int [0]", address=0)), "(int [0]){}")
def test_array_zeroes(self):
segment = bytearray(16)
self.add_memory_segment(segment, virt_addr=0xFFFF0000)
self.types.append(self.point_type)
self.types.append(self.prog.struct_type("empty", 0, ()))
obj = Object(self.prog, "int [2]", address=0xFFFF0000)
self.assertEqual(str(obj), "(int [2]){}")
self.assertEqual(obj.format_(implicit_elements=True), "(int [2]){ 0, 0 }")
segment[:4] = (99).to_bytes(4, "little")
self.assertEqual(str(obj), "(int [2]){ 99 }")
segment[:4] = (0).to_bytes(4, "little")
segment[4:8] = (99).to_bytes(4, "little")
self.assertEqual(str(obj), "(int [2]){ 0, 99 }")
obj = Object(self.prog, "struct point [2]", address=0xFFFF0000)
self.assertEqual(
str(obj),
"""\
(struct point [2]){
{
.x = (int)0,
.y = (int)99,
},
}""",
)
obj = Object(self.prog, "struct empty [2]", address=0)
self.assertEqual(str(obj), "(struct empty [2]){}")
def test_char_array(self):
segment = bytearray(16)
self.add_memory_segment(segment, virt_addr=0xFFFF0000)
obj = Object(self.prog, "char [4]", address=0xFFFF0000)
segment[:16] = b"hello, world\0\0\0\0"
self.assertEqual(str(obj), '(char [4])"hell"')
self.assertEqual(obj.format_(string=False), "(char [4]){ 104, 101, 108, 108 }")
self.assertEqual(str(obj.read_()), str(obj))
segment[2] = 0
self.assertEqual(str(obj), '(char [4])"he"')
self.assertEqual(str(obj.read_()), str(obj))
self.assertEqual(
str(Object(self.prog, "char [0]", address=0xFFFF0000)), "(char [0]){}"
)
self.assertEqual(
str(Object(self.prog, "char []", address=0xFFFF0000)), "(char []){}"
)
def test_function(self):
obj = Object(
self.prog,
self.prog.function_type(self.prog.void_type(), (), False),
address=0xFFFF0000,
)
self.assertEqual(str(obj), "(void (void))0xffff0000")
def test_absent(self):
self.assertRaises(TypeError, str, Object(self.prog, "void"))
for type_ in [
"int",
"char",
"_Bool",
"double",
self.point_type,
self.option_type,
self.coord_type,
self.color_type,
"size_t",
"void *",
"int [2]",
self.prog.function_type(self.prog.void_type(), ()),
]:
if isinstance(type_, Type):
type_name = type_.type_name()
else:
type_name = type_
self.assertEqual(str(Object(self.prog, type_)), f"({type_name})<absent>")
def test_bigint(self):
segment = bytearray(16)
self.add_memory_segment(segment, virt_addr=0xFFFF0000)
for byteorder in ("little", "big"):
with self.subTest(byteorder=byteorder):
obj = Object(
self.prog,
self.prog.int_type("unsigned __int128", 16, False, byteorder),
address=0xFFFF0000,
)
for value in (
0,
0x100,
0x112233445566778899AABBCCDDEEFF,
0xFEDCBA9876543210,
):
segment[:] = value.to_bytes(16, byteorder)
self.assertEqual(str(obj), "(unsigned __int128)" + hex(value))
def test_bigint_in_array(self):
segment = bytearray(16 * 3)
self.add_memory_segment(segment, virt_addr=0xFFFF0000)
for byteorder in ("little", "big"):
with self.subTest(byteorder=byteorder):
segment[:16] = (0x112233445566778899AABBCCDDEEFF).to_bytes(
16, byteorder
)
segment[16:32] = (0xDEADBEEF).to_bytes(16, byteorder)
obj = Object(
self.prog,
self.prog.array_type(
self.prog.int_type("unsigned __int128", 16, False, byteorder), 3
),
address=0xFFFF0000,
)
self.assertEqual(
str(obj),
"(unsigned __int128 [3]){ 0x112233445566778899aabbccddeeff, 0xdeadbeef }",
)
self.assertEqual(
obj.format_(implicit_elements=True),
"(unsigned __int128 [3]){ 0x112233445566778899aabbccddeeff, 0xdeadbeef, 0x0 }",
)
def test_bigint_in_struct(self):
segment = bytearray(16 * 3)
self.add_memory_segment(segment, virt_addr=0xFFFF0000)
for byteorder in ("little", "big"):
with self.subTest(byteorder=byteorder):
segment[:16] = (0x112233445566778899AABBCCDDEEFF).to_bytes(
16, byteorder
)
segment[16:32] = (0xDEADBEEF).to_bytes(16, byteorder)
type = self.prog.int_type("unsigned __int128", 16, False, byteorder)
obj = Object(
self.prog,
self.prog.struct_type(
"foo",
16 * 3,
(
TypeMember(type, "a"),
TypeMember(type, "b", 16 * 8),
TypeMember(type, "c", 16 * 8 * 2),
),
),
address=0xFFFF0000,
)
self.assertEqual(
str(obj),
"""\
(struct foo){
.a = (unsigned __int128)0x112233445566778899aabbccddeeff,
.b = (unsigned __int128)0xdeadbeef,
.c = (unsigned __int128)0x0,
}""",
)
self.assertEqual(
obj.format_(implicit_members=False),
"""\
(struct foo){
.a = (unsigned __int128)0x112233445566778899aabbccddeeff,
.b = (unsigned __int128)0xdeadbeef,
}""",
)
def test_bigint_typedef(self):
self.add_memory_segment(
(0xDEADBEEF).to_bytes(16, "little"), virt_addr=0xFFFF0000
)
type = self.prog.typedef_type(
"__uint128_t", self.prog.int_type("unsigned __int128", 16, False)
)
self.assertEqual(
str(Object(self.prog, type, address=0xFFFF0000)),
"(__uint128_t)0xdeadbeef",
)
self.assertEqual(
str(Object(self.prog, self.prog.array_type(type, 1), address=0xFFFF0000)),
"(__uint128_t [1]){ 0xdeadbeef }",
)
self.assertEqual(
str(
Object(
self.prog,
self.prog.struct_type("foo", 16, (TypeMember(type, "a"),)),
address=0xFFFF0000,
)
),
"""\
(struct foo){
.a = (__uint128_t)0xdeadbeef,
}""",
)