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/descriptors.py
ADDED
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
"""JVM type descriptor and generic signature parsing utilities.
|
|
2
|
+
|
|
3
|
+
Provides structured representations for JVM field descriptors (§4.3.2),
|
|
4
|
+
method descriptors (§4.3.3), and generic signatures (§4.7.9.1), along
|
|
5
|
+
with parsing, construction, validation, and slot-counting helpers.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from enum import Enum
|
|
12
|
+
from typing import Literal, Never
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"ArrayType",
|
|
16
|
+
"ArrayTypeSignature",
|
|
17
|
+
"BaseType",
|
|
18
|
+
"ClassSignature",
|
|
19
|
+
"ClassTypeSignature",
|
|
20
|
+
"FieldDescriptor",
|
|
21
|
+
"FieldSignature",
|
|
22
|
+
"InnerClassType",
|
|
23
|
+
"JavaTypeSignature",
|
|
24
|
+
"MethodDescriptor",
|
|
25
|
+
"MethodSignature",
|
|
26
|
+
"ObjectType",
|
|
27
|
+
"ReferenceTypeSignature",
|
|
28
|
+
"ReturnType",
|
|
29
|
+
"TypeArgument",
|
|
30
|
+
"TypeParameter",
|
|
31
|
+
"TypeVariable",
|
|
32
|
+
"VOID",
|
|
33
|
+
"VoidType",
|
|
34
|
+
"is_valid_field_descriptor",
|
|
35
|
+
"is_valid_method_descriptor",
|
|
36
|
+
"parameter_slot_count",
|
|
37
|
+
"parse_class_signature",
|
|
38
|
+
"parse_field_descriptor",
|
|
39
|
+
"parse_field_signature",
|
|
40
|
+
"parse_method_descriptor",
|
|
41
|
+
"parse_method_signature",
|
|
42
|
+
"slot_size",
|
|
43
|
+
"to_descriptor",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
# Descriptor data model (JVM spec §4.3.2, §4.3.3)
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class BaseType(Enum):
|
|
52
|
+
"""JVM primitive types as defined in §4.3.2.
|
|
53
|
+
|
|
54
|
+
Each member carries its single-character descriptor code as its value.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
BOOLEAN = "Z"
|
|
58
|
+
BYTE = "B"
|
|
59
|
+
CHAR = "C"
|
|
60
|
+
SHORT = "S"
|
|
61
|
+
INT = "I"
|
|
62
|
+
LONG = "J"
|
|
63
|
+
FLOAT = "F"
|
|
64
|
+
DOUBLE = "D"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
_BASE_TYPE_BY_CHAR: dict[str, BaseType] = {t.value: t for t in BaseType}
|
|
68
|
+
|
|
69
|
+
_TWO_SLOT_TYPES = frozenset({BaseType.LONG, BaseType.DOUBLE})
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class VoidType(Enum):
|
|
73
|
+
"""Sentinel for the ``V`` return descriptor (§4.3.3).
|
|
74
|
+
|
|
75
|
+
Only valid in the return-type position of a method descriptor.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
VOID = "V"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
VOID = VoidType.VOID
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass(frozen=True, slots=True)
|
|
85
|
+
class ObjectType:
|
|
86
|
+
"""Reference to a class or interface type in internal form (§4.3.2).
|
|
87
|
+
|
|
88
|
+
Attributes:
|
|
89
|
+
class_name: Fully-qualified name using ``/`` as the package separator
|
|
90
|
+
(e.g. ``java/lang/String``).
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
class_name: str
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@dataclass(frozen=True, slots=True)
|
|
97
|
+
class ArrayType:
|
|
98
|
+
"""Array type whose component may be any field descriptor (§4.3.2).
|
|
99
|
+
|
|
100
|
+
The component may itself be an ``ArrayType``, representing multi-dimensional
|
|
101
|
+
arrays.
|
|
102
|
+
|
|
103
|
+
Attributes:
|
|
104
|
+
component_type: The element type of the array.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
component_type: FieldDescriptor
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass(frozen=True, slots=True)
|
|
111
|
+
class MethodDescriptor:
|
|
112
|
+
"""Parsed method descriptor representing parameter and return types (§4.3.3).
|
|
113
|
+
|
|
114
|
+
Attributes:
|
|
115
|
+
parameter_types: Ordered tuple of parameter type descriptors.
|
|
116
|
+
return_type: The method's return type, which may be ``VoidType.VOID``.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
parameter_types: tuple[FieldDescriptor, ...]
|
|
120
|
+
return_type: ReturnType
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
type FieldDescriptor = BaseType | ObjectType | ArrayType
|
|
124
|
+
type ReturnType = FieldDescriptor | VoidType
|
|
125
|
+
_INVALID_UNQUALIFIED_NAME_CHARS = frozenset({".", ";", "[", "/", "<", ">", ":"})
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# ---------------------------------------------------------------------------
|
|
129
|
+
# Generic signature data model (JVM spec §4.7.9.1)
|
|
130
|
+
# ---------------------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@dataclass(frozen=True, slots=True)
|
|
134
|
+
class TypeVariable:
|
|
135
|
+
"""Type variable reference (§4.7.9.1).
|
|
136
|
+
|
|
137
|
+
For example, ``TT;`` in a generic signature parses to
|
|
138
|
+
``TypeVariable("T")``.
|
|
139
|
+
|
|
140
|
+
Attributes:
|
|
141
|
+
name: The identifier of the type variable.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
name: str
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@dataclass(frozen=True, slots=True)
|
|
148
|
+
class TypeArgument:
|
|
149
|
+
"""A single type argument inside angle brackets (§4.7.9.1).
|
|
150
|
+
|
|
151
|
+
Attributes:
|
|
152
|
+
wildcard: ``"+"`` (extends), ``"-"`` (super), or ``None`` (exact match).
|
|
153
|
+
signature: The bounding type, or ``None`` for the unbounded wildcard
|
|
154
|
+
``*``.
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
wildcard: Literal["+", "-"] | None
|
|
158
|
+
signature: ReferenceTypeSignature | None
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@dataclass(frozen=True, slots=True)
|
|
162
|
+
class InnerClassType:
|
|
163
|
+
"""An inner-class suffix inside a ``ClassTypeSignature`` (§4.7.9.1).
|
|
164
|
+
|
|
165
|
+
Represents a ``.SimpleClassName<TypeArgs>`` segment that follows the
|
|
166
|
+
outer class type.
|
|
167
|
+
|
|
168
|
+
Attributes:
|
|
169
|
+
name: Simple name of the inner class.
|
|
170
|
+
type_arguments: Generic type arguments applied to this inner class.
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
name: str
|
|
174
|
+
type_arguments: tuple[TypeArgument, ...]
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@dataclass(frozen=True, slots=True)
|
|
178
|
+
class ClassTypeSignature:
|
|
179
|
+
"""Fully-qualified generic class type (§4.7.9.1).
|
|
180
|
+
|
|
181
|
+
Example: ``Ljava/util/Map<TK;TV;>.Entry<TK;TV;>;``.
|
|
182
|
+
|
|
183
|
+
Attributes:
|
|
184
|
+
package: Package prefix in internal form (e.g. ``java/util/``), or
|
|
185
|
+
empty string for classes in the default package.
|
|
186
|
+
name: Simple class name.
|
|
187
|
+
type_arguments: Generic type arguments on the outer class.
|
|
188
|
+
inner: Inner-class suffixes with their own type arguments.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
package: str
|
|
192
|
+
name: str
|
|
193
|
+
type_arguments: tuple[TypeArgument, ...]
|
|
194
|
+
inner: tuple[InnerClassType, ...]
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@dataclass(frozen=True, slots=True)
|
|
198
|
+
class ArrayTypeSignature:
|
|
199
|
+
"""Generic array type signature (§4.7.9.1).
|
|
200
|
+
|
|
201
|
+
For example, ``[TT;`` represents an array of a type variable.
|
|
202
|
+
|
|
203
|
+
Attributes:
|
|
204
|
+
component: The element type signature of the array.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
component: JavaTypeSignature
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
type ReferenceTypeSignature = ClassTypeSignature | TypeVariable | ArrayTypeSignature
|
|
211
|
+
type JavaTypeSignature = BaseType | ReferenceTypeSignature
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@dataclass(frozen=True, slots=True)
|
|
215
|
+
class TypeParameter:
|
|
216
|
+
"""A formal type parameter declaration (§4.7.9.1).
|
|
217
|
+
|
|
218
|
+
For example, ``T:Ljava/lang/Object;`` declares a type parameter ``T``
|
|
219
|
+
bounded by ``Object``.
|
|
220
|
+
|
|
221
|
+
Attributes:
|
|
222
|
+
name: The type parameter identifier.
|
|
223
|
+
class_bound: Optional upper class bound, or ``None`` if absent.
|
|
224
|
+
interface_bounds: Additional interface bounds the type parameter must
|
|
225
|
+
satisfy.
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
name: str
|
|
229
|
+
class_bound: ReferenceTypeSignature | None
|
|
230
|
+
interface_bounds: tuple[ReferenceTypeSignature, ...]
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@dataclass(frozen=True, slots=True)
|
|
234
|
+
class ClassSignature:
|
|
235
|
+
"""Parsed generic class signature from a ``Signature`` attribute (§4.7.9.1).
|
|
236
|
+
|
|
237
|
+
Attributes:
|
|
238
|
+
type_parameters: Formal type parameters declared by the class.
|
|
239
|
+
super_class: The generic superclass type.
|
|
240
|
+
super_interfaces: Generic superinterface types.
|
|
241
|
+
"""
|
|
242
|
+
|
|
243
|
+
type_parameters: tuple[TypeParameter, ...]
|
|
244
|
+
super_class: ClassTypeSignature
|
|
245
|
+
super_interfaces: tuple[ClassTypeSignature, ...]
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@dataclass(frozen=True, slots=True)
|
|
249
|
+
class MethodSignature:
|
|
250
|
+
"""Parsed generic method signature from a ``Signature`` attribute (§4.7.9.1).
|
|
251
|
+
|
|
252
|
+
Attributes:
|
|
253
|
+
type_parameters: Formal type parameters declared by the method.
|
|
254
|
+
parameter_types: Generic parameter type signatures.
|
|
255
|
+
return_type: The return type signature, or ``VoidType.VOID``.
|
|
256
|
+
throws: Exception types declared in the throws clause.
|
|
257
|
+
"""
|
|
258
|
+
|
|
259
|
+
type_parameters: tuple[TypeParameter, ...]
|
|
260
|
+
parameter_types: tuple[JavaTypeSignature, ...]
|
|
261
|
+
return_type: JavaTypeSignature | VoidType
|
|
262
|
+
throws: tuple[ClassTypeSignature | TypeVariable, ...]
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
type FieldSignature = ReferenceTypeSignature
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
# ---------------------------------------------------------------------------
|
|
269
|
+
# Internal parser helpers
|
|
270
|
+
# ---------------------------------------------------------------------------
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class _Reader:
|
|
274
|
+
"""Minimal cursor over a string for recursive-descent parsing."""
|
|
275
|
+
|
|
276
|
+
__slots__ = ("_s", "_pos")
|
|
277
|
+
|
|
278
|
+
def __init__(self, s: str) -> None:
|
|
279
|
+
self._s = s
|
|
280
|
+
self._pos = 0
|
|
281
|
+
|
|
282
|
+
@property
|
|
283
|
+
def pos(self) -> int:
|
|
284
|
+
return self._pos
|
|
285
|
+
|
|
286
|
+
def at_end(self) -> bool:
|
|
287
|
+
return self._pos >= len(self._s)
|
|
288
|
+
|
|
289
|
+
def peek(self) -> str:
|
|
290
|
+
if self._pos >= len(self._s):
|
|
291
|
+
self._fail("unexpected end of string")
|
|
292
|
+
return self._s[self._pos]
|
|
293
|
+
|
|
294
|
+
def advance(self) -> str:
|
|
295
|
+
ch = self.peek()
|
|
296
|
+
self._pos += 1
|
|
297
|
+
return ch
|
|
298
|
+
|
|
299
|
+
def expect(self, ch: str) -> None:
|
|
300
|
+
actual = self.advance()
|
|
301
|
+
if actual != ch:
|
|
302
|
+
self._fail(f"expected '{ch}', got '{actual}'", self._pos - 1)
|
|
303
|
+
|
|
304
|
+
def remaining(self) -> str:
|
|
305
|
+
return self._s[self._pos :]
|
|
306
|
+
|
|
307
|
+
def _fail(self, msg: str, pos: int | None = None) -> Never:
|
|
308
|
+
p = pos if pos is not None else self._pos
|
|
309
|
+
raise ValueError(f"{msg} at position {p} in {self._s!r}")
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _validate_unqualified_name(name: str, r: _Reader, start: int, context: str) -> None:
|
|
313
|
+
if not name:
|
|
314
|
+
r._fail(f"empty {context}", start)
|
|
315
|
+
for ch in name:
|
|
316
|
+
if ch in _INVALID_UNQUALIFIED_NAME_CHARS:
|
|
317
|
+
r._fail(f"invalid character '{ch}' in {context}", start)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _validate_internal_class_name(class_name: str, r: _Reader, start: int) -> None:
|
|
321
|
+
if not class_name:
|
|
322
|
+
r._fail("empty class name in object type", start)
|
|
323
|
+
|
|
324
|
+
segments = class_name.split("/")
|
|
325
|
+
if any(segment == "" for segment in segments):
|
|
326
|
+
r._fail("empty class name segment in object type", start)
|
|
327
|
+
|
|
328
|
+
for segment in segments:
|
|
329
|
+
_validate_unqualified_name(segment, r, start, "class name")
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def _split_class_type_identifier(r: _Reader, full_ident: str, start: int) -> tuple[str, str]:
|
|
333
|
+
if not full_ident:
|
|
334
|
+
r._fail("empty class name in class type signature", start)
|
|
335
|
+
|
|
336
|
+
segments = full_ident.split("/")
|
|
337
|
+
if any(segment == "" for segment in segments):
|
|
338
|
+
r._fail("empty class name segment in class type signature", start)
|
|
339
|
+
|
|
340
|
+
for segment in segments:
|
|
341
|
+
_validate_unqualified_name(segment, r, start, "class name")
|
|
342
|
+
|
|
343
|
+
if len(segments) == 1:
|
|
344
|
+
return "", segments[0]
|
|
345
|
+
return "/".join(segments[:-1]) + "/", segments[-1]
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def _read_field_descriptor(r: _Reader) -> FieldDescriptor:
|
|
349
|
+
ch = r.peek()
|
|
350
|
+
bt = _BASE_TYPE_BY_CHAR.get(ch)
|
|
351
|
+
if bt is not None:
|
|
352
|
+
r.advance()
|
|
353
|
+
return bt
|
|
354
|
+
if ch == "L":
|
|
355
|
+
return _read_object_type(r)
|
|
356
|
+
if ch == "[":
|
|
357
|
+
r.advance()
|
|
358
|
+
return ArrayType(_read_field_descriptor(r))
|
|
359
|
+
r._fail(f"invalid descriptor character '{ch}'")
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _read_object_type(r: _Reader) -> ObjectType:
|
|
363
|
+
r.expect("L")
|
|
364
|
+
start = r.pos
|
|
365
|
+
while r.peek() != ";":
|
|
366
|
+
r.advance()
|
|
367
|
+
class_name = r._s[start : r.pos]
|
|
368
|
+
r.expect(";")
|
|
369
|
+
_validate_internal_class_name(class_name, r, start)
|
|
370
|
+
return ObjectType(class_name)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def _read_return_type(r: _Reader) -> ReturnType:
|
|
374
|
+
if r.peek() == "V":
|
|
375
|
+
r.advance()
|
|
376
|
+
return VOID
|
|
377
|
+
return _read_field_descriptor(r)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
# ---------------------------------------------------------------------------
|
|
381
|
+
# Generic signature parser helpers
|
|
382
|
+
# ---------------------------------------------------------------------------
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def _read_reference_type_signature(r: _Reader) -> ReferenceTypeSignature:
|
|
386
|
+
ch = r.peek()
|
|
387
|
+
if ch == "L":
|
|
388
|
+
return _read_class_type_signature(r)
|
|
389
|
+
if ch == "T":
|
|
390
|
+
return _read_type_variable(r)
|
|
391
|
+
if ch == "[":
|
|
392
|
+
return _read_array_type_signature(r)
|
|
393
|
+
r._fail(f"expected reference type signature, got '{ch}'")
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def _read_java_type_signature(r: _Reader) -> JavaTypeSignature:
|
|
397
|
+
ch = r.peek()
|
|
398
|
+
bt = _BASE_TYPE_BY_CHAR.get(ch)
|
|
399
|
+
if bt is not None:
|
|
400
|
+
r.advance()
|
|
401
|
+
return bt
|
|
402
|
+
return _read_reference_type_signature(r)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def _read_type_variable(r: _Reader) -> TypeVariable:
|
|
406
|
+
r.expect("T")
|
|
407
|
+
start = r.pos
|
|
408
|
+
while r.peek() != ";":
|
|
409
|
+
r.advance()
|
|
410
|
+
name = r._s[start : r.pos]
|
|
411
|
+
r.expect(";")
|
|
412
|
+
_validate_unqualified_name(name, r, start, "type variable name")
|
|
413
|
+
return TypeVariable(name)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def _read_type_arguments(r: _Reader) -> tuple[TypeArgument, ...]:
|
|
417
|
+
r.expect("<")
|
|
418
|
+
args: list[TypeArgument] = []
|
|
419
|
+
while r.peek() != ">":
|
|
420
|
+
args.append(_read_type_argument(r))
|
|
421
|
+
r.expect(">")
|
|
422
|
+
return tuple(args)
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def _read_type_argument(r: _Reader) -> TypeArgument:
|
|
426
|
+
ch = r.peek()
|
|
427
|
+
if ch == "*":
|
|
428
|
+
r.advance()
|
|
429
|
+
return TypeArgument(wildcard=None, signature=None)
|
|
430
|
+
wildcard: Literal["+", "-"] | None = None
|
|
431
|
+
if ch in ("+", "-"):
|
|
432
|
+
wildcard = ch # pyright: ignore[reportAssignmentType]
|
|
433
|
+
r.advance()
|
|
434
|
+
sig = _read_reference_type_signature(r)
|
|
435
|
+
return TypeArgument(wildcard=wildcard, signature=sig)
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def _read_class_type_signature(r: _Reader) -> ClassTypeSignature:
|
|
439
|
+
r.expect("L")
|
|
440
|
+
start = r.pos
|
|
441
|
+
|
|
442
|
+
# Collect the full identifier path (package + simple name) first.
|
|
443
|
+
ident_chars: list[str] = []
|
|
444
|
+
while r.peek() not in ("<", ".", ";"):
|
|
445
|
+
ident_chars.append(r.advance())
|
|
446
|
+
|
|
447
|
+
full_ident = "".join(ident_chars)
|
|
448
|
+
package, name = _split_class_type_identifier(r, full_ident, start)
|
|
449
|
+
|
|
450
|
+
# Optional type arguments.
|
|
451
|
+
type_arguments: tuple[TypeArgument, ...] = ()
|
|
452
|
+
if not r.at_end() and r.peek() == "<":
|
|
453
|
+
type_arguments = _read_type_arguments(r)
|
|
454
|
+
|
|
455
|
+
# Inner class suffixes.
|
|
456
|
+
inner: list[InnerClassType] = []
|
|
457
|
+
while not r.at_end() and r.peek() == ".":
|
|
458
|
+
r.advance() # consume '.'
|
|
459
|
+
inner_start = r.pos
|
|
460
|
+
inner_name_chars: list[str] = []
|
|
461
|
+
while r.peek() not in ("<", ".", ";"):
|
|
462
|
+
inner_name_chars.append(r.advance())
|
|
463
|
+
inner_name = "".join(inner_name_chars)
|
|
464
|
+
_validate_unqualified_name(inner_name, r, inner_start, "inner class name")
|
|
465
|
+
inner_type_args: tuple[TypeArgument, ...] = ()
|
|
466
|
+
if not r.at_end() and r.peek() == "<":
|
|
467
|
+
inner_type_args = _read_type_arguments(r)
|
|
468
|
+
inner.append(InnerClassType(inner_name, inner_type_args))
|
|
469
|
+
|
|
470
|
+
r.expect(";")
|
|
471
|
+
return ClassTypeSignature(package, name, type_arguments, tuple(inner))
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def _read_array_type_signature(r: _Reader) -> ArrayTypeSignature:
|
|
475
|
+
r.expect("[")
|
|
476
|
+
component = _read_java_type_signature(r)
|
|
477
|
+
return ArrayTypeSignature(component)
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def _read_type_parameters(r: _Reader) -> tuple[TypeParameter, ...]:
|
|
481
|
+
r.expect("<")
|
|
482
|
+
params: list[TypeParameter] = []
|
|
483
|
+
while r.peek() != ">":
|
|
484
|
+
params.append(_read_type_parameter(r))
|
|
485
|
+
r.expect(">")
|
|
486
|
+
return tuple(params)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def _read_type_parameter(r: _Reader) -> TypeParameter:
|
|
490
|
+
# Identifier ':' [ClassBound] {':' InterfaceBound}
|
|
491
|
+
start = r.pos
|
|
492
|
+
name_chars: list[str] = []
|
|
493
|
+
while r.peek() != ":":
|
|
494
|
+
name_chars.append(r.advance())
|
|
495
|
+
name = "".join(name_chars)
|
|
496
|
+
if not name:
|
|
497
|
+
r._fail("empty type parameter name", start)
|
|
498
|
+
|
|
499
|
+
r.expect(":")
|
|
500
|
+
|
|
501
|
+
# Class bound — may be empty (just ':' followed by another ':' or next param or '>').
|
|
502
|
+
class_bound: ReferenceTypeSignature | None = None
|
|
503
|
+
if not r.at_end() and r.peek() not in (":", ">"):
|
|
504
|
+
class_bound = _read_reference_type_signature(r)
|
|
505
|
+
|
|
506
|
+
# Interface bounds (each prefixed by ':').
|
|
507
|
+
interface_bounds: list[ReferenceTypeSignature] = []
|
|
508
|
+
while not r.at_end() and r.peek() == ":":
|
|
509
|
+
r.advance() # consume ':'
|
|
510
|
+
interface_bounds.append(_read_reference_type_signature(r))
|
|
511
|
+
|
|
512
|
+
return TypeParameter(name, class_bound, tuple(interface_bounds))
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
def _read_return_type_signature(r: _Reader) -> JavaTypeSignature | VoidType:
|
|
516
|
+
if r.peek() == "V":
|
|
517
|
+
r.advance()
|
|
518
|
+
return VOID
|
|
519
|
+
return _read_java_type_signature(r)
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def _read_throws_signature(r: _Reader) -> ClassTypeSignature | TypeVariable:
|
|
523
|
+
r.expect("^")
|
|
524
|
+
ch = r.peek()
|
|
525
|
+
if ch == "L":
|
|
526
|
+
return _read_class_type_signature(r)
|
|
527
|
+
if ch == "T":
|
|
528
|
+
return _read_type_variable(r)
|
|
529
|
+
r._fail(f"expected class type or type variable after '^', got '{ch}'")
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
# ---------------------------------------------------------------------------
|
|
533
|
+
# Public parsing API
|
|
534
|
+
# ---------------------------------------------------------------------------
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
def parse_field_descriptor(s: str) -> FieldDescriptor:
|
|
538
|
+
"""Parse a JVM field descriptor string into a structured type (§4.3.2).
|
|
539
|
+
|
|
540
|
+
Args:
|
|
541
|
+
s: A field descriptor string such as ``"I"`` or
|
|
542
|
+
``"Ljava/lang/String;"``.
|
|
543
|
+
|
|
544
|
+
Returns:
|
|
545
|
+
The parsed descriptor as a ``BaseType``, ``ObjectType``, or
|
|
546
|
+
``ArrayType``.
|
|
547
|
+
|
|
548
|
+
Raises:
|
|
549
|
+
ValueError: If *s* is not a valid field descriptor.
|
|
550
|
+
|
|
551
|
+
Examples:
|
|
552
|
+
>>> parse_field_descriptor("Ljava/lang/String;")
|
|
553
|
+
ObjectType(class_name='java/lang/String')
|
|
554
|
+
>>> parse_field_descriptor("[[I")
|
|
555
|
+
ArrayType(component_type=ArrayType(component_type=<BaseType.INT: 'I'>))
|
|
556
|
+
"""
|
|
557
|
+
r = _Reader(s)
|
|
558
|
+
result = _read_field_descriptor(r)
|
|
559
|
+
if not r.at_end():
|
|
560
|
+
r._fail("trailing characters after field descriptor")
|
|
561
|
+
return result
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def parse_method_descriptor(s: str) -> MethodDescriptor:
|
|
565
|
+
"""Parse a JVM method descriptor string into parameter and return types (§4.3.3).
|
|
566
|
+
|
|
567
|
+
Args:
|
|
568
|
+
s: A method descriptor string such as
|
|
569
|
+
``"(IDLjava/lang/Thread;)Ljava/lang/Object;"``.
|
|
570
|
+
|
|
571
|
+
Returns:
|
|
572
|
+
A ``MethodDescriptor`` with the parsed parameter and return types.
|
|
573
|
+
|
|
574
|
+
Raises:
|
|
575
|
+
ValueError: If *s* is not a valid method descriptor.
|
|
576
|
+
|
|
577
|
+
Examples:
|
|
578
|
+
>>> parse_method_descriptor("(IDLjava/lang/Thread;)Ljava/lang/Object;")
|
|
579
|
+
MethodDescriptor(parameter_types=(...), return_type=ObjectType(...))
|
|
580
|
+
"""
|
|
581
|
+
r = _Reader(s)
|
|
582
|
+
r.expect("(")
|
|
583
|
+
params: list[FieldDescriptor] = []
|
|
584
|
+
while r.peek() != ")":
|
|
585
|
+
params.append(_read_field_descriptor(r))
|
|
586
|
+
r.expect(")")
|
|
587
|
+
ret = _read_return_type(r)
|
|
588
|
+
if not r.at_end():
|
|
589
|
+
r._fail("trailing characters after method descriptor")
|
|
590
|
+
return MethodDescriptor(tuple(params), ret)
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def parse_class_signature(s: str) -> ClassSignature:
|
|
594
|
+
"""Parse a generic class signature from a ``Signature`` attribute (§4.7.9.1).
|
|
595
|
+
|
|
596
|
+
Args:
|
|
597
|
+
s: A class signature string such as
|
|
598
|
+
``"<T:Ljava/lang/Object;>Ljava/lang/Object;"``.
|
|
599
|
+
|
|
600
|
+
Returns:
|
|
601
|
+
A ``ClassSignature`` with type parameters, superclass, and
|
|
602
|
+
superinterfaces.
|
|
603
|
+
|
|
604
|
+
Raises:
|
|
605
|
+
ValueError: If *s* is not a valid class signature.
|
|
606
|
+
|
|
607
|
+
Examples:
|
|
608
|
+
>>> parse_class_signature("<T:Ljava/lang/Object;>Ljava/lang/Object;")
|
|
609
|
+
ClassSignature(...)
|
|
610
|
+
"""
|
|
611
|
+
r = _Reader(s)
|
|
612
|
+
type_params: tuple[TypeParameter, ...] = ()
|
|
613
|
+
if r.peek() == "<":
|
|
614
|
+
type_params = _read_type_parameters(r)
|
|
615
|
+
super_class = _read_class_type_signature(r)
|
|
616
|
+
super_interfaces: list[ClassTypeSignature] = []
|
|
617
|
+
while not r.at_end():
|
|
618
|
+
super_interfaces.append(_read_class_type_signature(r))
|
|
619
|
+
return ClassSignature(type_params, super_class, tuple(super_interfaces))
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
def parse_method_signature(s: str) -> MethodSignature:
|
|
623
|
+
"""Parse a generic method signature from a ``Signature`` attribute (§4.7.9.1).
|
|
624
|
+
|
|
625
|
+
Args:
|
|
626
|
+
s: A method signature string such as
|
|
627
|
+
``"<T:Ljava/lang/Object;>(TT;)TT;"``.
|
|
628
|
+
|
|
629
|
+
Returns:
|
|
630
|
+
A ``MethodSignature`` with type parameters, parameter types, return
|
|
631
|
+
type, and throws clause.
|
|
632
|
+
|
|
633
|
+
Raises:
|
|
634
|
+
ValueError: If *s* is not a valid method signature.
|
|
635
|
+
|
|
636
|
+
Examples:
|
|
637
|
+
>>> parse_method_signature("<T:Ljava/lang/Object;>(TT;)TT;")
|
|
638
|
+
MethodSignature(...)
|
|
639
|
+
"""
|
|
640
|
+
r = _Reader(s)
|
|
641
|
+
type_params: tuple[TypeParameter, ...] = ()
|
|
642
|
+
if r.peek() == "<":
|
|
643
|
+
type_params = _read_type_parameters(r)
|
|
644
|
+
r.expect("(")
|
|
645
|
+
param_types: list[JavaTypeSignature] = []
|
|
646
|
+
while r.peek() != ")":
|
|
647
|
+
param_types.append(_read_java_type_signature(r))
|
|
648
|
+
r.expect(")")
|
|
649
|
+
ret = _read_return_type_signature(r)
|
|
650
|
+
throws: list[ClassTypeSignature | TypeVariable] = []
|
|
651
|
+
while not r.at_end():
|
|
652
|
+
throws.append(_read_throws_signature(r))
|
|
653
|
+
return MethodSignature(type_params, tuple(param_types), ret, tuple(throws))
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
def parse_field_signature(s: str) -> FieldSignature:
|
|
657
|
+
"""Parse a generic field type signature from a ``Signature`` attribute (§4.7.9.1).
|
|
658
|
+
|
|
659
|
+
Args:
|
|
660
|
+
s: A field signature string such as
|
|
661
|
+
``"Ljava/util/List<Ljava/lang/String;>;"``.
|
|
662
|
+
|
|
663
|
+
Returns:
|
|
664
|
+
The parsed reference type signature.
|
|
665
|
+
|
|
666
|
+
Raises:
|
|
667
|
+
ValueError: If *s* is not a valid field signature.
|
|
668
|
+
|
|
669
|
+
Examples:
|
|
670
|
+
>>> parse_field_signature("Ljava/util/List<Ljava/lang/String;>;")
|
|
671
|
+
ClassTypeSignature(...)
|
|
672
|
+
"""
|
|
673
|
+
r = _Reader(s)
|
|
674
|
+
result = _read_reference_type_signature(r)
|
|
675
|
+
if not r.at_end():
|
|
676
|
+
r._fail("trailing characters after field signature")
|
|
677
|
+
return result
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
# ---------------------------------------------------------------------------
|
|
681
|
+
# Descriptor string construction (round-trip)
|
|
682
|
+
# ---------------------------------------------------------------------------
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
def _field_descriptor_to_str(t: FieldDescriptor) -> str:
|
|
686
|
+
if isinstance(t, BaseType):
|
|
687
|
+
return t.value
|
|
688
|
+
if isinstance(t, ObjectType):
|
|
689
|
+
return f"L{t.class_name};"
|
|
690
|
+
if isinstance(t, ArrayType): # pyright: ignore[reportUnnecessaryIsInstance]
|
|
691
|
+
return f"[{_field_descriptor_to_str(t.component_type)}"
|
|
692
|
+
raise TypeError(f"unexpected descriptor type: {type(t)}") # pyright: ignore[reportUnreachable]
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
def to_descriptor(t: FieldDescriptor | MethodDescriptor) -> str:
|
|
696
|
+
"""Convert a structured descriptor back into its JVM string form (§4.3).
|
|
697
|
+
|
|
698
|
+
Args:
|
|
699
|
+
t: A field or method descriptor to serialize.
|
|
700
|
+
|
|
701
|
+
Returns:
|
|
702
|
+
The canonical JVM descriptor string.
|
|
703
|
+
|
|
704
|
+
Raises:
|
|
705
|
+
TypeError: If *t* is not a recognized descriptor type.
|
|
706
|
+
|
|
707
|
+
Examples:
|
|
708
|
+
>>> to_descriptor(BaseType.INT)
|
|
709
|
+
'I'
|
|
710
|
+
>>> to_descriptor(ObjectType("java/lang/String"))
|
|
711
|
+
'Ljava/lang/String;'
|
|
712
|
+
>>> to_descriptor(MethodDescriptor((BaseType.INT,), VOID))
|
|
713
|
+
'(I)V'
|
|
714
|
+
"""
|
|
715
|
+
if isinstance(t, MethodDescriptor):
|
|
716
|
+
params = "".join(_field_descriptor_to_str(p) for p in t.parameter_types)
|
|
717
|
+
ret = "V" if isinstance(t.return_type, VoidType) else _field_descriptor_to_str(t.return_type)
|
|
718
|
+
return f"({params}){ret}"
|
|
719
|
+
return _field_descriptor_to_str(t)
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
# ---------------------------------------------------------------------------
|
|
723
|
+
# Slot helpers
|
|
724
|
+
# ---------------------------------------------------------------------------
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
def slot_size(t: FieldDescriptor) -> int:
|
|
728
|
+
"""Return the number of JVM local/stack slots a type occupies.
|
|
729
|
+
|
|
730
|
+
``long`` and ``double`` use two slots (§2.6.1); all other types use one.
|
|
731
|
+
|
|
732
|
+
Args:
|
|
733
|
+
t: A field descriptor for the value type.
|
|
734
|
+
|
|
735
|
+
Returns:
|
|
736
|
+
``2`` for ``long``/``double``, ``1`` otherwise.
|
|
737
|
+
"""
|
|
738
|
+
if isinstance(t, BaseType) and t in _TWO_SLOT_TYPES:
|
|
739
|
+
return 2
|
|
740
|
+
return 1
|
|
741
|
+
|
|
742
|
+
|
|
743
|
+
def parameter_slot_count(d: MethodDescriptor) -> int:
|
|
744
|
+
"""Return the total number of JVM parameter slots for a method descriptor.
|
|
745
|
+
|
|
746
|
+
Does **not** include the implicit ``this`` slot for instance methods.
|
|
747
|
+
|
|
748
|
+
Args:
|
|
749
|
+
d: A parsed method descriptor.
|
|
750
|
+
|
|
751
|
+
Returns:
|
|
752
|
+
Sum of slot sizes across all parameter types.
|
|
753
|
+
"""
|
|
754
|
+
return sum(slot_size(p) for p in d.parameter_types)
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
# ---------------------------------------------------------------------------
|
|
758
|
+
# Validation helpers
|
|
759
|
+
# ---------------------------------------------------------------------------
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
def is_valid_field_descriptor(s: str) -> bool:
|
|
763
|
+
"""Return ``True`` if *s* is a well-formed JVM field descriptor (§4.3.2).
|
|
764
|
+
|
|
765
|
+
Args:
|
|
766
|
+
s: The string to validate.
|
|
767
|
+
|
|
768
|
+
Returns:
|
|
769
|
+
Whether *s* can be parsed as a valid field descriptor.
|
|
770
|
+
"""
|
|
771
|
+
try:
|
|
772
|
+
parse_field_descriptor(s)
|
|
773
|
+
return True
|
|
774
|
+
except ValueError:
|
|
775
|
+
return False
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
def is_valid_method_descriptor(s: str) -> bool:
|
|
779
|
+
"""Return ``True`` if *s* is a well-formed JVM method descriptor (§4.3.3).
|
|
780
|
+
|
|
781
|
+
Args:
|
|
782
|
+
s: The string to validate.
|
|
783
|
+
|
|
784
|
+
Returns:
|
|
785
|
+
Whether *s* can be parsed as a valid method descriptor.
|
|
786
|
+
"""
|
|
787
|
+
try:
|
|
788
|
+
parse_method_descriptor(s)
|
|
789
|
+
return True
|
|
790
|
+
except ValueError:
|
|
791
|
+
return False
|