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/hierarchy.py ADDED
@@ -0,0 +1,561 @@
1
+ """Class hierarchy resolution helpers for JVM internal names.
2
+
3
+ This module provides a small, pluggable foundation for hierarchy-aware JVM
4
+ analysis. It intentionally stays narrower than full verifier type merging:
5
+ it resolves class/interface metadata, answers subtype queries, walks ancestor
6
+ chains, finds common superclasses, and reports inherited method declarations
7
+ that a class method overrides.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from collections.abc import Iterable, Iterator
13
+ from dataclasses import dataclass
14
+ from typing import TYPE_CHECKING, Protocol
15
+
16
+ from .constant_pool import ClassInfo
17
+ from .constant_pool_builder import ConstantPoolBuilder
18
+ from .constants import ClassAccessFlag, MethodAccessFlag
19
+ from .info import ClassFile
20
+
21
+ if TYPE_CHECKING:
22
+ from .model import ClassModel
23
+
24
+ JAVA_LANG_OBJECT = "java/lang/Object"
25
+
26
+
27
+ class HierarchyError(Exception):
28
+ """Base class for hierarchy-resolution failures."""
29
+
30
+
31
+ class UnresolvedClassError(HierarchyError, LookupError):
32
+ """Raised when a hierarchy query needs a class the resolver cannot provide."""
33
+
34
+ def __init__(self, class_name: str) -> None:
35
+ """Initialize with the unresolved class name.
36
+
37
+ Args:
38
+ class_name: JVM internal name that could not be resolved.
39
+ """
40
+ self.class_name = class_name
41
+ super().__init__(f"Could not resolve class {class_name!r}")
42
+
43
+
44
+ class HierarchyCycleError(HierarchyError):
45
+ """Raised when a malformed class graph contains an ancestry cycle."""
46
+
47
+ def __init__(self, cycle: tuple[str, ...]) -> None:
48
+ """Initialize with the detected cycle.
49
+
50
+ Args:
51
+ cycle: Sequence of internal names forming the cycle.
52
+ """
53
+ self.cycle = cycle
54
+ joined = " -> ".join(cycle)
55
+ super().__init__(f"Hierarchy cycle detected: {joined}")
56
+
57
+
58
+ @dataclass(frozen=True, slots=True)
59
+ class ResolvedMethod:
60
+ """A method declaration resolved out of raw class metadata.
61
+
62
+ Attributes:
63
+ name: Method name (e.g. ``<init>`` or ``toString``).
64
+ descriptor: JVM method descriptor (e.g. ``(I)V``).
65
+ access_flags: Bitfield of method access and property flags.
66
+ """
67
+
68
+ name: str
69
+ descriptor: str
70
+ access_flags: MethodAccessFlag
71
+
72
+
73
+ @dataclass(frozen=True, slots=True)
74
+ class ResolvedClass:
75
+ """Resolved hierarchy snapshot for one class or interface.
76
+
77
+ Attributes:
78
+ name: JVM internal name (e.g. ``java/lang/String``).
79
+ super_name: Internal name of the direct superclass, or ``None`` for ``java/lang/Object``.
80
+ interfaces: Internal names of directly implemented interfaces.
81
+ access_flags: Bitfield of class access and property flags.
82
+ methods: Declared methods extracted from the class.
83
+ """
84
+
85
+ name: str
86
+ super_name: str | None
87
+ interfaces: tuple[str, ...]
88
+ access_flags: ClassAccessFlag
89
+ methods: tuple[ResolvedMethod, ...] = ()
90
+
91
+ @property
92
+ def is_interface(self) -> bool:
93
+ """Whether this entry represents an interface."""
94
+
95
+ return bool(self.access_flags & ClassAccessFlag.INTERFACE)
96
+
97
+ def find_method(self, name: str, descriptor: str) -> ResolvedMethod | None:
98
+ """Return the declared method with the given signature, if present.
99
+
100
+ Args:
101
+ name: Method name to match.
102
+ descriptor: JVM method descriptor to match.
103
+
104
+ Returns:
105
+ The matching method, or ``None`` if not declared in this class.
106
+ """
107
+
108
+ for method in self.methods:
109
+ if method.name == name and method.descriptor == descriptor:
110
+ return method
111
+ return None
112
+
113
+ @classmethod
114
+ def from_classfile(cls, classfile: ClassFile) -> ResolvedClass:
115
+ """Build a resolved hierarchy snapshot from a parsed ``ClassFile``.
116
+
117
+ Args:
118
+ classfile: A parsed JVM class file.
119
+
120
+ Returns:
121
+ A new ``ResolvedClass`` populated from the class file metadata.
122
+ """
123
+
124
+ cp = ConstantPoolBuilder.from_pool(classfile.constant_pool)
125
+ methods = tuple(
126
+ ResolvedMethod(
127
+ cp.resolve_utf8(method.name_index),
128
+ cp.resolve_utf8(method.descriptor_index),
129
+ method.access_flags,
130
+ )
131
+ for method in classfile.methods
132
+ )
133
+ return cls(
134
+ name=_resolve_class_name(cp, classfile.this_class),
135
+ super_name=None if classfile.super_class == 0 else _resolve_class_name(cp, classfile.super_class),
136
+ interfaces=tuple(_resolve_class_name(cp, index) for index in classfile.interfaces),
137
+ access_flags=classfile.access_flags,
138
+ methods=methods,
139
+ )
140
+
141
+ @classmethod
142
+ def from_model(cls, model: ClassModel) -> ResolvedClass:
143
+ """Build a resolved hierarchy snapshot from a ``ClassModel``.
144
+
145
+ Args:
146
+ model: A high-level class model.
147
+
148
+ Returns:
149
+ A new ``ResolvedClass`` populated from the model.
150
+ """
151
+
152
+ return cls(
153
+ name=model.name,
154
+ super_name=model.super_name,
155
+ interfaces=tuple(model.interfaces),
156
+ access_flags=model.access_flags,
157
+ methods=tuple(
158
+ ResolvedMethod(method.name, method.descriptor, method.access_flags) for method in model.methods
159
+ ),
160
+ )
161
+
162
+
163
+ @dataclass(frozen=True, slots=True)
164
+ class InheritedMethod:
165
+ """A matching inherited method declaration found in a supertype.
166
+
167
+ Attributes:
168
+ owner: Internal name of the class that declares the method.
169
+ name: The method name.
170
+ descriptor: The JVM method descriptor.
171
+ access_flags: Bitfield of method access and property flags.
172
+ """
173
+
174
+ owner: str
175
+ name: str
176
+ descriptor: str
177
+ access_flags: MethodAccessFlag
178
+
179
+
180
+ class ClassResolver(Protocol):
181
+ """Protocol for supplying resolved class metadata by internal name."""
182
+
183
+ def resolve_class(self, class_name: str) -> ResolvedClass | None:
184
+ """Return resolved metadata for *class_name*, or ``None`` if unavailable.
185
+
186
+ Args:
187
+ class_name: JVM internal class name to resolve.
188
+
189
+ Returns:
190
+ Resolved class metadata, or ``None`` if the class cannot be found.
191
+ """
192
+
193
+
194
+ class MappingClassResolver:
195
+ """Simple in-memory ``ClassResolver`` backed by resolved class snapshots."""
196
+
197
+ __slots__ = ("_classes",)
198
+
199
+ def __init__(self, classes: Iterable[ResolvedClass]) -> None:
200
+ """Initialize from an iterable of resolved class snapshots.
201
+
202
+ Args:
203
+ classes: Resolved class entries to index by internal name.
204
+
205
+ Raises:
206
+ ValueError: If duplicate class names are found.
207
+ """
208
+ mapping: dict[str, ResolvedClass] = {}
209
+ for resolved in classes:
210
+ if resolved.name in mapping:
211
+ raise ValueError(f"Duplicate resolved class {resolved.name!r}")
212
+ mapping[resolved.name] = resolved
213
+ self._classes = mapping
214
+
215
+ def resolve_class(self, class_name: str) -> ResolvedClass | None:
216
+ """Resolve *class_name* from the in-memory mapping.
217
+
218
+ Args:
219
+ class_name: JVM internal class name to look up.
220
+
221
+ Returns:
222
+ Resolved class metadata, or ``None`` if not in the mapping.
223
+ """
224
+
225
+ return self._classes.get(class_name)
226
+
227
+ @classmethod
228
+ def from_classfiles(cls, classfiles: Iterable[ClassFile]) -> MappingClassResolver:
229
+ """Build a resolver from parsed ``ClassFile`` objects.
230
+
231
+ Args:
232
+ classfiles: Parsed JVM class files to index.
233
+
234
+ Returns:
235
+ A new resolver backed by the given class files.
236
+ """
237
+
238
+ return cls(ResolvedClass.from_classfile(classfile) for classfile in classfiles)
239
+
240
+ @classmethod
241
+ def from_models(cls, models: Iterable[ClassModel]) -> MappingClassResolver:
242
+ """Build a resolver from ``ClassModel`` objects.
243
+
244
+ Args:
245
+ models: High-level class models to index.
246
+
247
+ Returns:
248
+ A new resolver backed by the given models.
249
+ """
250
+
251
+ return cls(ResolvedClass.from_model(model) for model in models)
252
+
253
+
254
+ _IMPLICIT_OBJECT = ResolvedClass(
255
+ name=JAVA_LANG_OBJECT,
256
+ super_name=None,
257
+ interfaces=(),
258
+ access_flags=ClassAccessFlag.PUBLIC | ClassAccessFlag.SUPER,
259
+ methods=(),
260
+ )
261
+ _NON_OVERRIDABLE_METHOD_FLAGS = MethodAccessFlag.PRIVATE | MethodAccessFlag.STATIC | MethodAccessFlag.FINAL
262
+
263
+
264
+ def iter_superclasses(
265
+ resolver: ClassResolver,
266
+ class_name: str,
267
+ *,
268
+ include_self: bool = False,
269
+ ) -> Iterator[ResolvedClass]:
270
+ """Yield the linear superclass chain for *class_name*.
271
+
272
+ The returned chain always terminates at ``java/lang/Object``. If the
273
+ resolver does not explicitly provide that root class, an implicit stub is
274
+ used so ordinary user-defined class hierarchies can still terminate cleanly.
275
+
276
+ Args:
277
+ resolver: Provider of class metadata.
278
+ class_name: JVM internal name of the starting class.
279
+ include_self: If ``True``, include the starting class itself.
280
+
281
+ Yields:
282
+ Each superclass from the immediate parent up to ``java/lang/Object``.
283
+
284
+ Raises:
285
+ UnresolvedClassError: If a class in the chain cannot be resolved.
286
+ HierarchyCycleError: If a cycle is detected in the superclass chain.
287
+ """
288
+
289
+ current_name = class_name if include_self else _resolve_required(resolver, class_name).super_name
290
+ seen: set[str] = set()
291
+ path: list[str] = [class_name]
292
+
293
+ while current_name is not None:
294
+ if current_name in seen:
295
+ cycle_start = path.index(current_name) if current_name in path else 0
296
+ cycle = tuple(path[cycle_start:] + [current_name])
297
+ raise HierarchyCycleError(cycle)
298
+
299
+ seen.add(current_name)
300
+ current = _resolve_required(resolver, current_name)
301
+ yield current
302
+ if path[-1] != current.name:
303
+ path.append(current.name)
304
+ current_name = current.super_name
305
+
306
+
307
+ def iter_supertypes(
308
+ resolver: ClassResolver,
309
+ class_name: str,
310
+ *,
311
+ include_self: bool = False,
312
+ ) -> Iterator[ResolvedClass]:
313
+ """Yield all reachable supertypes of *class_name*.
314
+
315
+ Traversal is deterministic: the direct superclass chain is explored before
316
+ interface edges, and diamond/interface duplicates are yielded only once.
317
+ Malformed ancestry cycles still raise ``HierarchyCycleError``.
318
+
319
+ Args:
320
+ resolver: Provider of class metadata.
321
+ class_name: JVM internal name of the starting class.
322
+ include_self: If ``True``, include the starting class itself.
323
+
324
+ Yields:
325
+ Each unique supertype in depth-first order, superclasses before interfaces.
326
+
327
+ Raises:
328
+ UnresolvedClassError: If a class in the graph cannot be resolved.
329
+ HierarchyCycleError: If a cycle is detected in the type graph.
330
+ """
331
+
332
+ root = _resolve_required(resolver, class_name)
333
+ seen: set[str] = set()
334
+
335
+ def visit(name: str, stack: tuple[str, ...]) -> Iterator[ResolvedClass]:
336
+ if name in stack:
337
+ cycle = stack[stack.index(name) :] + (name,)
338
+ raise HierarchyCycleError(cycle)
339
+ if name in seen:
340
+ return
341
+
342
+ resolved = _resolve_required(resolver, name)
343
+ seen.add(name)
344
+ yield resolved
345
+
346
+ next_stack = stack + (name,)
347
+ if resolved.super_name is not None:
348
+ yield from visit(resolved.super_name, next_stack)
349
+ for interface_name in resolved.interfaces:
350
+ yield from visit(interface_name, next_stack)
351
+
352
+ if include_self:
353
+ yield from visit(root.name, ())
354
+ return
355
+
356
+ if root.super_name is not None:
357
+ yield from visit(root.super_name, (root.name,))
358
+ for interface_name in root.interfaces:
359
+ yield from visit(interface_name, (root.name,))
360
+
361
+
362
+ def is_subtype(resolver: ClassResolver, class_name: str, super_name: str) -> bool:
363
+ """Return whether *class_name* is assignable to *super_name* by ancestry.
364
+
365
+ Args:
366
+ resolver: Provider of class metadata.
367
+ class_name: JVM internal name of the candidate subtype.
368
+ super_name: JVM internal name of the candidate supertype.
369
+
370
+ Returns:
371
+ ``True`` if *class_name* equals or extends/implements *super_name*.
372
+
373
+ Raises:
374
+ UnresolvedClassError: If any class in the hierarchy cannot be resolved.
375
+ """
376
+
377
+ _resolve_required(resolver, super_name)
378
+ return any(resolved.name == super_name for resolved in iter_supertypes(resolver, class_name, include_self=True))
379
+
380
+
381
+ def common_superclass(resolver: ClassResolver, left: str, right: str) -> str:
382
+ """Return the nearest shared superclass for two internal class names.
383
+
384
+ Follows superclass edges only (cf. JVM spec §4.10.1.2 type merging).
385
+ Interface relationships collapse to ``java/lang/Object`` unless both
386
+ inputs share the same class name.
387
+
388
+ Args:
389
+ resolver: Provider of class metadata.
390
+ left: JVM internal name of the first class.
391
+ right: JVM internal name of the second class.
392
+
393
+ Returns:
394
+ Internal name of the nearest common superclass.
395
+
396
+ Raises:
397
+ UnresolvedClassError: If any class in either chain cannot be resolved.
398
+ """
399
+
400
+ left_chain = {resolved.name for resolved in iter_superclasses(resolver, left, include_self=True)}
401
+ for resolved in iter_superclasses(resolver, right, include_self=True):
402
+ if resolved.name in left_chain:
403
+ return resolved.name
404
+ return JAVA_LANG_OBJECT
405
+
406
+
407
+ def find_overridden_methods(
408
+ resolver: ClassResolver,
409
+ class_name: str,
410
+ method: ResolvedMethod,
411
+ ) -> tuple[InheritedMethod, ...]:
412
+ """Return inherited declarations overridden by *method* in *class_name*.
413
+
414
+ The check is intentionally classfile-oriented (cf. JVM spec §5.4.5):
415
+
416
+ - Constructors and class initializers never override.
417
+ - Declaring methods that are ``private`` or ``static`` never override.
418
+ - Inherited declarations that are ``private``, ``static``, or ``final``
419
+ are excluded.
420
+ - Package-private declarations only match within the same runtime package.
421
+
422
+ Args:
423
+ resolver: Provider of class metadata.
424
+ class_name: JVM internal name of the declaring class.
425
+ method: The method whose overridden ancestors to find.
426
+
427
+ Returns:
428
+ Matching inherited method declarations, possibly empty.
429
+
430
+ Raises:
431
+ UnresolvedClassError: If any class in the hierarchy cannot be resolved.
432
+ """
433
+
434
+ _resolve_required(resolver, class_name)
435
+ if method.name in ("<init>", "<clinit>"):
436
+ return ()
437
+ if method.access_flags & (MethodAccessFlag.PRIVATE | MethodAccessFlag.STATIC):
438
+ return ()
439
+
440
+ matches: list[InheritedMethod] = []
441
+ for supertype in iter_supertypes(resolver, class_name):
442
+ inherited = supertype.find_method(method.name, method.descriptor)
443
+ if inherited is None:
444
+ continue
445
+ if not _can_override(class_name, supertype.name, inherited):
446
+ continue
447
+ matches.append(
448
+ InheritedMethod(
449
+ owner=supertype.name,
450
+ name=inherited.name,
451
+ descriptor=inherited.descriptor,
452
+ access_flags=inherited.access_flags,
453
+ )
454
+ )
455
+ return tuple(matches)
456
+
457
+
458
+ def _resolve_required(resolver: ClassResolver, class_name: str) -> ResolvedClass:
459
+ """Resolve *class_name* or raise ``UnresolvedClassError``.
460
+
461
+ Falls back to an implicit ``java/lang/Object`` stub when the resolver
462
+ does not provide it.
463
+
464
+ Args:
465
+ resolver: Provider of class metadata.
466
+ class_name: JVM internal name to resolve.
467
+
468
+ Returns:
469
+ The resolved class metadata.
470
+
471
+ Raises:
472
+ UnresolvedClassError: If the class cannot be resolved and is not ``java/lang/Object``.
473
+ """
474
+
475
+ resolved = resolver.resolve_class(class_name)
476
+ if resolved is not None:
477
+ return resolved
478
+ if class_name == JAVA_LANG_OBJECT:
479
+ return _IMPLICIT_OBJECT
480
+ raise UnresolvedClassError(class_name)
481
+
482
+
483
+ def _resolve_class_name(cp: ConstantPoolBuilder, index: int) -> str:
484
+ """Resolve a ``CONSTANT_Class`` index to its internal name.
485
+
486
+ Args:
487
+ cp: Constant pool to read from.
488
+ index: Constant pool index pointing to a ``CONSTANT_Class_info`` entry (JVM spec §4.4.1).
489
+
490
+ Returns:
491
+ The UTF-8 internal name referenced by the class-info entry.
492
+
493
+ Raises:
494
+ ValueError: If the entry at *index* is not a ``ClassInfo``.
495
+ """
496
+
497
+ entry = cp.get(index)
498
+ if not isinstance(entry, ClassInfo):
499
+ raise ValueError(f"CP index {index} is not a CONSTANT_Class: {type(entry).__name__}")
500
+ return cp.resolve_utf8(entry.name_index)
501
+
502
+
503
+ def _package_name(class_name: str) -> str:
504
+ """Return the internal package prefix for *class_name*.
505
+
506
+ Args:
507
+ class_name: JVM internal name (e.g. ``java/lang/String``).
508
+
509
+ Returns:
510
+ The package portion (e.g. ``java/lang``), or an empty string for the default package.
511
+ """
512
+
513
+ package, _, _ = class_name.rpartition("/")
514
+ return package
515
+
516
+
517
+ def _can_override(
518
+ declaring_owner: str,
519
+ inherited_owner: str,
520
+ inherited_method: ResolvedMethod,
521
+ ) -> bool:
522
+ """Return whether *inherited_method* is overridable from *declaring_owner*.
523
+
524
+ Applies JVM override rules (JVM spec §5.4.5): constructors, ``private``,
525
+ ``static``, and ``final`` methods cannot be overridden, and package-private
526
+ access requires the same runtime package.
527
+
528
+ Args:
529
+ declaring_owner: Internal name of the overriding class.
530
+ inherited_owner: Internal name of the class declaring the inherited method.
531
+ inherited_method: The candidate inherited method declaration.
532
+
533
+ Returns:
534
+ ``True`` if the inherited method can be overridden from the declaring class.
535
+ """
536
+
537
+ if inherited_method.name in ("<init>", "<clinit>"):
538
+ return False
539
+ if inherited_method.access_flags & _NON_OVERRIDABLE_METHOD_FLAGS:
540
+ return False
541
+ if inherited_method.access_flags & (MethodAccessFlag.PUBLIC | MethodAccessFlag.PROTECTED):
542
+ return True
543
+ return _package_name(declaring_owner) == _package_name(inherited_owner)
544
+
545
+
546
+ __all__ = [
547
+ "ClassResolver",
548
+ "HierarchyCycleError",
549
+ "HierarchyError",
550
+ "InheritedMethod",
551
+ "JAVA_LANG_OBJECT",
552
+ "MappingClassResolver",
553
+ "ResolvedClass",
554
+ "ResolvedMethod",
555
+ "UnresolvedClassError",
556
+ "common_superclass",
557
+ "find_overridden_methods",
558
+ "is_subtype",
559
+ "iter_superclasses",
560
+ "iter_supertypes",
561
+ ]
pytecode/info.py ADDED
@@ -0,0 +1,123 @@
1
+ """Data structures for parsed JVM class file components.
2
+
3
+ Provides dataclass representations of the top-level structures defined in the
4
+ JVM specification: the ``ClassFile`` structure (§4.1), ``field_info`` (§4.5),
5
+ and ``method_info`` (§4.6).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass
11
+
12
+ from .attributes import AttributeInfo
13
+ from .constant_pool import ConstantPoolInfo
14
+ from .constants import ClassAccessFlag, FieldAccessFlag, MethodAccessFlag
15
+
16
+ __all__ = ["ClassFile", "FieldInfo", "MethodInfo"]
17
+
18
+
19
+ @dataclass
20
+ class FieldInfo:
21
+ """Parsed ``field_info`` structure (JVM spec §4.5).
22
+
23
+ Represents a single field declared in a class or interface.
24
+
25
+ Attributes:
26
+ access_flags: Mask of ``FieldAccessFlag`` values denoting access
27
+ permissions and properties of the field.
28
+ name_index: Index into the constant pool for the field's simple name.
29
+ descriptor_index: Index into the constant pool for the field's
30
+ descriptor string.
31
+ attributes_count: Number of additional attributes for this field.
32
+ attributes: Variable-length list of ``AttributeInfo`` structures
33
+ giving additional information about the field.
34
+ """
35
+
36
+ access_flags: FieldAccessFlag
37
+ name_index: int
38
+ descriptor_index: int
39
+ attributes_count: int
40
+ attributes: list[AttributeInfo]
41
+
42
+
43
+ @dataclass
44
+ class MethodInfo:
45
+ """Parsed ``method_info`` structure (JVM spec §4.6).
46
+
47
+ Represents a single method declared in a class or interface, including
48
+ instance methods, class methods, instance initialisation methods, and
49
+ the class/interface initialisation method.
50
+
51
+ Attributes:
52
+ access_flags: Mask of ``MethodAccessFlag`` values denoting access
53
+ permissions and properties of the method.
54
+ name_index: Index into the constant pool for the method's simple name.
55
+ descriptor_index: Index into the constant pool for the method's
56
+ descriptor string.
57
+ attributes_count: Number of additional attributes for this method.
58
+ attributes: Variable-length list of ``AttributeInfo`` structures
59
+ giving additional information about the method (e.g. bytecode).
60
+ """
61
+
62
+ access_flags: MethodAccessFlag
63
+ name_index: int
64
+ descriptor_index: int
65
+ attributes_count: int
66
+ attributes: list[AttributeInfo]
67
+
68
+
69
+ @dataclass
70
+ class ClassFile:
71
+ """Parsed ``ClassFile`` structure (JVM spec §4.1).
72
+
73
+ Top-level representation of a ``.class`` file produced by the parser.
74
+ Every field corresponds directly to an item in the ``ClassFile`` table
75
+ defined by the specification.
76
+
77
+ Attributes:
78
+ magic: The magic number identifying the class file format
79
+ (``0xCAFEBABE``).
80
+ minor_version: Minor version number of the class file.
81
+ major_version: Major version number of the class file.
82
+ constant_pool_count: Number of entries in the constant pool table
83
+ plus one.
84
+ constant_pool: Table of ``ConstantPoolInfo`` entries (indexed from 1).
85
+ Entries may be ``None`` for the unused slots that follow
86
+ ``CONSTANT_Long`` and ``CONSTANT_Double`` entries.
87
+ access_flags: Mask of ``ClassAccessFlag`` values denoting access
88
+ permissions and properties of this class or interface.
89
+ this_class: Constant pool index of a ``CONSTANT_Class_info`` entry
90
+ representing the class defined by this file.
91
+ super_class: Constant pool index of a ``CONSTANT_Class_info`` entry
92
+ representing the direct superclass, or ``0`` for
93
+ ``java.lang.Object``.
94
+ interfaces_count: Number of direct superinterfaces.
95
+ interfaces: List of constant pool indices, each referencing a
96
+ ``CONSTANT_Class_info`` entry for a direct superinterface.
97
+ fields_count: Number of ``FieldInfo`` structures in *fields*.
98
+ fields: List of ``FieldInfo`` structures representing all fields
99
+ declared by this class or interface.
100
+ methods_count: Number of ``MethodInfo`` structures in *methods*.
101
+ methods: List of ``MethodInfo`` structures representing all methods
102
+ declared by this class or interface.
103
+ attributes_count: Number of attributes in the *attributes* table.
104
+ attributes: List of ``AttributeInfo`` structures giving additional
105
+ class file attributes.
106
+ """
107
+
108
+ magic: int
109
+ minor_version: int
110
+ major_version: int
111
+ constant_pool_count: int
112
+ constant_pool: list[ConstantPoolInfo | None]
113
+ access_flags: ClassAccessFlag
114
+ this_class: int
115
+ super_class: int
116
+ interfaces_count: int
117
+ interfaces: list[int]
118
+ fields_count: int
119
+ fields: list[FieldInfo]
120
+ methods_count: int
121
+ methods: list[MethodInfo]
122
+ attributes_count: int
123
+ attributes: list[AttributeInfo]