type: make offsetof() and typeof() accept arbitrary member designators

Instead of only accepting an identifier.
This commit is contained in:
Omar Sandoval 2018-05-13 00:42:50 -07:00
parent 8f46673649
commit 1916528f5d
2 changed files with 59 additions and 9 deletions

View File

@ -19,8 +19,19 @@ import numbers
import re
import struct
import sys
from typing import Any, Callable, Dict, FrozenSet, List, Optional, Tuple, Union
from typing import (
Any,
Callable,
Dict,
FrozenSet,
List,
Optional,
Tuple,
Union,
cast,
)
from drgn.memberdesignator import parse_member_designator
from drgn.typename import (
ArrayTypeName,
BasicTypeName,
@ -530,6 +541,28 @@ class CompoundType(Type):
"""
return list(self._members_by_name)
def _member(self, member: str) -> Tuple[Type, int]:
designator = parse_member_designator(member)
type_: Type = self
offset = 0
for op, value in designator:
real_type = type_.real_type()
if op == '.':
if not isinstance(real_type, CompoundType):
raise ValueError('{str(type_.type_name())!r} is not a struct or union')
try:
member_offset, type_thunk = real_type._members_by_name[cast(str, value)]
except KeyError:
raise ValueError(f'{str(type_.type_name())!r} has no member {value!r}') from None
type_ = type_thunk()
offset += member_offset
else: # op == '[]'
if not isinstance(real_type, ArrayType):
raise ValueError('{str(type_.type_name())!r} is not an array')
type_ = real_type.type
offset += cast(int, value) * type_.sizeof()
return type_, offset
def offsetof(self, member: str) -> int:
"""
Return offsetof(type, member).
@ -537,10 +570,7 @@ class CompoundType(Type):
>>> print(prog['init_task'].fs.root.type_.offsetof('dentry'))
8
"""
try:
return self._members_by_name[member][0]
except KeyError:
raise ValueError(f'{str(self.type_name())!r} has no member {member!r}') from None
return self._member(member)[1]
def typeof(self, member: str) -> Type:
"""
@ -549,10 +579,7 @@ class CompoundType(Type):
>>> print(prog['init_task'].fs.root.type_.typeof('dentry'))
struct dentry *
"""
try:
return self._members_by_name[member][1]()
except KeyError:
raise ValueError(f'{str(self.type_name())!r} has no member {member!r}') from None
return self._member(member)[0]
class StructType(CompoundType):

View File

@ -67,6 +67,9 @@ line_segment_type = StructType('line_segment', 16, [
('a', 0, lambda: point_type),
('b', 8, lambda: point_type),
])
quadrilateral_type = StructType('quadrilateral', 16, [
('points', 0, lambda: ArrayType(point_type, 4)),
])
pointer_size = ctypes.sizeof(ctypes.c_void_p)
@ -212,6 +215,26 @@ struct line_segment {
struct point a;
struct point b;
}""")
self.assertEqual(line_segment_type.offsetof('a.x'), 0)
self.assertEqual(line_segment_type.offsetof('a.y'), 4)
self.assertEqual(line_segment_type.offsetof('b.x'), 8)
self.assertEqual(line_segment_type.offsetof('b.y'), 12)
self.assertRaisesRegex(ValueError, 'no member',
line_segment_type.offsetof, 'c')
self.assertRaisesRegex(ValueError, 'not a struct or union',
line_segment_type.offsetof, 'a.x.z')
self.assertRaisesRegex(ValueError, 'not an array',
line_segment_type.offsetof, 'a[0]')
self.assertEqual(str(quadrilateral_type), """\
struct quadrilateral {
struct point points[4];
}""")
for i in range(5):
self.assertEqual(quadrilateral_type.offsetof(f'points[{i}].x'),
8 * i)
self.assertEqual(quadrilateral_type.offsetof(f'points[{i}].y'),
8 * i + 4)
self.assertEqual(str(anonymous_point_type), """\
struct {