pytecode 0.0.1__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.
- pytecode/__init__.py +22 -0
- pytecode/analysis.py +2402 -0
- pytecode/attributes.py +868 -0
- pytecode/bytes_utils.py +208 -0
- pytecode/class_reader.py +810 -0
- pytecode/class_writer.py +630 -0
- pytecode/constant_pool.py +196 -0
- pytecode/constant_pool_builder.py +844 -0
- pytecode/constants.py +208 -0
- pytecode/debug_info.py +319 -0
- pytecode/descriptors.py +791 -0
- pytecode/hierarchy.py +561 -0
- pytecode/info.py +123 -0
- pytecode/instructions.py +495 -0
- pytecode/jar.py +271 -0
- pytecode/labels.py +1041 -0
- pytecode/model.py +929 -0
- pytecode/modified_utf8.py +145 -0
- pytecode/operands.py +683 -0
- pytecode/py.typed +0 -0
- pytecode/transforms.py +954 -0
- pytecode/verify.py +1386 -0
- pytecode-0.0.1.dist-info/METADATA +218 -0
- pytecode-0.0.1.dist-info/RECORD +27 -0
- pytecode-0.0.1.dist-info/WHEEL +5 -0
- pytecode-0.0.1.dist-info/licenses/LICENSE +21 -0
- pytecode-0.0.1.dist-info/top_level.txt +1 -0
pytecode/class_reader.py
ADDED
|
@@ -0,0 +1,810 @@
|
|
|
1
|
+
"""Parse JVM ``.class`` file bytes into a :class:`ClassFile` tree.
|
|
2
|
+
|
|
3
|
+
This module implements a single-pass reader that deserialises the binary
|
|
4
|
+
class-file format defined in *The Java Virtual Machine Specification* (JVMS §4)
|
|
5
|
+
into the in-memory ``ClassFile`` structure exposed by :mod:`pytecode.info`.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
from . import attributes, constant_pool, constants, info, instructions
|
|
13
|
+
from .bytes_utils import BytesReader
|
|
14
|
+
from .modified_utf8 import decode_modified_utf8
|
|
15
|
+
|
|
16
|
+
__all__ = ["ClassReader", "MalformedClassException"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MalformedClassException(Exception):
|
|
20
|
+
"""Raised when the input bytes do not conform to the JVM class-file format (JVMS §4)."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ClassReader(BytesReader):
|
|
24
|
+
"""Single-pass parser that converts ``.class`` file bytes into a :class:`~pytecode.info.ClassFile` tree.
|
|
25
|
+
|
|
26
|
+
The reader walks the binary layout defined in JVMS §4.1, populating the
|
|
27
|
+
constant pool first (§4.4) and then deserialising fields, methods, and
|
|
28
|
+
attributes in declaration order. The resulting :attr:`class_info` object
|
|
29
|
+
mirrors the on-disk ``ClassFile`` structure.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
class_info: info.ClassFile
|
|
33
|
+
|
|
34
|
+
def __init__(self, bytes_or_bytearray: bytes | bytearray) -> None:
|
|
35
|
+
"""Initialise the reader and immediately parse the class-file bytes.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
bytes_or_bytearray: Raw bytes of a ``.class`` file.
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
MalformedClassException: If the bytes are not a valid class file.
|
|
42
|
+
"""
|
|
43
|
+
super().__init__(bytes_or_bytearray)
|
|
44
|
+
self.constant_pool: list[constant_pool.ConstantPoolInfo | None] = []
|
|
45
|
+
self.read_class()
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def from_file(cls, path: str | os.PathLike[str]) -> ClassReader:
|
|
49
|
+
"""Construct a :class:`ClassReader` from a ``.class`` file on disk.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
path: Filesystem path to the ``.class`` file.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
A fully-parsed :class:`ClassReader` instance.
|
|
56
|
+
"""
|
|
57
|
+
with open(path, "rb") as f:
|
|
58
|
+
file_bytes = f.read()
|
|
59
|
+
return cls(file_bytes)
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def from_bytes(cls, bytes_or_bytearray: bytes | bytearray) -> ClassReader:
|
|
63
|
+
"""Construct a :class:`ClassReader` from raw bytes.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
bytes_or_bytearray: Raw bytes of a ``.class`` file.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
A fully-parsed :class:`ClassReader` instance.
|
|
70
|
+
"""
|
|
71
|
+
return cls(bytes_or_bytearray)
|
|
72
|
+
|
|
73
|
+
def read_constant_pool_index(self, index: int) -> tuple[constant_pool.ConstantPoolInfo, int]:
|
|
74
|
+
"""Read a single constant-pool entry at the given logical index (JVMS §4.4).
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
index: One-based constant-pool index for the entry being read.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
A tuple of the parsed constant-pool info object and the number of
|
|
81
|
+
extra index slots consumed (1 for ``long``/``double``, else 0).
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
ValueError: If the constant-pool tag is unrecognised.
|
|
85
|
+
"""
|
|
86
|
+
index_extra, offset, tag = 0, self.offset, self.read_u1()
|
|
87
|
+
cp_type = constant_pool.ConstantPoolInfoType(tag)
|
|
88
|
+
|
|
89
|
+
if cp_type is constant_pool.ConstantPoolInfoType.CLASS:
|
|
90
|
+
cp_info = constant_pool.ClassInfo(index, offset, tag, self.read_u2())
|
|
91
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.STRING:
|
|
92
|
+
cp_info = constant_pool.StringInfo(index, offset, tag, self.read_u2())
|
|
93
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.METHOD_TYPE:
|
|
94
|
+
cp_info = constant_pool.MethodTypeInfo(index, offset, tag, self.read_u2())
|
|
95
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.MODULE:
|
|
96
|
+
cp_info = constant_pool.ModuleInfo(index, offset, tag, self.read_u2())
|
|
97
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.PACKAGE:
|
|
98
|
+
cp_info = constant_pool.PackageInfo(index, offset, tag, self.read_u2())
|
|
99
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.FIELD_REF:
|
|
100
|
+
cp_info = constant_pool.FieldrefInfo(index, offset, tag, self.read_u2(), self.read_u2())
|
|
101
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.METHOD_REF:
|
|
102
|
+
cp_info = constant_pool.MethodrefInfo(index, offset, tag, self.read_u2(), self.read_u2())
|
|
103
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.INTERFACE_METHOD_REF:
|
|
104
|
+
cp_info = constant_pool.InterfaceMethodrefInfo(index, offset, tag, self.read_u2(), self.read_u2())
|
|
105
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.NAME_AND_TYPE:
|
|
106
|
+
cp_info = constant_pool.NameAndTypeInfo(index, offset, tag, self.read_u2(), self.read_u2())
|
|
107
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.DYNAMIC:
|
|
108
|
+
cp_info = constant_pool.DynamicInfo(index, offset, tag, self.read_u2(), self.read_u2())
|
|
109
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.INVOKE_DYNAMIC:
|
|
110
|
+
cp_info = constant_pool.InvokeDynamicInfo(index, offset, tag, self.read_u2(), self.read_u2())
|
|
111
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.INTEGER:
|
|
112
|
+
cp_info = constant_pool.IntegerInfo(index, offset, tag, self.read_u4())
|
|
113
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.FLOAT:
|
|
114
|
+
cp_info = constant_pool.FloatInfo(index, offset, tag, self.read_u4())
|
|
115
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.LONG:
|
|
116
|
+
cp_info = constant_pool.LongInfo(index, offset, tag, self.read_u4(), self.read_u4())
|
|
117
|
+
index_extra = 1
|
|
118
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.DOUBLE:
|
|
119
|
+
cp_info = constant_pool.DoubleInfo(index, offset, tag, self.read_u4(), self.read_u4())
|
|
120
|
+
index_extra = 1
|
|
121
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.UTF8:
|
|
122
|
+
length = self.read_u2()
|
|
123
|
+
str_bytes = self.read_bytes(length)
|
|
124
|
+
cp_info = constant_pool.Utf8Info(index, offset, tag, length, str_bytes)
|
|
125
|
+
elif cp_type is constant_pool.ConstantPoolInfoType.METHOD_HANDLE:
|
|
126
|
+
cp_info = constant_pool.MethodHandleInfo(index, offset, tag, self.read_u1(), self.read_u2())
|
|
127
|
+
else:
|
|
128
|
+
raise ValueError(f"Unknown ConstantPoolInfoType: {cp_type}")
|
|
129
|
+
return cp_info, index_extra
|
|
130
|
+
|
|
131
|
+
def read_align_bytes(self, current_offset: int) -> bytes:
|
|
132
|
+
"""Read and discard padding bytes to reach 4-byte alignment.
|
|
133
|
+
|
|
134
|
+
Used by ``tableswitch`` and ``lookupswitch`` instructions whose
|
|
135
|
+
operands must be 4-byte aligned (JVMS §6.5).
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
current_offset: Current bytecode offset within the method body.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
The consumed padding bytes (0–3 bytes).
|
|
142
|
+
"""
|
|
143
|
+
align_bytes = (4 - current_offset % 4) % 4
|
|
144
|
+
return self.read_bytes(align_bytes)
|
|
145
|
+
|
|
146
|
+
def read_instruction(self, current_method_offset: int) -> instructions.InsnInfo:
|
|
147
|
+
"""Read a single JVM bytecode instruction (JVMS §6.5).
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
current_method_offset: Byte offset of this instruction relative to
|
|
151
|
+
the start of the method's ``Code`` attribute bytecode array.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
The decoded instruction info object.
|
|
155
|
+
|
|
156
|
+
Raises:
|
|
157
|
+
Exception: If the opcode or its ``wide`` variant is invalid.
|
|
158
|
+
"""
|
|
159
|
+
opcode = self.read_u1()
|
|
160
|
+
inst_type = instructions.InsnInfoType(opcode)
|
|
161
|
+
instinfo = inst_type.instinfo
|
|
162
|
+
if instinfo is instructions.LocalIndex:
|
|
163
|
+
return instructions.LocalIndex(inst_type, current_method_offset, self.read_u1())
|
|
164
|
+
elif instinfo is instructions.ConstPoolIndex:
|
|
165
|
+
return instructions.ConstPoolIndex(inst_type, current_method_offset, self.read_u2())
|
|
166
|
+
elif instinfo is instructions.ByteValue:
|
|
167
|
+
return instructions.ByteValue(inst_type, current_method_offset, self.read_i1())
|
|
168
|
+
elif instinfo is instructions.ShortValue:
|
|
169
|
+
return instructions.ShortValue(inst_type, current_method_offset, self.read_i2())
|
|
170
|
+
elif instinfo is instructions.Branch:
|
|
171
|
+
return instructions.Branch(inst_type, current_method_offset, self.read_i2())
|
|
172
|
+
elif instinfo is instructions.BranchW:
|
|
173
|
+
return instructions.BranchW(inst_type, current_method_offset, self.read_i4())
|
|
174
|
+
elif instinfo is instructions.IInc:
|
|
175
|
+
index, value = self.read_u1(), self.read_i1()
|
|
176
|
+
return instructions.IInc(inst_type, current_method_offset, index, value)
|
|
177
|
+
elif instinfo is instructions.InvokeDynamic:
|
|
178
|
+
index, unused = self.read_u2(), self.read_bytes(2)
|
|
179
|
+
return instructions.InvokeDynamic(inst_type, current_method_offset, index, unused)
|
|
180
|
+
elif instinfo is instructions.InvokeInterface:
|
|
181
|
+
index, count, unused = self.read_u2(), self.read_u1(), self.read_bytes(1)
|
|
182
|
+
return instructions.InvokeInterface(inst_type, current_method_offset, index, count, unused)
|
|
183
|
+
elif instinfo is instructions.MultiANewArray:
|
|
184
|
+
index, dimensions = self.read_u2(), self.read_u1()
|
|
185
|
+
return instructions.MultiANewArray(inst_type, current_method_offset, index, dimensions)
|
|
186
|
+
elif instinfo is instructions.NewArray:
|
|
187
|
+
atype = instructions.ArrayType(self.read_u1())
|
|
188
|
+
return instructions.NewArray(inst_type, current_method_offset, atype)
|
|
189
|
+
elif instinfo is instructions.LookupSwitch:
|
|
190
|
+
self.read_align_bytes(current_method_offset + 1)
|
|
191
|
+
default, npairs = self.read_i4(), self.read_u4()
|
|
192
|
+
pairs = [instructions.MatchOffsetPair(self.read_i4(), self.read_i4()) for _ in range(npairs)]
|
|
193
|
+
return instructions.LookupSwitch(inst_type, current_method_offset, default, npairs, pairs)
|
|
194
|
+
elif instinfo is instructions.TableSwitch:
|
|
195
|
+
self.read_align_bytes(current_method_offset + 1)
|
|
196
|
+
default, low, high = self.read_i4(), self.read_i4(), self.read_i4()
|
|
197
|
+
offsets = [self.read_i4() for _ in range(high - low + 1)]
|
|
198
|
+
return instructions.TableSwitch(inst_type, current_method_offset, default, low, high, offsets)
|
|
199
|
+
elif inst_type is instructions.InsnInfoType.WIDE:
|
|
200
|
+
wide_opcode = self.read_u1()
|
|
201
|
+
wide_inst_type = instructions.InsnInfoType(opcode + wide_opcode)
|
|
202
|
+
if wide_inst_type.instinfo is instructions.LocalIndexW:
|
|
203
|
+
return instructions.LocalIndexW(wide_inst_type, current_method_offset, self.read_u2())
|
|
204
|
+
elif wide_inst_type.instinfo is instructions.IIncW:
|
|
205
|
+
index, value = self.read_u2(), self.read_i2()
|
|
206
|
+
return instructions.IIncW(wide_inst_type, current_method_offset, index, value)
|
|
207
|
+
elif instinfo is instructions.InsnInfo:
|
|
208
|
+
return instructions.InsnInfo(inst_type, current_method_offset)
|
|
209
|
+
|
|
210
|
+
raise Exception(f"Invalid InstInfoType: {inst_type.name} {inst_type.instinfo}")
|
|
211
|
+
|
|
212
|
+
def read_code_bytes(self, code_length: int) -> list[instructions.InsnInfo]:
|
|
213
|
+
"""Read the full bytecode array of a ``Code`` attribute (JVMS §4.7.3).
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
code_length: Number of bytes in the bytecode array.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
Ordered list of decoded instructions.
|
|
220
|
+
"""
|
|
221
|
+
start_method_offset = self.offset
|
|
222
|
+
results: list[instructions.InsnInfo] = []
|
|
223
|
+
while (current_method_offset := self.offset - start_method_offset) < code_length:
|
|
224
|
+
insn = self.read_instruction(current_method_offset)
|
|
225
|
+
results.append(insn)
|
|
226
|
+
return results
|
|
227
|
+
|
|
228
|
+
def read_verification_type_info(self) -> attributes.VerificationTypeInfo:
|
|
229
|
+
"""Read a single ``verification_type_info`` union (JVMS §4.7.4).
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
The decoded verification-type info variant.
|
|
233
|
+
|
|
234
|
+
Raises:
|
|
235
|
+
ValueError: If the verification-type tag is unrecognised.
|
|
236
|
+
"""
|
|
237
|
+
tag = self.read_u1()
|
|
238
|
+
match tag:
|
|
239
|
+
case constants.VerificationType.TOP:
|
|
240
|
+
return attributes.TopVariableInfo(tag)
|
|
241
|
+
case constants.VerificationType.INTEGER:
|
|
242
|
+
return attributes.IntegerVariableInfo(tag)
|
|
243
|
+
case constants.VerificationType.FLOAT:
|
|
244
|
+
return attributes.FloatVariableInfo(tag)
|
|
245
|
+
case constants.VerificationType.DOUBLE:
|
|
246
|
+
return attributes.DoubleVariableInfo(tag)
|
|
247
|
+
case constants.VerificationType.LONG:
|
|
248
|
+
return attributes.LongVariableInfo(tag)
|
|
249
|
+
case constants.VerificationType.NULL:
|
|
250
|
+
return attributes.NullVariableInfo(tag)
|
|
251
|
+
case constants.VerificationType.UNINITIALIZED_THIS:
|
|
252
|
+
return attributes.UninitializedThisVariableInfo(tag)
|
|
253
|
+
case constants.VerificationType.OBJECT:
|
|
254
|
+
return attributes.ObjectVariableInfo(tag, self.read_u2())
|
|
255
|
+
case constants.VerificationType.UNINITIALIZED:
|
|
256
|
+
return attributes.UninitializedVariableInfo(tag, self.read_u2())
|
|
257
|
+
case _:
|
|
258
|
+
raise ValueError(f"Unknown verification type tag: {tag}")
|
|
259
|
+
|
|
260
|
+
def read_element_value_info(self) -> attributes.ElementValueInfo:
|
|
261
|
+
"""Read an ``element_value`` structure from an annotation (JVMS §4.7.16.1).
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
The decoded element-value info.
|
|
265
|
+
|
|
266
|
+
Raises:
|
|
267
|
+
ValueError: If the element-value tag character is unrecognised.
|
|
268
|
+
"""
|
|
269
|
+
tag = self.read_u1().to_bytes(1, "big").decode("ascii")
|
|
270
|
+
|
|
271
|
+
match tag:
|
|
272
|
+
case x if x in ("B", "C", "D", "F", "I", "J", "S", "Z", "s"):
|
|
273
|
+
return attributes.ElementValueInfo(tag, attributes.ConstValueInfo(self.read_u2()))
|
|
274
|
+
case "e":
|
|
275
|
+
return attributes.ElementValueInfo(
|
|
276
|
+
tag,
|
|
277
|
+
attributes.EnumConstantValueInfo(self.read_u2(), self.read_u2()),
|
|
278
|
+
)
|
|
279
|
+
case "c":
|
|
280
|
+
return attributes.ElementValueInfo(tag, attributes.ClassInfoValueInfo(self.read_u2()))
|
|
281
|
+
case "@":
|
|
282
|
+
return attributes.ElementValueInfo(tag, self.read_annotation_info())
|
|
283
|
+
case "[":
|
|
284
|
+
num_values = self.read_u2()
|
|
285
|
+
values = [self.read_element_value_info() for _ in range(num_values)]
|
|
286
|
+
return attributes.ElementValueInfo(tag, attributes.ArrayValueInfo(num_values, values))
|
|
287
|
+
case _:
|
|
288
|
+
raise ValueError(f"Unknown element value tag: {tag}")
|
|
289
|
+
|
|
290
|
+
def read_annotation_info(self) -> attributes.AnnotationInfo:
|
|
291
|
+
"""Read an ``annotation`` structure (JVMS §4.7.16).
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
The decoded annotation info including its element-value pairs.
|
|
295
|
+
"""
|
|
296
|
+
type_index = self.read_u2()
|
|
297
|
+
num_element_value_pairs = self.read_u2()
|
|
298
|
+
element_value_pairs = [
|
|
299
|
+
attributes.ElementValuePairInfo(self.read_u2(), self.read_element_value_info())
|
|
300
|
+
for _ in range(num_element_value_pairs)
|
|
301
|
+
]
|
|
302
|
+
return attributes.AnnotationInfo(type_index, num_element_value_pairs, element_value_pairs)
|
|
303
|
+
|
|
304
|
+
def read_target_info(self, target_type: int) -> attributes.TargetInfo:
|
|
305
|
+
"""Read a ``target_info`` union for a type annotation (JVMS §4.7.20).
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
target_type: The ``target_type`` byte that selects the union variant.
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
The decoded target info variant.
|
|
312
|
+
|
|
313
|
+
Raises:
|
|
314
|
+
ValueError: If the target type is unrecognised.
|
|
315
|
+
"""
|
|
316
|
+
match target_type:
|
|
317
|
+
case x if x in constants.TargetInfoType.TYPE_PARAMETER.value:
|
|
318
|
+
return attributes.TypeParameterTargetInfo(self.read_u1())
|
|
319
|
+
case x if x in constants.TargetInfoType.SUPERTYPE.value:
|
|
320
|
+
return attributes.SupertypeTargetInfo(self.read_u2())
|
|
321
|
+
case x if x in constants.TargetInfoType.TYPE_PARAMETER_BOUND.value:
|
|
322
|
+
return attributes.TypeParameterBoundTargetInfo(self.read_u1(), self.read_u1())
|
|
323
|
+
case x if x in constants.TargetInfoType.EMPTY.value:
|
|
324
|
+
return attributes.EmptyTargetInfo()
|
|
325
|
+
case x if x in constants.TargetInfoType.FORMAL_PARAMETER.value:
|
|
326
|
+
return attributes.FormalParameterTargetInfo(self.read_u1())
|
|
327
|
+
case x if x in constants.TargetInfoType.THROWS.value:
|
|
328
|
+
return attributes.ThrowsTargetInfo(self.read_u2())
|
|
329
|
+
case x if x in constants.TargetInfoType.LOCALVAR.value:
|
|
330
|
+
table_length = self.read_u2()
|
|
331
|
+
table = [
|
|
332
|
+
attributes.TableInfo(self.read_u2(), self.read_u2(), self.read_u2()) for _ in range(table_length)
|
|
333
|
+
]
|
|
334
|
+
return attributes.LocalvarTargetInfo(table_length, table)
|
|
335
|
+
case x if x in constants.TargetInfoType.CATCH.value:
|
|
336
|
+
return attributes.CatchTargetInfo(self.read_u2())
|
|
337
|
+
case x if x in constants.TargetInfoType.OFFSET.value:
|
|
338
|
+
return attributes.OffsetTargetInfo(self.read_u2())
|
|
339
|
+
case x if x in constants.TargetInfoType.TYPE_ARGUMENT.value:
|
|
340
|
+
return attributes.TypeArgumentTargetInfo(self.read_u2(), self.read_u1())
|
|
341
|
+
case _:
|
|
342
|
+
raise ValueError(f"Unknown target info type: {target_type}")
|
|
343
|
+
|
|
344
|
+
def read_target_path(self) -> attributes.TypePathInfo:
|
|
345
|
+
"""Read a ``type_path`` structure for a type annotation (JVMS §4.7.20.2).
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
The decoded type-path info.
|
|
349
|
+
"""
|
|
350
|
+
path_length = self.read_u1()
|
|
351
|
+
path = [attributes.PathInfo(self.read_u1(), self.read_u1()) for _ in range(path_length)]
|
|
352
|
+
return attributes.TypePathInfo(path_length, path)
|
|
353
|
+
|
|
354
|
+
def read_type_annotation_info(self) -> attributes.TypeAnnotationInfo:
|
|
355
|
+
"""Read a ``type_annotation`` structure (JVMS §4.7.20).
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
The decoded type-annotation info.
|
|
359
|
+
"""
|
|
360
|
+
target_type = self.read_u1()
|
|
361
|
+
target_info = self.read_target_info(target_type)
|
|
362
|
+
target_path = self.read_target_path()
|
|
363
|
+
type_index = self.read_u2()
|
|
364
|
+
num_element_value_pairs = self.read_u2()
|
|
365
|
+
element_value_pairs = [
|
|
366
|
+
attributes.ElementValuePairInfo(self.read_u2(), self.read_element_value_info())
|
|
367
|
+
for _ in range(num_element_value_pairs)
|
|
368
|
+
]
|
|
369
|
+
return attributes.TypeAnnotationInfo(
|
|
370
|
+
target_type,
|
|
371
|
+
target_info,
|
|
372
|
+
target_path,
|
|
373
|
+
type_index,
|
|
374
|
+
num_element_value_pairs,
|
|
375
|
+
element_value_pairs,
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
def read_attribute(self) -> attributes.AttributeInfo:
|
|
379
|
+
"""Read a single ``attribute_info`` structure (JVMS §4.7).
|
|
380
|
+
|
|
381
|
+
Recognised attribute names are decoded into their specific subtypes;
|
|
382
|
+
unknown attributes are returned as :class:`~pytecode.attributes.UnimplementedAttr`.
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
The decoded attribute info.
|
|
386
|
+
|
|
387
|
+
Raises:
|
|
388
|
+
ValueError: If the attribute name index does not reference a
|
|
389
|
+
``CONSTANT_Utf8_info`` entry.
|
|
390
|
+
"""
|
|
391
|
+
name_index, length = self.read_u2(), self.read_u4()
|
|
392
|
+
|
|
393
|
+
name_cp = self.constant_pool[name_index]
|
|
394
|
+
if not isinstance(name_cp, constant_pool.Utf8Info):
|
|
395
|
+
raise ValueError(f"name_index({name_index}) should be Utf8Info, not {type(name_cp)}")
|
|
396
|
+
|
|
397
|
+
name = decode_modified_utf8(name_cp.str_bytes)
|
|
398
|
+
attr_type = attributes.AttributeInfoType(name)
|
|
399
|
+
|
|
400
|
+
if attr_type is attributes.AttributeInfoType.SYNTHETIC:
|
|
401
|
+
return attributes.SyntheticAttr(name_index, length)
|
|
402
|
+
|
|
403
|
+
elif attr_type is attributes.AttributeInfoType.DEPRECATED:
|
|
404
|
+
return attributes.DeprecatedAttr(name_index, length)
|
|
405
|
+
|
|
406
|
+
elif attr_type is attributes.AttributeInfoType.CONSTANT_VALUE:
|
|
407
|
+
return attributes.ConstantValueAttr(name_index, length, self.read_u2())
|
|
408
|
+
|
|
409
|
+
elif attr_type is attributes.AttributeInfoType.SIGNATURE:
|
|
410
|
+
return attributes.SignatureAttr(name_index, length, self.read_u2())
|
|
411
|
+
|
|
412
|
+
elif attr_type is attributes.AttributeInfoType.SOURCE_FILE:
|
|
413
|
+
return attributes.SourceFileAttr(name_index, length, self.read_u2())
|
|
414
|
+
|
|
415
|
+
elif attr_type is attributes.AttributeInfoType.MODULE_MAIN_CLASS:
|
|
416
|
+
return attributes.ModuleMainClassAttr(name_index, length, self.read_u2())
|
|
417
|
+
|
|
418
|
+
elif attr_type is attributes.AttributeInfoType.NEST_HOST:
|
|
419
|
+
return attributes.NestHostAttr(name_index, length, self.read_u2())
|
|
420
|
+
|
|
421
|
+
elif attr_type is attributes.AttributeInfoType.CODE:
|
|
422
|
+
max_stack, max_locals = self.read_u2(), self.read_u2()
|
|
423
|
+
code_length = self.read_u4()
|
|
424
|
+
code = self.read_code_bytes(code_length)
|
|
425
|
+
exception_table_length = self.read_u2()
|
|
426
|
+
exception_table = [
|
|
427
|
+
attributes.ExceptionInfo(self.read_u2(), self.read_u2(), self.read_u2(), self.read_u2())
|
|
428
|
+
for _ in range(exception_table_length)
|
|
429
|
+
]
|
|
430
|
+
attributes_count = self.read_u2()
|
|
431
|
+
attributes_list = [self.read_attribute() for _ in range(attributes_count)]
|
|
432
|
+
return attributes.CodeAttr(
|
|
433
|
+
name_index,
|
|
434
|
+
length,
|
|
435
|
+
max_stack,
|
|
436
|
+
max_locals,
|
|
437
|
+
code_length,
|
|
438
|
+
code,
|
|
439
|
+
exception_table_length,
|
|
440
|
+
exception_table,
|
|
441
|
+
attributes_count,
|
|
442
|
+
attributes_list,
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
elif attr_type is attributes.AttributeInfoType.STACK_MAP_TABLE:
|
|
446
|
+
number_of_entries = self.read_u2()
|
|
447
|
+
entries: list[attributes.StackMapFrameInfo] = []
|
|
448
|
+
for _ in range(number_of_entries):
|
|
449
|
+
frame_type = self.read_u1()
|
|
450
|
+
|
|
451
|
+
match frame_type:
|
|
452
|
+
case x if x in range(0, 64):
|
|
453
|
+
entries.append(attributes.SameFrameInfo(frame_type))
|
|
454
|
+
case x if x in range(64, 128):
|
|
455
|
+
entries.append(
|
|
456
|
+
attributes.SameLocals1StackItemFrameInfo(frame_type, self.read_verification_type_info())
|
|
457
|
+
)
|
|
458
|
+
case 247:
|
|
459
|
+
entries.append(
|
|
460
|
+
attributes.SameLocals1StackItemFrameExtendedInfo(
|
|
461
|
+
frame_type,
|
|
462
|
+
self.read_u2(),
|
|
463
|
+
self.read_verification_type_info(),
|
|
464
|
+
)
|
|
465
|
+
)
|
|
466
|
+
case x if x in range(248, 251):
|
|
467
|
+
entries.append(attributes.ChopFrameInfo(frame_type, self.read_u2()))
|
|
468
|
+
case 251:
|
|
469
|
+
entries.append(attributes.SameFrameExtendedInfo(frame_type, self.read_u2()))
|
|
470
|
+
case x if x in range(252, 255):
|
|
471
|
+
offset_delta = self.read_u2()
|
|
472
|
+
verification_type_infos = [self.read_verification_type_info() for __ in range(frame_type - 251)]
|
|
473
|
+
entries.append(attributes.AppendFrameInfo(frame_type, offset_delta, verification_type_infos))
|
|
474
|
+
case 255:
|
|
475
|
+
offset_delta = self.read_u2()
|
|
476
|
+
number_of_locals = self.read_u2()
|
|
477
|
+
locals = [self.read_verification_type_info() for __ in range(number_of_locals)]
|
|
478
|
+
number_of_stack_items = self.read_u2()
|
|
479
|
+
stack = [self.read_verification_type_info() for __ in range(number_of_stack_items)]
|
|
480
|
+
entries.append(
|
|
481
|
+
attributes.FullFrameInfo(
|
|
482
|
+
frame_type,
|
|
483
|
+
offset_delta,
|
|
484
|
+
number_of_locals,
|
|
485
|
+
locals,
|
|
486
|
+
number_of_stack_items,
|
|
487
|
+
stack,
|
|
488
|
+
)
|
|
489
|
+
)
|
|
490
|
+
case _:
|
|
491
|
+
raise ValueError(f"Unknown stack map frame type: {frame_type}")
|
|
492
|
+
|
|
493
|
+
return attributes.StackMapTableAttr(name_index, length, number_of_entries, entries)
|
|
494
|
+
|
|
495
|
+
elif attr_type is attributes.AttributeInfoType.EXCEPTIONS:
|
|
496
|
+
number_of_exceptions = self.read_u2()
|
|
497
|
+
exception_index_table = [self.read_u2() for _ in range(number_of_exceptions)]
|
|
498
|
+
return attributes.ExceptionsAttr(name_index, length, number_of_exceptions, exception_index_table)
|
|
499
|
+
|
|
500
|
+
elif attr_type is attributes.AttributeInfoType.INNER_CLASSES:
|
|
501
|
+
number_of_classes = self.read_u2()
|
|
502
|
+
classes = [
|
|
503
|
+
attributes.InnerClassInfo(
|
|
504
|
+
self.read_u2(),
|
|
505
|
+
self.read_u2(),
|
|
506
|
+
self.read_u2(),
|
|
507
|
+
constants.NestedClassAccessFlag(self.read_u2()),
|
|
508
|
+
)
|
|
509
|
+
for _ in range(number_of_classes)
|
|
510
|
+
]
|
|
511
|
+
return attributes.InnerClassesAttr(name_index, length, number_of_classes, classes)
|
|
512
|
+
|
|
513
|
+
elif attr_type is attributes.AttributeInfoType.ENCLOSING_METHOD:
|
|
514
|
+
return attributes.EnclosingMethodAttr(name_index, length, self.read_u2(), self.read_u2())
|
|
515
|
+
|
|
516
|
+
elif attr_type is attributes.AttributeInfoType.SOURCE_DEBUG_EXTENSION:
|
|
517
|
+
return attributes.SourceDebugExtensionAttr(name_index, length, self.read_bytes(length).decode("utf-8"))
|
|
518
|
+
|
|
519
|
+
elif attr_type is attributes.AttributeInfoType.LINE_NUMBER_TABLE:
|
|
520
|
+
line_number_table_length = self.read_u2()
|
|
521
|
+
line_number_table = [
|
|
522
|
+
attributes.LineNumberInfo(self.read_u2(), self.read_u2()) for _ in range(line_number_table_length)
|
|
523
|
+
]
|
|
524
|
+
return attributes.LineNumberTableAttr(name_index, length, line_number_table_length, line_number_table)
|
|
525
|
+
|
|
526
|
+
elif attr_type is attributes.AttributeInfoType.LOCAL_VARIABLE_TABLE:
|
|
527
|
+
local_variable_table_length = self.read_u2()
|
|
528
|
+
local_variable_table = [
|
|
529
|
+
attributes.LocalVariableInfo(
|
|
530
|
+
self.read_u2(),
|
|
531
|
+
self.read_u2(),
|
|
532
|
+
self.read_u2(),
|
|
533
|
+
self.read_u2(),
|
|
534
|
+
self.read_u2(),
|
|
535
|
+
)
|
|
536
|
+
for _ in range(local_variable_table_length)
|
|
537
|
+
]
|
|
538
|
+
return attributes.LocalVariableTableAttr(
|
|
539
|
+
name_index, length, local_variable_table_length, local_variable_table
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
elif attr_type is attributes.AttributeInfoType.LOCAL_VARIABLE_TYPE_TABLE:
|
|
543
|
+
local_variable_type_table_length = self.read_u2()
|
|
544
|
+
local_variable_type_table = [
|
|
545
|
+
attributes.LocalVariableTypeInfo(
|
|
546
|
+
self.read_u2(),
|
|
547
|
+
self.read_u2(),
|
|
548
|
+
self.read_u2(),
|
|
549
|
+
self.read_u2(),
|
|
550
|
+
self.read_u2(),
|
|
551
|
+
)
|
|
552
|
+
for _ in range(local_variable_type_table_length)
|
|
553
|
+
]
|
|
554
|
+
return attributes.LocalVariableTypeTableAttr(
|
|
555
|
+
name_index, length, local_variable_type_table_length, local_variable_type_table
|
|
556
|
+
)
|
|
557
|
+
|
|
558
|
+
elif attr_type is attributes.AttributeInfoType.RUNTIME_VISIBLE_ANNOTATIONS:
|
|
559
|
+
num_annotations = self.read_u2()
|
|
560
|
+
annotation_list = [self.read_annotation_info() for _ in range(num_annotations)]
|
|
561
|
+
return attributes.RuntimeVisibleAnnotationsAttr(name_index, length, num_annotations, annotation_list)
|
|
562
|
+
|
|
563
|
+
elif attr_type is attributes.AttributeInfoType.RUNTIME_INVISIBLE_ANNOTATIONS:
|
|
564
|
+
num_annotations = self.read_u2()
|
|
565
|
+
annotation_list = [self.read_annotation_info() for _ in range(num_annotations)]
|
|
566
|
+
return attributes.RuntimeInvisibleAnnotationsAttr(name_index, length, num_annotations, annotation_list)
|
|
567
|
+
|
|
568
|
+
elif attr_type is attributes.AttributeInfoType.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS:
|
|
569
|
+
num_parameters = self.read_u1()
|
|
570
|
+
parameter_annotations: list[attributes.ParameterAnnotationInfo] = []
|
|
571
|
+
for _ in range(num_parameters):
|
|
572
|
+
num_annotations = self.read_u2()
|
|
573
|
+
annotation_list = [self.read_annotation_info() for _ in range(num_annotations)]
|
|
574
|
+
parameter_annotations.append(attributes.ParameterAnnotationInfo(num_annotations, annotation_list))
|
|
575
|
+
return attributes.RuntimeVisibleParameterAnnotationsAttr(
|
|
576
|
+
name_index, length, num_parameters, parameter_annotations
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
elif attr_type is attributes.AttributeInfoType.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS:
|
|
580
|
+
num_parameters = self.read_u1()
|
|
581
|
+
parameter_annotations_list: list[attributes.ParameterAnnotationInfo] = []
|
|
582
|
+
for _ in range(num_parameters):
|
|
583
|
+
num_annotations = self.read_u2()
|
|
584
|
+
annotation_list = [self.read_annotation_info() for _ in range(num_annotations)]
|
|
585
|
+
parameter_annotations_list.append(attributes.ParameterAnnotationInfo(num_annotations, annotation_list))
|
|
586
|
+
return attributes.RuntimeInvisibleParameterAnnotationsAttr(
|
|
587
|
+
name_index, length, num_parameters, parameter_annotations_list
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
elif attr_type is attributes.AttributeInfoType.RUNTIME_VISIBLE_TYPE_ANNOTATIONS:
|
|
591
|
+
num_annotations = self.read_u2()
|
|
592
|
+
type_annotation_list = [self.read_type_annotation_info() for _ in range(num_annotations)]
|
|
593
|
+
return attributes.RuntimeVisibleTypeAnnotationsAttr(
|
|
594
|
+
name_index, length, num_annotations, type_annotation_list
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
elif attr_type is attributes.AttributeInfoType.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS:
|
|
598
|
+
num_annotations = self.read_u2()
|
|
599
|
+
type_annotation_list = [self.read_type_annotation_info() for _ in range(num_annotations)]
|
|
600
|
+
return attributes.RuntimeInvisibleTypeAnnotationsAttr(
|
|
601
|
+
name_index, length, num_annotations, type_annotation_list
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
elif attr_type is attributes.AttributeInfoType.ANNOTATION_DEFAULT:
|
|
605
|
+
return attributes.AnnotationDefaultAttr(name_index, length, self.read_element_value_info())
|
|
606
|
+
|
|
607
|
+
elif attr_type is attributes.AttributeInfoType.BOOTSTRAP_METHODS:
|
|
608
|
+
num_bootstrap_methods = self.read_u2()
|
|
609
|
+
bootstrap_methods: list[attributes.BootstrapMethodInfo] = []
|
|
610
|
+
for _ in range(num_bootstrap_methods):
|
|
611
|
+
bootstrap_method_ref = self.read_u2()
|
|
612
|
+
num_bootstrap_arguments = self.read_u2()
|
|
613
|
+
bootstrap_arguments = [self.read_u2() for __ in range(num_bootstrap_arguments)]
|
|
614
|
+
bootstrap_methods.append(
|
|
615
|
+
attributes.BootstrapMethodInfo(
|
|
616
|
+
bootstrap_method_ref,
|
|
617
|
+
num_bootstrap_arguments,
|
|
618
|
+
bootstrap_arguments,
|
|
619
|
+
)
|
|
620
|
+
)
|
|
621
|
+
return attributes.BootstrapMethodsAttr(name_index, length, num_bootstrap_methods, bootstrap_methods)
|
|
622
|
+
|
|
623
|
+
elif attr_type is attributes.AttributeInfoType.METHOD_PARAMETERS:
|
|
624
|
+
parameters_count = self.read_u1()
|
|
625
|
+
parameters = [
|
|
626
|
+
attributes.MethodParameterInfo(self.read_u2(), constants.MethodParameterAccessFlag(self.read_u2()))
|
|
627
|
+
for _ in range(parameters_count)
|
|
628
|
+
]
|
|
629
|
+
return attributes.MethodParametersAttr(name_index, length, parameters_count, parameters)
|
|
630
|
+
|
|
631
|
+
elif attr_type is attributes.AttributeInfoType.MODULE:
|
|
632
|
+
module_name_index = self.read_u2()
|
|
633
|
+
module_flags = constants.ModuleAccessFlag(self.read_u2())
|
|
634
|
+
module_version_index = self.read_u2()
|
|
635
|
+
|
|
636
|
+
requires_count = self.read_u2()
|
|
637
|
+
requires = [
|
|
638
|
+
attributes.RequiresInfo(
|
|
639
|
+
self.read_u2(),
|
|
640
|
+
constants.ModuleRequiresAccessFlag(self.read_u2()),
|
|
641
|
+
self.read_u2(),
|
|
642
|
+
)
|
|
643
|
+
for _ in range(requires_count)
|
|
644
|
+
]
|
|
645
|
+
|
|
646
|
+
exports_count = self.read_u2()
|
|
647
|
+
exports: list[attributes.ExportInfo] = []
|
|
648
|
+
for _ in range(exports_count):
|
|
649
|
+
exports_index = self.read_u2()
|
|
650
|
+
exports_flags = constants.ModuleExportsAccessFlag(self.read_u2())
|
|
651
|
+
exports_to_count = self.read_u2()
|
|
652
|
+
exports_to_index = [self.read_u2() for __ in range(exports_to_count)]
|
|
653
|
+
exports.append(attributes.ExportInfo(exports_index, exports_flags, exports_to_count, exports_to_index))
|
|
654
|
+
|
|
655
|
+
opens_count = self.read_u2()
|
|
656
|
+
opens: list[attributes.OpensInfo] = []
|
|
657
|
+
for _ in range(opens_count):
|
|
658
|
+
opens_index = self.read_u2()
|
|
659
|
+
opens_flags = constants.ModuleOpensAccessFlag(self.read_u2())
|
|
660
|
+
opens_to_count = self.read_u2()
|
|
661
|
+
opens_to_index = [self.read_u2() for __ in range(opens_to_count)]
|
|
662
|
+
opens.append(attributes.OpensInfo(opens_index, opens_flags, opens_to_count, opens_to_index))
|
|
663
|
+
|
|
664
|
+
uses_count = self.read_u2()
|
|
665
|
+
uses = [self.read_u2() for _ in range(uses_count)]
|
|
666
|
+
|
|
667
|
+
provides_count = self.read_u2()
|
|
668
|
+
provides: list[attributes.ProvidesInfo] = []
|
|
669
|
+
for _ in range(provides_count):
|
|
670
|
+
provides_index = self.read_u2()
|
|
671
|
+
provides_with_count = self.read_u2()
|
|
672
|
+
provides_with_index = [self.read_u2() for __ in range(provides_with_count)]
|
|
673
|
+
provides.append(attributes.ProvidesInfo(provides_index, provides_with_count, provides_with_index))
|
|
674
|
+
|
|
675
|
+
return attributes.ModuleAttr(
|
|
676
|
+
name_index,
|
|
677
|
+
length,
|
|
678
|
+
module_name_index,
|
|
679
|
+
module_flags,
|
|
680
|
+
module_version_index,
|
|
681
|
+
requires_count,
|
|
682
|
+
requires,
|
|
683
|
+
exports_count,
|
|
684
|
+
exports,
|
|
685
|
+
opens_count,
|
|
686
|
+
opens,
|
|
687
|
+
uses_count,
|
|
688
|
+
uses,
|
|
689
|
+
provides_count,
|
|
690
|
+
provides,
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
elif attr_type is attributes.AttributeInfoType.MODULE_PACKAGES:
|
|
694
|
+
package_count = self.read_u2()
|
|
695
|
+
package_index = [self.read_u2() for _ in range(package_count)]
|
|
696
|
+
return attributes.ModulePackagesAttr(name_index, length, package_count, package_index)
|
|
697
|
+
|
|
698
|
+
elif attr_type is attributes.AttributeInfoType.NEST_MEMBERS:
|
|
699
|
+
number_of_classes = self.read_u2()
|
|
700
|
+
classes_list = [self.read_u2() for _ in range(number_of_classes)]
|
|
701
|
+
return attributes.NestMembersAttr(name_index, length, number_of_classes, classes_list)
|
|
702
|
+
|
|
703
|
+
elif attr_type is attributes.AttributeInfoType.RECORD:
|
|
704
|
+
components_count = self.read_u2()
|
|
705
|
+
components: list[attributes.RecordComponentInfo] = []
|
|
706
|
+
for _ in range(components_count):
|
|
707
|
+
comp_name_index = self.read_u2()
|
|
708
|
+
descriptor_index = self.read_u2()
|
|
709
|
+
attributes_count = self.read_u2()
|
|
710
|
+
_attributes = [self.read_attribute() for _ in range(attributes_count)]
|
|
711
|
+
components.append(
|
|
712
|
+
attributes.RecordComponentInfo(comp_name_index, descriptor_index, attributes_count, _attributes)
|
|
713
|
+
)
|
|
714
|
+
return attributes.RecordAttr(name_index, length, components_count, components)
|
|
715
|
+
|
|
716
|
+
elif attr_type is attributes.AttributeInfoType.PERMITTED_SUBCLASSES:
|
|
717
|
+
number_of_classes = self.read_u2()
|
|
718
|
+
classes_list = [self.read_u2() for _ in range(number_of_classes)]
|
|
719
|
+
return attributes.PermittedSubclassesAttr(name_index, length, number_of_classes, classes_list)
|
|
720
|
+
|
|
721
|
+
return attributes.UnimplementedAttr(name_index, length, self.read_bytes(length), attr_type)
|
|
722
|
+
|
|
723
|
+
def read_field(self) -> info.FieldInfo:
|
|
724
|
+
"""Read a single ``field_info`` structure (JVMS §4.5).
|
|
725
|
+
|
|
726
|
+
Returns:
|
|
727
|
+
The decoded field info including its attributes.
|
|
728
|
+
"""
|
|
729
|
+
access_flags = constants.FieldAccessFlag(self.read_u2())
|
|
730
|
+
name_index = self.read_u2()
|
|
731
|
+
descriptor_index = self.read_u2()
|
|
732
|
+
attributes_count = self.read_u2()
|
|
733
|
+
attributes = [self.read_attribute() for _ in range(attributes_count)]
|
|
734
|
+
return info.FieldInfo(access_flags, name_index, descriptor_index, attributes_count, attributes)
|
|
735
|
+
|
|
736
|
+
def read_method(self) -> info.MethodInfo:
|
|
737
|
+
"""Read a single ``method_info`` structure (JVMS §4.6).
|
|
738
|
+
|
|
739
|
+
Returns:
|
|
740
|
+
The decoded method info including its attributes.
|
|
741
|
+
"""
|
|
742
|
+
access_flags = constants.MethodAccessFlag(self.read_u2())
|
|
743
|
+
name_index = self.read_u2()
|
|
744
|
+
descriptor_index = self.read_u2()
|
|
745
|
+
attributes_count = self.read_u2()
|
|
746
|
+
attributes = [self.read_attribute() for _ in range(attributes_count)]
|
|
747
|
+
return info.MethodInfo(access_flags, name_index, descriptor_index, attributes_count, attributes)
|
|
748
|
+
|
|
749
|
+
def read_class(self) -> None:
|
|
750
|
+
"""Parse the complete ``ClassFile`` structure (JVMS §4.1).
|
|
751
|
+
|
|
752
|
+
Validates the magic number and version, reads the constant pool,
|
|
753
|
+
access flags, class hierarchy info, fields, methods, and attributes.
|
|
754
|
+
The result is stored in :attr:`class_info`.
|
|
755
|
+
|
|
756
|
+
Raises:
|
|
757
|
+
MalformedClassException: If the magic number or version is invalid.
|
|
758
|
+
"""
|
|
759
|
+
self.rewind()
|
|
760
|
+
magic = self.read_u4()
|
|
761
|
+
if magic != constants.MAGIC:
|
|
762
|
+
raise MalformedClassException(f"Invalid magic number 0x{magic:x}, requires 0x{constants.MAGIC:x}")
|
|
763
|
+
|
|
764
|
+
minor, major = self.read_u2(), self.read_u2()
|
|
765
|
+
if major >= 56 and minor not in (0, 65535):
|
|
766
|
+
raise MalformedClassException(f"Invalid version {major}/{minor}")
|
|
767
|
+
|
|
768
|
+
cp_count = self.read_u2()
|
|
769
|
+
|
|
770
|
+
self.constant_pool = [None] * cp_count
|
|
771
|
+
index = 1
|
|
772
|
+
while index < cp_count:
|
|
773
|
+
cp_info, index_extra = self.read_constant_pool_index(index)
|
|
774
|
+
self.constant_pool[index] = cp_info
|
|
775
|
+
index += 1 + index_extra
|
|
776
|
+
|
|
777
|
+
access_flags = constants.ClassAccessFlag(self.read_u2())
|
|
778
|
+
this_class = self.read_u2()
|
|
779
|
+
super_class = self.read_u2()
|
|
780
|
+
|
|
781
|
+
interfaces_count = self.read_u2()
|
|
782
|
+
interfaces = [self.read_u2() for _ in range(interfaces_count)]
|
|
783
|
+
|
|
784
|
+
fields_count = self.read_u2()
|
|
785
|
+
fields = [self.read_field() for _ in range(fields_count)]
|
|
786
|
+
|
|
787
|
+
methods_count = self.read_u2()
|
|
788
|
+
methods = [self.read_method() for _ in range(methods_count)]
|
|
789
|
+
|
|
790
|
+
attributes_count = self.read_u2()
|
|
791
|
+
attributes = [self.read_attribute() for _ in range(attributes_count)]
|
|
792
|
+
|
|
793
|
+
self.class_info = info.ClassFile(
|
|
794
|
+
magic,
|
|
795
|
+
minor,
|
|
796
|
+
major,
|
|
797
|
+
cp_count,
|
|
798
|
+
self.constant_pool,
|
|
799
|
+
access_flags,
|
|
800
|
+
this_class,
|
|
801
|
+
super_class,
|
|
802
|
+
interfaces_count,
|
|
803
|
+
interfaces,
|
|
804
|
+
fields_count,
|
|
805
|
+
fields,
|
|
806
|
+
methods_count,
|
|
807
|
+
methods,
|
|
808
|
+
attributes_count,
|
|
809
|
+
attributes,
|
|
810
|
+
)
|