mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-23 09:43:06 +00:00
55a9700435
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*.
1877 lines
78 KiB
Python
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))
|