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/operands.py
ADDED
|
@@ -0,0 +1,683 @@
|
|
|
1
|
+
"""Symbolic instruction operand wrappers for the editing model.
|
|
2
|
+
|
|
3
|
+
Provides editing-model instruction types that replace raw constant-pool
|
|
4
|
+
indexes and local-variable slot encodings with resolved symbolic values.
|
|
5
|
+
These types are used inside ``CodeModel.instructions`` and are lifted from
|
|
6
|
+
raw ``InsnInfo`` records during ``ClassModel.from_classfile()``, then
|
|
7
|
+
lowered back to spec-faithful ``InsnInfo`` records during
|
|
8
|
+
``to_classfile()``.
|
|
9
|
+
|
|
10
|
+
All wrapper types inherit from ``InsnInfo`` so the existing
|
|
11
|
+
``type CodeItem = InsnInfo | Label`` alias and ``_instruction_byte_size``
|
|
12
|
+
dispatch remain valid without changes to their signatures.
|
|
13
|
+
|
|
14
|
+
Covered instruction families:
|
|
15
|
+
Constant-pool-backed: field access, method invocation, type operations,
|
|
16
|
+
constant loading, invokedynamic, multianewarray.
|
|
17
|
+
|
|
18
|
+
Local-variable-backed: all load/store families (including implicit
|
|
19
|
+
``_0``–``_3`` variants and ``WIDE`` forms), ``RET``, ``IINC``.
|
|
20
|
+
|
|
21
|
+
Out of scope (remain raw ``InsnInfo`` records):
|
|
22
|
+
``BIPUSH`` / ``SIPUSH`` — immediate integer values, no CP or slot
|
|
23
|
+
reference. ``NEWARRAY`` — primitive-type enum, no CP reference.
|
|
24
|
+
No-operand instructions — nothing to symbolise. Branch / switch
|
|
25
|
+
instructions — already symbolic via ``labels.py``.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from dataclasses import dataclass
|
|
31
|
+
from typing import TYPE_CHECKING
|
|
32
|
+
|
|
33
|
+
from .instructions import (
|
|
34
|
+
InsnInfo,
|
|
35
|
+
InsnInfoType,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if TYPE_CHECKING:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
"FieldInsn",
|
|
43
|
+
"IIncInsn",
|
|
44
|
+
"InterfaceMethodInsn",
|
|
45
|
+
"InvokeDynamicInsn",
|
|
46
|
+
"LdcClass",
|
|
47
|
+
"LdcDouble",
|
|
48
|
+
"LdcDynamic",
|
|
49
|
+
"LdcFloat",
|
|
50
|
+
"LdcInsn",
|
|
51
|
+
"LdcInt",
|
|
52
|
+
"LdcLong",
|
|
53
|
+
"LdcMethodHandle",
|
|
54
|
+
"LdcMethodType",
|
|
55
|
+
"LdcString",
|
|
56
|
+
"LdcValue",
|
|
57
|
+
"MethodInsn",
|
|
58
|
+
"MultiANewArrayInsn",
|
|
59
|
+
"TypeInsn",
|
|
60
|
+
"VarInsn",
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
# ---------------------------------------------------------------------------
|
|
64
|
+
# Opcode classification sets (used for validation in __init__)
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
_FIELD_OPCODES: frozenset[InsnInfoType] = frozenset(
|
|
68
|
+
{
|
|
69
|
+
InsnInfoType.GETFIELD,
|
|
70
|
+
InsnInfoType.PUTFIELD,
|
|
71
|
+
InsnInfoType.GETSTATIC,
|
|
72
|
+
InsnInfoType.PUTSTATIC,
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
_METHOD_OPCODES: frozenset[InsnInfoType] = frozenset(
|
|
77
|
+
{
|
|
78
|
+
InsnInfoType.INVOKEVIRTUAL,
|
|
79
|
+
InsnInfoType.INVOKESPECIAL,
|
|
80
|
+
InsnInfoType.INVOKESTATIC,
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
_TYPE_OPCODES: frozenset[InsnInfoType] = frozenset(
|
|
85
|
+
{
|
|
86
|
+
InsnInfoType.NEW,
|
|
87
|
+
InsnInfoType.CHECKCAST,
|
|
88
|
+
InsnInfoType.INSTANCEOF,
|
|
89
|
+
InsnInfoType.ANEWARRAY,
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# All opcodes that normalize into VarInsn, keyed for fast lookup.
|
|
94
|
+
# Explicit-index forms: ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE,
|
|
95
|
+
# FSTORE, DSTORE, ASTORE, RET (LocalIndex); and their WIDE counterparts
|
|
96
|
+
# (LocalIndexW).
|
|
97
|
+
_VAR_EXPLICIT_OPCODES: frozenset[InsnInfoType] = frozenset(
|
|
98
|
+
{
|
|
99
|
+
InsnInfoType.ILOAD,
|
|
100
|
+
InsnInfoType.LLOAD,
|
|
101
|
+
InsnInfoType.FLOAD,
|
|
102
|
+
InsnInfoType.DLOAD,
|
|
103
|
+
InsnInfoType.ALOAD,
|
|
104
|
+
InsnInfoType.ISTORE,
|
|
105
|
+
InsnInfoType.LSTORE,
|
|
106
|
+
InsnInfoType.FSTORE,
|
|
107
|
+
InsnInfoType.DSTORE,
|
|
108
|
+
InsnInfoType.ASTORE,
|
|
109
|
+
InsnInfoType.RET,
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Base opcodes that are valid canonical types for VarInsn (accepted in __init__)
|
|
114
|
+
_VAR_BASE_OPCODES: frozenset[InsnInfoType] = _VAR_EXPLICIT_OPCODES
|
|
115
|
+
|
|
116
|
+
# ---------------------------------------------------------------------------
|
|
117
|
+
# Implicit-slot variant mapping
|
|
118
|
+
# Maps each _N opcode → (canonical_opcode, slot)
|
|
119
|
+
# ---------------------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
_IMPLICIT_VAR_SLOTS: dict[InsnInfoType, tuple[InsnInfoType, int]] = {
|
|
122
|
+
# ILOAD_0..3
|
|
123
|
+
InsnInfoType.ILOAD_0: (InsnInfoType.ILOAD, 0),
|
|
124
|
+
InsnInfoType.ILOAD_1: (InsnInfoType.ILOAD, 1),
|
|
125
|
+
InsnInfoType.ILOAD_2: (InsnInfoType.ILOAD, 2),
|
|
126
|
+
InsnInfoType.ILOAD_3: (InsnInfoType.ILOAD, 3),
|
|
127
|
+
# LLOAD_0..3
|
|
128
|
+
InsnInfoType.LLOAD_0: (InsnInfoType.LLOAD, 0),
|
|
129
|
+
InsnInfoType.LLOAD_1: (InsnInfoType.LLOAD, 1),
|
|
130
|
+
InsnInfoType.LLOAD_2: (InsnInfoType.LLOAD, 2),
|
|
131
|
+
InsnInfoType.LLOAD_3: (InsnInfoType.LLOAD, 3),
|
|
132
|
+
# FLOAD_0..3
|
|
133
|
+
InsnInfoType.FLOAD_0: (InsnInfoType.FLOAD, 0),
|
|
134
|
+
InsnInfoType.FLOAD_1: (InsnInfoType.FLOAD, 1),
|
|
135
|
+
InsnInfoType.FLOAD_2: (InsnInfoType.FLOAD, 2),
|
|
136
|
+
InsnInfoType.FLOAD_3: (InsnInfoType.FLOAD, 3),
|
|
137
|
+
# DLOAD_0..3
|
|
138
|
+
InsnInfoType.DLOAD_0: (InsnInfoType.DLOAD, 0),
|
|
139
|
+
InsnInfoType.DLOAD_1: (InsnInfoType.DLOAD, 1),
|
|
140
|
+
InsnInfoType.DLOAD_2: (InsnInfoType.DLOAD, 2),
|
|
141
|
+
InsnInfoType.DLOAD_3: (InsnInfoType.DLOAD, 3),
|
|
142
|
+
# ALOAD_0..3
|
|
143
|
+
InsnInfoType.ALOAD_0: (InsnInfoType.ALOAD, 0),
|
|
144
|
+
InsnInfoType.ALOAD_1: (InsnInfoType.ALOAD, 1),
|
|
145
|
+
InsnInfoType.ALOAD_2: (InsnInfoType.ALOAD, 2),
|
|
146
|
+
InsnInfoType.ALOAD_3: (InsnInfoType.ALOAD, 3),
|
|
147
|
+
# ISTORE_0..3
|
|
148
|
+
InsnInfoType.ISTORE_0: (InsnInfoType.ISTORE, 0),
|
|
149
|
+
InsnInfoType.ISTORE_1: (InsnInfoType.ISTORE, 1),
|
|
150
|
+
InsnInfoType.ISTORE_2: (InsnInfoType.ISTORE, 2),
|
|
151
|
+
InsnInfoType.ISTORE_3: (InsnInfoType.ISTORE, 3),
|
|
152
|
+
# LSTORE_0..3
|
|
153
|
+
InsnInfoType.LSTORE_0: (InsnInfoType.LSTORE, 0),
|
|
154
|
+
InsnInfoType.LSTORE_1: (InsnInfoType.LSTORE, 1),
|
|
155
|
+
InsnInfoType.LSTORE_2: (InsnInfoType.LSTORE, 2),
|
|
156
|
+
InsnInfoType.LSTORE_3: (InsnInfoType.LSTORE, 3),
|
|
157
|
+
# FSTORE_0..3
|
|
158
|
+
InsnInfoType.FSTORE_0: (InsnInfoType.FSTORE, 0),
|
|
159
|
+
InsnInfoType.FSTORE_1: (InsnInfoType.FSTORE, 1),
|
|
160
|
+
InsnInfoType.FSTORE_2: (InsnInfoType.FSTORE, 2),
|
|
161
|
+
InsnInfoType.FSTORE_3: (InsnInfoType.FSTORE, 3),
|
|
162
|
+
# DSTORE_0..3
|
|
163
|
+
InsnInfoType.DSTORE_0: (InsnInfoType.DSTORE, 0),
|
|
164
|
+
InsnInfoType.DSTORE_1: (InsnInfoType.DSTORE, 1),
|
|
165
|
+
InsnInfoType.DSTORE_2: (InsnInfoType.DSTORE, 2),
|
|
166
|
+
InsnInfoType.DSTORE_3: (InsnInfoType.DSTORE, 3),
|
|
167
|
+
# ASTORE_0..3
|
|
168
|
+
InsnInfoType.ASTORE_0: (InsnInfoType.ASTORE, 0),
|
|
169
|
+
InsnInfoType.ASTORE_1: (InsnInfoType.ASTORE, 1),
|
|
170
|
+
InsnInfoType.ASTORE_2: (InsnInfoType.ASTORE, 2),
|
|
171
|
+
InsnInfoType.ASTORE_3: (InsnInfoType.ASTORE, 3),
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# Reverse: (canonical_opcode, slot) → implicit opcode (for lowering)
|
|
175
|
+
_VAR_SHORTCUTS: dict[tuple[InsnInfoType, int], InsnInfoType] = {v: k for k, v in _IMPLICIT_VAR_SLOTS.items()}
|
|
176
|
+
|
|
177
|
+
# WIDE opcode → canonical base opcode
|
|
178
|
+
_WIDE_TO_BASE: dict[InsnInfoType, InsnInfoType] = {
|
|
179
|
+
InsnInfoType.ILOADW: InsnInfoType.ILOAD,
|
|
180
|
+
InsnInfoType.LLOADW: InsnInfoType.LLOAD,
|
|
181
|
+
InsnInfoType.FLOADW: InsnInfoType.FLOAD,
|
|
182
|
+
InsnInfoType.DLOADW: InsnInfoType.DLOAD,
|
|
183
|
+
InsnInfoType.ALOADW: InsnInfoType.ALOAD,
|
|
184
|
+
InsnInfoType.ISTOREW: InsnInfoType.ISTORE,
|
|
185
|
+
InsnInfoType.LSTOREW: InsnInfoType.LSTORE,
|
|
186
|
+
InsnInfoType.FSTOREW: InsnInfoType.FSTORE,
|
|
187
|
+
InsnInfoType.DSTOREW: InsnInfoType.DSTORE,
|
|
188
|
+
InsnInfoType.ASTOREW: InsnInfoType.ASTORE,
|
|
189
|
+
InsnInfoType.RETW: InsnInfoType.RET,
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
# Canonical base opcode → WIDE opcode (for lowering)
|
|
193
|
+
_BASE_TO_WIDE: dict[InsnInfoType, InsnInfoType] = {v: k for k, v in _WIDE_TO_BASE.items()}
|
|
194
|
+
|
|
195
|
+
_U1_MAX = 0xFF
|
|
196
|
+
_U2_MAX = 0xFFFF
|
|
197
|
+
_I2_MIN = -(1 << 15)
|
|
198
|
+
_I2_MAX = (1 << 15) - 1
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _require_u2(value: int, *, context: str) -> int:
|
|
202
|
+
if not 0 <= value <= _U2_MAX:
|
|
203
|
+
raise ValueError(f"{context} must be in range [0, {_U2_MAX}], got {value}")
|
|
204
|
+
return value
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _require_i2(value: int, *, context: str) -> int:
|
|
208
|
+
if not _I2_MIN <= value <= _I2_MAX:
|
|
209
|
+
raise ValueError(f"{context} must be in range [{_I2_MIN}, {_I2_MAX}], got {value}")
|
|
210
|
+
return value
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _require_u1(value: int, *, context: str, minimum: int = 0) -> int:
|
|
214
|
+
if not minimum <= value <= _U1_MAX:
|
|
215
|
+
raise ValueError(f"{context} must be in range [{minimum}, {_U1_MAX}], got {value}")
|
|
216
|
+
return value
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
# ---------------------------------------------------------------------------
|
|
220
|
+
# LDC value types (frozen dataclasses)
|
|
221
|
+
# ---------------------------------------------------------------------------
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@dataclass(frozen=True)
|
|
225
|
+
class LdcInt:
|
|
226
|
+
"""Integer constant for ``ldc`` / ``ldc_w`` (CONSTANT_Integer, §4.4.4).
|
|
227
|
+
|
|
228
|
+
Attributes:
|
|
229
|
+
value: The signed 32-bit integer constant.
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
value: int
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@dataclass(frozen=True)
|
|
236
|
+
class LdcFloat:
|
|
237
|
+
"""Float constant for ``ldc`` / ``ldc_w`` (CONSTANT_Float, §4.4.4).
|
|
238
|
+
|
|
239
|
+
Stored as a raw IEEE 754 bit pattern rather than a Python ``float`` to
|
|
240
|
+
preserve NaN bit patterns and signed zeros exactly.
|
|
241
|
+
|
|
242
|
+
Attributes:
|
|
243
|
+
raw_bits: IEEE 754 single-precision bit pattern as an unsigned
|
|
244
|
+
32-bit integer.
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
raw_bits: int
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@dataclass(frozen=True)
|
|
251
|
+
class LdcLong:
|
|
252
|
+
"""Long constant for ``ldc2_w`` (CONSTANT_Long, §4.4.5).
|
|
253
|
+
|
|
254
|
+
Attributes:
|
|
255
|
+
value: The signed 64-bit integer constant.
|
|
256
|
+
"""
|
|
257
|
+
|
|
258
|
+
value: int
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
@dataclass(frozen=True)
|
|
262
|
+
class LdcDouble:
|
|
263
|
+
"""Double constant for ``ldc2_w`` (CONSTANT_Double, §4.4.5).
|
|
264
|
+
|
|
265
|
+
Stored as split high/low 32-bit words to preserve exact bit patterns.
|
|
266
|
+
|
|
267
|
+
Attributes:
|
|
268
|
+
high_bytes: Upper 32 bits of the IEEE 754 double-precision bit
|
|
269
|
+
pattern.
|
|
270
|
+
low_bytes: Lower 32 bits of the IEEE 754 double-precision bit
|
|
271
|
+
pattern.
|
|
272
|
+
"""
|
|
273
|
+
|
|
274
|
+
high_bytes: int
|
|
275
|
+
low_bytes: int
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@dataclass(frozen=True)
|
|
279
|
+
class LdcString:
|
|
280
|
+
"""String constant for ``ldc`` / ``ldc_w`` (CONSTANT_String, §4.4.3).
|
|
281
|
+
|
|
282
|
+
Attributes:
|
|
283
|
+
value: The string constant value.
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
value: str
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
@dataclass(frozen=True)
|
|
290
|
+
class LdcClass:
|
|
291
|
+
"""Class literal for ``ldc`` / ``ldc_w`` (CONSTANT_Class, §4.4.1).
|
|
292
|
+
|
|
293
|
+
Attributes:
|
|
294
|
+
name: JVM internal class name (e.g. ``java/lang/Object``).
|
|
295
|
+
"""
|
|
296
|
+
|
|
297
|
+
name: str
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@dataclass(frozen=True)
|
|
301
|
+
class LdcMethodType:
|
|
302
|
+
"""MethodType constant for ``ldc`` / ``ldc_w`` (CONSTANT_MethodType, §4.4.9).
|
|
303
|
+
|
|
304
|
+
Attributes:
|
|
305
|
+
descriptor: JVM method descriptor (e.g. ``(II)V``).
|
|
306
|
+
"""
|
|
307
|
+
|
|
308
|
+
descriptor: str
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@dataclass(frozen=True)
|
|
312
|
+
class LdcMethodHandle:
|
|
313
|
+
"""MethodHandle constant for ``ldc`` / ``ldc_w`` (CONSTANT_MethodHandle, §4.4.8).
|
|
314
|
+
|
|
315
|
+
Attributes:
|
|
316
|
+
reference_kind: Method handle behaviour kind (1–9, per
|
|
317
|
+
JVMS Table 5.4.3.5-A). Kinds 1–4 reference a Fieldref;
|
|
318
|
+
5–8 reference a Methodref or InterfaceMethodref; 9 references
|
|
319
|
+
an InterfaceMethodref only.
|
|
320
|
+
owner: JVM internal name of the class owning the referenced member.
|
|
321
|
+
name: Name of the referenced field or method.
|
|
322
|
+
descriptor: JVM field or method descriptor of the referenced member.
|
|
323
|
+
is_interface: Whether the owner is an interface type. Controls
|
|
324
|
+
emission of CONSTANT_InterfaceMethodref vs CONSTANT_Methodref.
|
|
325
|
+
|
|
326
|
+
Raises:
|
|
327
|
+
ValueError: If ``reference_kind`` is outside the [1, 9] range.
|
|
328
|
+
"""
|
|
329
|
+
|
|
330
|
+
reference_kind: int
|
|
331
|
+
owner: str
|
|
332
|
+
name: str
|
|
333
|
+
descriptor: str
|
|
334
|
+
is_interface: bool = False
|
|
335
|
+
|
|
336
|
+
def __post_init__(self) -> None:
|
|
337
|
+
if not 1 <= self.reference_kind <= 9:
|
|
338
|
+
raise ValueError(f"reference_kind must be in range [1, 9], got {self.reference_kind}")
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
@dataclass(frozen=True)
|
|
342
|
+
class LdcDynamic:
|
|
343
|
+
"""Dynamic constant for ``ldc`` / ``ldc_w`` (CONSTANT_Dynamic / condy, §4.4.10).
|
|
344
|
+
|
|
345
|
+
Attributes:
|
|
346
|
+
bootstrap_method_attr_index: Index into the ``BootstrapMethods``
|
|
347
|
+
attribute (must fit the JVM ``u2`` range).
|
|
348
|
+
name: Symbolic name of the dynamic constant.
|
|
349
|
+
descriptor: JVM field descriptor of the produced value.
|
|
350
|
+
|
|
351
|
+
Raises:
|
|
352
|
+
ValueError: If ``bootstrap_method_attr_index`` exceeds the ``u2``
|
|
353
|
+
range.
|
|
354
|
+
"""
|
|
355
|
+
|
|
356
|
+
bootstrap_method_attr_index: int
|
|
357
|
+
name: str
|
|
358
|
+
descriptor: str
|
|
359
|
+
|
|
360
|
+
def __post_init__(self) -> None:
|
|
361
|
+
_require_u2(
|
|
362
|
+
self.bootstrap_method_attr_index,
|
|
363
|
+
context="bootstrap_method_attr_index",
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
type LdcValue = (
|
|
368
|
+
LdcInt | LdcFloat | LdcLong | LdcDouble | LdcString | LdcClass | LdcMethodType | LdcMethodHandle | LdcDynamic
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
# ---------------------------------------------------------------------------
|
|
372
|
+
# Symbolic instruction wrapper types
|
|
373
|
+
# ---------------------------------------------------------------------------
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
@dataclass(init=False)
|
|
377
|
+
class FieldInsn(InsnInfo):
|
|
378
|
+
"""Symbolic instruction for field access (§6.5.getfield, §6.5.putfield, etc.).
|
|
379
|
+
|
|
380
|
+
Wraps GETFIELD, PUTFIELD, GETSTATIC, and PUTSTATIC with resolved
|
|
381
|
+
symbolic references instead of raw constant-pool indices.
|
|
382
|
+
|
|
383
|
+
Attributes:
|
|
384
|
+
owner: JVM internal name of the field's declaring class
|
|
385
|
+
(e.g. ``java/lang/System``).
|
|
386
|
+
name: Field name.
|
|
387
|
+
descriptor: JVM field descriptor (e.g. ``I``,
|
|
388
|
+
``Ljava/lang/String;``).
|
|
389
|
+
|
|
390
|
+
Raises:
|
|
391
|
+
ValueError: If ``insn_type`` is not a field access opcode.
|
|
392
|
+
"""
|
|
393
|
+
|
|
394
|
+
owner: str
|
|
395
|
+
name: str
|
|
396
|
+
descriptor: str
|
|
397
|
+
|
|
398
|
+
def __init__(
|
|
399
|
+
self,
|
|
400
|
+
insn_type: InsnInfoType,
|
|
401
|
+
owner: str,
|
|
402
|
+
name: str,
|
|
403
|
+
descriptor: str,
|
|
404
|
+
bytecode_offset: int = -1,
|
|
405
|
+
) -> None:
|
|
406
|
+
if insn_type not in _FIELD_OPCODES:
|
|
407
|
+
raise ValueError(f"{insn_type.name} is not a field access opcode")
|
|
408
|
+
super().__init__(insn_type, bytecode_offset)
|
|
409
|
+
self.owner = owner
|
|
410
|
+
self.name = name
|
|
411
|
+
self.descriptor = descriptor
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
@dataclass(init=False)
|
|
415
|
+
class MethodInsn(InsnInfo):
|
|
416
|
+
"""Symbolic instruction for method invocation (§6.5.invokevirtual, etc.).
|
|
417
|
+
|
|
418
|
+
Wraps INVOKEVIRTUAL, INVOKESPECIAL, and INVOKESTATIC with resolved
|
|
419
|
+
symbolic references. Use ``InterfaceMethodInsn`` for INVOKEINTERFACE.
|
|
420
|
+
|
|
421
|
+
Attributes:
|
|
422
|
+
owner: JVM internal name of the method's declaring class or
|
|
423
|
+
interface.
|
|
424
|
+
name: Method name.
|
|
425
|
+
descriptor: JVM method descriptor (e.g. ``(II)I``).
|
|
426
|
+
is_interface: Whether ``owner`` is an interface type. Controls
|
|
427
|
+
emission of CONSTANT_InterfaceMethodref vs CONSTANT_Methodref
|
|
428
|
+
(relevant for INVOKESTATIC / INVOKESPECIAL on interface
|
|
429
|
+
methods since Java 8+).
|
|
430
|
+
|
|
431
|
+
Raises:
|
|
432
|
+
ValueError: If ``insn_type`` is not a supported method invocation
|
|
433
|
+
opcode.
|
|
434
|
+
"""
|
|
435
|
+
|
|
436
|
+
owner: str
|
|
437
|
+
name: str
|
|
438
|
+
descriptor: str
|
|
439
|
+
is_interface: bool
|
|
440
|
+
|
|
441
|
+
def __init__(
|
|
442
|
+
self,
|
|
443
|
+
insn_type: InsnInfoType,
|
|
444
|
+
owner: str,
|
|
445
|
+
name: str,
|
|
446
|
+
descriptor: str,
|
|
447
|
+
is_interface: bool = False,
|
|
448
|
+
bytecode_offset: int = -1,
|
|
449
|
+
) -> None:
|
|
450
|
+
if insn_type not in _METHOD_OPCODES:
|
|
451
|
+
raise ValueError(
|
|
452
|
+
f"{insn_type.name} is not a method invocation opcode (use InterfaceMethodInsn for INVOKEINTERFACE)"
|
|
453
|
+
)
|
|
454
|
+
super().__init__(insn_type, bytecode_offset)
|
|
455
|
+
self.owner = owner
|
|
456
|
+
self.name = name
|
|
457
|
+
self.descriptor = descriptor
|
|
458
|
+
self.is_interface = is_interface
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
@dataclass(init=False)
|
|
462
|
+
class InterfaceMethodInsn(InsnInfo):
|
|
463
|
+
"""Symbolic instruction for INVOKEINTERFACE (§6.5.invokeinterface).
|
|
464
|
+
|
|
465
|
+
The ``count`` operand (argument word count) is computed automatically
|
|
466
|
+
during lowering from the method descriptor; callers do not set it.
|
|
467
|
+
|
|
468
|
+
Attributes:
|
|
469
|
+
owner: JVM internal name of the interface declaring the method.
|
|
470
|
+
name: Method name.
|
|
471
|
+
descriptor: JVM method descriptor.
|
|
472
|
+
"""
|
|
473
|
+
|
|
474
|
+
owner: str
|
|
475
|
+
name: str
|
|
476
|
+
descriptor: str
|
|
477
|
+
|
|
478
|
+
def __init__(
|
|
479
|
+
self,
|
|
480
|
+
owner: str,
|
|
481
|
+
name: str,
|
|
482
|
+
descriptor: str,
|
|
483
|
+
bytecode_offset: int = -1,
|
|
484
|
+
) -> None:
|
|
485
|
+
super().__init__(InsnInfoType.INVOKEINTERFACE, bytecode_offset)
|
|
486
|
+
self.owner = owner
|
|
487
|
+
self.name = name
|
|
488
|
+
self.descriptor = descriptor
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
@dataclass(init=False)
|
|
492
|
+
class TypeInsn(InsnInfo):
|
|
493
|
+
"""Symbolic instruction for type operations (§6.5.new, §6.5.checkcast, etc.).
|
|
494
|
+
|
|
495
|
+
Wraps NEW, CHECKCAST, INSTANCEOF, and ANEWARRAY with resolved symbolic
|
|
496
|
+
class references.
|
|
497
|
+
|
|
498
|
+
Attributes:
|
|
499
|
+
class_name: JVM internal class name
|
|
500
|
+
(e.g. ``java/lang/StringBuilder``). For ANEWARRAY, the
|
|
501
|
+
element type's internal name or descriptor.
|
|
502
|
+
|
|
503
|
+
Raises:
|
|
504
|
+
ValueError: If ``insn_type`` is not a type instruction opcode.
|
|
505
|
+
"""
|
|
506
|
+
|
|
507
|
+
class_name: str
|
|
508
|
+
|
|
509
|
+
def __init__(
|
|
510
|
+
self,
|
|
511
|
+
insn_type: InsnInfoType,
|
|
512
|
+
class_name: str,
|
|
513
|
+
bytecode_offset: int = -1,
|
|
514
|
+
) -> None:
|
|
515
|
+
if insn_type not in _TYPE_OPCODES:
|
|
516
|
+
raise ValueError(f"{insn_type.name} is not a type instruction opcode")
|
|
517
|
+
super().__init__(insn_type, bytecode_offset)
|
|
518
|
+
self.class_name = class_name
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
@dataclass(init=False)
|
|
522
|
+
class VarInsn(InsnInfo):
|
|
523
|
+
"""Symbolic instruction for local variable access (§6.5.iload, §6.5.astore, etc.).
|
|
524
|
+
|
|
525
|
+
Normalises all implicit slot-encoded opcodes (``ILOAD_0``–``ASTORE_3``),
|
|
526
|
+
standard explicit forms (``ILOAD`` through ``ASTORE``, ``RET``), and WIDE
|
|
527
|
+
variants (``ILOADW``–``RETW``) into a single representation.
|
|
528
|
+
|
|
529
|
+
The ``type`` field always holds the *canonical* non-WIDE base opcode
|
|
530
|
+
(e.g. ``ILOAD``, not ``ILOAD_0`` or ``ILOADW``).
|
|
531
|
+
|
|
532
|
+
Lowering selects the optimal encoding automatically:
|
|
533
|
+
|
|
534
|
+
- slot 0–3 with a matching implicit form → implicit 1-byte opcode
|
|
535
|
+
- slot 0–255 → explicit 2-byte form (opcode + u1)
|
|
536
|
+
- slot 256–65535 → WIDE 4-byte form (WIDE prefix + opcode + u2)
|
|
537
|
+
|
|
538
|
+
RET has no implicit forms, but supports WIDE encoding for slots > 255.
|
|
539
|
+
|
|
540
|
+
Attributes:
|
|
541
|
+
slot: Local variable table index (``u2`` range, 0–65535).
|
|
542
|
+
|
|
543
|
+
Raises:
|
|
544
|
+
ValueError: If ``insn_type`` is not a local variable opcode or
|
|
545
|
+
``slot`` exceeds the ``u2`` range.
|
|
546
|
+
"""
|
|
547
|
+
|
|
548
|
+
slot: int
|
|
549
|
+
|
|
550
|
+
def __init__(
|
|
551
|
+
self,
|
|
552
|
+
insn_type: InsnInfoType,
|
|
553
|
+
slot: int,
|
|
554
|
+
bytecode_offset: int = -1,
|
|
555
|
+
) -> None:
|
|
556
|
+
if insn_type not in _VAR_BASE_OPCODES:
|
|
557
|
+
raise ValueError(f"{insn_type.name} is not a local variable instruction opcode")
|
|
558
|
+
super().__init__(insn_type, bytecode_offset)
|
|
559
|
+
self.slot = _require_u2(slot, context="local variable slot")
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
@dataclass(init=False)
|
|
563
|
+
class IIncInsn(InsnInfo):
|
|
564
|
+
"""Symbolic instruction for IINC / IINCW (§6.5.iinc).
|
|
565
|
+
|
|
566
|
+
Normalises both the standard and WIDE forms. Lowering selects the
|
|
567
|
+
appropriate encoding:
|
|
568
|
+
|
|
569
|
+
- slot 0–255 and increment fits in i1 (–128..127) → standard 3-byte form
|
|
570
|
+
- slot > 255 or increment outside i1 range → WIDE 6-byte form
|
|
571
|
+
|
|
572
|
+
Attributes:
|
|
573
|
+
slot: Local variable table index (``u2`` range, 0–65535).
|
|
574
|
+
increment: Signed increment value (``i2`` range, –32768..32767).
|
|
575
|
+
|
|
576
|
+
Raises:
|
|
577
|
+
ValueError: If ``slot`` exceeds the ``u2`` range or ``increment``
|
|
578
|
+
exceeds the ``i2`` range.
|
|
579
|
+
"""
|
|
580
|
+
|
|
581
|
+
slot: int
|
|
582
|
+
increment: int
|
|
583
|
+
|
|
584
|
+
def __init__(
|
|
585
|
+
self,
|
|
586
|
+
slot: int,
|
|
587
|
+
increment: int,
|
|
588
|
+
bytecode_offset: int = -1,
|
|
589
|
+
) -> None:
|
|
590
|
+
super().__init__(InsnInfoType.IINC, bytecode_offset)
|
|
591
|
+
self.slot = _require_u2(slot, context="local variable slot")
|
|
592
|
+
self.increment = _require_i2(increment, context="iinc increment")
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
@dataclass(init=False)
|
|
596
|
+
class LdcInsn(InsnInfo):
|
|
597
|
+
"""Symbolic instruction for constant loading (§6.5.ldc, §6.5.ldc_w, §6.5.ldc2_w).
|
|
598
|
+
|
|
599
|
+
Lowering selects the minimal encoding: ``ldc`` (2 bytes) when the
|
|
600
|
+
constant-pool index fits in one byte (≤ 255), ``ldc_w`` (3 bytes)
|
|
601
|
+
otherwise, for single-slot constants (int, float, string, class,
|
|
602
|
+
method-type, method-handle, dynamic). Double-slot constants (long,
|
|
603
|
+
double) always use ``ldc2_w`` (3 bytes).
|
|
604
|
+
|
|
605
|
+
Attributes:
|
|
606
|
+
value: Tagged constant determining the constant-pool entry type.
|
|
607
|
+
"""
|
|
608
|
+
|
|
609
|
+
value: LdcValue
|
|
610
|
+
|
|
611
|
+
def __init__(
|
|
612
|
+
self,
|
|
613
|
+
value: LdcValue,
|
|
614
|
+
bytecode_offset: int = -1,
|
|
615
|
+
) -> None:
|
|
616
|
+
super().__init__(InsnInfoType.LDC_W, bytecode_offset)
|
|
617
|
+
self.value = value
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
@dataclass(init=False)
|
|
621
|
+
class InvokeDynamicInsn(InsnInfo):
|
|
622
|
+
"""Symbolic instruction for INVOKEDYNAMIC (§6.5.invokedynamic).
|
|
623
|
+
|
|
624
|
+
Attributes:
|
|
625
|
+
bootstrap_method_attr_index: Index into the ``BootstrapMethods``
|
|
626
|
+
attribute (must fit the JVM ``u2`` range).
|
|
627
|
+
name: Symbolic method name resolved via the bootstrap method.
|
|
628
|
+
descriptor: JVM method descriptor of the call site.
|
|
629
|
+
|
|
630
|
+
Raises:
|
|
631
|
+
ValueError: If ``bootstrap_method_attr_index`` exceeds the ``u2``
|
|
632
|
+
range.
|
|
633
|
+
"""
|
|
634
|
+
|
|
635
|
+
bootstrap_method_attr_index: int
|
|
636
|
+
name: str
|
|
637
|
+
descriptor: str
|
|
638
|
+
|
|
639
|
+
def __init__(
|
|
640
|
+
self,
|
|
641
|
+
bootstrap_method_attr_index: int,
|
|
642
|
+
name: str,
|
|
643
|
+
descriptor: str,
|
|
644
|
+
bytecode_offset: int = -1,
|
|
645
|
+
) -> None:
|
|
646
|
+
super().__init__(InsnInfoType.INVOKEDYNAMIC, bytecode_offset)
|
|
647
|
+
self.bootstrap_method_attr_index = _require_u2(
|
|
648
|
+
bootstrap_method_attr_index,
|
|
649
|
+
context="bootstrap_method_attr_index",
|
|
650
|
+
)
|
|
651
|
+
self.name = name
|
|
652
|
+
self.descriptor = descriptor
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
@dataclass(init=False)
|
|
656
|
+
class MultiANewArrayInsn(InsnInfo):
|
|
657
|
+
"""Symbolic instruction for MULTIANEWARRAY (§6.5.multianewarray).
|
|
658
|
+
|
|
659
|
+
Attributes:
|
|
660
|
+
class_name: JVM internal name of the array type
|
|
661
|
+
(e.g. ``[[Ljava/lang/String;`` for ``String[][]``).
|
|
662
|
+
dimensions: Number of dimensions to allocate (``u1`` range, 1–255).
|
|
663
|
+
|
|
664
|
+
Raises:
|
|
665
|
+
ValueError: If ``dimensions`` is outside the [1, 255] range.
|
|
666
|
+
"""
|
|
667
|
+
|
|
668
|
+
class_name: str
|
|
669
|
+
dimensions: int
|
|
670
|
+
|
|
671
|
+
def __init__(
|
|
672
|
+
self,
|
|
673
|
+
class_name: str,
|
|
674
|
+
dimensions: int,
|
|
675
|
+
bytecode_offset: int = -1,
|
|
676
|
+
) -> None:
|
|
677
|
+
super().__init__(InsnInfoType.MULTIANEWARRAY, bytecode_offset)
|
|
678
|
+
self.class_name = class_name
|
|
679
|
+
self.dimensions = _require_u1(
|
|
680
|
+
dimensions,
|
|
681
|
+
context="multianewarray dimensions",
|
|
682
|
+
minimum=1,
|
|
683
|
+
)
|
pytecode/py.typed
ADDED
|
File without changes
|