8308899: Introduce Classfile context and improve Classfile options

8306650: Improve control of stack maps generation in Classfile API
8308646: Typo in ConstantValueAttribute

Reviewed-by: mcimadamore
This commit is contained in:
Adam Sotona 2023-06-27 05:05:17 +00:00
parent 79c056ec8b
commit 990e3a700d
113 changed files with 1602 additions and 1190 deletions

View File

@ -1590,8 +1590,8 @@ public final class Module implements AnnotatedElement {
*/ */
private Class<?> loadModuleInfoClass(InputStream in) throws IOException { private Class<?> loadModuleInfoClass(InputStream in) throws IOException {
final String MODULE_INFO = "module-info"; final String MODULE_INFO = "module-info";
byte[] bytes = Classfile.parse(in.readAllBytes(), var cc = Classfile.of(Classfile.ConstantPoolSharingOption.NEW_POOL);
Classfile.Option.constantPoolSharing(false)).transform((clb, cle) -> { byte[] bytes = cc.transform(cc.parse(in.readAllBytes()), (clb, cle) -> {
switch (cle) { switch (cle) {
case AccessFlags af -> clb.withFlags(AccessFlag.INTERFACE, case AccessFlags af -> clb.withFlags(AccessFlag.INTERFACE,
AccessFlag.ABSTRACT, AccessFlag.SYNTHETIC); AccessFlag.ABSTRACT, AccessFlag.SYNTHETIC);

View File

@ -48,10 +48,8 @@ import static java.lang.constant.ConstantDescs.CD_Object;
public interface ClassHierarchyResolver { public interface ClassHierarchyResolver {
/** /**
* Returns a default instance of {@linkplain ClassHierarchyResolver} * Returns a default instance of {@linkplain ClassHierarchyResolver} that
* that reads from system class loader with * gets {@link ClassHierarchyInfo} from system class loader with reflection.
* {@link ClassLoader#getSystemResourceAsStream(String)} and falls
* back to reflection if a class is not found.
*/ */
static ClassHierarchyResolver defaultResolver() { static ClassHierarchyResolver defaultResolver() {
return ClassHierarchyImpl.DEFAULT_RESOLVER; return ClassHierarchyImpl.DEFAULT_RESOLVER;

View File

@ -72,25 +72,6 @@ public sealed interface ClassModel
/** {@return the interfaces implemented by this class} */ /** {@return the interfaces implemented by this class} */
List<ClassEntry> interfaces(); List<ClassEntry> interfaces();
/**
* Transform this classfile into a new classfile with the aid of a
* {@link ClassTransform}. The transform will receive each element of
* this class, as well as a {@link ClassBuilder} for building the new class.
* The transform is free to preserve, remove, or replace elements as it
* sees fit.
*
* @implNote
* <p>This method behaves as if:
* {@snippet lang=java :
* Classfile.build(thisClass(), ConstantPoolBuilder.of(this),
* b -> b.transform(this, transform));
* }
*
* @param transform the transform
* @return the bytes of the new class
*/
byte[] transform(ClassTransform transform);
/** {@return whether this class is a module descriptor} */ /** {@return whether this class is a module descriptor} */
boolean isModuleInfo(); boolean isModuleInfo();

View File

@ -52,7 +52,7 @@ public sealed interface ClassReader extends ConstantPool
/** /**
* {@return the table of custom attribute mappers} This is derived from * {@return the table of custom attribute mappers} This is derived from
* the processing option {@link Classfile.Option#attributeMapper(Function)}. * the processing option {@link Classfile.AttributeMapperOption}.
*/ */
Function<Utf8Entry, AttributeMapper<?>> customAttributes(); Function<Utf8Entry, AttributeMapper<?>> customAttributes();

View File

@ -32,7 +32,7 @@ import jdk.internal.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
/** /**
* A transformation on streams of elements. Transforms are used during * A transformation on streams of elements. Transforms are used during
* transformation of classfile entities; a transform is provided to a method like * transformation of classfile entities; a transform is provided to a method like
* {@link ClassModel#transform(ClassTransform)}, and the elements of the class, * {@link Classfile#transform(ClassModel, ClassTransform)}, and the elements of the class,
* along with a builder, are presented to the transform. * along with a builder, are presented to the transform.
* *
* <p>The subtypes of {@linkplain * <p>The subtypes of {@linkplain

View File

@ -39,7 +39,7 @@ import jdk.internal.classfile.impl.AbstractPseudoInstruction;
* between instructions and labels. Pseudo-instructions are delivered as part * between instructions and labels. Pseudo-instructions are delivered as part
* of the element stream of a {@link CodeModel}. Delivery of some * of the element stream of a {@link CodeModel}. Delivery of some
* pseudo-instructions can be disabled by modifying the value of classfile * pseudo-instructions can be disabled by modifying the value of classfile
* options (e.g., {@link Classfile.Option#processDebug(boolean)}). * options (e.g., {@link Classfile.DebugElementsOption}).
*/ */
public sealed interface PseudoInstruction public sealed interface PseudoInstruction
extends CodeElement extends CodeElement

View File

@ -67,7 +67,7 @@ public sealed interface ConstantValueAttribute
case Long l -> TemporaryConstantPool.INSTANCE.longEntry(l); case Long l -> TemporaryConstantPool.INSTANCE.longEntry(l);
case Double d -> TemporaryConstantPool.INSTANCE.doubleEntry(d); case Double d -> TemporaryConstantPool.INSTANCE.doubleEntry(d);
case String s -> TemporaryConstantPool.INSTANCE.stringEntry(s); case String s -> TemporaryConstantPool.INSTANCE.stringEntry(s);
default -> throw new IllegalArgumentException("Invalid ConstantValueAtrtibute value: " + value); default -> throw new IllegalArgumentException("Invalid ConstantValueAttribute value: " + value);
}); });
} }
} }

View File

@ -36,7 +36,7 @@ import jdk.internal.classfile.impl.UnboundAttribute;
* the code table and line numbers in the source file. * the code table and line numbers in the source file.
* Delivered as a {@link jdk.internal.classfile.instruction.LineNumber} when traversing the * Delivered as a {@link jdk.internal.classfile.instruction.LineNumber} when traversing the
* elements of a {@link jdk.internal.classfile.CodeModel}, according to the setting of the * elements of a {@link jdk.internal.classfile.CodeModel}, according to the setting of the
* {@link jdk.internal.classfile.Classfile.Option#processLineNumbers(boolean)} option. * {@link jdk.internal.classfile.Classfile.LineNumbersOption} option.
*/ */
public sealed interface LineNumberTableAttribute public sealed interface LineNumberTableAttribute
extends Attribute<LineNumberTableAttribute> extends Attribute<LineNumberTableAttribute>

View File

@ -36,7 +36,7 @@ import java.util.List;
* variables. * variables.
* Delivered as a {@link jdk.internal.classfile.instruction.LocalVariable} when traversing the * Delivered as a {@link jdk.internal.classfile.instruction.LocalVariable} when traversing the
* elements of a {@link jdk.internal.classfile.CodeModel}, according to the setting of the * elements of a {@link jdk.internal.classfile.CodeModel}, according to the setting of the
* {@link jdk.internal.classfile.Classfile.Option#processDebug(boolean)} option. * {@link jdk.internal.classfile.Classfile.DebugElementsOption} option.
*/ */
public sealed interface LocalVariableTableAttribute public sealed interface LocalVariableTableAttribute
extends Attribute<LocalVariableTableAttribute> extends Attribute<LocalVariableTableAttribute>

View File

@ -37,7 +37,7 @@ import java.util.List;
* variables. * variables.
* Delivered as a {@link jdk.internal.classfile.instruction.LocalVariable} when traversing the * Delivered as a {@link jdk.internal.classfile.instruction.LocalVariable} when traversing the
* elements of a {@link jdk.internal.classfile.CodeModel}, according to the setting of the * elements of a {@link jdk.internal.classfile.CodeModel}, according to the setting of the
* {@link jdk.internal.classfile.Classfile.Option#processLineNumbers(boolean)} option. * {@link jdk.internal.classfile.Classfile.LineNumbersOption} option.
*/ */
public sealed interface LocalVariableTypeTableAttribute public sealed interface LocalVariableTypeTableAttribute
extends Attribute<LocalVariableTypeTableAttribute> extends Attribute<LocalVariableTypeTableAttribute>

View File

@ -96,11 +96,11 @@ public sealed interface ClassRemapper extends ClassTransform permits ClassRemapp
/** /**
* Remaps the whole ClassModel into a new class file, including the class name. * Remaps the whole ClassModel into a new class file, including the class name.
* @param context Classfile context
* @param clm class model to re-map * @param clm class model to re-map
* @return re-mapped class file bytes * @return re-mapped class file bytes
*/ */
default byte[] remapClass(ClassModel clm) { default byte[] remapClass(Classfile context, ClassModel clm) {
return Classfile.build(map(clm.thisClass().asSymbol()), return context.transform(clm, map(clm.thisClass().asSymbol()), this);
clb -> clm.forEachElement(resolve(clb).consumer()));
} }
} }

View File

@ -30,6 +30,7 @@ import java.util.ArrayDeque;
import java.util.Map; import java.util.Map;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import jdk.internal.classfile.Classfile;
import jdk.internal.classfile.ClassModel; import jdk.internal.classfile.ClassModel;
import jdk.internal.classfile.ClassTransform; import jdk.internal.classfile.ClassTransform;
import jdk.internal.classfile.CodeModel; import jdk.internal.classfile.CodeModel;
@ -101,9 +102,9 @@ class PackageSnippets {
// @start region="singleClassRemap" // @start region="singleClassRemap"
var classRemapper = ClassRemapper.of( var classRemapper = ClassRemapper.of(
Map.of(ClassDesc.of("Foo"), ClassDesc.of("Bar"))); Map.of(ClassDesc.of("Foo"), ClassDesc.of("Bar")));
var cc = Classfile.of();
for (var classModel : allMyClasses) { for (var classModel : allMyClasses) {
byte[] newBytes = classRemapper.remapClass(classModel); byte[] newBytes = classRemapper.remapClass(cc, classModel);
} }
// @end // @end
@ -113,9 +114,9 @@ class PackageSnippets {
// @start region="allPackageRemap" // @start region="allPackageRemap"
var classRemapper = ClassRemapper.of(cd -> var classRemapper = ClassRemapper.of(cd ->
ClassDesc.ofDescriptor(cd.descriptorString().replace("Lcom/oldpackage/", "Lcom/newpackage/"))); ClassDesc.ofDescriptor(cd.descriptorString().replace("Lcom/oldpackage/", "Lcom/newpackage/")));
var cc = Classfile.of();
for (var classModel : allMyClasses) { for (var classModel : allMyClasses) {
byte[] newBytes = classRemapper.remapClass(classModel); byte[] newBytes = classRemapper.remapClass(cc, classModel);
} }
// @end // @end
@ -123,20 +124,23 @@ class PackageSnippets {
void codeLocalsShifting(ClassModel classModel) { void codeLocalsShifting(ClassModel classModel) {
// @start region="codeLocalsShifting" // @start region="codeLocalsShifting"
byte[] newBytes = classModel.transform((classBuilder, classElement) -> { byte[] newBytes = Classfile.of().transform(
if (classElement instanceof MethodModel method) classModel,
classBuilder.transformMethod(method, (classBuilder, classElement) -> {
MethodTransform.transformingCode( if (classElement instanceof MethodModel method)
CodeLocalsShifter.of(method.flags(), method.methodTypeSymbol()))); classBuilder.transformMethod(method,
else MethodTransform.transformingCode(
classBuilder.accept(classElement); CodeLocalsShifter.of(method.flags(), method.methodTypeSymbol())));
}); else
classBuilder.accept(classElement);
});
// @end // @end
} }
void codeRelabeling(ClassModel classModel) { void codeRelabeling(ClassModel classModel) {
// @start region="codeRelabeling" // @start region="codeRelabeling"
byte[] newBytes = classModel.transform( byte[] newBytes = Classfile.of().transform(
classModel,
ClassTransform.transformingMethodBodies( ClassTransform.transformingMethodBodies(
CodeTransform.ofStateful(CodeRelabeler::of))); CodeTransform.ofStateful(CodeRelabeler::of)));
// @end // @end
@ -150,7 +154,7 @@ class PackageSnippets {
var targetFieldNames = target.fields().stream().map(f -> f.fieldName().stringValue()).collect(Collectors.toSet()); var targetFieldNames = target.fields().stream().map(f -> f.fieldName().stringValue()).collect(Collectors.toSet());
var targetMethods = target.methods().stream().map(m -> m.methodName().stringValue() + m.methodType().stringValue()).collect(Collectors.toSet()); var targetMethods = target.methods().stream().map(m -> m.methodName().stringValue() + m.methodType().stringValue()).collect(Collectors.toSet());
var instrumentorClassRemapper = ClassRemapper.of(Map.of(instrumentor.thisClass().asSymbol(), target.thisClass().asSymbol())); var instrumentorClassRemapper = ClassRemapper.of(Map.of(instrumentor.thisClass().asSymbol(), target.thisClass().asSymbol()));
return target.transform( return Classfile.of().transform(target,
ClassTransform.transformingMethods( ClassTransform.transformingMethods(
instrumentedMethodsFilter, instrumentedMethodsFilter,
(mb, me) -> { (mb, me) -> {

View File

@ -39,7 +39,7 @@ import jdk.internal.classfile.ClassBuilder;
import jdk.internal.classfile.ClassModel; import jdk.internal.classfile.ClassModel;
import jdk.internal.classfile.Classfile; import jdk.internal.classfile.Classfile;
import jdk.internal.classfile.impl.ClassReaderImpl; import jdk.internal.classfile.impl.ClassReaderImpl;
import jdk.internal.classfile.impl.Options; import jdk.internal.classfile.impl.ClassfileImpl;
import java.lang.constant.ModuleDesc; import java.lang.constant.ModuleDesc;
import java.lang.constant.PackageDesc; import java.lang.constant.PackageDesc;
import jdk.internal.classfile.WritableElement; import jdk.internal.classfile.WritableElement;
@ -63,29 +63,22 @@ public sealed interface ConstantPoolBuilder
permits SplitConstantPool, TemporaryConstantPool { permits SplitConstantPool, TemporaryConstantPool {
/** /**
* {@return a new constant pool builder} The new constant pool builder * {@return a new constant pool builder} The new constant pool builder will
* will inherit the classfile processing options of the specified class. * be pre-populated with the contents of the constant pool associated with
* If the processing options include {@link Classfile.Option#constantPoolSharing(boolean)}, * the class reader.
* (the default) the new constant pool builder will be also be pre-populated with the
* contents of the constant pool associated with the class reader.
* *
* @param classModel the class to copy from * @param classModel the class to copy from
*/ */
static ConstantPoolBuilder of(ClassModel classModel) { static ConstantPoolBuilder of(ClassModel classModel) {
ClassReaderImpl reader = (ClassReaderImpl) classModel.constantPool(); return new SplitConstantPool((ClassReaderImpl) classModel.constantPool());
return reader.options().cpSharing
? new SplitConstantPool(reader)
: new SplitConstantPool(reader.options());
} }
/** /**
* {@return a new constant pool builder} The new constant pool builder * {@return a new constant pool builder} The new constant pool builder
* will be empty and have the specified classfile processing options. * will be empty.
*
* @param options the processing options
*/ */
static ConstantPoolBuilder of(Collection<Classfile.Option> options) { static ConstantPoolBuilder of() {
return new SplitConstantPool(new Options(options)); return new SplitConstantPool();
} }
/** /**

View File

@ -30,11 +30,13 @@ import jdk.internal.classfile.Attribute;
public class AbstractDirectBuilder<M> { public class AbstractDirectBuilder<M> {
protected final SplitConstantPool constantPool; protected final SplitConstantPool constantPool;
protected final ClassfileImpl context;
protected final AttributeHolder attributes = new AttributeHolder(); protected final AttributeHolder attributes = new AttributeHolder();
protected M original; protected M original;
public AbstractDirectBuilder(SplitConstantPool constantPool) { public AbstractDirectBuilder(SplitConstantPool constantPool, ClassfileImpl context) {
this.constantPool = constantPool; this.constantPool = constantPool;
this.context = context;
} }
public SplitConstantPool constantPool() { public SplitConstantPool constantPool() {

View File

@ -30,6 +30,8 @@ import jdk.internal.classfile.constantpool.*;
import java.lang.constant.ConstantDesc; import java.lang.constant.ConstantDesc;
import java.util.List; import java.util.List;
import static jdk.internal.classfile.Classfile.*;
public final class AnnotationImpl implements Annotation { public final class AnnotationImpl implements Annotation {
private final Utf8Entry className; private final Utf8Entry className;
private final List<AnnotationElement> elements; private final List<AnnotationElement> elements;
@ -113,7 +115,7 @@ public final class AnnotationImpl implements Annotation {
@Override @Override
public char tag() { public char tag() {
return 's'; return AEV_STRING;
} }
@Override @Override
@ -127,7 +129,7 @@ public final class AnnotationImpl implements Annotation {
@Override @Override
public char tag() { public char tag() {
return 'D'; return AEV_DOUBLE;
} }
@Override @Override
@ -141,7 +143,7 @@ public final class AnnotationImpl implements Annotation {
@Override @Override
public char tag() { public char tag() {
return 'F'; return AEV_FLOAT;
} }
@Override @Override
@ -155,7 +157,7 @@ public final class AnnotationImpl implements Annotation {
@Override @Override
public char tag() { public char tag() {
return 'J'; return AEV_LONG;
} }
@Override @Override
@ -169,7 +171,7 @@ public final class AnnotationImpl implements Annotation {
@Override @Override
public char tag() { public char tag() {
return 'I'; return AEV_INT;
} }
@Override @Override
@ -183,7 +185,7 @@ public final class AnnotationImpl implements Annotation {
@Override @Override
public char tag() { public char tag() {
return 'S'; return AEV_SHORT;
} }
@Override @Override
@ -197,7 +199,7 @@ public final class AnnotationImpl implements Annotation {
@Override @Override
public char tag() { public char tag() {
return 'C'; return AEV_CHAR;
} }
@Override @Override
@ -211,7 +213,7 @@ public final class AnnotationImpl implements Annotation {
@Override @Override
public char tag() { public char tag() {
return 'B'; return AEV_BYTE;
} }
@Override @Override
@ -225,7 +227,7 @@ public final class AnnotationImpl implements Annotation {
@Override @Override
public char tag() { public char tag() {
return 'Z'; return AEV_BOOLEAN;
} }
@Override @Override
@ -243,7 +245,7 @@ public final class AnnotationImpl implements Annotation {
@Override @Override
public char tag() { public char tag() {
return '['; return AEV_ARRAY;
} }
@Override @Override
@ -258,7 +260,7 @@ public final class AnnotationImpl implements Annotation {
implements AnnotationValue.OfEnum { implements AnnotationValue.OfEnum {
@Override @Override
public char tag() { public char tag() {
return 'e'; return AEV_ENUM;
} }
@Override @Override
@ -274,7 +276,7 @@ public final class AnnotationImpl implements Annotation {
implements AnnotationValue.OfAnnotation { implements AnnotationValue.OfAnnotation {
@Override @Override
public char tag() { public char tag() {
return '@'; return AEV_ANNOTATION;
} }
@Override @Override
@ -289,7 +291,7 @@ public final class AnnotationImpl implements Annotation {
implements AnnotationValue.OfClass { implements AnnotationValue.OfClass {
@Override @Override
public char tag() { public char tag() {
return 'c'; return AEV_CLASS;
} }
@Override @Override

View File

@ -58,19 +58,19 @@ class AnnotationReader {
char tag = (char) classReader.readU1(p); char tag = (char) classReader.readU1(p);
++p; ++p;
return switch (tag) { return switch (tag) {
case 'B' -> new AnnotationImpl.OfByteImpl((IntegerEntry)classReader.readEntry(p)); case AEV_BYTE -> new AnnotationImpl.OfByteImpl((IntegerEntry)classReader.readEntry(p));
case 'C' -> new AnnotationImpl.OfCharacterImpl((IntegerEntry)classReader.readEntry(p)); case AEV_CHAR -> new AnnotationImpl.OfCharacterImpl((IntegerEntry)classReader.readEntry(p));
case 'D' -> new AnnotationImpl.OfDoubleImpl((DoubleEntry)classReader.readEntry(p)); case AEV_DOUBLE -> new AnnotationImpl.OfDoubleImpl((DoubleEntry)classReader.readEntry(p));
case 'F' -> new AnnotationImpl.OfFloatImpl((FloatEntry)classReader.readEntry(p)); case AEV_FLOAT -> new AnnotationImpl.OfFloatImpl((FloatEntry)classReader.readEntry(p));
case 'I' -> new AnnotationImpl.OfIntegerImpl((IntegerEntry)classReader.readEntry(p)); case AEV_INT -> new AnnotationImpl.OfIntegerImpl((IntegerEntry)classReader.readEntry(p));
case 'J' -> new AnnotationImpl.OfLongImpl((LongEntry)classReader.readEntry(p)); case AEV_LONG -> new AnnotationImpl.OfLongImpl((LongEntry)classReader.readEntry(p));
case 'S' -> new AnnotationImpl.OfShortImpl((IntegerEntry)classReader.readEntry(p)); case AEV_SHORT -> new AnnotationImpl.OfShortImpl((IntegerEntry)classReader.readEntry(p));
case 'Z' -> new AnnotationImpl.OfBooleanImpl((IntegerEntry)classReader.readEntry(p)); case AEV_BOOLEAN -> new AnnotationImpl.OfBooleanImpl((IntegerEntry)classReader.readEntry(p));
case 's' -> new AnnotationImpl.OfStringImpl(classReader.readUtf8Entry(p)); case AEV_STRING -> new AnnotationImpl.OfStringImpl(classReader.readUtf8Entry(p));
case 'e' -> new AnnotationImpl.OfEnumImpl(classReader.readUtf8Entry(p), classReader.readUtf8Entry(p + 2)); case AEV_ENUM -> new AnnotationImpl.OfEnumImpl(classReader.readUtf8Entry(p), classReader.readUtf8Entry(p + 2));
case 'c' -> new AnnotationImpl.OfClassImpl(classReader.readUtf8Entry(p)); case AEV_CLASS -> new AnnotationImpl.OfClassImpl(classReader.readUtf8Entry(p));
case '@' -> new AnnotationImpl.OfAnnotationImpl(readAnnotation(classReader, p)); case AEV_ANNOTATION -> new AnnotationImpl.OfAnnotationImpl(readAnnotation(classReader, p));
case '[' -> { case AEV_ARRAY -> {
int numValues = classReader.readU2(p); int numValues = classReader.readU2(p);
p += 2; p += 2;
var values = new Object[numValues]; var values = new Object[numValues];

View File

@ -144,7 +144,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
} }
if (mapper != null) { if (mapper != null) {
filled[i] = mapper.readAttribute(enclosing, reader, p); filled[i] = mapper.readAttribute(enclosing, reader, p);
} else if (((ClassReaderImpl)reader).options().processUnknownAttributes) { } else if (((ClassReaderImpl)reader).context().unknownAttributesOption() == Classfile.UnknownAttributesOption.PASS_UNKNOWN_ATTRIBUTES) {
AttributeMapper<UnknownAttribute> fakeMapper = new AttributeMapper<>() { AttributeMapper<UnknownAttribute> fakeMapper = new AttributeMapper<>() {
@Override @Override
public String name() { public String name() {

View File

@ -39,22 +39,24 @@ import jdk.internal.classfile.constantpool.PoolEntry;
public final class BufWriterImpl implements BufWriter { public final class BufWriterImpl implements BufWriter {
private final ConstantPoolBuilder constantPool; private final ConstantPoolBuilder constantPool;
private final ClassfileImpl context;
private LabelContext labelContext; private LabelContext labelContext;
private final ClassEntry thisClass; private final ClassEntry thisClass;
private final int majorVersion; private final int majorVersion;
byte[] elems; byte[] elems;
int offset = 0; int offset = 0;
public BufWriterImpl(ConstantPoolBuilder constantPool) { public BufWriterImpl(ConstantPoolBuilder constantPool, ClassfileImpl context) {
this(constantPool, 64, null, 0); this(constantPool, context, 64, null, 0);
} }
public BufWriterImpl(ConstantPoolBuilder constantPool, int initialSize) { public BufWriterImpl(ConstantPoolBuilder constantPool, ClassfileImpl context, int initialSize) {
this(constantPool, initialSize, null, 0); this(constantPool, context, initialSize, null, 0);
} }
public BufWriterImpl(ConstantPoolBuilder constantPool, int initialSize, ClassEntry thisClass, int majorVersion) { public BufWriterImpl(ConstantPoolBuilder constantPool, ClassfileImpl context, int initialSize, ClassEntry thisClass, int majorVersion) {
this.constantPool = constantPool; this.constantPool = constantPool;
this.context = context;
elems = new byte[initialSize]; elems = new byte[initialSize];
this.thisClass = thisClass; this.thisClass = thisClass;
this.majorVersion = majorVersion; this.majorVersion = majorVersion;
@ -85,6 +87,10 @@ public final class BufWriterImpl implements BufWriter {
return majorVersion; return majorVersion;
} }
public ClassfileImpl context() {
return context;
}
@Override @Override
public void writeU1(int x) { public void writeU1(int x) {
writeIntBytes(1, x); writeIntBytes(1, x);

View File

@ -45,6 +45,7 @@ import java.util.function.Consumer;
public final class BufferedCodeBuilder public final class BufferedCodeBuilder
implements TerminalCodeBuilder, LabelContext { implements TerminalCodeBuilder, LabelContext {
private final SplitConstantPool constantPool; private final SplitConstantPool constantPool;
private final ClassfileImpl context;
private final List<CodeElement> elements = new ArrayList<>(); private final List<CodeElement> elements = new ArrayList<>();
private final LabelImpl startLabel, endLabel; private final LabelImpl startLabel, endLabel;
private final CodeModel original; private final CodeModel original;
@ -54,8 +55,10 @@ public final class BufferedCodeBuilder
public BufferedCodeBuilder(MethodInfo methodInfo, public BufferedCodeBuilder(MethodInfo methodInfo,
SplitConstantPool constantPool, SplitConstantPool constantPool,
ClassfileImpl context,
CodeModel original) { CodeModel original) {
this.constantPool = constantPool; this.constantPool = constantPool;
this.context = context;
this.startLabel = new LabelImpl(this, -1); this.startLabel = new LabelImpl(this, -1);
this.endLabel = new LabelImpl(this, -1); this.endLabel = new LabelImpl(this, -1);
this.original = original; this.original = original;
@ -204,7 +207,7 @@ public final class BufferedCodeBuilder
} }
public void writeTo(BufWriter buf) { public void writeTo(BufWriter buf) {
DirectCodeBuilder.build(methodInfo, cb -> elements.forEach(cb), constantPool, null).writeTo(buf); DirectCodeBuilder.build(methodInfo, cb -> elements.forEach(cb), constantPool, context, null).writeTo(buf);
} }
@Override @Override

View File

@ -36,6 +36,7 @@ import jdk.internal.classfile.constantpool.Utf8Entry;
public final class BufferedFieldBuilder public final class BufferedFieldBuilder
implements TerminalFieldBuilder { implements TerminalFieldBuilder {
private final SplitConstantPool constantPool; private final SplitConstantPool constantPool;
private final ClassfileImpl context;
private final Utf8Entry name; private final Utf8Entry name;
private final Utf8Entry desc; private final Utf8Entry desc;
private final List<FieldElement> elements = new ArrayList<>(); private final List<FieldElement> elements = new ArrayList<>();
@ -43,10 +44,12 @@ public final class BufferedFieldBuilder
private final FieldModel original; private final FieldModel original;
public BufferedFieldBuilder(SplitConstantPool constantPool, public BufferedFieldBuilder(SplitConstantPool constantPool,
ClassfileImpl context,
Utf8Entry name, Utf8Entry name,
Utf8Entry type, Utf8Entry type,
FieldModel original) { FieldModel original) {
this.constantPool = constantPool; this.constantPool = constantPool;
this.context = context;
this.name = name; this.name = name;
this.desc = type; this.desc = type;
this.flags = AccessFlags.ofField(); this.flags = AccessFlags.ofField();
@ -119,7 +122,7 @@ public final class BufferedFieldBuilder
@Override @Override
public void writeTo(BufWriter buf) { public void writeTo(BufWriter buf) {
DirectFieldBuilder fb = new DirectFieldBuilder(constantPool, name, desc, null); DirectFieldBuilder fb = new DirectFieldBuilder(constantPool, context, name, desc, null);
elements.forEach(fb); elements.forEach(fb);
fb.writeTo(buf); fb.writeTo(buf);
} }

View File

@ -47,6 +47,7 @@ public final class BufferedMethodBuilder
implements TerminalMethodBuilder, MethodInfo { implements TerminalMethodBuilder, MethodInfo {
private final List<MethodElement> elements; private final List<MethodElement> elements;
private final SplitConstantPool constantPool; private final SplitConstantPool constantPool;
private final ClassfileImpl context;
private final Utf8Entry name; private final Utf8Entry name;
private final Utf8Entry desc; private final Utf8Entry desc;
private AccessFlags flags; private AccessFlags flags;
@ -55,11 +56,13 @@ public final class BufferedMethodBuilder
MethodTypeDesc mDesc; MethodTypeDesc mDesc;
public BufferedMethodBuilder(SplitConstantPool constantPool, public BufferedMethodBuilder(SplitConstantPool constantPool,
ClassfileImpl context,
Utf8Entry nameInfo, Utf8Entry nameInfo,
Utf8Entry typeInfo, Utf8Entry typeInfo,
MethodModel original) { MethodModel original) {
this.elements = new ArrayList<>(); this.elements = new ArrayList<>();
this.constantPool = constantPool; this.constantPool = constantPool;
this.context = context;
this.name = nameInfo; this.name = nameInfo;
this.desc = typeInfo; this.desc = typeInfo;
this.flags = AccessFlags.ofMethod(); this.flags = AccessFlags.ofMethod();
@ -119,21 +122,21 @@ public final class BufferedMethodBuilder
@Override @Override
public MethodBuilder withCode(Consumer<? super CodeBuilder> handler) { public MethodBuilder withCode(Consumer<? super CodeBuilder> handler) {
return with(new BufferedCodeBuilder(this, constantPool, null) return with(new BufferedCodeBuilder(this, constantPool, context, null)
.run(handler) .run(handler)
.toModel()); .toModel());
} }
@Override @Override
public MethodBuilder transformCode(CodeModel code, CodeTransform transform) { public MethodBuilder transformCode(CodeModel code, CodeTransform transform) {
BufferedCodeBuilder builder = new BufferedCodeBuilder(this, constantPool, code); BufferedCodeBuilder builder = new BufferedCodeBuilder(this, constantPool, context, code);
builder.transform(code, transform); builder.transform(code, transform);
return with(builder.toModel()); return with(builder.toModel());
} }
@Override @Override
public BufferedCodeBuilder bufferedCodeBuilder(CodeModel original) { public BufferedCodeBuilder bufferedCodeBuilder(CodeModel original) {
return new BufferedCodeBuilder(this, constantPool, original); return new BufferedCodeBuilder(this, constantPool, context, original);
} }
public BufferedMethodBuilder run(Consumer<? super MethodBuilder> handler) { public BufferedMethodBuilder run(Consumer<? super MethodBuilder> handler) {
@ -204,7 +207,7 @@ public final class BufferedMethodBuilder
@Override @Override
public void writeTo(BufWriter buf) { public void writeTo(BufWriter buf) {
DirectMethodBuilder mb = new DirectMethodBuilder(constantPool, name, desc, methodFlags(), null); DirectMethodBuilder mb = new DirectMethodBuilder(constantPool, context, name, desc, methodFlags(), null);
elements.forEach(mb); elements.forEach(mb);
mb.writeTo(buf); mb.writeTo(buf);
} }

View File

@ -60,7 +60,7 @@ public final class ChainedClassBuilder
@Override @Override
public ClassBuilder withField(Utf8Entry name, Utf8Entry descriptor, Consumer<? super FieldBuilder> handler) { public ClassBuilder withField(Utf8Entry name, Utf8Entry descriptor, Consumer<? super FieldBuilder> handler) {
return downstream.with(new BufferedFieldBuilder(terminal.constantPool, return downstream.with(new BufferedFieldBuilder(terminal.constantPool, terminal.context,
name, descriptor, null) name, descriptor, null)
.run(handler) .run(handler)
.toModel()); .toModel());
@ -68,7 +68,7 @@ public final class ChainedClassBuilder
@Override @Override
public ClassBuilder transformField(FieldModel field, FieldTransform transform) { public ClassBuilder transformField(FieldModel field, FieldTransform transform) {
BufferedFieldBuilder builder = new BufferedFieldBuilder(terminal.constantPool, BufferedFieldBuilder builder = new BufferedFieldBuilder(terminal.constantPool, terminal.context,
field.fieldName(), field.fieldType(), field.fieldName(), field.fieldType(),
field); field);
builder.transform(field, transform); builder.transform(field, transform);
@ -78,7 +78,7 @@ public final class ChainedClassBuilder
@Override @Override
public ClassBuilder withMethod(Utf8Entry name, Utf8Entry descriptor, int flags, public ClassBuilder withMethod(Utf8Entry name, Utf8Entry descriptor, int flags,
Consumer<? super MethodBuilder> handler) { Consumer<? super MethodBuilder> handler) {
return downstream.with(new BufferedMethodBuilder(terminal.constantPool, return downstream.with(new BufferedMethodBuilder(terminal.constantPool, terminal.context,
name, descriptor, null) name, descriptor, null)
.run(handler) .run(handler)
.toModel()); .toModel());
@ -86,7 +86,7 @@ public final class ChainedClassBuilder
@Override @Override
public ClassBuilder transformMethod(MethodModel method, MethodTransform transform) { public ClassBuilder transformMethod(MethodModel method, MethodTransform transform) {
BufferedMethodBuilder builder = new BufferedMethodBuilder(terminal.constantPool, BufferedMethodBuilder builder = new BufferedMethodBuilder(terminal.constantPool, terminal.context,
method.methodName(), method.methodType(), method); method.methodName(), method.methodType(), method);
builder.transform(method, transform); builder.transform(method, transform);
return downstream.with(builder.toModel()); return downstream.with(builder.toModel());

View File

@ -32,14 +32,13 @@ import java.lang.constant.ClassDesc;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import jdk.internal.classfile.ClassHierarchyResolver; import jdk.internal.classfile.ClassHierarchyResolver;
import static java.lang.constant.ConstantDescs.CD_Object; import static java.lang.constant.ConstantDescs.CD_Object;
import static jdk.internal.classfile.Classfile.*; import static jdk.internal.classfile.Classfile.*;
import static java.util.Objects.requireNonNull;
/** /**
* Class hierarchy resolution framework is answering questions about classes assignability, common classes ancestor and whether the class represents an interface. * Class hierarchy resolution framework is answering questions about classes assignability, common classes ancestor and whether the class represents an interface.
@ -52,15 +51,8 @@ public final class ClassHierarchyImpl {
static final ClassHierarchyResolver.ClassHierarchyInfo OBJECT_INFO = new ClassHierarchyInfoImpl(null, false); static final ClassHierarchyResolver.ClassHierarchyInfo OBJECT_INFO = new ClassHierarchyInfoImpl(null, false);
} }
public static final ClassHierarchyResolver DEFAULT_RESOLVER = ClassHierarchyResolver public static final ClassHierarchyResolver DEFAULT_RESOLVER =
.ofResourceParsing(ResourceParsingClassHierarchyResolver.SYSTEM_STREAM_PROVIDER) new ClassLoadingClassHierarchyResolver(ClassLoadingClassHierarchyResolver.SYSTEM_CLASS_PROVIDER);
.orElse(new ClassLoadingClassHierarchyResolver(ClassLoadingClassHierarchyResolver.SYSTEM_CLASS_PROVIDER))
.cached(new Supplier<>() {
@Override
public Map<ClassDesc, ClassHierarchyResolver.ClassHierarchyInfo> get() {
return new ConcurrentHashMap<>();
}
});
private final ClassHierarchyResolver resolver; private final ClassHierarchyResolver resolver;
@ -69,7 +61,10 @@ public final class ClassHierarchyImpl {
* @param classHierarchyResolver <code>ClassHierarchyInfoResolver</code> instance * @param classHierarchyResolver <code>ClassHierarchyInfoResolver</code> instance
*/ */
public ClassHierarchyImpl(ClassHierarchyResolver classHierarchyResolver) { public ClassHierarchyImpl(ClassHierarchyResolver classHierarchyResolver) {
this.resolver = classHierarchyResolver; requireNonNull(classHierarchyResolver);
this.resolver = classHierarchyResolver instanceof CachedClassHierarchyResolver
? classHierarchyResolver
: classHierarchyResolver.cached();
} }
private ClassHierarchyInfoImpl resolve(ClassDesc classDesc) { private ClassHierarchyInfoImpl resolve(ClassDesc classDesc) {

View File

@ -63,9 +63,8 @@ public final class ClassImpl
private List<Attribute<?>> attributes; private List<Attribute<?>> attributes;
private List<ClassEntry> interfaces; private List<ClassEntry> interfaces;
public ClassImpl(byte[] cfbytes, public ClassImpl(byte[] cfbytes, ClassfileImpl context) {
Collection<Classfile.Option> options) { this.reader = new ClassReaderImpl(cfbytes, context);
this.reader = new ClassReaderImpl(cfbytes, options);
ClassReaderImpl reader = (ClassReaderImpl) this.reader; ClassReaderImpl reader = (ClassReaderImpl) this.reader;
int p = reader.interfacesPos; int p = reader.interfacesPos;
int icnt = reader.readU2(p); int icnt = reader.readU2(p);
@ -94,6 +93,10 @@ public final class ClassImpl
reader.setContainedClass(this); reader.setContainedClass(this);
} }
public int classfileLength() {
return reader.classfileLength();
}
@Override @Override
public AccessFlags flags() { public AccessFlags flags() {
return AccessFlags.ofClass(reader.flags()); return AccessFlags.ofClass(reader.flags());
@ -169,20 +172,6 @@ public final class ClassImpl
} }
} }
@Override
public byte[] transform(ClassTransform transform) {
ConstantPoolBuilder constantPool = ConstantPoolBuilder.of(this);
return Classfile.build(thisClass(), constantPool,
new Consumer<ClassBuilder>() {
@Override
public void accept(ClassBuilder builder) {
((DirectClassBuilder) builder).setOriginal(ClassImpl.this);
((DirectClassBuilder) builder).setSizeHint(reader.classfileLength());
builder.transform(ClassImpl.this, transform);
}
});
}
@Override @Override
public List<FieldModel> fields() { public List<FieldModel> fields() {
return fields; return fields;

View File

@ -77,7 +77,7 @@ public final class ClassReaderImpl
private final int constantPoolCount; private final int constantPoolCount;
private final int[] cpOffset; private final int[] cpOffset;
final Options options; final ClassfileImpl context;
final int interfacesPos; final int interfacesPos;
final PoolEntry[] cp; final PoolEntry[] cp;
@ -86,11 +86,11 @@ public final class ClassReaderImpl
private BootstrapMethodsAttribute bootstrapMethodsAttribute; private BootstrapMethodsAttribute bootstrapMethodsAttribute;
ClassReaderImpl(byte[] classfileBytes, ClassReaderImpl(byte[] classfileBytes,
Collection<Classfile.Option> options) { ClassfileImpl context) {
this.buffer = classfileBytes; this.buffer = classfileBytes;
this.classfileLength = classfileBytes.length; this.classfileLength = classfileBytes.length;
this.options = new Options(options); this.context = context;
this.attributeMapper = this.options.attributeMapper; this.attributeMapper = this.context.attributeMapperOption().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");
} }
@ -134,8 +134,8 @@ public final class ClassReaderImpl
this.interfacesPos = p; this.interfacesPos = p;
} }
public Options options() { public ClassfileImpl context() {
return options; return context;
} }
@Override @Override

View File

@ -0,0 +1,138 @@
/*
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.classfile.impl;
import java.util.List;
import java.util.function.Function;
import java.util.function.Consumer;
import jdk.internal.classfile.AttributeMapper;
import jdk.internal.classfile.Classfile;
import jdk.internal.classfile.Classfile.*;
import jdk.internal.classfile.ClassBuilder;
import jdk.internal.classfile.ClassHierarchyResolver;
import jdk.internal.classfile.ClassModel;
import jdk.internal.classfile.ClassTransform;
import jdk.internal.classfile.constantpool.ClassEntry;
import jdk.internal.classfile.constantpool.ConstantPoolBuilder;
import jdk.internal.classfile.constantpool.Utf8Entry;
public record ClassfileImpl(StackMapsOption stackMapsOption,
DebugElementsOption debugElementsOption,
LineNumbersOption lineNumbersOption,
UnknownAttributesOption unknownAttributesOption,
ConstantPoolSharingOption constantPoolSharingOption,
ShortJumpsOption shortJumpsOption,
DeadCodeOption deadCodeOption,
DeadLabelsOption deadLabelsOption,
ClassHierarchyResolverOption classHierarchyResolverOption,
AttributeMapperOption attributeMapperOption) implements Classfile {
public static final ClassfileImpl DEFAULT_CONTEXT = new ClassfileImpl(
StackMapsOption.STACK_MAPS_WHEN_REQUIRED,
DebugElementsOption.PASS_DEBUG,
LineNumbersOption.PASS_LINE_NUMBERS,
UnknownAttributesOption.PASS_UNKNOWN_ATTRIBUTES,
ConstantPoolSharingOption.SHARED_POOL,
ShortJumpsOption.FIX_SHORT_JUMPS,
DeadCodeOption.PATCH_DEAD_CODE,
DeadLabelsOption.FAIL_ON_DEAD_LABELS,
new ClassHierarchyResolverOptionImpl(ClassHierarchyResolver.defaultResolver()),
new AttributeMapperOptionImpl(new Function<>() {
@Override
public AttributeMapper<?> apply(Utf8Entry k) {
return null;
}
}));
@SuppressWarnings("unchecked")
@Override
public ClassfileImpl withOptions(Option... options) {
var smo = stackMapsOption;
var deo = debugElementsOption;
var lno = lineNumbersOption;
var uao = unknownAttributesOption;
var cpso = constantPoolSharingOption;
var sjo = shortJumpsOption;
var dco = deadCodeOption;
var dlo = deadLabelsOption;
var chro = classHierarchyResolverOption;
var amo = attributeMapperOption;
for (var o : options) {
switch (o) {
case StackMapsOption oo -> smo = oo;
case DebugElementsOption oo -> deo = oo;
case LineNumbersOption oo -> lno = oo;
case UnknownAttributesOption oo -> uao = oo;
case ConstantPoolSharingOption oo -> cpso = oo;
case ShortJumpsOption oo -> sjo = oo;
case DeadCodeOption oo -> dco = oo;
case DeadLabelsOption oo -> dlo = oo;
case ClassHierarchyResolverOption oo -> chro = oo;
case AttributeMapperOption oo -> amo = oo;
}
}
return new ClassfileImpl(smo, deo, lno, uao, cpso, sjo, dco, dlo, chro, amo);
}
@Override
public ClassModel parse(byte[] bytes) {
return new ClassImpl(bytes, this);
}
@Override
public byte[] build(ClassEntry thisClassEntry,
ConstantPoolBuilder constantPool,
Consumer<? super ClassBuilder> handler) {
thisClassEntry = AbstractPoolEntry.maybeClone(constantPool, thisClassEntry);
DirectClassBuilder builder = new DirectClassBuilder((SplitConstantPool)constantPool, this, thisClassEntry);
handler.accept(builder);
return builder.build();
}
@Override
public byte[] transform(ClassModel model, ClassEntry newClassName, ClassTransform transform) {
ConstantPoolBuilder constantPool = constantPoolSharingOption() == ConstantPoolSharingOption.SHARED_POOL
? ConstantPoolBuilder.of(model)
: ConstantPoolBuilder.of();
return build(newClassName, constantPool,
new Consumer<ClassBuilder>() {
@Override
public void accept(ClassBuilder builder) {
((DirectClassBuilder) builder).setOriginal((ClassImpl)model);
((DirectClassBuilder) builder).setSizeHint(((ClassImpl)model).classfileLength());
builder.transform((ClassImpl)model, transform);
}
});
}
public record AttributeMapperOptionImpl(Function<Utf8Entry, AttributeMapper<?>> attributeMapper)
implements AttributeMapperOption {
}
public record ClassHierarchyResolverOptionImpl(ClassHierarchyResolver classHierarchyResolver)
implements ClassHierarchyResolverOption {
}
}

View File

@ -118,7 +118,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 (((ClassReaderImpl)classReader).options().processLineNumbers) if (((ClassReaderImpl)classReader).context().lineNumbersOption() == Classfile.LineNumbersOption.PASS_LINE_NUMBERS)
inflateLineNumbers(); inflateLineNumbers();
inflateJumpTargets(); inflateJumpTargets();
inflateTypeAnnotations(); inflateTypeAnnotations();
@ -150,6 +150,7 @@ public final class CodeImpl
} }
}, },
(SplitConstantPool)buf.constantPool(), (SplitConstantPool)buf.constantPool(),
((BufWriterImpl)buf).context(),
null).writeTo(buf); null).writeTo(buf);
} }
} }
@ -166,7 +167,7 @@ public final class CodeImpl
inflateMetadata(); inflateMetadata();
boolean doLineNumbers = (lineNumbers != null); boolean doLineNumbers = (lineNumbers != null);
generateCatchTargets(consumer); generateCatchTargets(consumer);
if (((ClassReaderImpl)classReader).options().processDebug) if (((ClassReaderImpl)classReader).context().debugElementsOption() == Classfile.DebugElementsOption.PASS_DEBUG)
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

@ -61,14 +61,15 @@ public final class DirectClassBuilder
private int sizeHint; private int sizeHint;
public DirectClassBuilder(SplitConstantPool constantPool, public DirectClassBuilder(SplitConstantPool constantPool,
ClassfileImpl context,
ClassEntry thisClass) { ClassEntry thisClass) {
super(constantPool); super(constantPool, context);
this.thisClassEntry = AbstractPoolEntry.maybeClone(constantPool, thisClass); this.thisClassEntry = AbstractPoolEntry.maybeClone(constantPool, thisClass);
this.flags = Classfile.DEFAULT_CLASS_FLAGS; this.flags = Classfile.DEFAULT_CLASS_FLAGS;
this.superclassEntry = null; this.superclassEntry = null;
this.interfaceEntries = Collections.emptyList(); this.interfaceEntries = Collections.emptyList();
this.majorVersion = Classfile.LATEST_MAJOR_VERSION; this.majorVersion = Classfile.latestMajorVersion();
this.minorVersion = Classfile.LATEST_MINOR_VERSION; this.minorVersion = Classfile.latestMinorVersion();
} }
@Override @Override
@ -81,13 +82,13 @@ public final class DirectClassBuilder
public ClassBuilder withField(Utf8Entry name, public ClassBuilder withField(Utf8Entry name,
Utf8Entry descriptor, Utf8Entry descriptor,
Consumer<? super FieldBuilder> handler) { Consumer<? super FieldBuilder> handler) {
return withField(new DirectFieldBuilder(constantPool, name, descriptor, null) return withField(new DirectFieldBuilder(constantPool, context, name, descriptor, null)
.run(handler)); .run(handler));
} }
@Override @Override
public ClassBuilder transformField(FieldModel field, FieldTransform transform) { public ClassBuilder transformField(FieldModel field, FieldTransform transform) {
DirectFieldBuilder builder = new DirectFieldBuilder(constantPool, field.fieldName(), DirectFieldBuilder builder = new DirectFieldBuilder(constantPool, context, field.fieldName(),
field.fieldType(), field); field.fieldType(), field);
builder.transform(field, transform); builder.transform(field, transform);
return withField(builder); return withField(builder);
@ -98,13 +99,13 @@ public final class DirectClassBuilder
Utf8Entry descriptor, Utf8Entry descriptor,
int flags, int flags,
Consumer<? super MethodBuilder> handler) { Consumer<? super MethodBuilder> handler) {
return withMethod(new DirectMethodBuilder(constantPool, name, descriptor, flags, null) return withMethod(new DirectMethodBuilder(constantPool, context, name, descriptor, flags, null)
.run(handler)); .run(handler));
} }
@Override @Override
public ClassBuilder transformMethod(MethodModel method, MethodTransform transform) { public ClassBuilder transformMethod(MethodModel method, MethodTransform transform) {
DirectMethodBuilder builder = new DirectMethodBuilder(constantPool, method.methodName(), DirectMethodBuilder builder = new DirectMethodBuilder(constantPool, context, method.methodName(),
method.methodType(), method.methodType(),
method.flags().flagsMask(), method.flags().flagsMask(),
method); method);
@ -141,7 +142,7 @@ public final class DirectClassBuilder
this.flags = flags; this.flags = flags;
} }
void setSizeHint(int sizeHint) { public void setSizeHint(int sizeHint) {
this.sizeHint = sizeHint; this.sizeHint = sizeHint;
} }
@ -166,8 +167,8 @@ public final class DirectClassBuilder
// We maintain two writers, and then we join them at the end // We maintain two writers, and then we join them at the end
int size = sizeHint == 0 ? 256 : sizeHint; int size = sizeHint == 0 ? 256 : sizeHint;
BufWriter head = new BufWriterImpl(constantPool, size); BufWriter head = new BufWriterImpl(constantPool, context, size);
BufWriterImpl tail = new BufWriterImpl(constantPool, size, thisClassEntry, majorVersion); BufWriterImpl tail = new BufWriterImpl(constantPool, context, size, thisClassEntry, majorVersion);
// The tail consists of fields and methods, and attributes // The tail consists of fields and methods, and attributes
// This should trigger all the CP/BSM mutation // This should trigger all the CP/BSM mutation

View File

@ -41,6 +41,7 @@ import jdk.internal.classfile.Classfile;
import jdk.internal.classfile.CodeBuilder; import jdk.internal.classfile.CodeBuilder;
import jdk.internal.classfile.CodeElement; import jdk.internal.classfile.CodeElement;
import jdk.internal.classfile.CodeModel; import jdk.internal.classfile.CodeModel;
import jdk.internal.classfile.Instruction;
import jdk.internal.classfile.Label; import jdk.internal.classfile.Label;
import jdk.internal.classfile.Opcode; import jdk.internal.classfile.Opcode;
import jdk.internal.classfile.TypeKind; import jdk.internal.classfile.TypeKind;
@ -101,14 +102,15 @@ public final class DirectCodeBuilder
public static Attribute<CodeAttribute> build(MethodInfo methodInfo, public static Attribute<CodeAttribute> build(MethodInfo methodInfo,
Consumer<? super CodeBuilder> handler, Consumer<? super CodeBuilder> handler,
SplitConstantPool constantPool, SplitConstantPool constantPool,
ClassfileImpl context,
CodeModel original) { CodeModel original) {
DirectCodeBuilder cb; DirectCodeBuilder cb;
try { try {
handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, original, false)); handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, false));
cb.buildContent(); cb.buildContent();
} catch (LabelOverflowException loe) { } catch (LabelOverflowException loe) {
if (constantPool.options().fixJumps) { if (context.shortJumpsOption() == Classfile.ShortJumpsOption.FIX_SHORT_JUMPS) {
handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, original, true)); handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, true));
cb.buildContent(); cb.buildContent();
} }
else else
@ -119,15 +121,16 @@ public final class DirectCodeBuilder
private DirectCodeBuilder(MethodInfo methodInfo, private DirectCodeBuilder(MethodInfo methodInfo,
SplitConstantPool constantPool, SplitConstantPool constantPool,
ClassfileImpl context,
CodeModel original, CodeModel original,
boolean transformFwdJumps) { boolean transformFwdJumps) {
super(constantPool); super(constantPool, context);
setOriginal(original); setOriginal(original);
this.methodInfo = methodInfo; this.methodInfo = methodInfo;
this.transformFwdJumps = transformFwdJumps; this.transformFwdJumps = transformFwdJumps;
this.transformBackJumps = constantPool.options().fixJumps; this.transformBackJumps = context.shortJumpsOption() == Classfile.ShortJumpsOption.FIX_SHORT_JUMPS;
bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, cai.codeLength()) bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, context, cai.codeLength())
: new BufWriterImpl(constantPool); : new BufWriterImpl(constantPool, context);
this.startLabel = new LabelImpl(this, 0); this.startLabel = new LabelImpl(this, 0);
this.endLabel = new LabelImpl(this, -1); this.endLabel = new LabelImpl(this, -1);
this.topLocal = Util.maxLocals(methodInfo.methodFlags(), methodInfo.methodTypeSymbol()); this.topLocal = Util.maxLocals(methodInfo.methodFlags(), methodInfo.methodTypeSymbol());
@ -196,7 +199,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 (constantPool.options().filterDeadLabels) { if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) {
handlersSize--; handlersSize--;
} else { } else {
throw new IllegalArgumentException("Unbound label in exception handler"); throw new IllegalArgumentException("Unbound label in exception handler");
@ -220,7 +223,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 (constantPool.options().processDebug) { if (context.debugElementsOption() == Classfile.DebugElementsOption.PASS_DEBUG) {
if (!characterRanges.isEmpty()) { if (!characterRanges.isEmpty()) {
Attribute<?> a = new UnboundAttribute.AdHocAttribute<>(Attributes.CHARACTER_RANGE_TABLE) { Attribute<?> a = new UnboundAttribute.AdHocAttribute<>(Attributes.CHARACTER_RANGE_TABLE) {
@ -233,7 +236,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 (constantPool.options().filterDeadLabels) { if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) {
crSize--; crSize--;
} else { } else {
throw new IllegalArgumentException("Unbound label in character range"); throw new IllegalArgumentException("Unbound label in character range");
@ -262,7 +265,7 @@ public final class DirectCodeBuilder
b.writeU2(lvSize); b.writeU2(lvSize);
for (LocalVariable l : localVariables) { for (LocalVariable l : localVariables) {
if (!l.writeTo(b)) { if (!l.writeTo(b)) {
if (constantPool.options().filterDeadLabels) { if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) {
lvSize--; lvSize--;
} else { } else {
throw new IllegalArgumentException("Unbound label in local variable type"); throw new IllegalArgumentException("Unbound label in local variable type");
@ -285,7 +288,7 @@ public final class DirectCodeBuilder
b.writeU2(localVariableTypes.size()); b.writeU2(localVariableTypes.size());
for (LocalVariableType l : localVariableTypes) { for (LocalVariableType l : localVariableTypes) {
if (!l.writeTo(b)) { if (!l.writeTo(b)) {
if (constantPool.options().filterDeadLabels) { if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) {
lvtSize--; lvtSize--;
} else { } else {
throw new IllegalArgumentException("Unbound label in local variable type"); throw new IllegalArgumentException("Unbound label in local variable type");
@ -305,6 +308,44 @@ public final class DirectCodeBuilder
} }
content = new UnboundAttribute.AdHocAttribute<>(Attributes.CODE) { content = new UnboundAttribute.AdHocAttribute<>(Attributes.CODE) {
private void writeCounters(boolean codeMatch, BufWriterImpl buf) {
if (codeMatch) {
buf.writeU2(original.maxStack());
buf.writeU2(original.maxLocals());
} else {
StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf);
buf.writeU2(cntr.maxStack());
buf.writeU2(cntr.maxLocals());
}
}
private void generateStackMaps(BufWriterImpl buf) throws IllegalArgumentException {
//new instance of generator immediately calculates maxStack, maxLocals, all frames,
// patches dead bytecode blocks and removes them from exception table
StackMapGenerator gen = StackMapGenerator.of(DirectCodeBuilder.this, buf);
attributes.withAttribute(gen.stackMapTableAttribute());
buf.writeU2(gen.maxStack());
buf.writeU2(gen.maxLocals());
}
private void tryGenerateStackMaps(boolean codeMatch, BufWriterImpl buf) {
if (buf.getMajorVersion() >= Classfile.JAVA_6_VERSION) {
try {
generateStackMaps(buf);
} catch (IllegalArgumentException e) {
//failover following JVMS-4.10
if (buf.getMajorVersion() == Classfile.JAVA_6_VERSION) {
writeCounters(codeMatch, buf);
} else {
throw e;
}
}
} else {
writeCounters(codeMatch, buf);
}
}
@Override @Override
public void writeBody(BufWriter b) { public void writeBody(BufWriter b) {
BufWriterImpl buf = (BufWriterImpl) b; BufWriterImpl buf = (BufWriterImpl) b;
@ -318,52 +359,29 @@ public final class DirectCodeBuilder
methodInfo.methodName().stringValue(), methodInfo.methodName().stringValue(),
methodInfo.methodTypeSymbol().displayDescriptor())); methodInfo.methodTypeSymbol().displayDescriptor()));
} }
int maxStack, maxLocals;
Attribute<? extends StackMapTableAttribute> stackMapAttr;
boolean canReuseStackmaps = codeAndExceptionsMatch(codeLength);
if (!constantPool.options().generateStackmaps) { if (codeAndExceptionsMatch(codeLength)) {
StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf); switch (context.stackMapsOption()) {
maxStack = cntr.maxStack(); case STACK_MAPS_WHEN_REQUIRED -> {
maxLocals = cntr.maxLocals(); attributes.withAttribute(original.findAttribute(Attributes.STACK_MAP_TABLE).orElse(null));
stackMapAttr = null; writeCounters(true, buf);
}
else if (canReuseStackmaps) {
maxLocals = original.maxLocals();
maxStack = original.maxStack();
stackMapAttr = original.findAttribute(Attributes.STACK_MAP_TABLE).orElse(null);
}
else if (buf.getMajorVersion() >= Classfile.JAVA_6_VERSION) {
try {
//new instance of generator immediately calculates maxStack, maxLocals, all frames,
// patches dead bytecode blocks and removes them from exception table
StackMapGenerator gen = StackMapGenerator.of(DirectCodeBuilder.this, buf);
maxStack = gen.maxStack();
maxLocals = gen.maxLocals();
stackMapAttr = gen.stackMapTableAttribute();
} catch (IllegalArgumentException e) {
if (buf.getMajorVersion() == Classfile.JAVA_6_VERSION) {
//failover following JVMS-4.10
StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf);
maxStack = cntr.maxStack();
maxLocals = cntr.maxLocals();
stackMapAttr = null;
} else {
throw e;
} }
case GENERATE_STACK_MAPS ->
generateStackMaps(buf);
case DROP_STACK_MAPS ->
writeCounters(true, buf);
}
} else {
switch (context.stackMapsOption()) {
case STACK_MAPS_WHEN_REQUIRED ->
tryGenerateStackMaps(false, buf);
case GENERATE_STACK_MAPS ->
generateStackMaps(buf);
case DROP_STACK_MAPS ->
writeCounters(false, buf);
} }
} }
else {
StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf);
maxStack = cntr.maxStack();
maxLocals = cntr.maxLocals();
stackMapAttr = null;
}
attributes.withAttribute(stackMapAttr);
buf.writeU2(maxStack);
buf.writeU2(maxLocals);
buf.writeInt(codeLength); buf.writeInt(codeLength);
buf.writeBytes(bytecodesBufWriter); buf.writeBytes(bytecodesBufWriter);
writeExceptionHandlers(b); writeExceptionHandlers(b);
@ -377,9 +395,9 @@ public final class DirectCodeBuilder
private final BufWriterImpl buf; private final BufWriterImpl buf;
private int lastPc, lastLine, writtenLine; private int lastPc, lastLine, writtenLine;
public DedupLineNumberTableAttribute(ConstantPoolBuilder constantPool) { public DedupLineNumberTableAttribute(ConstantPoolBuilder constantPool, ClassfileImpl context) {
super(Attributes.LINE_NUMBER_TABLE); super(Attributes.LINE_NUMBER_TABLE);
buf = new BufWriterImpl(constantPool); buf = new BufWriterImpl(constantPool, context);
lastPc = -1; lastPc = -1;
writtenLine = -1; writtenLine = -1;
} }
@ -424,7 +442,7 @@ public final class DirectCodeBuilder
codeAttributesMatch = cai.codeLength == curPc() codeAttributesMatch = cai.codeLength == curPc()
&& cai.compareCodeBytes(bytecodesBufWriter, 0, codeLength); && cai.compareCodeBytes(bytecodesBufWriter, 0, codeLength);
if (codeAttributesMatch) { if (codeAttributesMatch) {
BufWriter bw = new BufWriterImpl(constantPool); BufWriter bw = new BufWriterImpl(constantPool, context);
writeExceptionHandlers(bw); writeExceptionHandlers(bw);
codeAttributesMatch = cai.classReader.compare(bw, 0, cai.exceptionHandlerPos, bw.size()); codeAttributesMatch = cai.classReader.compare(bw, 0, cai.exceptionHandlerPos, bw.size());
} }
@ -682,7 +700,7 @@ public final class DirectCodeBuilder
public void setLineNumber(int lineNo) { public void setLineNumber(int lineNo) {
if (lineNumberWriter == null) if (lineNumberWriter == null)
lineNumberWriter = new DedupLineNumberTableAttribute(constantPool); lineNumberWriter = new DedupLineNumberTableAttribute(constantPool, context);
lineNumberWriter.writeLineNumber(curPc(), lineNo); lineNumberWriter.writeLineNumber(curPc(), lineNo);
} }

View File

@ -42,10 +42,11 @@ public final class DirectFieldBuilder
private int flags; private int flags;
public DirectFieldBuilder(SplitConstantPool constantPool, public DirectFieldBuilder(SplitConstantPool constantPool,
ClassfileImpl context,
Utf8Entry name, Utf8Entry name,
Utf8Entry type, Utf8Entry type,
FieldModel original) { FieldModel original) {
super(constantPool); super(constantPool, context);
setOriginal(original); setOriginal(original);
this.name = name; this.name = name;
this.desc = type; this.desc = type;

View File

@ -50,11 +50,12 @@ public final class DirectMethodBuilder
MethodTypeDesc mDesc; MethodTypeDesc mDesc;
public DirectMethodBuilder(SplitConstantPool constantPool, public DirectMethodBuilder(SplitConstantPool constantPool,
ClassfileImpl context,
Utf8Entry nameInfo, Utf8Entry nameInfo,
Utf8Entry typeInfo, Utf8Entry typeInfo,
int flags, int flags,
MethodModel original) { MethodModel original) {
super(constantPool); super(constantPool, context);
setOriginal(original); setOriginal(original);
this.name = nameInfo; this.name = nameInfo;
this.desc = typeInfo; this.desc = typeInfo;
@ -105,7 +106,7 @@ public final class DirectMethodBuilder
@Override @Override
public BufferedCodeBuilder bufferedCodeBuilder(CodeModel original) { public BufferedCodeBuilder bufferedCodeBuilder(CodeModel original) {
return new BufferedCodeBuilder(this, constantPool, original); return new BufferedCodeBuilder(this, constantPool, context, original);
} }
@Override @Override
@ -116,7 +117,7 @@ public final class DirectMethodBuilder
private MethodBuilder withCode(CodeModel original, private MethodBuilder withCode(CodeModel original,
Consumer<? super CodeBuilder> handler) { Consumer<? super CodeBuilder> handler) {
var cb = DirectCodeBuilder.build(this, handler, constantPool, original); var cb = DirectCodeBuilder.build(this, handler, constantPool, context, original);
writeAttribute(cb); writeAttribute(cb);
return this; return this;
} }

View File

@ -1,82 +0,0 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.classfile.impl;
import java.util.Collection;
import java.util.function.Function;
import jdk.internal.classfile.AttributeMapper;
import jdk.internal.classfile.ClassHierarchyResolver;
import jdk.internal.classfile.Classfile;
import jdk.internal.classfile.constantpool.Utf8Entry;
import static jdk.internal.classfile.ClassHierarchyResolver.defaultResolver;
public class Options {
public enum Key {
GENERATE_STACK_MAPS, PROCESS_DEBUG, PROCESS_LINE_NUMBERS, PROCESS_UNKNOWN_ATTRIBUTES,
CP_SHARING, FIX_SHORT_JUMPS, PATCH_DEAD_CODE, HIERARCHY_RESOLVER, ATTRIBUTE_MAPPER,
FILTER_DEAD_LABELS;
}
public record OptionValue(Key key, Object value) implements Classfile.Option { }
public Boolean generateStackmaps = true;
public Boolean processDebug = true;
public Boolean processLineNumbers = true;
public Boolean processUnknownAttributes = true;
public Boolean cpSharing = true;
public Boolean fixJumps = true;
public Boolean patchCode = true;
public Boolean filterDeadLabels = false;
public ClassHierarchyResolver classHierarchyResolver = defaultResolver();
public Function<Utf8Entry, AttributeMapper<?>> attributeMapper = new Function<>() {
@Override
public AttributeMapper<?> apply(Utf8Entry k) {
return null;
}
};
@SuppressWarnings("unchecked")
public Options(Collection<Classfile.Option> options) {
for (var o : options) {
var ov = ((OptionValue)o);
var v = ov.value();
switch (ov.key()) {
case GENERATE_STACK_MAPS -> generateStackmaps = (Boolean) v;
case PROCESS_DEBUG -> processDebug = (Boolean) v;
case PROCESS_LINE_NUMBERS -> processLineNumbers = (Boolean) v;
case PROCESS_UNKNOWN_ATTRIBUTES -> processUnknownAttributes = (Boolean) v;
case CP_SHARING -> cpSharing = (Boolean) v;
case FIX_SHORT_JUMPS -> fixJumps = (Boolean) v;
case PATCH_DEAD_CODE -> patchCode = (Boolean) v;
case HIERARCHY_RESOLVER -> classHierarchyResolver = (ClassHierarchyResolver) v;
case ATTRIBUTE_MAPPER -> attributeMapper = (Function<Utf8Entry, AttributeMapper<?>>) v;
case FILTER_DEAD_LABELS -> filterDeadLabels = (Boolean) v;
}
}
}
}

View File

@ -27,7 +27,6 @@ package jdk.internal.classfile.impl;
import java.lang.constant.ConstantDesc; import java.lang.constant.ConstantDesc;
import java.lang.constant.MethodTypeDesc; import java.lang.constant.MethodTypeDesc;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import jdk.internal.classfile.Attribute; import jdk.internal.classfile.Attribute;
@ -81,7 +80,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
private final ClassReaderImpl parent; private final ClassReaderImpl parent;
private final int parentSize, parentBsmSize; private final int parentSize, parentBsmSize;
final Options options;
private int size, bsmSize; private int size, bsmSize;
private PoolEntry[] myEntries; private PoolEntry[] myEntries;
@ -91,10 +89,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
private EntryMap<BootstrapMethodEntryImpl> bsmMap; private EntryMap<BootstrapMethodEntryImpl> bsmMap;
public SplitConstantPool() { public SplitConstantPool() {
this(new Options(Collections.emptyList()));
}
public SplitConstantPool(Options options) {
this.size = 1; this.size = 1;
this.bsmSize = 0; this.bsmSize = 0;
this.myEntries = new PoolEntry[1024]; this.myEntries = new PoolEntry[1024];
@ -102,12 +96,10 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
this.parent = null; this.parent = null;
this.parentSize = 0; this.parentSize = 0;
this.parentBsmSize = 0; this.parentBsmSize = 0;
this.options = options;
this.doneFullScan = true; this.doneFullScan = true;
} }
public SplitConstantPool(ClassReader parent) { public SplitConstantPool(ClassReader parent) {
this.options = ((ClassReaderImpl) parent).options;
this.parent = (ClassReaderImpl) parent; this.parent = (ClassReaderImpl) parent;
this.parentSize = parent.entryCount(); this.parentSize = parent.entryCount();
this.parentBsmSize = parent.bootstrapMethodCount(); this.parentBsmSize = parent.bootstrapMethodCount();
@ -117,18 +109,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
this.myBsmEntries = new BootstrapMethodEntryImpl[8]; this.myBsmEntries = new BootstrapMethodEntryImpl[8];
} }
//clone constructor for internal purposes
SplitConstantPool(SplitConstantPool cloneFrom, Options options) {
this.options = options;
this.parent = cloneFrom.parent;
this.parentSize = cloneFrom.parentSize;
this.parentBsmSize = cloneFrom.parentBsmSize;
this.size = cloneFrom.size;
this.bsmSize = cloneFrom.bsmSize;
this.myEntries = Arrays.copyOf(cloneFrom.myEntries, cloneFrom.myEntries.length);
this.myBsmEntries = Arrays.copyOf(cloneFrom.myBsmEntries, cloneFrom.myBsmEntries.length);
}
@Override @Override
public int entryCount() { public int entryCount() {
return size; return size;
@ -153,10 +133,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
: myBsmEntries[index - parentBsmSize]; : myBsmEntries[index - parentBsmSize];
} }
public Options options() {
return options;
}
@Override @Override
public boolean canWriteDirect(ConstantPool other) { public boolean canWriteDirect(ConstantPool other) {
return this == other || parent == other; return this == other || parent == other;

View File

@ -153,6 +153,7 @@ public final class StackMapGenerator {
(dcb.methodInfo.methodFlags() & ACC_STATIC) != 0, (dcb.methodInfo.methodFlags() & ACC_STATIC) != 0,
dcb.bytecodesBufWriter.asByteBuffer().slice(0, dcb.bytecodesBufWriter.size()), dcb.bytecodesBufWriter.asByteBuffer().slice(0, dcb.bytecodesBufWriter.size()),
dcb.constantPool, dcb.constantPool,
dcb.context,
dcb.handlers); dcb.handlers);
} }
@ -194,6 +195,7 @@ public final class StackMapGenerator {
private final List<RawExceptionCatch> rawHandlers; private final List<RawExceptionCatch> rawHandlers;
private final ClassHierarchyImpl classHierarchy; private final ClassHierarchyImpl classHierarchy;
private final boolean patchDeadCode; private final boolean patchDeadCode;
private final boolean filterDeadLabels;
private List<Frame> frames; private List<Frame> frames;
private final Frame currentFrame; private final Frame currentFrame;
private int maxStack, maxLocals; private int maxStack, maxLocals;
@ -221,6 +223,7 @@ public final class StackMapGenerator {
boolean isStatic, boolean isStatic,
ByteBuffer bytecode, ByteBuffer bytecode,
SplitConstantPool cp, SplitConstantPool cp,
ClassfileImpl context,
List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) { List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) {
this.thisType = Type.referenceType(thisClass); this.thisType = Type.referenceType(thisClass);
this.methodName = methodName; this.methodName = methodName;
@ -231,8 +234,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(cp.options().classHierarchyResolver); this.classHierarchy = new ClassHierarchyImpl(context.classHierarchyResolverOption().classHierarchyResolver());
this.patchDeadCode = cp.options().patchCode; this.patchDeadCode = context.deadCodeOption() == Classfile.DeadCodeOption.PATCH_DEAD_CODE;
this.filterDeadLabels = context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS;
this.currentFrame = new Frame(classHierarchy); this.currentFrame = new Frame(classHierarchy);
generate(); generate();
} }
@ -832,22 +836,21 @@ public final class StackMapGenerator {
methodDesc.parameterList().stream().map(ClassDesc::displayName).collect(Collectors.joining(",")))); methodDesc.parameterList().stream().map(ClassDesc::displayName).collect(Collectors.joining(","))));
//try to attach debug info about corrupted bytecode to the message //try to attach debug info about corrupted bytecode to the message
try { try {
//clone SplitConstantPool with alternate Options var cc = Classfile.of();
var newCp = new SplitConstantPool(cp, new Options(List.of(Classfile.Option.generateStackmap(false)))); var clm = cc.parse(cc.build(cp.classEntry(thisType.sym()), cp, clb ->
var clb = new DirectClassBuilder(newCp, newCp.classEntry(ClassDesc.of("FakeClass"))); clb.withMethod(methodName, methodDesc, isStatic ? ACC_STATIC : 0, mb ->
clb.withMethod(methodName, methodDesc, isStatic ? ACC_STATIC : 0, mb -> ((DirectMethodBuilder)mb).writeAttribute(new UnboundAttribute.AdHocAttribute<CodeAttribute>(Attributes.CODE) {
((DirectMethodBuilder)mb).writeAttribute(new UnboundAttribute.AdHocAttribute<CodeAttribute>(Attributes.CODE) { @Override
@Override public void writeBody(BufWriter b) {
public void writeBody(BufWriter b) { b.writeU2(-1);//max stack
b.writeU2(-1);//max stack b.writeU2(-1);//max locals
b.writeU2(-1);//max locals b.writeInt(bytecode.limit());
b.writeInt(bytecode.limit()); b.writeBytes(bytecode.array(), 0, bytecode.limit());
b.writeBytes(bytecode.array(), 0, bytecode.limit()); b.writeU2(0);//exception handlers
b.writeU2(0);//exception handlers b.writeU2(0);//attributes
b.writeU2(0);//attributes }
} }))));
})); ClassPrinter.toYaml(clm.methods().get(0).code().get(), ClassPrinter.Verbosity.TRACE_ALL, sb::append);
ClassPrinter.toYaml(Classfile.parse(clb.build()).methods().get(0).code().get(), ClassPrinter.Verbosity.TRACE_ALL, sb::append);
} catch (Error | Exception suppresed) { } catch (Error | Exception suppresed) {
//fallback to bytecode hex dump //fallback to bytecode hex dump
bytecode.rewind(); bytecode.rewind();
@ -931,7 +934,7 @@ public final class StackMapGenerator {
for (var exhandler : rawHandlers) try { for (var exhandler : rawHandlers) try {
offsets.set(exhandler.handler()); offsets.set(exhandler.handler());
} catch (IllegalArgumentException iae) { } catch (IllegalArgumentException iae) {
if (!cp.options().filterDeadLabels) if (!filterDeadLabels)
generatorError("Detected exception handler out of bytecode range"); generatorError("Detected exception handler out of bytecode range");
} }
return offsets; return offsets;

View File

@ -37,7 +37,7 @@ import jdk.internal.classfile.impl.BoundCharacterRange;
* A pseudo-instruction which models a single entry in the * A pseudo-instruction which models a single entry in the
* {@link CharacterRangeTableAttribute}. Delivered as a {@link CodeElement} * {@link CharacterRangeTableAttribute}. Delivered as a {@link CodeElement}
* during traversal of the elements of a {@link CodeModel}, according to * during traversal of the elements of a {@link CodeModel}, according to
* the setting of the {@link Classfile.Option#processDebug(boolean)} option. * the setting of the {@link Classfile.DebugElementsOption} option.
*/ */
public sealed interface CharacterRange extends PseudoInstruction public sealed interface CharacterRange extends PseudoInstruction
permits AbstractPseudoInstruction.UnboundCharacterRange, BoundCharacterRange { permits AbstractPseudoInstruction.UnboundCharacterRange, BoundCharacterRange {

View File

@ -136,7 +136,7 @@ public sealed interface ConstantInstruction extends Instruction {
static ArgumentConstantInstruction ofArgument(Opcode op, int value) { static ArgumentConstantInstruction ofArgument(Opcode op, int value) {
Util.checkKind(op, Opcode.Kind.CONSTANT); Util.checkKind(op, Opcode.Kind.CONSTANT);
if (op != Opcode.BIPUSH && op != Opcode.SIPUSH) if (op != Opcode.BIPUSH && op != Opcode.SIPUSH)
throw new IllegalArgumentException(String.format("Wrong opcode specified; found %s, expected BIPUSH or SIPUSH", op, op.kind())); throw new IllegalArgumentException(String.format("Wrong opcode specified; found %s, expected BIPUSH or SIPUSH", op));
return new AbstractInstruction.UnboundArgumentConstantInstruction(op, value); return new AbstractInstruction.UnboundArgumentConstantInstruction(op, value);
} }
@ -150,7 +150,7 @@ public sealed interface ConstantInstruction extends Instruction {
static LoadConstantInstruction ofLoad(Opcode op, LoadableConstantEntry constant) { static LoadConstantInstruction ofLoad(Opcode op, LoadableConstantEntry constant) {
Util.checkKind(op, Opcode.Kind.CONSTANT); Util.checkKind(op, Opcode.Kind.CONSTANT);
if (op != Opcode.LDC && op != Opcode.LDC_W && op != Opcode.LDC2_W) if (op != Opcode.LDC && op != Opcode.LDC_W && op != Opcode.LDC2_W)
throw new IllegalArgumentException(String.format("Wrong opcode specified; found %s, expected LDC, LDC_W or LDC2_W", op, op.kind())); throw new IllegalArgumentException(String.format("Wrong opcode specified; found %s, expected LDC, LDC_W or LDC2_W", op));
return new AbstractInstruction.UnboundLoadConstantInstruction(op, constant); return new AbstractInstruction.UnboundLoadConstantInstruction(op, constant);
} }
} }

View File

@ -35,7 +35,7 @@ import jdk.internal.classfile.impl.LineNumberImpl;
* A pseudo-instruction which models a single entry in the * A pseudo-instruction which models a single entry in the
* {@link LineNumberTableAttribute}. Delivered as a {@link CodeElement} * {@link LineNumberTableAttribute}. Delivered as a {@link CodeElement}
* during traversal of the elements of a {@link CodeModel}, according to * during traversal of the elements of a {@link CodeModel}, according to
* the setting of the {@link Classfile.Option#processLineNumbers(boolean)} option. * the setting of the {@link Classfile.LineNumbersOption} option.
* *
* @see PseudoInstruction * @see PseudoInstruction
*/ */

View File

@ -42,7 +42,7 @@ import jdk.internal.classfile.impl.TemporaryConstantPool;
* A pseudo-instruction which models a single entry in the * A pseudo-instruction which models a single entry in the
* {@link LocalVariableTableAttribute}. Delivered as a {@link CodeElement} * {@link LocalVariableTableAttribute}. Delivered as a {@link CodeElement}
* during traversal of the elements of a {@link CodeModel}, according to * during traversal of the elements of a {@link CodeModel}, according to
* the setting of the {@link Classfile.Option#processDebug(boolean)} option. * the setting of the {@link Classfile.DebugElementsOption} option.
* *
* @see PseudoInstruction * @see PseudoInstruction
*/ */

View File

@ -41,7 +41,7 @@ import jdk.internal.classfile.impl.TemporaryConstantPool;
* A pseudo-instruction which models a single entry in the {@link * A pseudo-instruction which models a single entry in the {@link
* LocalVariableTypeTableAttribute}. Delivered as a {@link CodeElement} during * LocalVariableTypeTableAttribute}. Delivered as a {@link CodeElement} during
* traversal of the elements of a {@link CodeModel}, according to the setting of * traversal of the elements of a {@link CodeModel}, according to the setting of
* the {@link Classfile.Option#processDebug(boolean)} option. * the {@link Classfile.DebugElementsOption} option.
*/ */
public sealed interface LocalVariableType extends PseudoInstruction public sealed interface LocalVariableType extends PseudoInstruction
permits AbstractPseudoInstruction.UnboundLocalVariableType, BoundLocalVariableType { permits AbstractPseudoInstruction.UnboundLocalVariableType, BoundLocalVariableType {

View File

@ -33,10 +33,10 @@
* <h2>Reading classfiles</h2> * <h2>Reading classfiles</h2>
* The main class for reading classfiles is {@link jdk.internal.classfile.ClassModel}; we * The main class for reading classfiles is {@link jdk.internal.classfile.ClassModel}; we
* convert bytes into a {@link jdk.internal.classfile.ClassModel} with {@link * convert bytes into a {@link jdk.internal.classfile.ClassModel} with {@link
* jdk.internal.classfile.Classfile#parse(byte[], jdk.internal.classfile.Classfile.Option[])}: * jdk.internal.classfile.Classfile#parse(byte[])}:
* <p> * <p>
* {@snippet lang=java : * {@snippet lang=java :
* ClassModel cm = Classfile.parse(bytes); * ClassModel cm = Classfile.of().parse(bytes);
* } * }
* <p> * <p>
* There are several additional overloads of {@code parse} that let you specify * There are several additional overloads of {@code parse} that let you specify
@ -164,31 +164,30 @@
* <p> * <p>
* For nonstandard attributes, user-provided attribute mappers can be specified * For nonstandard attributes, user-provided attribute mappers can be specified
* through the use of the {@link * through the use of the {@link
* jdk.internal.classfile.Classfile.Option#attributeMapper(java.util.function.Function)}} * jdk.internal.classfile.Classfile.AttributeMapperOption#of(java.util.function.Function)}}
* classfile option. Implementations of custom attributes should extend {@link * classfile option. Implementations of custom attributes should extend {@link
* jdk.internal.classfile.CustomAttribute}. * jdk.internal.classfile.CustomAttribute}.
* *
* <h3>Options</h3> * <h3>Options</h3>
* <p> * <p>
* {@link jdk.internal.classfile.Classfile#parse(byte[], jdk.internal.classfile.Classfile.Option[])} * {@link jdk.internal.classfile.Classfile#of(jdk.internal.classfile.Classfile.Option[])}
* accepts a list of options. {@link jdk.internal.classfile.Classfile.Option} exports some * accepts a list of options. {@link jdk.internal.classfile.Classfile.Option} is a base interface
* static boolean options, as well as factories for more complex options, * for some statically enumerated options, as well as factories for more complex options,
* including: * including:
* <ul> * <ul>
* <li>{@link jdk.internal.classfile.Classfile.Option#generateStackmap(boolean)} * <li>{@link jdk.internal.classfile.Classfile.StackMapsOption}
* -- generate stackmaps (default is true)</li> * -- generate stackmaps (default is {@code STACK_MAPS_WHEN_REQUIRED})</li>
* <li>{@link jdk.internal.classfile.Classfile.Option#processDebug(boolean)} * <li>{@link jdk.internal.classfile.Classfile.DebugElementsOption}
* -- processing of debug information, such as local variable metadata (default is true) </li> * -- processing of debug information, such as local variable metadata (default is {@code PASS_DEBUG}) </li>
* <li>{@link jdk.internal.classfile.Classfile.Option#processLineNumbers(boolean)} * <li>{@link jdk.internal.classfile.Classfile.LineNumbersOption}
* -- processing of line numbers (default is true) </li> * -- processing of line numbers (default is {@code PASS_LINE_NUMBERS}) </li>
* <li>{@link jdk.internal.classfile.Classfile.Option#processUnknownAttributes(boolean)} * <li>{@link jdk.internal.classfile.Classfile.UnknownAttributesOption}
* -- processing of unrecognized attributes (default is true)</li> * -- processing of unrecognized attributes (default is {@code PASS_UNKNOWN_ATTRIBUTES})</li>
* <li>{@link jdk.internal.classfile.Classfile.Option#constantPoolSharing(boolean)}} * <li>{@link jdk.internal.classfile.Classfile.ConstantPoolSharingOption}}
* -- share constant pool when transforming (default is true)</li> * -- share constant pool when transforming (default is {@code SHARED_POOL})</li>
* <li>{@link jdk.internal.classfile.Classfile.Option#classHierarchyResolver(jdk.internal.classfile.ClassHierarchyResolver)} * <li>{@link jdk.internal.classfile.Classfile.ClassHierarchyResolverOption#of(jdk.internal.classfile.ClassHierarchyResolver)}
* -- specify a custom class hierarchy * -- specify a custom class hierarchy resolver used by stack map generation</li>
* resolver used by stack map generation</li> * <li>{@link jdk.internal.classfile.Classfile.AttributeMapperOption#of(java.util.function.Function)}
* <li>{@link jdk.internal.classfile.Classfile.Option#attributeMapper(java.util.function.Function)}
* -- specify format of custom attributes</li> * -- specify format of custom attributes</li>
* </ul> * </ul>
* <p> * <p>
@ -304,7 +303,8 @@
* <p> * <p>
* and then transform the classfile: * and then transform the classfile:
* {@snippet lang=java : * {@snippet lang=java :
* byte[] newBytes = Classfile.parse(bytes).transform(ct); * var cc = Classfile.of();
* byte[] newBytes = cc.transform(cc.parse(bytes), ct);
* } * }
* <p> * <p>
* This is much more concise (and less error-prone) than the equivalent * This is much more concise (and less error-prone) than the equivalent
@ -322,10 +322,11 @@
* jdk.internal.classfile.CodeTransform#andThen(jdk.internal.classfile.CodeTransform)}: * jdk.internal.classfile.CodeTransform#andThen(jdk.internal.classfile.CodeTransform)}:
* <p> * <p>
* {@snippet lang=java : * {@snippet lang=java :
* byte[] newBytes = Classfile.parse(bytes) * var cc = Classfile.of();
* .transform(ClassTransform.transformingMethods( * byte[] newBytes = cc.transform(cc.parse(bytes),
* MethodTransform.transformingCode( * ClassTransform.transformingMethods(
* fooToBar.andThen(instrumentCalls)))); * MethodTransform.transformingCode(
* fooToBar.andThen(instrumentCalls))));
* } * }
* *
* Transform {@code instrumentCalls} will receive all code elements produced by * Transform {@code instrumentCalls} will receive all code elements produced by
@ -341,7 +342,7 @@
* attributes that are not transformed can be processed by bulk-copying their * attributes that are not transformed can be processed by bulk-copying their
* bytes, rather than parsing them and regenerating their contents.) If * bytes, rather than parsing them and regenerating their contents.) If
* constant pool sharing is not desired it can be suppressed * constant pool sharing is not desired it can be suppressed
* with the {@link jdk.internal.classfile.Classfile.Option#constantPoolSharing(boolean)} option. * with the {@link jdk.internal.classfile.Classfile.ConstantPoolSharingOption} option.
* Such suppression may be beneficial when transformation removes many elements, * Such suppression may be beneficial when transformation removes many elements,
* resulting in many unreferenced constant pool entries. * resulting in many unreferenced constant pool entries.
* *

View File

@ -30,6 +30,7 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.lang.reflect.AccessFlag; import java.lang.reflect.AccessFlag;
import java.util.ArrayDeque;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Map; import java.util.Map;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -62,7 +63,7 @@ import jdk.internal.classfile.instruction.StoreInstruction;
class PackageSnippets { class PackageSnippets {
void enumerateFieldsMethods1(byte[] bytes) { void enumerateFieldsMethods1(byte[] bytes) {
// @start region="enumerateFieldsMethods1" // @start region="enumerateFieldsMethods1"
ClassModel cm = Classfile.parse(bytes); ClassModel cm = Classfile.of().parse(bytes);
for (FieldModel fm : cm.fields()) for (FieldModel fm : cm.fields())
System.out.printf("Field %s%n", fm.fieldName().stringValue()); System.out.printf("Field %s%n", fm.fieldName().stringValue());
for (MethodModel mm : cm.methods()) for (MethodModel mm : cm.methods())
@ -72,7 +73,7 @@ class PackageSnippets {
void enumerateFieldsMethods2(byte[] bytes) { void enumerateFieldsMethods2(byte[] bytes) {
// @start region="enumerateFieldsMethods2" // @start region="enumerateFieldsMethods2"
ClassModel cm = Classfile.parse(bytes); ClassModel cm = Classfile.of().parse(bytes);
for (ClassElement ce : cm) { for (ClassElement ce : cm) {
switch (ce) { switch (ce) {
case MethodModel mm -> System.out.printf("Method %s%n", mm.methodName().stringValue()); case MethodModel mm -> System.out.printf("Method %s%n", mm.methodName().stringValue());
@ -85,7 +86,7 @@ class PackageSnippets {
void gatherDependencies1(byte[] bytes) { void gatherDependencies1(byte[] bytes) {
// @start region="gatherDependencies1" // @start region="gatherDependencies1"
ClassModel cm = Classfile.parse(bytes); ClassModel cm = Classfile.of().parse(bytes);
Set<ClassDesc> dependencies = new HashSet<>(); Set<ClassDesc> dependencies = new HashSet<>();
for (ClassElement ce : cm) { for (ClassElement ce : cm) {
@ -108,7 +109,7 @@ class PackageSnippets {
void gatherDependencies2(byte[] bytes) { void gatherDependencies2(byte[] bytes) {
// @start region="gatherDependencies2" // @start region="gatherDependencies2"
ClassModel cm = Classfile.parse(bytes); ClassModel cm = Classfile.of().parse(bytes);
Set<ClassDesc> dependencies = Set<ClassDesc> dependencies =
cm.elementStream() cm.elementStream()
.flatMap(ce -> ce instanceof MethodModel mm ? mm.elementStream() : Stream.empty()) .flatMap(ce -> ce instanceof MethodModel mm ? mm.elementStream() : Stream.empty())
@ -126,7 +127,7 @@ class PackageSnippets {
void writeHelloWorld() { void writeHelloWorld() {
// @start region="helloWorld" // @start region="helloWorld"
byte[] bytes = Classfile.build(ClassDesc.of("Hello"), cb -> { byte[] bytes = Classfile.of().build(ClassDesc.of("Hello"), cb -> {
cb.withFlags(AccessFlag.PUBLIC); cb.withFlags(AccessFlag.PUBLIC);
cb.withMethod("<init>", MethodTypeDesc.of(ConstantDescs.CD_void), Classfile.ACC_PUBLIC, cb.withMethod("<init>", MethodTypeDesc.of(ConstantDescs.CD_void), Classfile.ACC_PUBLIC,
mb -> mb.withCode( mb -> mb.withCode(
@ -152,8 +153,8 @@ class PackageSnippets {
void stripDebugMethods1(byte[] bytes) { void stripDebugMethods1(byte[] bytes) {
// @start region="stripDebugMethods1" // @start region="stripDebugMethods1"
ClassModel classModel = Classfile.parse(bytes); ClassModel classModel = Classfile.of().parse(bytes);
byte[] newBytes = Classfile.build(classModel.thisClass().asSymbol(), byte[] newBytes = Classfile.of().build(classModel.thisClass().asSymbol(),
classBuilder -> { classBuilder -> {
for (ClassElement ce : classModel) { for (ClassElement ce : classModel) {
if (!(ce instanceof MethodModel mm if (!(ce instanceof MethodModel mm
@ -170,7 +171,8 @@ class PackageSnippets {
if (!(element instanceof MethodModel mm && mm.methodName().stringValue().startsWith("debug"))) if (!(element instanceof MethodModel mm && mm.methodName().stringValue().startsWith("debug")))
builder.with(element); builder.with(element);
}; };
byte[] newBytes = Classfile.parse(bytes).transform(ct); var cc = Classfile.of();
byte[] newBytes = cc.transform(cc.parse(bytes), ct);
// @end // @end
} }
@ -202,7 +204,7 @@ class PackageSnippets {
void fooToBarUnrolled(ClassModel classModel) { void fooToBarUnrolled(ClassModel classModel) {
// @start region="fooToBarUnrolled" // @start region="fooToBarUnrolled"
byte[] newBytes = Classfile.build(classModel.thisClass().asSymbol(), byte[] newBytes = Classfile.of().build(classModel.thisClass().asSymbol(),
classBuilder -> { classBuilder -> {
for (ClassElement ce : classModel) { for (ClassElement ce : classModel) {
if (ce instanceof MethodModel mm) { if (ce instanceof MethodModel mm) {
@ -234,7 +236,7 @@ class PackageSnippets {
void codeRelabeling(ClassModel classModel) { void codeRelabeling(ClassModel classModel) {
// @start region="codeRelabeling" // @start region="codeRelabeling"
byte[] newBytes = classModel.transform( byte[] newBytes = Classfile.of().transform(classModel,
ClassTransform.transformingMethodBodies( ClassTransform.transformingMethodBodies(
CodeTransform.ofStateful(CodeRelabeler::of))); CodeTransform.ofStateful(CodeRelabeler::of)));
// @end // @end
@ -244,11 +246,11 @@ class PackageSnippets {
byte[] classInstrumentation(ClassModel target, ClassModel instrumentor, Predicate<MethodModel> instrumentedMethodsFilter) { byte[] classInstrumentation(ClassModel target, ClassModel instrumentor, Predicate<MethodModel> instrumentedMethodsFilter) {
var instrumentorCodeMap = instrumentor.methods().stream() var instrumentorCodeMap = instrumentor.methods().stream()
.filter(instrumentedMethodsFilter) .filter(instrumentedMethodsFilter)
.collect(Collectors.toMap(mm -> mm.methodName().stringValue() + mm.methodType().stringValue(), mm -> mm.code().orElse(null))); .collect(Collectors.toMap(mm -> mm.methodName().stringValue() + mm.methodType().stringValue(), mm -> mm.code().orElseThrow()));
var targetFieldNames = target.fields().stream().map(f -> f.fieldName().stringValue()).collect(Collectors.toSet()); var targetFieldNames = target.fields().stream().map(f -> f.fieldName().stringValue()).collect(Collectors.toSet());
var targetMethods = target.methods().stream().map(m -> m.methodName().stringValue() + m.methodType().stringValue()).collect(Collectors.toSet()); var targetMethods = target.methods().stream().map(m -> m.methodName().stringValue() + m.methodType().stringValue()).collect(Collectors.toSet());
var instrumentorClassRemapper = ClassRemapper.of(Map.of(instrumentor.thisClass().asSymbol(), target.thisClass().asSymbol())); var instrumentorClassRemapper = ClassRemapper.of(Map.of(instrumentor.thisClass().asSymbol(), target.thisClass().asSymbol()));
return target.transform( return Classfile.of().transform(target,
ClassTransform.transformingMethods( ClassTransform.transformingMethods(
instrumentedMethodsFilter, instrumentedMethodsFilter,
(mb, me) -> { (mb, me) -> {
@ -266,13 +268,13 @@ class PackageSnippets {
&& mm.methodType().stringValue().equals(inv.type().stringValue())) { && mm.methodType().stringValue().equals(inv.type().stringValue())) {
//store stacked method parameters into locals //store stacked method parameters into locals
var storeStack = new LinkedList<StoreInstruction>(); var storeStack = new ArrayDeque<StoreInstruction>();
int slot = 0; int slot = 0;
if (!mm.flags().has(AccessFlag.STATIC)) if (!mm.flags().has(AccessFlag.STATIC))
storeStack.add(StoreInstruction.of(TypeKind.ReferenceType, slot++)); storeStack.push(StoreInstruction.of(TypeKind.ReferenceType, slot++));
for (var pt : mm.methodTypeSymbol().parameterList()) { for (var pt : mm.methodTypeSymbol().parameterList()) {
var tk = TypeKind.from(pt); var tk = TypeKind.from(pt);
storeStack.addFirst(StoreInstruction.of(tk, slot)); storeStack.push(StoreInstruction.of(tk, slot));
slot += tk.slotSize(); slot += tk.slotSize();
} }
storeStack.forEach(codeBuilder::with); storeStack.forEach(codeBuilder::with);

View File

@ -186,7 +186,7 @@ public class BindingSpecializer {
private static byte[] specializeHelper(MethodType leafType, MethodType callerMethodType, private static byte[] specializeHelper(MethodType leafType, MethodType callerMethodType,
CallingSequence callingSequence, ABIDescriptor abi) { CallingSequence callingSequence, ABIDescriptor abi) {
String className = callingSequence.forDowncall() ? CLASS_NAME_DOWNCALL : CLASS_NAME_UPCALL; String className = callingSequence.forDowncall() ? CLASS_NAME_DOWNCALL : CLASS_NAME_UPCALL;
byte[] bytes = Classfile.build(ClassDesc.ofInternalName(className), clb -> { byte[] bytes = Classfile.of().build(ClassDesc.ofInternalName(className), clb -> {
clb.withFlags(ACC_PUBLIC + ACC_FINAL + ACC_SUPER); clb.withFlags(ACC_PUBLIC + ACC_FINAL + ACC_SUPER);
clb.withSuperclass(CD_Object); clb.withSuperclass(CD_Object);
clb.withVersion(CLASSFILE_VERSION, 0); clb.withVersion(CLASSFILE_VERSION, 0);
@ -207,7 +207,7 @@ public class BindingSpecializer {
} }
if (PERFORM_VERIFICATION) { if (PERFORM_VERIFICATION) {
List<VerifyError> errors = Classfile.parse(bytes).verify(null); List<VerifyError> errors = Classfile.of().parse(bytes).verify(null);
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
errors.forEach(System.err::println); errors.forEach(System.err::println);
throw new IllegalStateException("Verification error(s)"); throw new IllegalStateException("Verification error(s)");

View File

@ -150,9 +150,10 @@ public final class ModuleInfoExtender {
* be discarded. * be discarded.
*/ */
public byte[] toByteArray() throws IOException { public byte[] toByteArray() throws IOException {
var cm = Classfile.parse(in.readAllBytes()); var cc = Classfile.of();
var cm = cc.parse(in.readAllBytes());
Version v = ModuleInfoExtender.this.version; Version v = ModuleInfoExtender.this.version;
return cm.transform(ClassTransform.endHandler(clb -> { return cc.transform(cm, ClassTransform.endHandler(clb -> {
// ModuleMainClass attribute // ModuleMainClass attribute
if (mainClass != null) { if (mainClass != null) {
clb.with(ModuleMainClassAttribute.of(ClassDesc.of(mainClass))); clb.with(ModuleMainClassAttribute.of(ClassDesc.of(mainClass)));

View File

@ -166,7 +166,7 @@ final class FingerPrint {
} }
private static ClassAttributes getClassAttributes(byte[] bytes) { private static ClassAttributes getClassAttributes(byte[] bytes) {
var cm = Classfile.parse(bytes); var cm = Classfile.of().parse(bytes);
ClassAttributes attrs = new ClassAttributes( ClassAttributes attrs = new ClassAttributes(
cm.flags(), cm.flags(),
cm.thisClass().asInternalName(), cm.thisClass().asInternalName(),

View File

@ -368,7 +368,7 @@ class JImageTask {
if (name.endsWith(".class") && !name.endsWith("module-info.class")) { if (name.endsWith(".class") && !name.endsWith("module-info.class")) {
try { try {
byte[] bytes = reader.getResource(location); byte[] bytes = reader.getResource(location);
Classfile.parse(bytes).forEachElement(cle -> { Classfile.of().parse(bytes).forEachElement(cle -> {
if (cle instanceof MethodModel mm) mm.forEachElement(me -> { if (cle instanceof MethodModel mm) mm.forEachElement(me -> {
if (me instanceof CodeModel com) com.forEachElement(coe -> { if (me instanceof CodeModel com) com.forEachElement(coe -> {
//do nothing here, just visit each model element //do nothing here, just visit each model element

View File

@ -88,7 +88,7 @@ public abstract class AbstractPlugin implements Plugin {
ClassModel newClassReader(String path, ResourcePoolEntry resource, Classfile.Option... options) { ClassModel newClassReader(String path, ResourcePoolEntry resource, Classfile.Option... options) {
byte[] content = resource.contentBytes(); byte[] content = resource.contentBytes();
try { try {
return Classfile.parse(content, options); return Classfile.of(options).parse(content);
} catch (Exception e) { } catch (Exception e) {
if (JlinkTask.DEBUG) { if (JlinkTask.DEBUG) {
System.err.printf("Failed to parse class file: %s from resource of type %s\n", path, System.err.printf("Failed to parse class file: %s from resource of type %s\n", path,
@ -102,7 +102,7 @@ public abstract class AbstractPlugin implements Plugin {
protected ClassModel newClassReader(String path, byte[] buf, Classfile.Option... options) { protected ClassModel newClassReader(String path, byte[] buf, Classfile.Option... options) {
try { try {
return Classfile.parse(buf, options); return Classfile.of(options).parse(buf);
} catch (Exception e) { } catch (Exception e) {
if (JlinkTask.DEBUG) { if (JlinkTask.DEBUG) {
System.err.printf("Failed to parse class file: %s\n", path); System.err.printf("Failed to parse class file: %s\n", path);

View File

@ -64,15 +64,16 @@ public final class StripJavaDebugAttributesPlugin extends AbstractPlugin {
if (path.endsWith("module-info.class")) { if (path.endsWith("module-info.class")) {
// XXX. Do we have debug info? // XXX. Do we have debug info?
} else { } else {
byte[] content = newClassReader(path, resource, var clm = newClassReader(path, resource,
Classfile.Option.processDebug(false), Classfile.DebugElementsOption.DROP_DEBUG,
Classfile.Option.processLineNumbers(false)).transform(ClassTransform Classfile.LineNumbersOption.DROP_LINE_NUMBERS);
byte[] content = Classfile.of().transform(clm, ClassTransform
.dropping(cle -> cle instanceof SourceFileAttribute .dropping(cle -> cle instanceof SourceFileAttribute
|| cle instanceof SourceDebugExtensionAttribute) || cle instanceof SourceDebugExtensionAttribute)
.andThen(ClassTransform.transformingMethods(MethodTransform .andThen(ClassTransform.transformingMethods(MethodTransform
.dropping(me -> me instanceof MethodParametersAttribute) .dropping(me -> me instanceof MethodParametersAttribute)
.andThen(MethodTransform .andThen(MethodTransform
.transformingCode(CodeTransform.ACCEPT_ALL))))); .transformingCode(CodeTransform.ACCEPT_ALL)))));
res = resource.copyWithContent(content); res = resource.copyWithContent(content);
} }
} }

View File

@ -431,7 +431,7 @@ public final class SystemModulesPlugin extends AbstractPlugin {
boolean hasModulePackages() throws IOException { boolean hasModulePackages() throws IOException {
try (InputStream in = getInputStream()) { try (InputStream in = getInputStream()) {
// parse module-info.class // parse module-info.class
return Classfile.parse(in.readAllBytes()).elementStream() return Classfile.of().parse(in.readAllBytes()).elementStream()
.anyMatch(e -> e instanceof ModulePackagesAttribute mpa .anyMatch(e -> e instanceof ModulePackagesAttribute mpa
&& !mpa.packages().isEmpty()); && !mpa.packages().isEmpty());
} }
@ -579,7 +579,7 @@ public final class SystemModulesPlugin extends AbstractPlugin {
* Generate SystemModules class * Generate SystemModules class
*/ */
public byte[] genClassBytes(Configuration cf) { public byte[] genClassBytes(Configuration cf) {
return Classfile.build(classDesc, return Classfile.of().build(classDesc,
clb -> { clb -> {
clb.withFlags(ACC_FINAL + ACC_SUPER) clb.withFlags(ACC_FINAL + ACC_SUPER)
.withInterfaceSymbols(List.of(CD_SYSTEM_MODULES)) .withInterfaceSymbols(List.of(CD_SYSTEM_MODULES))
@ -1676,7 +1676,7 @@ public final class SystemModulesPlugin extends AbstractPlugin {
// write the class file to the pool as a resource // write the class file to the pool as a resource
String rn = "/java.base/" + SYSTEM_MODULES_MAP_CLASSNAME + ".class"; String rn = "/java.base/" + SYSTEM_MODULES_MAP_CLASSNAME + ".class";
ResourcePoolEntry e = ResourcePoolEntry.create(rn, Classfile.build( ResourcePoolEntry e = ResourcePoolEntry.create(rn, Classfile.of().build(
CD_SYSTEM_MODULES_MAP, CD_SYSTEM_MODULES_MAP,
clb -> clb.withFlags(ACC_FINAL + ACC_SUPER) clb -> clb.withFlags(ACC_FINAL + ACC_SUPER)
.withVersion(52, 0) .withVersion(52, 0)

View File

@ -26,6 +26,7 @@
package jdk.tools.jlink.internal.plugins; package jdk.tools.jlink.internal.plugins;
import java.util.Map; import java.util.Map;
import jdk.internal.classfile.Classfile;
import jdk.internal.classfile.ClassTransform; import jdk.internal.classfile.ClassTransform;
import jdk.internal.classfile.CodeBuilder; import jdk.internal.classfile.CodeBuilder;
import jdk.internal.classfile.CodeElement; import jdk.internal.classfile.CodeElement;
@ -99,7 +100,8 @@ abstract class VersionPropsPlugin extends AbstractPlugin {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private byte[] redefine(String path, byte[] classFile) { private byte[] redefine(String path, byte[] classFile) {
return newClassReader(path, classFile).transform(ClassTransform.transformingMethodBodies( return Classfile.of().transform(newClassReader(path, classFile),
ClassTransform.transformingMethodBodies(
mm -> mm.methodName().equalsString("<clinit>"), mm -> mm.methodName().equalsString("<clinit>"),
new CodeTransform() { new CodeTransform() {
private CodeElement pendingLDC = null; private CodeElement pendingLDC = null;

View File

@ -80,8 +80,9 @@ public class LocalExecutionControl extends DirectExecutionControl {
private static final MethodTypeDesc MTD_void = MethodTypeDesc.of(ConstantDescs.CD_void); private static final MethodTypeDesc MTD_void = MethodTypeDesc.of(ConstantDescs.CD_void);
private static byte[] instrument(byte[] classFile) { private static byte[] instrument(byte[] classFile) {
return Classfile.parse(classFile) var cc = Classfile.of();
.transform(ClassTransform.transformingMethodBodies((cob, coe) -> { return cc.transform(cc.parse(classFile),
ClassTransform.transformingMethodBodies((cob, coe) -> {
if (coe instanceof BranchInstruction) if (coe instanceof BranchInstruction)
cob.invokestatic(CD_Cancel, "stopCheck", MTD_void); cob.invokestatic(CD_Cancel, "stopCheck", MTD_void);
cob.with(coe); cob.with(coe);
@ -89,7 +90,7 @@ public class LocalExecutionControl extends DirectExecutionControl {
} }
private static ClassBytecodes genCancelClass() { private static ClassBytecodes genCancelClass() {
return new ClassBytecodes(CANCEL_CLASS, Classfile.build(CD_Cancel, clb -> return new ClassBytecodes(CANCEL_CLASS, Classfile.of().build(CD_Cancel, clb ->
clb.withFlags(Classfile.ACC_PUBLIC) clb.withFlags(Classfile.ACC_PUBLIC)
.withField("allStop", ConstantDescs.CD_boolean, Classfile.ACC_PUBLIC | Classfile.ACC_STATIC | Classfile.ACC_VOLATILE) .withField("allStop", ConstantDescs.CD_boolean, Classfile.ACC_PUBLIC | Classfile.ACC_STATIC | Classfile.ACC_VOLATILE)
.withMethodBody("stopCheck", MTD_void, Classfile.ACC_PUBLIC | Classfile.ACC_STATIC, cob -> .withMethodBody("stopCheck", MTD_void, Classfile.ACC_PUBLIC | Classfile.ACC_STATIC, cob ->

View File

@ -1371,7 +1371,7 @@ public class ModuleDescriptorTest {
* complete set of packages. * complete set of packages.
*/ */
public void testReadsWithBadPackageFinder() throws Exception { public void testReadsWithBadPackageFinder() throws Exception {
ByteBuffer bb = ByteBuffer.wrap(Classfile.buildModule( ByteBuffer bb = ByteBuffer.wrap(Classfile.of().buildModule(
ModuleAttribute.of( ModuleAttribute.of(
ModuleDesc.of("foo"), ModuleDesc.of("foo"),
mb -> mb.requires(ModuleDesc.of("java.base"), 0, null) mb -> mb.requires(ModuleDesc.of("java.base"), 0, null)

View File

@ -58,9 +58,10 @@ class AdaptCodeTest {
@Test @Test
void testNullAdaptIterator() throws Exception { void testNullAdaptIterator() throws Exception {
ClassModel cm = Classfile.parse(testClassPath); var cc = Classfile.of();
ClassModel cm = cc.parse(testClassPath);
for (ClassTransform t : Transforms.noops) { for (ClassTransform t : Transforms.noops) {
byte[] newBytes = cm.transform(t); byte[] newBytes = cc.transform(cm, t);
String result = (String) String result = (String)
new ByteArrayClassLoader(AdaptCodeTest.class.getClassLoader(), testClassName, newBytes) new ByteArrayClassLoader(AdaptCodeTest.class.getClassLoader(), testClassName, newBytes)
.getMethod(testClassName, "many") .getMethod(testClassName, "many")
@ -77,15 +78,17 @@ class AdaptCodeTest {
}) })
void testNullAdaptIterator2(String path) throws Exception { void testNullAdaptIterator2(String path) throws Exception {
FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/")); FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
ClassModel cm = Classfile.parse(fs.getPath(path)); var cc = Classfile.of();
ClassModel cm = cc.parse(fs.getPath(path));
for (ClassTransform t : Transforms.noops) { for (ClassTransform t : Transforms.noops) {
byte[] newBytes = cm.transform(t); byte[] newBytes = cc.transform(cm, t);
} }
} }
@Test @Test
void testSevenOfThirteenIterator() throws Exception { void testSevenOfThirteenIterator() throws Exception {
ClassModel cm = Classfile.parse(testClassPath); var cc = Classfile.of();
ClassModel cm = cc.parse(testClassPath);
var transform = ClassTransform.transformingMethodBodies((codeB, codeE) -> { var transform = ClassTransform.transformingMethodBodies((codeB, codeE) -> {
switch (codeE) { switch (codeE) {
@ -100,7 +103,7 @@ class AdaptCodeTest {
} }
}); });
byte[] newBytes = cm.transform(transform); byte[] newBytes = cc.transform(cm, transform);
// Files.write(Path.of("foo.class"), newBytes); // Files.write(Path.of("foo.class"), newBytes);
String result = (String) String result = (String)
new ByteArrayClassLoader(AdaptCodeTest.class.getClassLoader(), testClassName, newBytes) new ByteArrayClassLoader(AdaptCodeTest.class.getClassLoader(), testClassName, newBytes)
@ -111,8 +114,9 @@ class AdaptCodeTest {
@Test @Test
void testCopy() throws Exception { void testCopy() throws Exception {
ClassModel cm = Classfile.parse(testClassPath); var cc = Classfile.of();
byte[] newBytes = Classfile.build(cm.thisClass().asSymbol(), cb -> cm.forEachElement(cb)); ClassModel cm = cc.parse(testClassPath);
byte[] newBytes = cc.build(cm.thisClass().asSymbol(), cb -> cm.forEachElement(cb));
// TestUtil.writeClass(newBytes, "TestClass.class"); // TestUtil.writeClass(newBytes, "TestClass.class");
String result = (String) String result = (String)
new ByteArrayClassLoader(AdaptCodeTest.class.getClassLoader(), testClassName, newBytes) new ByteArrayClassLoader(AdaptCodeTest.class.getClassLoader(), testClassName, newBytes)

View File

@ -75,8 +75,9 @@ class AdvancedTransformationsTest {
@Test @Test
void testShiftLocals() throws Exception { void testShiftLocals() throws Exception {
try (var in = StackMapGenerator.class.getResourceAsStream("StackMapGenerator.class")) { try (var in = StackMapGenerator.class.getResourceAsStream("StackMapGenerator.class")) {
var clm = Classfile.parse(in.readAllBytes()); var cc = Classfile.of();
var remapped = Classfile.parse(clm.transform((clb, cle) -> { var clm = cc.parse(in.readAllBytes());
var remapped = cc.parse(cc.transform(clm, (clb, cle) -> {
if (cle instanceof MethodModel mm) { if (cle instanceof MethodModel mm) {
clb.transformMethod(mm, (mb, me) -> { clb.transformMethod(mm, (mb, me) -> {
if (me instanceof CodeModel com) { if (me instanceof CodeModel com) {
@ -113,8 +114,9 @@ class AdvancedTransformationsTest {
ClassDesc.ofDescriptor(StackMapGenerator.class.descriptorString()), ClassDesc.of("remapped.StackMapGenerator") ClassDesc.ofDescriptor(StackMapGenerator.class.descriptorString()), ClassDesc.of("remapped.StackMapGenerator")
); );
try (var in = StackMapGenerator.class.getResourceAsStream("StackMapGenerator.class")) { try (var in = StackMapGenerator.class.getResourceAsStream("StackMapGenerator.class")) {
var clm = Classfile.parse(in.readAllBytes()); var cc = Classfile.of();
var remapped = Classfile.parse(ClassRemapper.of(map).remapClass(clm)); var clm = cc.parse(in.readAllBytes());
var remapped = cc.parse(ClassRemapper.of(map).remapClass(cc, clm));
assertEmpty(remapped.verify( assertEmpty(remapped.verify(
ClassHierarchyResolver.of(Set.of(ClassDesc.of("remapped.List")), Map.of( ClassHierarchyResolver.of(Set.of(ClassDesc.of("remapped.List")), Map.of(
ClassDesc.of("remapped.RemappedBytecode"), ConstantDescs.CD_Object, ClassDesc.of("remapped.RemappedBytecode"), ConstantDescs.CD_Object,
@ -167,11 +169,12 @@ class AdvancedTransformationsTest {
void testRemapModule() throws Exception { void testRemapModule() throws Exception {
var foo = ClassDesc.ofDescriptor(Foo.class.descriptorString()); var foo = ClassDesc.ofDescriptor(Foo.class.descriptorString());
var bar = ClassDesc.ofDescriptor(Bar.class.descriptorString()); var bar = ClassDesc.ofDescriptor(Bar.class.descriptorString());
var cc = Classfile.of();
var ma = Classfile.parse( var ma = cc.parse(
ClassRemapper.of(Map.of(foo, bar)).remapClass( ClassRemapper.of(Map.of(foo, bar)).remapClass(
Classfile.parse( cc,
Classfile.buildModule( cc.parse(
cc.buildModule(
ModuleAttribute.of(ModuleDesc.of("MyModule"), mab -> ModuleAttribute.of(ModuleDesc.of("MyModule"), mab ->
mab.uses(foo).provides(foo, foo)))))).findAttribute(Attributes.MODULE).get(); mab.uses(foo).provides(foo, foo)))))).findAttribute(Attributes.MODULE).get();
assertEquals(ma.uses().get(0).asSymbol(), bar); assertEquals(ma.uses().get(0).asSymbol(), bar);
@ -187,10 +190,11 @@ class AdvancedTransformationsTest {
var fooAnno = ClassDesc.ofDescriptor(FooAnno.class.descriptorString()); var fooAnno = ClassDesc.ofDescriptor(FooAnno.class.descriptorString());
var barAnno = ClassDesc.ofDescriptor(BarAnno.class.descriptorString()); var barAnno = ClassDesc.ofDescriptor(BarAnno.class.descriptorString());
var rec = ClassDesc.ofDescriptor(Rec.class.descriptorString()); var rec = ClassDesc.ofDescriptor(Rec.class.descriptorString());
var cc = Classfile.of();
var remapped = Classfile.parse( var remapped = cc.parse(
ClassRemapper.of(Map.of(foo, bar, fooAnno, barAnno)).remapClass( ClassRemapper.of(Map.of(foo, bar, fooAnno, barAnno)).remapClass(
Classfile.parse( cc,
cc.parse(
Rec.class.getResourceAsStream(Rec.class.getName() + ".class") Rec.class.getResourceAsStream(Rec.class.getName() + ".class")
.readAllBytes()))); .readAllBytes())));
var sb = new StringBuilder(); var sb = new StringBuilder();
@ -233,10 +237,11 @@ class AdvancedTransformationsTest {
@Test @Test
void testInstrumentClass() throws Exception { void testInstrumentClass() throws Exception {
var instrumentor = Classfile.parse(AdvancedTransformationsTest.class.getResourceAsStream("AdvancedTransformationsTest$InstrumentorClass.class").readAllBytes()); var cc = Classfile.of();
var target = Classfile.parse(AdvancedTransformationsTest.class.getResourceAsStream("AdvancedTransformationsTest$TargetClass.class").readAllBytes()); var instrumentor = cc.parse(AdvancedTransformationsTest.class.getResourceAsStream("AdvancedTransformationsTest$InstrumentorClass.class").readAllBytes());
var target = cc.parse(AdvancedTransformationsTest.class.getResourceAsStream("AdvancedTransformationsTest$TargetClass.class").readAllBytes());
var instrumentedBytes = instrument(target, instrumentor, mm -> mm.methodName().stringValue().equals("instrumentedMethod")); var instrumentedBytes = instrument(target, instrumentor, mm -> mm.methodName().stringValue().equals("instrumentedMethod"));
assertEmpty(Classfile.parse(instrumentedBytes).verify(null)); //System.out::print)); assertEmpty(cc.parse(instrumentedBytes).verify(null)); //System.out::print));
var targetClass = new ByteArrayClassLoader(AdvancedTransformationsTest.class.getClassLoader(), "AdvancedTransformationsTest$TargetClass", instrumentedBytes).loadClass("AdvancedTransformationsTest$TargetClass"); var targetClass = new ByteArrayClassLoader(AdvancedTransformationsTest.class.getClassLoader(), "AdvancedTransformationsTest$TargetClass", instrumentedBytes).loadClass("AdvancedTransformationsTest$TargetClass");
assertEquals(targetClass.getDeclaredMethod("instrumentedMethod", Boolean.class).invoke(targetClass.getDeclaredConstructor().newInstance(), false), 34); assertEquals(targetClass.getDeclaredMethod("instrumentedMethod", Boolean.class).invoke(targetClass.getDeclaredConstructor().newInstance(), false), 34);
} }
@ -297,7 +302,7 @@ class AdvancedTransformationsTest {
var targetFieldNames = target.fields().stream().map(f -> f.fieldName().stringValue()).collect(Collectors.toSet()); var targetFieldNames = target.fields().stream().map(f -> f.fieldName().stringValue()).collect(Collectors.toSet());
var targetMethods = target.methods().stream().map(m -> m.methodName().stringValue() + m.methodType().stringValue()).collect(Collectors.toSet()); var targetMethods = target.methods().stream().map(m -> m.methodName().stringValue() + m.methodType().stringValue()).collect(Collectors.toSet());
var instrumentorClassRemapper = ClassRemapper.of(Map.of(instrumentor.thisClass().asSymbol(), target.thisClass().asSymbol())); var instrumentorClassRemapper = ClassRemapper.of(Map.of(instrumentor.thisClass().asSymbol(), target.thisClass().asSymbol()));
return target.transform( return Classfile.of().transform(target,
ClassTransform.transformingMethods( ClassTransform.transformingMethods(
instrumentedMethodsFilter, instrumentedMethodsFilter,
(mb, me) -> { (mb, me) -> {

View File

@ -55,7 +55,7 @@ class AnnotationModelTest {
@Test @Test
void readAnnos() { void readAnnos() {
var model = Classfile.parse(fileBytes); var model = Classfile.of().parse(fileBytes);
var annotations = model.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).get().annotations(); var annotations = model.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).get().annotations();
assertEquals(annotations.size(), 3); assertEquals(annotations.size(), 3);

View File

@ -127,12 +127,13 @@ class AnnotationTest {
@Test @Test
void testAnnos() { void testAnnos() {
byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { var cc = Classfile.of();
byte[] bytes = cc.build(ClassDesc.of("Foo"), cb -> {
((DirectClassBuilder) cb).writeAttribute(buildAnnotationsWithCPB(cb.constantPool())); ((DirectClassBuilder) cb).writeAttribute(buildAnnotationsWithCPB(cb.constantPool()));
cb.withMethod("foo", MethodTypeDesc.of(CD_void), 0, mb -> mb.with(buildAnnotationsWithCPB(mb.constantPool()))); cb.withMethod("foo", MethodTypeDesc.of(CD_void), 0, mb -> mb.with(buildAnnotationsWithCPB(mb.constantPool())));
cb.withField("foo", CD_int, fb -> fb.with(buildAnnotationsWithCPB(fb.constantPool()))); cb.withField("foo", CD_int, fb -> fb.with(buildAnnotationsWithCPB(fb.constantPool())));
}); });
ClassModel cm = Classfile.parse(bytes); ClassModel cm = cc.parse(bytes);
List<ClassElement> ces = cm.elementList(); List<ClassElement> ces = cm.elementList();
List<Annotation> annos = ces.stream() List<Annotation> annos = ces.stream()
.filter(ce -> ce instanceof RuntimeVisibleAnnotationsAttribute) .filter(ce -> ce instanceof RuntimeVisibleAnnotationsAttribute)
@ -172,12 +173,13 @@ class AnnotationTest {
@Test @Test
void testAnnosNoCPB() { void testAnnosNoCPB() {
byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { var cc = Classfile.of();
byte[] bytes = cc.build(ClassDesc.of("Foo"), cb -> {
((DirectClassBuilder) cb).writeAttribute(buildAnnotations()); ((DirectClassBuilder) cb).writeAttribute(buildAnnotations());
cb.withMethod("foo", MethodTypeDesc.of(CD_void), 0, mb -> mb.with(buildAnnotations())); cb.withMethod("foo", MethodTypeDesc.of(CD_void), 0, mb -> mb.with(buildAnnotations()));
cb.withField("foo", CD_int, fb -> fb.with(buildAnnotations())); cb.withField("foo", CD_int, fb -> fb.with(buildAnnotations()));
}); });
ClassModel cm = Classfile.parse(bytes); ClassModel cm = cc.parse(bytes);
List<ClassElement> ces = cm.elementList(); List<ClassElement> ces = cm.elementList();
List<Annotation> annos = ces.stream() List<Annotation> annos = ces.stream()
.filter(ce -> ce instanceof RuntimeVisibleAnnotationsAttribute) .filter(ce -> ce instanceof RuntimeVisibleAnnotationsAttribute)

View File

@ -53,7 +53,7 @@ class ArrayTest {
@Test @Test
void testArrayNew() throws Exception { void testArrayNew() throws Exception {
ClassModel cm = Classfile.parse(testClassPath); ClassModel cm = Classfile.of().parse(testClassPath);
for (MethodModel mm : cm.methods()) { for (MethodModel mm : cm.methods()) {
mm.code().ifPresent(code -> { mm.code().ifPresent(code -> {

View File

@ -60,8 +60,9 @@ public class BSMTest {
@Test @Test
void testSevenOfThirteenIterator() throws Exception { void testSevenOfThirteenIterator() throws Exception {
ClassModel cm = Classfile.parse(testClassPath); var cc = Classfile.of();
byte[] newBytes = cm.transform((cb, ce) -> { ClassModel cm = cc.parse(testClassPath);
byte[] newBytes = cc.transform(cm, (cb, ce) -> {
if (ce instanceof MethodModel mm) { if (ce instanceof MethodModel mm) {
cb.transformMethod(mm, (mb, me) -> { cb.transformMethod(mm, (mb, me) -> {
if (me instanceof CodeModel xm) { if (me instanceof CodeModel xm) {

View File

@ -57,8 +57,9 @@ class BasicBlockTest {
@Test @Test
void testPatternsCausingBasicBlockTroubles() throws IOException { void testPatternsCausingBasicBlockTroubles() throws IOException {
try (InputStream in = BasicBlockTest.class.getResourceAsStream("BasicBlockTest.class")) { try (InputStream in = BasicBlockTest.class.getResourceAsStream("BasicBlockTest.class")) {
var classModel = Classfile.parse(in.readAllBytes()); var cc = Classfile.of();
Classfile.build(classModel.thisClass().asSymbol(), cb -> classModel.forEachElement(cb)); var classModel = cc.parse(in.readAllBytes());
cc.build(classModel.thisClass().asSymbol(), cb -> classModel.forEachElement(cb));
} }
} }
} }

View File

@ -51,16 +51,17 @@ class BoundAttributeTest {
@Test @Test
void testReadMethodParametersAttributeWithoutParameterName() { void testReadMethodParametersAttributeWithoutParameterName() {
var cc = Classfile.of();
// build a simple method: void method(int) // build a simple method: void method(int)
MethodTypeDesc methodTypeDesc = MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_int); MethodTypeDesc methodTypeDesc = MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_int);
byte[] raw = Classfile.build(ClassDesc.of("TestClass"), builder -> { byte[] raw = cc.build(ClassDesc.of("TestClass"), builder -> {
builder.withMethod("method", methodTypeDesc, 0, mb -> { builder.withMethod("method", methodTypeDesc, 0, mb -> {
mb.withCode(CodeBuilder::return_); mb.withCode(CodeBuilder::return_);
// add a MethodParameters attribute without name for the parameter // add a MethodParameters attribute without name for the parameter
mb.with(MethodParametersAttribute.of(MethodParameterInfo.ofParameter(Optional.empty(), 0))); mb.with(MethodParametersAttribute.of(MethodParameterInfo.ofParameter(Optional.empty(), 0)));
}); });
}); });
ClassModel model = Classfile.parse(raw); ClassModel model = cc.parse(raw);
MethodParametersAttribute methodParametersAttribute = model.methods().get(0) MethodParametersAttribute methodParametersAttribute = model.methods().get(0)
.findAttribute(Attributes.METHOD_PARAMETERS) .findAttribute(Attributes.METHOD_PARAMETERS)
.orElseThrow(() -> new AssertionFailedError("Attribute not present")); .orElseThrow(() -> new AssertionFailedError("Attribute not present"));

View File

@ -61,7 +61,7 @@ class BuilderBlockTest {
// Ensure that start=0 at top level, end is undefined until code is done, then end=1 // Ensure that start=0 at top level, end is undefined until code is done, then end=1
Label startEnd[] = new Label[2]; Label startEnd[] = new Label[2];
byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { byte[] bytes = Classfile.of().build(ClassDesc.of("Foo"), cb -> {
cb.withMethod("foo", MethodTypeDesc.of(CD_void), 0, cb.withMethod("foo", MethodTypeDesc.of(CD_void), 0,
mb -> mb.withCode(xb -> { mb -> mb.withCode(xb -> {
startEnd[0] = xb.startLabel(); startEnd[0] = xb.startLabel();
@ -80,7 +80,7 @@ class BuilderBlockTest {
void testStartEndBlock() throws Exception { void testStartEndBlock() throws Exception {
Label startEnd[] = new Label[4]; Label startEnd[] = new Label[4];
byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { byte[] bytes = Classfile.of().build(ClassDesc.of("Foo"), cb -> {
cb.withMethod("foo", MethodTypeDesc.of(CD_void), 0, cb.withMethod("foo", MethodTypeDesc.of(CD_void), 0,
mb -> mb.withCode(xb -> { mb -> mb.withCode(xb -> {
startEnd[0] = xb.startLabel(); startEnd[0] = xb.startLabel();
@ -103,7 +103,7 @@ class BuilderBlockTest {
@Test @Test
void testIfThenReturn() throws Exception { void testIfThenReturn() throws Exception {
byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { byte[] bytes = Classfile.of().build(ClassDesc.of("Foo"), cb -> {
cb.withFlags(AccessFlag.PUBLIC); cb.withFlags(AccessFlag.PUBLIC);
cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int),
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(),
@ -122,7 +122,7 @@ class BuilderBlockTest {
@Test @Test
void testIfThenElseReturn() throws Exception { void testIfThenElseReturn() throws Exception {
byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { byte[] bytes = Classfile.of().build(ClassDesc.of("Foo"), cb -> {
cb.withFlags(AccessFlag.PUBLIC); cb.withFlags(AccessFlag.PUBLIC);
cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int),
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(),
@ -140,7 +140,7 @@ class BuilderBlockTest {
@Test @Test
void testIfThenBadOpcode() { void testIfThenBadOpcode() {
Classfile.build(ClassDesc.of("Foo"), cb -> { Classfile.of().build(ClassDesc.of("Foo"), cb -> {
cb.withFlags(AccessFlag.PUBLIC); cb.withFlags(AccessFlag.PUBLIC);
cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int), cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int),
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(),
@ -160,7 +160,7 @@ class BuilderBlockTest {
@Test @Test
void testIfThenElseImplicitBreak() throws Exception { void testIfThenElseImplicitBreak() throws Exception {
byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { byte[] bytes = Classfile.of().build(ClassDesc.of("Foo"), cb -> {
cb.withFlags(AccessFlag.PUBLIC); cb.withFlags(AccessFlag.PUBLIC);
cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int),
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(),
@ -180,7 +180,7 @@ class BuilderBlockTest {
@Test @Test
void testIfThenElseExplicitBreak() throws Exception { void testIfThenElseExplicitBreak() throws Exception {
byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { byte[] bytes = Classfile.of().build(ClassDesc.of("Foo"), cb -> {
cb.withFlags(AccessFlag.PUBLIC); cb.withFlags(AccessFlag.PUBLIC);
cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int),
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(),
@ -199,7 +199,7 @@ class BuilderBlockTest {
@Test @Test
void testIfThenElseOpcode() throws Exception { void testIfThenElseOpcode() throws Exception {
byte[] bytes = Classfile.build(ClassDesc.of("Foo"), cb -> { byte[] bytes = Classfile.of().build(ClassDesc.of("Foo"), cb -> {
cb.withFlags(AccessFlag.PUBLIC); cb.withFlags(AccessFlag.PUBLIC);
cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int), cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int),
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(),
@ -224,7 +224,7 @@ class BuilderBlockTest {
@Test @Test
void testIfThenElseBadOpcode() { void testIfThenElseBadOpcode() {
Classfile.build(ClassDesc.of("Foo"), cb -> { Classfile.of().build(ClassDesc.of("Foo"), cb -> {
cb.withFlags(AccessFlag.PUBLIC); cb.withFlags(AccessFlag.PUBLIC);
cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int), cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int),
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(),
@ -245,7 +245,7 @@ class BuilderBlockTest {
@Test @Test
void testAllocateLocal() { void testAllocateLocal() {
Classfile.build(ClassDesc.of("Foo"), cb -> { Classfile.of().build(ClassDesc.of("Foo"), cb -> {
cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), Classfile.ACC_STATIC, cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), Classfile.ACC_STATIC,
mb -> mb.withCode(xb -> { mb -> mb.withCode(xb -> {
int slot1 = xb.allocateLocal(TypeKind.IntType); int slot1 = xb.allocateLocal(TypeKind.IntType);
@ -262,7 +262,7 @@ class BuilderBlockTest {
@Test @Test
void testAllocateLocalBlock() { void testAllocateLocalBlock() {
Classfile.build(ClassDesc.of("Foo"), cb -> { Classfile.of().build(ClassDesc.of("Foo"), cb -> {
cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), Classfile.ACC_STATIC, cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), Classfile.ACC_STATIC,
mb -> mb.withCode(xb -> { mb -> mb.withCode(xb -> {
xb.block(bb -> { xb.block(bb -> {
@ -283,7 +283,7 @@ class BuilderBlockTest {
@Test @Test
void testAllocateLocalIfThen() { void testAllocateLocalIfThen() {
Classfile.build(ClassDesc.of("Foo"), cb -> { Classfile.of().build(ClassDesc.of("Foo"), cb -> {
cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), Classfile.ACC_STATIC, cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), Classfile.ACC_STATIC,
mb -> mb.withCode(xb -> { mb -> mb.withCode(xb -> {
xb.iconst_0(); xb.iconst_0();

View File

@ -44,8 +44,8 @@ import static org.junit.jupiter.api.Assertions.*;
class BuilderParamTest { class BuilderParamTest {
@Test @Test
void testDirectBuilder() { void testDirectBuilder() {
var cc = Classfile.of();
Classfile.build(ClassDesc.of("Foo"), cb -> { cc.build(ClassDesc.of("Foo"), cb -> {
cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), 0, cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), 0,
mb -> mb.withCode(xb -> { mb -> mb.withCode(xb -> {
assertEquals(xb.receiverSlot(), 0); assertEquals(xb.receiverSlot(), 0);
@ -55,8 +55,7 @@ class BuilderParamTest {
xb.return_(); xb.return_();
})); }));
}); });
cc.build(ClassDesc.of("Foo"), cb -> {
Classfile.build(ClassDesc.of("Foo"), cb -> {
cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), ACC_STATIC, cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), ACC_STATIC,
mb -> mb.withCode(xb -> { mb -> mb.withCode(xb -> {
assertEquals(xb.parameterSlot(0), 0); assertEquals(xb.parameterSlot(0), 0);

View File

@ -171,7 +171,7 @@ class BuilderTryCatchTest {
void testTryEmptyCatch() { void testTryEmptyCatch() {
byte[] bytes = generateTryCatchMethod(catchBuilder -> {}); byte[] bytes = generateTryCatchMethod(catchBuilder -> {});
boolean anyGotos = Classfile.parse(bytes).methods().stream() boolean anyGotos = Classfile.of().parse(bytes).methods().stream()
.flatMap(mm -> mm.code().stream()) .flatMap(mm -> mm.code().stream())
.flatMap(CompoundElement::elementStream) .flatMap(CompoundElement::elementStream)
.anyMatch(codeElement -> .anyMatch(codeElement ->
@ -182,7 +182,7 @@ class BuilderTryCatchTest {
@Test @Test
void testEmptyTry() { void testEmptyTry() {
byte[] bytes = Classfile.build(ClassDesc.of("C"), cb -> { byte[] bytes = Classfile.of().build(ClassDesc.of("C"), cb -> {
cb.withMethod("main", MethodTypeDesc.of(CD_String, CD_String.arrayType()), cb.withMethod("main", MethodTypeDesc.of(CD_String, CD_String.arrayType()),
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> { AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> {
mb.withCode(xb -> { mb.withCode(xb -> {
@ -213,7 +213,7 @@ class BuilderTryCatchTest {
@Test @Test
void testLocalAllocation() throws Throwable { void testLocalAllocation() throws Throwable {
byte[] bytes = Classfile.build(ClassDesc.of("C"), cb -> { byte[] bytes = Classfile.of().build(ClassDesc.of("C"), cb -> {
cb.withMethod("main", MethodTypeDesc.of(CD_String, CD_String.arrayType()), cb.withMethod("main", MethodTypeDesc.of(CD_String, CD_String.arrayType()),
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> { AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> {
mb.withCode(xb -> { mb.withCode(xb -> {
@ -276,7 +276,7 @@ class BuilderTryCatchTest {
} }
static byte[] generateTryCatchMethod(Consumer<CodeBuilder.CatchBuilder> c) { static byte[] generateTryCatchMethod(Consumer<CodeBuilder.CatchBuilder> c) {
byte[] bytes = Classfile.build(ClassDesc.of("C"), cb -> { byte[] bytes = Classfile.of().build(ClassDesc.of("C"), cb -> {
cb.withMethod("main", MethodTypeDesc.of(CD_String, CD_String.arrayType()), cb.withMethod("main", MethodTypeDesc.of(CD_String, CD_String.arrayType()),
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> { AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> {
mb.withCode(xb -> { mb.withCode(xb -> {

View File

@ -49,9 +49,10 @@ import java.util.Objects;
public class ClassBuildingTest { public class ClassBuildingTest {
@Test @Test
public void test() throws Throwable { public void test() throws Throwable {
var cc = Classfile.of();
ClassModel cm; ClassModel cm;
try (var in = ClassBuildingTest.class.getResourceAsStream("/Outer$1Local.class")) { try (var in = ClassBuildingTest.class.getResourceAsStream("/Outer$1Local.class")) {
cm = Classfile.parse(Objects.requireNonNull(in).readAllBytes()); cm = cc.parse(Objects.requireNonNull(in).readAllBytes());
} }
ClassTransform transform = ClassRemapper.of(Map.of(ClassDesc.of("Outer"), ClassDesc.of("Router"))); ClassTransform transform = ClassRemapper.of(Map.of(ClassDesc.of("Outer"), ClassDesc.of("Router")));
@ -60,7 +61,7 @@ public class ClassBuildingTest {
transform = transform.andThen(ClassTransform.transformingMethods(MethodTransform.dropping(me transform = transform.andThen(ClassTransform.transformingMethods(MethodTransform.dropping(me
-> me instanceof SignatureAttribute))); -> me instanceof SignatureAttribute)));
MethodHandles.lookup().defineClass(cm.transform(transform)); MethodHandles.lookup().defineClass(cc.transform(cm, transform));
} }
} }

View File

@ -128,8 +128,8 @@ class ClassHierarchyInfoTest {
void transformAndVerifySingle(ClassHierarchyResolver res) throws Exception { void transformAndVerifySingle(ClassHierarchyResolver res) throws Exception {
Path path = FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java/util/HashMap.class"); Path path = FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java/util/HashMap.class");
var classModel = Classfile.parse(path, Classfile.Option.classHierarchyResolver(res)); var classModel = Classfile.of().parse(path);
byte[] newBytes = classModel.transform( byte[] newBytes = Classfile.of(Classfile.ClassHierarchyResolverOption.of(res)).transform(classModel,
(clb, cle) -> { (clb, cle) -> {
if (cle instanceof MethodModel mm) { if (cle instanceof MethodModel mm) {
clb.transformMethod(mm, (mb, me) -> { clb.transformMethod(mm, (mb, me) -> {
@ -143,7 +143,7 @@ class ClassHierarchyInfoTest {
else else
clb.with(cle); clb.with(cle);
}); });
var errors = Classfile.parse(newBytes).verify(null); var errors = Classfile.of().parse(newBytes).verify(null);
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
var itr = errors.iterator(); var itr = errors.iterator();
var thrown = itr.next(); var thrown = itr.next();

View File

@ -43,7 +43,8 @@ import static org.junit.jupiter.api.Assertions.*;
class ClassPrinterTest { class ClassPrinterTest {
ClassModel getClassModel() { ClassModel getClassModel() {
return Classfile.parse(Classfile.build(ClassDesc.of("Foo"), clb -> var cc = Classfile.of();
return cc.parse(cc.build(ClassDesc.of("Foo"), clb ->
clb.withVersion(61, 0) clb.withVersion(61, 0)
.withFlags(Classfile.ACC_PUBLIC) .withFlags(Classfile.ACC_PUBLIC)
.with(SourceFileAttribute.of("Foo.java")) .with(SourceFileAttribute.of("Foo.java"))

View File

@ -71,13 +71,14 @@ import static org.junit.jupiter.api.Assertions.*;
class ConstantPoolCopyTest { class ConstantPoolCopyTest {
private static ClassModel[] rtJarToClassLow(FileSystem fs) { private static ClassModel[] rtJarToClassLow(FileSystem fs) {
try { try {
var cc = Classfile.of();
var modules = Stream.of( var modules = Stream.of(
Files.walk(fs.getPath("modules/java.base/java")), Files.walk(fs.getPath("modules/java.base/java")),
Files.walk(fs.getPath("modules"), 2).filter(p -> p.endsWith("module-info.class"))) Files.walk(fs.getPath("modules"), 2).filter(p -> p.endsWith("module-info.class")))
.flatMap(p -> p) .flatMap(p -> p)
.filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".class")) .filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".class"))
.map(ConstantPoolCopyTest::readAllBytes) .map(ConstantPoolCopyTest::readAllBytes)
.map(bytes -> Classfile.parse(bytes)) .map(bytes -> cc.parse(bytes))
.toArray(ClassModel[]::new); .toArray(ClassModel[]::new);
return modules; return modules;
} catch (IOException ioe) { } catch (IOException ioe) {

View File

@ -79,7 +79,8 @@ class CorpusTest {
static void splitTableAttributes(String sourceClassFile, String targetClassFile) throws IOException, URISyntaxException { static void splitTableAttributes(String sourceClassFile, String targetClassFile) throws IOException, URISyntaxException {
var root = Paths.get(URI.create(CorpusTest.class.getResource("CorpusTest.class").toString())).getParent(); var root = Paths.get(URI.create(CorpusTest.class.getResource("CorpusTest.class").toString())).getParent();
Files.write(root.resolve(targetClassFile), Classfile.parse(root.resolve(sourceClassFile)).transform(ClassTransform.transformingMethodBodies((cob, coe) -> { var cc = Classfile.of();
Files.write(root.resolve(targetClassFile), cc.transform(cc.parse(root.resolve(sourceClassFile)), ClassTransform.transformingMethodBodies((cob, coe) -> {
var dcob = (DirectCodeBuilder)cob; var dcob = (DirectCodeBuilder)cob;
var curPc = dcob.curPc(); var curPc = dcob.curPc();
switch (coe) { switch (coe) {
@ -145,8 +146,8 @@ class CorpusTest {
try { try {
byte[] transformed = m.shared && m.classTransform != null byte[] transformed = m.shared && m.classTransform != null
? Classfile.parse(bytes, Classfile.Option.generateStackmap(false)) ? Classfile.of(Classfile.StackMapsOption.DROP_STACK_MAPS)
.transform(m.classTransform) .transform(Classfile.of().parse(bytes), m.classTransform)
: m.transform.apply(bytes); : m.transform.apply(bytes);
Map<Integer, Integer> newDups = findDups(transformed); Map<Integer, Integer> newDups = findDups(transformed);
oldRecord = m.classRecord(bytes); oldRecord = m.classRecord(bytes);
@ -196,15 +197,15 @@ class CorpusTest {
@MethodSource("corpus") @MethodSource("corpus")
void testReadAndTransform(Path path) throws IOException { void testReadAndTransform(Path path) throws IOException {
byte[] bytes = Files.readAllBytes(path); byte[] bytes = Files.readAllBytes(path);
var cc = Classfile.of();
var classModel = Classfile.parse(bytes); var classModel = cc.parse(bytes);
assertEqualsDeep(ClassRecord.ofClassModel(classModel), ClassRecord.ofStreamingElements(classModel), assertEqualsDeep(ClassRecord.ofClassModel(classModel), ClassRecord.ofStreamingElements(classModel),
"ClassModel (actual) vs StreamingElements (expected)"); "ClassModel (actual) vs StreamingElements (expected)");
byte[] newBytes = Classfile.build( byte[] newBytes = cc.build(
classModel.thisClass().asSymbol(), classModel.thisClass().asSymbol(),
classModel::forEachElement); classModel::forEachElement);
var newModel = Classfile.parse(newBytes, Classfile.Option.generateStackmap(false)); var newModel = cc.parse(newBytes);
assertEqualsDeep(ClassRecord.ofClassModel(newModel, CompatibilityFilter.By_ClassBuilder), assertEqualsDeep(ClassRecord.ofClassModel(newModel, CompatibilityFilter.By_ClassBuilder),
ClassRecord.ofClassModel(classModel, CompatibilityFilter.By_ClassBuilder), ClassRecord.ofClassModel(classModel, CompatibilityFilter.By_ClassBuilder),
"ClassModel[%s] transformed by ClassBuilder (actual) vs ClassModel before transformation (expected)".formatted(path)); "ClassModel[%s] transformed by ClassBuilder (actual) vs ClassModel before transformation (expected)".formatted(path));
@ -212,8 +213,10 @@ class CorpusTest {
assertEmpty(newModel.verify(null)); assertEmpty(newModel.verify(null));
//testing maxStack and maxLocals are calculated identically by StackMapGenerator and StackCounter //testing maxStack and maxLocals are calculated identically by StackMapGenerator and StackCounter
byte[] noStackMaps = newModel.transform(ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL)); byte[] noStackMaps = Classfile.of(Classfile.StackMapsOption.DROP_STACK_MAPS)
var noStackModel = Classfile.parse(noStackMaps); .transform(newModel,
ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL));
var noStackModel = cc.parse(noStackMaps);
var itStack = newModel.methods().iterator(); var itStack = newModel.methods().iterator();
var itNoStack = noStackModel.methods().iterator(); var itNoStack = noStackModel.methods().iterator();
while (itStack.hasNext()) { while (itStack.hasNext()) {
@ -243,8 +246,9 @@ class CorpusTest {
// } // }
private void compareCp(byte[] orig, byte[] transformed) { private void compareCp(byte[] orig, byte[] transformed) {
var cp1 = Classfile.parse(orig).constantPool(); var cc = Classfile.of();
var cp2 = Classfile.parse(transformed).constantPool(); var cp1 = cc.parse(orig).constantPool();
var cp2 = cc.parse(transformed).constantPool();
for (int i = 1; i < cp1.entryCount(); i += cp1.entryByIndex(i).width()) { for (int i = 1; i < cp1.entryCount(); i += cp1.entryByIndex(i).width()) {
assertEquals(cpiToString(cp1.entryByIndex(i)), cpiToString(cp2.entryByIndex(i))); assertEquals(cpiToString(cp1.entryByIndex(i)), cpiToString(cp2.entryByIndex(i)));
@ -267,7 +271,7 @@ class CorpusTest {
private static Map<Integer, Integer> findDups(byte[] bytes) { private static Map<Integer, Integer> findDups(byte[] bytes) {
Map<Integer, Integer> dups = new HashMap<>(); Map<Integer, Integer> dups = new HashMap<>();
var cf = Classfile.parse(bytes); var cf = Classfile.of().parse(bytes);
var pool = cf.constantPool(); var pool = cf.constantPool();
Set<String> entryStrings = new HashSet<>(); Set<String> entryStrings = new HashSet<>();
for (int i = 1; i < pool.entryCount(); i += pool.entryByIndex(i).width()) { for (int i = 1; i < pool.entryCount(); i += pool.entryByIndex(i).width()) {

View File

@ -47,7 +47,8 @@ class DiscontinuedInstructionsTest {
var testClass = "JsrAndRetSample"; var testClass = "JsrAndRetSample";
var testMethod = "testMethod"; var testMethod = "testMethod";
var cd_list = ArrayList.class.describeConstable().get(); var cd_list = ArrayList.class.describeConstable().get();
var bytes = Classfile.build(ClassDesc.of(testClass), clb -> clb var cc = Classfile.of();
var bytes = cc.build(ClassDesc.of(testClass), clb -> clb
.withVersion(JAVA_5_VERSION, 0) .withVersion(JAVA_5_VERSION, 0)
.withMethodBody(testMethod, MethodTypeDesc.of(CD_void, cd_list), ACC_PUBLIC | ACC_STATIC, cob -> cob .withMethodBody(testMethod, MethodTypeDesc.of(CD_void, cd_list), ACC_PUBLIC | ACC_STATIC, cob -> cob
.block(bb -> { .block(bb -> {
@ -64,7 +65,7 @@ class DiscontinuedInstructionsTest {
.pop() .pop()
.with(DiscontinuedInstruction.RetInstruction.of(355)))); .with(DiscontinuedInstruction.RetInstruction.of(355))));
var c = Classfile.parse(bytes).methods().get(0).code().get(); var c = cc.parse(bytes).methods().get(0).code().get();
assertEquals(356, c.maxLocals()); assertEquals(356, c.maxLocals());
assertEquals(6, c.maxStack()); assertEquals(6, c.maxStack());
@ -75,22 +76,27 @@ class DiscontinuedInstructionsTest {
.invoke(null, list); .invoke(null, list);
assertEquals(list, List.of("Hello", "World")); assertEquals(list, List.of("Hello", "World"));
bytes = Classfile.parse(bytes).transform(ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL)); bytes = cc.transform(cc.parse(bytes), ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL));
new ByteArrayClassLoader(DiscontinuedInstructionsTest.class.getClassLoader(), testClass, bytes) new ByteArrayClassLoader(DiscontinuedInstructionsTest.class.getClassLoader(), testClass, bytes)
.getMethod(testClass, testMethod) .getMethod(testClass, testMethod)
.invoke(null, list); .invoke(null, list);
assertEquals(list, List.of("Hello", "World", "Hello", "World")); assertEquals(list, List.of("Hello", "World", "Hello", "World"));
var clm = Classfile.parse(bytes); var clm = cc.parse(bytes);
//test failover stack map generation //test failover stack map generation
clm.transform(ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL) cc.transform(clm, ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL)
.andThen(ClassTransform.endHandler(clb -> clb.withVersion(JAVA_6_VERSION, 0)))); .andThen(ClassTransform.endHandler(clb -> clb.withVersion(JAVA_6_VERSION, 0))));
//test failure of stack map generation //test failure of stack map generation for Java 7
assertThrows(IllegalArgumentException.class, () -> assertThrows(IllegalArgumentException.class, () ->
clm.transform(ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL) cc.transform(clm, ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL)
.andThen(ClassTransform.endHandler(clb -> clb.withVersion(JAVA_7_VERSION, 0))))); .andThen(ClassTransform.endHandler(clb -> clb.withVersion(JAVA_7_VERSION, 0)))));
//test failure of stack map generation when enforced to generate
assertThrows(IllegalArgumentException.class, () ->
Classfile.of(Classfile.StackMapsOption.GENERATE_STACK_MAPS)
.transform(clm, ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL)));
} }
} }

View File

@ -60,7 +60,8 @@ class FilterDeadLabelsTest {
@Test @Test
void testFilterDeadLabels() { void testFilterDeadLabels() {
var code = Classfile.parse(Classfile.build(ClassDesc.of("cls"), List.of(Classfile.Option.filterDeadLabels(true)), clb -> var cc = Classfile.of(Classfile.DeadLabelsOption.DROP_DEAD_LABELS);
var code = cc.parse(cc.build(ClassDesc.of("cls"), clb ->
clb.withMethodBody("m", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> { clb.withMethodBody("m", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> {
cob.return_(); cob.return_();
deadLabelFragments().forEach(f -> f.accept(cob)); deadLabelFragments().forEach(f -> f.accept(cob));
@ -75,7 +76,7 @@ class FilterDeadLabelsTest {
@ParameterizedTest @ParameterizedTest
@MethodSource("deadLabelFragments") @MethodSource("deadLabelFragments")
void testThrowOnDeadLabels(Consumer<CodeBuilder> fragment) { void testThrowOnDeadLabels(Consumer<CodeBuilder> fragment) {
assertThrows(IllegalArgumentException.class, () -> Classfile.build(ClassDesc.of("cls"), clb -> assertThrows(IllegalArgumentException.class, () -> Classfile.of().build(ClassDesc.of("cls"), clb ->
clb.withMethodBody("m", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> { clb.withMethodBody("m", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> {
cob.return_(); cob.return_();
fragment.accept(cob); fragment.accept(cob);

View File

@ -46,7 +46,8 @@ import jdk.internal.classfile.instruction.ConstantInstruction;
class LDCTest { class LDCTest {
@Test @Test
void testLDCisConvertedToLDCW() throws Exception { void testLDCisConvertedToLDCW() throws Exception {
byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { var cc = Classfile.of();
byte[] bytes = cc.build(ClassDesc.of("MyClass"), cb -> {
cb.withFlags(AccessFlag.PUBLIC); cb.withFlags(AccessFlag.PUBLIC);
cb.withVersion(52, 0); cb.withVersion(52, 0);
cb.withMethod("<init>", MethodTypeDesc.of(CD_void), 0, mb -> mb cb.withMethod("<init>", MethodTypeDesc.of(CD_void), 0, mb -> mb
@ -75,7 +76,7 @@ class LDCTest {
})); }));
}); });
var model = Classfile.parse(bytes); var model = cc.parse(bytes);
var code = model.elementStream() var code = model.elementStream()
.filter(e -> e instanceof MethodModel) .filter(e -> e instanceof MethodModel)
.map(e -> (MethodModel) e) .map(e -> (MethodModel) e)

View File

@ -39,7 +39,7 @@ class LimitsTest {
@Test @Test
void testCPSizeLimit() { void testCPSizeLimit() {
Classfile.build(ClassDesc.of("BigClass"), cb -> { Classfile.of().build(ClassDesc.of("BigClass"), cb -> {
for (int i = 1; i < 65000; i++) { for (int i = 1; i < 65000; i++) {
cb.withField("field" + i, ConstantDescs.CD_int, fb -> {}); cb.withField("field" + i, ConstantDescs.CD_int, fb -> {});
} }
@ -48,7 +48,7 @@ class LimitsTest {
@Test @Test
void testCPOverLimit() { void testCPOverLimit() {
assertThrows(IllegalArgumentException.class, () -> Classfile.build(ClassDesc.of("BigClass"), cb -> { assertThrows(IllegalArgumentException.class, () -> Classfile.of().build(ClassDesc.of("BigClass"), cb -> {
for (int i = 1; i < 66000; i++) { for (int i = 1; i < 66000; i++) {
cb.withField("field" + i, ConstantDescs.CD_int, fb -> {}); cb.withField("field" + i, ConstantDescs.CD_int, fb -> {});
} }
@ -57,7 +57,7 @@ class LimitsTest {
@Test @Test
void testCodeOverLimit() { void testCodeOverLimit() {
assertThrows(IllegalArgumentException.class, () -> Classfile.build(ClassDesc.of("BigClass"), cb -> cb.withMethodBody( assertThrows(IllegalArgumentException.class, () -> Classfile.of().build(ClassDesc.of("BigClass"), cb -> cb.withMethodBody(
"bigMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> { "bigMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> {
for (int i = 0; i < 65535; i++) { for (int i = 0; i < 65535; i++) {
cob.nop(); cob.nop();
@ -68,7 +68,7 @@ class LimitsTest {
@Test @Test
void testEmptyCode() { void testEmptyCode() {
assertThrows(IllegalArgumentException.class, () -> Classfile.build(ClassDesc.of("EmptyClass"), cb -> cb.withMethodBody( assertThrows(IllegalArgumentException.class, () -> Classfile.of().build(ClassDesc.of("EmptyClass"), cb -> cb.withMethodBody(
"emptyMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> {}))); "emptyMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> {})));
} }
} }

View File

@ -56,7 +56,8 @@ class LowAdaptTest {
@Test @Test
void testAdapt() throws Exception { void testAdapt() throws Exception {
ClassModel cl = Classfile.parse(Paths.get(URI.create(LowAdaptTest.class.getResource(test + ".class").toString()))); var cc = Classfile.of();
ClassModel cl = cc.parse(Paths.get(URI.create(LowAdaptTest.class.getResource(test + ".class").toString())));
DirectMethodHandleDesc bsm = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.STATIC, DirectMethodHandleDesc bsm = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.STATIC,
ClassDesc.of("java.lang.invoke.LambdaMetafactory"), ClassDesc.of("java.lang.invoke.LambdaMetafactory"),
@ -73,7 +74,7 @@ class LowAdaptTest {
MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC, ClassDesc.of(test), "fib", "(I)I"), MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC, ClassDesc.of(test), "fib", "(I)I"),
MethodTypeDesc.ofDescriptor("(I)I")); MethodTypeDesc.ofDescriptor("(I)I"));
byte[] clazz = Classfile.build(ClassDesc.of(test), cb -> { byte[] clazz = cc.build(ClassDesc.of(test), cb -> {
cb.withFlags(AccessFlag.PUBLIC); cb.withFlags(AccessFlag.PUBLIC);
cb.with(SourceFileAttribute.of("/some/madeup/TestClass.java")); cb.with(SourceFileAttribute.of("/some/madeup/TestClass.java"));
cl.methods().forEach(m -> ((DirectClassBuilder) cb).withMethod(m)); cl.methods().forEach(m -> ((DirectClassBuilder) cb).withMethod(m));

View File

@ -61,7 +61,7 @@ class LowJCovAttributeTest {
LowJCovAttributeTest() throws IOException { LowJCovAttributeTest() throws IOException {
this.path = Paths.get(URI.create(LowJCovAttributeTest.class.getResource(TEST_FILE).toString())); this.path = Paths.get(URI.create(LowJCovAttributeTest.class.getResource(TEST_FILE).toString()));
this.classLow = Classfile.parse(path); this.classLow = Classfile.of().parse(path);
} }
@Test @Test

View File

@ -68,7 +68,7 @@ class LowModuleTest {
void testRead(Path path, TestInfo test) throws Exception { void testRead(Path path, TestInfo test) throws Exception {
try { try {
printf("%nCHECK %s%n", test.getDisplayName()); printf("%nCHECK %s%n", test.getDisplayName());
ClassModel classLow = Classfile.parse(path); ClassModel classLow = Classfile.of().parse(path);
testRead0(classLow); testRead0(classLow);
} catch(Exception ex) { } catch(Exception ex) {
System.err.printf("%nFAIL %s - %s%n", path, ex); System.err.printf("%nFAIL %s - %s%n", path, ex);

View File

@ -81,7 +81,7 @@ class LvtTest {
@Test @Test
void getLVTEntries() { void getLVTEntries() {
ClassModel c = Classfile.parse(fileBytes); ClassModel c = Classfile.of().parse(fileBytes);
CodeModel co = c.methods().stream() CodeModel co = c.methods().stream()
.filter(mm -> mm.methodName().stringValue().equals("m")) .filter(mm -> mm.methodName().stringValue().equals("m"))
.map(MethodModel::code) .map(MethodModel::code)
@ -106,18 +106,20 @@ class LvtTest {
@Test @Test
void buildLVTEntries() throws Exception { void buildLVTEntries() throws Exception {
ClassModel c = Classfile.parse(fileBytes); var cc = Classfile.of();
ClassModel c = cc.parse(fileBytes);
// Compare transformed model and original with CodeBuilder filter // Compare transformed model and original with CodeBuilder filter
byte[] newClass = c.transform(Transforms.threeLevelNoop); byte[] newClass = cc.transform(c, Transforms.threeLevelNoop);
ClassRecord orig = ClassRecord.ofClassModel(Classfile.parse(fileBytes), ClassRecord.CompatibilityFilter.By_ClassBuilder); ClassRecord orig = ClassRecord.ofClassModel(cc.parse(fileBytes), ClassRecord.CompatibilityFilter.By_ClassBuilder);
ClassRecord transformed = ClassRecord.ofClassModel(Classfile.parse(newClass), ClassRecord.CompatibilityFilter.By_ClassBuilder); ClassRecord transformed = ClassRecord.ofClassModel(cc.parse(newClass), ClassRecord.CompatibilityFilter.By_ClassBuilder);
ClassRecord.assertEqualsDeep(transformed, orig); ClassRecord.assertEqualsDeep(transformed, orig);
} }
@Test @Test
void testCreateLoadLVT() throws Exception { void testCreateLoadLVT() throws Exception {
byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { var cc = Classfile.of();
byte[] bytes = cc.build(ClassDesc.of("MyClass"), cb -> {
cb.withFlags(AccessFlag.PUBLIC); cb.withFlags(AccessFlag.PUBLIC);
cb.withVersion(52, 0); cb.withVersion(52, 0);
cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java")))) cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java"))))
@ -172,7 +174,7 @@ class LvtTest {
})); }));
}); });
var c = Classfile.parse(bytes); var c = cc.parse(bytes);
var main = c.methods().get(1); var main = c.methods().get(1);
var lvt = main.code().get().findAttribute(Attributes.LOCAL_VARIABLE_TABLE).get(); var lvt = main.code().get().findAttribute(Attributes.LOCAL_VARIABLE_TABLE).get();
var lvs = lvt.localVariables(); var lvs = lvt.localVariables();
@ -189,7 +191,7 @@ class LvtTest {
@Test @Test
void getLVTTEntries() { void getLVTTEntries() {
ClassModel c = Classfile.parse(fileBytes); ClassModel c = Classfile.of().parse(fileBytes);
CodeModel co = c.methods().stream() CodeModel co = c.methods().stream()
.filter(mm -> mm.methodName().stringValue().equals("n")) .filter(mm -> mm.methodName().stringValue().equals("n"))
.map(MethodModel::code) .map(MethodModel::code)
@ -229,7 +231,8 @@ class LvtTest {
@Test @Test
void testCreateLoadLVTT() throws Exception { void testCreateLoadLVTT() throws Exception {
byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { var cc = Classfile.of();
byte[] bytes = cc.build(ClassDesc.of("MyClass"), cb -> {
cb.withFlags(AccessFlag.PUBLIC); cb.withFlags(AccessFlag.PUBLIC);
cb.withVersion(52, 0); cb.withVersion(52, 0);
cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java")))) cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java"))))
@ -275,7 +278,7 @@ class LvtTest {
.localVariable(1, u, jlObject, start, end); .localVariable(1, u, jlObject, start, end);
})); }));
}); });
var c = Classfile.parse(bytes); var c = cc.parse(bytes);
var main = c.methods().get(1); var main = c.methods().get(1);
var lvtt = main.code().get().findAttribute(Attributes.LOCAL_VARIABLE_TYPE_TABLE).get(); var lvtt = main.code().get().findAttribute(Attributes.LOCAL_VARIABLE_TYPE_TABLE).get();
var lvts = lvtt.localVariableTypes(); var lvts = lvtt.localVariableTypes();
@ -301,7 +304,7 @@ class LvtTest {
@Test @Test
void skipDebugSkipsLVT() { void skipDebugSkipsLVT() {
ClassModel c = Classfile.parse(fileBytes, Classfile.Option.processDebug(false)); ClassModel c = Classfile.of(Classfile.DebugElementsOption.DROP_DEBUG).parse(fileBytes);
c.forEachElement(e -> { c.forEachElement(e -> {
if (e instanceof MethodModel m) { if (e instanceof MethodModel m) {

View File

@ -76,13 +76,13 @@ class MassAdaptCopyCodeTest {
} }
void copy(String name, byte[] bytes) throws Exception { void copy(String name, byte[] bytes) throws Exception {
byte[] newBytes = adaptCopy(Classfile.parse(bytes)); byte[] newBytes = adaptCopy(Classfile.of().parse(bytes));
classNameToClass.put(name, new ByteArrayClassLoader.ClassData(name, newBytes)); classNameToClass.put(name, new ByteArrayClassLoader.ClassData(name, newBytes));
if (name.contains("/")) throw new RuntimeException(name); if (name.contains("/")) throw new RuntimeException(name);
} }
public byte[] adaptCopy(ClassModel cm) { public byte[] adaptCopy(ClassModel cm) {
return cm.transform((cb, ce) -> { return Classfile.of().transform(cm, (cb, ce) -> {
if (ce instanceof MethodModel mm) { if (ce instanceof MethodModel mm) {
cb.transformMethod(mm, (mb, me) -> { cb.transformMethod(mm, (mb, me) -> {
if (me instanceof CodeModel xm) { if (me instanceof CodeModel xm) {

View File

@ -96,11 +96,12 @@ class MassAdaptCopyPrimitiveMatchCodeTest {
void copy(String name, byte[] bytes) throws Exception { void copy(String name, byte[] bytes) throws Exception {
//System.err.printf("MassAdaptCopyPrimitiveMatchCodeTest - %s%n", name); //System.err.printf("MassAdaptCopyPrimitiveMatchCodeTest - %s%n", name);
ClassModel cm =(Classfile.parse(bytes)); var cc = Classfile.of();
ClassModel cm =cc.parse(bytes);
Map<String, byte[]> m2b = new HashMap<>(); Map<String, byte[]> m2b = new HashMap<>();
Map<String, CodeAttribute> m2c = new HashMap<>(); Map<String, CodeAttribute> m2c = new HashMap<>();
byte[] resultBytes = byte[] resultBytes =
cm.transform((cb, e) -> { cc.transform(cm, (cb, e) -> {
if (e instanceof MethodModel mm) { if (e instanceof MethodModel mm) {
Optional<CodeModel> code = mm.code(); Optional<CodeModel> code = mm.code();
if (code.isPresent()) { if (code.isPresent()) {
@ -125,7 +126,7 @@ class MassAdaptCopyPrimitiveMatchCodeTest {
System.err.printf("MassAdaptCopyPrimitiveMatchCodeTest: Ignored because it is a record%n - %s%n", name); System.err.printf("MassAdaptCopyPrimitiveMatchCodeTest: Ignored because it is a record%n - %s%n", name);
return; return;
} }
ClassModel rcm = Classfile.parse(resultBytes); ClassModel rcm = cc.parse(resultBytes);
for (MethodModel rmm : rcm.methods()) { for (MethodModel rmm : rcm.methods()) {
Optional<CodeModel> code = rmm.code(); Optional<CodeModel> code = rmm.code();
if (code.isPresent()) { if (code.isPresent()) {

View File

@ -64,7 +64,8 @@ class ModuleBuilderTest {
private final ModuleAttribute attr; private final ModuleAttribute attr;
public ModuleBuilderTest() { public ModuleBuilderTest() {
byte[] modInfo = Classfile.buildModule( var cc = Classfile.of();
byte[] modInfo = cc.buildModule(
ModuleAttribute.of(modName, mb -> mb ModuleAttribute.of(modName, mb -> mb
.moduleVersion(modVsn) .moduleVersion(modVsn)
@ -88,7 +89,7 @@ class ModuleBuilderTest {
clb -> clb.with(ModuleMainClassAttribute.of(ClassDesc.of("main.Class"))) clb -> clb.with(ModuleMainClassAttribute.of(ClassDesc.of("main.Class")))
.with(ModulePackagesAttribute.ofNames(PackageDesc.of("foo.bar.baz"), PackageDesc.of("quux"))) .with(ModulePackagesAttribute.ofNames(PackageDesc.of("foo.bar.baz"), PackageDesc.of("quux")))
.with(ModuleMainClassAttribute.of(ClassDesc.of("overwritten.main.Class")))); .with(ModuleMainClassAttribute.of(ClassDesc.of("overwritten.main.Class"))));
moduleModel = Classfile.parse(modInfo); moduleModel = cc.parse(modInfo);
attr = ((ModuleAttribute) moduleModel.attributes().stream() attr = ((ModuleAttribute) moduleModel.attributes().stream()
.filter(a -> a.attributeMapper() == Attributes.MODULE) .filter(a -> a.attributeMapper() == Attributes.MODULE)
.findFirst() .findFirst()
@ -98,10 +99,11 @@ class ModuleBuilderTest {
@Test @Test
void testCreateModuleInfo() { void testCreateModuleInfo() {
// Build the module-info.class bytes // Build the module-info.class bytes
byte[] modBytes = Classfile.buildModule(ModuleAttribute.of(modName, mb -> mb.moduleVersion(modVsn))); var cc = Classfile.of();
byte[] modBytes = cc.buildModule(ModuleAttribute.of(modName, mb -> mb.moduleVersion(modVsn)));
// Verify // Verify
var cm = Classfile.parse(modBytes); var cm = cc.parse(modBytes);
var attr =cm.findAttribute(Attributes.MODULE).get(); var attr =cm.findAttribute(Attributes.MODULE).get();
assertEquals(attr.moduleName().name().stringValue(), modName.name()); assertEquals(attr.moduleName().name().stringValue(), modName.name());
@ -195,7 +197,7 @@ class ModuleBuilderTest {
void verifyIsModuleInfo() throws Exception { void verifyIsModuleInfo() throws Exception {
assertTrue(moduleModel.isModuleInfo()); assertTrue(moduleModel.isModuleInfo());
ClassModel m = Classfile.parse(Paths.get(URI.create(ModuleBuilderTest.class.getResource("ModuleBuilderTest.class").toString()))); ClassModel m = Classfile.of().parse(Paths.get(URI.create(ModuleBuilderTest.class.getResource("ModuleBuilderTest.class").toString())));
assertFalse(m.isModuleInfo()); assertFalse(m.isModuleInfo());
} }
} }

View File

@ -63,8 +63,8 @@ class OneToOneTest {
@Test @Test
void testClassWriteRead() { void testClassWriteRead() {
var cc = Classfile.of();
byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { byte[] bytes = cc.build(ClassDesc.of("MyClass"), cb -> {
cb.withFlags(AccessFlag.PUBLIC); cb.withFlags(AccessFlag.PUBLIC);
cb.withVersion(52, 0); cb.withVersion(52, 0);
cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java")))) cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java"))))
@ -107,7 +107,7 @@ class OneToOneTest {
} }
); );
ClassModel cm = Classfile.parse(bytes); ClassModel cm = cc.parse(bytes);
List<MethodModel> ms = cm.methods(); List<MethodModel> ms = cm.methods();
assertEquals(ms.size(), 2); assertEquals(ms.size(), 2);
boolean found = false; boolean found = false;

View File

@ -105,7 +105,7 @@ public class OpcodesValidationTest {
} }
private void testPositiveCase(Opcode opcode, Object constant) { private void testPositiveCase(Opcode opcode, Object constant) {
Classfile.build(ClassDesc.of("MyClass"), Classfile.of().build(ClassDesc.of("MyClass"),
cb -> cb.withFlags(AccessFlag.PUBLIC) cb -> cb.withFlags(AccessFlag.PUBLIC)
.withMethod("<init>", MethodTypeDesc.of(CD_void), 0, .withMethod("<init>", MethodTypeDesc.of(CD_void), 0,
mb -> mb.withCode( mb -> mb.withCode(
@ -122,7 +122,7 @@ public class OpcodesValidationTest {
} }
private void testNegativeCase(Opcode opcode, Object constant) { private void testNegativeCase(Opcode opcode, Object constant) {
Classfile.build(ClassDesc.of("MyClass"), Classfile.of().build(ClassDesc.of("MyClass"),
cb -> cb.withFlags(AccessFlag.PUBLIC) cb -> cb.withFlags(AccessFlag.PUBLIC)
.withMethod("<init>", MethodTypeDesc.of(CD_void), 0, .withMethod("<init>", MethodTypeDesc.of(CD_void), 0,
mb -> mb .withCode( mb -> mb .withCode(

View File

@ -53,7 +53,7 @@ public final class PrimitiveClassConstantTest {
public void test() throws Throwable { public void test() throws Throwable {
ClassDesc ape = ClassDesc.of("Ape"); ClassDesc ape = ClassDesc.of("Ape");
var lookup = MethodHandles.lookup(); var lookup = MethodHandles.lookup();
Class<?> a = lookup.defineClass(Classfile.build(ape, clb -> { Class<?> a = lookup.defineClass(Classfile.of().build(ape, clb -> {
clb.withSuperclass(CD_Object); clb.withSuperclass(CD_Object);
clb.withInterfaceSymbols(Supplier.class.describeConstable().orElseThrow()); clb.withInterfaceSymbols(Supplier.class.describeConstable().orElseThrow());
clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> { clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> {

View File

@ -33,6 +33,7 @@ import java.lang.constant.ConstantDescs;
import java.lang.constant.MethodTypeDesc; import java.lang.constant.MethodTypeDesc;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import jdk.internal.classfile.ClassModel;
import jdk.internal.classfile.ClassTransform; import jdk.internal.classfile.ClassTransform;
import jdk.internal.classfile.Classfile; import jdk.internal.classfile.Classfile;
import jdk.internal.classfile.Instruction; import jdk.internal.classfile.Instruction;
@ -99,109 +100,114 @@ class ShortJumpsFixTest {
}; };
} }
static final Classfile
CC_Fixed_Jumps = Classfile.of(Classfile.ShortJumpsOption.FIX_SHORT_JUMPS),
CC_Not_Fixed_Jumps = Classfile.of(Classfile.ShortJumpsOption.FAIL_ON_SHORT_JUMPS),
CC_No_Stack_No_Patch = Classfile.of(Classfile.StackMapsOption.DROP_STACK_MAPS,
Classfile.DeadCodeOption.KEEP_DEAD_CODE);
@ParameterizedTest @ParameterizedTest
@MethodSource("provideFwd") @MethodSource("provideFwd")
void testFixFwdJumpsDirectGen(Sample sample) throws Exception { void testFixFwdJumpsDirectGen(Sample sample) throws Exception {
assertFixed(sample, generateFwd(sample, true, Classfile.Option.fixShortJumps(true))); assertFixed(sample,
generateFwd(CC_Fixed_Jumps, sample, true));
} }
@ParameterizedTest @ParameterizedTest
@MethodSource("provideBack") @MethodSource("provideBack")
void testFixBackJumpsDirectGen(Sample sample) throws Exception { void testFixBackJumpsDirectGen(Sample sample) throws Exception {
assertFixed(sample, generateBack(sample, true, Classfile.Option.fixShortJumps(true))); assertFixed(sample,
generateBack(CC_Fixed_Jumps, sample, true));
} }
@ParameterizedTest @ParameterizedTest
@MethodSource("provideFwd") @MethodSource("provideFwd")
void testFailFwdJumpsDirectGen(Sample sample) throws Exception { void testFailFwdJumpsDirectGen(Sample sample) throws Exception {
assertThrows(IllegalArgumentException.class, () -> generateFwd(sample, true, Classfile.Option.fixShortJumps(false))); assertThrows(IllegalArgumentException.class, () ->
generateFwd(CC_Not_Fixed_Jumps, sample, true));
} }
@ParameterizedTest @ParameterizedTest
@MethodSource("provideBack") @MethodSource("provideBack")
void testFailBackJumpsDirectGen(Sample sample) throws Exception { void testFailBackJumpsDirectGen(Sample sample) throws Exception {
assertThrows(IllegalArgumentException.class, () -> generateBack(sample, true, Classfile.Option.fixShortJumps(false))); assertThrows(IllegalArgumentException.class, () ->
generateBack(CC_Not_Fixed_Jumps, sample, true));
} }
@ParameterizedTest @ParameterizedTest
@MethodSource("provideFwd") @MethodSource("provideFwd")
void testFixFwdJumpsTransform(Sample sample) throws Exception { void testFixFwdJumpsTransform(Sample sample) throws Exception {
assertFixed(sample, Classfile.parse( assertFixed(sample,
generateFwd(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), CC_Fixed_Jumps.transform(
Classfile.Option.fixShortJumps(true)) generateFwd(CC_No_Stack_No_Patch, sample, false),
.transform(overflow())); overflow()));
} }
@ParameterizedTest @ParameterizedTest
@MethodSource("provideBack") @MethodSource("provideBack")
void testFixBackJumpsTransform(Sample sample) throws Exception { void testFixBackJumpsTransform(Sample sample) throws Exception {
assertFixed(sample, Classfile.parse( assertFixed(sample,
generateBack(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), CC_Fixed_Jumps.transform(
Classfile.Option.fixShortJumps(true)) generateBack(CC_No_Stack_No_Patch, sample, false),
.transform(overflow())); overflow()));
} }
@ParameterizedTest @ParameterizedTest
@MethodSource("provideFwd") @MethodSource("provideFwd")
void testFailFwdJumpsTransform(Sample sample) throws Exception { void testFailFwdJumpsTransform(Sample sample) throws Exception {
assertThrows(IllegalArgumentException.class, () -> assertThrows(IllegalArgumentException.class, () ->
Classfile.parse( CC_Not_Fixed_Jumps.transform(
generateFwd(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), generateFwd(CC_No_Stack_No_Patch, sample, false),
Classfile.Option.fixShortJumps(false)) overflow()));
.transform(overflow()));
} }
@ParameterizedTest @ParameterizedTest
@MethodSource("provideBack") @MethodSource("provideBack")
void testFailBackJumpsTransform(Sample sample) throws Exception { void testFailBackJumpsTransform(Sample sample) throws Exception {
assertThrows(IllegalArgumentException.class, () -> assertThrows(IllegalArgumentException.class, () ->
Classfile.parse( CC_Not_Fixed_Jumps.transform(
generateBack(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), generateBack(CC_No_Stack_No_Patch, sample, false),
Classfile.Option.fixShortJumps(false)) overflow()));
.transform(overflow()));
} }
@ParameterizedTest @ParameterizedTest
@MethodSource("provideFwd") @MethodSource("provideFwd")
void testFixFwdJumpsChainedTransform(Sample sample) throws Exception { void testFixFwdJumpsChainedTransform(Sample sample) throws Exception {
assertFixed(sample, Classfile.parse( assertFixed(sample,
generateFwd(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), CC_Fixed_Jumps.transform(
Classfile.Option.fixShortJumps(true)) generateFwd(CC_No_Stack_No_Patch, sample, false),
.transform(ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here
} }
@ParameterizedTest @ParameterizedTest
@MethodSource("provideBack") @MethodSource("provideBack")
void testFixBackJumpsChainedTransform(Sample sample) throws Exception { void testFixBackJumpsChainedTransform(Sample sample) throws Exception {
assertFixed(sample, Classfile.parse( assertFixed(sample,
generateBack(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), CC_Fixed_Jumps.transform(
Classfile.Option.fixShortJumps(true)) generateBack(CC_No_Stack_No_Patch, sample, false),
.transform(ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here
} }
@ParameterizedTest @ParameterizedTest
@MethodSource("provideFwd") @MethodSource("provideFwd")
void testFailFwdJumpsChainedTransform(Sample sample) throws Exception { void testFailFwdJumpsChainedTransform(Sample sample) throws Exception {
assertThrows(IllegalArgumentException.class, () -> assertThrows(IllegalArgumentException.class, () ->
Classfile.parse( CC_Not_Fixed_Jumps.transform(
generateFwd(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), generateFwd(CC_No_Stack_No_Patch, sample, false),
Classfile.Option.fixShortJumps(false)) ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here
.transform(ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here
} }
@ParameterizedTest @ParameterizedTest
@MethodSource("provideBack") @MethodSource("provideBack")
void testFailBackJumpsChainedTransform(Sample sample) throws Exception { void testFailBackJumpsChainedTransform(Sample sample) throws Exception {
assertThrows(IllegalArgumentException.class, () -> assertThrows(IllegalArgumentException.class, () ->
Classfile.parse( CC_Not_Fixed_Jumps.transform(
generateBack(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)), generateBack(CC_No_Stack_No_Patch, sample, false),
Classfile.Option.fixShortJumps(false)) ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here
.transform(ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here
} }
private static byte[] generateFwd(Sample sample, boolean overflow, Classfile.Option... options) { private static ClassModel generateFwd(Classfile cc, Sample sample, boolean overflow) {
return Classfile.build(ClassDesc.of("WhateverClass"), List.of(options), return cc.parse(cc.build(ClassDesc.of("WhateverClass"),
cb -> cb.withMethod("whateverMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cb -> cb.withMethod("whateverMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0,
mb -> mb.withCode(cob -> { mb -> mb.withCode(cob -> {
for (int i = 0; i < sample.expected.length - 4; i++) //cherry-pick XCONST_ instructions from expected output for (int i = 0; i < sample.expected.length - 4; i++) //cherry-pick XCONST_ instructions from expected output
@ -212,11 +218,11 @@ class ShortJumpsFixTest {
cob.nopInstruction(); cob.nopInstruction();
cob.labelBinding(target); cob.labelBinding(target);
cob.return_(); cob.return_();
}))); }))));
} }
private static byte[] generateBack(Sample sample, boolean overflow, Classfile.Option... options) { private static ClassModel generateBack(Classfile cc, Sample sample, boolean overflow) {
return Classfile.build(ClassDesc.of("WhateverClass"), List.of(options), return cc.parse(cc.build(ClassDesc.of("WhateverClass"),
cb -> cb.withMethod("whateverMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cb -> cb.withMethod("whateverMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0,
mb -> mb.withCode(cob -> { mb -> mb.withCode(cob -> {
var target = cob.newLabel(); var target = cob.newLabel();
@ -231,7 +237,7 @@ class ShortJumpsFixTest {
cob.with(ConstantInstruction.ofIntrinsic(sample.expected[i])); cob.with(ConstantInstruction.ofIntrinsic(sample.expected[i]));
cob.branchInstruction(sample.jumpCode, target); cob.branchInstruction(sample.jumpCode, target);
cob.return_(); cob.return_();
}))); }))));
} }
private static ClassTransform overflow() { private static ClassTransform overflow() {
@ -246,8 +252,12 @@ class ShortJumpsFixTest {
} }
private static void assertFixed(Sample sample, byte[] classFile) { private static void assertFixed(Sample sample, byte[] classFile) {
assertFixed(sample, Classfile.of().parse(classFile));
}
private static void assertFixed(Sample sample, ClassModel clm) {
var found = new LinkedList<Opcode>(); var found = new LinkedList<Opcode>();
for (var e : Classfile.parse(classFile).methods().get(0).code().get()) for (var e : clm.methods().get(0).code().get())
if (e instanceof Instruction i && found.peekLast() != i.opcode()) //dedup subsequent (NOPs) if (e instanceof Instruction i && found.peekLast() != i.opcode()) //dedup subsequent (NOPs)
found.add(i.opcode()); found.add(i.opcode());
assertEquals(found, List.of(sample.expected)); assertEquals(found, List.of(sample.expected));

View File

@ -128,7 +128,7 @@ class SignaturesTest {
.flatMap(p -> p) .flatMap(p -> p)
.filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".class")).forEach(path -> { .filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".class")).forEach(path -> {
try { try {
var cm = Classfile.parse(path); var cm = Classfile.of().parse(path);
cm.findAttribute(Attributes.SIGNATURE).ifPresent(csig -> { cm.findAttribute(Attributes.SIGNATURE).ifPresent(csig -> {
assertEquals( assertEquals(
ClassSignature.parseFrom(csig.signature().stringValue()).signatureString(), ClassSignature.parseFrom(csig.signature().stringValue()).signatureString(),

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @summary Compile Classfile API snippets
* @run junit SnippetsTest
*/
import java.nio.file.Paths;
import java.util.List;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
public class SnippetsTest {
@ParameterizedTest
@ValueSource(strings = {
"src/java.base/share/classes/jdk/internal/classfile/snippet-files/PackageSnippets.java",
"src/java.base/share/classes/jdk/internal/classfile/components/snippet-files/PackageSnippets.java"})
void testSnippet(String source) throws Exception {
var p = Paths.get(System.getProperty("test.src", ".")).toAbsolutePath();
while ((p = p.getParent()) != null) {
var src = p.resolve(source).toFile();
if (src.isFile()) {
var compiler = ToolProvider.getSystemJavaCompiler();
try (var fileManager = compiler.getStandardFileManager(null, null, null)) {
var compilationUnits = fileManager.getJavaFileObjectsFromFiles(List.of(src));
fileManager.setLocation(StandardLocation.CLASS_OUTPUT,
List.of(Paths.get(System.getProperty("test.classes", ".")).toFile()));
var task = compiler.getTask(null, fileManager, null, List.of(
"--add-exports", "java.base/jdk.internal.classfile=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.attribute=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.components=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.constantpool=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.instruction=ALL-UNNAMED"),
null, compilationUnits);
if (task.call()) return;
throw new RuntimeException("Error compiling " + source);
}
}
}
Assumptions.abort("Source file not found: " + source); //do not fail in source-less test environment
}
}

View File

@ -54,9 +54,9 @@ import java.lang.reflect.AccessFlag;
class StackMapsTest { class StackMapsTest {
private byte[] buildDeadCode() { private byte[] buildDeadCode() {
return Classfile.build( return Classfile.of(Classfile.StackMapsOption.DROP_STACK_MAPS,
Classfile.DeadCodeOption.KEEP_DEAD_CODE).build(
ClassDesc.of("DeadCodePattern"), ClassDesc.of("DeadCodePattern"),
List.of(Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)),
clb -> clb.withMethodBody( clb -> clb.withMethodBody(
"twoReturns", "twoReturns",
MethodTypeDesc.of(ConstantDescs.CD_void), MethodTypeDesc.of(ConstantDescs.CD_void),
@ -97,7 +97,7 @@ class StackMapsTest {
@Test @Test
void testDeadCodePatternFail() throws Exception { void testDeadCodePatternFail() throws Exception {
var error = assertThrows(IllegalArgumentException.class, () -> testTransformedStackMaps(buildDeadCode(), Classfile.Option.patchDeadCode(false))); var error = assertThrows(IllegalArgumentException.class, () -> testTransformedStackMaps(buildDeadCode(), Classfile.DeadCodeOption.KEEP_DEAD_CODE));
assertLinesMatch( assertLinesMatch(
""" """
Unable to generate stack map frame for dead code at bytecode offset 1 of method twoReturns() Unable to generate stack map frame for dead code at bytecode offset 1 of method twoReturns()
@ -172,9 +172,10 @@ class StackMapsTest {
@Test @Test
void testFrameOutOfBytecodeRange() { void testFrameOutOfBytecodeRange() {
var cc = Classfile.of();
var error = assertThrows(IllegalArgumentException.class, () -> var error = assertThrows(IllegalArgumentException.class, () ->
Classfile.parse( cc.parse(
Classfile.build(ClassDesc.of("TestClass"), clb -> cc.build(ClassDesc.of("TestClass"), clb ->
clb.withMethodBody("frameOutOfRangeMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> { clb.withMethodBody("frameOutOfRangeMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> {
var l = cob.newLabel(); var l = cob.newLabel();
cob.goto_(l);//jump to the end of method body triggers invalid frame creation cob.goto_(l);//jump to the end of method body triggers invalid frame creation
@ -194,7 +195,7 @@ class StackMapsTest {
@Test @Test
void testMethodSwitchFromStatic() { void testMethodSwitchFromStatic() {
assertThrows(IllegalArgumentException.class, () -> assertThrows(IllegalArgumentException.class, () ->
Classfile.build(ClassDesc.of("TestClass"), clb -> Classfile.of().build(ClassDesc.of("TestClass"), clb ->
clb.withMethod("testMethod", MethodTypeDesc.of(ConstantDescs.CD_Object, ConstantDescs.CD_int), clb.withMethod("testMethod", MethodTypeDesc.of(ConstantDescs.CD_Object, ConstantDescs.CD_int),
ACC_STATIC, ACC_STATIC,
mb -> mb.withCode(cob -> { mb -> mb.withCode(cob -> {
@ -207,7 +208,7 @@ class StackMapsTest {
@Test @Test
void testMethodSwitchToStatic() { void testMethodSwitchToStatic() {
assertThrows(IllegalArgumentException.class, () -> assertThrows(IllegalArgumentException.class, () ->
Classfile.build(ClassDesc.of("TestClass"), clb -> Classfile.of().build(ClassDesc.of("TestClass"), clb ->
clb.withMethod("testMethod", MethodTypeDesc.of(ConstantDescs.CD_int, ConstantDescs.CD_int), clb.withMethod("testMethod", MethodTypeDesc.of(ConstantDescs.CD_int, ConstantDescs.CD_int),
0, mb -> 0, mb ->
mb.withCode(cob -> { mb.withCode(cob -> {
@ -219,16 +220,23 @@ class StackMapsTest {
@Test @Test
void testClassVersions() throws Exception { void testClassVersions() throws Exception {
var actualVersion = Classfile.parse(StackMapsTest.class.getResourceAsStream("/testdata/Pattern1.class").readAllBytes()); var cc = Classfile.of();
var actualVersion = cc.parse(StackMapsTest.class.getResourceAsStream("/testdata/Pattern1.class").readAllBytes());
//test transformation to class version 49 with removal of StackMapTable attributes //test transformation to class version 49 with removal of StackMapTable attributes
var version49 = Classfile.parse(actualVersion.transform(ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL) var version49 = cc.parse(cc.transform(
.andThen(ClassTransform.endHandler(clb -> clb.withVersion(49, 0))))); actualVersion,
assertFalse(ClassPrinter.toTree(version49, ClassPrinter.Verbosity.CRITICAL_ATTRIBUTES).walk().anyMatch(n -> n.name().equals("stack map frames"))); ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL)
.andThen(ClassTransform.endHandler(clb -> clb.withVersion(49, 0)))));
assertFalse(ClassPrinter.toTree(version49, ClassPrinter.Verbosity.CRITICAL_ATTRIBUTES)
.walk().anyMatch(n -> n.name().equals("stack map frames")));
//test transformation to class version 50 with re-generation of StackMapTable attributes //test transformation to class version 50 with re-generation of StackMapTable attributes
assertEmpty(Classfile.parse(version49.transform(ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL) assertEmpty(cc.parse(cc.transform(
.andThen(ClassTransform.endHandler(clb -> clb.withVersion(50, 0))))).verify(null)); version49,
ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL)
.andThen(ClassTransform.endHandler(clb -> clb.withVersion(50, 0)))))
.verify(null));
} }
private static final FileSystem JRT = FileSystems.getFileSystem(URI.create("jrt:/")); private static final FileSystem JRT = FileSystems.getFileSystem(URI.create("jrt:/"));
@ -243,8 +251,9 @@ class StackMapsTest {
private static void testTransformedStackMaps(byte[] originalBytes, Classfile.Option... options) throws Exception { private static void testTransformedStackMaps(byte[] originalBytes, Classfile.Option... options) throws Exception {
//transform the class model //transform the class model
var classModel = Classfile.parse(originalBytes, options); Classfile cc = Classfile.of(options);
var transformedBytes = Classfile.build(classModel.thisClass().asSymbol(), List.of(options), var classModel = cc.parse(originalBytes);
var transformedBytes = cc.build(classModel.thisClass().asSymbol(),
cb -> { cb -> {
// classModel.superclass().ifPresent(cb::withSuperclass); // classModel.superclass().ifPresent(cb::withSuperclass);
// cb.withInterfaces(classModel.interfaces()); // cb.withInterfaces(classModel.interfaces());
@ -253,6 +262,6 @@ class StackMapsTest {
}); });
//then verify transformed bytecode //then verify transformed bytecode
assertEmpty(Classfile.parse(transformedBytes).verify(null)); assertEmpty(cc.parse(transformedBytes).verify(null));
} }
} }

View File

@ -45,7 +45,7 @@ class StackTrackerTest {
@Test @Test
void testStackTracker() { void testStackTracker() {
Classfile.build(ClassDesc.of("Foo"), clb -> Classfile.of().build(ClassDesc.of("Foo"), clb ->
clb.withMethodBody("m", MethodTypeDesc.of(ConstantDescs.CD_Void), 0, cob -> { clb.withMethodBody("m", MethodTypeDesc.of(ConstantDescs.CD_Void), 0, cob -> {
var stackTracker = CodeStackTracker.of(DoubleType, FloatType); //initial stack tracker pre-set var stackTracker = CodeStackTracker.of(DoubleType, FloatType); //initial stack tracker pre-set
cob.transforming(stackTracker, stcb -> { cob.transforming(stackTracker, stcb -> {
@ -81,7 +81,7 @@ class StackTrackerTest {
@Test @Test
void testTrackingLost() { void testTrackingLost() {
Classfile.build(ClassDesc.of("Foo"), clb -> Classfile.of().build(ClassDesc.of("Foo"), clb ->
clb.withMethodBody("m", MethodTypeDesc.of(ConstantDescs.CD_Void), 0, cob -> { clb.withMethodBody("m", MethodTypeDesc.of(ConstantDescs.CD_Void), 0, cob -> {
var stackTracker = CodeStackTracker.of(); var stackTracker = CodeStackTracker.of();
cob.transforming(stackTracker, stcb -> { cob.transforming(stackTracker, stcb -> {

View File

@ -78,7 +78,7 @@ class StreamedVsListTest {
private class Vs { private class Vs {
boolean failed; boolean failed;
ClassModel cm = Classfile.parse(fileBytes); ClassModel cm = Classfile.of().parse(fileBytes);
String meth; String meth;
CodeElement iim; CodeElement iim;
CodeElement mim; CodeElement mim;

View File

@ -47,7 +47,7 @@ class SwapTest {
MethodType mt = MethodType.methodType(String.class, String.class, String.class); MethodType mt = MethodType.methodType(String.class, String.class, String.class);
MethodTypeDesc mtd = mt.describeConstable().get(); MethodTypeDesc mtd = mt.describeConstable().get();
byte[] bytes = Classfile.build(ClassDesc.of("C"), cb -> { byte[] bytes = Classfile.of().build(ClassDesc.of("C"), cb -> {
cb.withMethodBody("m", mtd, AccessFlags.ofMethod(PUBLIC, STATIC).flagsMask(), xb -> { cb.withMethodBody("m", mtd, AccessFlags.ofMethod(PUBLIC, STATIC).flagsMask(), xb -> {
xb.aload(0); // 0 xb.aload(0); // 0
xb.aload(1); // 1, 0 xb.aload(1); // 1, 0

View File

@ -55,7 +55,8 @@ class TempConstantPoolBuilderTest {
@Test @Test
void addAnno() { void addAnno() {
byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { var cc = Classfile.of();
byte[] bytes = cc.build(ClassDesc.of("MyClass"), cb -> {
cb.withFlags(AccessFlag.PUBLIC) cb.withFlags(AccessFlag.PUBLIC)
.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java")))) .with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java"))))
.withMethod("<init>", MethodTypeDesc.of(CD_void), 0, mb -> mb .withMethod("<init>", MethodTypeDesc.of(CD_void), 0, mb -> mb
@ -67,7 +68,7 @@ class TempConstantPoolBuilderTest {
AnnotationElement.ofString("foo", "bar")))) AnnotationElement.ofString("foo", "bar"))))
); );
}); });
ClassModel m = Classfile.parse(bytes); ClassModel m = cc.parse(bytes);
//ClassPrinter.toJson(m, ClassPrinter.Verbosity.TRACE_ALL, System.out::println); //ClassPrinter.toJson(m, ClassPrinter.Verbosity.TRACE_ALL, System.out::println);
} }
} }

View File

@ -55,7 +55,8 @@ class TestRecordComponent {
@Test @Test
void testAdapt() throws Exception { void testAdapt() throws Exception {
ClassModel cm = Classfile.parse(Files.readAllBytes(testClassPath)); var cc = Classfile.of();
ClassModel cm = cc.parse(Files.readAllBytes(testClassPath));
ClassTransform xform = (cb, ce) -> { ClassTransform xform = (cb, ce) -> {
if (ce instanceof RecordAttribute rm) { if (ce instanceof RecordAttribute rm) {
List<RecordComponentInfo> components = rm.components(); List<RecordComponentInfo> components = rm.components();
@ -66,21 +67,23 @@ class TestRecordComponent {
} else } else
cb.with(ce); cb.with(ce);
}; };
ClassModel newModel = Classfile.parse(cm.transform(xform)); ClassModel newModel = cc.parse(cc.transform(cm, xform));
ClassRecord.assertEquals(newModel, cm); ClassRecord.assertEquals(newModel, cm);
} }
@Test @Test
void testPassThrough() throws Exception { void testPassThrough() throws Exception {
ClassModel cm = Classfile.parse(Files.readAllBytes(testClassPath)); var cc = Classfile.of();
ClassModel cm = cc.parse(Files.readAllBytes(testClassPath));
ClassTransform xform = (cb, ce) -> cb.with(ce); ClassTransform xform = (cb, ce) -> cb.with(ce);
ClassModel newModel = Classfile.parse(cm.transform(xform)); ClassModel newModel = cc.parse(cc.transform(cm, xform));
ClassRecord.assertEquals(newModel, cm); ClassRecord.assertEquals(newModel, cm);
} }
@Test @Test
void testChagne() throws Exception { void testChagne() throws Exception {
ClassModel cm = Classfile.parse(Files.readAllBytes(testClassPath)); var cc = Classfile.of();
ClassModel cm = cc.parse(Files.readAllBytes(testClassPath));
ClassTransform xform = (cb, ce) -> { ClassTransform xform = (cb, ce) -> {
if (ce instanceof RecordAttribute ra) { if (ce instanceof RecordAttribute ra) {
List<RecordComponentInfo> components = ra.components(); List<RecordComponentInfo> components = ra.components();
@ -91,7 +94,7 @@ class TestRecordComponent {
else else
cb.with(ce); cb.with(ce);
}; };
ClassModel newModel = Classfile.parse(cm.transform(xform)); ClassModel newModel = cc.parse(cc.transform(cm, xform));
RecordAttribute ra = newModel.findAttribute(Attributes.RECORD).orElseThrow(); RecordAttribute ra = newModel.findAttribute(Attributes.RECORD).orElseThrow();
assertEquals(ra.components().size(), 2, "Should have two components"); assertEquals(ra.components().size(), 2, "Should have two components");
assertEquals(ra.components().get(0).name().stringValue(), "fooXYZ"); assertEquals(ra.components().get(0).name().stringValue(), "fooXYZ");
@ -103,7 +106,7 @@ class TestRecordComponent {
@Test @Test
void testOptions() throws Exception { void testOptions() throws Exception {
AtomicInteger count = new AtomicInteger(0); AtomicInteger count = new AtomicInteger(0);
ClassModel cm = Classfile.parse(Files.readAllBytes(testClassPath)); ClassModel cm = Classfile.of().parse(Files.readAllBytes(testClassPath));
cm.forEachElement((ce) -> { cm.forEachElement((ce) -> {
if (ce instanceof RecordAttribute rm) { if (ce instanceof RecordAttribute rm) {
count.addAndGet(rm.components().size()); count.addAndGet(rm.components().size());

View File

@ -95,34 +95,37 @@ class TransformTests {
void testSingleTransform() throws Exception { void testSingleTransform() throws Exception {
byte[] bytes = Files.readAllBytes(testClassPath); byte[] bytes = Files.readAllBytes(testClassPath);
ClassModel cm = Classfile.parse(bytes); var cc = Classfile.of();
ClassModel cm = cc.parse(bytes);
assertEquals(invoke(bytes), "foo"); assertEquals(invoke(bytes), "foo");
assertEquals(invoke(cm.transform(transformCode(foo2foo))), "foo"); assertEquals(invoke(cc.transform(cm, transformCode(foo2foo))), "foo");
assertEquals(invoke(cm.transform(transformCode(foo2bar))), "bar"); assertEquals(invoke(cc.transform(cm, transformCode(foo2bar))), "bar");
} }
@Test @Test
void testSeq2() throws Exception { void testSeq2() throws Exception {
byte[] bytes = Files.readAllBytes(testClassPath); byte[] bytes = Files.readAllBytes(testClassPath);
ClassModel cm = Classfile.parse(bytes); var cc = Classfile.of();
ClassModel cm = cc.parse(bytes);
assertEquals(invoke(bytes), "foo"); assertEquals(invoke(bytes), "foo");
ClassTransform transform = transformCode(foo2bar.andThen(bar2baz)); ClassTransform transform = transformCode(foo2bar.andThen(bar2baz));
assertEquals(invoke(cm.transform(transform)), "baz"); assertEquals(invoke(cc.transform(cm, transform)), "baz");
} }
@Test @Test
void testSeqN() throws Exception { void testSeqN() throws Exception {
byte[] bytes = Files.readAllBytes(testClassPath); byte[] bytes = Files.readAllBytes(testClassPath);
ClassModel cm = Classfile.parse(bytes); var cc = Classfile.of();
ClassModel cm = cc.parse(bytes);
assertEquals(invoke(bytes), "foo"); assertEquals(invoke(bytes), "foo");
assertEquals(invoke(cm.transform(transformCode(foo2bar.andThen(bar2baz).andThen(baz2foo)))), "foo"); assertEquals(invoke(cc.transform(cm, transformCode(foo2bar.andThen(bar2baz).andThen(baz2foo)))), "foo");
assertEquals(invoke(cm.transform(transformCode(foo2bar.andThen(bar2baz).andThen(baz2quux)))), "quux"); assertEquals(invoke(cc.transform(cm, transformCode(foo2bar.andThen(bar2baz).andThen(baz2quux)))), "quux");
assertEquals(invoke(cm.transform(transformCode(foo2foo.andThen(foo2bar).andThen(bar2baz)))), "baz"); assertEquals(invoke(cc.transform(cm, transformCode(foo2foo.andThen(foo2bar).andThen(bar2baz)))), "baz");
} }
public static class TestClass { public static class TestClass {

View File

@ -83,7 +83,7 @@ class Utf8EntryTest {
void testParse(String s) { void testParse(String s) {
byte[] classfile = createClassfile(s); byte[] classfile = createClassfile(s);
ClassModel cm = Classfile.parse(classfile); ClassModel cm = Classfile.of().parse(classfile);
StringEntry se = obtainStringEntry(cm.constantPool()); StringEntry se = obtainStringEntry(cm.constantPool());
Utf8Entry utf8Entry = se.utf8(); Utf8Entry utf8Entry = se.utf8();
@ -163,7 +163,7 @@ class Utf8EntryTest {
byte[] classfile = createClassfile(marker); byte[] classfile = createClassfile(marker);
replace(classfile, marker, f); replace(classfile, marker, f);
ClassModel cm = Classfile.parse(classfile); ClassModel cm = Classfile.of().parse(classfile);
StringEntry se = obtainStringEntry(cm.constantPool()); StringEntry se = obtainStringEntry(cm.constantPool());
assertThrows(RuntimeException.class, () -> { assertThrows(RuntimeException.class, () -> {
@ -197,7 +197,7 @@ class Utf8EntryTest {
} }
static byte[] createClassfile(String s) { static byte[] createClassfile(String s) {
return Classfile.build(ClassDesc.of("C"), return Classfile.of().build(ClassDesc.of("C"),
clb -> clb.withMethod("m", MethodTypeDesc.of(CD_void), 0, clb -> clb.withMethod("m", MethodTypeDesc.of(CD_void), 0,
mb -> mb.withCode(cb -> cb.constantInstruction(s) mb -> mb.withCode(cb -> cb.constantInstruction(s)
.returnInstruction(VoidType)))); .returnInstruction(VoidType))));

View File

@ -53,7 +53,7 @@ class VerifierSelfTest {
.flatMap(p -> p) .flatMap(p -> p)
.filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".class")).forEach(path -> { .filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".class")).forEach(path -> {
try { try {
Classfile.parse(path).verify(null); Classfile.of().parse(path).verify(null);
} catch (IOException e) { } catch (IOException e) {
throw new AssertionError(e); throw new AssertionError(e);
} }
@ -63,9 +63,10 @@ class VerifierSelfTest {
@Test @Test
void testFailedDump() throws IOException { void testFailedDump() throws IOException {
Path path = FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java/util/HashMap.class"); Path path = FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java/util/HashMap.class");
var classModel = Classfile.parse(path, Classfile.Option.classHierarchyResolver( var cc = Classfile.of(Classfile.ClassHierarchyResolverOption.of(
className -> ClassHierarchyResolver.ClassHierarchyInfo.ofClass(null))); className -> ClassHierarchyResolver.ClassHierarchyInfo.ofClass(null)));
byte[] brokenClassBytes = classModel.transform( var classModel = cc.parse(path);
byte[] brokenClassBytes = cc.transform(classModel,
(clb, cle) -> { (clb, cle) -> {
if (cle instanceof MethodModel mm) { if (cle instanceof MethodModel mm) {
clb.transformMethod(mm, (mb, me) -> { clb.transformMethod(mm, (mb, me) -> {
@ -80,7 +81,7 @@ class VerifierSelfTest {
clb.with(cle); clb.with(cle);
}); });
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (Classfile.parse(brokenClassBytes).verify(sb::append).isEmpty()) { if (Classfile.of().parse(brokenClassBytes).verify(sb::append).isEmpty()) {
throw new AssertionError("expected verification failure"); throw new AssertionError("expected verification failure");
} }
String output = sb.toString(); String output = sb.toString();

View File

@ -52,7 +52,7 @@ class WriteTest {
@Test @Test
void testJavapWrite() { void testJavapWrite() {
byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { byte[] bytes = Classfile.of().build(ClassDesc.of("MyClass"), cb -> {
cb.withFlags(AccessFlag.PUBLIC); cb.withFlags(AccessFlag.PUBLIC);
cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java")))) cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java"))))
.withMethod("<init>", MethodTypeDesc.of(CD_void), 0, mb -> mb .withMethod("<init>", MethodTypeDesc.of(CD_void), 0, mb -> mb
@ -94,7 +94,7 @@ class WriteTest {
@Test @Test
void testPrimitiveWrite() { void testPrimitiveWrite() {
byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> { byte[] bytes = Classfile.of().build(ClassDesc.of("MyClass"), cb -> {
cb.withFlags(AccessFlag.PUBLIC) cb.withFlags(AccessFlag.PUBLIC)
.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java")))) .with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java"))))
.withMethod("<init>", MethodTypeDesc.of(CD_void), 0, mb -> mb .withMethod("<init>", MethodTypeDesc.of(CD_void), 0, mb -> mb

View File

@ -49,7 +49,7 @@ public class AnnotationsExamples {
public byte[] addAnno(ClassModel m) { public byte[] addAnno(ClassModel m) {
// @@@ Not correct // @@@ Not correct
List<Annotation> annos = List.of(Annotation.of(ClassDesc.of("java.lang.FunctionalInterface"))); List<Annotation> annos = List.of(Annotation.of(ClassDesc.of("java.lang.FunctionalInterface")));
return m.transform(ClassTransform.endHandler(cb -> cb.with(RuntimeVisibleAnnotationsAttribute.of(annos)))); return Classfile.of().transform(m, ClassTransform.endHandler(cb -> cb.with(RuntimeVisibleAnnotationsAttribute.of(annos))));
} }
/** /**
@ -73,9 +73,10 @@ public class AnnotationsExamples {
if (m.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).isPresent()) { if (m.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).isPresent()) {
RuntimeVisibleAnnotationsAttribute a = m.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).get(); RuntimeVisibleAnnotationsAttribute a = m.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).get();
var cc = Classfile.of();
for (Annotation ann : a.annotations()) { for (Annotation ann : a.annotations()) {
if (ann.className().stringValue().equals("Ljava/lang/annotation/Documented;")) { if (ann.className().stringValue().equals("Ljava/lang/annotation/Documented;")) {
m2 = Classfile.parse(m.transform(SWAP_ANNO_TRANSFORM)); m2 = cc.parse(cc.transform(m, SWAP_ANNO_TRANSFORM));
} }
} }
} }
@ -116,9 +117,10 @@ public class AnnotationsExamples {
if (m.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).isPresent()) { if (m.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).isPresent()) {
RuntimeVisibleAnnotationsAttribute a = m.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).get(); RuntimeVisibleAnnotationsAttribute a = m.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).get();
var cc = Classfile.of();
for (Annotation ann : a.annotations()) { for (Annotation ann : a.annotations()) {
if (ann.className().stringValue().equals("Ljava/lang/FunctionalInterface;")) { if (ann.className().stringValue().equals("Ljava/lang/FunctionalInterface;")) {
m2 = Classfile.parse(m.transform((cb, ce) -> { m2 = cc.parse(cc.transform(m, (cb, ce) -> {
if (ce instanceof RuntimeVisibleAnnotationsAttribute ra) { if (ce instanceof RuntimeVisibleAnnotationsAttribute ra) {
var oldAnnos = ra.annotations(); var oldAnnos = ra.annotations();
List<Annotation> newAnnos = new ArrayList<>(oldAnnos.size() + 1); List<Annotation> newAnnos = new ArrayList<>(oldAnnos.size() + 1);
@ -144,7 +146,7 @@ public class AnnotationsExamples {
} }
public byte[] viaEndHandlerClassBuilderEdition(ClassModel m) { public byte[] viaEndHandlerClassBuilderEdition(ClassModel m) {
return m.transform(ClassTransform.ofStateful(() -> new ClassTransform() { return Classfile.of().transform(m, ClassTransform.ofStateful(() -> new ClassTransform() {
boolean found = false; boolean found = false;
@Override @Override
@ -171,7 +173,7 @@ public class AnnotationsExamples {
} }
public byte[] viaEndHandlerClassTransformEdition(ClassModel m) { public byte[] viaEndHandlerClassTransformEdition(ClassModel m) {
return m.transform(ClassTransform.ofStateful(() -> new ClassTransform() { return Classfile.of().transform(m, ClassTransform.ofStateful(() -> new ClassTransform() {
boolean found = false; boolean found = false;
@Override @Override

View File

@ -64,7 +64,7 @@ import jdk.internal.classfile.instruction.InvokeInstruction;
*/ */
public class ExampleGallery { public class ExampleGallery {
public byte[] changeClassVersion(ClassModel cm) { public byte[] changeClassVersion(ClassModel cm) {
return cm.transform((cb, ce) -> { return Classfile.of().transform(cm, (cb, ce) -> {
switch (ce) { switch (ce) {
case ClassfileVersion cv -> cb.withVersion(57, 0); case ClassfileVersion cv -> cb.withVersion(57, 0);
default -> cb.with(ce); default -> cb.with(ce);
@ -73,7 +73,7 @@ public class ExampleGallery {
} }
public byte[] incrementClassVersion(ClassModel cm) { public byte[] incrementClassVersion(ClassModel cm) {
return cm.transform((cb, ce) -> { return Classfile.of().transform(cm, (cb, ce) -> {
switch (ce) { switch (ce) {
case ClassfileVersion cv -> cb.withVersion(cv.majorVersion() + 1, 0); case ClassfileVersion cv -> cb.withVersion(cv.majorVersion() + 1, 0);
default -> cb.with(ce); default -> cb.with(ce);
@ -82,7 +82,7 @@ public class ExampleGallery {
} }
public byte[] changeSuperclass(ClassModel cm, ClassDesc superclass) { public byte[] changeSuperclass(ClassModel cm, ClassDesc superclass) {
return cm.transform((cb, ce) -> { return Classfile.of().transform(cm, (cb, ce) -> {
switch (ce) { switch (ce) {
case Superclass sc -> cb.withSuperclass(superclass); case Superclass sc -> cb.withSuperclass(superclass);
default -> cb.with(ce); default -> cb.with(ce);
@ -91,11 +91,11 @@ public class ExampleGallery {
} }
public byte[] overrideSuperclass(ClassModel cm, ClassDesc superclass) { public byte[] overrideSuperclass(ClassModel cm, ClassDesc superclass) {
return cm.transform(ClassTransform.endHandler(cb -> cb.withSuperclass(superclass))); return Classfile.of().transform(cm, ClassTransform.endHandler(cb -> cb.withSuperclass(superclass)));
} }
public byte[] removeInterface(ClassModel cm, String internalName) { public byte[] removeInterface(ClassModel cm, String internalName) {
return cm.transform((cb, ce) -> { return Classfile.of().transform(cm, (cb, ce) -> {
switch (ce) { switch (ce) {
case Interfaces i -> cb.withInterfaces(i.interfaces().stream() case Interfaces i -> cb.withInterfaces(i.interfaces().stream()
.filter(e -> !e.asInternalName().equals(internalName)) .filter(e -> !e.asInternalName().equals(internalName))
@ -106,7 +106,7 @@ public class ExampleGallery {
} }
public byte[] addInterface(ClassModel cm, ClassDesc newIntf) { public byte[] addInterface(ClassModel cm, ClassDesc newIntf) {
return cm.transform(ClassTransform.ofStateful(() -> new ClassTransform() { return Classfile.of().transform(cm, ClassTransform.ofStateful(() -> new ClassTransform() {
boolean seen = false; boolean seen = false;
@Override @Override
@ -135,7 +135,7 @@ public class ExampleGallery {
} }
public byte[] addInterface1(ClassModel cm, ClassDesc newIntf) { public byte[] addInterface1(ClassModel cm, ClassDesc newIntf) {
return cm.transform(ClassTransform.ofStateful(() -> new ClassTransform() { return Classfile.of().transform(cm, ClassTransform.ofStateful(() -> new ClassTransform() {
Interfaces interfaces; Interfaces interfaces;
@Override @Override
@ -162,11 +162,11 @@ public class ExampleGallery {
} }
public byte[] removeSignature(ClassModel cm) { public byte[] removeSignature(ClassModel cm) {
return cm.transform(ClassTransform.dropping(e -> e instanceof SignatureAttribute)); return Classfile.of().transform(cm, ClassTransform.dropping(e -> e instanceof SignatureAttribute));
} }
public byte[] changeSignature(ClassModel cm) { public byte[] changeSignature(ClassModel cm) {
return cm.transform((cb, ce) -> { return Classfile.of().transform(cm, (cb, ce) -> {
switch (ce) { switch (ce) {
case SignatureAttribute sa -> { case SignatureAttribute sa -> {
String result = sa.signature().stringValue(); String result = sa.signature().stringValue();
@ -178,7 +178,7 @@ public class ExampleGallery {
} }
public byte[] setSignature(ClassModel cm) { public byte[] setSignature(ClassModel cm) {
return cm.transform(ClassTransform.dropping(e -> e instanceof SignatureAttribute) return Classfile.of().transform(cm, ClassTransform.dropping(e -> e instanceof SignatureAttribute)
.andThen(ClassTransform.endHandler(b -> b.with(SignatureAttribute.of( .andThen(ClassTransform.endHandler(b -> b.with(SignatureAttribute.of(
ClassSignature.of( ClassSignature.of(
ClassTypeSig.of(ClassDesc.of("impl.Fox"), ClassTypeSig.of(ClassDesc.of("impl.Fox"),
@ -189,16 +189,16 @@ public class ExampleGallery {
// @@@ strip annos (class, all) // @@@ strip annos (class, all)
public byte[] stripFields(ClassModel cm, Predicate<String> filter) { public byte[] stripFields(ClassModel cm, Predicate<String> filter) {
return cm.transform(ClassTransform.dropping(e -> e instanceof FieldModel fm return Classfile.of().transform(cm, ClassTransform.dropping(e -> e instanceof FieldModel fm
&& filter.test(fm.fieldName().stringValue()))); && filter.test(fm.fieldName().stringValue())));
} }
public byte[] addField(ClassModel cm) { public byte[] addField(ClassModel cm) {
return cm.transform(ClassTransform.endHandler(cb -> cb.withField("cool", ClassDesc.ofDescriptor("(I)D"), Classfile.ACC_PUBLIC))); return Classfile.of().transform(cm, ClassTransform.endHandler(cb -> cb.withField("cool", ClassDesc.ofDescriptor("(I)D"), Classfile.ACC_PUBLIC)));
} }
public byte[] changeFieldSig(ClassModel cm) { public byte[] changeFieldSig(ClassModel cm) {
return cm.transform(ClassTransform.transformingFields((fb, fe) -> { return Classfile.of().transform(cm, ClassTransform.transformingFields((fb, fe) -> {
if (fe instanceof SignatureAttribute sa) if (fe instanceof SignatureAttribute sa)
fb.with(SignatureAttribute.of(Signature.parseFrom(sa.signature().stringValue().replace("this/", "that/")))); fb.with(SignatureAttribute.of(Signature.parseFrom(sa.signature().stringValue().replace("this/", "that/"))));
else else
@ -207,7 +207,7 @@ public class ExampleGallery {
} }
public byte[] changeFieldFlags(ClassModel cm) { public byte[] changeFieldFlags(ClassModel cm) {
return cm.transform(ClassTransform.transformingFields((fb, fe) -> { return Classfile.of().transform(cm, ClassTransform.transformingFields((fb, fe) -> {
switch (fe) { switch (fe) {
case AccessFlags a -> fb.with(AccessFlags.ofField(a.flagsMask() & ~Classfile.ACC_PUBLIC & ~Classfile.ACC_PROTECTED)); case AccessFlags a -> fb.with(AccessFlags.ofField(a.flagsMask() & ~Classfile.ACC_PUBLIC & ~Classfile.ACC_PROTECTED));
default -> fb.with(fe); default -> fb.with(fe);
@ -216,7 +216,7 @@ public class ExampleGallery {
} }
public byte[] addException(ClassModel cm, ClassDesc ex) { public byte[] addException(ClassModel cm, ClassDesc ex) {
return cm.transform(ClassTransform.transformingMethods( return Classfile.of().transform(cm, ClassTransform.transformingMethods(
MethodTransform.ofStateful(() -> new MethodTransform() { MethodTransform.ofStateful(() -> new MethodTransform() {
ExceptionsAttribute attr; ExceptionsAttribute attr;
@ -260,11 +260,11 @@ public class ExampleGallery {
} }
}); });
return cm.transform(ClassTransform.transformingMethodBodies(transform)); return Classfile.of().transform(cm, ClassTransform.transformingMethodBodies(transform));
} }
public byte[] addInstrumentationBeforeInvoke(ClassModel cm) { public byte[] addInstrumentationBeforeInvoke(ClassModel cm) {
return cm.transform(ClassTransform.transformingMethodBodies((codeB, codeE) -> { return Classfile.of().transform(cm, ClassTransform.transformingMethodBodies((codeB, codeE) -> {
switch (codeE) { switch (codeE) {
case InvokeInstruction i -> { case InvokeInstruction i -> {
codeB.nopInstruction(); codeB.nopInstruction();
@ -276,7 +276,7 @@ public class ExampleGallery {
} }
public byte[] replaceIntegerConstant(ClassModel cm) { public byte[] replaceIntegerConstant(ClassModel cm) {
return cm.transform(ClassTransform.transformingMethodBodies((codeB, codeE) -> { return Classfile.of().transform(cm, ClassTransform.transformingMethodBodies((codeB, codeE) -> {
switch (codeE) { switch (codeE) {
case ConstantInstruction ci -> { case ConstantInstruction ci -> {
if (ci.constantValue() instanceof Integer i) codeB.constantInstruction(i + 1); if (ci.constantValue() instanceof Integer i) codeB.constantInstruction(i + 1);

View File

@ -54,7 +54,7 @@ public class ExperimentalTransformExamples {
}; };
public byte[] deleteAnnotations(ClassModel cm) { public byte[] deleteAnnotations(ClassModel cm) {
return cm.transform((cb, ce) -> { return Classfile.of().transform(cm, (cb, ce) -> {
switch (ce) { switch (ce) {
case MethodModel m -> cb.transformMethod(m, dropMethodAnnos); case MethodModel m -> cb.transformMethod(m, dropMethodAnnos);
case FieldModel f -> cb.transformField(f, dropFieldAnnos); case FieldModel f -> cb.transformField(f, dropFieldAnnos);

View File

@ -53,7 +53,7 @@ public class ModuleExamples {
private static final FileSystem JRT = FileSystems.getFileSystem(URI.create("jrt:/")); private static final FileSystem JRT = FileSystems.getFileSystem(URI.create("jrt:/"));
public void examineModule() throws IOException { public void examineModule() throws IOException {
ClassModel cm = Classfile.parse(JRT.getPath("modules/java.base/module-info.class")); ClassModel cm = Classfile.of().parse(JRT.getPath("modules/java.base/module-info.class"));
System.out.println("Is JVMS $4.7 compatible module-info: " + cm.isModuleInfo()); System.out.println("Is JVMS $4.7 compatible module-info: " + cm.isModuleInfo());
ModuleAttribute ma = cm.findAttribute(Attributes.MODULE).orElseThrow(); ModuleAttribute ma = cm.findAttribute(Attributes.MODULE).orElseThrow();
@ -78,7 +78,8 @@ public class ModuleExamples {
}); });
// Build it // Build it
byte[] moduleInfo = Classfile.buildModule(ModuleAttribute.of(moduleName, handler), clb -> { var cc = Classfile.of();
byte[] moduleInfo = cc.buildModule(ModuleAttribute.of(moduleName, handler), clb -> {
// Add an annotation to the module // Add an annotation to the module
clb.with(RuntimeVisibleAnnotationsAttribute.of(Annotation.of(ClassDesc.ofDescriptor("Ljava/lang/Deprecated;"), clb.with(RuntimeVisibleAnnotationsAttribute.of(Annotation.of(ClassDesc.ofDescriptor("Ljava/lang/Deprecated;"),
@ -87,7 +88,7 @@ public class ModuleExamples {
}); });
// Examine it // Examine it
ClassModel mm = Classfile.parse(moduleInfo); ClassModel mm = cc.parse(moduleInfo);
System.out.println("Is module info?: " + mm.isModuleInfo()); System.out.println("Is module info?: " + mm.isModuleInfo());
} }
} }

View File

@ -28,6 +28,7 @@
* @summary Testing Classfile TransformExamples compilation. * @summary Testing Classfile TransformExamples compilation.
* @compile TransformExamples.java * @compile TransformExamples.java
*/ */
import jdk.internal.classfile.Classfile;
import jdk.internal.classfile.ClassModel; import jdk.internal.classfile.ClassModel;
import jdk.internal.classfile.ClassTransform; import jdk.internal.classfile.ClassTransform;
import jdk.internal.classfile.FieldModel; import jdk.internal.classfile.FieldModel;
@ -39,18 +40,18 @@ import jdk.internal.classfile.Attribute;
*/ */
public class TransformExamples { public class TransformExamples {
public byte[] noop(ClassModel cm) { public byte[] noop(ClassModel cm) {
return cm.transform(ClassTransform.ACCEPT_ALL); return Classfile.of().transform(cm, ClassTransform.ACCEPT_ALL);
} }
public byte[] deleteAllMethods(ClassModel cm) { public byte[] deleteAllMethods(ClassModel cm) {
return cm.transform((b, e) -> { return Classfile.of().transform(cm, (b, e) -> {
if (!(e instanceof MethodModel)) if (!(e instanceof MethodModel))
b.with(e); b.with(e);
}); });
} }
public byte[] deleteFieldsWithDollarInName(ClassModel cm) { public byte[] deleteFieldsWithDollarInName(ClassModel cm) {
return cm.transform((b, e) -> return Classfile.of().transform(cm, (b, e) ->
{ {
if (!(e instanceof FieldModel fm && fm.fieldName().stringValue().contains("$"))) if (!(e instanceof FieldModel fm && fm.fieldName().stringValue().contains("$")))
b.with(e); b.with(e);
@ -58,14 +59,14 @@ public class TransformExamples {
} }
public byte[] deleteAttributes(ClassModel cm) { public byte[] deleteAttributes(ClassModel cm) {
return cm.transform((b, e) -> { return Classfile.of().transform(cm, (b, e) -> {
if (!(e instanceof Attribute)) if (!(e instanceof Attribute))
b.with(e); b.with(e);
}); });
} }
public byte[] keepMethodsAndFields(ClassModel cm) { public byte[] keepMethodsAndFields(ClassModel cm) {
return cm.transform((b, e) -> { return Classfile.of().transform(cm, (b, e) -> {
if (e instanceof MethodModel || e instanceof FieldModel) if (e instanceof MethodModel || e instanceof FieldModel)
b.with(e); b.with(e);
}); });

Some files were not shown because too many files have changed in this diff Show More