APIdia Launch Partner
Top Description Inners Fields Constructors Methods
jdk.internal.classfile.impl

public final Class StackMapGenerator

extends Object
Class Inheritance
Imports
java.lang.classfile.Attribute, .Attributes, .Label, java.lang.classfile.attribute.StackMapTableAttribute, java.lang.classfile.constantpool.ClassEntry, .ConstantDynamicEntry, .ConstantPoolBuilder, .InvokeDynamicEntry, .MemberRefEntry, .Utf8Entry, java.lang.constant.ClassDesc, .MethodTypeDesc, java.util.ArrayList, .Arrays, .List, .Objects, java.util.stream.Collectors, jdk.internal.constant.ClassOrInterfaceDescImpl, jdk.internal.util.Preconditions

StackMapGenerator is responsible for stack map frames generation.

Stack map frames are computed from serialized bytecode similar way they are verified during class loading process.

The frames computation consists of following steps:

  1. Detection of mandatory stack map frames:
    • Mandatory stack map frame include all jump and switch instructions targets, offsets immediately following "no control flow" and all exception table handlers.
    • Detection is performed in a single fast pass through the bytecode, with no auxiliary structures construction nor further instructions processing.
  2. Generator loop processing bytecode instructions:
    • Generator loop simulates sequence instructions processing effect on the actual stack and locals.
    • All mandatory frames detected in the step #1 are retro-filled (or reverse-merged in subsequent processing) with the actual stack and locals for all matching jump, switch and exception handler targets.
    • All frames modified by reverse merges are marked as dirty for further processing.
    • Code blocks with not yet known entry frame content are skipped and related frames are also marked as dirty.
    • Generator loop process is repeated until all mandatory frames are cleared or until an error state is reached.
    • Generator loop always passes all instructions at least once to calculate max stack and max locals code attributes.
    • More than one pass is usually not necessary, except for more complex bytecode sequences.
      (Note: experimental measurements showed that more than 99% of the cases required only single pass to clear all frames, less than 1% of the cases required second pass and remaining 0,01% of the cases required third pass to clear all frames.).
  3. Dead code patching to pass class loading verification:
    • Dead code blocks are indicated by frames remaining without content after leaving the Generator loop.
    • Each dead code block is filled with NOP instructions, terminated with ATHROW instruction, and removed from exception handlers table.
    • Dead code block entry frame is set to 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").

Reverse-merge of general type categories
to \ fromTOPPRIMITIVEUNINITIALIZEDREFERENCE
TOPTOPTOPTOPTOP
PRIMITIVETOPReverse-merge of primitive typesTOPTOP
UNINITIALIZEDTOPTOPIs NEW offset matching ? UNINITIALIZED : TOPTOP
REFERENCETOPTOPTOPReverse-merge of reference types

Reverse-merge of primitive types
to \ fromSHORTBYTEBOOLEANLONGDOUBLEFLOATINTEGER
SHORTSHORTTOPTOPTOPTOPTOPSHORT
BYTETOPBYTETOPTOPTOPTOPBYTE
BOOLEANTOPTOPBOOLEANTOPTOPTOPBOOLEAN
LONGTOPTOPTOPLONGTOPTOPTOP
DOUBLETOPTOPTOPTOPDOUBLETOPTOP
FLOATTOPTOPTOPTOPTOPFLOATTOP
INTEGERTOPTOPTOPTOPTOPTOPINTEGER

Reverse merge of reference types
to \ fromNULLj.l.Objectj.l.Cloneablej.i.SerializableARRAYINTERFACE*OBJECT**
NULLNULLj.l.Objectj.l.Cloneablej.i.SerializableARRAYINTERFACEOBJECT
j.l.Objectj.l.Objectj.l.Objectj.l.Objectj.l.Objectj.l.Objectj.l.Objectj.l.Object
j.l.Cloneablej.l.Cloneablej.l.Cloneablej.l.Cloneablej.l.Cloneablej.l.Objectj.l.Cloneablej.l.Cloneable
j.i.Serializablej.i.Serializablej.i.Serializablej.i.Serializablej.i.Serializablej.l.Objectj.i.Serializablej.i.Serializable
ARRAYARRAYj.l.Objectj.l.Objectj.l.ObjectReverse merge of arraysj.l.Objectj.l.Object
INTERFACE*INTERFACEj.l.Objectj.l.Objectj.l.Objectj.l.Objectj.l.Objectj.l.Object
OBJECT**OBJECTj.l.Objectj.l.Objectj.l.Objectj.l.Objectj.l.ObjectResolved 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:

Nested and Inner Type Summary

Modifier and TypeClass and Description
private class
pack-priv static record
private static record

Field Summary

Modifier and TypeField and Description
public static final int
public static final int
private static final StackMapGenerator.Type[]
private final RawBytecodeHelper.CodeRange
public static final int
public static final int
private final ClassHierarchyImpl
private final SplitConstantPool
private final StackMapGenerator.Frame
private static final StackMapGenerator.Frame[]
private int
private int
private static final int
private static final int
private StackMapGenerator.Frame[]
private int
public static final int
private final List<AbstractPseudoInstruction.ExceptionCatchImpl>
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<StackMapGenerator.RawExceptionCatch>
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.Type

Constructor Summary

AccessConstructor and Description
public
StackMapGenerator(LabelContext
LabelContext instance used to resolve or patch ExceptionHandler labels to bytecode offsets (or vice versa)
labelContext
,
ClassDesc
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.CodeRange
R/W ByteBuffer wrapping method bytecode, the content is altered in case Generator detects and patches dead code
bytecode
,
SplitConstantPool
R/W ConstantPoolBuilder instance used to resolve all involved CP entries and also generate new entries referenced from the generated stack maps
cp
,
ClassFileImpl context, List<AbstractPseudoInstruction.ExceptionCatchImpl>
R/W ExceptionHandler 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 handlers
handlers
)

Primary constructor of the Generator class.

Method Summary

Modifier and TypeMethod and Description
private void
addFrame(int offset)

private void
private static StackMapGenerator.Type
private void
private void

Returns:

detected frames
detectFrames
()

Performs detection of mandatory stack map frames in a single bytecode traversing pass

private void
private void
private IllegalArgumentException

Returns:

the generator error with attached details
generatorError
(String
error message
msg
)

Returns the generator error with attached details.

private IllegalArgumentException

Returns:

the generator error with attached details
generatorError
(String
error message
msg
,
int
bytecode offset where the error occurred
offset
)

Returns the generator error with attached details.

private StackMapGenerator.Frame
getFrame(int offset)

private StackMapGenerator.Type
getNewarrayType(int index)

private boolean
public int

Returns:

maximum number of the locals required
maxLocals
()

Calculated maximum number of the locals required

public int

Returns:

maximum stack size required
maxStack
()

Calculated maximum stack size required

pack-priv static StackMapGenerator
private void
processAnewarray(int index)

private boolean
private void
processExceptionHandlerTargets(int bci, boolean this_uninit)

private void
private boolean
processInvokeInstructions(RawBytecodeHelper bcs, boolean inTryBlock, boolean thisUninit)

private void
processLdc(int index)

private void
private void
private void
removeRangeFromExcTable(int rangeStart, int rangeEnd)

public Attribute<? extends StackMapTableAttribute>

Returns:

StackMapTableAttribute or null if stack map is empty
stackMapTableAttribute
()

Getter of the generated StackMapTableAttribute or null if stack map is empty

Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAlltoStringwaitwaitwait