Stack map frames are computed from serialized bytecode similar way they are verified during class loading process.
The frames computation consists of following steps:
NOP instructions, terminated with
ATHROW instruction, and removed from exception handlers table.
java.lang.Throwable single stack item and no locals.
Reverse-merge of the stack map frames may in some situations require to determine class hierarchy relations.
Reverse-merge of individual types is performed when a target frame has already been retro-filled and it is necessary to adjust its existing stack entries and locals to also match actual stack map frame conditions. Following tables describe how new target stack entry or local type is calculated, based on the actual frame stack entry or local ("from") and actual value of the target stack entry or local ("to").
| to \ from | TOP | PRIMITIVE | UNINITIALIZED | REFERENCE |
|---|---|---|---|---|
| TOP | TOP | TOP | TOP | TOP |
| PRIMITIVE | TOP | Reverse-merge of primitive types | TOP | TOP |
| UNINITIALIZED | TOP | TOP | Is NEW offset matching ? UNINITIALIZED : TOP | TOP |
| REFERENCE | TOP | TOP | TOP | Reverse-merge of reference types |
| to \ from | SHORT | BYTE | BOOLEAN | LONG | DOUBLE | FLOAT | INTEGER |
|---|---|---|---|---|---|---|---|
| SHORT | SHORT | TOP | TOP | TOP | TOP | TOP | SHORT |
| BYTE | TOP | BYTE | TOP | TOP | TOP | TOP | BYTE |
| BOOLEAN | TOP | TOP | BOOLEAN | TOP | TOP | TOP | BOOLEAN |
| LONG | TOP | TOP | TOP | LONG | TOP | TOP | TOP |
| DOUBLE | TOP | TOP | TOP | TOP | DOUBLE | TOP | TOP |
| FLOAT | TOP | TOP | TOP | TOP | TOP | FLOAT | TOP |
| INTEGER | TOP | TOP | TOP | TOP | TOP | TOP | INTEGER |
| to \ from | NULL | j.l.Object | j.l.Cloneable | j.i.Serializable | ARRAY | INTERFACE* | OBJECT** |
|---|---|---|---|---|---|---|---|
| NULL | NULL | j.l.Object | j.l.Cloneable | j.i.Serializable | ARRAY | INTERFACE | OBJECT |
| j.l.Object | j.l.Object | j.l.Object | j.l.Object | j.l.Object | j.l.Object | j.l.Object | j.l.Object |
| j.l.Cloneable | j.l.Cloneable | j.l.Cloneable | j.l.Cloneable | j.l.Cloneable | j.l.Object | j.l.Cloneable | j.l.Cloneable |
| j.i.Serializable | j.i.Serializable | j.i.Serializable | j.i.Serializable | j.i.Serializable | j.l.Object | j.i.Serializable | j.i.Serializable |
| ARRAY | ARRAY | j.l.Object | j.l.Object | j.l.Object | Reverse merge of arrays | j.l.Object | j.l.Object |
| INTERFACE* | INTERFACE | j.l.Object | j.l.Object | j.l.Object | j.l.Object | j.l.Object | j.l.Object |
| OBJECT** | OBJECT | j.l.Object | j.l.Object | j.l.Object | j.l.Object | j.l.Object | Resolved common ancestor |
| *any interface reference except for j.l.Cloneable and j.i.Serializable **any object reference except for j.l.Object | |||||||
Array types are reverse-merged as reference to array type constructed from reverse-merged components. Reference to j.l.Object is an alternate result when construction of the array type is not possible (when reverse-merge of components returned TOP or other non-reference and non-primitive type).
Custom class hierarchy resolver has been implemented as a part of the library to avoid heavy class loading and to allow stack maps generation even for code with incomplete dependency classpath. However stack maps generated with warnings of unresolved dependencies may later fail to verify during class loading process.
Focus of the whole algorithm is on high performance and low memory footprint:
| Modifier and Type | Class and Description |
|---|---|
| private class | |
| pack-priv static record | |
| private static record |
| Modifier and Type | Field and Description |
|---|---|
| public static final int | |
| public static final int | |
| private static final StackMapGenerator. | |
| private final RawBytecodeHelper. | |
| public static final int | |
| public static final int | |
| private final ClassHierarchyImpl | |
| private final SplitConstantPool | |
| private final StackMapGenerator. | |
| private static final StackMapGenerator. | |
| private int | |
| private int | |
| private static final int | |
| private static final int | |
| private StackMapGenerator. | |
| private int | |
| public static final int | |
| private final List | |
| private final boolean | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| private final LabelContext | |
| private int | |
| private int | |
| private final MethodTypeDesc | |
| private final String | |
| private static final String | |
| private final boolean | |
| private final List | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| public static final int | |
| private static final int | |
| private static final int | |
| private final StackMapGenerator. |
| Access | Constructor and Description |
|---|---|
| public | StackMapGenerator(LabelContext
LabelContext instance used to resolve or patch ExceptionHandler
labels to bytecode offsets (or vice versa)class to generate stack maps for thisClass, String method name to generate stack maps for methodName, MethodTypeDesc method descriptor to generate stack maps for methodDesc, boolean information whether the method is static isStatic, RawBytecodeHelper.R/W bytecode,ByteBuffer wrapping method bytecode, the content is altered in case Generator detects and patches dead codeR/W cp, ClassFileImpl context, List<AbstractPseudoInstruction.ConstantPoolBuilder instance used to resolve all involved CP entries and also generate new entries referenced from the generated stack mapsR/W handlersExceptionHandler list used to detect mandatory frame offsets as well as to determine stack maps in exception handlers
and also to be altered when dead code is detected and must be excluded from exception handlersPrimary constructor of the |
| Modifier and Type | Method and Description |
|---|---|
| private void | |
| private void | |
| private static StackMapGenerator. | |
| private void | |
| private void | Returns: detected framesPerforms detection of mandatory stack map frames in a single bytecode traversing pass |
| private void | |
| private void | |
| private IllegalArgumentException | Returns: the generator error with attached detailserror message msg)Returns the generator error with attached details. |
| private IllegalArgumentException | Returns: the generator error with attached detailserror message msg, int bytecode offset where the error occurred offset)Returns the generator error with attached details. |
| private StackMapGenerator. | |
| private StackMapGenerator. | |
| private boolean | |
| public int | Returns: maximum number of the locals requiredCalculated maximum number of the locals required |
| public int | |
| pack-priv static StackMapGenerator | |
| private void | |
| private boolean | |
| private void | |
| private void | |
| private boolean | |
| private void | |
| private void | |
| private void | |
| private void | |
| public Attribute | Returns: StackMapTableAttribute or null if stack map is emptyGetter of the generated |