diff --git a/amitools/vamos/astructs/array.py b/amitools/vamos/astructs/array.py index ad971b2b..ed85a4e1 100644 --- a/amitools/vamos/astructs/array.py +++ b/amitools/vamos/astructs/array.py @@ -23,6 +23,8 @@ def __init__(self, mem, addr, **kwargs): def get(self, index): """Return n-th element in array""" + if type(index) is str: + index = int(index) entry_addr = self._get_entry_addr(index) cls_type = self._element_type.get_alias_type() return cls_type(self._mem, entry_addr) @@ -34,6 +36,17 @@ def _get_entry_addr(self, index): def __getitem__(self, key): return self.get(key) + def get_path(self, path): + if len(path) == 0: + return self + # allow and index '[num]' + arg = self._get_path_arg(path) + if arg: + index = int(arg) + sub_obj = self.get(index) + sub_path = self._skip_path_arg(path, arg) + return sub_obj.get_path(sub_path) + class ArrayIter: def __init__(self, array): diff --git a/amitools/vamos/astructs/astruct.py b/amitools/vamos/astructs/astruct.py index 257564f4..9da870ba 100644 --- a/amitools/vamos/astructs/astruct.py +++ b/amitools/vamos/astructs/astruct.py @@ -401,3 +401,13 @@ def __setattr__(self, field_name, val): "Field {} is read-only in {}".format(field_name, self) ) super().__setattr__(field_name, val) + + def get_path(self, path): + if len(path) == 0: + return self + field = self._get_next_path_field(path) + if field: + sub_obj = self.get(field) + if sub_obj is not None: + sub_path = self._skip_path_field(path, field) + return sub_obj.get_path(sub_path) diff --git a/amitools/vamos/astructs/typebase.py b/amitools/vamos/astructs/typebase.py index 5c8b3531..d433c606 100644 --- a/amitools/vamos/astructs/typebase.py +++ b/amitools/vamos/astructs/typebase.py @@ -63,6 +63,11 @@ def __init__( if reg: assert cpu + def clone(self, cls): + """clone type into a new class""" + kw_args = {"alloc": self._cpu, "mem_obj": self._mem_obj} + return cls(self._mem, self._addr, **kw_args) + def __eq__(self, other): if type(other) is int: return self._addr == other @@ -71,6 +76,10 @@ def __eq__(self, other): else: return NotImplemented + def __bool__(self): + # type objects are always true, so "if obj" works reliably + return True + def get_addr(self): """if type is bound to memory then return address otherwise None.""" return self._addr @@ -94,6 +103,54 @@ def get_base_offset(self): """if type is embedded in a sub structure then return its global offset""" return self._base_offset + def get_path(self, path): + """find object along the given path""" + # by default the path has to be empty + assert len(path) == 0 + return self + + def _get_next_path_field(self, path): + """split next field from path + + return field + """ + # arguments in field are surrounded in '[' and ']' + pos = path.find("[") + if pos != -1: + path = path[:pos] + # fields are separated by '.' + pos = path.find(".") + if pos != -1: + path = path[:pos] + return path + + def _skip_path_field(self, path, field): + n = len(field) + if path == field: + return "" + if path[n] == ".": + return path[n + 1 :] + else: + return path[n:] + + def _get_path_arg(self, path): + """return arg or None""" + if len(path) == 0: + return path + if path[0] == "[": + pos = path.find("]") + if pos != -1: + return path[1:pos] + else: + # error: no closing bracket + return None + else: + return None + + def _skip_path_arg(self, path, arg): + # skip '[...].' + return path[len(arg) + 3 :] + def __getattr__(self, key): if key == "addr": return self._addr diff --git a/amitools/vamos/libtypes/__init__.py b/amitools/vamos/libtypes/__init__.py index 8746bf6b..ef8424c7 100644 --- a/amitools/vamos/libtypes/__init__.py +++ b/amitools/vamos/libtypes/__init__.py @@ -10,6 +10,3 @@ # dos from .lock import FileLock, FileHandle from .process import CLI, Process, PathList - -# type promotion -from .promote import promote_type diff --git a/amitools/vamos/libtypes/list_.py b/amitools/vamos/libtypes/list_.py index 75748edc..8500d5da 100644 --- a/amitools/vamos/libtypes/list_.py +++ b/amitools/vamos/libtypes/list_.py @@ -4,9 +4,10 @@ class ListIter(object): - def __init__(self, alist, start_node=None): + def __init__(self, alist, start_node=None, promote=False): self.alist = alist self.mem = self.alist._mem + self.promote = promote if start_node is None: self.node = alist._head.succ.ref else: @@ -21,7 +22,10 @@ def __next__(self): raise StopIteration() res = self.node self.node = succ - return res + if self.promote: + return res.promote_type() + else: + return res class ListBase: @@ -114,6 +118,9 @@ def __init__(self, mem, addr, **kwargs): self._head = Node(mem, self.addr) self._tail = Node(mem, self.addr + 4) + def __iter__(self): + return ListIter(self, promote=True) + def __str__(self): return "[List:@%06x,h=%06x,t=%06x,tp=%06x,%s]" % ( self.addr, @@ -123,6 +130,18 @@ def __str__(self): self.type, ) + def get_path(self, path): + # allow to search list via arg + if len(path) == 0: + return self + # arg? + arg = self._get_path_arg(path) + if arg: + sub_obj = self.find_name(arg) + if sub_obj: + sub_path = self._skip_path_arg(path, arg) + return sub_obj.get_path(sub_path) + # ----- list ops ----- def new_list(self, lt): diff --git a/amitools/vamos/libtypes/node.py b/amitools/vamos/libtypes/node.py index ebb2310a..3cbe37f0 100644 --- a/amitools/vamos/libtypes/node.py +++ b/amitools/vamos/libtypes/node.py @@ -1,5 +1,15 @@ -from amitools.vamos.libstructs import NodeStruct, MinNodeStruct +from amitools.vamos.libstructs import NodeStruct, NodeType, MinNodeStruct, LibraryStruct from amitools.vamos.astructs import AmigaClassDef +from amitools.vamos.libtypes.process import Process +from amitools.vamos.libtypes.task import Task + + +node_map = { + NodeType.NT_TASK: Task, + NodeType.NT_PROCESS: Process, + NodeType.NT_DEVICE: LibraryStruct, + NodeType.NT_LIBRARY: LibraryStruct, +} class NodeBase: @@ -43,6 +53,15 @@ def __str__(self): # ----- node ops ----- + def promote_type(self): + """convert objects according to Amiga rules""" + node_type = self.type.get() + if node_type in node_map: + node_cls = node_map[node_type] + return self.clone(node_cls) + else: + return self + def find_name(self, name): """find name after this node""" succ = self.succ.ref diff --git a/amitools/vamos/libtypes/promote.py b/amitools/vamos/libtypes/promote.py deleted file mode 100644 index 2c3faff9..00000000 --- a/amitools/vamos/libtypes/promote.py +++ /dev/null @@ -1,33 +0,0 @@ -from amitools.vamos.libstructs import NodeType, LibraryStruct -from .node import Node -from .task import Task -from .process import Process - -node_map = { - NodeType.NT_TASK: Task, - NodeType.NT_PROCESS: Process, - NodeType.NT_DEVICE: LibraryStruct, - NodeType.NT_LIBRARY: LibraryStruct, -} - - -def promote_type(obj): - """convert objects according to Amiga rules - - * if its a node then use the node type to derive the actual class - * if its a task but of type process - """ - if type(obj) is Node: - node_type = obj.type.get() - if node_type in node_map: - node_cls = node_map[node_type] - return obj.clone(node_cls) - - # promote a task to a process - elif type(obj) is Task: - node_type = obj.node.type.get() - if node_type == NodeType.NT_PROCESS: - return obj.clone(Process) - - # no promotion applied - return obj diff --git a/amitools/vamos/libtypes/task.py b/amitools/vamos/libtypes/task.py index 10cafcb7..118dc9c7 100644 --- a/amitools/vamos/libtypes/task.py +++ b/amitools/vamos/libtypes/task.py @@ -1,5 +1,6 @@ from amitools.vamos.libstructs import TaskStruct, NodeType, TaskState from amitools.vamos.astructs import AmigaClassDef +from amitools.vamos.libtypes.process import Process @AmigaClassDef @@ -12,3 +13,11 @@ def new_task(self, pri=0, flags=0, nt=NodeType.NT_TASK): self.flags.val = flags self.state.val = TaskState.TS_INVALID self.mem_entry.new_list(NodeType.NT_MEMORY) + + def promote_type(self): + """promote task to process if type is set accordingly""" + node_type = self.node.type.get() + if node_type == NodeType.NT_PROCESS: + return self.clone(Process) + else: + return self diff --git a/amitools/vamos/tools/state.py b/amitools/vamos/tools/state.py index fd8a932e..d1377852 100644 --- a/amitools/vamos/tools/state.py +++ b/amitools/vamos/tools/state.py @@ -3,6 +3,7 @@ from .tool import Tool from amitools.vamos.astructs import TypeDumper from amitools.vamos.libstructs import ExecLibraryStruct +from amitools.vamos.libtypes import List, MinList from amitools.vamos.machine.mock import MockMemory, MultiMockMemory, DummyMockMemory from amitools.state import ASFFile, ASFParser import amitools.rom @@ -25,7 +26,10 @@ def add_args(self, arg_parser): # show parser = sub.add_parser("show", help="show data structures in RAM") parser.add_argument("file", help="UAE state file (.uss)") - parser.add_argument("-r", "--rom", nargs="+", help="rom files", default=[]) + parser.add_argument("what", nargs="?", help="what to show") + parser.add_argument( + "-r", "--rom", action="append", help="rom files", default=[] + ) def run(self, args): type_cmd = args.type_cmd @@ -104,8 +108,27 @@ def show_cmd(self, args): mem.add(m) mem.add(DummyMockMemory()) - # dump exec library + # get exec library exec_base = mem.r32(4) exec_lib = ExecLibraryStruct(mem, exec_base) + + # find sub object + obj = exec_lib + if args.what: + obj = obj.get_path(args.what) + if obj is None: + print(f"can't resolve path '{args.what}'") + return 1 + dumper = TypeDumper() - dumper.dump_obj(exec_lib) + if type(obj) in (List, MinList): + # dump list + if len(obj) == 0: + print("Empty list") + else: + for elem in obj: + dumper.dump_obj(elem) + else: + # dump object + dumper.dump_obj(obj) + return 0 diff --git a/test/unit/astructs_array.py b/test/unit/astructs_array.py index 71b8ee70..30149c36 100644 --- a/test/unit/astructs_array.py +++ b/test/unit/astructs_array.py @@ -27,6 +27,10 @@ def astructs_array_test(): a[0].val = 23 assert a[0].val == 23 assert a.get(0).val == 23 + # path + assert a.get_path("") is a + assert a.get_path("[0]") == a.get(0) + assert a.get_path("[9]") == a.get(9) def astructs_array_iter_test(): diff --git a/test/unit/astructs_astruct.py b/test/unit/astructs_astruct.py index 14488ec3..94fa3eb9 100644 --- a/test/unit/astructs_astruct.py +++ b/test/unit/astructs_astruct.py @@ -101,6 +101,9 @@ def astructs_astruct_base_inst_test(): # try to assign field directly -> forbidden! with pytest.raises(AttributeError): ms.ms_Word = 42 + # path test + assert ms.get_path("") is ms + assert ms.get_path("ms_Word") is ms.get_path("ms_Word") def astructs_astruct_base_inst_reg_test(): @@ -206,6 +209,10 @@ def astructs_astruct_sub_struct_inst_test(): # find sub field field = ss.ss_My2.ms_Pad assert ss.sfields.find_sub_field_by_def(SubStruct.sdef.ss_My2.ms_Pad) == field + # path test + assert ss.get_path("") is ss + assert ss.get_path("ss_My") is ms + assert ss.get_path("ss_My.ms_Word") is ms.get("ms_Word") def astructs_astruct_baddr_test(): diff --git a/test/unit/libtypes_list.py b/test/unit/libtypes_list.py index c26b1a78..7e452c25 100644 --- a/test/unit/libtypes_list.py +++ b/test/unit/libtypes_list.py @@ -1,6 +1,6 @@ from amitools.vamos.machine.mock import MockMemory from amitools.vamos.mem import MemoryAlloc -from amitools.vamos.libtypes import List, MinList, Node, MinNode +from amitools.vamos.libtypes import List, MinList, Node, MinNode, Task, Process from amitools.vamos.libstructs import ListStruct, MinListStruct, NodeType @@ -167,8 +167,9 @@ def libtypes_list_iter_at_test(): assert [a for a in l.iter_at(n2)] == [n2] -def add_node(alist, addr, name): +def add_node(alist, addr, name, type=NodeType.NT_UNKNOWN): n = Node(alist.mem, addr) + n.type.set(type) addr += n.get_size() name_addr = addr alist.mem.w_cstr(addr, name) @@ -198,6 +199,21 @@ def libtypes_list_find_name_test(): n = l.find_name("hello") assert n == n1 assert n.find_name("hello") == n3 + # get path + assert l.get_path("") is l + assert l.get_path("[world]") == n2 + assert l.get_path("[world].type") == n2.get("type") + + +def libtypes_list_find_name_promote_test(): + l = new_list() + addr = 0x60 + n1, addr = add_node(l, addr, "hello", type=NodeType.NT_TASK) + n2, addr = add_node(l, addr, "world", type=NodeType.NT_PROCESS) + task = l.find_name("hello") + assert type(task) is Task + proc = l.find_name("world") + assert type(proc) is Process def libtypes_list_alloc_test(): diff --git a/test/unit/libtypes_node.py b/test/unit/libtypes_node.py index fb61387b..31543ba3 100644 --- a/test/unit/libtypes_node.py +++ b/test/unit/libtypes_node.py @@ -1,7 +1,7 @@ import pytest from amitools.vamos.machine.mock import MockMemory from amitools.vamos.mem import MemoryAlloc -from amitools.vamos.libtypes import Node, MinNode +from amitools.vamos.libtypes import Node, MinNode, Task from amitools.vamos.libstructs import NodeStruct, MinNodeStruct, NodeType @@ -123,3 +123,10 @@ def libtypes_node_alloc_min_test(): assert node.get_size() == MinNodeStruct.get_size() node.free() assert alloc.is_all_free() + + +def libtypes_node_promote_test(): + mem = MockMemory() + node = Node(mem, 0x80, type=NodeType.NT_TASK) + task = node.promote_type() + assert type(task) is Task diff --git a/test/unit/libtypes_promote.py b/test/unit/libtypes_promote.py deleted file mode 100644 index 6cdf0b6a..00000000 --- a/test/unit/libtypes_promote.py +++ /dev/null @@ -1,21 +0,0 @@ -from amitools.vamos.machine.mock import MockMemory -from amitools.vamos.libstructs import NodeType -from amitools.vamos.libtypes import promote_type, Node, Task, Process -from amitools.vamos.libtypes.promote import node_map - - -def libtypes_promote_node_test(): - mem = MockMemory() - for node_type, node_cls in node_map.items(): - node = Node(mem, 0x40) - node.type.set(node_type) - new_node = promote_type(node) - assert type(new_node) is node_cls - - -def libtypes_promote_task_test(): - mem = MockMemory() - task = Task(mem, 0x40) - task.node.type.set(NodeType.NT_PROCESS) - proc = promote_type(task) - assert type(proc) is Process