Package not found. Please check the package name and try again.
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_writer.py
ADDED
|
@@ -0,0 +1,630 @@
|
|
|
1
|
+
"""Serialize a ClassFile tree into JVM ``.class`` file bytes (JVMS §4)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from . import attributes, constant_pool, instructions
|
|
6
|
+
from .bytes_utils import BytesWriter
|
|
7
|
+
from .info import ClassFile, FieldInfo, MethodInfo
|
|
8
|
+
|
|
9
|
+
__all__ = ["ClassWriter"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ClassWriter:
|
|
13
|
+
"""Serializer that converts a ``ClassFile`` tree into JVM ``.class`` bytes.
|
|
14
|
+
|
|
15
|
+
This writer walks every section of the ``ClassFile`` structure — constant
|
|
16
|
+
pool, fields, methods, and attributes — and emits the binary encoding
|
|
17
|
+
defined by the JVM class-file format (JVMS §4).
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def write(classfile: ClassFile) -> bytes:
|
|
22
|
+
"""Serialize a ``ClassFile`` into raw ``.class`` file bytes.
|
|
23
|
+
|
|
24
|
+
Encodes all sections of the class file (magic number, version,
|
|
25
|
+
constant pool, access flags, fields, methods, and attributes) into
|
|
26
|
+
the binary format specified by JVMS §4.1.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
classfile: The parsed class-file structure to serialize.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
The complete ``.class`` binary content.
|
|
33
|
+
"""
|
|
34
|
+
writer = BytesWriter()
|
|
35
|
+
_write_classfile(writer, classfile)
|
|
36
|
+
return writer.to_bytes()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _write_classfile(writer: BytesWriter, classfile: ClassFile) -> None:
|
|
40
|
+
writer.write_u4(classfile.magic)
|
|
41
|
+
writer.write_u2(classfile.minor_version)
|
|
42
|
+
writer.write_u2(classfile.major_version)
|
|
43
|
+
|
|
44
|
+
pool = classfile.constant_pool
|
|
45
|
+
writer.write_u2(len(pool))
|
|
46
|
+
for entry in _iter_constant_pool_entries(pool):
|
|
47
|
+
_write_constant_pool_entry(writer, entry)
|
|
48
|
+
|
|
49
|
+
writer.write_u2(int(classfile.access_flags))
|
|
50
|
+
writer.write_u2(classfile.this_class)
|
|
51
|
+
writer.write_u2(classfile.super_class)
|
|
52
|
+
|
|
53
|
+
writer.write_u2(len(classfile.interfaces))
|
|
54
|
+
for interface_index in classfile.interfaces:
|
|
55
|
+
writer.write_u2(interface_index)
|
|
56
|
+
|
|
57
|
+
writer.write_u2(len(classfile.fields))
|
|
58
|
+
for field in classfile.fields:
|
|
59
|
+
_write_field_info(writer, field)
|
|
60
|
+
|
|
61
|
+
writer.write_u2(len(classfile.methods))
|
|
62
|
+
for method in classfile.methods:
|
|
63
|
+
_write_method_info(writer, method)
|
|
64
|
+
|
|
65
|
+
_write_attributes(writer, classfile.attributes)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _iter_constant_pool_entries(
|
|
69
|
+
pool: list[constant_pool.ConstantPoolInfo | None],
|
|
70
|
+
) -> list[constant_pool.ConstantPoolInfo]:
|
|
71
|
+
if not pool:
|
|
72
|
+
raise ValueError("constant pool must include slot 0")
|
|
73
|
+
if pool[0] is not None:
|
|
74
|
+
raise ValueError("constant pool slot 0 must be None")
|
|
75
|
+
|
|
76
|
+
entries: list[constant_pool.ConstantPoolInfo] = []
|
|
77
|
+
expect_gap = False
|
|
78
|
+
for index in range(1, len(pool)):
|
|
79
|
+
entry = pool[index]
|
|
80
|
+
if expect_gap:
|
|
81
|
+
if entry is not None:
|
|
82
|
+
raise ValueError(f"constant pool slot {index} must be empty after a Long/Double entry")
|
|
83
|
+
expect_gap = False
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
if entry is None:
|
|
87
|
+
raise ValueError(f"constant pool slot {index} is unexpectedly empty")
|
|
88
|
+
|
|
89
|
+
entries.append(entry)
|
|
90
|
+
expect_gap = isinstance(entry, (constant_pool.LongInfo, constant_pool.DoubleInfo))
|
|
91
|
+
|
|
92
|
+
if expect_gap:
|
|
93
|
+
raise ValueError("constant pool is missing the trailing gap slot for a Long/Double entry")
|
|
94
|
+
|
|
95
|
+
return entries
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _write_constant_pool_entry(writer: BytesWriter, entry: constant_pool.ConstantPoolInfo) -> None:
|
|
99
|
+
writer.write_u1(entry.tag)
|
|
100
|
+
|
|
101
|
+
if isinstance(entry, constant_pool.ClassInfo):
|
|
102
|
+
writer.write_u2(entry.name_index)
|
|
103
|
+
elif isinstance(entry, constant_pool.StringInfo):
|
|
104
|
+
writer.write_u2(entry.string_index)
|
|
105
|
+
elif isinstance(entry, constant_pool.MethodTypeInfo):
|
|
106
|
+
writer.write_u2(entry.descriptor_index)
|
|
107
|
+
elif isinstance(entry, constant_pool.ModuleInfo):
|
|
108
|
+
writer.write_u2(entry.name_index)
|
|
109
|
+
elif isinstance(entry, constant_pool.PackageInfo):
|
|
110
|
+
writer.write_u2(entry.name_index)
|
|
111
|
+
elif isinstance(entry, constant_pool.FieldrefInfo):
|
|
112
|
+
writer.write_u2(entry.class_index)
|
|
113
|
+
writer.write_u2(entry.name_and_type_index)
|
|
114
|
+
elif isinstance(entry, constant_pool.MethodrefInfo):
|
|
115
|
+
writer.write_u2(entry.class_index)
|
|
116
|
+
writer.write_u2(entry.name_and_type_index)
|
|
117
|
+
elif isinstance(entry, constant_pool.InterfaceMethodrefInfo):
|
|
118
|
+
writer.write_u2(entry.class_index)
|
|
119
|
+
writer.write_u2(entry.name_and_type_index)
|
|
120
|
+
elif isinstance(entry, constant_pool.NameAndTypeInfo):
|
|
121
|
+
writer.write_u2(entry.name_index)
|
|
122
|
+
writer.write_u2(entry.descriptor_index)
|
|
123
|
+
elif isinstance(entry, constant_pool.DynamicInfo):
|
|
124
|
+
writer.write_u2(entry.bootstrap_method_attr_index)
|
|
125
|
+
writer.write_u2(entry.name_and_type_index)
|
|
126
|
+
elif isinstance(entry, constant_pool.InvokeDynamicInfo):
|
|
127
|
+
writer.write_u2(entry.bootstrap_method_attr_index)
|
|
128
|
+
writer.write_u2(entry.name_and_type_index)
|
|
129
|
+
elif isinstance(entry, constant_pool.IntegerInfo):
|
|
130
|
+
writer.write_u4(entry.value_bytes)
|
|
131
|
+
elif isinstance(entry, constant_pool.FloatInfo):
|
|
132
|
+
writer.write_u4(entry.value_bytes)
|
|
133
|
+
elif isinstance(entry, constant_pool.LongInfo):
|
|
134
|
+
writer.write_u4(entry.high_bytes)
|
|
135
|
+
writer.write_u4(entry.low_bytes)
|
|
136
|
+
elif isinstance(entry, constant_pool.DoubleInfo):
|
|
137
|
+
writer.write_u4(entry.high_bytes)
|
|
138
|
+
writer.write_u4(entry.low_bytes)
|
|
139
|
+
elif isinstance(entry, constant_pool.Utf8Info):
|
|
140
|
+
writer.write_u2(len(entry.str_bytes))
|
|
141
|
+
writer.write_bytes(entry.str_bytes)
|
|
142
|
+
elif isinstance(entry, constant_pool.MethodHandleInfo):
|
|
143
|
+
writer.write_u1(entry.reference_kind)
|
|
144
|
+
writer.write_u2(entry.reference_index)
|
|
145
|
+
else:
|
|
146
|
+
raise ValueError(f"Unsupported constant-pool entry type: {type(entry).__name__}")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _write_field_info(writer: BytesWriter, field: FieldInfo) -> None:
|
|
150
|
+
writer.write_u2(int(field.access_flags))
|
|
151
|
+
writer.write_u2(field.name_index)
|
|
152
|
+
writer.write_u2(field.descriptor_index)
|
|
153
|
+
_write_attributes(writer, field.attributes)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _write_method_info(writer: BytesWriter, method: MethodInfo) -> None:
|
|
157
|
+
writer.write_u2(int(method.access_flags))
|
|
158
|
+
writer.write_u2(method.name_index)
|
|
159
|
+
writer.write_u2(method.descriptor_index)
|
|
160
|
+
_write_attributes(writer, method.attributes)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _write_attributes(writer: BytesWriter, attrs: list[attributes.AttributeInfo]) -> None:
|
|
164
|
+
writer.write_u2(len(attrs))
|
|
165
|
+
for attr in attrs:
|
|
166
|
+
_write_attribute(writer, attr)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _write_attribute(writer: BytesWriter, attr: attributes.AttributeInfo) -> None:
|
|
170
|
+
payload_writer = BytesWriter()
|
|
171
|
+
_write_attribute_payload(payload_writer, attr)
|
|
172
|
+
payload = payload_writer.to_bytes()
|
|
173
|
+
|
|
174
|
+
writer.write_u2(attr.attribute_name_index)
|
|
175
|
+
writer.write_u4(len(payload))
|
|
176
|
+
writer.write_bytes(payload)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _write_attribute_payload(writer: BytesWriter, attr: attributes.AttributeInfo) -> None:
|
|
180
|
+
if isinstance(attr, (attributes.SyntheticAttr, attributes.DeprecatedAttr)):
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
if isinstance(attr, attributes.ConstantValueAttr):
|
|
184
|
+
writer.write_u2(attr.constantvalue_index)
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
if isinstance(attr, attributes.SignatureAttr):
|
|
188
|
+
writer.write_u2(attr.signature_index)
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
if isinstance(attr, attributes.SourceFileAttr):
|
|
192
|
+
writer.write_u2(attr.sourcefile_index)
|
|
193
|
+
return
|
|
194
|
+
|
|
195
|
+
if isinstance(attr, attributes.ModuleMainClassAttr):
|
|
196
|
+
writer.write_u2(attr.main_class_index)
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
if isinstance(attr, attributes.NestHostAttr):
|
|
200
|
+
writer.write_u2(attr.host_class_index)
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
if isinstance(attr, attributes.CodeAttr):
|
|
204
|
+
code_writer = BytesWriter()
|
|
205
|
+
for insn in attr.code:
|
|
206
|
+
_write_instruction(code_writer, insn)
|
|
207
|
+
code_bytes = code_writer.to_bytes()
|
|
208
|
+
|
|
209
|
+
writer.write_u2(attr.max_stacks)
|
|
210
|
+
writer.write_u2(attr.max_locals)
|
|
211
|
+
writer.write_u4(len(code_bytes))
|
|
212
|
+
writer.write_bytes(code_bytes)
|
|
213
|
+
writer.write_u2(len(attr.exception_table))
|
|
214
|
+
for exception in attr.exception_table:
|
|
215
|
+
writer.write_u2(exception.start_pc)
|
|
216
|
+
writer.write_u2(exception.end_pc)
|
|
217
|
+
writer.write_u2(exception.handler_pc)
|
|
218
|
+
writer.write_u2(exception.catch_type)
|
|
219
|
+
_write_attributes(writer, attr.attributes)
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
if isinstance(attr, attributes.StackMapTableAttr):
|
|
223
|
+
writer.write_u2(len(attr.entries))
|
|
224
|
+
for entry in attr.entries:
|
|
225
|
+
_write_stack_map_frame_info(writer, entry)
|
|
226
|
+
return
|
|
227
|
+
|
|
228
|
+
if isinstance(attr, attributes.ExceptionsAttr):
|
|
229
|
+
writer.write_u2(len(attr.exception_index_table))
|
|
230
|
+
for exception_index in attr.exception_index_table:
|
|
231
|
+
writer.write_u2(exception_index)
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
if isinstance(attr, attributes.InnerClassesAttr):
|
|
235
|
+
writer.write_u2(len(attr.classes))
|
|
236
|
+
for entry in attr.classes:
|
|
237
|
+
writer.write_u2(entry.inner_class_info_index)
|
|
238
|
+
writer.write_u2(entry.outer_class_info_index)
|
|
239
|
+
writer.write_u2(entry.inner_name_index)
|
|
240
|
+
writer.write_u2(int(entry.inner_class_access_flags))
|
|
241
|
+
return
|
|
242
|
+
|
|
243
|
+
if isinstance(attr, attributes.EnclosingMethodAttr):
|
|
244
|
+
writer.write_u2(attr.class_index)
|
|
245
|
+
writer.write_u2(attr.method_index)
|
|
246
|
+
return
|
|
247
|
+
|
|
248
|
+
if isinstance(attr, attributes.SourceDebugExtensionAttr):
|
|
249
|
+
writer.write_bytes(attr.debug_extension.encode("utf-8"))
|
|
250
|
+
return
|
|
251
|
+
|
|
252
|
+
if isinstance(attr, attributes.LineNumberTableAttr):
|
|
253
|
+
writer.write_u2(len(attr.line_number_table))
|
|
254
|
+
for entry in attr.line_number_table:
|
|
255
|
+
writer.write_u2(entry.start_pc)
|
|
256
|
+
writer.write_u2(entry.line_number)
|
|
257
|
+
return
|
|
258
|
+
|
|
259
|
+
if isinstance(attr, attributes.LocalVariableTableAttr):
|
|
260
|
+
writer.write_u2(len(attr.local_variable_table))
|
|
261
|
+
for entry in attr.local_variable_table:
|
|
262
|
+
writer.write_u2(entry.start_pc)
|
|
263
|
+
writer.write_u2(entry.length)
|
|
264
|
+
writer.write_u2(entry.name_index)
|
|
265
|
+
writer.write_u2(entry.descriptor_index)
|
|
266
|
+
writer.write_u2(entry.index)
|
|
267
|
+
return
|
|
268
|
+
|
|
269
|
+
if isinstance(attr, attributes.LocalVariableTypeTableAttr):
|
|
270
|
+
writer.write_u2(len(attr.local_variable_type_table))
|
|
271
|
+
for entry in attr.local_variable_type_table:
|
|
272
|
+
writer.write_u2(entry.start_pc)
|
|
273
|
+
writer.write_u2(entry.length)
|
|
274
|
+
writer.write_u2(entry.name_index)
|
|
275
|
+
writer.write_u2(entry.signature_index)
|
|
276
|
+
writer.write_u2(entry.index)
|
|
277
|
+
return
|
|
278
|
+
|
|
279
|
+
if isinstance(
|
|
280
|
+
attr,
|
|
281
|
+
(
|
|
282
|
+
attributes.RuntimeVisibleAnnotationsAttr,
|
|
283
|
+
attributes.RuntimeInvisibleAnnotationsAttr,
|
|
284
|
+
),
|
|
285
|
+
):
|
|
286
|
+
writer.write_u2(len(attr.annotations))
|
|
287
|
+
for annotation in attr.annotations:
|
|
288
|
+
_write_annotation_info(writer, annotation)
|
|
289
|
+
return
|
|
290
|
+
|
|
291
|
+
if isinstance(
|
|
292
|
+
attr,
|
|
293
|
+
(
|
|
294
|
+
attributes.RuntimeVisibleParameterAnnotationsAttr,
|
|
295
|
+
attributes.RuntimeInvisibleParameterAnnotationsAttr,
|
|
296
|
+
),
|
|
297
|
+
):
|
|
298
|
+
writer.write_u1(len(attr.parameter_annotations))
|
|
299
|
+
for parameter in attr.parameter_annotations:
|
|
300
|
+
writer.write_u2(len(parameter.annotations))
|
|
301
|
+
for annotation in parameter.annotations:
|
|
302
|
+
_write_annotation_info(writer, annotation)
|
|
303
|
+
return
|
|
304
|
+
|
|
305
|
+
if isinstance(
|
|
306
|
+
attr,
|
|
307
|
+
(
|
|
308
|
+
attributes.RuntimeVisibleTypeAnnotationsAttr,
|
|
309
|
+
attributes.RuntimeInvisibleTypeAnnotationsAttr,
|
|
310
|
+
),
|
|
311
|
+
):
|
|
312
|
+
writer.write_u2(len(attr.annotations))
|
|
313
|
+
for annotation in attr.annotations:
|
|
314
|
+
_write_type_annotation_info(writer, annotation)
|
|
315
|
+
return
|
|
316
|
+
|
|
317
|
+
if isinstance(attr, attributes.AnnotationDefaultAttr):
|
|
318
|
+
_write_element_value_info(writer, attr.default_value)
|
|
319
|
+
return
|
|
320
|
+
|
|
321
|
+
if isinstance(attr, attributes.BootstrapMethodsAttr):
|
|
322
|
+
writer.write_u2(len(attr.bootstrap_methods))
|
|
323
|
+
for method in attr.bootstrap_methods:
|
|
324
|
+
writer.write_u2(method.bootstrap_method_ref)
|
|
325
|
+
writer.write_u2(len(method.boostrap_arguments))
|
|
326
|
+
for argument in method.boostrap_arguments:
|
|
327
|
+
writer.write_u2(argument)
|
|
328
|
+
return
|
|
329
|
+
|
|
330
|
+
if isinstance(attr, attributes.MethodParametersAttr):
|
|
331
|
+
writer.write_u1(len(attr.parameters))
|
|
332
|
+
for parameter in attr.parameters:
|
|
333
|
+
writer.write_u2(parameter.name_index)
|
|
334
|
+
writer.write_u2(int(parameter.access_flags))
|
|
335
|
+
return
|
|
336
|
+
|
|
337
|
+
if isinstance(attr, attributes.ModuleAttr):
|
|
338
|
+
writer.write_u2(attr.module_name_index)
|
|
339
|
+
writer.write_u2(int(attr.module_flags))
|
|
340
|
+
writer.write_u2(attr.module_version_index)
|
|
341
|
+
|
|
342
|
+
writer.write_u2(len(attr.requires))
|
|
343
|
+
for require in attr.requires:
|
|
344
|
+
writer.write_u2(require.requires_index)
|
|
345
|
+
writer.write_u2(int(require.requires_flag))
|
|
346
|
+
writer.write_u2(require.requires_version_index)
|
|
347
|
+
|
|
348
|
+
writer.write_u2(len(attr.exports))
|
|
349
|
+
for export in attr.exports:
|
|
350
|
+
writer.write_u2(export.exports_index)
|
|
351
|
+
writer.write_u2(int(export.exports_flags))
|
|
352
|
+
writer.write_u2(len(export.exports_to_index))
|
|
353
|
+
for target in export.exports_to_index:
|
|
354
|
+
writer.write_u2(target)
|
|
355
|
+
|
|
356
|
+
writer.write_u2(len(attr.opens))
|
|
357
|
+
for opened in attr.opens:
|
|
358
|
+
writer.write_u2(opened.opens_index)
|
|
359
|
+
writer.write_u2(int(opened.opens_flags))
|
|
360
|
+
writer.write_u2(len(opened.opens_to_index))
|
|
361
|
+
for target in opened.opens_to_index:
|
|
362
|
+
writer.write_u2(target)
|
|
363
|
+
|
|
364
|
+
writer.write_u2(len(attr.uses_index))
|
|
365
|
+
for use in attr.uses_index:
|
|
366
|
+
writer.write_u2(use)
|
|
367
|
+
|
|
368
|
+
writer.write_u2(len(attr.provides))
|
|
369
|
+
for provide in attr.provides:
|
|
370
|
+
writer.write_u2(provide.provides_index)
|
|
371
|
+
writer.write_u2(len(provide.provides_with_index))
|
|
372
|
+
for implementation in provide.provides_with_index:
|
|
373
|
+
writer.write_u2(implementation)
|
|
374
|
+
return
|
|
375
|
+
|
|
376
|
+
if isinstance(attr, attributes.ModulePackagesAttr):
|
|
377
|
+
writer.write_u2(len(attr.package_index))
|
|
378
|
+
for package_index in attr.package_index:
|
|
379
|
+
writer.write_u2(package_index)
|
|
380
|
+
return
|
|
381
|
+
|
|
382
|
+
if isinstance(attr, attributes.NestMembersAttr):
|
|
383
|
+
writer.write_u2(len(attr.classes))
|
|
384
|
+
for class_index in attr.classes:
|
|
385
|
+
writer.write_u2(class_index)
|
|
386
|
+
return
|
|
387
|
+
|
|
388
|
+
if isinstance(attr, attributes.RecordAttr):
|
|
389
|
+
writer.write_u2(len(attr.components))
|
|
390
|
+
for component in attr.components:
|
|
391
|
+
writer.write_u2(component.name_index)
|
|
392
|
+
writer.write_u2(component.descriptor_index)
|
|
393
|
+
_write_attributes(writer, component.attributes)
|
|
394
|
+
return
|
|
395
|
+
|
|
396
|
+
if isinstance(attr, attributes.PermittedSubclassesAttr):
|
|
397
|
+
writer.write_u2(len(attr.classes))
|
|
398
|
+
for class_index in attr.classes:
|
|
399
|
+
writer.write_u2(class_index)
|
|
400
|
+
return
|
|
401
|
+
|
|
402
|
+
if isinstance(attr, attributes.UnimplementedAttr):
|
|
403
|
+
writer.write_bytes(attr.info)
|
|
404
|
+
return
|
|
405
|
+
|
|
406
|
+
raise ValueError(f"Unsupported attribute type: {type(attr).__name__}")
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def _write_stack_map_frame_info(writer: BytesWriter, entry: attributes.StackMapFrameInfo) -> None:
|
|
410
|
+
writer.write_u1(entry.frame_type)
|
|
411
|
+
|
|
412
|
+
if isinstance(entry, attributes.SameFrameInfo):
|
|
413
|
+
return
|
|
414
|
+
if isinstance(entry, attributes.SameLocals1StackItemFrameInfo):
|
|
415
|
+
_write_verification_type_info(writer, entry.stack)
|
|
416
|
+
return
|
|
417
|
+
if isinstance(entry, attributes.SameLocals1StackItemFrameExtendedInfo):
|
|
418
|
+
writer.write_u2(entry.offset_delta)
|
|
419
|
+
_write_verification_type_info(writer, entry.stack)
|
|
420
|
+
return
|
|
421
|
+
if isinstance(entry, attributes.ChopFrameInfo):
|
|
422
|
+
writer.write_u2(entry.offset_delta)
|
|
423
|
+
return
|
|
424
|
+
if isinstance(entry, attributes.SameFrameExtendedInfo):
|
|
425
|
+
writer.write_u2(entry.offset_delta)
|
|
426
|
+
return
|
|
427
|
+
if isinstance(entry, attributes.AppendFrameInfo):
|
|
428
|
+
writer.write_u2(entry.offset_delta)
|
|
429
|
+
for local in entry.locals:
|
|
430
|
+
_write_verification_type_info(writer, local)
|
|
431
|
+
return
|
|
432
|
+
if isinstance(entry, attributes.FullFrameInfo):
|
|
433
|
+
writer.write_u2(entry.offset_delta)
|
|
434
|
+
writer.write_u2(len(entry.locals))
|
|
435
|
+
for local in entry.locals:
|
|
436
|
+
_write_verification_type_info(writer, local)
|
|
437
|
+
writer.write_u2(len(entry.stack))
|
|
438
|
+
for stack_item in entry.stack:
|
|
439
|
+
_write_verification_type_info(writer, stack_item)
|
|
440
|
+
return
|
|
441
|
+
|
|
442
|
+
raise ValueError(f"Unsupported stack-map frame type: {type(entry).__name__}")
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _write_verification_type_info(writer: BytesWriter, entry: attributes.VerificationTypeInfo) -> None:
|
|
446
|
+
writer.write_u1(int(entry.tag))
|
|
447
|
+
|
|
448
|
+
if isinstance(entry, attributes.ObjectVariableInfo):
|
|
449
|
+
writer.write_u2(entry.cpool_index)
|
|
450
|
+
elif isinstance(entry, attributes.UninitializedVariableInfo):
|
|
451
|
+
writer.write_u2(entry.offset)
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def _write_annotation_info(writer: BytesWriter, annotation: attributes.AnnotationInfo) -> None:
|
|
455
|
+
writer.write_u2(annotation.type_index)
|
|
456
|
+
writer.write_u2(len(annotation.element_value_pairs))
|
|
457
|
+
for pair in annotation.element_value_pairs:
|
|
458
|
+
writer.write_u2(pair.element_name_index)
|
|
459
|
+
_write_element_value_info(writer, pair.element_value)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def _write_element_value_info(writer: BytesWriter, element_value: attributes.ElementValueInfo) -> None:
|
|
463
|
+
tag = _element_value_tag(element_value.tag)
|
|
464
|
+
writer.write_u1(tag)
|
|
465
|
+
|
|
466
|
+
if tag in {ord("B"), ord("C"), ord("D"), ord("F"), ord("I"), ord("J"), ord("S"), ord("Z"), ord("s")}:
|
|
467
|
+
if not isinstance(element_value.value, attributes.ConstValueInfo):
|
|
468
|
+
raise ValueError("const element value must carry ConstValueInfo")
|
|
469
|
+
writer.write_u2(element_value.value.const_value_index)
|
|
470
|
+
return
|
|
471
|
+
|
|
472
|
+
if tag == ord("e"):
|
|
473
|
+
if not isinstance(element_value.value, attributes.EnumConstantValueInfo):
|
|
474
|
+
raise ValueError("enum element value must carry EnumConstantValueInfo")
|
|
475
|
+
writer.write_u2(element_value.value.type_name_index)
|
|
476
|
+
writer.write_u2(element_value.value.const_name_index)
|
|
477
|
+
return
|
|
478
|
+
|
|
479
|
+
if tag == ord("c"):
|
|
480
|
+
if not isinstance(element_value.value, attributes.ClassInfoValueInfo):
|
|
481
|
+
raise ValueError("class element value must carry ClassInfoValueInfo")
|
|
482
|
+
writer.write_u2(element_value.value.class_info_index)
|
|
483
|
+
return
|
|
484
|
+
|
|
485
|
+
if tag == ord("@"):
|
|
486
|
+
if not isinstance(element_value.value, attributes.AnnotationInfo):
|
|
487
|
+
raise ValueError("annotation element value must carry AnnotationInfo")
|
|
488
|
+
_write_annotation_info(writer, element_value.value)
|
|
489
|
+
return
|
|
490
|
+
|
|
491
|
+
if tag == ord("["):
|
|
492
|
+
if not isinstance(element_value.value, attributes.ArrayValueInfo):
|
|
493
|
+
raise ValueError("array element value must carry ArrayValueInfo")
|
|
494
|
+
writer.write_u2(len(element_value.value.values))
|
|
495
|
+
for nested in element_value.value.values:
|
|
496
|
+
_write_element_value_info(writer, nested)
|
|
497
|
+
return
|
|
498
|
+
|
|
499
|
+
raise ValueError(f"Unsupported element-value tag: {tag!r}")
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def _element_value_tag(tag: int | str) -> int:
|
|
503
|
+
if isinstance(tag, int):
|
|
504
|
+
if not 0 <= tag <= 255:
|
|
505
|
+
raise ValueError(f"element-value tag must fit in u1, got {tag}")
|
|
506
|
+
return tag
|
|
507
|
+
if len(tag) != 1:
|
|
508
|
+
raise ValueError(f"element-value tag must be a single character, got {tag!r}")
|
|
509
|
+
return ord(tag)
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def _write_type_annotation_info(writer: BytesWriter, annotation: attributes.TypeAnnotationInfo) -> None:
|
|
513
|
+
writer.write_u1(annotation.target_type)
|
|
514
|
+
_write_target_info(writer, annotation.target_info)
|
|
515
|
+
_write_type_path_info(writer, annotation.target_path)
|
|
516
|
+
writer.write_u2(annotation.type_index)
|
|
517
|
+
writer.write_u2(len(annotation.element_value_pairs))
|
|
518
|
+
for pair in annotation.element_value_pairs:
|
|
519
|
+
writer.write_u2(pair.element_name_index)
|
|
520
|
+
_write_element_value_info(writer, pair.element_value)
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def _write_target_info(writer: BytesWriter, target_info: attributes.TargetInfo) -> None:
|
|
524
|
+
if isinstance(target_info, attributes.TypeParameterTargetInfo):
|
|
525
|
+
writer.write_u1(target_info.type_parameter_index)
|
|
526
|
+
return
|
|
527
|
+
if isinstance(target_info, attributes.SupertypeTargetInfo):
|
|
528
|
+
writer.write_u2(target_info.supertype_index)
|
|
529
|
+
return
|
|
530
|
+
if isinstance(target_info, attributes.TypeParameterBoundTargetInfo):
|
|
531
|
+
writer.write_u1(target_info.type_parameter_index)
|
|
532
|
+
writer.write_u1(target_info.bound_index)
|
|
533
|
+
return
|
|
534
|
+
if isinstance(target_info, attributes.EmptyTargetInfo):
|
|
535
|
+
return
|
|
536
|
+
if isinstance(target_info, attributes.FormalParameterTargetInfo):
|
|
537
|
+
writer.write_u1(target_info.formal_parameter_index)
|
|
538
|
+
return
|
|
539
|
+
if isinstance(target_info, attributes.ThrowsTargetInfo):
|
|
540
|
+
writer.write_u2(target_info.throws_type_index)
|
|
541
|
+
return
|
|
542
|
+
if isinstance(target_info, attributes.LocalvarTargetInfo):
|
|
543
|
+
writer.write_u2(len(target_info.table))
|
|
544
|
+
for table_entry in target_info.table:
|
|
545
|
+
writer.write_u2(table_entry.start_pc)
|
|
546
|
+
writer.write_u2(table_entry.length)
|
|
547
|
+
writer.write_u2(table_entry.index)
|
|
548
|
+
return
|
|
549
|
+
if isinstance(target_info, attributes.CatchTargetInfo):
|
|
550
|
+
writer.write_u2(target_info.exception_table_index)
|
|
551
|
+
return
|
|
552
|
+
if isinstance(target_info, attributes.OffsetTargetInfo):
|
|
553
|
+
writer.write_u2(target_info.offset)
|
|
554
|
+
return
|
|
555
|
+
if isinstance(target_info, attributes.TypeArgumentTargetInfo):
|
|
556
|
+
writer.write_u2(target_info.offset)
|
|
557
|
+
writer.write_u1(target_info.type_argument_index)
|
|
558
|
+
return
|
|
559
|
+
|
|
560
|
+
raise ValueError(f"Unsupported target-info type: {type(target_info).__name__}")
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
def _write_type_path_info(writer: BytesWriter, type_path: attributes.TypePathInfo) -> None:
|
|
564
|
+
writer.write_u1(len(type_path.path))
|
|
565
|
+
for path_item in type_path.path:
|
|
566
|
+
writer.write_u1(path_item.type_path_kind)
|
|
567
|
+
writer.write_u1(path_item.type_argument_index)
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def _write_instruction(writer: BytesWriter, insn: instructions.InsnInfo) -> None:
|
|
571
|
+
if isinstance(insn, instructions.LocalIndexW):
|
|
572
|
+
writer.write_u1(int(instructions.InsnInfoType.WIDE))
|
|
573
|
+
writer.write_u1(int(insn.type) - int(instructions.InsnInfoType.WIDE))
|
|
574
|
+
writer.write_u2(insn.index)
|
|
575
|
+
return
|
|
576
|
+
|
|
577
|
+
if isinstance(insn, instructions.IIncW):
|
|
578
|
+
writer.write_u1(int(instructions.InsnInfoType.WIDE))
|
|
579
|
+
writer.write_u1(int(insn.type) - int(instructions.InsnInfoType.WIDE))
|
|
580
|
+
writer.write_u2(insn.index)
|
|
581
|
+
writer.write_i2(insn.value)
|
|
582
|
+
return
|
|
583
|
+
|
|
584
|
+
writer.write_u1(int(insn.type))
|
|
585
|
+
|
|
586
|
+
if isinstance(insn, instructions.LocalIndex):
|
|
587
|
+
writer.write_u1(insn.index)
|
|
588
|
+
elif isinstance(insn, instructions.ConstPoolIndex):
|
|
589
|
+
writer.write_u2(insn.index)
|
|
590
|
+
elif isinstance(insn, instructions.ByteValue):
|
|
591
|
+
writer.write_i1(insn.value)
|
|
592
|
+
elif isinstance(insn, instructions.ShortValue):
|
|
593
|
+
writer.write_i2(insn.value)
|
|
594
|
+
elif isinstance(insn, instructions.Branch):
|
|
595
|
+
writer.write_i2(insn.offset)
|
|
596
|
+
elif isinstance(insn, instructions.BranchW):
|
|
597
|
+
writer.write_i4(insn.offset)
|
|
598
|
+
elif isinstance(insn, instructions.IInc):
|
|
599
|
+
writer.write_u1(insn.index)
|
|
600
|
+
writer.write_i1(insn.value)
|
|
601
|
+
elif isinstance(insn, instructions.InvokeDynamic):
|
|
602
|
+
if len(insn.unused) != 2:
|
|
603
|
+
raise ValueError("InvokeDynamic unused bytes must be exactly 2 bytes")
|
|
604
|
+
writer.write_u2(insn.index)
|
|
605
|
+
writer.write_bytes(insn.unused)
|
|
606
|
+
elif isinstance(insn, instructions.InvokeInterface):
|
|
607
|
+
if len(insn.unused) != 1:
|
|
608
|
+
raise ValueError("InvokeInterface unused bytes must be exactly 1 byte")
|
|
609
|
+
writer.write_u2(insn.index)
|
|
610
|
+
writer.write_u1(insn.count)
|
|
611
|
+
writer.write_bytes(insn.unused)
|
|
612
|
+
elif isinstance(insn, instructions.MultiANewArray):
|
|
613
|
+
writer.write_u2(insn.index)
|
|
614
|
+
writer.write_u1(insn.dimensions)
|
|
615
|
+
elif isinstance(insn, instructions.NewArray):
|
|
616
|
+
writer.write_u1(int(insn.atype))
|
|
617
|
+
elif isinstance(insn, instructions.LookupSwitch):
|
|
618
|
+
writer.align(4)
|
|
619
|
+
writer.write_i4(insn.default)
|
|
620
|
+
writer.write_u4(len(insn.pairs))
|
|
621
|
+
for pair in insn.pairs:
|
|
622
|
+
writer.write_i4(pair.match)
|
|
623
|
+
writer.write_i4(pair.offset)
|
|
624
|
+
elif isinstance(insn, instructions.TableSwitch):
|
|
625
|
+
writer.align(4)
|
|
626
|
+
writer.write_i4(insn.default)
|
|
627
|
+
writer.write_i4(insn.low)
|
|
628
|
+
writer.write_i4(insn.high)
|
|
629
|
+
for offset in insn.offsets:
|
|
630
|
+
writer.write_i4(offset)
|