pyflashkit 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. flashkit/__init__.py +54 -0
  2. flashkit/abc/__init__.py +79 -0
  3. flashkit/abc/builder.py +847 -0
  4. flashkit/abc/constants.py +198 -0
  5. flashkit/abc/disasm.py +364 -0
  6. flashkit/abc/parser.py +434 -0
  7. flashkit/abc/types.py +275 -0
  8. flashkit/abc/writer.py +230 -0
  9. flashkit/analysis/__init__.py +28 -0
  10. flashkit/analysis/call_graph.py +317 -0
  11. flashkit/analysis/inheritance.py +267 -0
  12. flashkit/analysis/references.py +371 -0
  13. flashkit/analysis/strings.py +299 -0
  14. flashkit/cli/__init__.py +75 -0
  15. flashkit/cli/_util.py +52 -0
  16. flashkit/cli/build.py +36 -0
  17. flashkit/cli/callees.py +30 -0
  18. flashkit/cli/callers.py +30 -0
  19. flashkit/cli/class_cmd.py +83 -0
  20. flashkit/cli/classes.py +71 -0
  21. flashkit/cli/disasm.py +77 -0
  22. flashkit/cli/extract.py +36 -0
  23. flashkit/cli/info.py +41 -0
  24. flashkit/cli/packages.py +30 -0
  25. flashkit/cli/refs.py +31 -0
  26. flashkit/cli/strings.py +58 -0
  27. flashkit/cli/tags.py +32 -0
  28. flashkit/cli/tree.py +52 -0
  29. flashkit/errors.py +33 -0
  30. flashkit/info/__init__.py +31 -0
  31. flashkit/info/class_info.py +176 -0
  32. flashkit/info/member_info.py +275 -0
  33. flashkit/info/package_info.py +60 -0
  34. flashkit/search/__init__.py +16 -0
  35. flashkit/search/search.py +456 -0
  36. flashkit/swf/__init__.py +66 -0
  37. flashkit/swf/builder.py +283 -0
  38. flashkit/swf/parser.py +164 -0
  39. flashkit/swf/tags.py +120 -0
  40. flashkit/workspace/__init__.py +20 -0
  41. flashkit/workspace/resource.py +189 -0
  42. flashkit/workspace/workspace.py +232 -0
  43. pyflashkit-1.0.0.dist-info/METADATA +281 -0
  44. pyflashkit-1.0.0.dist-info/RECORD +48 -0
  45. pyflashkit-1.0.0.dist-info/WHEEL +5 -0
  46. pyflashkit-1.0.0.dist-info/entry_points.txt +2 -0
  47. pyflashkit-1.0.0.dist-info/licenses/LICENSE +21 -0
  48. pyflashkit-1.0.0.dist-info/top_level.txt +1 -0
