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
@@ -0,0 +1,275 @@
1
+ """
2
+ Resolved member (field and method) information.
3
+
4
+ Provides ``FieldInfo`` and ``MethodInfoResolved`` which resolve raw ABC
5
+ trait data into usable descriptors with string names and type names.
6
+
7
+ Also provides ``resolve_trait()`` to parse the raw bytes stored in
8
+ ``TraitInfo.data`` into structured field/method details.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass, field
14
+
15
+ from ..abc.types import AbcFile, TraitInfo, MethodBodyInfo
16
+ from ..abc.parser import read_u30, read_u8
17
+ from ..abc.constants import (
18
+ TRAIT_Slot, TRAIT_Const, TRAIT_Method, TRAIT_Getter, TRAIT_Setter,
19
+ TRAIT_Class, TRAIT_Function,
20
+ CONSTANT_QName, CONSTANT_QNameA,
21
+ CONSTANT_RTQName, CONSTANT_RTQNameA,
22
+ CONSTANT_Multiname, CONSTANT_MultinameA,
23
+ ATTR_Metadata,
24
+ )
25
+
26
+
27
+ def resolve_multiname(abc: AbcFile, index: int) -> str:
28
+ """Resolve a multiname pool index to a human-readable name string.
29
+
30
+ Args:
31
+ abc: The AbcFile containing the constant pools.
32
+ index: Index into the multiname pool.
33
+
34
+ Returns:
35
+ Resolved name string, or ``"*"`` for index 0 (any type).
36
+ """
37
+ if index == 0 or index >= len(abc.multiname_pool):
38
+ return "*"
39
+ mn = abc.multiname_pool[index]
40
+ if mn.kind in (CONSTANT_QName, CONSTANT_QNameA):
41
+ if 0 < mn.name < len(abc.string_pool):
42
+ return abc.string_pool[mn.name]
43
+ elif mn.kind in (CONSTANT_RTQName, CONSTANT_RTQNameA):
44
+ if 0 < mn.name < len(abc.string_pool):
45
+ return abc.string_pool[mn.name]
46
+ elif mn.kind in (CONSTANT_Multiname, CONSTANT_MultinameA):
47
+ if 0 < mn.name < len(abc.string_pool):
48
+ return abc.string_pool[mn.name]
49
+ return f"multiname[{index}]"
50
+
51
+
52
+ def resolve_multiname_full(abc: AbcFile, index: int) -> tuple[str, str]:
53
+ """Resolve a multiname to (package, name) tuple.
54
+
55
+ Args:
56
+ abc: The AbcFile containing the constant pools.
57
+ index: Index into the multiname pool.
58
+
59
+ Returns:
60
+ Tuple of (package_string, name_string).
61
+ """
62
+ if index == 0 or index >= len(abc.multiname_pool):
63
+ return ("", "*")
64
+ mn = abc.multiname_pool[index]
65
+ name = "*"
66
+ package = ""
67
+ if mn.kind in (CONSTANT_QName, CONSTANT_QNameA):
68
+ if 0 < mn.name < len(abc.string_pool):
69
+ name = abc.string_pool[mn.name]
70
+ if 0 < mn.ns < len(abc.namespace_pool):
71
+ ns = abc.namespace_pool[mn.ns]
72
+ if 0 < ns.name < len(abc.string_pool):
73
+ package = abc.string_pool[ns.name]
74
+ elif mn.kind in (CONSTANT_RTQName, CONSTANT_RTQNameA,
75
+ CONSTANT_Multiname, CONSTANT_MultinameA):
76
+ if 0 < mn.name < len(abc.string_pool):
77
+ name = abc.string_pool[mn.name]
78
+ return (package, name)
79
+
80
+
81
+ @dataclass
82
+ class FieldInfo:
83
+ """A resolved field (variable or constant) on a class.
84
+
85
+ Attributes:
86
+ name: Field name string.
87
+ type_name: Type name string (``"*"`` if untyped).
88
+ is_static: Whether this is a static field.
89
+ is_const: True for TRAIT_Const, False for TRAIT_Slot.
90
+ slot_id: Slot index in the object's slot array.
91
+ default_value: Default value if specified, else None.
92
+ trait_index: Index of the original trait in the trait list.
93
+ multiname_index: Original multiname index for the field name.
94
+ type_multiname_index: Original multiname index for the field type.
95
+ """
96
+ name: str
97
+ type_name: str
98
+ is_static: bool = False
99
+ is_const: bool = False
100
+ slot_id: int = 0
101
+ default_value: object = None
102
+ trait_index: int = 0
103
+ multiname_index: int = 0
104
+ type_multiname_index: int = 0
105
+
106
+
107
+ @dataclass
108
+ class MethodInfoResolved:
109
+ """A resolved method, getter, or setter on a class.
110
+
111
+ Attributes:
112
+ name: Method name string.
113
+ param_names: Parameter name strings (from debug info, may be empty).
114
+ param_types: Parameter type name strings.
115
+ return_type: Return type name string.
116
+ is_static: Whether this is a static method.
117
+ is_getter: True if this is a getter property.
118
+ is_setter: True if this is a setter property.
119
+ method_index: Index into AbcFile.methods.
120
+ body_index: Index into AbcFile.method_bodies, or -1 if no body.
121
+ disp_id: Dispatch ID.
122
+ trait_index: Index of the original trait in the trait list.
123
+ multiname_index: Original multiname index for the method name.
124
+ """
125
+ name: str
126
+ param_names: list[str] = field(default_factory=list)
127
+ param_types: list[str] = field(default_factory=list)
128
+ return_type: str = "*"
129
+ is_static: bool = False
130
+ is_getter: bool = False
131
+ is_setter: bool = False
132
+ method_index: int = 0
133
+ body_index: int = -1
134
+ disp_id: int = 0
135
+ trait_index: int = 0
136
+ multiname_index: int = 0
137
+
138
+
139
+ def parse_slot_trait(data: bytes) -> tuple[int, int, int, int | None]:
140
+ """Parse a TRAIT_Slot or TRAIT_Const trait's raw data.
141
+
142
+ Args:
143
+ data: The raw TraitInfo.data bytes.
144
+
145
+ Returns:
146
+ Tuple of (name_mn, slot_id, type_mn, default_value_index).
147
+ default_value_index is None if no default.
148
+ """
149
+ off = 0
150
+ name_mn, off = read_u30(data, off)
151
+ _kind_byte, off = read_u8(data, off)
152
+ slot_id, off = read_u30(data, off)
153
+ type_mn, off = read_u30(data, off)
154
+ vindex, off = read_u30(data, off)
155
+ return (name_mn, slot_id, type_mn, vindex if vindex else None)
156
+
157
+
158
+ def parse_method_trait(data: bytes) -> tuple[int, int, int]:
159
+ """Parse a TRAIT_Method, TRAIT_Getter, or TRAIT_Setter trait's raw data.
160
+
161
+ Args:
162
+ data: The raw TraitInfo.data bytes.
163
+
164
+ Returns:
165
+ Tuple of (name_mn, disp_id, method_index).
166
+ """
167
+ off = 0
168
+ name_mn, off = read_u30(data, off)
169
+ _kind_byte, off = read_u8(data, off)
170
+ disp_id, off = read_u30(data, off)
171
+ method_idx, off = read_u30(data, off)
172
+ return (name_mn, disp_id, method_idx)
173
+
174
+
175
+ def parse_class_trait(data: bytes) -> tuple[int, int, int]:
176
+ """Parse a TRAIT_Class trait's raw data.
177
+
178
+ Returns:
179
+ Tuple of (name_mn, slot_id, class_index).
180
+ """
181
+ off = 0
182
+ name_mn, off = read_u30(data, off)
183
+ _kind_byte, off = read_u8(data, off)
184
+ slot_id, off = read_u30(data, off)
185
+ class_idx, off = read_u30(data, off)
186
+ return (name_mn, slot_id, class_idx)
187
+
188
+
189
+ def resolve_traits(
190
+ abc: AbcFile,
191
+ traits: list[TraitInfo],
192
+ is_static: bool = False,
193
+ method_body_map: dict[int, int] | None = None,
194
+ ) -> tuple[list[FieldInfo], list[MethodInfoResolved]]:
195
+ """Resolve a list of raw traits into FieldInfo and MethodInfoResolved.
196
+
197
+ Args:
198
+ abc: The AbcFile for constant pool lookups.
199
+ traits: List of raw TraitInfo objects.
200
+ is_static: Whether these are static traits.
201
+ method_body_map: Optional mapping of method_index → body_index
202
+ in AbcFile.method_bodies. If None, body_index won't be set.
203
+
204
+ Returns:
205
+ Tuple of (fields, methods).
206
+ """
207
+ fields: list[FieldInfo] = []
208
+ methods: list[MethodInfoResolved] = []
209
+
210
+ for i, trait in enumerate(traits):
211
+ if trait.kind in (TRAIT_Slot, TRAIT_Const):
212
+ name_mn, slot_id, type_mn, default_val = parse_slot_trait(trait.data)
213
+ fi = FieldInfo(
214
+ name=resolve_multiname(abc, name_mn),
215
+ type_name=resolve_multiname(abc, type_mn),
216
+ is_static=is_static,
217
+ is_const=(trait.kind == TRAIT_Const),
218
+ slot_id=slot_id,
219
+ default_value=default_val,
220
+ trait_index=i,
221
+ multiname_index=name_mn,
222
+ type_multiname_index=type_mn,
223
+ )
224
+ fields.append(fi)
225
+
226
+ elif trait.kind in (TRAIT_Method, TRAIT_Getter, TRAIT_Setter):
227
+ name_mn, disp_id, method_idx = parse_method_trait(trait.data)
228
+
229
+ # Resolve method signature
230
+ param_types: list[str] = []
231
+ param_names: list[str] = []
232
+ return_type = "*"
233
+ if 0 <= method_idx < len(abc.methods):
234
+ mi = abc.methods[method_idx]
235
+ return_type = resolve_multiname(abc, mi.return_type)
236
+ param_types = [
237
+ resolve_multiname(abc, pt) for pt in mi.param_types]
238
+ param_names = [
239
+ abc.string_pool[pn] if 0 < pn < len(abc.string_pool) else ""
240
+ for pn in mi.param_names
241
+ ]
242
+
243
+ body_idx = -1
244
+ if method_body_map and method_idx in method_body_map:
245
+ body_idx = method_body_map[method_idx]
246
+
247
+ mri = MethodInfoResolved(
248
+ name=resolve_multiname(abc, name_mn),
249
+ param_names=param_names,
250
+ param_types=param_types,
251
+ return_type=return_type,
252
+ is_static=is_static,
253
+ is_getter=(trait.kind == TRAIT_Getter),
254
+ is_setter=(trait.kind == TRAIT_Setter),
255
+ method_index=method_idx,
256
+ body_index=body_idx,
257
+ disp_id=disp_id,
258
+ trait_index=i,
259
+ multiname_index=name_mn,
260
+ )
261
+ methods.append(mri)
262
+
263
+ return fields, methods
264
+
265
+
266
+ def build_method_body_map(abc: AbcFile) -> dict[int, int]:
267
+ """Build a mapping from method_index → index in AbcFile.method_bodies.
268
+
269
+ Args:
270
+ abc: The AbcFile to index.
271
+
272
+ Returns:
273
+ Dict mapping method indices to their body indices.
274
+ """
275
+ return {mb.method: i for i, mb in enumerate(abc.method_bodies)}
@@ -0,0 +1,60 @@
1
+ """
2
+ Package grouping for ABC classes.
3
+
4
+ Groups ``ClassInfo`` objects by their namespace (package name),
5
+ providing a tree-like view of the class hierarchy similar to what
6
+ a class explorer UI would show.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from collections import defaultdict
12
+ from dataclasses import dataclass, field
13
+
14
+ from .class_info import ClassInfo
15
+
16
+
17
+ @dataclass
18
+ class PackageInfo:
19
+ """A package containing one or more classes.
20
+
21
+ Attributes:
22
+ name: Full package name (e.g. ``"flash.display"``), empty for default.
23
+ classes: List of ClassInfo in this package.
24
+ """
25
+ name: str
26
+ classes: list[ClassInfo] = field(default_factory=list)
27
+
28
+ @property
29
+ def class_count(self) -> int:
30
+ return len(self.classes)
31
+
32
+ def get_class(self, name: str) -> ClassInfo | None:
33
+ """Find a class by simple name within this package."""
34
+ for cls in self.classes:
35
+ if cls.name == name:
36
+ return cls
37
+ return None
38
+
39
+
40
+ def group_by_package(classes: list[ClassInfo]) -> list[PackageInfo]:
41
+ """Group a list of ClassInfo objects by package name.
42
+
43
+ Args:
44
+ classes: List of ClassInfo to group.
45
+
46
+ Returns:
47
+ List of PackageInfo, sorted by package name.
48
+ """
49
+ by_package: dict[str, list[ClassInfo]] = defaultdict(list)
50
+ for cls in classes:
51
+ by_package[cls.package].append(cls)
52
+
53
+ result = []
54
+ for pkg_name in sorted(by_package.keys()):
55
+ pkg = PackageInfo(
56
+ name=pkg_name,
57
+ classes=sorted(by_package[pkg_name], key=lambda c: c.name),
58
+ )
59
+ result.append(pkg)
60
+ return result
@@ -0,0 +1,16 @@
1
+ """
2
+ Unified search and query engine.
3
+
4
+ Combines string search, reference lookup, inheritance queries, and
5
+ call graph traversal into a single interface for querying workspace
6
+ content.
7
+ """
8
+
9
+ from .search import SearchEngine, ClassResult, MemberResult, StringResult
10
+
11
+ __all__ = [
12
+ "SearchEngine",
13
+ "ClassResult",
14
+ "MemberResult",
15
+ "StringResult",
16
+ ]