drgn/tests/test_object.py
Omar Sandoval ab876f3dbd libdrgn/python: allow specifying Object value positionally
It's annoying to have to do value= when creating objects, especially in
interactive mode. Let's allow passing in the value positionally so that
`Object(prog, "int", value=0)` becomes `Object(prog, "int", 0)`. It's
clear enough that this is creating an int with value 0.
2020-05-18 00:07:49 -07:00

2537 lines
84 KiB
Python

# Copyright (c) Facebook, Inc. and its affiliates.
# SPDX-License-Identifier: GPL-3.0+
import math
import operator
import struct
from drgn import (
FaultError,
Object,
OutOfBoundsError,
Qualifiers,
Type,
TypeEnumerator,
TypeMember,
array_type,
cast,
container_of,
enum_type,
float_type,
function_type,
int_type,
pointer_type,
reinterpret,
sizeof,
struct_type,
typedef_type,
union_type,
void_type,
)
from tests import (
MockMemorySegment,
ObjectTestCase,
color_type,
coord_type,
line_segment_type,
mock_program,
option_type,
pid_type,
point_type,
)
class TestInit(ObjectTestCase):
def test_reinit(self):
obj = Object(self.prog, "int", value=1)
self.assertEqual(obj.value_(), 1)
obj.__init__(self.prog, value=2)
self.assertEqual(obj.value_(), 2)
prog = mock_program()
self.assertRaisesRegex(
ValueError, "cannot change object program", obj.__init__, prog, value=3
)
def test_type_stays_alive(self):
obj = Object(self.prog, int_type("int", 4, True), value=0)
self.assertEqual(obj.type_, int_type("int", 4, True))
type_ = obj.type_
del obj
self.assertEqual(type_, int_type("int", 4, True))
del self.prog
self.assertEqual(type_, int_type("int", 4, True))
def test_type(self):
self.assertRaisesRegex(
TypeError, "type must be Type, str, or None", Object, self.prog, 1, value=0
)
self.assertRaisesRegex(
ValueError, "reference must have type", Object, self.prog, address=0
)
def test_address_xor_value(self):
self.assertRaisesRegex(
ValueError, "object must have either address or value", Object, self.prog
)
self.assertRaisesRegex(
ValueError,
"object must have either address or value",
Object,
self.prog,
"int",
)
self.assertRaisesRegex(
ValueError,
"object cannot have address and value",
Object,
self.prog,
"int",
0,
address=0,
)
self.assertRaisesRegex(
ValueError,
"object cannot have address and value",
Object,
self.prog,
"int",
value=0,
address=0,
)
def test_integer_address(self):
self.assertRaises(TypeError, Object, self.prog, "int", address="NULL")
def test_byteorder(self):
self.assertRaises(
ValueError, Object, self.prog, "int", address=0, byteorder="middle"
)
self.assertRaisesRegex(
ValueError,
"primitive value cannot have byteorder",
Object,
self.prog,
"int",
value=0,
byteorder="little",
)
def test_bit_field_size(self):
self.assertRaises(
TypeError, Object, self.prog, "int", address=0, bit_field_size="1"
)
self.assertRaisesRegex(
ValueError,
"bit field size cannot be zero",
Object,
self.prog,
"int",
address=0,
bit_field_size=0,
)
def test_bit_offset(self):
self.assertRaisesRegex(
ValueError,
"primitive value cannot have bit offset",
Object,
self.prog,
"int",
value=0,
bit_offset=4,
)
class TestReference(ObjectTestCase):
def test_basic(self):
prog = mock_program(
segments=[
MockMemorySegment((1000).to_bytes(4, "little"), virt_addr=0xFFFF0000),
]
)
obj = Object(prog, "int", address=0xFFFF0000)
self.assertIs(obj.prog_, prog)
self.assertEqual(obj.type_, prog.type("int"))
self.assertEqual(obj.address_, 0xFFFF0000)
self.assertEqual(obj.byteorder_, "little")
self.assertEqual(obj.bit_offset_, 0)
self.assertIsNone(obj.bit_field_size_)
self.assertEqual(obj.value_(), 1000)
self.assertEqual(repr(obj), "Object(prog, 'int', address=0xffff0000)")
self.assertEqual(obj.read_(), Object(prog, "int", value=1000))
obj = Object(prog, "int", address=0xFFFF0000, byteorder="big")
self.assertEqual(obj.byteorder_, "big")
self.assertEqual(obj.value_(), -402456576)
self.assertEqual(
repr(obj), "Object(prog, 'int', address=0xffff0000, byteorder='big')"
)
self.assertEqual(sizeof(obj), 4)
obj = Object(prog, "unsigned int", address=0xFFFF0000, bit_field_size=4)
self.assertEqual(obj.bit_offset_, 0)
self.assertEqual(obj.bit_field_size_, 4)
self.assertEqual(obj.value_(), 8)
self.assertEqual(
repr(obj),
"Object(prog, 'unsigned int', address=0xffff0000, bit_field_size=4)",
)
self.assertRaises(TypeError, sizeof, obj)
obj = Object(
prog, "unsigned int", address=0xFFFF0000, bit_field_size=4, bit_offset=4
)
self.assertEqual(obj.bit_offset_, 4)
self.assertEqual(obj.bit_field_size_, 4)
self.assertEqual(obj.value_(), 14)
self.assertEqual(
repr(obj),
"Object(prog, 'unsigned int', address=0xffff0000, bit_offset=4, bit_field_size=4)",
)
def test_overflow(self):
Object(self.prog, "char", address=0xFFFFFFFFFFFFFFFF)
Object(
self.prog,
"char",
address=0xFFFFFFFFFFFFFFFF,
bit_field_size=1,
bit_offset=7,
)
Object(self.prog, f"char [{(2**64 - 1) // 8}]", address=0, bit_offset=7)
def test_read_unsigned(self):
value = 12345678912345678989
for bit_size in range(1, 65):
for bit_offset in range(8):
size = (bit_size + bit_offset + 7) // 8
size_mask = (1 << (8 * size)) - 1
for byteorder in ["little", "big"]:
if byteorder == "little":
tmp = value << bit_offset
else:
tmp = value << (8 - bit_size - bit_offset) % 8
tmp &= size_mask
buf = tmp.to_bytes(size, byteorder)
prog = mock_program(segments=[MockMemorySegment(buf, 0)])
obj = Object(
prog,
"unsigned long long",
address=0,
bit_field_size=bit_size,
bit_offset=bit_offset,
byteorder=byteorder,
)
self.assertEqual(obj.value_(), value & ((1 << bit_size) - 1))
def test_read_float(self):
pi32 = struct.unpack("f", struct.pack("f", math.pi))[0]
for bit_size in [32, 64]:
for bit_offset in range(8):
for byteorder in ["little", "big"]:
if bit_size == 64:
fmt = "<d"
type_ = "double"
expected = math.pi
else:
fmt = "<f"
type_ = "float"
expected = pi32
tmp = int.from_bytes(struct.pack(fmt, math.pi), "little")
if byteorder == "little":
tmp <<= bit_offset
else:
tmp <<= (8 - bit_size - bit_offset) % 8
buf = tmp.to_bytes((bit_size + bit_offset + 7) // 8, byteorder)
prog = mock_program(segments=[MockMemorySegment(buf, 0)])
obj = Object(
prog,
type_,
address=0,
bit_offset=bit_offset,
byteorder=byteorder,
)
self.assertEqual(obj.value_(), expected)
def test_struct(self):
segment = (
(99).to_bytes(4, "little")
+ (-1).to_bytes(4, "little", signed=True)
+ (12345).to_bytes(4, "little")
+ (0).to_bytes(4, "little")
)
prog = mock_program(
segments=[MockMemorySegment(segment, virt_addr=0xFFFF0000),],
types=[point_type],
)
obj = Object(prog, "struct point", address=0xFFFF0000)
self.assertEqual(obj.value_(), {"x": 99, "y": -1})
self.assertEqual(sizeof(obj), 8)
type_ = struct_type(
"foo",
16,
(
TypeMember(point_type, "point"),
TypeMember(
struct_type(
None,
8,
(
TypeMember(int_type("int", 4, True), "bar"),
TypeMember(int_type("int", 4, True), "baz", 32),
),
),
None,
64,
),
),
)
obj = Object(prog, type_, address=0xFFFF0000)
self.assertEqual(
obj.value_(), {"point": {"x": 99, "y": -1}, "bar": 12345, "baz": 0}
)
def test_array(self):
segment = bytearray()
for i in range(10):
segment.extend(i.to_bytes(4, "little"))
prog = mock_program(
segments=[MockMemorySegment(segment, virt_addr=0xFFFF0000),]
)
obj = Object(prog, "int [5]", address=0xFFFF0000)
self.assertEqual(obj.value_(), [0, 1, 2, 3, 4])
self.assertEqual(sizeof(obj), 20)
obj = Object(prog, "int [2][5]", address=0xFFFF0000)
self.assertEqual(obj.value_(), [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]])
obj = Object(prog, "int [2][2][2]", address=0xFFFF0000)
self.assertEqual(obj.value_(), [[[0, 1], [2, 3]], [[4, 5], [6, 7]]])
def test_void(self):
obj = Object(self.prog, void_type(), address=0)
self.assertIs(obj.prog_, self.prog)
self.assertEqual(obj.type_, void_type())
self.assertEqual(obj.address_, 0)
self.assertEqual(obj.byteorder_, "little")
self.assertEqual(obj.bit_offset_, 0)
self.assertIsNone(obj.bit_field_size_)
self.assertRaisesRegex(
TypeError, "cannot read object with void type", obj.value_
)
self.assertRaisesRegex(
TypeError, "cannot read object with void type", obj.read_
)
self.assertRaises(TypeError, sizeof, obj)
def test_function(self):
obj = Object(self.prog, function_type(void_type(), (), False), address=0)
self.assertIs(obj.prog_, self.prog)
self.assertEqual(obj.type_, function_type(void_type(), (), False))
self.assertEqual(obj.address_, 0)
self.assertEqual(obj.byteorder_, "little")
self.assertEqual(obj.bit_offset_, 0)
self.assertIsNone(obj.bit_field_size_)
self.assertRaisesRegex(
TypeError, "cannot read object with function type", obj.value_
)
self.assertRaisesRegex(
TypeError, "cannot read object with function type", obj.read_
)
self.assertRaises(TypeError, sizeof, obj)
def test_incomplete(self):
# It's valid to create references with incomplete type, but not to read
# from them.
obj = Object(self.prog, struct_type("foo"), address=0)
self.assertRaisesRegex(
TypeError, "cannot read object with incomplete structure type", obj.value_
)
self.assertRaisesRegex(
TypeError, "cannot read object with incomplete structure type", obj.read_
)
self.assertRaises(TypeError, sizeof, obj)
obj = Object(self.prog, union_type("foo"), address=0)
self.assertRaisesRegex(
TypeError, "cannot read object with incomplete union type", obj.value_
)
self.assertRaisesRegex(
TypeError, "cannot read object with incomplete union type", obj.read_
)
obj = Object(self.prog, enum_type("foo"), address=0)
self.assertRaisesRegex(
TypeError, "cannot read object with incomplete enumerated type", obj.value_
)
self.assertRaisesRegex(
TypeError, "cannot read object with incomplete enumerated type", obj.read_
)
obj = Object(self.prog, array_type(None, int_type("int", 4, True)), address=0)
self.assertRaisesRegex(
TypeError, "cannot read object with incomplete array type", obj.value_
)
self.assertRaisesRegex(
TypeError, "cannot read object with incomplete array type", obj.read_
)
class TestValue(ObjectTestCase):
def test_positional(self):
self.assertEqual(Object(self.prog, "int", 1), Object(self.prog, "int", value=1))
def test_signed(self):
obj = Object(self.prog, "int", value=-4)
self.assertIs(obj.prog_, self.prog)
self.assertEqual(obj.type_, self.prog.type("int"))
self.assertIsNone(obj.address_)
self.assertIsNone(obj.byteorder_)
self.assertIsNone(obj.bit_offset_)
self.assertIsNone(obj.bit_field_size_)
self.assertEqual(obj.value_(), -4)
self.assertEqual(repr(obj), "Object(prog, 'int', value=-4)")
self.assertEqual(obj.read_(), obj)
self.assertEqual(Object(self.prog, "int", value=2 ** 32 - 4), obj)
self.assertEqual(Object(self.prog, "int", value=2 ** 64 - 4), obj)
self.assertEqual(Object(self.prog, "int", value=2 ** 128 - 4), obj)
self.assertEqual(Object(self.prog, "int", value=-4.6), obj)
self.assertRaisesRegex(
TypeError,
"'int' value must be number",
Object,
self.prog,
"int",
value=b"asdf",
)
obj = Object(self.prog, "int", value=8, bit_field_size=4)
self.assertIsNone(obj.bit_offset_)
self.assertEqual(obj.bit_field_size_, 4)
self.assertEqual(obj.value_(), -8)
self.assertEqual(repr(obj), "Object(prog, 'int', value=-8, bit_field_size=4)")
value = 12345678912345678989
for bit_size in range(1, 65):
tmp = value & ((1 << bit_size) - 1)
mask = 1 << (bit_size - 1)
tmp = (tmp ^ mask) - mask
self.assertEqual(
Object(
self.prog, "long", value=value, bit_field_size=bit_size
).value_(),
tmp,
)
def test_unsigned(self):
obj = Object(self.prog, "unsigned int", value=2 ** 32 - 1)
self.assertIs(obj.prog_, self.prog)
self.assertEqual(obj.type_, self.prog.type("unsigned int"))
self.assertIsNone(obj.address_)
self.assertIsNone(obj.byteorder_)
self.assertIsNone(obj.bit_offset_)
self.assertIsNone(obj.bit_field_size_)
self.assertEqual(obj.value_(), 2 ** 32 - 1)
self.assertEqual(repr(obj), "Object(prog, 'unsigned int', value=4294967295)")
self.assertEqual(Object(self.prog, "unsigned int", value=-1), obj)
self.assertEqual(Object(self.prog, "unsigned int", value=2 ** 64 - 1), obj)
self.assertEqual(Object(self.prog, "unsigned int", value=2 ** 65 - 1), obj)
self.assertEqual(
Object(self.prog, "unsigned int", value=2 ** 32 - 1 + 0.9), obj
)
self.assertRaisesRegex(
TypeError,
"'unsigned int' value must be number",
Object,
self.prog,
"unsigned int",
value="foo",
)
obj = Object(self.prog, "unsigned int", value=24, bit_field_size=4)
self.assertIsNone(obj.bit_offset_)
self.assertEqual(obj.bit_field_size_, 4)
self.assertEqual(obj.value_(), 8)
self.assertEqual(
repr(obj), "Object(prog, 'unsigned int', value=8, bit_field_size=4)"
)
value = 12345678912345678989
for bit_size in range(1, 65):
self.assertEqual(
Object(
self.prog,
"unsigned long long",
value=value,
bit_field_size=bit_size,
).value_(),
value & ((1 << bit_size) - 1),
)
def test_float(self):
obj = Object(self.prog, "double", value=3.14)
self.assertIs(obj.prog_, self.prog)
self.assertEqual(obj.type_, self.prog.type("double"))
self.assertIsNone(obj.address_)
self.assertIsNone(obj.byteorder_)
self.assertEqual(obj.value_(), 3.14)
self.assertEqual(repr(obj), "Object(prog, 'double', value=3.14)")
obj = Object(self.prog, "double", value=-100.0)
self.assertEqual(Object(self.prog, "double", value=-100), obj)
self.assertRaisesRegex(
TypeError,
"'double' value must be number",
Object,
self.prog,
"double",
value={},
)
self.assertEqual(Object(self.prog, "double", value=math.e).value_(), math.e)
self.assertEqual(
Object(self.prog, "float", value=math.e).value_(),
struct.unpack("f", struct.pack("f", math.e))[0],
)
def test_enum(self):
self.assertEqual(Object(self.prog, color_type, value=0).value_(), 0)
def test_incomplete(self):
self.assertRaisesRegex(
TypeError,
"cannot create object with incomplete structure type",
Object,
self.prog,
struct_type("foo"),
value={},
)
self.assertRaisesRegex(
TypeError,
"cannot create object with incomplete union type",
Object,
self.prog,
union_type("foo"),
value={},
)
self.assertRaisesRegex(
TypeError,
"cannot create object with incomplete enumerated type",
Object,
self.prog,
enum_type("foo"),
value=0,
)
self.assertRaisesRegex(
TypeError,
"cannot create object with incomplete array type",
Object,
self.prog,
array_type(None, int_type("int", 4, True)),
value=[],
)
def test_compound(self):
obj = Object(self.prog, point_type, value={"x": 100, "y": -5})
self.assertEqual(obj.x, Object(self.prog, "int", value=100))
self.assertEqual(obj.y, Object(self.prog, "int", value=-5))
self.assertEqual(
Object(self.prog, point_type, value={}),
Object(self.prog, point_type, value={"x": 0, "y": 0}),
)
value = {
"a": {"x": 1, "y": 2},
"b": {"x": 3, "y": 4},
}
obj = Object(self.prog, line_segment_type, value=value)
self.assertEqual(obj.a, Object(self.prog, point_type, value={"x": 1, "y": 2}))
self.assertEqual(obj.b, Object(self.prog, point_type, value={"x": 3, "y": 4}))
self.assertEqual(obj.value_(), value)
invalid_struct = struct_type(
"foo",
4,
(
TypeMember(int_type("short", 2, True), "a"),
# Straddles the end of the structure.
TypeMember(int_type("int", 4, True), "b", 16),
# Beyond the end of the structure.
TypeMember(int_type("int", 4, True), "c", 32),
),
)
Object(self.prog, invalid_struct, value={"a": 0})
self.assertRaisesRegex(
OutOfBoundsError,
"out of bounds of value",
Object,
self.prog,
invalid_struct,
value={"a": 0, "b": 4},
)
self.assertRaisesRegex(
OutOfBoundsError,
"out of bounds of value",
Object,
self.prog,
invalid_struct,
value={"a": 0, "c": 4},
)
self.assertRaisesRegex(
TypeError,
"must be dictionary or mapping",
Object,
self.prog,
point_type,
value=1,
)
self.assertRaisesRegex(
TypeError,
"member key must be string",
Object,
self.prog,
point_type,
value={0: 0},
)
self.assertRaisesRegex(
TypeError, "must be number", Object, self.prog, point_type, value={"x": []}
)
self.assertRaisesRegex(
LookupError,
"has no member 'z'",
Object,
self.prog,
point_type,
value={"z": 999},
)
def test_pointer(self):
obj = Object(self.prog, "int *", value=0xFFFF0000)
self.assertIsNone(obj.address_)
self.assertEqual(obj.value_(), 0xFFFF0000)
self.assertEqual(repr(obj), "Object(prog, 'int *', value=0xffff0000)")
obj = Object(
self.prog, typedef_type("INTP", self.prog.type("int *")), value=0xFFFF0000
)
self.assertIsNone(obj.address_)
self.assertEqual(obj.value_(), 0xFFFF0000)
self.assertEqual(repr(obj), "Object(prog, 'INTP', value=0xffff0000)")
def test_array(self):
obj = Object(self.prog, "int [2]", value=[1, 2])
self.assertEqual(obj[0], Object(self.prog, "int", value=1))
self.assertEqual(obj[1], Object(self.prog, "int", value=2))
self.assertEqual(
Object(self.prog, "int [2]", value=[]),
Object(self.prog, "int [2]", value=[0, 0]),
)
self.assertRaisesRegex(
TypeError, "must be iterable", Object, self.prog, "int [1]", value=1
)
self.assertRaisesRegex(
ValueError,
"too many items in array value",
Object,
self.prog,
"int [1]",
value=[1, 2],
)
class TestConversions(ObjectTestCase):
def test_bool(self):
self.assertTrue(Object(self.prog, "int", value=-1))
self.assertFalse(Object(self.prog, "int", value=0))
self.assertTrue(Object(self.prog, "unsigned int", value=1))
self.assertFalse(Object(self.prog, "unsigned int", value=0))
self.assertTrue(Object(self.prog, "double", value=3.14))
self.assertFalse(Object(self.prog, "double", value=0.0))
self.assertTrue(Object(self.prog, "int *", value=0xFFFF0000))
self.assertFalse(Object(self.prog, "int *", value=0x0))
self.assertTrue(Object(self.prog, "int []", address=0))
self.assertRaisesRegex(
TypeError,
"cannot convert 'struct point' to bool",
bool,
Object(self.prog, point_type, address=0),
)
def test_int(self):
self.assertEqual(int(Object(self.prog, "int", value=-1)), -1)
self.assertEqual(int(Object(self.prog, "unsigned int", value=1)), 1)
self.assertEqual(int(Object(self.prog, "double", value=9.99)), 9)
self.assertEqual(int(Object(self.prog, "int *", value=0)), 0)
self.assertRaisesRegex(
TypeError,
r"cannot convert 'int \[\]' to int",
int,
Object(self.prog, "int []", address=0),
)
def test_float(self):
self.assertEqual(float(Object(self.prog, "int", value=-1)), -1.0)
self.assertEqual(float(Object(self.prog, "unsigned int", value=1)), 1.0)
self.assertEqual(float(Object(self.prog, "double", value=9.99)), 9.99)
self.assertRaisesRegex(
TypeError,
r"cannot convert 'int \*' to float",
float,
Object(self.prog, "int *", value=0xFFFF0000),
)
self.assertRaisesRegex(
TypeError,
r"cannot convert 'int \[\]' to float",
float,
Object(self.prog, "int []", address=0),
)
def test_index(self):
self.assertEqual(operator.index(Object(self.prog, "int", value=-1)), -1)
self.assertEqual(operator.index(Object(self.prog, "unsigned int", value=1)), 1)
self.assertEqual(operator.index(Object(self.prog, "int *", value=0)), 0)
self.assertRaisesRegex(
TypeError,
"'double' object cannot be interpreted as an integer",
operator.index,
Object(self.prog, "double", value=9.99),
)
self.assertRaisesRegex(
TypeError,
r"'int \[\]' object cannot be interpreted as an integer",
operator.index,
Object(self.prog, "int []", address=0),
)
class TestInvalidBitField(ObjectTestCase):
def test_integer(self):
self.assertRaisesRegex(
ValueError,
"bit field size is larger than type size",
Object,
self.prog,
"int",
value=0,
bit_field_size=64,
)
self.assertRaisesRegex(
ValueError,
"bit field size is larger than type size",
Object,
self.prog,
"int",
address=0,
bit_field_size=64,
)
self.assertRaisesRegex(
ValueError,
"bit field size is larger than type size",
Object,
self.prog,
"unsigned int",
value=0,
bit_field_size=64,
)
self.assertRaisesRegex(
ValueError,
"bit field size is larger than type size",
Object,
self.prog,
"unsigned int",
address=0,
bit_field_size=64,
)
def test_float(self):
self.assertRaisesRegex(
ValueError,
"bit field must be integer",
Object,
self.prog,
"float",
value=0,
bit_field_size=16,
)
self.assertRaisesRegex(
ValueError,
"bit field must be integer",
Object,
self.prog,
"float",
address=0,
bit_field_size=16,
)
def test_reference(self):
self.assertRaisesRegex(
ValueError,
"bit field must be integer",
Object,
self.prog,
point_type,
address=0,
bit_field_size=4,
)
self.assertRaisesRegex(
ValueError,
"bit field must be integer",
Object,
self.prog,
point_type,
value={},
bit_field_size=4,
)
def test_member(self):
type_ = struct_type("foo", 8, (TypeMember(point_type, "p", 0, 4),))
obj = Object(self.prog, type_, address=0)
self.assertRaisesRegex(
ValueError, "bit field must be integer", obj.member_, "p"
)
class TestCLiteral(ObjectTestCase):
def test_int(self):
self.assertEqual(Object(self.prog, value=1), Object(self.prog, "int", value=1))
self.assertEqual(
Object(self.prog, value=-1), Object(self.prog, "int", value=-1)
)
self.assertEqual(
Object(self.prog, value=2 ** 31 - 1),
Object(self.prog, "int", value=2 ** 31 - 1),
)
self.assertEqual(
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.assertEqual(
Object(self.prog, value=-(2 ** 31)),
Object(self.prog, "long", value=-(2 ** 31)),
)
self.assertEqual(
Object(self.prog, value=2 ** 63),
Object(self.prog, "unsigned long long", value=2 ** 63),
)
self.assertEqual(
Object(self.prog, value=2 ** 64 - 1),
Object(self.prog, "unsigned long long", value=2 ** 64 - 1),
)
self.assertEqual(
Object(self.prog, value=-(2 ** 64 - 1)),
Object(self.prog, "unsigned long long", value=1),
)
def test_bool(self):
self.assertEqual(
Object(self.prog, value=True), Object(self.prog, "int", value=1)
)
self.assertEqual(
Object(self.prog, value=False), Object(self.prog, "int", value=0)
)
def test_float(self):
self.assertEqual(
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 TestCIntegerPromotion(ObjectTestCase):
def test_conversion_rank_less_than_int(self):
self.assertEqual(+self.bool(False), self.int(0))
self.assertEqual(
+Object(self.prog, "char", value=1), Object(self.prog, "int", value=1)
)
self.assertEqual(
+Object(self.prog, "signed char", value=2),
Object(self.prog, "int", value=2),
)
self.assertEqual(
+Object(self.prog, "unsigned char", value=3),
Object(self.prog, "int", value=3),
)
self.assertEqual(
+Object(self.prog, "short", value=1), Object(self.prog, "int", value=1)
)
self.assertEqual(
+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.assertEqual(
+Object(self.prog, int_type("short", 4, True), value=1),
Object(self.prog, "int", value=1),
)
self.assertEqual(
+Object(self.prog, int_type("unsigned short", 4, False), value=2),
Object(self.prog, "unsigned int", value=2),
)
def test_int(self):
self.assertEqual(
+Object(self.prog, "int", value=-1), Object(self.prog, "int", value=-1)
)
self.assertEqual(
+Object(self.prog, "unsigned int", value=-1),
Object(self.prog, "unsigned int", value=-1),
)
def test_conversion_rank_greater_than_int(self):
self.assertEqual(
+Object(self.prog, "long", value=-1), Object(self.prog, "long", value=-1)
)
self.assertEqual(
+Object(self.prog, "unsigned long", value=-1),
Object(self.prog, "unsigned long", value=-1),
)
self.assertEqual(
+Object(self.prog, "long long", value=-1),
Object(self.prog, "long long", value=-1),
)
self.assertEqual(
+Object(self.prog, "unsigned long long", value=-1),
Object(self.prog, "unsigned long long", value=-1),
)
def test_extended_integer(self):
self.assertEqual(
+Object(self.prog, int_type("byte", 1, True), value=1),
Object(self.prog, "int", value=1),
)
self.assertEqual(
+Object(self.prog, int_type("ubyte", 1, False), value=-1),
Object(self.prog, "int", value=0xFF),
)
self.assertEqual(
+Object(self.prog, int_type("qword", 8, True), value=1),
Object(self.prog, int_type("qword", 8, True), value=1),
)
self.assertEqual(
+Object(self.prog, int_type("qword", 8, False), value=1),
Object(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.assertEqual(
+Object(self.prog, "int", value=1, bit_field_size=4),
Object(self.prog, "int", value=1),
)
self.assertEqual(
+Object(self.prog, "long", value=1, bit_field_size=4),
Object(self.prog, "int", value=1),
)
self.assertEqual(
+Object(self.prog, "int", value=1, bit_field_size=32),
Object(self.prog, "int", value=1),
)
self.assertEqual(
+Object(self.prog, "long", value=1, bit_field_size=32),
Object(self.prog, "int", value=1),
)
self.assertEqual(
+Object(self.prog, "unsigned int", value=1, bit_field_size=4),
Object(self.prog, "int", value=1),
)
self.assertEqual(
+Object(self.prog, "unsigned long", value=1, bit_field_size=4),
Object(self.prog, "int", value=1),
)
self.assertEqual(
+Object(self.prog, "unsigned int", value=1, bit_field_size=32),
Object(self.prog, "unsigned int", value=1),
)
self.assertEqual(
+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.assertEqual(
+Object(self.prog, "long", value=1, bit_field_size=40),
Object(self.prog, "long", value=1, bit_field_size=40),
)
self.assertEqual(
+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.assertEqual(
+Object(self.prog, color_type, value=1),
Object(self.prog, "unsigned int", value=1),
)
type_ = enum_type(
"color",
self.prog.type("unsigned long long"),
(
TypeEnumerator("RED", 0),
TypeEnumerator("GREEN", 1),
TypeEnumerator("BLUE", 2),
),
)
self.assertEqual(
+Object(self.prog, type_, value=1),
Object(self.prog, "unsigned long long", value=1),
)
type_ = enum_type(
"color",
self.prog.type("char"),
(
TypeEnumerator("RED", 0),
TypeEnumerator("GREEN", 1),
TypeEnumerator("BLUE", 2),
),
)
self.assertEqual(
+Object(self.prog, type_, value=1), Object(self.prog, "int", value=1)
)
def test_typedef(self):
type_ = typedef_type("SHORT", self.prog.type("short"))
self.assertEqual(
+Object(self.prog, type_, value=5), Object(self.prog, "int", value=5)
)
# Typedef should be preserved if the type wasn't promoted.
type_ = typedef_type("self.int", self.prog.type("int"))
self.assertEqual(
+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.assertEqual(
+Object(self.prog, "double", value=3.14),
Object(self.prog, "double", value=3.14),
)
class TestCCommonRealType(ObjectTestCase):
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.assertEqual(obj1 * obj2, expected_obj)
if commutative:
self.assertEqual(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 = 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 = int_type("int64", 8, True)
qword = 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 = int_type("int64", 8, True)
uint64 = 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 = int_type("int64", 8, True)
weirduint = 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(color_type, color_type, "unsigned int")
def test_typedef(self):
type_ = 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_ = typedef_type("LONG", self.prog.type("long"))
self.assertCommonRealType(type_, "int", type_)
class TestCOperators(ObjectTestCase):
def test_cast_array(self):
obj = Object(self.prog, "int []", address=0xFFFF0000)
self.assertEqual(
cast("int *", obj), Object(self.prog, "int *", value=0xFFFF0000)
)
self.assertEqual(
cast("void *", obj), Object(self.prog, "void *", value=0xFFFF0000)
)
self.assertEqual(
cast("unsigned long", obj),
Object(self.prog, "unsigned long", value=0xFFFF0000),
)
self.assertRaisesRegex(
TypeError, r"cannot convert 'int \*' to 'int \[2]'", cast, "int [2]", obj
)
def test_cast_function(self):
func = Object(
self.prog, function_type(void_type(), (), False), address=0xFFFF0000
)
self.assertEqual(
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.assertEqual(op(self.int(lhs), self.int(rhs)), self.int(result))
self.assertEqual(op(self.int(lhs), self.long(rhs)), self.long(result))
self.assertEqual(op(self.long(lhs), self.int(rhs)), self.long(result))
self.assertEqual(op(self.long(lhs), self.long(rhs)), self.long(result))
self.assertEqual(op(self.int(lhs), rhs), self.int(result))
self.assertEqual(op(self.long(lhs), rhs), self.long(result))
self.assertEqual(op(lhs, self.int(rhs)), self.int(result))
self.assertEqual(op(lhs, self.long(rhs)), self.long(result))
if floating_point:
self.assertEqual(
op(self.double(lhs), self.double(rhs)), self.double(result)
)
self.assertEqual(op(self.double(lhs), self.int(rhs)), self.double(result))
self.assertEqual(op(self.int(lhs), self.double(rhs)), self.double(result))
self.assertEqual(op(self.double(lhs), float(rhs)), self.double(result))
self.assertEqual(op(float(lhs), self.double(rhs)), self.double(result))
self.assertEqual(op(float(lhs), self.int(rhs)), self.double(result))
self.assertEqual(op(self.int(lhs), float(rhs)), self.double(result))
def _test_shift(self, op, lhs, rhs, result):
self.assertEqual(op(self.int(lhs), self.int(rhs)), self.int(result))
self.assertEqual(op(self.int(lhs), self.long(rhs)), self.int(result))
self.assertEqual(op(self.long(lhs), self.int(rhs)), self.long(result))
self.assertEqual(op(self.long(lhs), self.long(rhs)), self.long(result))
self.assertEqual(op(self.int(lhs), rhs), self.int(result))
self.assertEqual(op(self.long(lhs), rhs), self.long(result))
self.assertEqual(op(lhs, self.int(rhs)), self.int(result))
self.assertEqual(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, function_type(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, 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.assertEqual(ptr + self.int(1), ptr1)
self.assertEqual(self.unsigned_int(1) + ptr, ptr1)
self.assertEqual(arr + self.int(1), ptr1)
self.assertEqual(ptr1 + self.int(-1), ptr)
self.assertEqual(self.int(-1) + ptr1, ptr)
self.assertEqual(ptr + 1, ptr1)
self.assertEqual(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.assertEqual(void_ptr + self.int(1), void_ptr1)
self.assertEqual(self.unsigned_int(1) + void_ptr, void_ptr1)
self.assertEqual(void_ptr + 1, void_ptr1)
self.assertEqual(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.assertEqual(ptr1 - ptr, Object(self.prog, "ptrdiff_t", value=1))
self.assertEqual(ptr - ptr1, Object(self.prog, "ptrdiff_t", value=-1))
self.assertEqual(ptr - self.int(0), ptr)
self.assertEqual(ptr1 - self.int(1), ptr)
self.assertEqual(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.assertEqual(void_ptr1 - void_ptr, Object(self.prog, "ptrdiff_t", value=1))
self.assertEqual(void_ptr - void_ptr1, Object(self.prog, "ptrdiff_t", value=-1))
self.assertEqual(void_ptr - self.int(0), void_ptr)
self.assertEqual(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.assertEqual(self.int(2) * self.int(-3), self.int(-6))
self.assertEqual(self.int(-2) * self.int(3), self.int(-6))
self.assertEqual(self.int(-2) * self.int(-3), self.int(6))
# Integer overflow.
self.assertEqual(self.int(0x8000) * self.int(0x10000), self.int(-(2 ** 31)))
self.assertEqual(
self.unsigned_int(0x8000) * self.int(0x10000), self.unsigned_int(2 ** 31)
)
self.assertEqual(
self.unsigned_int(0xFFFFFFFF) * self.unsigned_int(0xFFFFFFFF),
self.unsigned_int(1),
)
self.assertEqual(
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.assertEqual(self.bool(True) << self.bool(True), self.int(2))
self.assertEqual(self.int(1) << self.int(32), self.int(0))
def test_rshift(self):
self._test_shift(operator.rshift, 16, 3, 2)
self.assertEqual(self.int(-2) >> self.int(1), self.int(-1))
self.assertEqual(self.int(1) >> self.int(32), self.int(0))
self.assertEqual(self.int(-1) >> self.int(32), self.int(-1))
def test_and(self):
self._test_arithmetic(operator.and_, 1, 3, 1)
self.assertEqual(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.assertEqual(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.assertEqual(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):
# TestCIntegerPromotion 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.assertEqual(-Object(self.prog, "unsigned char", value=1), self.int(-1))
self.assertEqual(-self.int(-1), self.int(1))
self.assertEqual(-self.unsigned_int(1), self.unsigned_int(0xFFFFFFFF))
self.assertEqual(
-Object(self.prog, "long", value=-0x8000000000000000),
Object(self.prog, "long", value=-0x8000000000000000),
)
self.assertEqual(-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.assertEqual(~self.int(1), self.int(-2))
self.assertEqual(
~Object(self.prog, "unsigned long long", value=-1),
Object(self.prog, "unsigned long long", value=0),
)
self.assertEqual(~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, point_type, "x")
self.assertEqual(
container_of(obj, point_type, "x"),
Object(self.prog, pointer_type(8, point_type), value=0xFFFF000C),
)
self.assertEqual(
container_of(obj, point_type, "y"),
Object(self.prog, pointer_type(8, point_type), value=0xFFFF0008),
)
self.assertEqual(
container_of(obj, line_segment_type, "a.x"),
Object(self.prog, pointer_type(8, line_segment_type), value=0xFFFF000C),
)
self.assertEqual(
container_of(obj, line_segment_type, "b.x"),
Object(self.prog, pointer_type(8, line_segment_type), value=0xFFFF0004),
)
polygon_type = struct_type(
"polygon", 0, (TypeMember(array_type(None, point_type), "points"),)
)
self.assertEqual(
container_of(obj, polygon_type, "points[3].x"),
Object(self.prog, pointer_type(8, polygon_type), value=0xFFFEFFF4),
)
small_point_type = struct_type(
"small_point",
1,
(
TypeMember(int_type("int", 4, True), "x", 0, 4),
TypeMember(int_type("int", 4, True), "y", 4, 4),
),
)
self.assertRaisesRegex(
ValueError,
r"container_of\(\) 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],
point_type,
"x",
)
self.assertRaisesRegex(
TypeError,
"not a structure, union, or class",
container_of,
obj,
obj.type_,
"x",
),
type_ = struct_type(
"foo",
16,
(
TypeMember(array_type(8, int_type("int", 4, True)), "arr"),
TypeMember(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 TestCPretty(ObjectTestCase):
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,
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_ = typedef_type("INT", int_type("int", 4, True))
self.assertEqual(str(Object(self.prog, type_, value=99)), "(INT)99")
type_ = typedef_type("INT", int_type("int", 4, True), Qualifiers.CONST)
self.assertEqual(str(Object(self.prog, type_, value=99)), "(const INT)99")
type_ = typedef_type("CINT", int_type("int", 4, True, 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)
)
prog = mock_program(
segments=[MockMemorySegment(segment, virt_addr=0xFFFF0000),],
types=[point_type],
)
obj = Object(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_ = struct_type(
"foo",
16,
(
TypeMember(point_type, "point"),
TypeMember(
struct_type(
None,
8,
(
TypeMember(int_type("int", 4, True), "bar"),
TypeMember(int_type("int", 4, True), "baz", 32),
),
),
None,
64,
),
),
)
obj = Object(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)
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)
)
prog = mock_program(
segments=[MockMemorySegment(segment, virt_addr=0xFFFF0000),]
)
type_ = struct_type(
"foo",
32,
(
TypeMember(
struct_type(
"long_point",
16,
(
TypeMember(int_type("long", 8, True), "x"),
TypeMember(int_type("long", 8, True), "y", 64),
),
),
"point",
),
TypeMember(int_type("long", 8, True), "bar", 128),
TypeMember(int_type("long", 8, True), "baz", 192),
),
)
obj = Object(prog, type_, address=0xFFFF0000)
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_ = struct_type("foo", 0, ())
self.assertEqual(str(Object(prog, type_, address=0)), "(struct foo){}")
obj = Object(prog, 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(prog, 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):
segment = b"\x07\x10\x5e\x5f\x1f\0\0\0"
prog = mock_program(
segments=[MockMemorySegment(segment, virt_addr=0xFFFF0000),]
)
type_ = struct_type(
"bits",
8,
(
TypeMember(int_type("int", 4, True), "x", 0, 4),
TypeMember(int_type("int", 4, True, Qualifiers.CONST), "y", 4, 28),
TypeMember(int_type("int", 4, True), "z", 32, 5),
),
)
obj = Object(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):
segment = b"\0\0\x80?"
prog = mock_program(
segments=[MockMemorySegment(segment, virt_addr=0xFFFF0000),],
types=[option_type],
)
self.assertEqual(
str(Object(prog, "union option", address=0xFFFF0000)),
"""\
(union option){
.i = (int)1065353216,
.f = (float)1.0,
}""",
)
def test_enum(self):
self.assertEqual(str(Object(self.prog, color_type, value=0)), "(enum color)RED")
self.assertEqual(
str(Object(self.prog, color_type, value=1)), "(enum color)GREEN"
)
self.assertEqual(str(Object(self.prog, color_type, value=4)), "(enum color)4")
obj = Object(self.prog, enum_type("color"), address=0)
self.assertRaisesRegex(TypeError, "cannot format incomplete enum", str, obj)
def test_pointer(self):
prog = mock_program(
segments=[
MockMemorySegment((99).to_bytes(4, "little"), virt_addr=0xFFFF0000),
]
)
obj = Object(prog, "int *", value=0xFFFF0000)
self.assertEqual(str(obj), "*(int *)0xffff0000 = 99")
self.assertEqual(obj.format_(dereference=False), "(int *)0xffff0000")
self.assertEqual(
str(Object(prog, "int *", value=0x7FFFFFFF)), "(int *)0x7fffffff"
)
def test_void_pointer(self):
prog = mock_program(
segments=[
MockMemorySegment((99).to_bytes(8, "little"), virt_addr=0xFFFF0000),
]
)
self.assertEqual(
str(Object(prog, "void *", value=0xFFFF0000)), "(void *)0xffff0000"
)
def test_pointer_typedef(self):
prog = mock_program(
segments=[
MockMemorySegment(
(0xFFFF00F0).to_bytes(8, "little"), virt_addr=0xFFFF0000
),
]
)
type_ = typedef_type("HANDLE", pointer_type(8, pointer_type(8, void_type())))
self.assertEqual(
str(Object(prog, type_, value=0xFFFF0000)),
"*(HANDLE)0xffff0000 = 0xffff00f0",
)
# TODO: test symbolize.
def test_c_string(self):
prog = mock_program(
segments=[
MockMemorySegment(b"hello\0", virt_addr=0xFFFF0000),
MockMemorySegment(b"unterminated", virt_addr=0xFFFF0010),
MockMemorySegment(b'"escape\tme\\\0', virt_addr=0xFFFF0020),
]
)
obj = Object(prog, "char *", value=0xFFFF0000)
self.assertEqual(str(obj), '(char *)0xffff0000 = "hello"')
self.assertEqual(obj.format_(string=False), "*(char *)0xffff0000 = 104")
self.assertEqual(str(Object(prog, "char *", value=0x0)), "(char *)0x0")
self.assertEqual(
str(Object(prog, "char *", value=0xFFFF0010)), "(char *)0xffff0010"
)
self.assertEqual(
str(Object(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"))
prog = mock_program(
segments=[MockMemorySegment(segment, virt_addr=0xFFFF0000),]
)
obj = Object(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"))
prog = mock_program(
segments=[MockMemorySegment(segment, virt_addr=0xFFFF0000),]
)
obj = Object(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"))
prog = mock_program(
segments=[MockMemorySegment(segment, virt_addr=0xFFFF0000),]
)
type_ = struct_type(
None, 20, (TypeMember(array_type(5, int_type("int", 4, True)), "arr"),)
)
obj = Object(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"))
prog = mock_program(
segments=[MockMemorySegment(segment, virt_addr=0xFFFF0000),],
types=[point_type],
)
obj = Object(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)
prog = mock_program(
segments=[MockMemorySegment(segment, virt_addr=0xFFFF0000),],
types=[point_type, struct_type("empty", 0, ()),],
)
obj = Object(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(prog, "struct point [2]", address=0xFFFF0000)
self.assertEqual(
str(obj),
"""\
(struct point [2]){
{
.x = (int)0,
.y = (int)99,
},
}""",
)
obj = Object(prog, "struct empty [2]", address=0)
self.assertEqual(str(obj), "(struct empty [2]){}")
def test_char_array(self):
segment = bytearray(16)
prog = mock_program(
segments=[MockMemorySegment(segment, virt_addr=0xFFFF0000),]
)
obj = Object(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(prog, "char [0]", address=0xFFFF0000)), "(char [0]){}"
)
self.assertEqual(
str(Object(prog, "char []", address=0xFFFF0000)), "(char []){}"
)
def test_function(self):
obj = Object(
self.prog, function_type(void_type(), (), False), address=0xFFFF0000
)
self.assertEqual(str(obj), "(void (void))0xffff0000")
class TestGenericOperators(ObjectTestCase):
def setUp(self):
super().setUp()
self.prog = mock_program(
segments=[
MockMemorySegment(
b"".join(i.to_bytes(4, "little") for i in range(4)),
virt_addr=0xFFFF0000,
),
]
)
def test_len(self):
self.assertEqual(len(Object(self.prog, "int [0]", address=0)), 0)
self.assertEqual(len(Object(self.prog, "int [10]", address=0)), 10)
self.assertRaisesRegex(
TypeError, "'int' has no len()", len, Object(self.prog, "int", address=0)
)
self.assertRaisesRegex(
TypeError,
r"'int \[\]' has no len()",
len,
Object(self.prog, "int []", address=0),
)
def test_address_of(self):
obj = Object(self.prog, "int", address=0xFFFF0000)
self.assertEqual(
obj.address_of_(), Object(self.prog, "int *", value=0xFFFF0000)
)
obj = obj.read_()
self.assertRaisesRegex(
ValueError, "cannot take address of value", obj.address_of_
)
obj = Object(self.prog, "int", address=0xFFFF0000, bit_field_size=4)
self.assertRaisesRegex(
ValueError, "cannot take address of bit field", obj.address_of_
)
obj = Object(self.prog, "int", address=0xFFFF0000, bit_offset=4)
self.assertRaisesRegex(
ValueError, "cannot take address of bit field", obj.address_of_
)
def test_subscript(self):
arr = Object(self.prog, "int [4]", address=0xFFFF0000)
incomplete_arr = Object(self.prog, "int []", address=0xFFFF0000)
ptr = Object(self.prog, "int *", value=0xFFFF0000)
for obj in [arr, incomplete_arr, ptr]:
for i in range(5):
self.assertEqual(
obj[i], Object(self.prog, "int", address=0xFFFF0000 + 4 * i)
)
if i < 4:
self.assertEqual(obj[i].read_(), Object(self.prog, "int", value=i))
else:
self.assertRaises(FaultError, obj[i].read_)
obj = arr.read_()
for i in range(4):
self.assertEqual(obj[i], Object(self.prog, "int", value=i))
self.assertRaisesRegex(OutOfBoundsError, "out of bounds", obj.__getitem__, 4)
obj = Object(self.prog, "int", value=0)
self.assertRaises(TypeError, obj.__getitem__, 0)
def test_cast_primitive_value(self):
obj = Object(self.prog, "long", value=2 ** 32 + 1)
self.assertEqual(cast("int", obj), Object(self.prog, "int", value=1))
self.assertEqual(cast("int", obj.read_()), Object(self.prog, "int", value=1))
self.assertEqual(
cast("const int", Object(self.prog, "int", value=1)),
Object(self.prog, "const int", value=1),
)
self.assertRaisesRegex(
TypeError,
"cannot convert 'int' to 'struct point'",
cast,
point_type,
Object(self.prog, "int", value=1),
)
def test_cast_compound_value(self):
obj = Object(self.prog, point_type, address=0xFFFF0000).read_()
self.assertEqual(cast(point_type, obj), obj)
const_point_type = point_type.qualified(Qualifiers.CONST)
self.assertEqual(
cast(const_point_type, obj),
Object(self.prog, const_point_type, address=0xFFFF0000).read_(),
)
self.assertRaisesRegex(
TypeError,
"cannot convert 'struct point' to 'enum color'",
cast,
color_type,
obj,
)
def test_cast_invalid(self):
obj = Object(self.prog, "int", value=1)
self.assertRaisesRegex(TypeError, "cannot cast to void type", cast, "void", obj)
def test_reinterpret_reference(self):
obj = Object(self.prog, "int", address=0xFFFF0000)
self.assertEqual(reinterpret("int", obj), obj)
self.assertEqual(
reinterpret("int", obj, byteorder="big"),
Object(self.prog, "int", address=0xFFFF0000, byteorder="big"),
)
obj = Object(self.prog, "int []", address=0xFFFF0000)
self.assertEqual(
reinterpret("int [4]", obj),
Object(self.prog, "int [4]", address=0xFFFF0000),
)
def test_reinterpret_value(self):
segment = (1).to_bytes(4, "little") + (2).to_bytes(4, "little")
prog = mock_program(
segments=[MockMemorySegment(segment, virt_addr=0xFFFF0000),],
types=[
point_type,
struct_type(
"foo", 8, (TypeMember(int_type("long", 8, True), "counter"),)
),
],
)
obj = Object(prog, "struct point", address=0xFFFF0000).read_()
self.assertEqual(
reinterpret("struct foo", obj),
Object(prog, "struct foo", address=0xFFFF0000).read_(),
)
self.assertEqual(
reinterpret(obj.type_, obj, byteorder="big"),
Object(prog, "struct point", address=0xFFFF0000, byteorder="big").read_(),
)
self.assertEqual(reinterpret("int", obj), Object(prog, "int", value=1))
def test_member(self):
reference = Object(self.prog, point_type, address=0xFFFF0000)
unnamed_reference = Object(
self.prog,
struct_type(
"point",
8,
(TypeMember(struct_type(None, 8, point_type.members), None),),
),
address=0xFFFF0000,
)
ptr = Object(self.prog, pointer_type(8, point_type), value=0xFFFF0000)
for obj in [reference, unnamed_reference, ptr]:
self.assertEqual(
obj.member_("x"), Object(self.prog, "int", address=0xFFFF0000)
)
self.assertEqual(obj.member_("x"), obj.x)
self.assertEqual(
obj.member_("y"), Object(self.prog, "int", address=0xFFFF0004)
)
self.assertEqual(obj.member_("y"), obj.y)
self.assertRaisesRegex(
LookupError, "'struct point' has no member 'z'", obj.member_, "z"
)
self.assertRaisesRegex(
AttributeError, "'struct point' has no member 'z'", getattr, obj, "z"
)
obj = reference.read_()
self.assertEqual(obj.x, Object(self.prog, "int", value=0))
self.assertEqual(obj.y, Object(self.prog, "int", value=1))
obj = Object(self.prog, "int", value=1)
self.assertRaisesRegex(
TypeError, "'int' is not a structure, union, or class", obj.member_, "x"
)
self.assertRaisesRegex(AttributeError, "no attribute", getattr, obj, "x")
def test_bit_field_member(self):
segment = b"\x07\x10\x5e\x5f\x1f\0\0\0"
prog = mock_program(
segments=[MockMemorySegment(segment, virt_addr=0xFFFF0000),]
)
type_ = struct_type(
"bits",
8,
(
TypeMember(int_type("int", 4, True), "x", 0, 4),
TypeMember(int_type("int", 4, True, Qualifiers.CONST), "y", 4, 28),
TypeMember(int_type("int", 4, True), "z", 32, 5),
),
)
obj = Object(prog, type_, address=0xFFFF0000)
self.assertEqual(
obj.x,
Object(
prog, int_type("int", 4, True), address=0xFFFF0000, bit_field_size=4
),
)
self.assertEqual(
obj.y,
Object(
prog,
int_type("int", 4, True, Qualifiers.CONST),
address=0xFFFF0000,
bit_field_size=28,
bit_offset=4,
),
)
self.assertEqual(
obj.z,
Object(
prog, int_type("int", 4, True), address=0xFFFF0004, bit_field_size=5
),
)
def test_member_out_of_bounds(self):
obj = Object(
self.prog, struct_type("foo", 4, point_type.members), address=0xFFFF0000
).read_()
self.assertRaisesRegex(OutOfBoundsError, "out of bounds", getattr, obj, "y")
def test_string(self):
prog = mock_program(
segments=[
MockMemorySegment(
b"\x00\x00\xff\xff\x00\x00\x00\x00", virt_addr=0xFFFEFFF8
),
MockMemorySegment(b"hello\0world\0", virt_addr=0xFFFF0000),
]
)
strings = [
(Object(prog, "char *", address=0xFFFEFFF8), b"hello"),
(Object(prog, "char [2]", address=0xFFFF0000), b"he"),
(Object(prog, "char [8]", address=0xFFFF0000), b"hello"),
]
for obj, expected in strings:
with self.subTest(obj=obj):
self.assertEqual(obj.string_(), expected)
self.assertEqual(obj.read_().string_(), expected)
strings = [
Object(prog, "char []", address=0xFFFF0000),
Object(prog, "int []", address=0xFFFF0000),
Object(prog, "int [2]", address=0xFFFF0000),
Object(prog, "int *", value=0xFFFF0000),
]
for obj in strings:
self.assertEqual(obj.string_(), b"hello")
self.assertRaisesRegex(
TypeError,
"must be an array or pointer",
Object(prog, "int", value=1).string_,
)
class TestSpecialMethods(ObjectTestCase):
def test_dir(self):
obj = Object(self.prog, "int", value=0)
self.assertEqual(dir(obj), sorted(object.__dir__(obj)))
obj = Object(self.prog, point_type, address=0xFFFF0000)
self.assertEqual(dir(obj), sorted(object.__dir__(obj) + ["x", "y"]))
self.assertEqual(dir(obj.address_of_()), dir(obj))
def test_round(self):
for func in [round, math.trunc, math.floor, math.ceil]:
for value in [0.0, -0.0, -0.4, 0.4, 0.5, -0.5, 0.6, -0.6, 1.0, -1.0]:
self.assertEqual(
func(Object(self.prog, "double", value=value)), func(value)
)
self.assertEqual(
func(Object(self.prog, "int", value=value)), func(int(value))
)
self.assertEqual(
round(Object(self.prog, "int", value=1), 2),
Object(self.prog, "int", value=1),
)
self.assertEqual(
round(Object(self.prog, "double", value=0.123), 2),
Object(self.prog, "double", value=0.12),
)
def test_iter(self):
obj = Object(self.prog, "int [4]", value=[0, 1, 2, 3])
for i, element in enumerate(obj):
self.assertEqual(element, Object(self.prog, "int", value=i))
self.assertEqual(operator.length_hint(iter(obj)), 4)
self.assertRaisesRegex(
TypeError, "'int' is not iterable", iter, Object(self.prog, "int", value=0)
)
self.assertRaisesRegex(
TypeError,
r"'int \[\]' is not iterable",
iter,
Object(self.prog, "int []", address=0),
)