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/abc/writer.py ADDED
@@ -0,0 +1,230 @@
1
+ """
2
+ ABC bytecode serializer.
3
+
4
+ Serializes an ``AbcFile`` structure back to raw ABC binary data.
5
+ The output is byte-for-byte identical to the original input when
6
+ no modifications have been made (round-trip fidelity).
7
+
8
+ Usage::
9
+
10
+ from flashkit.abc.parser import parse_abc
11
+ from flashkit.abc.writer import serialize_abc
12
+
13
+ abc = parse_abc(raw_bytes)
14
+ # ... modify abc ...
15
+ output = serialize_abc(abc)
16
+
17
+ Reference: Adobe AVM2 Overview, Chapter 4 (abc file format).
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import struct
23
+
24
+ from ..errors import SerializeError
25
+ from .types import AbcFile, TraitInfo
26
+ from .parser import write_u30, write_s32
27
+ from .constants import (
28
+ CONSTANT_QName, CONSTANT_QNameA,
29
+ CONSTANT_RTQName, CONSTANT_RTQNameA,
30
+ CONSTANT_RTQNameL, CONSTANT_RTQNameLA,
31
+ CONSTANT_Multiname, CONSTANT_MultinameA,
32
+ CONSTANT_MultinameL, CONSTANT_MultinameLA,
33
+ CONSTANT_TypeName,
34
+ METHOD_HasOptional, METHOD_HasParamNames,
35
+ INSTANCE_ProtectedNs,
36
+ )
37
+
38
+
39
+ def _write_traits(traits: list[TraitInfo]) -> bytes:
40
+ """Serialize a trait list using the raw binary data stored during parse."""
41
+ out = write_u30(len(traits))
42
+ for t in traits:
43
+ out += t.data
44
+ return out
45
+
46
+
47
+ def serialize_abc(abc: AbcFile) -> bytes:
48
+ """Serialize an AbcFile back to raw ABC bytecode.
49
+
50
+ Args:
51
+ abc: The AbcFile to serialize.
52
+
53
+ Returns:
54
+ Raw ABC bytecode bytes ready to embed in a DoABC/DoABC2 tag.
55
+
56
+ Raises:
57
+ SerializeError: If the AbcFile structure is invalid.
58
+ """
59
+ try:
60
+ return _serialize_abc_inner(abc)
61
+ except SerializeError:
62
+ raise
63
+ except (IndexError, struct.error, ValueError, TypeError,
64
+ AttributeError) as e:
65
+ raise SerializeError(f"Failed to serialize ABC: {e}") from e
66
+
67
+
68
+ def _serialize_abc_inner(abc: AbcFile) -> bytes:
69
+ """Internal serializer (no error wrapping)."""
70
+ out = bytearray()
71
+ out += struct.pack("<HH", abc.minor_version, abc.major_version)
72
+
73
+ # ── Constant pool ────────────────────────────────────────────────────
74
+
75
+ # Integers (count 0 means empty pool — only the implicit default entry)
76
+ # Use raw LEB128 bytes when available for round-trip fidelity, since
77
+ # the AVM2 spec allows non-minimal s32 encodings.
78
+ int_extra = abc.int_pool[1:]
79
+ int_raw = abc._int_pool_raw[1:] if len(abc._int_pool_raw) > 1 else []
80
+ out += write_u30(len(int_extra) + 1 if int_extra else 0)
81
+ for i, v in enumerate(int_extra):
82
+ if i < len(int_raw) and int_raw[i]:
83
+ out += int_raw[i]
84
+ else:
85
+ out += write_s32(v)
86
+
87
+ # Unsigned integers
88
+ # Use raw bytes for round-trip fidelity (AVM2 uint values can exceed
89
+ # 30 bits despite the spec calling the encoding "u30").
90
+ uint_extra = abc.uint_pool[1:]
91
+ uint_raw = abc._uint_pool_raw[1:] if len(abc._uint_pool_raw) > 1 else []
92
+ out += write_u30(len(uint_extra) + 1 if uint_extra else 0)
93
+ for i, v in enumerate(uint_extra):
94
+ if i < len(uint_raw) and uint_raw[i]:
95
+ out += uint_raw[i]
96
+ else:
97
+ out += write_u30(v)
98
+
99
+ # Doubles
100
+ dbl_extra = abc.double_pool[1:]
101
+ out += write_u30(len(dbl_extra) + 1 if dbl_extra else 0)
102
+ for v in dbl_extra:
103
+ out += struct.pack("<d", v)
104
+
105
+ # Strings
106
+ str_extra = abc.string_pool[1:]
107
+ out += write_u30(len(str_extra) + 1 if str_extra else 0)
108
+ for s in str_extra:
109
+ encoded = s.encode("utf-8")
110
+ out += write_u30(len(encoded))
111
+ out += encoded
112
+
113
+ # Namespaces
114
+ ns_extra = abc.namespace_pool[1:]
115
+ out += write_u30(len(ns_extra) + 1 if ns_extra else 0)
116
+ for ns in ns_extra:
117
+ out += bytes([ns.kind])
118
+ out += write_u30(ns.name)
119
+
120
+ # Namespace sets
121
+ nss_extra = abc.ns_set_pool[1:]
122
+ out += write_u30(len(nss_extra) + 1 if nss_extra else 0)
123
+ for nss in nss_extra:
124
+ out += write_u30(len(nss.namespaces))
125
+ for ns in nss.namespaces:
126
+ out += write_u30(ns)
127
+
128
+ # Multinames
129
+ mn_extra = abc.multiname_pool[1:]
130
+ out += write_u30(len(mn_extra) + 1 if mn_extra else 0)
131
+ for mn in mn_extra:
132
+ out += bytes([mn.kind])
133
+ if mn.kind in (CONSTANT_QName, CONSTANT_QNameA):
134
+ out += write_u30(mn.ns)
135
+ out += write_u30(mn.name)
136
+ elif mn.kind in (CONSTANT_RTQName, CONSTANT_RTQNameA):
137
+ out += write_u30(mn.name)
138
+ elif mn.kind in (CONSTANT_RTQNameL, CONSTANT_RTQNameLA):
139
+ pass
140
+ elif mn.kind in (CONSTANT_Multiname, CONSTANT_MultinameA):
141
+ out += write_u30(mn.name)
142
+ out += write_u30(mn.ns_set)
143
+ elif mn.kind in (CONSTANT_MultinameL, CONSTANT_MultinameLA):
144
+ out += write_u30(mn.ns_set)
145
+ elif mn.kind == CONSTANT_TypeName:
146
+ out += write_u30(mn.ns) # base type multiname index
147
+ out += write_u30(mn.name) # parameter count
148
+ out += mn.data # pre-serialized parameter u30s
149
+ else:
150
+ raise SerializeError(
151
+ f"Unknown multiname kind 0x{mn.kind:02X}")
152
+
153
+ # ── Methods ──────────────────────────────────────────────────────────
154
+
155
+ out += write_u30(len(abc.methods))
156
+ for mi in abc.methods:
157
+ out += write_u30(mi.param_count)
158
+ out += write_u30(mi.return_type)
159
+ for pt in mi.param_types:
160
+ out += write_u30(pt)
161
+ out += write_u30(mi.name)
162
+ out += bytes([mi.flags])
163
+
164
+ if mi.flags & METHOD_HasOptional:
165
+ out += write_u30(len(mi.options))
166
+ for val, vkind in mi.options:
167
+ out += write_u30(val)
168
+ out += bytes([vkind])
169
+
170
+ if mi.flags & METHOD_HasParamNames:
171
+ for pn in mi.param_names:
172
+ out += write_u30(pn)
173
+
174
+ # ── Metadata ─────────────────────────────────────────────────────────
175
+
176
+ out += write_u30(len(abc.metadata))
177
+ for md in abc.metadata:
178
+ out += write_u30(md.name)
179
+ out += write_u30(len(md.items))
180
+ for k, v in md.items:
181
+ out += write_u30(k)
182
+ out += write_u30(v)
183
+
184
+ # ── Instances + Classes ──────────────────────────────────────────────
185
+
186
+ out += write_u30(len(abc.instances))
187
+ for inst in abc.instances:
188
+ out += write_u30(inst.name)
189
+ out += write_u30(inst.super_name)
190
+ out += bytes([inst.flags])
191
+ if inst.flags & INSTANCE_ProtectedNs:
192
+ out += write_u30(inst.protectedNs)
193
+ out += write_u30(len(inst.interfaces))
194
+ for ifc in inst.interfaces:
195
+ out += write_u30(ifc)
196
+ out += write_u30(inst.iinit)
197
+ out += _write_traits(inst.traits)
198
+
199
+ for ci in abc.classes:
200
+ out += write_u30(ci.cinit)
201
+ out += _write_traits(ci.traits)
202
+
203
+ # ── Scripts ──────────────────────────────────────────────────────────
204
+
205
+ out += write_u30(len(abc.scripts))
206
+ for si in abc.scripts:
207
+ out += write_u30(si.init)
208
+ out += _write_traits(si.traits)
209
+
210
+ # ── Method bodies ────────────────────────────────────────────────────
211
+
212
+ out += write_u30(len(abc.method_bodies))
213
+ for mb in abc.method_bodies:
214
+ out += write_u30(mb.method)
215
+ out += write_u30(mb.max_stack)
216
+ out += write_u30(mb.local_count)
217
+ out += write_u30(mb.init_scope_depth)
218
+ out += write_u30(mb.max_scope_depth)
219
+ out += write_u30(len(mb.code))
220
+ out += mb.code
221
+ out += write_u30(len(mb.exceptions))
222
+ for ei in mb.exceptions:
223
+ out += write_u30(ei.from_offset)
224
+ out += write_u30(ei.to_offset)
225
+ out += write_u30(ei.target)
226
+ out += write_u30(ei.exc_type)
227
+ out += write_u30(ei.var_name)
228
+ out += _write_traits(mb.traits)
229
+
230
+ return bytes(out)
@@ -0,0 +1,28 @@
1
+ """
2
+ Analysis services for ABC content.
3
+
4
+ This package provides graph-based and index-based analysis of the loaded
5
+ ABC bytecode. Each module builds a specific data structure from the parsed
6
+ ABC data that enables efficient queries.
7
+
8
+ Modules:
9
+ inheritance: InheritanceGraph — class hierarchy (parent/child/interface).
10
+ call_graph: CallGraph — method-to-method call edges from bytecode.
11
+ references: ReferenceIndex — cross-references (field types, instantiations, imports).
12
+ strings: StringIndex — string constant search and classification.
13
+ """
14
+
15
+ from .inheritance import InheritanceGraph
16
+ from .call_graph import CallGraph, CallEdge
17
+ from .references import ReferenceIndex, Reference
18
+ from .strings import StringIndex, StringUsage
19
+
20
+ __all__ = [
21
+ "InheritanceGraph",
22
+ "CallGraph",
23
+ "CallEdge",
24
+ "ReferenceIndex",
25
+ "Reference",
26
+ "StringIndex",
27
+ "StringUsage",
28
+ ]
@@ -0,0 +1,317 @@
1
+ """
2
+ Call graph extraction from method body bytecode.
3
+
4
+ Scans AVM2 bytecode instructions in MethodBodyInfo.code to build a
5
+ graph of method-to-method call edges. Each edge records the calling
6
+ method, target multiname, opcode type, and bytecode offset.
7
+
8
+ Usage::
9
+
10
+ from flashkit.workspace import Workspace
11
+ from flashkit.analysis.call_graph import CallGraph
12
+
13
+ ws = Workspace()
14
+ ws.load_swf("application.swf")
15
+ graph = CallGraph.from_workspace(ws)
16
+
17
+ callers = graph.get_callers("doSomething")
18
+ callees = graph.get_callees("MyClass.init")
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from dataclasses import dataclass, field
24
+ from collections import defaultdict
25
+
26
+ from ..abc.types import AbcFile
27
+ from ..abc.disasm import decode_instructions
28
+ from ..abc.constants import (
29
+ OP_callproperty, OP_callpropvoid, OP_constructprop,
30
+ OP_getproperty, OP_setproperty, OP_initproperty,
31
+ OP_getlex, OP_findpropstrict, OP_newclass,
32
+ )
33
+ from ..info.member_info import resolve_multiname, build_method_body_map
34
+ from ..info.class_info import ClassInfo
35
+
36
+
37
+ # Opcode categories for edges
38
+ CALL_OPS = {OP_callproperty, OP_callpropvoid}
39
+ CONSTRUCT_OPS = {OP_constructprop}
40
+ PROPERTY_READ_OPS = {OP_getproperty, OP_getlex, OP_findpropstrict}
41
+ PROPERTY_WRITE_OPS = {OP_setproperty, OP_initproperty}
42
+ CLASS_OPS = {OP_newclass}
43
+
44
+ # All opcodes that reference a multiname in their first operand
45
+ _MULTINAME_OPS = (
46
+ CALL_OPS | CONSTRUCT_OPS | PROPERTY_READ_OPS
47
+ | PROPERTY_WRITE_OPS | CLASS_OPS
48
+ )
49
+
50
+
51
+ @dataclass
52
+ class CallEdge:
53
+ """A single call/reference edge in the graph.
54
+
55
+ Attributes:
56
+ caller: Qualified name of the calling method (``"Class.method"``).
57
+ caller_method_index: Method index in the AbcFile.
58
+ target: Target multiname string (the called/referenced name).
59
+ opcode: The opcode that generated this edge.
60
+ mnemonic: Human-readable opcode name.
61
+ offset: Bytecode offset within the method body.
62
+ edge_type: Category string: ``"call"``, ``"construct"``, ``"read"``,
63
+ ``"write"``, or ``"class"``.
64
+ """
65
+ caller: str
66
+ caller_method_index: int
67
+ target: str
68
+ opcode: int
69
+ mnemonic: str
70
+ offset: int
71
+ edge_type: str
72
+
73
+
74
+ def _classify_op(opcode: int) -> str:
75
+ """Classify an opcode into an edge type string."""
76
+ if opcode in CALL_OPS:
77
+ return "call"
78
+ elif opcode in CONSTRUCT_OPS:
79
+ return "construct"
80
+ elif opcode in PROPERTY_READ_OPS:
81
+ return "read"
82
+ elif opcode in PROPERTY_WRITE_OPS:
83
+ return "write"
84
+ elif opcode in CLASS_OPS:
85
+ return "class"
86
+ return "unknown"
87
+
88
+
89
+ @dataclass
90
+ class CallGraph:
91
+ """Graph of method call and reference edges extracted from bytecode.
92
+
93
+ Attributes:
94
+ edges: All edges in the graph.
95
+ callers_index: Map of target name → list of edges calling it.
96
+ callees_index: Map of caller name → list of edges it produces.
97
+ """
98
+ edges: list[CallEdge] = field(default_factory=list)
99
+ callers_index: dict[str, list[CallEdge]] = field(
100
+ default_factory=lambda: defaultdict(list))
101
+ callees_index: dict[str, list[CallEdge]] = field(
102
+ default_factory=lambda: defaultdict(list))
103
+
104
+ @classmethod
105
+ def from_workspace(cls, workspace: object) -> CallGraph:
106
+ """Build a CallGraph from a Workspace.
107
+
108
+ Iterates all ABC blocks and their method bodies, decodes
109
+ instructions, and collects edges for multiname-referencing opcodes.
110
+
111
+ Args:
112
+ workspace: A Workspace instance (with .abc_blocks and .classes).
113
+
114
+ Returns:
115
+ Populated CallGraph.
116
+ """
117
+ graph = cls()
118
+
119
+ # Build a map from method_index → class_name.method_name
120
+ # for all classes in the workspace
121
+ from ..workspace.workspace import Workspace
122
+ ws: Workspace = workspace # type: ignore[assignment]
123
+
124
+ for abc in ws.abc_blocks:
125
+ method_name_map = _build_method_name_map(abc, ws.classes)
126
+ method_body_map = build_method_body_map(abc)
127
+
128
+ for body in abc.method_bodies:
129
+ caller_name = method_name_map.get(
130
+ body.method, f"method_{body.method}")
131
+
132
+ try:
133
+ instructions = decode_instructions(body.code)
134
+ except Exception:
135
+ continue
136
+
137
+ for instr in instructions:
138
+ if instr.opcode in _MULTINAME_OPS and instr.operands:
139
+ mn_index = instr.operands[0]
140
+ target = resolve_multiname(abc, mn_index)
141
+ if target == "*" or target.startswith("multiname["):
142
+ continue
143
+
144
+ edge = CallEdge(
145
+ caller=caller_name,
146
+ caller_method_index=body.method,
147
+ target=target,
148
+ opcode=instr.opcode,
149
+ mnemonic=instr.mnemonic,
150
+ offset=instr.offset,
151
+ edge_type=_classify_op(instr.opcode),
152
+ )
153
+ graph.edges.append(edge)
154
+ graph.callers_index[target].append(edge)
155
+ graph.callees_index[caller_name].append(edge)
156
+
157
+ return graph
158
+
159
+ @classmethod
160
+ def from_abc(cls, abc: AbcFile,
161
+ classes: list[ClassInfo] | None = None) -> CallGraph:
162
+ """Build a CallGraph from a single AbcFile.
163
+
164
+ Args:
165
+ abc: The AbcFile to analyze.
166
+ classes: Optional class list for method name resolution.
167
+
168
+ Returns:
169
+ Populated CallGraph.
170
+ """
171
+ graph = cls()
172
+ method_name_map = _build_method_name_map(abc, classes or [])
173
+
174
+ for body in abc.method_bodies:
175
+ caller_name = method_name_map.get(
176
+ body.method, f"method_{body.method}")
177
+
178
+ try:
179
+ instructions = decode_instructions(body.code)
180
+ except Exception:
181
+ continue
182
+
183
+ for instr in instructions:
184
+ if instr.opcode in _MULTINAME_OPS and instr.operands:
185
+ mn_index = instr.operands[0]
186
+ target = resolve_multiname(abc, mn_index)
187
+ if target == "*" or target.startswith("multiname["):
188
+ continue
189
+
190
+ edge = CallEdge(
191
+ caller=caller_name,
192
+ caller_method_index=body.method,
193
+ target=target,
194
+ opcode=instr.opcode,
195
+ mnemonic=instr.mnemonic,
196
+ offset=instr.offset,
197
+ edge_type=_classify_op(instr.opcode),
198
+ )
199
+ graph.edges.append(edge)
200
+ graph.callers_index[target].append(edge)
201
+ graph.callees_index[caller_name].append(edge)
202
+
203
+ return graph
204
+
205
+ def get_callers(self, target: str) -> list[CallEdge]:
206
+ """Get all edges where *target* is called or referenced.
207
+
208
+ Args:
209
+ target: Target method/property name.
210
+
211
+ Returns:
212
+ List of CallEdge objects referencing this target.
213
+ """
214
+ return self.callers_index.get(target, [])
215
+
216
+ def get_callees(self, caller: str) -> list[CallEdge]:
217
+ """Get all edges originating from *caller*.
218
+
219
+ Args:
220
+ caller: Caller method name (``"Class.method"`` format).
221
+
222
+ Returns:
223
+ List of CallEdge objects from this caller.
224
+ """
225
+ return self.callees_index.get(caller, [])
226
+
227
+ def get_callers_by_type(self, target: str,
228
+ edge_type: str) -> list[CallEdge]:
229
+ """Get edges of a specific type referencing *target*.
230
+
231
+ Args:
232
+ target: Target name.
233
+ edge_type: One of ``"call"``, ``"construct"``, ``"read"``,
234
+ ``"write"``, ``"class"``.
235
+
236
+ Returns:
237
+ Filtered list of CallEdge objects.
238
+ """
239
+ return [e for e in self.get_callers(target)
240
+ if e.edge_type == edge_type]
241
+
242
+ def get_instantiators(self, class_name: str) -> list[str]:
243
+ """Get unique caller names that construct instances of *class_name*.
244
+
245
+ Args:
246
+ class_name: The class being instantiated.
247
+
248
+ Returns:
249
+ Sorted list of unique caller names.
250
+ """
251
+ edges = self.get_callers_by_type(class_name, "construct")
252
+ return sorted(set(e.caller for e in edges))
253
+
254
+ def get_unique_callers(self, target: str) -> list[str]:
255
+ """Get unique caller names for a target (calls only).
256
+
257
+ Args:
258
+ target: Target method name.
259
+
260
+ Returns:
261
+ Sorted list of unique caller names.
262
+ """
263
+ edges = self.get_callers_by_type(target, "call")
264
+ return sorted(set(e.caller for e in edges))
265
+
266
+ def get_unique_callees(self, caller: str) -> list[str]:
267
+ """Get unique target names called by a caller (calls only).
268
+
269
+ Args:
270
+ caller: Caller method name.
271
+
272
+ Returns:
273
+ Sorted list of unique target names.
274
+ """
275
+ edges = [e for e in self.get_callees(caller) if e.edge_type == "call"]
276
+ return sorted(set(e.target for e in edges))
277
+
278
+ @property
279
+ def edge_count(self) -> int:
280
+ return len(self.edges)
281
+
282
+ @property
283
+ def unique_targets(self) -> int:
284
+ return len(self.callers_index)
285
+
286
+ @property
287
+ def unique_callers(self) -> int:
288
+ return len(self.callees_index)
289
+
290
+
291
+ def _build_method_name_map(abc: AbcFile,
292
+ classes: list[ClassInfo]) -> dict[int, str]:
293
+ """Build a mapping from method_index → ``"ClassName.methodName"``.
294
+
295
+ Uses ClassInfo's resolved methods to build readable names.
296
+ Falls back to ``"method_N"`` for methods not attached to classes.
297
+ """
298
+ name_map: dict[int, str] = {}
299
+
300
+ for ci in classes:
301
+ # Instance constructor
302
+ name_map[ci.constructor_index] = f"{ci.name}.<init>"
303
+ # Static initializer
304
+ name_map[ci.static_init_index] = f"{ci.name}.<cinit>"
305
+
306
+ for m in ci.methods:
307
+ prefix = ""
308
+ if m.is_getter:
309
+ prefix = "get "
310
+ elif m.is_setter:
311
+ prefix = "set "
312
+ name_map[m.method_index] = f"{ci.name}.{prefix}{m.name}"
313
+
314
+ for m in ci.static_methods:
315
+ name_map[m.method_index] = f"{ci.name}.static {m.name}"
316
+
317
+ return name_map