drgn/tests/test_object.py
Omar Sandoval 55a9700435 libdrgn: python: accept integer-like arguments in more places
There are a few places (e.g., Program.symbol(), Program.read()) where it
makes sense to accept, e.g., a drgn.Object with integer type. Replace
index_arg() with a converter function and use it everywhere that we use
the "K" format for PyArg_Parse*.
2019-10-15 21:10:11 -07:00

1877 lines
78 KiB
Python

import math
import operator
import struct
from drgn import (
FaultError,
Object,
Qualifiers,
Type,
array_type,
cast,
container_of,
enum_type,
float_type,
function_type,
int_type,
pointer_type,
reinterpret,
struct_type,
typedef_type,
union_type,
void_type,
)
from tests import (
MockMemorySegment,
ObjectTestCase,
color_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', value=0, address=0)
def test_integer_address(self):
self.assertRaises(TypeError, Object, self.prog, 'int', address='NULL')
def test_byteorder(self):
self.assertRaisesRegex(ValueError,
"byteorder must be 'little', 'big', or None",
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')")
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)")
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_read_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})
type_ = struct_type('foo', 16, (
(point_type, 'point'),
(struct_type(None, 8, (
(int_type('int', 4, True), 'bar'),
(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_read_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])
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_)
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_)
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_)
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_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, (
(int_type('short', 2, True), 'a'),
# Straddles the end of the structure.
(int_type('int', 4, True), 'b', 16),
# Beyond the end of the structure.
(int_type('int', 4, True), 'c', 32),
))
Object(self.prog, invalid_struct, value={'a': 0})
self.assertRaisesRegex(FaultError, 'out of bounds of value', Object,
self.prog, invalid_struct,
value={'a': 0, 'b': 4})
self.assertRaisesRegex(FaultError, '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, "cannot convert 'double' to index",
operator.index, Object(self.prog, 'double', value=9.99))
self.assertRaisesRegex(TypeError, r"cannot convert 'int \[\]' to index",
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, (
(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'),
(('RED', 0), ('GREEN', 1), ('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'),
(('RED', 0), ('GREEN', 1), ('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(-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, (
(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, (
(int_type('int', 4, True), 'x', 0, 4),
(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 or union',
container_of, obj, obj.type_, 'x'),
type_ = struct_type('foo', 16, (
(array_type(8, int_type('int', 4, True)), 'arr'),
(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):
self.assertEqual(str(Object(self.prog, 'int', value=99)), '(int)99')
self.assertEqual(str(Object(self.prog, 'const int', value=-99)),
'(const int)-99')
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])
self.assertEqual(str(Object(prog, 'struct point', address=0xffff0000)),
"""\
(struct point){
.x = (int)99,
.y = (int)-1,
}""")
type_ = struct_type('foo', 16, (
(point_type, 'point'),
(struct_type(None, 8, (
(int_type('int', 4, True), 'bar'),
(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, (
(struct_type('long_point', 16, (
(int_type('long', 8, True), 'x'),
(int_type('long', 8, True), 'y', 64),
)), 'point'),
(int_type('long', 8, True), 'bar', 128),
(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){}')
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, (
(int_type('int', 4, True), 'x', 0, 4),
(int_type('int', 4, True, Qualifiers.CONST), 'y', 4, 28),
(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),
])
self.assertEqual(str(Object(prog, 'int *', value=0xffff0000)),
'*(int *)0xffff0000 = 99')
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')
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),
])
self.assertEqual(str(Object(prog, 'char *', value=0xffff0000)),
'(char *)0xffff0000 = "hello"')
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(f'{obj:.27}', str(obj))
for columns in range(22, 27):
self.assertEqual(f'{obj:.{columns}}', """\
(int [5]){
0, 1, 2, 3, 4,
}""")
for columns in range(19, 22):
self.assertEqual(f'{obj:.{columns}}', """\
(int [5]){
0, 1, 2, 3,
4,
}""")
for columns in range(16, 19):
self.assertEqual(f'{obj:.{columns}}', """\
(int [5]){
0, 1, 2,
3, 4,
}""")
for columns in range(13, 16):
self.assertEqual(f'{obj:.{columns}}', """\
(int [5]){
0, 1,
2, 3,
4,
}""")
for columns in range(13):
self.assertEqual(f'{obj:.{columns}}', """\
(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(f'{obj:.55}', str(obj))
for columns in range(47, 55):
self.assertEqual(f'{obj:.{columns}}', """\
(int [2][5]){
{ 0, 1, 2, 3, 4, }, { 5, 6, 7, 8, 9, },
}""")
for columns in range(27, 47):
self.assertEqual(f'{obj:.{columns}}', """\
(int [2][5]){
{ 0, 1, 2, 3, 4, },
{ 5, 6, 7, 8, 9, },
}""")
for columns in range(24, 27):
self.assertEqual(f'{obj:.{columns}}', """\
(int [2][5]){
{
0, 1, 2,
3, 4,
},
{
5, 6, 7,
8, 9,
},
}""")
for columns in range(21, 24):
self.assertEqual(f'{obj:.{columns}}', """\
(int [2][5]){
{
0, 1,
2, 3,
4,
},
{
5, 6,
7, 8,
9,
},
}""")
for columns in range(21):
self.assertEqual(f'{obj:.{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, (
(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(f'{obj:.43}', str(obj))
self.assertEqual(f'{obj:.42}', """\
(struct <anonymous>){
.arr = (int [5]){
0, 1, 2, 3, 4,
},
}""")
self.assertEqual(f'{obj:.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]){}')
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(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(FaultError, '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, ((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, (
(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 or union",
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, (
(int_type('int', 4, True), 'x', 0, 4),
(int_type('int', 4, True, Qualifiers.CONST), 'y', 4, 28),
(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(FaultError, '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_format(self):
obj = Object(self.prog, 'int', value=0)
obj.__format__('.10')
self.assertRaisesRegex(ValueError,
'Format specifier can only include precision',
obj.__format__, '10')
self.assertRaisesRegex(ValueError,
'Format specifier can only include precision',
obj.__format__, '.10 ')
self.assertRaisesRegex(ValueError,
'Format specifier precision must be non-negative',
obj.__format__, '.-10')
self.assertRaisesRegex(OverflowError,
'Format specifier precision is too large',
obj.__format__, f'.{2**128}')
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))