flashkit/cli/disasm.py ADDED
@@ -0,0 +1,77 @@
1
+ """``flashkit disasm`` — disassemble method bytecode."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+
7
+ from ._util import load, bold
8
+
9
+
10
+ def register(sub: argparse._SubParsersAction) -> None:
11
+ p = sub.add_parser("disasm", help="Disassemble method bytecode")
12
+ p.add_argument("file", help="SWF or SWZ file")
13
+ p.add_argument("--class", dest="class_name",
14
+ help="Class to disassemble")
15
+ p.add_argument("--method-index", type=int,
16
+ help="Method index to disassemble")
17
+ p.set_defaults(func=run)
18
+
19
+
20
+ def run(args: argparse.Namespace) -> None:
21
+ ws = load(args.file)
22
+
23
+ from ..abc.disasm import decode_instructions
24
+
25
+ if args.method_index is not None:
26
+ for abc in ws.abc_blocks:
27
+ for mb in abc.method_bodies:
28
+ if mb.method == args.method_index:
29
+ print(bold(f"Method {mb.method}") +
30
+ f" (max_stack={mb.max_stack}, "
31
+ f"locals={mb.local_count}, "
32
+ f"code={len(mb.code)} bytes)")
33
+ for instr in decode_instructions(mb.code):
34
+ ops = ", ".join(str(o) for o in instr.operands)
35
+ print(f" 0x{instr.offset:04X} "
36
+ f"{instr.mnemonic:<24s} {ops}")
37
+ return
38
+ print(f"Method index {args.method_index} not found.")
39
+ return
40
+
41
+ if args.class_name:
42
+ cls = ws.get_class(args.class_name)
43
+ if cls is None:
44
+ matches = ws.find_classes(name=args.class_name)
45
+ if len(matches) == 1:
46
+ cls = matches[0]
47
+ else:
48
+ print(f"Class '{args.class_name}' not found.")
49
+ return
50
+
51
+ for abc in ws.abc_blocks:
52
+ method_indices = set()
53
+ for m in cls.all_methods:
54
+ method_indices.add(m.method_index)
55
+ method_indices.add(cls.constructor_index)
56
+
57
+ for mb in abc.method_bodies:
58
+ if mb.method in method_indices:
59
+ mname = f"method_{mb.method}"
60
+ if mb.method == cls.constructor_index:
61
+ mname = f"{cls.name}()"
62
+ else:
63
+ for m in cls.all_methods:
64
+ if m.method_index == mb.method:
65
+ mname = m.name
66
+ break
67
+
68
+ print(bold(f"{cls.name}.{mname}") +
69
+ f" ({len(mb.code)} bytes)")
70
+ for instr in decode_instructions(mb.code):
71
+ ops = ", ".join(str(o) for o in instr.operands)
72
+ print(f" 0x{instr.offset:04X} "
73
+ f"{instr.mnemonic:<24s} {ops}")
74
+ print()
75
+ return
76
+
77
+ print("Specify --class or --method-index.")
@@ -0,0 +1,36 @@
1
+ """``flashkit extract`` — extract ABC bytecode from a SWF."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from pathlib import Path
7
+
8
+ from ._util import load
9
+
10
+
11
+ def register(sub: argparse._SubParsersAction) -> None:
12
+ p = sub.add_parser("extract", help="Extract ABC blocks from SWF")
13
+ p.add_argument("file", help="SWF file")
14
+ p.add_argument("-o", "--output", help="Output directory")
15
+ p.set_defaults(func=run)
16
+
17
+
18
+ def run(args: argparse.Namespace) -> None:
19
+ ws = load(args.file)
20
+ res = ws.resources[0]
21
+ out_dir = Path(args.output) if args.output else Path(".")
22
+
23
+ if not res.abc_blocks:
24
+ print("No ABC blocks found.")
25
+ return
26
+
27
+ from ..abc.writer import serialize_abc
28
+
29
+ for i, abc in enumerate(res.abc_blocks):
30
+ raw = serialize_abc(abc)
31
+ name = f"abc_{i}.abc"
32
+ dest = out_dir / name
33
+ dest.write_bytes(raw)
34
+ print(f" Wrote {dest} ({len(raw)} bytes)")
35
+
36
+ print(f"\nExtracted {len(res.abc_blocks)} ABC block(s)")
flashkit/cli/info.py ADDED
@@ -0,0 +1,41 @@
1
+ """``flashkit info`` — show high-level file summary."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+
7
+ from ._util import load, bold
8
+
9
+
10
+ def register(sub: argparse._SubParsersAction) -> None:
11
+ p = sub.add_parser("info", help="Show file summary")
12
+ p.add_argument("file", help="SWF or SWZ file")
13
+ p.set_defaults(func=run)
14
+
15
+
16
+ def run(args: argparse.Namespace) -> None:
17
+ ws = load(args.file)
18
+ res = ws.resources[0]
19
+
20
+ print(bold(f"File: {res.path}"))
21
+ print(f" Format: {res.kind.upper()}")
22
+ if res.swf_version is not None:
23
+ print(f" SWF version: {res.swf_version}")
24
+ if res.swf_tags is not None:
25
+ print(f" Tags: {len(res.swf_tags)}")
26
+ print(f" ABC blocks: {len(res.abc_blocks)}")
27
+ print(f" Classes: {res.class_count}")
28
+ print(f" Methods: {res.method_count}")
29
+ print(f" Strings: {res.string_count}")
30
+
31
+ if res.abc_blocks:
32
+ abc = res.abc_blocks[0]
33
+ print(f" Namespaces: {len(abc.namespace_pool)}")
34
+ print(f" Multinames: {len(abc.multiname_pool)}")
35
+ interfaces = sum(1 for c in ws.classes if c.is_interface)
36
+ if interfaces:
37
+ print(f" Interfaces: {interfaces}")
38
+
39
+ pkgs = ws.packages
40
+ if pkgs:
41
+ print(f" Packages: {len(pkgs)}")
@@ -0,0 +1,30 @@
1
+ """``flashkit packages`` — list packages and their class counts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+
7
+ from ._util import load, bold, dim
8
+
9
+
10
+ def register(sub: argparse._SubParsersAction) -> None:
11
+ p = sub.add_parser("packages", help="List packages")
12
+ p.add_argument("file", help="SWF or SWZ file")
13
+ p.set_defaults(func=run)
14
+
15
+
16
+ def run(args: argparse.Namespace) -> None:
17
+ ws = load(args.file)
18
+ pkgs = ws.packages
19
+
20
+ if not pkgs:
21
+ print("No packages found.")
22
+ return
23
+
24
+ print(bold(f"{'Package':<50} {'Classes':>8}"))
25
+ print("-" * 60)
26
+ for p in sorted(pkgs, key=lambda p: p.name):
27
+ name = p.name or dim("(default)")
28
+ print(f"{name:<50} {len(p.classes):>8}")
29
+
30
+ print(f"\n{len(pkgs)} package(s)")
flashkit/cli/refs.py ADDED
@@ -0,0 +1,31 @@
1
+ """``flashkit refs`` — find cross-references to a name."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+
7
+ from ._util import load, bold, dim
8
+
9
+
10
+ def register(sub: argparse._SubParsersAction) -> None:
11
+ p = sub.add_parser("refs", help="Find cross-references to a name")
12
+ p.add_argument("file", help="SWF or SWZ file")
13
+ p.add_argument("name", help="Target name")
14
+ p.set_defaults(func=run)
15
+
16
+
17
+ def run(args: argparse.Namespace) -> None:
18
+ ws = load(args.file)
19
+
20
+ from ..analysis.references import ReferenceIndex
21
+ refs = ReferenceIndex.from_workspace(ws)
22
+ results = refs.references_to(args.name)
23
+
24
+ if not results:
25
+ print(f"No references to '{args.name}'.")
26
+ return
27
+
28
+ print(bold(f"References to '{args.name}'") + f" ({len(results)} refs)")
29
+ for r in results:
30
+ loc = f"{r.source_class}.{r.source_member}" if r.source_member else r.source_class
31
+ print(f" {loc} {dim(r.ref_kind)}")
@@ -0,0 +1,58 @@
1
+ """``flashkit strings`` — list or search string constants."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+
7
+ from ._util import load, bold, dim, green
8
+
9
+
10
+ def register(sub: argparse._SubParsersAction) -> None:
11
+ p = sub.add_parser("strings", help="List or search strings")
12
+ p.add_argument("file", help="SWF or SWZ file")
13
+ p.add_argument("-s", "--search", help="Search term")
14
+ p.add_argument("-r", "--regex", action="store_true",
15
+ help="Treat search term as regex")
16
+ p.add_argument("-c", "--classify", action="store_true",
17
+ help="Classify strings (URLs, debug markers)")
18
+ p.add_argument("-v", "--verbose", action="store_true",
19
+ help="Show usage locations")
20
+ p.set_defaults(func=run)
21
+
22
+
23
+ def run(args: argparse.Namespace) -> None:
24
+ ws = load(args.file)
25
+
26
+ from ..analysis.strings import StringIndex
27
+ idx = StringIndex.from_workspace(ws)
28
+
29
+ if args.search:
30
+ matches = idx.search(args.search, regex=args.regex)
31
+ if not matches:
32
+ print("No matching strings.")
33
+ return
34
+
35
+ for s in matches:
36
+ print(f" {green(repr(s))}")
37
+ if args.verbose:
38
+ for u in idx.by_string.get(s, []):
39
+ print(f" used in {dim(u.class_name)}.{u.method_name}")
40
+ print(f"\n{len(matches)} unique string(s)")
41
+ else:
42
+ all_strings = sorted(idx.pool_strings)
43
+ if args.classify:
44
+ urls = idx.url_strings()
45
+ files = idx.debug_markers()
46
+ print(bold("URLs:"))
47
+ for s in urls:
48
+ print(f" {s}")
49
+ print(f"\n{bold('Debug markers:')}")
50
+ for s in files:
51
+ print(f" {s}")
52
+ print(f"\n{len(urls)} URL(s), {len(files)} debug marker(s), "
53
+ f"{len(all_strings)} total strings")
54
+ else:
55
+ for s in all_strings:
56
+ if s:
57
+ print(f" {s}")
58
+ print(f"\n{len(all_strings)} string(s)")
flashkit/cli/tags.py ADDED
@@ -0,0 +1,32 @@
1
+ """``flashkit tags`` — list all SWF tags."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+
7
+ from ._util import load, bold
8
+
9
+
10
+ def register(sub: argparse._SubParsersAction) -> None:
11
+ p = sub.add_parser("tags", help="List SWF tags")
12
+ p.add_argument("file", help="SWF file")
13
+ p.set_defaults(func=run)
14
+
15
+
16
+ def run(args: argparse.Namespace) -> None:
17
+ ws = load(args.file)
18
+ res = ws.resources[0]
19
+
20
+ if res.swf_tags is None:
21
+ print("No SWF tags (file is SWZ format).")
22
+ return
23
+
24
+ print(bold(f"{'#':<6} {'Type':<8} {'Name':<35} {'Size':>10}"))
25
+ print("-" * 62)
26
+ for i, tag in enumerate(res.swf_tags):
27
+ type_str = str(tag.tag_type)
28
+ name = tag.type_name
29
+ size = len(tag.payload)
30
+ print(f"{i:<6} {type_str:<8} {name:<35} {size:>10}")
31
+
32
+ print(f"\n{len(res.swf_tags)} tags total")
flashkit/cli/tree.py ADDED
@@ -0,0 +1,52 @@
1
+ """``flashkit tree`` — show inheritance tree for a class."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+
7
+ from ._util import load, bold, green
8
+
9
+
10
+ def register(sub: argparse._SubParsersAction) -> None:
11
+ p = sub.add_parser("tree", help="Show inheritance tree")
12
+ p.add_argument("file", help="SWF or SWZ file")
13
+ p.add_argument("name", help="Class name")
14
+ p.add_argument("-a", "--ancestors", action="store_true",
15
+ help="Show ancestors instead of descendants")
16
+ p.set_defaults(func=run)
17
+
18
+
19
+ def run(args: argparse.Namespace) -> None:
20
+ ws = load(args.file)
21
+
22
+ from ..analysis.inheritance import InheritanceGraph
23
+ graph = InheritanceGraph.from_classes(ws.classes)
24
+
25
+ name = args.name
26
+
27
+ if args.ancestors:
28
+ chain = graph.get_all_parents(name)
29
+ if not chain:
30
+ print(f"No ancestors for '{name}' (root or not found).")
31
+ return
32
+ print(bold(f"Ancestors of {name}:"))
33
+ for i, c in enumerate(chain):
34
+ print(f" {' ' * i}{c}")
35
+ print(f" {' ' * len(chain)}{green(name)}")
36
+ return
37
+
38
+ children = graph.get_all_children(name)
39
+ direct = graph.get_children(name)
40
+
41
+ if not children and not direct:
42
+ print(f"No subclasses of '{name}'.")
43
+ return
44
+
45
+ def _print_tree(n: str, depth: int = 0) -> None:
46
+ prefix = " " * depth
47
+ print(f"{prefix}{green(n) if depth == 0 else n}")
48
+ for child in sorted(graph.get_children(n)):
49
+ _print_tree(child, depth + 1)
50
+
51
+ _print_tree(name)
52
+ print(f"\n{len(children)} descendant(s)")
flashkit/errors.py ADDED
@@ -0,0 +1,33 @@
1
+ """
2
+ flashkit error hierarchy.
3
+
4
+ All flashkit-specific exceptions inherit from ``FlashkitError`` so
5
+ consumers can ``except FlashkitError`` to catch any library error.
6
+ """
7
+
8
+
9
+ class FlashkitError(Exception):
10
+ """Base exception for all flashkit errors."""
11
+
12
+
13
+ class ParseError(FlashkitError):
14
+ """Raised when binary data cannot be parsed.
15
+
16
+ Covers both SWF container parsing and ABC bytecode parsing.
17
+ """
18
+
19
+
20
+ class SWFParseError(ParseError):
21
+ """Raised when SWF data is invalid or corrupted."""
22
+
23
+
24
+ class ABCParseError(ParseError):
25
+ """Raised when ABC bytecode is invalid or corrupted."""
26
+
27
+
28
+ class SerializeError(FlashkitError):
29
+ """Raised when an AbcFile cannot be serialized back to bytes."""
30
+
31
+
32
+ class ResourceError(FlashkitError):
33
+ """Raised when a resource file cannot be loaded."""
@@ -0,0 +1,31 @@
1
+ """
2
+ Rich resolved model for ABC class, field, and method information.
3
+
4
+ This package resolves raw ABC constant pool indices into human-readable
5
+ names, types, and signatures.
6
+ """
7
+
8
+ from .class_info import ClassInfo, build_class_info, build_all_classes
9
+ from .member_info import (
10
+ FieldInfo,
11
+ MethodInfoResolved,
12
+ resolve_multiname,
13
+ resolve_multiname_full,
14
+ resolve_traits,
15
+ build_method_body_map,
16
+ )
17
+ from .package_info import PackageInfo, group_by_package
18
+
19
+ __all__ = [
20
+ "ClassInfo",
21
+ "build_class_info",
22
+ "build_all_classes",
23
+ "FieldInfo",
24
+ "MethodInfoResolved",
25
+ "resolve_multiname",
26
+ "resolve_multiname_full",
27
+ "resolve_traits",
28
+ "build_method_body_map",
29
+ "PackageInfo",
30
+ "group_by_package",
31
+ ]
@@ -0,0 +1,176 @@
1
+ """
2
+ Resolved class information.
3
+
4
+ ``ClassInfo`` wraps an ABC InstanceInfo + ClassInfo pair with all names
5
+ resolved from the constant pool. Consumers get direct string access to
6
+ class names, superclass names, interface names, and fully resolved
7
+ field/method lists.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass, field
13
+
14
+ from ..abc.types import AbcFile
15
+ from ..abc.constants import INSTANCE_Interface
16
+ from .member_info import (
17
+ FieldInfo, MethodInfoResolved,
18
+ resolve_multiname, resolve_multiname_full, resolve_traits,
19
+ build_method_body_map,
20
+ )
21
+
22
+
23
+ @dataclass
24
+ class ClassInfo:
25
+ """A fully resolved class definition.
26
+
27
+ All names are resolved from the ABC constant pool into strings.
28
+ Fields and methods are parsed from raw trait data into structured
29
+ ``FieldInfo`` and ``MethodInfoResolved`` objects.
30
+
31
+ Attributes:
32
+ name: Class name string.
33
+ package: Package/namespace string (empty for default package).
34
+ qualified_name: Full ``package.name`` string.
35
+ super_name: Superclass name (``"Object"`` for root, ``"*"`` if none).
36
+ super_package: Superclass package string.
37
+ interfaces: List of interface name strings.
38
+ is_interface: Whether this class is an interface.
39
+ is_sealed: Whether this class is sealed (no dynamic properties).
40
+ is_final: Whether this class is final (cannot be subclassed).
41
+ fields: Instance fields (FieldInfo list).
42
+ methods: Instance methods, getters, setters (MethodInfoResolved list).
43
+ static_fields: Static fields (FieldInfo list).
44
+ static_methods: Static methods (MethodInfoResolved list).
45
+ constructor_index: Method index for the instance initializer.
46
+ static_init_index: Method index for the static initializer.
47
+ instance_index: Index in AbcFile.instances (and AbcFile.classes).
48
+ multiname_index: Original multiname index for the class name.
49
+ super_multiname_index: Original multiname index for the superclass.
50
+ interface_multiname_indices: Original multiname indices for interfaces.
51
+ """
52
+ name: str = ""
53
+ package: str = ""
54
+ qualified_name: str = ""
55
+ super_name: str = "*"
56
+ super_package: str = ""
57
+ interfaces: list[str] = field(default_factory=list)
58
+ is_interface: bool = False
59
+ is_sealed: bool = False
60
+ is_final: bool = False
61
+ fields: list[FieldInfo] = field(default_factory=list)
62
+ methods: list[MethodInfoResolved] = field(default_factory=list)
63
+ static_fields: list[FieldInfo] = field(default_factory=list)
64
+ static_methods: list[MethodInfoResolved] = field(default_factory=list)
65
+ constructor_index: int = 0
66
+ static_init_index: int = 0
67
+ instance_index: int = 0
68
+ multiname_index: int = 0
69
+ super_multiname_index: int = 0
70
+ interface_multiname_indices: list[int] = field(default_factory=list)
71
+
72
+ @property
73
+ def all_fields(self) -> list[FieldInfo]:
74
+ """All fields (instance + static)."""
75
+ return self.fields + self.static_fields
76
+
77
+ @property
78
+ def all_methods(self) -> list[MethodInfoResolved]:
79
+ """All methods (instance + static)."""
80
+ return self.methods + self.static_methods
81
+
82
+ def get_field(self, name: str) -> FieldInfo | None:
83
+ """Find a field by name (searches instance then static)."""
84
+ for f in self.fields:
85
+ if f.name == name:
86
+ return f
87
+ for f in self.static_fields:
88
+ if f.name == name:
89
+ return f
90
+ return None
91
+
92
+ def get_method(self, name: str) -> MethodInfoResolved | None:
93
+ """Find a method by name (searches instance then static)."""
94
+ for m in self.methods:
95
+ if m.name == name:
96
+ return m
97
+ for m in self.static_methods:
98
+ if m.name == name:
99
+ return m
100
+ return None
101
+
102
+
103
+ def build_class_info(abc: AbcFile, index: int,
104
+ method_body_map: dict[int, int] | None = None) -> ClassInfo:
105
+ """Build a ClassInfo from an AbcFile instance/class pair.
106
+
107
+ Args:
108
+ abc: The AbcFile containing the class.
109
+ index: Index into abc.instances and abc.classes.
110
+ method_body_map: Optional pre-built method→body index map.
111
+ If None, one will be built automatically.
112
+
113
+ Returns:
114
+ Fully resolved ClassInfo.
115
+ """
116
+ if method_body_map is None:
117
+ method_body_map = build_method_body_map(abc)
118
+
119
+ inst = abc.instances[index]
120
+ cls = abc.classes[index]
121
+
122
+ # Resolve class name
123
+ package, name = resolve_multiname_full(abc, inst.name)
124
+ qualified = f"{package}.{name}" if package else name
125
+
126
+ # Resolve superclass
127
+ super_package, super_name = resolve_multiname_full(abc, inst.super_name)
128
+
129
+ # Resolve interfaces
130
+ iface_names = [resolve_multiname(abc, i) for i in inst.interfaces]
131
+
132
+ # Resolve instance traits (fields + methods)
133
+ inst_fields, inst_methods = resolve_traits(
134
+ abc, inst.traits, is_static=False, method_body_map=method_body_map)
135
+
136
+ # Resolve static traits (static fields + static methods)
137
+ static_fields, static_methods = resolve_traits(
138
+ abc, cls.traits, is_static=True, method_body_map=method_body_map)
139
+
140
+ return ClassInfo(
141
+ name=name,
142
+ package=package,
143
+ qualified_name=qualified,
144
+ super_name=super_name,
145
+ super_package=super_package,
146
+ interfaces=iface_names,
147
+ is_interface=bool(inst.flags & INSTANCE_Interface),
148
+ is_sealed=bool(inst.flags & 0x01),
149
+ is_final=bool(inst.flags & 0x02),
150
+ fields=inst_fields,
151
+ methods=inst_methods,
152
+ static_fields=static_fields,
153
+ static_methods=static_methods,
154
+ constructor_index=inst.iinit,
155
+ static_init_index=cls.cinit,
156
+ instance_index=index,
157
+ multiname_index=inst.name,
158
+ super_multiname_index=inst.super_name,
159
+ interface_multiname_indices=list(inst.interfaces),
160
+ )
161
+
162
+
163
+ def build_all_classes(abc: AbcFile) -> list[ClassInfo]:
164
+ """Build ClassInfo objects for all classes in an AbcFile.
165
+
166
+ Args:
167
+ abc: The AbcFile to process.
168
+
169
+ Returns:
170
+ List of ClassInfo, one per class, in the same order as abc.instances.
171
+ """
172
+ method_body_map = build_method_body_map(abc)
173
+ return [
174
+ build_class_info(abc, i, method_body_map)
175
+ for i in range(len(abc.instances))
176
+ ]