8339742: Refactor ClassFileImpl to allow loading Option classes lazily

Reviewed-by: asotona
This commit is contained in:
Claes Redestad 2024-09-09 14:18:20 +00:00
parent c54fc08aa3
commit d53e405a26
7 changed files with 135 additions and 64 deletions

View file

@ -51,7 +51,7 @@ public class AbstractDirectBuilder<M> {
} }
public void writeAttribute(Attribute<?> a) { public void writeAttribute(Attribute<?> a) {
if (Util.isAttributeAllowed(a, context.attributesProcessingOption())) { if (Util.isAttributeAllowed(a, context)) {
attributes.withAttribute(a); attributes.withAttribute(a);
} }
} }

View file

@ -31,7 +31,6 @@ import java.util.function.Consumer;
import java.lang.classfile.AttributeMapper; import java.lang.classfile.AttributeMapper;
import java.lang.classfile.ClassFile; import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassFile.*;
import java.lang.classfile.ClassBuilder; import java.lang.classfile.ClassBuilder;
import java.lang.classfile.ClassHierarchyResolver; import java.lang.classfile.ClassHierarchyResolver;
import java.lang.classfile.ClassModel; import java.lang.classfile.ClassModel;
@ -41,33 +40,53 @@ import java.lang.classfile.constantpool.ConstantPoolBuilder;
import java.lang.classfile.constantpool.Utf8Entry; import java.lang.classfile.constantpool.Utf8Entry;
import jdk.internal.classfile.impl.verifier.VerifierImpl; import jdk.internal.classfile.impl.verifier.VerifierImpl;
public record ClassFileImpl(StackMapsOption stackMapsOption, public final class ClassFileImpl implements ClassFile {
DebugElementsOption debugElementsOption,
LineNumbersOption lineNumbersOption, private Option stackMapsOption;
AttributesProcessingOption attributesProcessingOption, private Option debugElementsOption;
ConstantPoolSharingOption constantPoolSharingOption, private Option lineNumbersOption;
ShortJumpsOption shortJumpsOption, private Option attributesProcessingOption;
DeadCodeOption deadCodeOption, private Option constantPoolSharingOption;
DeadLabelsOption deadLabelsOption, private Option shortJumpsOption;
ClassHierarchyResolverOption classHierarchyResolverOption, private Option deadCodeOption;
AttributeMapperOption attributeMapperOption) implements ClassFile { private Option deadLabelsOption;
private Option classHierarchyResolverOption;
private Option attributeMapperOption;
private ClassFileImpl(Option stackMapsOption,
Option debugElementsOption,
Option lineNumbersOption,
Option attributesProcessingOption,
Option constantPoolSharingOption,
Option shortJumpsOption,
Option deadCodeOption,
Option deadLabelsOption,
Option classHierarchyResolverOption,
Option attributeMapperOption) {
this.stackMapsOption = stackMapsOption;
this.debugElementsOption = debugElementsOption;
this.lineNumbersOption = lineNumbersOption;
this.attributesProcessingOption = attributesProcessingOption;
this.constantPoolSharingOption = constantPoolSharingOption;
this.shortJumpsOption = shortJumpsOption;
this.deadCodeOption = deadCodeOption;
this.deadLabelsOption = deadLabelsOption;
this.classHierarchyResolverOption = classHierarchyResolverOption;
this.attributeMapperOption = attributeMapperOption;
}
public static final ClassFileImpl DEFAULT_CONTEXT = new ClassFileImpl( public static final ClassFileImpl DEFAULT_CONTEXT = new ClassFileImpl(
StackMapsOption.STACK_MAPS_WHEN_REQUIRED, null, // StackMapsOption.STACK_MAPS_WHEN_REQUIRED
DebugElementsOption.PASS_DEBUG, null, // DebugElementsOption.PASS_DEBUG,
LineNumbersOption.PASS_LINE_NUMBERS, null, // LineNumbersOption.PASS_LINE_NUMBERS,
AttributesProcessingOption.PASS_ALL_ATTRIBUTES, null, // AttributesProcessingOption.PASS_ALL_ATTRIBUTES,
ConstantPoolSharingOption.SHARED_POOL, null, // ConstantPoolSharingOption.SHARED_POOL,
ShortJumpsOption.FIX_SHORT_JUMPS, null, // ShortJumpsOption.FIX_SHORT_JUMPS,
DeadCodeOption.PATCH_DEAD_CODE, null, // DeadCodeOption.PATCH_DEAD_CODE,
DeadLabelsOption.FAIL_ON_DEAD_LABELS, null, // DeadLabelsOption.FAIL_ON_DEAD_LABELS,
new ClassHierarchyResolverOptionImpl(ClassHierarchyResolver.defaultResolver()), null, // new ClassHierarchyResolverOptionImpl(ClassHierarchyResolver.defaultResolver()),
new AttributeMapperOptionImpl(new Function<>() { null // _ -> null
@Override );
public AttributeMapper<?> apply(Utf8Entry k) {
return null;
}
}));
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
@ -85,6 +104,8 @@ public record ClassFileImpl(StackMapsOption stackMapsOption,
for (var o : options) { for (var o : options) {
if (o instanceof StackMapsOption oo) { if (o instanceof StackMapsOption oo) {
smo = oo; smo = oo;
} else if (o instanceof ClassHierarchyResolverOption oo) {
chro = oo;
} else if (o instanceof DebugElementsOption oo) { } else if (o instanceof DebugElementsOption oo) {
deo = oo; deo = oo;
} else if (o instanceof LineNumbersOption oo) { } else if (o instanceof LineNumbersOption oo) {
@ -99,8 +120,6 @@ public record ClassFileImpl(StackMapsOption stackMapsOption,
dco = oo; dco = oo;
} else if (o instanceof DeadLabelsOption oo) { } else if (o instanceof DeadLabelsOption oo) {
dlo = oo; dlo = oo;
} else if (o instanceof ClassHierarchyResolverOption oo) {
chro = oo;
} else if (o instanceof AttributeMapperOption oo) { } else if (o instanceof AttributeMapperOption oo) {
amo = oo; amo = oo;
} else { // null or unknown Option type } else { // null or unknown Option type
@ -127,9 +146,8 @@ public record ClassFileImpl(StackMapsOption stackMapsOption,
@Override @Override
public byte[] transformClass(ClassModel model, ClassEntry newClassName, ClassTransform transform) { public byte[] transformClass(ClassModel model, ClassEntry newClassName, ClassTransform transform) {
ConstantPoolBuilder constantPool = constantPoolSharingOption() == ConstantPoolSharingOption.SHARED_POOL ConstantPoolBuilder constantPool = sharedConstantPool() ? ConstantPoolBuilder.of(model)
? ConstantPoolBuilder.of(model) : ConstantPoolBuilder.of();
: ConstantPoolBuilder.of();
return build(newClassName, constantPool, return build(newClassName, constantPool,
new Consumer<ClassBuilder>() { new Consumer<ClassBuilder>() {
@Override @Override
@ -141,10 +159,14 @@ public record ClassFileImpl(StackMapsOption stackMapsOption,
}); });
} }
public boolean sharedConstantPool() {
return constantPoolSharingOption == null || constantPoolSharingOption == ConstantPoolSharingOption.SHARED_POOL;
}
@Override @Override
public List<VerifyError> verify(ClassModel model) { public List<VerifyError> verify(ClassModel model) {
try { try {
return VerifierImpl.verify(model, classHierarchyResolverOption().classHierarchyResolver(), null); return VerifierImpl.verify(model, classHierarchyResolver(), null);
} catch (IllegalArgumentException verifierInitializationError) { } catch (IllegalArgumentException verifierInitializationError) {
return List.of(new VerifyError(verifierInitializationError.getMessage())); return List.of(new VerifyError(verifierInitializationError.getMessage()));
} }
@ -159,6 +181,58 @@ public record ClassFileImpl(StackMapsOption stackMapsOption,
} }
} }
public Function<Utf8Entry, AttributeMapper<?>> attributeMapper() {
if (attributeMapperOption == null) {
return _ -> null;
} else {
return ((AttributeMapperOption)attributeMapperOption).attributeMapper();
}
}
public ClassHierarchyResolver classHierarchyResolver() {
if (classHierarchyResolverOption == null) {
return ClassHierarchyImpl.DEFAULT_RESOLVER;
} else {
return ((ClassHierarchyResolverOption)classHierarchyResolverOption).classHierarchyResolver();
}
}
public boolean dropDeadLabels() {
return (deadLabelsOption != null && deadLabelsOption == DeadLabelsOption.DROP_DEAD_LABELS);
}
public boolean passDebugElements() {
return (debugElementsOption == null || debugElementsOption == DebugElementsOption.PASS_DEBUG);
}
public boolean passLineNumbers() {
return (lineNumbersOption == null || lineNumbersOption == LineNumbersOption.PASS_LINE_NUMBERS);
}
public AttributesProcessingOption attributesProcessingOption() {
return (attributesProcessingOption == null) ? AttributesProcessingOption.PASS_ALL_ATTRIBUTES : (AttributesProcessingOption)attributesProcessingOption;
}
public boolean fixShortJumps() {
return (shortJumpsOption == null || shortJumpsOption == ShortJumpsOption.FIX_SHORT_JUMPS);
}
public boolean stackMapsWhenRequired() {
return (stackMapsOption == null || stackMapsOption == StackMapsOption.STACK_MAPS_WHEN_REQUIRED);
}
public boolean generateStackMaps() {
return (stackMapsOption == StackMapsOption.GENERATE_STACK_MAPS);
}
public boolean dropStackMaps() {
return (stackMapsOption == StackMapsOption.DROP_STACK_MAPS);
}
public boolean patchDeadCode() {
return (deadCodeOption == null || deadCodeOption == DeadCodeOption.PATCH_DEAD_CODE);
}
public record AttributeMapperOptionImpl(Function<Utf8Entry, AttributeMapper<?>> attributeMapper) public record AttributeMapperOptionImpl(Function<Utf8Entry, AttributeMapper<?>> attributeMapper)
implements AttributeMapperOption { implements AttributeMapperOption {
} }

View file

@ -82,7 +82,7 @@ public final class ClassReaderImpl
this.buffer = classfileBytes; this.buffer = classfileBytes;
this.classfileLength = classfileBytes.length; this.classfileLength = classfileBytes.length;
this.context = context; this.context = context;
this.attributeMapper = this.context.attributeMapperOption().attributeMapper(); this.attributeMapper = this.context.attributeMapper();
if (classfileLength < 4 || readInt(0) != 0xCAFEBABE) { if (classfileLength < 4 || readInt(0) != 0xCAFEBABE) {
throw new IllegalArgumentException("Bad magic number"); throw new IllegalArgumentException("Bad magic number");
} }

View file

@ -122,7 +122,7 @@ public final class CodeImpl
if (!inflated) { if (!inflated) {
if (labels == null) if (labels == null)
labels = new LabelImpl[codeLength + 1]; labels = new LabelImpl[codeLength + 1];
if (classReader.context().lineNumbersOption() == ClassFile.LineNumbersOption.PASS_LINE_NUMBERS) if (classReader.context().passLineNumbers())
inflateLineNumbers(); inflateLineNumbers();
inflateJumpTargets(); inflateJumpTargets();
inflateTypeAnnotations(); inflateTypeAnnotations();
@ -167,7 +167,7 @@ public final class CodeImpl
inflateMetadata(); inflateMetadata();
boolean doLineNumbers = (lineNumbers != null); boolean doLineNumbers = (lineNumbers != null);
generateCatchTargets(consumer); generateCatchTargets(consumer);
if (classReader.context().debugElementsOption() == ClassFile.DebugElementsOption.PASS_DEBUG) if (classReader.context().passDebugElements())
generateDebugElements(consumer); generateDebugElements(consumer);
for (int pos=codeStart; pos<codeEnd; ) { for (int pos=codeStart; pos<codeEnd; ) {
if (labels[pos - codeStart] != null) if (labels[pos - codeStart] != null)

View file

@ -103,7 +103,7 @@ public final class DirectCodeBuilder
handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, false)); handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, false));
cb.buildContent(); cb.buildContent();
} catch (LabelOverflowException loe) { } catch (LabelOverflowException loe) {
if (context.shortJumpsOption() == ClassFile.ShortJumpsOption.FIX_SHORT_JUMPS) { if (context.fixShortJumps()) {
handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, true)); handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, true));
cb.buildContent(); cb.buildContent();
} }
@ -122,7 +122,7 @@ public final class DirectCodeBuilder
setOriginal(original); setOriginal(original);
this.methodInfo = methodInfo; this.methodInfo = methodInfo;
this.transformFwdJumps = transformFwdJumps; this.transformFwdJumps = transformFwdJumps;
this.transformBackJumps = context.shortJumpsOption() == ClassFile.ShortJumpsOption.FIX_SHORT_JUMPS; this.transformBackJumps = context.fixShortJumps();
bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, context, cai.codeLength()) bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, context, cai.codeLength())
: new BufWriterImpl(constantPool, context); : new BufWriterImpl(constantPool, context);
this.startLabel = new LabelImpl(this, 0); this.startLabel = new LabelImpl(this, 0);
@ -195,7 +195,7 @@ public final class DirectCodeBuilder
int endPc = labelToBci(h.tryEnd()); int endPc = labelToBci(h.tryEnd());
int handlerPc = labelToBci(h.handler()); int handlerPc = labelToBci(h.handler());
if (startPc == -1 || endPc == -1 || handlerPc == -1) { if (startPc == -1 || endPc == -1 || handlerPc == -1) {
if (context.deadLabelsOption() == ClassFile.DeadLabelsOption.DROP_DEAD_LABELS) { if (context.dropDeadLabels()) {
handlersSize--; handlersSize--;
} else { } else {
throw new IllegalArgumentException("Unbound label in exception handler"); throw new IllegalArgumentException("Unbound label in exception handler");
@ -219,7 +219,7 @@ public final class DirectCodeBuilder
// Backfill branches for which Label didn't have position yet // Backfill branches for which Label didn't have position yet
processDeferredLabels(); processDeferredLabels();
if (context.debugElementsOption() == ClassFile.DebugElementsOption.PASS_DEBUG) { if (context.passDebugElements()) {
if (!characterRanges.isEmpty()) { if (!characterRanges.isEmpty()) {
Attribute<?> a = new UnboundAttribute.AdHocAttribute<>(Attributes.characterRangeTable()) { Attribute<?> a = new UnboundAttribute.AdHocAttribute<>(Attributes.characterRangeTable()) {
@ -232,7 +232,7 @@ public final class DirectCodeBuilder
var start = labelToBci(cr.startScope()); var start = labelToBci(cr.startScope());
var end = labelToBci(cr.endScope()); var end = labelToBci(cr.endScope());
if (start == -1 || end == -1) { if (start == -1 || end == -1) {
if (context.deadLabelsOption() == ClassFile.DeadLabelsOption.DROP_DEAD_LABELS) { if (context.dropDeadLabels()) {
crSize--; crSize--;
} else { } else {
throw new IllegalArgumentException("Unbound label in character range"); throw new IllegalArgumentException("Unbound label in character range");
@ -261,7 +261,7 @@ public final class DirectCodeBuilder
b.writeU2(lvSize); b.writeU2(lvSize);
for (LocalVariable l : localVariables) { for (LocalVariable l : localVariables) {
if (!Util.writeLocalVariable(b, l)) { if (!Util.writeLocalVariable(b, l)) {
if (context.deadLabelsOption() == ClassFile.DeadLabelsOption.DROP_DEAD_LABELS) { if (context.dropDeadLabels()) {
lvSize--; lvSize--;
} else { } else {
throw new IllegalArgumentException("Unbound label in local variable type"); throw new IllegalArgumentException("Unbound label in local variable type");
@ -284,7 +284,7 @@ public final class DirectCodeBuilder
b.writeU2(localVariableTypes.size()); b.writeU2(localVariableTypes.size());
for (LocalVariableType l : localVariableTypes) { for (LocalVariableType l : localVariableTypes) {
if (!Util.writeLocalVariable(b, l)) { if (!Util.writeLocalVariable(b, l)) {
if (context.deadLabelsOption() == ClassFile.DeadLabelsOption.DROP_DEAD_LABELS) { if (context.dropDeadLabels()) {
lvtSize--; lvtSize--;
} else { } else {
throw new IllegalArgumentException("Unbound label in local variable type"); throw new IllegalArgumentException("Unbound label in local variable type");
@ -357,24 +357,21 @@ public final class DirectCodeBuilder
} }
if (codeAndExceptionsMatch(codeLength)) { if (codeAndExceptionsMatch(codeLength)) {
switch (context.stackMapsOption()) { if (context.stackMapsWhenRequired()) {
case STACK_MAPS_WHEN_REQUIRED -> { attributes.withAttribute(original.findAttribute(Attributes.stackMapTable()).orElse(null));
attributes.withAttribute(original.findAttribute(Attributes.stackMapTable()).orElse(null)); writeCounters(true, buf);
writeCounters(true, buf); } else if (context.generateStackMaps()) {
} generateStackMaps(buf);
case GENERATE_STACK_MAPS -> } else if (context.dropStackMaps()) {
generateStackMaps(buf); writeCounters(true, buf);
case DROP_STACK_MAPS ->
writeCounters(true, buf);
} }
} else { } else {
switch (context.stackMapsOption()) { if (context.stackMapsWhenRequired()) {
case STACK_MAPS_WHEN_REQUIRED -> tryGenerateStackMaps(false, buf);
tryGenerateStackMaps(false, buf); } else if (context.generateStackMaps()) {
case GENERATE_STACK_MAPS -> generateStackMaps(buf);
generateStackMaps(buf); } else if (context.dropStackMaps()) {
case DROP_STACK_MAPS -> writeCounters(false, buf);
writeCounters(false, buf);
} }
} }

View file

@ -235,9 +235,9 @@ public final class StackMapGenerator {
this.labelContext = labelContext; this.labelContext = labelContext;
this.handlers = handlers; this.handlers = handlers;
this.rawHandlers = new ArrayList<>(handlers.size()); this.rawHandlers = new ArrayList<>(handlers.size());
this.classHierarchy = new ClassHierarchyImpl(context.classHierarchyResolverOption().classHierarchyResolver()); this.classHierarchy = new ClassHierarchyImpl(context.classHierarchyResolver());
this.patchDeadCode = context.deadCodeOption() == ClassFile.DeadCodeOption.PATCH_DEAD_CODE; this.patchDeadCode = context.patchDeadCode();
this.filterDeadLabels = context.deadLabelsOption() == ClassFile.DeadLabelsOption.DROP_DEAD_LABELS; this.filterDeadLabels = context.dropDeadLabels();
this.currentFrame = new Frame(classHierarchy); this.currentFrame = new Frame(classHierarchy);
generate(); generate();
} }

View file

@ -102,9 +102,9 @@ public class Util {
private static final int ATTRIBUTE_STABILITY_COUNT = AttributeMapper.AttributeStability.values().length; private static final int ATTRIBUTE_STABILITY_COUNT = AttributeMapper.AttributeStability.values().length;
public static boolean isAttributeAllowed(final Attribute<?> attr, public static boolean isAttributeAllowed(final Attribute<?> attr,
final ClassFile.AttributesProcessingOption processingOption) { final ClassFileImpl context) {
return attr instanceof BoundAttribute return attr instanceof BoundAttribute
? ATTRIBUTE_STABILITY_COUNT - attr.attributeMapper().stability().ordinal() > processingOption.ordinal() ? ATTRIBUTE_STABILITY_COUNT - attr.attributeMapper().stability().ordinal() > context.attributesProcessingOption().ordinal()
: true; : true;
} }