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:
parent
79c056ec8b
commit
990e3a700d
@ -1590,8 +1590,8 @@ public final class Module implements AnnotatedElement {
|
||||
*/
|
||||
private Class<?> loadModuleInfoClass(InputStream in) throws IOException {
|
||||
final String MODULE_INFO = "module-info";
|
||||
byte[] bytes = Classfile.parse(in.readAllBytes(),
|
||||
Classfile.Option.constantPoolSharing(false)).transform((clb, cle) -> {
|
||||
var cc = Classfile.of(Classfile.ConstantPoolSharingOption.NEW_POOL);
|
||||
byte[] bytes = cc.transform(cc.parse(in.readAllBytes()), (clb, cle) -> {
|
||||
switch (cle) {
|
||||
case AccessFlags af -> clb.withFlags(AccessFlag.INTERFACE,
|
||||
AccessFlag.ABSTRACT, AccessFlag.SYNTHETIC);
|
||||
|
@ -48,10 +48,8 @@ import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
public interface ClassHierarchyResolver {
|
||||
|
||||
/**
|
||||
* Returns a default instance of {@linkplain ClassHierarchyResolver}
|
||||
* that reads from system class loader with
|
||||
* {@link ClassLoader#getSystemResourceAsStream(String)} and falls
|
||||
* back to reflection if a class is not found.
|
||||
* Returns a default instance of {@linkplain ClassHierarchyResolver} that
|
||||
* gets {@link ClassHierarchyInfo} from system class loader with reflection.
|
||||
*/
|
||||
static ClassHierarchyResolver defaultResolver() {
|
||||
return ClassHierarchyImpl.DEFAULT_RESOLVER;
|
||||
|
@ -72,25 +72,6 @@ public sealed interface ClassModel
|
||||
/** {@return the interfaces implemented by this class} */
|
||||
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} */
|
||||
boolean isModuleInfo();
|
||||
|
||||
|
@ -52,7 +52,7 @@ public sealed interface ClassReader extends ConstantPool
|
||||
|
||||
/**
|
||||
* {@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();
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -32,7 +32,7 @@ import jdk.internal.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
|
||||
/**
|
||||
* A transformation on streams of elements. Transforms are used during
|
||||
* 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.
|
||||
*
|
||||
* <p>The subtypes of {@linkplain
|
||||
|
@ -39,7 +39,7 @@ import jdk.internal.classfile.impl.AbstractPseudoInstruction;
|
||||
* between instructions and labels. Pseudo-instructions are delivered as part
|
||||
* of the element stream of a {@link CodeModel}. Delivery of some
|
||||
* 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
|
||||
extends CodeElement
|
||||
|
@ -67,7 +67,7 @@ public sealed interface ConstantValueAttribute
|
||||
case Long l -> TemporaryConstantPool.INSTANCE.longEntry(l);
|
||||
case Double d -> TemporaryConstantPool.INSTANCE.doubleEntry(d);
|
||||
case String s -> TemporaryConstantPool.INSTANCE.stringEntry(s);
|
||||
default -> throw new IllegalArgumentException("Invalid ConstantValueAtrtibute value: " + value);
|
||||
default -> throw new IllegalArgumentException("Invalid ConstantValueAttribute value: " + value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ import jdk.internal.classfile.impl.UnboundAttribute;
|
||||
* the code table and line numbers in the source file.
|
||||
* 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
|
||||
* {@link jdk.internal.classfile.Classfile.Option#processLineNumbers(boolean)} option.
|
||||
* {@link jdk.internal.classfile.Classfile.LineNumbersOption} option.
|
||||
*/
|
||||
public sealed interface LineNumberTableAttribute
|
||||
extends Attribute<LineNumberTableAttribute>
|
||||
|
@ -36,7 +36,7 @@ import java.util.List;
|
||||
* variables.
|
||||
* 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
|
||||
* {@link jdk.internal.classfile.Classfile.Option#processDebug(boolean)} option.
|
||||
* {@link jdk.internal.classfile.Classfile.DebugElementsOption} option.
|
||||
*/
|
||||
public sealed interface LocalVariableTableAttribute
|
||||
extends Attribute<LocalVariableTableAttribute>
|
||||
|
@ -37,7 +37,7 @@ import java.util.List;
|
||||
* variables.
|
||||
* 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
|
||||
* {@link jdk.internal.classfile.Classfile.Option#processLineNumbers(boolean)} option.
|
||||
* {@link jdk.internal.classfile.Classfile.LineNumbersOption} option.
|
||||
*/
|
||||
public sealed interface LocalVariableTypeTableAttribute
|
||||
extends Attribute<LocalVariableTypeTableAttribute>
|
||||
|
@ -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.
|
||||
* @param context Classfile context
|
||||
* @param clm class model to re-map
|
||||
* @return re-mapped class file bytes
|
||||
*/
|
||||
default byte[] remapClass(ClassModel clm) {
|
||||
return Classfile.build(map(clm.thisClass().asSymbol()),
|
||||
clb -> clm.forEachElement(resolve(clb).consumer()));
|
||||
default byte[] remapClass(Classfile context, ClassModel clm) {
|
||||
return context.transform(clm, map(clm.thisClass().asSymbol()), this);
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import java.util.ArrayDeque;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import jdk.internal.classfile.ClassModel;
|
||||
import jdk.internal.classfile.ClassTransform;
|
||||
import jdk.internal.classfile.CodeModel;
|
||||
@ -101,9 +102,9 @@ class PackageSnippets {
|
||||
// @start region="singleClassRemap"
|
||||
var classRemapper = ClassRemapper.of(
|
||||
Map.of(ClassDesc.of("Foo"), ClassDesc.of("Bar")));
|
||||
|
||||
var cc = Classfile.of();
|
||||
for (var classModel : allMyClasses) {
|
||||
byte[] newBytes = classRemapper.remapClass(classModel);
|
||||
byte[] newBytes = classRemapper.remapClass(cc, classModel);
|
||||
|
||||
}
|
||||
// @end
|
||||
@ -113,9 +114,9 @@ class PackageSnippets {
|
||||
// @start region="allPackageRemap"
|
||||
var classRemapper = ClassRemapper.of(cd ->
|
||||
ClassDesc.ofDescriptor(cd.descriptorString().replace("Lcom/oldpackage/", "Lcom/newpackage/")));
|
||||
|
||||
var cc = Classfile.of();
|
||||
for (var classModel : allMyClasses) {
|
||||
byte[] newBytes = classRemapper.remapClass(classModel);
|
||||
byte[] newBytes = classRemapper.remapClass(cc, classModel);
|
||||
|
||||
}
|
||||
// @end
|
||||
@ -123,7 +124,9 @@ class PackageSnippets {
|
||||
|
||||
void codeLocalsShifting(ClassModel classModel) {
|
||||
// @start region="codeLocalsShifting"
|
||||
byte[] newBytes = classModel.transform((classBuilder, classElement) -> {
|
||||
byte[] newBytes = Classfile.of().transform(
|
||||
classModel,
|
||||
(classBuilder, classElement) -> {
|
||||
if (classElement instanceof MethodModel method)
|
||||
classBuilder.transformMethod(method,
|
||||
MethodTransform.transformingCode(
|
||||
@ -136,7 +139,8 @@ class PackageSnippets {
|
||||
|
||||
void codeRelabeling(ClassModel classModel) {
|
||||
// @start region="codeRelabeling"
|
||||
byte[] newBytes = classModel.transform(
|
||||
byte[] newBytes = Classfile.of().transform(
|
||||
classModel,
|
||||
ClassTransform.transformingMethodBodies(
|
||||
CodeTransform.ofStateful(CodeRelabeler::of)));
|
||||
// @end
|
||||
@ -150,7 +154,7 @@ class PackageSnippets {
|
||||
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 instrumentorClassRemapper = ClassRemapper.of(Map.of(instrumentor.thisClass().asSymbol(), target.thisClass().asSymbol()));
|
||||
return target.transform(
|
||||
return Classfile.of().transform(target,
|
||||
ClassTransform.transformingMethods(
|
||||
instrumentedMethodsFilter,
|
||||
(mb, me) -> {
|
||||
|
@ -39,7 +39,7 @@ import jdk.internal.classfile.ClassBuilder;
|
||||
import jdk.internal.classfile.ClassModel;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
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.PackageDesc;
|
||||
import jdk.internal.classfile.WritableElement;
|
||||
@ -63,29 +63,22 @@ public sealed interface ConstantPoolBuilder
|
||||
permits SplitConstantPool, TemporaryConstantPool {
|
||||
|
||||
/**
|
||||
* {@return a new constant pool builder} The new constant pool builder
|
||||
* will inherit the classfile processing options of the specified class.
|
||||
* If the processing options include {@link Classfile.Option#constantPoolSharing(boolean)},
|
||||
* (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.
|
||||
* {@return a new constant pool builder} The new constant pool builder will
|
||||
* be pre-populated with the contents of the constant pool associated with
|
||||
* the class reader.
|
||||
*
|
||||
* @param classModel the class to copy from
|
||||
*/
|
||||
static ConstantPoolBuilder of(ClassModel classModel) {
|
||||
ClassReaderImpl reader = (ClassReaderImpl) classModel.constantPool();
|
||||
return reader.options().cpSharing
|
||||
? new SplitConstantPool(reader)
|
||||
: new SplitConstantPool(reader.options());
|
||||
return new SplitConstantPool((ClassReaderImpl) classModel.constantPool());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a new constant pool builder} The new constant pool builder
|
||||
* will be empty and have the specified classfile processing options.
|
||||
*
|
||||
* @param options the processing options
|
||||
* will be empty.
|
||||
*/
|
||||
static ConstantPoolBuilder of(Collection<Classfile.Option> options) {
|
||||
return new SplitConstantPool(new Options(options));
|
||||
static ConstantPoolBuilder of() {
|
||||
return new SplitConstantPool();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,11 +30,13 @@ import jdk.internal.classfile.Attribute;
|
||||
|
||||
public class AbstractDirectBuilder<M> {
|
||||
protected final SplitConstantPool constantPool;
|
||||
protected final ClassfileImpl context;
|
||||
protected final AttributeHolder attributes = new AttributeHolder();
|
||||
protected M original;
|
||||
|
||||
public AbstractDirectBuilder(SplitConstantPool constantPool) {
|
||||
public AbstractDirectBuilder(SplitConstantPool constantPool, ClassfileImpl context) {
|
||||
this.constantPool = constantPool;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public SplitConstantPool constantPool() {
|
||||
|
@ -30,6 +30,8 @@ import jdk.internal.classfile.constantpool.*;
|
||||
import java.lang.constant.ConstantDesc;
|
||||
import java.util.List;
|
||||
|
||||
import static jdk.internal.classfile.Classfile.*;
|
||||
|
||||
public final class AnnotationImpl implements Annotation {
|
||||
private final Utf8Entry className;
|
||||
private final List<AnnotationElement> elements;
|
||||
@ -113,7 +115,7 @@ public final class AnnotationImpl implements Annotation {
|
||||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 's';
|
||||
return AEV_STRING;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -127,7 +129,7 @@ public final class AnnotationImpl implements Annotation {
|
||||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'D';
|
||||
return AEV_DOUBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -141,7 +143,7 @@ public final class AnnotationImpl implements Annotation {
|
||||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'F';
|
||||
return AEV_FLOAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -155,7 +157,7 @@ public final class AnnotationImpl implements Annotation {
|
||||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'J';
|
||||
return AEV_LONG;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -169,7 +171,7 @@ public final class AnnotationImpl implements Annotation {
|
||||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'I';
|
||||
return AEV_INT;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -183,7 +185,7 @@ public final class AnnotationImpl implements Annotation {
|
||||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'S';
|
||||
return AEV_SHORT;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -197,7 +199,7 @@ public final class AnnotationImpl implements Annotation {
|
||||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'C';
|
||||
return AEV_CHAR;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -211,7 +213,7 @@ public final class AnnotationImpl implements Annotation {
|
||||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'B';
|
||||
return AEV_BYTE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -225,7 +227,7 @@ public final class AnnotationImpl implements Annotation {
|
||||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'Z';
|
||||
return AEV_BOOLEAN;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -243,7 +245,7 @@ public final class AnnotationImpl implements Annotation {
|
||||
|
||||
@Override
|
||||
public char tag() {
|
||||
return '[';
|
||||
return AEV_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -258,7 +260,7 @@ public final class AnnotationImpl implements Annotation {
|
||||
implements AnnotationValue.OfEnum {
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'e';
|
||||
return AEV_ENUM;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -274,7 +276,7 @@ public final class AnnotationImpl implements Annotation {
|
||||
implements AnnotationValue.OfAnnotation {
|
||||
@Override
|
||||
public char tag() {
|
||||
return '@';
|
||||
return AEV_ANNOTATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -289,7 +291,7 @@ public final class AnnotationImpl implements Annotation {
|
||||
implements AnnotationValue.OfClass {
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'c';
|
||||
return AEV_CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -58,19 +58,19 @@ class AnnotationReader {
|
||||
char tag = (char) classReader.readU1(p);
|
||||
++p;
|
||||
return switch (tag) {
|
||||
case 'B' -> new AnnotationImpl.OfByteImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case 'C' -> new AnnotationImpl.OfCharacterImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case 'D' -> new AnnotationImpl.OfDoubleImpl((DoubleEntry)classReader.readEntry(p));
|
||||
case 'F' -> new AnnotationImpl.OfFloatImpl((FloatEntry)classReader.readEntry(p));
|
||||
case 'I' -> new AnnotationImpl.OfIntegerImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case 'J' -> new AnnotationImpl.OfLongImpl((LongEntry)classReader.readEntry(p));
|
||||
case 'S' -> new AnnotationImpl.OfShortImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case 'Z' -> new AnnotationImpl.OfBooleanImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case 's' -> new AnnotationImpl.OfStringImpl(classReader.readUtf8Entry(p));
|
||||
case 'e' -> new AnnotationImpl.OfEnumImpl(classReader.readUtf8Entry(p), classReader.readUtf8Entry(p + 2));
|
||||
case 'c' -> new AnnotationImpl.OfClassImpl(classReader.readUtf8Entry(p));
|
||||
case '@' -> new AnnotationImpl.OfAnnotationImpl(readAnnotation(classReader, p));
|
||||
case '[' -> {
|
||||
case AEV_BYTE -> new AnnotationImpl.OfByteImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case AEV_CHAR -> new AnnotationImpl.OfCharacterImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case AEV_DOUBLE -> new AnnotationImpl.OfDoubleImpl((DoubleEntry)classReader.readEntry(p));
|
||||
case AEV_FLOAT -> new AnnotationImpl.OfFloatImpl((FloatEntry)classReader.readEntry(p));
|
||||
case AEV_INT -> new AnnotationImpl.OfIntegerImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case AEV_LONG -> new AnnotationImpl.OfLongImpl((LongEntry)classReader.readEntry(p));
|
||||
case AEV_SHORT -> new AnnotationImpl.OfShortImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case AEV_BOOLEAN -> new AnnotationImpl.OfBooleanImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case AEV_STRING -> new AnnotationImpl.OfStringImpl(classReader.readUtf8Entry(p));
|
||||
case AEV_ENUM -> new AnnotationImpl.OfEnumImpl(classReader.readUtf8Entry(p), classReader.readUtf8Entry(p + 2));
|
||||
case AEV_CLASS -> new AnnotationImpl.OfClassImpl(classReader.readUtf8Entry(p));
|
||||
case AEV_ANNOTATION -> new AnnotationImpl.OfAnnotationImpl(readAnnotation(classReader, p));
|
||||
case AEV_ARRAY -> {
|
||||
int numValues = classReader.readU2(p);
|
||||
p += 2;
|
||||
var values = new Object[numValues];
|
||||
|
@ -144,7 +144,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
|
||||
}
|
||||
if (mapper != null) {
|
||||
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<>() {
|
||||
@Override
|
||||
public String name() {
|
||||
|
@ -39,22 +39,24 @@ import jdk.internal.classfile.constantpool.PoolEntry;
|
||||
public final class BufWriterImpl implements BufWriter {
|
||||
|
||||
private final ConstantPoolBuilder constantPool;
|
||||
private final ClassfileImpl context;
|
||||
private LabelContext labelContext;
|
||||
private final ClassEntry thisClass;
|
||||
private final int majorVersion;
|
||||
byte[] elems;
|
||||
int offset = 0;
|
||||
|
||||
public BufWriterImpl(ConstantPoolBuilder constantPool) {
|
||||
this(constantPool, 64, null, 0);
|
||||
public BufWriterImpl(ConstantPoolBuilder constantPool, ClassfileImpl context) {
|
||||
this(constantPool, context, 64, null, 0);
|
||||
}
|
||||
|
||||
public BufWriterImpl(ConstantPoolBuilder constantPool, int initialSize) {
|
||||
this(constantPool, initialSize, null, 0);
|
||||
public BufWriterImpl(ConstantPoolBuilder constantPool, ClassfileImpl context, int initialSize) {
|
||||
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.context = context;
|
||||
elems = new byte[initialSize];
|
||||
this.thisClass = thisClass;
|
||||
this.majorVersion = majorVersion;
|
||||
@ -85,6 +87,10 @@ public final class BufWriterImpl implements BufWriter {
|
||||
return majorVersion;
|
||||
}
|
||||
|
||||
public ClassfileImpl context() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeU1(int x) {
|
||||
writeIntBytes(1, x);
|
||||
|
@ -45,6 +45,7 @@ import java.util.function.Consumer;
|
||||
public final class BufferedCodeBuilder
|
||||
implements TerminalCodeBuilder, LabelContext {
|
||||
private final SplitConstantPool constantPool;
|
||||
private final ClassfileImpl context;
|
||||
private final List<CodeElement> elements = new ArrayList<>();
|
||||
private final LabelImpl startLabel, endLabel;
|
||||
private final CodeModel original;
|
||||
@ -54,8 +55,10 @@ public final class BufferedCodeBuilder
|
||||
|
||||
public BufferedCodeBuilder(MethodInfo methodInfo,
|
||||
SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
CodeModel original) {
|
||||
this.constantPool = constantPool;
|
||||
this.context = context;
|
||||
this.startLabel = new LabelImpl(this, -1);
|
||||
this.endLabel = new LabelImpl(this, -1);
|
||||
this.original = original;
|
||||
@ -204,7 +207,7 @@ public final class BufferedCodeBuilder
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -36,6 +36,7 @@ import jdk.internal.classfile.constantpool.Utf8Entry;
|
||||
public final class BufferedFieldBuilder
|
||||
implements TerminalFieldBuilder {
|
||||
private final SplitConstantPool constantPool;
|
||||
private final ClassfileImpl context;
|
||||
private final Utf8Entry name;
|
||||
private final Utf8Entry desc;
|
||||
private final List<FieldElement> elements = new ArrayList<>();
|
||||
@ -43,10 +44,12 @@ public final class BufferedFieldBuilder
|
||||
private final FieldModel original;
|
||||
|
||||
public BufferedFieldBuilder(SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
Utf8Entry name,
|
||||
Utf8Entry type,
|
||||
FieldModel original) {
|
||||
this.constantPool = constantPool;
|
||||
this.context = context;
|
||||
this.name = name;
|
||||
this.desc = type;
|
||||
this.flags = AccessFlags.ofField();
|
||||
@ -119,7 +122,7 @@ public final class BufferedFieldBuilder
|
||||
|
||||
@Override
|
||||
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);
|
||||
fb.writeTo(buf);
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ public final class BufferedMethodBuilder
|
||||
implements TerminalMethodBuilder, MethodInfo {
|
||||
private final List<MethodElement> elements;
|
||||
private final SplitConstantPool constantPool;
|
||||
private final ClassfileImpl context;
|
||||
private final Utf8Entry name;
|
||||
private final Utf8Entry desc;
|
||||
private AccessFlags flags;
|
||||
@ -55,11 +56,13 @@ public final class BufferedMethodBuilder
|
||||
MethodTypeDesc mDesc;
|
||||
|
||||
public BufferedMethodBuilder(SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
Utf8Entry nameInfo,
|
||||
Utf8Entry typeInfo,
|
||||
MethodModel original) {
|
||||
this.elements = new ArrayList<>();
|
||||
this.constantPool = constantPool;
|
||||
this.context = context;
|
||||
this.name = nameInfo;
|
||||
this.desc = typeInfo;
|
||||
this.flags = AccessFlags.ofMethod();
|
||||
@ -119,21 +122,21 @@ public final class BufferedMethodBuilder
|
||||
|
||||
@Override
|
||||
public MethodBuilder withCode(Consumer<? super CodeBuilder> handler) {
|
||||
return with(new BufferedCodeBuilder(this, constantPool, null)
|
||||
return with(new BufferedCodeBuilder(this, constantPool, context, null)
|
||||
.run(handler)
|
||||
.toModel());
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
return with(builder.toModel());
|
||||
}
|
||||
|
||||
@Override
|
||||
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) {
|
||||
@ -204,7 +207,7 @@ public final class BufferedMethodBuilder
|
||||
|
||||
@Override
|
||||
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);
|
||||
mb.writeTo(buf);
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public final class ChainedClassBuilder
|
||||
|
||||
@Override
|
||||
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)
|
||||
.run(handler)
|
||||
.toModel());
|
||||
@ -68,7 +68,7 @@ public final class ChainedClassBuilder
|
||||
|
||||
@Override
|
||||
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);
|
||||
builder.transform(field, transform);
|
||||
@ -78,7 +78,7 @@ public final class ChainedClassBuilder
|
||||
@Override
|
||||
public ClassBuilder withMethod(Utf8Entry name, Utf8Entry descriptor, int flags,
|
||||
Consumer<? super MethodBuilder> handler) {
|
||||
return downstream.with(new BufferedMethodBuilder(terminal.constantPool,
|
||||
return downstream.with(new BufferedMethodBuilder(terminal.constantPool, terminal.context,
|
||||
name, descriptor, null)
|
||||
.run(handler)
|
||||
.toModel());
|
||||
@ -86,7 +86,7 @@ public final class ChainedClassBuilder
|
||||
|
||||
@Override
|
||||
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);
|
||||
builder.transform(method, transform);
|
||||
return downstream.with(builder.toModel());
|
||||
|
@ -32,14 +32,13 @@ import java.lang.constant.ClassDesc;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import jdk.internal.classfile.ClassHierarchyResolver;
|
||||
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
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.
|
||||
@ -52,15 +51,8 @@ public final class ClassHierarchyImpl {
|
||||
static final ClassHierarchyResolver.ClassHierarchyInfo OBJECT_INFO = new ClassHierarchyInfoImpl(null, false);
|
||||
}
|
||||
|
||||
public static final ClassHierarchyResolver DEFAULT_RESOLVER = ClassHierarchyResolver
|
||||
.ofResourceParsing(ResourceParsingClassHierarchyResolver.SYSTEM_STREAM_PROVIDER)
|
||||
.orElse(new ClassLoadingClassHierarchyResolver(ClassLoadingClassHierarchyResolver.SYSTEM_CLASS_PROVIDER))
|
||||
.cached(new Supplier<>() {
|
||||
@Override
|
||||
public Map<ClassDesc, ClassHierarchyResolver.ClassHierarchyInfo> get() {
|
||||
return new ConcurrentHashMap<>();
|
||||
}
|
||||
});
|
||||
public static final ClassHierarchyResolver DEFAULT_RESOLVER =
|
||||
new ClassLoadingClassHierarchyResolver(ClassLoadingClassHierarchyResolver.SYSTEM_CLASS_PROVIDER);
|
||||
|
||||
private final ClassHierarchyResolver resolver;
|
||||
|
||||
@ -69,7 +61,10 @@ public final class ClassHierarchyImpl {
|
||||
* @param classHierarchyResolver <code>ClassHierarchyInfoResolver</code> instance
|
||||
*/
|
||||
public ClassHierarchyImpl(ClassHierarchyResolver classHierarchyResolver) {
|
||||
this.resolver = classHierarchyResolver;
|
||||
requireNonNull(classHierarchyResolver);
|
||||
this.resolver = classHierarchyResolver instanceof CachedClassHierarchyResolver
|
||||
? classHierarchyResolver
|
||||
: classHierarchyResolver.cached();
|
||||
}
|
||||
|
||||
private ClassHierarchyInfoImpl resolve(ClassDesc classDesc) {
|
||||
|
@ -63,9 +63,8 @@ public final class ClassImpl
|
||||
private List<Attribute<?>> attributes;
|
||||
private List<ClassEntry> interfaces;
|
||||
|
||||
public ClassImpl(byte[] cfbytes,
|
||||
Collection<Classfile.Option> options) {
|
||||
this.reader = new ClassReaderImpl(cfbytes, options);
|
||||
public ClassImpl(byte[] cfbytes, ClassfileImpl context) {
|
||||
this.reader = new ClassReaderImpl(cfbytes, context);
|
||||
ClassReaderImpl reader = (ClassReaderImpl) this.reader;
|
||||
int p = reader.interfacesPos;
|
||||
int icnt = reader.readU2(p);
|
||||
@ -94,6 +93,10 @@ public final class ClassImpl
|
||||
reader.setContainedClass(this);
|
||||
}
|
||||
|
||||
public int classfileLength() {
|
||||
return reader.classfileLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessFlags 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
|
||||
public List<FieldModel> fields() {
|
||||
return fields;
|
||||
|
@ -77,7 +77,7 @@ public final class ClassReaderImpl
|
||||
private final int constantPoolCount;
|
||||
private final int[] cpOffset;
|
||||
|
||||
final Options options;
|
||||
final ClassfileImpl context;
|
||||
final int interfacesPos;
|
||||
final PoolEntry[] cp;
|
||||
|
||||
@ -86,11 +86,11 @@ public final class ClassReaderImpl
|
||||
private BootstrapMethodsAttribute bootstrapMethodsAttribute;
|
||||
|
||||
ClassReaderImpl(byte[] classfileBytes,
|
||||
Collection<Classfile.Option> options) {
|
||||
ClassfileImpl context) {
|
||||
this.buffer = classfileBytes;
|
||||
this.classfileLength = classfileBytes.length;
|
||||
this.options = new Options(options);
|
||||
this.attributeMapper = this.options.attributeMapper;
|
||||
this.context = context;
|
||||
this.attributeMapper = this.context.attributeMapperOption().attributeMapper();
|
||||
if (classfileLength < 4 || readInt(0) != 0xCAFEBABE) {
|
||||
throw new IllegalArgumentException("Bad magic number");
|
||||
}
|
||||
@ -134,8 +134,8 @@ public final class ClassReaderImpl
|
||||
this.interfacesPos = p;
|
||||
}
|
||||
|
||||
public Options options() {
|
||||
return options;
|
||||
public ClassfileImpl context() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -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 {
|
||||
}
|
||||
}
|
@ -118,7 +118,7 @@ public final class CodeImpl
|
||||
if (!inflated) {
|
||||
if (labels == null)
|
||||
labels = new LabelImpl[codeLength + 1];
|
||||
if (((ClassReaderImpl)classReader).options().processLineNumbers)
|
||||
if (((ClassReaderImpl)classReader).context().lineNumbersOption() == Classfile.LineNumbersOption.PASS_LINE_NUMBERS)
|
||||
inflateLineNumbers();
|
||||
inflateJumpTargets();
|
||||
inflateTypeAnnotations();
|
||||
@ -150,6 +150,7 @@ public final class CodeImpl
|
||||
}
|
||||
},
|
||||
(SplitConstantPool)buf.constantPool(),
|
||||
((BufWriterImpl)buf).context(),
|
||||
null).writeTo(buf);
|
||||
}
|
||||
}
|
||||
@ -166,7 +167,7 @@ public final class CodeImpl
|
||||
inflateMetadata();
|
||||
boolean doLineNumbers = (lineNumbers != null);
|
||||
generateCatchTargets(consumer);
|
||||
if (((ClassReaderImpl)classReader).options().processDebug)
|
||||
if (((ClassReaderImpl)classReader).context().debugElementsOption() == Classfile.DebugElementsOption.PASS_DEBUG)
|
||||
generateDebugElements(consumer);
|
||||
for (int pos=codeStart; pos<codeEnd; ) {
|
||||
if (labels[pos - codeStart] != null)
|
||||
|
@ -61,14 +61,15 @@ public final class DirectClassBuilder
|
||||
private int sizeHint;
|
||||
|
||||
public DirectClassBuilder(SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
ClassEntry thisClass) {
|
||||
super(constantPool);
|
||||
super(constantPool, context);
|
||||
this.thisClassEntry = AbstractPoolEntry.maybeClone(constantPool, thisClass);
|
||||
this.flags = Classfile.DEFAULT_CLASS_FLAGS;
|
||||
this.superclassEntry = null;
|
||||
this.interfaceEntries = Collections.emptyList();
|
||||
this.majorVersion = Classfile.LATEST_MAJOR_VERSION;
|
||||
this.minorVersion = Classfile.LATEST_MINOR_VERSION;
|
||||
this.majorVersion = Classfile.latestMajorVersion();
|
||||
this.minorVersion = Classfile.latestMinorVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -81,13 +82,13 @@ public final class DirectClassBuilder
|
||||
public ClassBuilder withField(Utf8Entry name,
|
||||
Utf8Entry descriptor,
|
||||
Consumer<? super FieldBuilder> handler) {
|
||||
return withField(new DirectFieldBuilder(constantPool, name, descriptor, null)
|
||||
return withField(new DirectFieldBuilder(constantPool, context, name, descriptor, null)
|
||||
.run(handler));
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
builder.transform(field, transform);
|
||||
return withField(builder);
|
||||
@ -98,13 +99,13 @@ public final class DirectClassBuilder
|
||||
Utf8Entry descriptor,
|
||||
int flags,
|
||||
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));
|
||||
}
|
||||
|
||||
@Override
|
||||
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.flags().flagsMask(),
|
||||
method);
|
||||
@ -141,7 +142,7 @@ public final class DirectClassBuilder
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
void setSizeHint(int sizeHint) {
|
||||
public void setSizeHint(int sizeHint) {
|
||||
this.sizeHint = sizeHint;
|
||||
}
|
||||
|
||||
@ -166,8 +167,8 @@ public final class DirectClassBuilder
|
||||
|
||||
// We maintain two writers, and then we join them at the end
|
||||
int size = sizeHint == 0 ? 256 : sizeHint;
|
||||
BufWriter head = new BufWriterImpl(constantPool, size);
|
||||
BufWriterImpl tail = new BufWriterImpl(constantPool, size, thisClassEntry, majorVersion);
|
||||
BufWriter head = new BufWriterImpl(constantPool, context, size);
|
||||
BufWriterImpl tail = new BufWriterImpl(constantPool, context, size, thisClassEntry, majorVersion);
|
||||
|
||||
// The tail consists of fields and methods, and attributes
|
||||
// This should trigger all the CP/BSM mutation
|
||||
|
@ -41,6 +41,7 @@ import jdk.internal.classfile.Classfile;
|
||||
import jdk.internal.classfile.CodeBuilder;
|
||||
import jdk.internal.classfile.CodeElement;
|
||||
import jdk.internal.classfile.CodeModel;
|
||||
import jdk.internal.classfile.Instruction;
|
||||
import jdk.internal.classfile.Label;
|
||||
import jdk.internal.classfile.Opcode;
|
||||
import jdk.internal.classfile.TypeKind;
|
||||
@ -101,14 +102,15 @@ public final class DirectCodeBuilder
|
||||
public static Attribute<CodeAttribute> build(MethodInfo methodInfo,
|
||||
Consumer<? super CodeBuilder> handler,
|
||||
SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
CodeModel original) {
|
||||
DirectCodeBuilder cb;
|
||||
try {
|
||||
handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, original, false));
|
||||
handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, false));
|
||||
cb.buildContent();
|
||||
} catch (LabelOverflowException loe) {
|
||||
if (constantPool.options().fixJumps) {
|
||||
handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, original, true));
|
||||
if (context.shortJumpsOption() == Classfile.ShortJumpsOption.FIX_SHORT_JUMPS) {
|
||||
handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, true));
|
||||
cb.buildContent();
|
||||
}
|
||||
else
|
||||
@ -119,15 +121,16 @@ public final class DirectCodeBuilder
|
||||
|
||||
private DirectCodeBuilder(MethodInfo methodInfo,
|
||||
SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
CodeModel original,
|
||||
boolean transformFwdJumps) {
|
||||
super(constantPool);
|
||||
super(constantPool, context);
|
||||
setOriginal(original);
|
||||
this.methodInfo = methodInfo;
|
||||
this.transformFwdJumps = transformFwdJumps;
|
||||
this.transformBackJumps = constantPool.options().fixJumps;
|
||||
bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, cai.codeLength())
|
||||
: new BufWriterImpl(constantPool);
|
||||
this.transformBackJumps = context.shortJumpsOption() == Classfile.ShortJumpsOption.FIX_SHORT_JUMPS;
|
||||
bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, context, cai.codeLength())
|
||||
: new BufWriterImpl(constantPool, context);
|
||||
this.startLabel = new LabelImpl(this, 0);
|
||||
this.endLabel = new LabelImpl(this, -1);
|
||||
this.topLocal = Util.maxLocals(methodInfo.methodFlags(), methodInfo.methodTypeSymbol());
|
||||
@ -196,7 +199,7 @@ public final class DirectCodeBuilder
|
||||
int endPc = labelToBci(h.tryEnd());
|
||||
int handlerPc = labelToBci(h.handler());
|
||||
if (startPc == -1 || endPc == -1 || handlerPc == -1) {
|
||||
if (constantPool.options().filterDeadLabels) {
|
||||
if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) {
|
||||
handlersSize--;
|
||||
} else {
|
||||
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
|
||||
processDeferredLabels();
|
||||
|
||||
if (constantPool.options().processDebug) {
|
||||
if (context.debugElementsOption() == Classfile.DebugElementsOption.PASS_DEBUG) {
|
||||
if (!characterRanges.isEmpty()) {
|
||||
Attribute<?> a = new UnboundAttribute.AdHocAttribute<>(Attributes.CHARACTER_RANGE_TABLE) {
|
||||
|
||||
@ -233,7 +236,7 @@ public final class DirectCodeBuilder
|
||||
var start = labelToBci(cr.startScope());
|
||||
var end = labelToBci(cr.endScope());
|
||||
if (start == -1 || end == -1) {
|
||||
if (constantPool.options().filterDeadLabels) {
|
||||
if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) {
|
||||
crSize--;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unbound label in character range");
|
||||
@ -262,7 +265,7 @@ public final class DirectCodeBuilder
|
||||
b.writeU2(lvSize);
|
||||
for (LocalVariable l : localVariables) {
|
||||
if (!l.writeTo(b)) {
|
||||
if (constantPool.options().filterDeadLabels) {
|
||||
if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) {
|
||||
lvSize--;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unbound label in local variable type");
|
||||
@ -285,7 +288,7 @@ public final class DirectCodeBuilder
|
||||
b.writeU2(localVariableTypes.size());
|
||||
for (LocalVariableType l : localVariableTypes) {
|
||||
if (!l.writeTo(b)) {
|
||||
if (constantPool.options().filterDeadLabels) {
|
||||
if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) {
|
||||
lvtSize--;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unbound label in local variable type");
|
||||
@ -305,6 +308,44 @@ public final class DirectCodeBuilder
|
||||
}
|
||||
|
||||
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
|
||||
public void writeBody(BufWriter b) {
|
||||
BufWriterImpl buf = (BufWriterImpl) b;
|
||||
@ -318,52 +359,29 @@ public final class DirectCodeBuilder
|
||||
methodInfo.methodName().stringValue(),
|
||||
methodInfo.methodTypeSymbol().displayDescriptor()));
|
||||
}
|
||||
int maxStack, maxLocals;
|
||||
Attribute<? extends StackMapTableAttribute> stackMapAttr;
|
||||
boolean canReuseStackmaps = codeAndExceptionsMatch(codeLength);
|
||||
|
||||
if (!constantPool.options().generateStackmaps) {
|
||||
StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf);
|
||||
maxStack = cntr.maxStack();
|
||||
maxLocals = cntr.maxLocals();
|
||||
stackMapAttr = null;
|
||||
if (codeAndExceptionsMatch(codeLength)) {
|
||||
switch (context.stackMapsOption()) {
|
||||
case STACK_MAPS_WHEN_REQUIRED -> {
|
||||
attributes.withAttribute(original.findAttribute(Attributes.STACK_MAP_TABLE).orElse(null));
|
||||
writeCounters(true, buf);
|
||||
}
|
||||
else if (canReuseStackmaps) {
|
||||
maxLocals = original.maxLocals();
|
||||
maxStack = original.maxStack();
|
||||
stackMapAttr = original.findAttribute(Attributes.STACK_MAP_TABLE).orElse(null);
|
||||
case GENERATE_STACK_MAPS ->
|
||||
generateStackMaps(buf);
|
||||
case DROP_STACK_MAPS ->
|
||||
writeCounters(true, buf);
|
||||
}
|
||||
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;
|
||||
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.writeBytes(bytecodesBufWriter);
|
||||
writeExceptionHandlers(b);
|
||||
@ -377,9 +395,9 @@ public final class DirectCodeBuilder
|
||||
private final BufWriterImpl buf;
|
||||
private int lastPc, lastLine, writtenLine;
|
||||
|
||||
public DedupLineNumberTableAttribute(ConstantPoolBuilder constantPool) {
|
||||
public DedupLineNumberTableAttribute(ConstantPoolBuilder constantPool, ClassfileImpl context) {
|
||||
super(Attributes.LINE_NUMBER_TABLE);
|
||||
buf = new BufWriterImpl(constantPool);
|
||||
buf = new BufWriterImpl(constantPool, context);
|
||||
lastPc = -1;
|
||||
writtenLine = -1;
|
||||
}
|
||||
@ -424,7 +442,7 @@ public final class DirectCodeBuilder
|
||||
codeAttributesMatch = cai.codeLength == curPc()
|
||||
&& cai.compareCodeBytes(bytecodesBufWriter, 0, codeLength);
|
||||
if (codeAttributesMatch) {
|
||||
BufWriter bw = new BufWriterImpl(constantPool);
|
||||
BufWriter bw = new BufWriterImpl(constantPool, context);
|
||||
writeExceptionHandlers(bw);
|
||||
codeAttributesMatch = cai.classReader.compare(bw, 0, cai.exceptionHandlerPos, bw.size());
|
||||
}
|
||||
@ -682,7 +700,7 @@ public final class DirectCodeBuilder
|
||||
|
||||
public void setLineNumber(int lineNo) {
|
||||
if (lineNumberWriter == null)
|
||||
lineNumberWriter = new DedupLineNumberTableAttribute(constantPool);
|
||||
lineNumberWriter = new DedupLineNumberTableAttribute(constantPool, context);
|
||||
lineNumberWriter.writeLineNumber(curPc(), lineNo);
|
||||
}
|
||||
|
||||
|
@ -42,10 +42,11 @@ public final class DirectFieldBuilder
|
||||
private int flags;
|
||||
|
||||
public DirectFieldBuilder(SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
Utf8Entry name,
|
||||
Utf8Entry type,
|
||||
FieldModel original) {
|
||||
super(constantPool);
|
||||
super(constantPool, context);
|
||||
setOriginal(original);
|
||||
this.name = name;
|
||||
this.desc = type;
|
||||
|
@ -50,11 +50,12 @@ public final class DirectMethodBuilder
|
||||
MethodTypeDesc mDesc;
|
||||
|
||||
public DirectMethodBuilder(SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
Utf8Entry nameInfo,
|
||||
Utf8Entry typeInfo,
|
||||
int flags,
|
||||
MethodModel original) {
|
||||
super(constantPool);
|
||||
super(constantPool, context);
|
||||
setOriginal(original);
|
||||
this.name = nameInfo;
|
||||
this.desc = typeInfo;
|
||||
@ -105,7 +106,7 @@ public final class DirectMethodBuilder
|
||||
|
||||
@Override
|
||||
public BufferedCodeBuilder bufferedCodeBuilder(CodeModel original) {
|
||||
return new BufferedCodeBuilder(this, constantPool, original);
|
||||
return new BufferedCodeBuilder(this, constantPool, context, original);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -116,7 +117,7 @@ public final class DirectMethodBuilder
|
||||
|
||||
private MethodBuilder withCode(CodeModel original,
|
||||
Consumer<? super CodeBuilder> handler) {
|
||||
var cb = DirectCodeBuilder.build(this, handler, constantPool, original);
|
||||
var cb = DirectCodeBuilder.build(this, handler, constantPool, context, original);
|
||||
writeAttribute(cb);
|
||||
return this;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -27,7 +27,6 @@ package jdk.internal.classfile.impl;
|
||||
import java.lang.constant.ConstantDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.internal.classfile.Attribute;
|
||||
@ -81,7 +80,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
|
||||
|
||||
private final ClassReaderImpl parent;
|
||||
private final int parentSize, parentBsmSize;
|
||||
final Options options;
|
||||
|
||||
private int size, bsmSize;
|
||||
private PoolEntry[] myEntries;
|
||||
@ -91,10 +89,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
|
||||
private EntryMap<BootstrapMethodEntryImpl> bsmMap;
|
||||
|
||||
public SplitConstantPool() {
|
||||
this(new Options(Collections.emptyList()));
|
||||
}
|
||||
|
||||
public SplitConstantPool(Options options) {
|
||||
this.size = 1;
|
||||
this.bsmSize = 0;
|
||||
this.myEntries = new PoolEntry[1024];
|
||||
@ -102,12 +96,10 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
|
||||
this.parent = null;
|
||||
this.parentSize = 0;
|
||||
this.parentBsmSize = 0;
|
||||
this.options = options;
|
||||
this.doneFullScan = true;
|
||||
}
|
||||
|
||||
public SplitConstantPool(ClassReader parent) {
|
||||
this.options = ((ClassReaderImpl) parent).options;
|
||||
this.parent = (ClassReaderImpl) parent;
|
||||
this.parentSize = parent.entryCount();
|
||||
this.parentBsmSize = parent.bootstrapMethodCount();
|
||||
@ -117,18 +109,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
|
||||
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
|
||||
public int entryCount() {
|
||||
return size;
|
||||
@ -153,10 +133,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
|
||||
: myBsmEntries[index - parentBsmSize];
|
||||
}
|
||||
|
||||
public Options options() {
|
||||
return options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWriteDirect(ConstantPool other) {
|
||||
return this == other || parent == other;
|
||||
|
@ -153,6 +153,7 @@ public final class StackMapGenerator {
|
||||
(dcb.methodInfo.methodFlags() & ACC_STATIC) != 0,
|
||||
dcb.bytecodesBufWriter.asByteBuffer().slice(0, dcb.bytecodesBufWriter.size()),
|
||||
dcb.constantPool,
|
||||
dcb.context,
|
||||
dcb.handlers);
|
||||
}
|
||||
|
||||
@ -194,6 +195,7 @@ public final class StackMapGenerator {
|
||||
private final List<RawExceptionCatch> rawHandlers;
|
||||
private final ClassHierarchyImpl classHierarchy;
|
||||
private final boolean patchDeadCode;
|
||||
private final boolean filterDeadLabels;
|
||||
private List<Frame> frames;
|
||||
private final Frame currentFrame;
|
||||
private int maxStack, maxLocals;
|
||||
@ -221,6 +223,7 @@ public final class StackMapGenerator {
|
||||
boolean isStatic,
|
||||
ByteBuffer bytecode,
|
||||
SplitConstantPool cp,
|
||||
ClassfileImpl context,
|
||||
List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) {
|
||||
this.thisType = Type.referenceType(thisClass);
|
||||
this.methodName = methodName;
|
||||
@ -231,8 +234,9 @@ public final class StackMapGenerator {
|
||||
this.labelContext = labelContext;
|
||||
this.handlers = handlers;
|
||||
this.rawHandlers = new ArrayList<>(handlers.size());
|
||||
this.classHierarchy = new ClassHierarchyImpl(cp.options().classHierarchyResolver);
|
||||
this.patchDeadCode = cp.options().patchCode;
|
||||
this.classHierarchy = new ClassHierarchyImpl(context.classHierarchyResolverOption().classHierarchyResolver());
|
||||
this.patchDeadCode = context.deadCodeOption() == Classfile.DeadCodeOption.PATCH_DEAD_CODE;
|
||||
this.filterDeadLabels = context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS;
|
||||
this.currentFrame = new Frame(classHierarchy);
|
||||
generate();
|
||||
}
|
||||
@ -832,9 +836,8 @@ public final class StackMapGenerator {
|
||||
methodDesc.parameterList().stream().map(ClassDesc::displayName).collect(Collectors.joining(","))));
|
||||
//try to attach debug info about corrupted bytecode to the message
|
||||
try {
|
||||
//clone SplitConstantPool with alternate Options
|
||||
var newCp = new SplitConstantPool(cp, new Options(List.of(Classfile.Option.generateStackmap(false))));
|
||||
var clb = new DirectClassBuilder(newCp, newCp.classEntry(ClassDesc.of("FakeClass")));
|
||||
var cc = Classfile.of();
|
||||
var clm = cc.parse(cc.build(cp.classEntry(thisType.sym()), cp, clb ->
|
||||
clb.withMethod(methodName, methodDesc, isStatic ? ACC_STATIC : 0, mb ->
|
||||
((DirectMethodBuilder)mb).writeAttribute(new UnboundAttribute.AdHocAttribute<CodeAttribute>(Attributes.CODE) {
|
||||
@Override
|
||||
@ -846,8 +849,8 @@ public final class StackMapGenerator {
|
||||
b.writeU2(0);//exception handlers
|
||||
b.writeU2(0);//attributes
|
||||
}
|
||||
}));
|
||||
ClassPrinter.toYaml(Classfile.parse(clb.build()).methods().get(0).code().get(), ClassPrinter.Verbosity.TRACE_ALL, sb::append);
|
||||
}))));
|
||||
ClassPrinter.toYaml(clm.methods().get(0).code().get(), ClassPrinter.Verbosity.TRACE_ALL, sb::append);
|
||||
} catch (Error | Exception suppresed) {
|
||||
//fallback to bytecode hex dump
|
||||
bytecode.rewind();
|
||||
@ -931,7 +934,7 @@ public final class StackMapGenerator {
|
||||
for (var exhandler : rawHandlers) try {
|
||||
offsets.set(exhandler.handler());
|
||||
} catch (IllegalArgumentException iae) {
|
||||
if (!cp.options().filterDeadLabels)
|
||||
if (!filterDeadLabels)
|
||||
generatorError("Detected exception handler out of bytecode range");
|
||||
}
|
||||
return offsets;
|
||||
|
@ -37,7 +37,7 @@ import jdk.internal.classfile.impl.BoundCharacterRange;
|
||||
* A pseudo-instruction which models a single entry in the
|
||||
* {@link CharacterRangeTableAttribute}. Delivered as a {@link CodeElement}
|
||||
* 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
|
||||
permits AbstractPseudoInstruction.UnboundCharacterRange, BoundCharacterRange {
|
||||
|
@ -136,7 +136,7 @@ public sealed interface ConstantInstruction extends Instruction {
|
||||
static ArgumentConstantInstruction ofArgument(Opcode op, int value) {
|
||||
Util.checkKind(op, Opcode.Kind.CONSTANT);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -150,7 +150,7 @@ public sealed interface ConstantInstruction extends Instruction {
|
||||
static LoadConstantInstruction ofLoad(Opcode op, LoadableConstantEntry constant) {
|
||||
Util.checkKind(op, Opcode.Kind.CONSTANT);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ import jdk.internal.classfile.impl.LineNumberImpl;
|
||||
* A pseudo-instruction which models a single entry in the
|
||||
* {@link LineNumberTableAttribute}. Delivered as a {@link CodeElement}
|
||||
* 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
|
||||
*/
|
||||
|
@ -42,7 +42,7 @@ import jdk.internal.classfile.impl.TemporaryConstantPool;
|
||||
* A pseudo-instruction which models a single entry in the
|
||||
* {@link LocalVariableTableAttribute}. Delivered as a {@link CodeElement}
|
||||
* 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
|
||||
*/
|
||||
|
@ -41,7 +41,7 @@ import jdk.internal.classfile.impl.TemporaryConstantPool;
|
||||
* A pseudo-instruction which models a single entry in the {@link
|
||||
* LocalVariableTypeTableAttribute}. Delivered as a {@link CodeElement} during
|
||||
* 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
|
||||
permits AbstractPseudoInstruction.UnboundLocalVariableType, BoundLocalVariableType {
|
||||
|
@ -33,10 +33,10 @@
|
||||
* <h2>Reading classfiles</h2>
|
||||
* The main class for reading classfiles is {@link jdk.internal.classfile.ClassModel}; we
|
||||
* 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>
|
||||
* {@snippet lang=java :
|
||||
* ClassModel cm = Classfile.parse(bytes);
|
||||
* ClassModel cm = Classfile.of().parse(bytes);
|
||||
* }
|
||||
* <p>
|
||||
* There are several additional overloads of {@code parse} that let you specify
|
||||
@ -164,31 +164,30 @@
|
||||
* <p>
|
||||
* For nonstandard attributes, user-provided attribute mappers can be specified
|
||||
* 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
|
||||
* jdk.internal.classfile.CustomAttribute}.
|
||||
*
|
||||
* <h3>Options</h3>
|
||||
* <p>
|
||||
* {@link jdk.internal.classfile.Classfile#parse(byte[], jdk.internal.classfile.Classfile.Option[])}
|
||||
* accepts a list of options. {@link jdk.internal.classfile.Classfile.Option} exports some
|
||||
* static boolean options, as well as factories for more complex options,
|
||||
* {@link jdk.internal.classfile.Classfile#of(jdk.internal.classfile.Classfile.Option[])}
|
||||
* accepts a list of options. {@link jdk.internal.classfile.Classfile.Option} is a base interface
|
||||
* for some statically enumerated options, as well as factories for more complex options,
|
||||
* including:
|
||||
* <ul>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.Option#generateStackmap(boolean)}
|
||||
* -- generate stackmaps (default is true)</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.Option#processDebug(boolean)}
|
||||
* -- processing of debug information, such as local variable metadata (default is true) </li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.Option#processLineNumbers(boolean)}
|
||||
* -- processing of line numbers (default is true) </li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.Option#processUnknownAttributes(boolean)}
|
||||
* -- processing of unrecognized attributes (default is true)</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.Option#constantPoolSharing(boolean)}}
|
||||
* -- share constant pool when transforming (default is true)</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.Option#classHierarchyResolver(jdk.internal.classfile.ClassHierarchyResolver)}
|
||||
* -- specify a custom class hierarchy
|
||||
* resolver used by stack map generation</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.Option#attributeMapper(java.util.function.Function)}
|
||||
* <li>{@link jdk.internal.classfile.Classfile.StackMapsOption}
|
||||
* -- generate stackmaps (default is {@code STACK_MAPS_WHEN_REQUIRED})</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.DebugElementsOption}
|
||||
* -- processing of debug information, such as local variable metadata (default is {@code PASS_DEBUG}) </li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.LineNumbersOption}
|
||||
* -- processing of line numbers (default is {@code PASS_LINE_NUMBERS}) </li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.UnknownAttributesOption}
|
||||
* -- processing of unrecognized attributes (default is {@code PASS_UNKNOWN_ATTRIBUTES})</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.ConstantPoolSharingOption}}
|
||||
* -- share constant pool when transforming (default is {@code SHARED_POOL})</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.ClassHierarchyResolverOption#of(jdk.internal.classfile.ClassHierarchyResolver)}
|
||||
* -- specify a custom class hierarchy resolver used by stack map generation</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.AttributeMapperOption#of(java.util.function.Function)}
|
||||
* -- specify format of custom attributes</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
@ -304,7 +303,8 @@
|
||||
* <p>
|
||||
* and then transform the classfile:
|
||||
* {@snippet lang=java :
|
||||
* byte[] newBytes = Classfile.parse(bytes).transform(ct);
|
||||
* var cc = Classfile.of();
|
||||
* byte[] newBytes = cc.transform(cc.parse(bytes), ct);
|
||||
* }
|
||||
* <p>
|
||||
* This is much more concise (and less error-prone) than the equivalent
|
||||
@ -322,8 +322,9 @@
|
||||
* jdk.internal.classfile.CodeTransform#andThen(jdk.internal.classfile.CodeTransform)}:
|
||||
* <p>
|
||||
* {@snippet lang=java :
|
||||
* byte[] newBytes = Classfile.parse(bytes)
|
||||
* .transform(ClassTransform.transformingMethods(
|
||||
* var cc = Classfile.of();
|
||||
* byte[] newBytes = cc.transform(cc.parse(bytes),
|
||||
* ClassTransform.transformingMethods(
|
||||
* MethodTransform.transformingCode(
|
||||
* fooToBar.andThen(instrumentCalls))));
|
||||
* }
|
||||
@ -341,7 +342,7 @@
|
||||
* attributes that are not transformed can be processed by bulk-copying their
|
||||
* bytes, rather than parsing them and regenerating their contents.) If
|
||||
* 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,
|
||||
* resulting in many unreferenced constant pool entries.
|
||||
*
|
||||
|
@ -30,6 +30,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import java.lang.reflect.AccessFlag;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
@ -62,7 +63,7 @@ import jdk.internal.classfile.instruction.StoreInstruction;
|
||||
class PackageSnippets {
|
||||
void enumerateFieldsMethods1(byte[] bytes) {
|
||||
// @start region="enumerateFieldsMethods1"
|
||||
ClassModel cm = Classfile.parse(bytes);
|
||||
ClassModel cm = Classfile.of().parse(bytes);
|
||||
for (FieldModel fm : cm.fields())
|
||||
System.out.printf("Field %s%n", fm.fieldName().stringValue());
|
||||
for (MethodModel mm : cm.methods())
|
||||
@ -72,7 +73,7 @@ class PackageSnippets {
|
||||
|
||||
void enumerateFieldsMethods2(byte[] bytes) {
|
||||
// @start region="enumerateFieldsMethods2"
|
||||
ClassModel cm = Classfile.parse(bytes);
|
||||
ClassModel cm = Classfile.of().parse(bytes);
|
||||
for (ClassElement ce : cm) {
|
||||
switch (ce) {
|
||||
case MethodModel mm -> System.out.printf("Method %s%n", mm.methodName().stringValue());
|
||||
@ -85,7 +86,7 @@ class PackageSnippets {
|
||||
|
||||
void gatherDependencies1(byte[] bytes) {
|
||||
// @start region="gatherDependencies1"
|
||||
ClassModel cm = Classfile.parse(bytes);
|
||||
ClassModel cm = Classfile.of().parse(bytes);
|
||||
Set<ClassDesc> dependencies = new HashSet<>();
|
||||
|
||||
for (ClassElement ce : cm) {
|
||||
@ -108,7 +109,7 @@ class PackageSnippets {
|
||||
|
||||
void gatherDependencies2(byte[] bytes) {
|
||||
// @start region="gatherDependencies2"
|
||||
ClassModel cm = Classfile.parse(bytes);
|
||||
ClassModel cm = Classfile.of().parse(bytes);
|
||||
Set<ClassDesc> dependencies =
|
||||
cm.elementStream()
|
||||
.flatMap(ce -> ce instanceof MethodModel mm ? mm.elementStream() : Stream.empty())
|
||||
@ -126,7 +127,7 @@ class PackageSnippets {
|
||||
|
||||
void writeHelloWorld() {
|
||||
// @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.withMethod("<init>", MethodTypeDesc.of(ConstantDescs.CD_void), Classfile.ACC_PUBLIC,
|
||||
mb -> mb.withCode(
|
||||
@ -152,8 +153,8 @@ class PackageSnippets {
|
||||
|
||||
void stripDebugMethods1(byte[] bytes) {
|
||||
// @start region="stripDebugMethods1"
|
||||
ClassModel classModel = Classfile.parse(bytes);
|
||||
byte[] newBytes = Classfile.build(classModel.thisClass().asSymbol(),
|
||||
ClassModel classModel = Classfile.of().parse(bytes);
|
||||
byte[] newBytes = Classfile.of().build(classModel.thisClass().asSymbol(),
|
||||
classBuilder -> {
|
||||
for (ClassElement ce : classModel) {
|
||||
if (!(ce instanceof MethodModel mm
|
||||
@ -170,7 +171,8 @@ class PackageSnippets {
|
||||
if (!(element instanceof MethodModel mm && mm.methodName().stringValue().startsWith("debug")))
|
||||
builder.with(element);
|
||||
};
|
||||
byte[] newBytes = Classfile.parse(bytes).transform(ct);
|
||||
var cc = Classfile.of();
|
||||
byte[] newBytes = cc.transform(cc.parse(bytes), ct);
|
||||
// @end
|
||||
}
|
||||
|
||||
@ -202,7 +204,7 @@ class PackageSnippets {
|
||||
|
||||
void fooToBarUnrolled(ClassModel classModel) {
|
||||
// @start region="fooToBarUnrolled"
|
||||
byte[] newBytes = Classfile.build(classModel.thisClass().asSymbol(),
|
||||
byte[] newBytes = Classfile.of().build(classModel.thisClass().asSymbol(),
|
||||
classBuilder -> {
|
||||
for (ClassElement ce : classModel) {
|
||||
if (ce instanceof MethodModel mm) {
|
||||
@ -234,7 +236,7 @@ class PackageSnippets {
|
||||
|
||||
void codeRelabeling(ClassModel classModel) {
|
||||
// @start region="codeRelabeling"
|
||||
byte[] newBytes = classModel.transform(
|
||||
byte[] newBytes = Classfile.of().transform(classModel,
|
||||
ClassTransform.transformingMethodBodies(
|
||||
CodeTransform.ofStateful(CodeRelabeler::of)));
|
||||
// @end
|
||||
@ -244,11 +246,11 @@ class PackageSnippets {
|
||||
byte[] classInstrumentation(ClassModel target, ClassModel instrumentor, Predicate<MethodModel> instrumentedMethodsFilter) {
|
||||
var instrumentorCodeMap = instrumentor.methods().stream()
|
||||
.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 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()));
|
||||
return target.transform(
|
||||
return Classfile.of().transform(target,
|
||||
ClassTransform.transformingMethods(
|
||||
instrumentedMethodsFilter,
|
||||
(mb, me) -> {
|
||||
@ -266,13 +268,13 @@ class PackageSnippets {
|
||||
&& mm.methodType().stringValue().equals(inv.type().stringValue())) {
|
||||
|
||||
//store stacked method parameters into locals
|
||||
var storeStack = new LinkedList<StoreInstruction>();
|
||||
var storeStack = new ArrayDeque<StoreInstruction>();
|
||||
int slot = 0;
|
||||
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()) {
|
||||
var tk = TypeKind.from(pt);
|
||||
storeStack.addFirst(StoreInstruction.of(tk, slot));
|
||||
storeStack.push(StoreInstruction.of(tk, slot));
|
||||
slot += tk.slotSize();
|
||||
}
|
||||
storeStack.forEach(codeBuilder::with);
|
||||
|
@ -186,7 +186,7 @@ public class BindingSpecializer {
|
||||
private static byte[] specializeHelper(MethodType leafType, MethodType callerMethodType,
|
||||
CallingSequence callingSequence, ABIDescriptor abi) {
|
||||
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.withSuperclass(CD_Object);
|
||||
clb.withVersion(CLASSFILE_VERSION, 0);
|
||||
@ -207,7 +207,7 @@ public class BindingSpecializer {
|
||||
}
|
||||
|
||||
if (PERFORM_VERIFICATION) {
|
||||
List<VerifyError> errors = Classfile.parse(bytes).verify(null);
|
||||
List<VerifyError> errors = Classfile.of().parse(bytes).verify(null);
|
||||
if (!errors.isEmpty()) {
|
||||
errors.forEach(System.err::println);
|
||||
throw new IllegalStateException("Verification error(s)");
|
||||
|
@ -150,9 +150,10 @@ public final class ModuleInfoExtender {
|
||||
* be discarded.
|
||||
*/
|
||||
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;
|
||||
return cm.transform(ClassTransform.endHandler(clb -> {
|
||||
return cc.transform(cm, ClassTransform.endHandler(clb -> {
|
||||
// ModuleMainClass attribute
|
||||
if (mainClass != null) {
|
||||
clb.with(ModuleMainClassAttribute.of(ClassDesc.of(mainClass)));
|
||||
|
@ -166,7 +166,7 @@ final class FingerPrint {
|
||||
}
|
||||
|
||||
private static ClassAttributes getClassAttributes(byte[] bytes) {
|
||||
var cm = Classfile.parse(bytes);
|
||||
var cm = Classfile.of().parse(bytes);
|
||||
ClassAttributes attrs = new ClassAttributes(
|
||||
cm.flags(),
|
||||
cm.thisClass().asInternalName(),
|
||||
|
@ -368,7 +368,7 @@ class JImageTask {
|
||||
if (name.endsWith(".class") && !name.endsWith("module-info.class")) {
|
||||
try {
|
||||
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 (me instanceof CodeModel com) com.forEachElement(coe -> {
|
||||
//do nothing here, just visit each model element
|
||||
|
@ -88,7 +88,7 @@ public abstract class AbstractPlugin implements Plugin {
|
||||
ClassModel newClassReader(String path, ResourcePoolEntry resource, Classfile.Option... options) {
|
||||
byte[] content = resource.contentBytes();
|
||||
try {
|
||||
return Classfile.parse(content, options);
|
||||
return Classfile.of(options).parse(content);
|
||||
} catch (Exception e) {
|
||||
if (JlinkTask.DEBUG) {
|
||||
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) {
|
||||
try {
|
||||
return Classfile.parse(buf, options);
|
||||
return Classfile.of(options).parse(buf);
|
||||
} catch (Exception e) {
|
||||
if (JlinkTask.DEBUG) {
|
||||
System.err.printf("Failed to parse class file: %s\n", path);
|
||||
|
@ -64,9 +64,10 @@ public final class StripJavaDebugAttributesPlugin extends AbstractPlugin {
|
||||
if (path.endsWith("module-info.class")) {
|
||||
// XXX. Do we have debug info?
|
||||
} else {
|
||||
byte[] content = newClassReader(path, resource,
|
||||
Classfile.Option.processDebug(false),
|
||||
Classfile.Option.processLineNumbers(false)).transform(ClassTransform
|
||||
var clm = newClassReader(path, resource,
|
||||
Classfile.DebugElementsOption.DROP_DEBUG,
|
||||
Classfile.LineNumbersOption.DROP_LINE_NUMBERS);
|
||||
byte[] content = Classfile.of().transform(clm, ClassTransform
|
||||
.dropping(cle -> cle instanceof SourceFileAttribute
|
||||
|| cle instanceof SourceDebugExtensionAttribute)
|
||||
.andThen(ClassTransform.transformingMethods(MethodTransform
|
||||
|
@ -431,7 +431,7 @@ public final class SystemModulesPlugin extends AbstractPlugin {
|
||||
boolean hasModulePackages() throws IOException {
|
||||
try (InputStream in = getInputStream()) {
|
||||
// parse module-info.class
|
||||
return Classfile.parse(in.readAllBytes()).elementStream()
|
||||
return Classfile.of().parse(in.readAllBytes()).elementStream()
|
||||
.anyMatch(e -> e instanceof ModulePackagesAttribute mpa
|
||||
&& !mpa.packages().isEmpty());
|
||||
}
|
||||
@ -579,7 +579,7 @@ public final class SystemModulesPlugin extends AbstractPlugin {
|
||||
* Generate SystemModules class
|
||||
*/
|
||||
public byte[] genClassBytes(Configuration cf) {
|
||||
return Classfile.build(classDesc,
|
||||
return Classfile.of().build(classDesc,
|
||||
clb -> {
|
||||
clb.withFlags(ACC_FINAL + ACC_SUPER)
|
||||
.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
|
||||
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,
|
||||
clb -> clb.withFlags(ACC_FINAL + ACC_SUPER)
|
||||
.withVersion(52, 0)
|
||||
|
@ -26,6 +26,7 @@
|
||||
package jdk.tools.jlink.internal.plugins;
|
||||
|
||||
import java.util.Map;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import jdk.internal.classfile.ClassTransform;
|
||||
import jdk.internal.classfile.CodeBuilder;
|
||||
import jdk.internal.classfile.CodeElement;
|
||||
@ -99,7 +100,8 @@ abstract class VersionPropsPlugin extends AbstractPlugin {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
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>"),
|
||||
new CodeTransform() {
|
||||
private CodeElement pendingLDC = null;
|
||||
|
@ -80,8 +80,9 @@ public class LocalExecutionControl extends DirectExecutionControl {
|
||||
private static final MethodTypeDesc MTD_void = MethodTypeDesc.of(ConstantDescs.CD_void);
|
||||
|
||||
private static byte[] instrument(byte[] classFile) {
|
||||
return Classfile.parse(classFile)
|
||||
.transform(ClassTransform.transformingMethodBodies((cob, coe) -> {
|
||||
var cc = Classfile.of();
|
||||
return cc.transform(cc.parse(classFile),
|
||||
ClassTransform.transformingMethodBodies((cob, coe) -> {
|
||||
if (coe instanceof BranchInstruction)
|
||||
cob.invokestatic(CD_Cancel, "stopCheck", MTD_void);
|
||||
cob.with(coe);
|
||||
@ -89,7 +90,7 @@ public class LocalExecutionControl extends DirectExecutionControl {
|
||||
}
|
||||
|
||||
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)
|
||||
.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 ->
|
||||
|
@ -1371,7 +1371,7 @@ public class ModuleDescriptorTest {
|
||||
* complete set of packages.
|
||||
*/
|
||||
public void testReadsWithBadPackageFinder() throws Exception {
|
||||
ByteBuffer bb = ByteBuffer.wrap(Classfile.buildModule(
|
||||
ByteBuffer bb = ByteBuffer.wrap(Classfile.of().buildModule(
|
||||
ModuleAttribute.of(
|
||||
ModuleDesc.of("foo"),
|
||||
mb -> mb.requires(ModuleDesc.of("java.base"), 0, null)
|
||||
|
@ -58,9 +58,10 @@ class AdaptCodeTest {
|
||||
|
||||
@Test
|
||||
void testNullAdaptIterator() throws Exception {
|
||||
ClassModel cm = Classfile.parse(testClassPath);
|
||||
var cc = Classfile.of();
|
||||
ClassModel cm = cc.parse(testClassPath);
|
||||
for (ClassTransform t : Transforms.noops) {
|
||||
byte[] newBytes = cm.transform(t);
|
||||
byte[] newBytes = cc.transform(cm, t);
|
||||
String result = (String)
|
||||
new ByteArrayClassLoader(AdaptCodeTest.class.getClassLoader(), testClassName, newBytes)
|
||||
.getMethod(testClassName, "many")
|
||||
@ -77,15 +78,17 @@ class AdaptCodeTest {
|
||||
})
|
||||
void testNullAdaptIterator2(String path) throws Exception {
|
||||
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) {
|
||||
byte[] newBytes = cm.transform(t);
|
||||
byte[] newBytes = cc.transform(cm, t);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSevenOfThirteenIterator() throws Exception {
|
||||
ClassModel cm = Classfile.parse(testClassPath);
|
||||
var cc = Classfile.of();
|
||||
ClassModel cm = cc.parse(testClassPath);
|
||||
|
||||
var transform = ClassTransform.transformingMethodBodies((codeB, 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);
|
||||
String result = (String)
|
||||
new ByteArrayClassLoader(AdaptCodeTest.class.getClassLoader(), testClassName, newBytes)
|
||||
@ -111,8 +114,9 @@ class AdaptCodeTest {
|
||||
|
||||
@Test
|
||||
void testCopy() throws Exception {
|
||||
ClassModel cm = Classfile.parse(testClassPath);
|
||||
byte[] newBytes = Classfile.build(cm.thisClass().asSymbol(), cb -> cm.forEachElement(cb));
|
||||
var cc = Classfile.of();
|
||||
ClassModel cm = cc.parse(testClassPath);
|
||||
byte[] newBytes = cc.build(cm.thisClass().asSymbol(), cb -> cm.forEachElement(cb));
|
||||
// TestUtil.writeClass(newBytes, "TestClass.class");
|
||||
String result = (String)
|
||||
new ByteArrayClassLoader(AdaptCodeTest.class.getClassLoader(), testClassName, newBytes)
|
||||
|
@ -75,8 +75,9 @@ class AdvancedTransformationsTest {
|
||||
@Test
|
||||
void testShiftLocals() throws Exception {
|
||||
try (var in = StackMapGenerator.class.getResourceAsStream("StackMapGenerator.class")) {
|
||||
var clm = Classfile.parse(in.readAllBytes());
|
||||
var remapped = Classfile.parse(clm.transform((clb, cle) -> {
|
||||
var cc = Classfile.of();
|
||||
var clm = cc.parse(in.readAllBytes());
|
||||
var remapped = cc.parse(cc.transform(clm, (clb, cle) -> {
|
||||
if (cle instanceof MethodModel mm) {
|
||||
clb.transformMethod(mm, (mb, me) -> {
|
||||
if (me instanceof CodeModel com) {
|
||||
@ -113,8 +114,9 @@ class AdvancedTransformationsTest {
|
||||
ClassDesc.ofDescriptor(StackMapGenerator.class.descriptorString()), ClassDesc.of("remapped.StackMapGenerator")
|
||||
);
|
||||
try (var in = StackMapGenerator.class.getResourceAsStream("StackMapGenerator.class")) {
|
||||
var clm = Classfile.parse(in.readAllBytes());
|
||||
var remapped = Classfile.parse(ClassRemapper.of(map).remapClass(clm));
|
||||
var cc = Classfile.of();
|
||||
var clm = cc.parse(in.readAllBytes());
|
||||
var remapped = cc.parse(ClassRemapper.of(map).remapClass(cc, clm));
|
||||
assertEmpty(remapped.verify(
|
||||
ClassHierarchyResolver.of(Set.of(ClassDesc.of("remapped.List")), Map.of(
|
||||
ClassDesc.of("remapped.RemappedBytecode"), ConstantDescs.CD_Object,
|
||||
@ -167,11 +169,12 @@ class AdvancedTransformationsTest {
|
||||
void testRemapModule() throws Exception {
|
||||
var foo = ClassDesc.ofDescriptor(Foo.class.descriptorString());
|
||||
var bar = ClassDesc.ofDescriptor(Bar.class.descriptorString());
|
||||
|
||||
var ma = Classfile.parse(
|
||||
var cc = Classfile.of();
|
||||
var ma = cc.parse(
|
||||
ClassRemapper.of(Map.of(foo, bar)).remapClass(
|
||||
Classfile.parse(
|
||||
Classfile.buildModule(
|
||||
cc,
|
||||
cc.parse(
|
||||
cc.buildModule(
|
||||
ModuleAttribute.of(ModuleDesc.of("MyModule"), mab ->
|
||||
mab.uses(foo).provides(foo, foo)))))).findAttribute(Attributes.MODULE).get();
|
||||
assertEquals(ma.uses().get(0).asSymbol(), bar);
|
||||
@ -187,10 +190,11 @@ class AdvancedTransformationsTest {
|
||||
var fooAnno = ClassDesc.ofDescriptor(FooAnno.class.descriptorString());
|
||||
var barAnno = ClassDesc.ofDescriptor(BarAnno.class.descriptorString());
|
||||
var rec = ClassDesc.ofDescriptor(Rec.class.descriptorString());
|
||||
|
||||
var remapped = Classfile.parse(
|
||||
var cc = Classfile.of();
|
||||
var remapped = cc.parse(
|
||||
ClassRemapper.of(Map.of(foo, bar, fooAnno, barAnno)).remapClass(
|
||||
Classfile.parse(
|
||||
cc,
|
||||
cc.parse(
|
||||
Rec.class.getResourceAsStream(Rec.class.getName() + ".class")
|
||||
.readAllBytes())));
|
||||
var sb = new StringBuilder();
|
||||
@ -233,10 +237,11 @@ class AdvancedTransformationsTest {
|
||||
|
||||
@Test
|
||||
void testInstrumentClass() throws Exception {
|
||||
var instrumentor = Classfile.parse(AdvancedTransformationsTest.class.getResourceAsStream("AdvancedTransformationsTest$InstrumentorClass.class").readAllBytes());
|
||||
var target = Classfile.parse(AdvancedTransformationsTest.class.getResourceAsStream("AdvancedTransformationsTest$TargetClass.class").readAllBytes());
|
||||
var cc = Classfile.of();
|
||||
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"));
|
||||
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");
|
||||
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 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()));
|
||||
return target.transform(
|
||||
return Classfile.of().transform(target,
|
||||
ClassTransform.transformingMethods(
|
||||
instrumentedMethodsFilter,
|
||||
(mb, me) -> {
|
||||
|
@ -55,7 +55,7 @@ class AnnotationModelTest {
|
||||
|
||||
@Test
|
||||
void readAnnos() {
|
||||
var model = Classfile.parse(fileBytes);
|
||||
var model = Classfile.of().parse(fileBytes);
|
||||
var annotations = model.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).get().annotations();
|
||||
|
||||
assertEquals(annotations.size(), 3);
|
||||
|
@ -127,12 +127,13 @@ class AnnotationTest {
|
||||
|
||||
@Test
|
||||
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()));
|
||||
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())));
|
||||
});
|
||||
ClassModel cm = Classfile.parse(bytes);
|
||||
ClassModel cm = cc.parse(bytes);
|
||||
List<ClassElement> ces = cm.elementList();
|
||||
List<Annotation> annos = ces.stream()
|
||||
.filter(ce -> ce instanceof RuntimeVisibleAnnotationsAttribute)
|
||||
@ -172,12 +173,13 @@ class AnnotationTest {
|
||||
|
||||
@Test
|
||||
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());
|
||||
cb.withMethod("foo", MethodTypeDesc.of(CD_void), 0, mb -> mb.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<Annotation> annos = ces.stream()
|
||||
.filter(ce -> ce instanceof RuntimeVisibleAnnotationsAttribute)
|
||||
|
@ -53,7 +53,7 @@ class ArrayTest {
|
||||
|
||||
@Test
|
||||
void testArrayNew() throws Exception {
|
||||
ClassModel cm = Classfile.parse(testClassPath);
|
||||
ClassModel cm = Classfile.of().parse(testClassPath);
|
||||
|
||||
for (MethodModel mm : cm.methods()) {
|
||||
mm.code().ifPresent(code -> {
|
||||
|
@ -60,8 +60,9 @@ public class BSMTest {
|
||||
|
||||
@Test
|
||||
void testSevenOfThirteenIterator() throws Exception {
|
||||
ClassModel cm = Classfile.parse(testClassPath);
|
||||
byte[] newBytes = cm.transform((cb, ce) -> {
|
||||
var cc = Classfile.of();
|
||||
ClassModel cm = cc.parse(testClassPath);
|
||||
byte[] newBytes = cc.transform(cm, (cb, ce) -> {
|
||||
if (ce instanceof MethodModel mm) {
|
||||
cb.transformMethod(mm, (mb, me) -> {
|
||||
if (me instanceof CodeModel xm) {
|
||||
|
@ -57,8 +57,9 @@ class BasicBlockTest {
|
||||
@Test
|
||||
void testPatternsCausingBasicBlockTroubles() throws IOException {
|
||||
try (InputStream in = BasicBlockTest.class.getResourceAsStream("BasicBlockTest.class")) {
|
||||
var classModel = Classfile.parse(in.readAllBytes());
|
||||
Classfile.build(classModel.thisClass().asSymbol(), cb -> classModel.forEachElement(cb));
|
||||
var cc = Classfile.of();
|
||||
var classModel = cc.parse(in.readAllBytes());
|
||||
cc.build(classModel.thisClass().asSymbol(), cb -> classModel.forEachElement(cb));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,16 +51,17 @@ class BoundAttributeTest {
|
||||
|
||||
@Test
|
||||
void testReadMethodParametersAttributeWithoutParameterName() {
|
||||
var cc = Classfile.of();
|
||||
// build a simple method: void method(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 -> {
|
||||
mb.withCode(CodeBuilder::return_);
|
||||
// add a MethodParameters attribute without name for the parameter
|
||||
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)
|
||||
.findAttribute(Attributes.METHOD_PARAMETERS)
|
||||
.orElseThrow(() -> new AssertionFailedError("Attribute not present"));
|
||||
|
@ -61,7 +61,7 @@ class BuilderBlockTest {
|
||||
// Ensure that start=0 at top level, end is undefined until code is done, then end=1
|
||||
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,
|
||||
mb -> mb.withCode(xb -> {
|
||||
startEnd[0] = xb.startLabel();
|
||||
@ -80,7 +80,7 @@ class BuilderBlockTest {
|
||||
void testStartEndBlock() throws Exception {
|
||||
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,
|
||||
mb -> mb.withCode(xb -> {
|
||||
startEnd[0] = xb.startLabel();
|
||||
@ -103,7 +103,7 @@ class BuilderBlockTest {
|
||||
|
||||
@Test
|
||||
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.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int),
|
||||
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(),
|
||||
@ -122,7 +122,7 @@ class BuilderBlockTest {
|
||||
|
||||
@Test
|
||||
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.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int),
|
||||
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(),
|
||||
@ -140,7 +140,7 @@ class BuilderBlockTest {
|
||||
|
||||
@Test
|
||||
void testIfThenBadOpcode() {
|
||||
Classfile.build(ClassDesc.of("Foo"), cb -> {
|
||||
Classfile.of().build(ClassDesc.of("Foo"), cb -> {
|
||||
cb.withFlags(AccessFlag.PUBLIC);
|
||||
cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int),
|
||||
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(),
|
||||
@ -160,7 +160,7 @@ class BuilderBlockTest {
|
||||
|
||||
@Test
|
||||
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.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int),
|
||||
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(),
|
||||
@ -180,7 +180,7 @@ class BuilderBlockTest {
|
||||
|
||||
@Test
|
||||
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.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int),
|
||||
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(),
|
||||
@ -199,7 +199,7 @@ class BuilderBlockTest {
|
||||
|
||||
@Test
|
||||
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.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int),
|
||||
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(),
|
||||
@ -224,7 +224,7 @@ class BuilderBlockTest {
|
||||
|
||||
@Test
|
||||
void testIfThenElseBadOpcode() {
|
||||
Classfile.build(ClassDesc.of("Foo"), cb -> {
|
||||
Classfile.of().build(ClassDesc.of("Foo"), cb -> {
|
||||
cb.withFlags(AccessFlag.PUBLIC);
|
||||
cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int),
|
||||
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(),
|
||||
@ -245,7 +245,7 @@ class BuilderBlockTest {
|
||||
|
||||
@Test
|
||||
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,
|
||||
mb -> mb.withCode(xb -> {
|
||||
int slot1 = xb.allocateLocal(TypeKind.IntType);
|
||||
@ -262,7 +262,7 @@ class BuilderBlockTest {
|
||||
|
||||
@Test
|
||||
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,
|
||||
mb -> mb.withCode(xb -> {
|
||||
xb.block(bb -> {
|
||||
@ -283,7 +283,7 @@ class BuilderBlockTest {
|
||||
|
||||
@Test
|
||||
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,
|
||||
mb -> mb.withCode(xb -> {
|
||||
xb.iconst_0();
|
||||
|
@ -44,8 +44,8 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
class BuilderParamTest {
|
||||
@Test
|
||||
void testDirectBuilder() {
|
||||
|
||||
Classfile.build(ClassDesc.of("Foo"), cb -> {
|
||||
var cc = Classfile.of();
|
||||
cc.build(ClassDesc.of("Foo"), cb -> {
|
||||
cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), 0,
|
||||
mb -> mb.withCode(xb -> {
|
||||
assertEquals(xb.receiverSlot(), 0);
|
||||
@ -55,8 +55,7 @@ class BuilderParamTest {
|
||||
xb.return_();
|
||||
}));
|
||||
});
|
||||
|
||||
Classfile.build(ClassDesc.of("Foo"), cb -> {
|
||||
cc.build(ClassDesc.of("Foo"), cb -> {
|
||||
cb.withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), ACC_STATIC,
|
||||
mb -> mb.withCode(xb -> {
|
||||
assertEquals(xb.parameterSlot(0), 0);
|
||||
|
@ -171,7 +171,7 @@ class BuilderTryCatchTest {
|
||||
void testTryEmptyCatch() {
|
||||
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(CompoundElement::elementStream)
|
||||
.anyMatch(codeElement ->
|
||||
@ -182,7 +182,7 @@ class BuilderTryCatchTest {
|
||||
|
||||
@Test
|
||||
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()),
|
||||
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> {
|
||||
mb.withCode(xb -> {
|
||||
@ -213,7 +213,7 @@ class BuilderTryCatchTest {
|
||||
|
||||
@Test
|
||||
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()),
|
||||
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> {
|
||||
mb.withCode(xb -> {
|
||||
@ -276,7 +276,7 @@ class BuilderTryCatchTest {
|
||||
}
|
||||
|
||||
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()),
|
||||
AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> {
|
||||
mb.withCode(xb -> {
|
||||
|
@ -49,9 +49,10 @@ import java.util.Objects;
|
||||
public class ClassBuildingTest {
|
||||
@Test
|
||||
public void test() throws Throwable {
|
||||
var cc = Classfile.of();
|
||||
ClassModel cm;
|
||||
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")));
|
||||
@ -60,7 +61,7 @@ public class ClassBuildingTest {
|
||||
transform = transform.andThen(ClassTransform.transformingMethods(MethodTransform.dropping(me
|
||||
-> me instanceof SignatureAttribute)));
|
||||
|
||||
MethodHandles.lookup().defineClass(cm.transform(transform));
|
||||
MethodHandles.lookup().defineClass(cc.transform(cm, transform));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,8 +128,8 @@ class ClassHierarchyInfoTest {
|
||||
|
||||
void transformAndVerifySingle(ClassHierarchyResolver res) throws Exception {
|
||||
Path path = FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java/util/HashMap.class");
|
||||
var classModel = Classfile.parse(path, Classfile.Option.classHierarchyResolver(res));
|
||||
byte[] newBytes = classModel.transform(
|
||||
var classModel = Classfile.of().parse(path);
|
||||
byte[] newBytes = Classfile.of(Classfile.ClassHierarchyResolverOption.of(res)).transform(classModel,
|
||||
(clb, cle) -> {
|
||||
if (cle instanceof MethodModel mm) {
|
||||
clb.transformMethod(mm, (mb, me) -> {
|
||||
@ -143,7 +143,7 @@ class ClassHierarchyInfoTest {
|
||||
else
|
||||
clb.with(cle);
|
||||
});
|
||||
var errors = Classfile.parse(newBytes).verify(null);
|
||||
var errors = Classfile.of().parse(newBytes).verify(null);
|
||||
if (!errors.isEmpty()) {
|
||||
var itr = errors.iterator();
|
||||
var thrown = itr.next();
|
||||
|
@ -43,7 +43,8 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
class ClassPrinterTest {
|
||||
|
||||
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)
|
||||
.withFlags(Classfile.ACC_PUBLIC)
|
||||
.with(SourceFileAttribute.of("Foo.java"))
|
||||
|
@ -71,13 +71,14 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
class ConstantPoolCopyTest {
|
||||
private static ClassModel[] rtJarToClassLow(FileSystem fs) {
|
||||
try {
|
||||
var cc = Classfile.of();
|
||||
var modules = Stream.of(
|
||||
Files.walk(fs.getPath("modules/java.base/java")),
|
||||
Files.walk(fs.getPath("modules"), 2).filter(p -> p.endsWith("module-info.class")))
|
||||
.flatMap(p -> p)
|
||||
.filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".class"))
|
||||
.map(ConstantPoolCopyTest::readAllBytes)
|
||||
.map(bytes -> Classfile.parse(bytes))
|
||||
.map(bytes -> cc.parse(bytes))
|
||||
.toArray(ClassModel[]::new);
|
||||
return modules;
|
||||
} catch (IOException ioe) {
|
||||
|
@ -79,7 +79,8 @@ class CorpusTest {
|
||||
|
||||
static void splitTableAttributes(String sourceClassFile, String targetClassFile) throws IOException, URISyntaxException {
|
||||
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 curPc = dcob.curPc();
|
||||
switch (coe) {
|
||||
@ -145,8 +146,8 @@ class CorpusTest {
|
||||
|
||||
try {
|
||||
byte[] transformed = m.shared && m.classTransform != null
|
||||
? Classfile.parse(bytes, Classfile.Option.generateStackmap(false))
|
||||
.transform(m.classTransform)
|
||||
? Classfile.of(Classfile.StackMapsOption.DROP_STACK_MAPS)
|
||||
.transform(Classfile.of().parse(bytes), m.classTransform)
|
||||
: m.transform.apply(bytes);
|
||||
Map<Integer, Integer> newDups = findDups(transformed);
|
||||
oldRecord = m.classRecord(bytes);
|
||||
@ -196,15 +197,15 @@ class CorpusTest {
|
||||
@MethodSource("corpus")
|
||||
void testReadAndTransform(Path path) throws IOException {
|
||||
byte[] bytes = Files.readAllBytes(path);
|
||||
|
||||
var classModel = Classfile.parse(bytes);
|
||||
var cc = Classfile.of();
|
||||
var classModel = cc.parse(bytes);
|
||||
assertEqualsDeep(ClassRecord.ofClassModel(classModel), ClassRecord.ofStreamingElements(classModel),
|
||||
"ClassModel (actual) vs StreamingElements (expected)");
|
||||
|
||||
byte[] newBytes = Classfile.build(
|
||||
byte[] newBytes = cc.build(
|
||||
classModel.thisClass().asSymbol(),
|
||||
classModel::forEachElement);
|
||||
var newModel = Classfile.parse(newBytes, Classfile.Option.generateStackmap(false));
|
||||
var newModel = cc.parse(newBytes);
|
||||
assertEqualsDeep(ClassRecord.ofClassModel(newModel, CompatibilityFilter.By_ClassBuilder),
|
||||
ClassRecord.ofClassModel(classModel, CompatibilityFilter.By_ClassBuilder),
|
||||
"ClassModel[%s] transformed by ClassBuilder (actual) vs ClassModel before transformation (expected)".formatted(path));
|
||||
@ -212,8 +213,10 @@ class CorpusTest {
|
||||
assertEmpty(newModel.verify(null));
|
||||
|
||||
//testing maxStack and maxLocals are calculated identically by StackMapGenerator and StackCounter
|
||||
byte[] noStackMaps = newModel.transform(ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL));
|
||||
var noStackModel = Classfile.parse(noStackMaps);
|
||||
byte[] noStackMaps = Classfile.of(Classfile.StackMapsOption.DROP_STACK_MAPS)
|
||||
.transform(newModel,
|
||||
ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL));
|
||||
var noStackModel = cc.parse(noStackMaps);
|
||||
var itStack = newModel.methods().iterator();
|
||||
var itNoStack = noStackModel.methods().iterator();
|
||||
while (itStack.hasNext()) {
|
||||
@ -243,8 +246,9 @@ class CorpusTest {
|
||||
// }
|
||||
|
||||
private void compareCp(byte[] orig, byte[] transformed) {
|
||||
var cp1 = Classfile.parse(orig).constantPool();
|
||||
var cp2 = Classfile.parse(transformed).constantPool();
|
||||
var cc = Classfile.of();
|
||||
var cp1 = cc.parse(orig).constantPool();
|
||||
var cp2 = cc.parse(transformed).constantPool();
|
||||
|
||||
for (int i = 1; i < cp1.entryCount(); i += cp1.entryByIndex(i).width()) {
|
||||
assertEquals(cpiToString(cp1.entryByIndex(i)), cpiToString(cp2.entryByIndex(i)));
|
||||
@ -267,7 +271,7 @@ class CorpusTest {
|
||||
|
||||
private static Map<Integer, Integer> findDups(byte[] bytes) {
|
||||
Map<Integer, Integer> dups = new HashMap<>();
|
||||
var cf = Classfile.parse(bytes);
|
||||
var cf = Classfile.of().parse(bytes);
|
||||
var pool = cf.constantPool();
|
||||
Set<String> entryStrings = new HashSet<>();
|
||||
for (int i = 1; i < pool.entryCount(); i += pool.entryByIndex(i).width()) {
|
||||
|
@ -47,7 +47,8 @@ class DiscontinuedInstructionsTest {
|
||||
var testClass = "JsrAndRetSample";
|
||||
var testMethod = "testMethod";
|
||||
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)
|
||||
.withMethodBody(testMethod, MethodTypeDesc.of(CD_void, cd_list), ACC_PUBLIC | ACC_STATIC, cob -> cob
|
||||
.block(bb -> {
|
||||
@ -64,7 +65,7 @@ class DiscontinuedInstructionsTest {
|
||||
.pop()
|
||||
.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(6, c.maxStack());
|
||||
|
||||
@ -75,22 +76,27 @@ class DiscontinuedInstructionsTest {
|
||||
.invoke(null, list);
|
||||
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)
|
||||
.getMethod(testClass, testMethod)
|
||||
.invoke(null, list);
|
||||
assertEquals(list, List.of("Hello", "World", "Hello", "World"));
|
||||
|
||||
var clm = Classfile.parse(bytes);
|
||||
var clm = cc.parse(bytes);
|
||||
|
||||
//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))));
|
||||
|
||||
//test failure of stack map generation
|
||||
//test failure of stack map generation for Java 7
|
||||
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)))));
|
||||
|
||||
//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)));
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,8 @@ class FilterDeadLabelsTest {
|
||||
|
||||
@Test
|
||||
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 -> {
|
||||
cob.return_();
|
||||
deadLabelFragments().forEach(f -> f.accept(cob));
|
||||
@ -75,7 +76,7 @@ class FilterDeadLabelsTest {
|
||||
@ParameterizedTest
|
||||
@MethodSource("deadLabelFragments")
|
||||
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 -> {
|
||||
cob.return_();
|
||||
fragment.accept(cob);
|
||||
|
@ -46,7 +46,8 @@ import jdk.internal.classfile.instruction.ConstantInstruction;
|
||||
class LDCTest {
|
||||
@Test
|
||||
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.withVersion(52, 0);
|
||||
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()
|
||||
.filter(e -> e instanceof MethodModel)
|
||||
.map(e -> (MethodModel) e)
|
||||
|
@ -39,7 +39,7 @@ class LimitsTest {
|
||||
|
||||
@Test
|
||||
void testCPSizeLimit() {
|
||||
Classfile.build(ClassDesc.of("BigClass"), cb -> {
|
||||
Classfile.of().build(ClassDesc.of("BigClass"), cb -> {
|
||||
for (int i = 1; i < 65000; i++) {
|
||||
cb.withField("field" + i, ConstantDescs.CD_int, fb -> {});
|
||||
}
|
||||
@ -48,7 +48,7 @@ class LimitsTest {
|
||||
|
||||
@Test
|
||||
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++) {
|
||||
cb.withField("field" + i, ConstantDescs.CD_int, fb -> {});
|
||||
}
|
||||
@ -57,7 +57,7 @@ class LimitsTest {
|
||||
|
||||
@Test
|
||||
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 -> {
|
||||
for (int i = 0; i < 65535; i++) {
|
||||
cob.nop();
|
||||
@ -68,7 +68,7 @@ class LimitsTest {
|
||||
|
||||
@Test
|
||||
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 -> {})));
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,8 @@ class LowAdaptTest {
|
||||
|
||||
@Test
|
||||
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,
|
||||
ClassDesc.of("java.lang.invoke.LambdaMetafactory"),
|
||||
@ -73,7 +74,7 @@ class LowAdaptTest {
|
||||
MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC, ClassDesc.of(test), "fib", "(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.with(SourceFileAttribute.of("/some/madeup/TestClass.java"));
|
||||
cl.methods().forEach(m -> ((DirectClassBuilder) cb).withMethod(m));
|
||||
|
@ -61,7 +61,7 @@ class LowJCovAttributeTest {
|
||||
|
||||
LowJCovAttributeTest() throws IOException {
|
||||
this.path = Paths.get(URI.create(LowJCovAttributeTest.class.getResource(TEST_FILE).toString()));
|
||||
this.classLow = Classfile.parse(path);
|
||||
this.classLow = Classfile.of().parse(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -68,7 +68,7 @@ class LowModuleTest {
|
||||
void testRead(Path path, TestInfo test) throws Exception {
|
||||
try {
|
||||
printf("%nCHECK %s%n", test.getDisplayName());
|
||||
ClassModel classLow = Classfile.parse(path);
|
||||
ClassModel classLow = Classfile.of().parse(path);
|
||||
testRead0(classLow);
|
||||
} catch(Exception ex) {
|
||||
System.err.printf("%nFAIL %s - %s%n", path, ex);
|
||||
|
@ -81,7 +81,7 @@ class LvtTest {
|
||||
|
||||
@Test
|
||||
void getLVTEntries() {
|
||||
ClassModel c = Classfile.parse(fileBytes);
|
||||
ClassModel c = Classfile.of().parse(fileBytes);
|
||||
CodeModel co = c.methods().stream()
|
||||
.filter(mm -> mm.methodName().stringValue().equals("m"))
|
||||
.map(MethodModel::code)
|
||||
@ -106,18 +106,20 @@ class LvtTest {
|
||||
|
||||
@Test
|
||||
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
|
||||
byte[] newClass = c.transform(Transforms.threeLevelNoop);
|
||||
ClassRecord orig = ClassRecord.ofClassModel(Classfile.parse(fileBytes), ClassRecord.CompatibilityFilter.By_ClassBuilder);
|
||||
ClassRecord transformed = ClassRecord.ofClassModel(Classfile.parse(newClass), ClassRecord.CompatibilityFilter.By_ClassBuilder);
|
||||
byte[] newClass = cc.transform(c, Transforms.threeLevelNoop);
|
||||
ClassRecord orig = ClassRecord.ofClassModel(cc.parse(fileBytes), ClassRecord.CompatibilityFilter.By_ClassBuilder);
|
||||
ClassRecord transformed = ClassRecord.ofClassModel(cc.parse(newClass), ClassRecord.CompatibilityFilter.By_ClassBuilder);
|
||||
ClassRecord.assertEqualsDeep(transformed, orig);
|
||||
}
|
||||
|
||||
@Test
|
||||
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.withVersion(52, 0);
|
||||
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 lvt = main.code().get().findAttribute(Attributes.LOCAL_VARIABLE_TABLE).get();
|
||||
var lvs = lvt.localVariables();
|
||||
@ -189,7 +191,7 @@ class LvtTest {
|
||||
|
||||
@Test
|
||||
void getLVTTEntries() {
|
||||
ClassModel c = Classfile.parse(fileBytes);
|
||||
ClassModel c = Classfile.of().parse(fileBytes);
|
||||
CodeModel co = c.methods().stream()
|
||||
.filter(mm -> mm.methodName().stringValue().equals("n"))
|
||||
.map(MethodModel::code)
|
||||
@ -229,7 +231,8 @@ class LvtTest {
|
||||
|
||||
@Test
|
||||
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.withVersion(52, 0);
|
||||
cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java"))))
|
||||
@ -275,7 +278,7 @@ class LvtTest {
|
||||
.localVariable(1, u, jlObject, start, end);
|
||||
}));
|
||||
});
|
||||
var c = Classfile.parse(bytes);
|
||||
var c = cc.parse(bytes);
|
||||
var main = c.methods().get(1);
|
||||
var lvtt = main.code().get().findAttribute(Attributes.LOCAL_VARIABLE_TYPE_TABLE).get();
|
||||
var lvts = lvtt.localVariableTypes();
|
||||
@ -301,7 +304,7 @@ class LvtTest {
|
||||
|
||||
@Test
|
||||
void skipDebugSkipsLVT() {
|
||||
ClassModel c = Classfile.parse(fileBytes, Classfile.Option.processDebug(false));
|
||||
ClassModel c = Classfile.of(Classfile.DebugElementsOption.DROP_DEBUG).parse(fileBytes);
|
||||
|
||||
c.forEachElement(e -> {
|
||||
if (e instanceof MethodModel m) {
|
||||
|
@ -76,13 +76,13 @@ class MassAdaptCopyCodeTest {
|
||||
}
|
||||
|
||||
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));
|
||||
if (name.contains("/")) throw new RuntimeException(name);
|
||||
}
|
||||
|
||||
public byte[] adaptCopy(ClassModel cm) {
|
||||
return cm.transform((cb, ce) -> {
|
||||
return Classfile.of().transform(cm, (cb, ce) -> {
|
||||
if (ce instanceof MethodModel mm) {
|
||||
cb.transformMethod(mm, (mb, me) -> {
|
||||
if (me instanceof CodeModel xm) {
|
||||
|
@ -96,11 +96,12 @@ class MassAdaptCopyPrimitiveMatchCodeTest {
|
||||
|
||||
void copy(String name, byte[] bytes) throws Exception {
|
||||
//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, CodeAttribute> m2c = new HashMap<>();
|
||||
byte[] resultBytes =
|
||||
cm.transform((cb, e) -> {
|
||||
cc.transform(cm, (cb, e) -> {
|
||||
if (e instanceof MethodModel mm) {
|
||||
Optional<CodeModel> code = mm.code();
|
||||
if (code.isPresent()) {
|
||||
@ -125,7 +126,7 @@ class MassAdaptCopyPrimitiveMatchCodeTest {
|
||||
System.err.printf("MassAdaptCopyPrimitiveMatchCodeTest: Ignored because it is a record%n - %s%n", name);
|
||||
return;
|
||||
}
|
||||
ClassModel rcm = Classfile.parse(resultBytes);
|
||||
ClassModel rcm = cc.parse(resultBytes);
|
||||
for (MethodModel rmm : rcm.methods()) {
|
||||
Optional<CodeModel> code = rmm.code();
|
||||
if (code.isPresent()) {
|
||||
|
@ -64,7 +64,8 @@ class ModuleBuilderTest {
|
||||
private final ModuleAttribute attr;
|
||||
|
||||
public ModuleBuilderTest() {
|
||||
byte[] modInfo = Classfile.buildModule(
|
||||
var cc = Classfile.of();
|
||||
byte[] modInfo = cc.buildModule(
|
||||
ModuleAttribute.of(modName, mb -> mb
|
||||
.moduleVersion(modVsn)
|
||||
|
||||
@ -88,7 +89,7 @@ class ModuleBuilderTest {
|
||||
clb -> clb.with(ModuleMainClassAttribute.of(ClassDesc.of("main.Class")))
|
||||
.with(ModulePackagesAttribute.ofNames(PackageDesc.of("foo.bar.baz"), PackageDesc.of("quux")))
|
||||
.with(ModuleMainClassAttribute.of(ClassDesc.of("overwritten.main.Class"))));
|
||||
moduleModel = Classfile.parse(modInfo);
|
||||
moduleModel = cc.parse(modInfo);
|
||||
attr = ((ModuleAttribute) moduleModel.attributes().stream()
|
||||
.filter(a -> a.attributeMapper() == Attributes.MODULE)
|
||||
.findFirst()
|
||||
@ -98,10 +99,11 @@ class ModuleBuilderTest {
|
||||
@Test
|
||||
void testCreateModuleInfo() {
|
||||
// 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
|
||||
var cm = Classfile.parse(modBytes);
|
||||
var cm = cc.parse(modBytes);
|
||||
|
||||
var attr =cm.findAttribute(Attributes.MODULE).get();
|
||||
assertEquals(attr.moduleName().name().stringValue(), modName.name());
|
||||
@ -195,7 +197,7 @@ class ModuleBuilderTest {
|
||||
void verifyIsModuleInfo() throws Exception {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -63,8 +63,8 @@ class OneToOneTest {
|
||||
|
||||
@Test
|
||||
void testClassWriteRead() {
|
||||
|
||||
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.withVersion(52, 0);
|
||||
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();
|
||||
assertEquals(ms.size(), 2);
|
||||
boolean found = false;
|
||||
|
@ -105,7 +105,7 @@ public class OpcodesValidationTest {
|
||||
}
|
||||
|
||||
private void testPositiveCase(Opcode opcode, Object constant) {
|
||||
Classfile.build(ClassDesc.of("MyClass"),
|
||||
Classfile.of().build(ClassDesc.of("MyClass"),
|
||||
cb -> cb.withFlags(AccessFlag.PUBLIC)
|
||||
.withMethod("<init>", MethodTypeDesc.of(CD_void), 0,
|
||||
mb -> mb.withCode(
|
||||
@ -122,7 +122,7 @@ public class OpcodesValidationTest {
|
||||
}
|
||||
|
||||
private void testNegativeCase(Opcode opcode, Object constant) {
|
||||
Classfile.build(ClassDesc.of("MyClass"),
|
||||
Classfile.of().build(ClassDesc.of("MyClass"),
|
||||
cb -> cb.withFlags(AccessFlag.PUBLIC)
|
||||
.withMethod("<init>", MethodTypeDesc.of(CD_void), 0,
|
||||
mb -> mb .withCode(
|
||||
|
@ -53,7 +53,7 @@ public final class PrimitiveClassConstantTest {
|
||||
public void test() throws Throwable {
|
||||
ClassDesc ape = ClassDesc.of("Ape");
|
||||
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.withInterfaceSymbols(Supplier.class.describeConstable().orElseThrow());
|
||||
clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> {
|
||||
|
@ -33,6 +33,7 @@ import java.lang.constant.ConstantDescs;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import jdk.internal.classfile.ClassModel;
|
||||
import jdk.internal.classfile.ClassTransform;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
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
|
||||
@MethodSource("provideFwd")
|
||||
void testFixFwdJumpsDirectGen(Sample sample) throws Exception {
|
||||
assertFixed(sample, generateFwd(sample, true, Classfile.Option.fixShortJumps(true)));
|
||||
assertFixed(sample,
|
||||
generateFwd(CC_Fixed_Jumps, sample, true));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideBack")
|
||||
void testFixBackJumpsDirectGen(Sample sample) throws Exception {
|
||||
assertFixed(sample, generateBack(sample, true, Classfile.Option.fixShortJumps(true)));
|
||||
assertFixed(sample,
|
||||
generateBack(CC_Fixed_Jumps, sample, true));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideFwd")
|
||||
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
|
||||
@MethodSource("provideBack")
|
||||
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
|
||||
@MethodSource("provideFwd")
|
||||
void testFixFwdJumpsTransform(Sample sample) throws Exception {
|
||||
assertFixed(sample, Classfile.parse(
|
||||
generateFwd(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)),
|
||||
Classfile.Option.fixShortJumps(true))
|
||||
.transform(overflow()));
|
||||
assertFixed(sample,
|
||||
CC_Fixed_Jumps.transform(
|
||||
generateFwd(CC_No_Stack_No_Patch, sample, false),
|
||||
overflow()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideBack")
|
||||
void testFixBackJumpsTransform(Sample sample) throws Exception {
|
||||
assertFixed(sample, Classfile.parse(
|
||||
generateBack(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)),
|
||||
Classfile.Option.fixShortJumps(true))
|
||||
.transform(overflow()));
|
||||
assertFixed(sample,
|
||||
CC_Fixed_Jumps.transform(
|
||||
generateBack(CC_No_Stack_No_Patch, sample, false),
|
||||
overflow()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideFwd")
|
||||
void testFailFwdJumpsTransform(Sample sample) throws Exception {
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
Classfile.parse(
|
||||
generateFwd(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)),
|
||||
Classfile.Option.fixShortJumps(false))
|
||||
.transform(overflow()));
|
||||
CC_Not_Fixed_Jumps.transform(
|
||||
generateFwd(CC_No_Stack_No_Patch, sample, false),
|
||||
overflow()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideBack")
|
||||
void testFailBackJumpsTransform(Sample sample) throws Exception {
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
Classfile.parse(
|
||||
generateBack(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)),
|
||||
Classfile.Option.fixShortJumps(false))
|
||||
.transform(overflow()));
|
||||
CC_Not_Fixed_Jumps.transform(
|
||||
generateBack(CC_No_Stack_No_Patch, sample, false),
|
||||
overflow()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideFwd")
|
||||
void testFixFwdJumpsChainedTransform(Sample sample) throws Exception {
|
||||
assertFixed(sample, Classfile.parse(
|
||||
generateFwd(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)),
|
||||
Classfile.Option.fixShortJumps(true))
|
||||
.transform(ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here
|
||||
assertFixed(sample,
|
||||
CC_Fixed_Jumps.transform(
|
||||
generateFwd(CC_No_Stack_No_Patch, sample, false),
|
||||
ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideBack")
|
||||
void testFixBackJumpsChainedTransform(Sample sample) throws Exception {
|
||||
assertFixed(sample, Classfile.parse(
|
||||
generateBack(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)),
|
||||
Classfile.Option.fixShortJumps(true))
|
||||
.transform(ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here
|
||||
assertFixed(sample,
|
||||
CC_Fixed_Jumps.transform(
|
||||
generateBack(CC_No_Stack_No_Patch, sample, false),
|
||||
ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideFwd")
|
||||
void testFailFwdJumpsChainedTransform(Sample sample) throws Exception {
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
Classfile.parse(
|
||||
generateFwd(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)),
|
||||
Classfile.Option.fixShortJumps(false))
|
||||
.transform(ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here
|
||||
CC_Not_Fixed_Jumps.transform(
|
||||
generateFwd(CC_No_Stack_No_Patch, sample, false),
|
||||
ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideBack")
|
||||
void testFailBackJumpsChainedTransform(Sample sample) throws Exception {
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
Classfile.parse(
|
||||
generateBack(sample, false, Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)),
|
||||
Classfile.Option.fixShortJumps(false))
|
||||
.transform(ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here
|
||||
CC_Not_Fixed_Jumps.transform(
|
||||
generateBack(CC_No_Stack_No_Patch, sample, false),
|
||||
ClassTransform.ACCEPT_ALL.andThen(overflow()))); //involve BufferedCodeBuilder here
|
||||
}
|
||||
|
||||
private static byte[] generateFwd(Sample sample, boolean overflow, Classfile.Option... options) {
|
||||
return Classfile.build(ClassDesc.of("WhateverClass"), List.of(options),
|
||||
private static ClassModel generateFwd(Classfile cc, Sample sample, boolean overflow) {
|
||||
return cc.parse(cc.build(ClassDesc.of("WhateverClass"),
|
||||
cb -> cb.withMethod("whateverMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0,
|
||||
mb -> mb.withCode(cob -> {
|
||||
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.labelBinding(target);
|
||||
cob.return_();
|
||||
})));
|
||||
}))));
|
||||
}
|
||||
|
||||
private static byte[] generateBack(Sample sample, boolean overflow, Classfile.Option... options) {
|
||||
return Classfile.build(ClassDesc.of("WhateverClass"), List.of(options),
|
||||
private static ClassModel generateBack(Classfile cc, Sample sample, boolean overflow) {
|
||||
return cc.parse(cc.build(ClassDesc.of("WhateverClass"),
|
||||
cb -> cb.withMethod("whateverMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0,
|
||||
mb -> mb.withCode(cob -> {
|
||||
var target = cob.newLabel();
|
||||
@ -231,7 +237,7 @@ class ShortJumpsFixTest {
|
||||
cob.with(ConstantInstruction.ofIntrinsic(sample.expected[i]));
|
||||
cob.branchInstruction(sample.jumpCode, target);
|
||||
cob.return_();
|
||||
})));
|
||||
}))));
|
||||
}
|
||||
|
||||
private static ClassTransform overflow() {
|
||||
@ -246,8 +252,12 @@ class ShortJumpsFixTest {
|
||||
}
|
||||
|
||||
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>();
|
||||
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)
|
||||
found.add(i.opcode());
|
||||
assertEquals(found, List.of(sample.expected));
|
||||
|
@ -128,7 +128,7 @@ class SignaturesTest {
|
||||
.flatMap(p -> p)
|
||||
.filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".class")).forEach(path -> {
|
||||
try {
|
||||
var cm = Classfile.parse(path);
|
||||
var cm = Classfile.of().parse(path);
|
||||
cm.findAttribute(Attributes.SIGNATURE).ifPresent(csig -> {
|
||||
assertEquals(
|
||||
ClassSignature.parseFrom(csig.signature().stringValue()).signatureString(),
|
||||
|
69
test/jdk/jdk/classfile/SnippetsTest.java
Normal file
69
test/jdk/jdk/classfile/SnippetsTest.java
Normal 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
|
||||
}
|
||||
}
|
@ -54,9 +54,9 @@ import java.lang.reflect.AccessFlag;
|
||||
class StackMapsTest {
|
||||
|
||||
private byte[] buildDeadCode() {
|
||||
return Classfile.build(
|
||||
return Classfile.of(Classfile.StackMapsOption.DROP_STACK_MAPS,
|
||||
Classfile.DeadCodeOption.KEEP_DEAD_CODE).build(
|
||||
ClassDesc.of("DeadCodePattern"),
|
||||
List.of(Classfile.Option.generateStackmap(false), Classfile.Option.patchDeadCode(false)),
|
||||
clb -> clb.withMethodBody(
|
||||
"twoReturns",
|
||||
MethodTypeDesc.of(ConstantDescs.CD_void),
|
||||
@ -97,7 +97,7 @@ class StackMapsTest {
|
||||
|
||||
@Test
|
||||
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(
|
||||
"""
|
||||
Unable to generate stack map frame for dead code at bytecode offset 1 of method twoReturns()
|
||||
@ -172,9 +172,10 @@ class StackMapsTest {
|
||||
|
||||
@Test
|
||||
void testFrameOutOfBytecodeRange() {
|
||||
var cc = Classfile.of();
|
||||
var error = assertThrows(IllegalArgumentException.class, () ->
|
||||
Classfile.parse(
|
||||
Classfile.build(ClassDesc.of("TestClass"), clb ->
|
||||
cc.parse(
|
||||
cc.build(ClassDesc.of("TestClass"), clb ->
|
||||
clb.withMethodBody("frameOutOfRangeMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, cob -> {
|
||||
var l = cob.newLabel();
|
||||
cob.goto_(l);//jump to the end of method body triggers invalid frame creation
|
||||
@ -194,7 +195,7 @@ class StackMapsTest {
|
||||
@Test
|
||||
void testMethodSwitchFromStatic() {
|
||||
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),
|
||||
ACC_STATIC,
|
||||
mb -> mb.withCode(cob -> {
|
||||
@ -207,7 +208,7 @@ class StackMapsTest {
|
||||
@Test
|
||||
void testMethodSwitchToStatic() {
|
||||
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),
|
||||
0, mb ->
|
||||
mb.withCode(cob -> {
|
||||
@ -219,16 +220,23 @@ class StackMapsTest {
|
||||
|
||||
@Test
|
||||
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
|
||||
var version49 = Classfile.parse(actualVersion.transform(ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL)
|
||||
var version49 = cc.parse(cc.transform(
|
||||
actualVersion,
|
||||
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")));
|
||||
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
|
||||
assertEmpty(Classfile.parse(version49.transform(ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL)
|
||||
.andThen(ClassTransform.endHandler(clb -> clb.withVersion(50, 0))))).verify(null));
|
||||
assertEmpty(cc.parse(cc.transform(
|
||||
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:/"));
|
||||
@ -243,8 +251,9 @@ class StackMapsTest {
|
||||
|
||||
private static void testTransformedStackMaps(byte[] originalBytes, Classfile.Option... options) throws Exception {
|
||||
//transform the class model
|
||||
var classModel = Classfile.parse(originalBytes, options);
|
||||
var transformedBytes = Classfile.build(classModel.thisClass().asSymbol(), List.of(options),
|
||||
Classfile cc = Classfile.of(options);
|
||||
var classModel = cc.parse(originalBytes);
|
||||
var transformedBytes = cc.build(classModel.thisClass().asSymbol(),
|
||||
cb -> {
|
||||
// classModel.superclass().ifPresent(cb::withSuperclass);
|
||||
// cb.withInterfaces(classModel.interfaces());
|
||||
@ -253,6 +262,6 @@ class StackMapsTest {
|
||||
});
|
||||
|
||||
//then verify transformed bytecode
|
||||
assertEmpty(Classfile.parse(transformedBytes).verify(null));
|
||||
assertEmpty(cc.parse(transformedBytes).verify(null));
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ class StackTrackerTest {
|
||||
|
||||
@Test
|
||||
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 -> {
|
||||
var stackTracker = CodeStackTracker.of(DoubleType, FloatType); //initial stack tracker pre-set
|
||||
cob.transforming(stackTracker, stcb -> {
|
||||
@ -81,7 +81,7 @@ class StackTrackerTest {
|
||||
|
||||
@Test
|
||||
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 -> {
|
||||
var stackTracker = CodeStackTracker.of();
|
||||
cob.transforming(stackTracker, stcb -> {
|
||||
|
@ -78,7 +78,7 @@ class StreamedVsListTest {
|
||||
|
||||
private class Vs {
|
||||
boolean failed;
|
||||
ClassModel cm = Classfile.parse(fileBytes);
|
||||
ClassModel cm = Classfile.of().parse(fileBytes);
|
||||
String meth;
|
||||
CodeElement iim;
|
||||
CodeElement mim;
|
||||
|
@ -47,7 +47,7 @@ class SwapTest {
|
||||
MethodType mt = MethodType.methodType(String.class, String.class, String.class);
|
||||
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 -> {
|
||||
xb.aload(0); // 0
|
||||
xb.aload(1); // 1, 0
|
||||
|
@ -55,7 +55,8 @@ class TempConstantPoolBuilderTest {
|
||||
|
||||
@Test
|
||||
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)
|
||||
.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java"))))
|
||||
.withMethod("<init>", MethodTypeDesc.of(CD_void), 0, mb -> mb
|
||||
@ -67,7 +68,7 @@ class TempConstantPoolBuilderTest {
|
||||
AnnotationElement.ofString("foo", "bar"))))
|
||||
);
|
||||
});
|
||||
ClassModel m = Classfile.parse(bytes);
|
||||
ClassModel m = cc.parse(bytes);
|
||||
//ClassPrinter.toJson(m, ClassPrinter.Verbosity.TRACE_ALL, System.out::println);
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,8 @@ class TestRecordComponent {
|
||||
|
||||
@Test
|
||||
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) -> {
|
||||
if (ce instanceof RecordAttribute rm) {
|
||||
List<RecordComponentInfo> components = rm.components();
|
||||
@ -66,21 +67,23 @@ class TestRecordComponent {
|
||||
} else
|
||||
cb.with(ce);
|
||||
};
|
||||
ClassModel newModel = Classfile.parse(cm.transform(xform));
|
||||
ClassModel newModel = cc.parse(cc.transform(cm, xform));
|
||||
ClassRecord.assertEquals(newModel, cm);
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
ClassModel newModel = Classfile.parse(cm.transform(xform));
|
||||
ClassModel newModel = cc.parse(cc.transform(cm, xform));
|
||||
ClassRecord.assertEquals(newModel, cm);
|
||||
}
|
||||
|
||||
@Test
|
||||
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) -> {
|
||||
if (ce instanceof RecordAttribute ra) {
|
||||
List<RecordComponentInfo> components = ra.components();
|
||||
@ -91,7 +94,7 @@ class TestRecordComponent {
|
||||
else
|
||||
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();
|
||||
assertEquals(ra.components().size(), 2, "Should have two components");
|
||||
assertEquals(ra.components().get(0).name().stringValue(), "fooXYZ");
|
||||
@ -103,7 +106,7 @@ class TestRecordComponent {
|
||||
@Test
|
||||
void testOptions() throws Exception {
|
||||
AtomicInteger count = new AtomicInteger(0);
|
||||
ClassModel cm = Classfile.parse(Files.readAllBytes(testClassPath));
|
||||
ClassModel cm = Classfile.of().parse(Files.readAllBytes(testClassPath));
|
||||
cm.forEachElement((ce) -> {
|
||||
if (ce instanceof RecordAttribute rm) {
|
||||
count.addAndGet(rm.components().size());
|
||||
|
@ -95,34 +95,37 @@ class TransformTests {
|
||||
void testSingleTransform() throws Exception {
|
||||
|
||||
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(cm.transform(transformCode(foo2foo))), "foo");
|
||||
assertEquals(invoke(cm.transform(transformCode(foo2bar))), "bar");
|
||||
assertEquals(invoke(cc.transform(cm, transformCode(foo2foo))), "foo");
|
||||
assertEquals(invoke(cc.transform(cm, transformCode(foo2bar))), "bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSeq2() throws Exception {
|
||||
|
||||
byte[] bytes = Files.readAllBytes(testClassPath);
|
||||
ClassModel cm = Classfile.parse(bytes);
|
||||
var cc = Classfile.of();
|
||||
ClassModel cm = cc.parse(bytes);
|
||||
|
||||
assertEquals(invoke(bytes), "foo");
|
||||
ClassTransform transform = transformCode(foo2bar.andThen(bar2baz));
|
||||
assertEquals(invoke(cm.transform(transform)), "baz");
|
||||
assertEquals(invoke(cc.transform(cm, transform)), "baz");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSeqN() throws Exception {
|
||||
|
||||
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(cm.transform(transformCode(foo2bar.andThen(bar2baz).andThen(baz2foo)))), "foo");
|
||||
assertEquals(invoke(cm.transform(transformCode(foo2bar.andThen(bar2baz).andThen(baz2quux)))), "quux");
|
||||
assertEquals(invoke(cm.transform(transformCode(foo2foo.andThen(foo2bar).andThen(bar2baz)))), "baz");
|
||||
assertEquals(invoke(cc.transform(cm, transformCode(foo2bar.andThen(bar2baz).andThen(baz2foo)))), "foo");
|
||||
assertEquals(invoke(cc.transform(cm, transformCode(foo2bar.andThen(bar2baz).andThen(baz2quux)))), "quux");
|
||||
assertEquals(invoke(cc.transform(cm, transformCode(foo2foo.andThen(foo2bar).andThen(bar2baz)))), "baz");
|
||||
}
|
||||
|
||||
public static class TestClass {
|
||||
|
@ -83,7 +83,7 @@ class Utf8EntryTest {
|
||||
void testParse(String s) {
|
||||
byte[] classfile = createClassfile(s);
|
||||
|
||||
ClassModel cm = Classfile.parse(classfile);
|
||||
ClassModel cm = Classfile.of().parse(classfile);
|
||||
StringEntry se = obtainStringEntry(cm.constantPool());
|
||||
|
||||
Utf8Entry utf8Entry = se.utf8();
|
||||
@ -163,7 +163,7 @@ class Utf8EntryTest {
|
||||
byte[] classfile = createClassfile(marker);
|
||||
replace(classfile, marker, f);
|
||||
|
||||
ClassModel cm = Classfile.parse(classfile);
|
||||
ClassModel cm = Classfile.of().parse(classfile);
|
||||
StringEntry se = obtainStringEntry(cm.constantPool());
|
||||
|
||||
assertThrows(RuntimeException.class, () -> {
|
||||
@ -197,7 +197,7 @@ class Utf8EntryTest {
|
||||
}
|
||||
|
||||
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,
|
||||
mb -> mb.withCode(cb -> cb.constantInstruction(s)
|
||||
.returnInstruction(VoidType))));
|
||||
|
@ -53,7 +53,7 @@ class VerifierSelfTest {
|
||||
.flatMap(p -> p)
|
||||
.filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".class")).forEach(path -> {
|
||||
try {
|
||||
Classfile.parse(path).verify(null);
|
||||
Classfile.of().parse(path).verify(null);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@ -63,9 +63,10 @@ class VerifierSelfTest {
|
||||
@Test
|
||||
void testFailedDump() throws IOException {
|
||||
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)));
|
||||
byte[] brokenClassBytes = classModel.transform(
|
||||
var classModel = cc.parse(path);
|
||||
byte[] brokenClassBytes = cc.transform(classModel,
|
||||
(clb, cle) -> {
|
||||
if (cle instanceof MethodModel mm) {
|
||||
clb.transformMethod(mm, (mb, me) -> {
|
||||
@ -80,7 +81,7 @@ class VerifierSelfTest {
|
||||
clb.with(cle);
|
||||
});
|
||||
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");
|
||||
}
|
||||
String output = sb.toString();
|
||||
|
@ -52,7 +52,7 @@ class WriteTest {
|
||||
@Test
|
||||
void testJavapWrite() {
|
||||
|
||||
byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> {
|
||||
byte[] bytes = Classfile.of().build(ClassDesc.of("MyClass"), cb -> {
|
||||
cb.withFlags(AccessFlag.PUBLIC);
|
||||
cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java"))))
|
||||
.withMethod("<init>", MethodTypeDesc.of(CD_void), 0, mb -> mb
|
||||
@ -94,7 +94,7 @@ class WriteTest {
|
||||
@Test
|
||||
void testPrimitiveWrite() {
|
||||
|
||||
byte[] bytes = Classfile.build(ClassDesc.of("MyClass"), cb -> {
|
||||
byte[] bytes = Classfile.of().build(ClassDesc.of("MyClass"), cb -> {
|
||||
cb.withFlags(AccessFlag.PUBLIC)
|
||||
.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java"))))
|
||||
.withMethod("<init>", MethodTypeDesc.of(CD_void), 0, mb -> mb
|
||||
|
@ -49,7 +49,7 @@ public class AnnotationsExamples {
|
||||
public byte[] addAnno(ClassModel m) {
|
||||
// @@@ Not correct
|
||||
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()) {
|
||||
RuntimeVisibleAnnotationsAttribute a = m.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).get();
|
||||
var cc = Classfile.of();
|
||||
for (Annotation ann : a.annotations()) {
|
||||
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()) {
|
||||
RuntimeVisibleAnnotationsAttribute a = m.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).get();
|
||||
var cc = Classfile.of();
|
||||
for (Annotation ann : a.annotations()) {
|
||||
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) {
|
||||
var oldAnnos = ra.annotations();
|
||||
List<Annotation> newAnnos = new ArrayList<>(oldAnnos.size() + 1);
|
||||
@ -144,7 +146,7 @@ public class AnnotationsExamples {
|
||||
}
|
||||
|
||||
public byte[] viaEndHandlerClassBuilderEdition(ClassModel m) {
|
||||
return m.transform(ClassTransform.ofStateful(() -> new ClassTransform() {
|
||||
return Classfile.of().transform(m, ClassTransform.ofStateful(() -> new ClassTransform() {
|
||||
boolean found = false;
|
||||
|
||||
@Override
|
||||
@ -171,7 +173,7 @@ public class AnnotationsExamples {
|
||||
}
|
||||
|
||||
public byte[] viaEndHandlerClassTransformEdition(ClassModel m) {
|
||||
return m.transform(ClassTransform.ofStateful(() -> new ClassTransform() {
|
||||
return Classfile.of().transform(m, ClassTransform.ofStateful(() -> new ClassTransform() {
|
||||
boolean found = false;
|
||||
|
||||
@Override
|
||||
|
@ -64,7 +64,7 @@ import jdk.internal.classfile.instruction.InvokeInstruction;
|
||||
*/
|
||||
public class ExampleGallery {
|
||||
public byte[] changeClassVersion(ClassModel cm) {
|
||||
return cm.transform((cb, ce) -> {
|
||||
return Classfile.of().transform(cm, (cb, ce) -> {
|
||||
switch (ce) {
|
||||
case ClassfileVersion cv -> cb.withVersion(57, 0);
|
||||
default -> cb.with(ce);
|
||||
@ -73,7 +73,7 @@ public class ExampleGallery {
|
||||
}
|
||||
|
||||
public byte[] incrementClassVersion(ClassModel cm) {
|
||||
return cm.transform((cb, ce) -> {
|
||||
return Classfile.of().transform(cm, (cb, ce) -> {
|
||||
switch (ce) {
|
||||
case ClassfileVersion cv -> cb.withVersion(cv.majorVersion() + 1, 0);
|
||||
default -> cb.with(ce);
|
||||
@ -82,7 +82,7 @@ public class ExampleGallery {
|
||||
}
|
||||
|
||||
public byte[] changeSuperclass(ClassModel cm, ClassDesc superclass) {
|
||||
return cm.transform((cb, ce) -> {
|
||||
return Classfile.of().transform(cm, (cb, ce) -> {
|
||||
switch (ce) {
|
||||
case Superclass sc -> cb.withSuperclass(superclass);
|
||||
default -> cb.with(ce);
|
||||
@ -91,11 +91,11 @@ public class ExampleGallery {
|
||||
}
|
||||
|
||||
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) {
|
||||
return cm.transform((cb, ce) -> {
|
||||
return Classfile.of().transform(cm, (cb, ce) -> {
|
||||
switch (ce) {
|
||||
case Interfaces i -> cb.withInterfaces(i.interfaces().stream()
|
||||
.filter(e -> !e.asInternalName().equals(internalName))
|
||||
@ -106,7 +106,7 @@ public class ExampleGallery {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@Override
|
||||
@ -135,7 +135,7 @@ public class ExampleGallery {
|
||||
|
||||
}
|
||||
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;
|
||||
|
||||
@Override
|
||||
@ -162,11 +162,11 @@ public class ExampleGallery {
|
||||
}
|
||||
|
||||
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) {
|
||||
return cm.transform((cb, ce) -> {
|
||||
return Classfile.of().transform(cm, (cb, ce) -> {
|
||||
switch (ce) {
|
||||
case SignatureAttribute sa -> {
|
||||
String result = sa.signature().stringValue();
|
||||
@ -178,7 +178,7 @@ public class ExampleGallery {
|
||||
}
|
||||
|
||||
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(
|
||||
ClassSignature.of(
|
||||
ClassTypeSig.of(ClassDesc.of("impl.Fox"),
|
||||
@ -189,16 +189,16 @@ public class ExampleGallery {
|
||||
// @@@ strip annos (class, all)
|
||||
|
||||
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())));
|
||||
}
|
||||
|
||||
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) {
|
||||
return cm.transform(ClassTransform.transformingFields((fb, fe) -> {
|
||||
return Classfile.of().transform(cm, ClassTransform.transformingFields((fb, fe) -> {
|
||||
if (fe instanceof SignatureAttribute sa)
|
||||
fb.with(SignatureAttribute.of(Signature.parseFrom(sa.signature().stringValue().replace("this/", "that/"))));
|
||||
else
|
||||
@ -207,7 +207,7 @@ public class ExampleGallery {
|
||||
}
|
||||
|
||||
public byte[] changeFieldFlags(ClassModel cm) {
|
||||
return cm.transform(ClassTransform.transformingFields((fb, fe) -> {
|
||||
return Classfile.of().transform(cm, ClassTransform.transformingFields((fb, fe) -> {
|
||||
switch (fe) {
|
||||
case AccessFlags a -> fb.with(AccessFlags.ofField(a.flagsMask() & ~Classfile.ACC_PUBLIC & ~Classfile.ACC_PROTECTED));
|
||||
default -> fb.with(fe);
|
||||
@ -216,7 +216,7 @@ public class ExampleGallery {
|
||||
}
|
||||
|
||||
public byte[] addException(ClassModel cm, ClassDesc ex) {
|
||||
return cm.transform(ClassTransform.transformingMethods(
|
||||
return Classfile.of().transform(cm, ClassTransform.transformingMethods(
|
||||
MethodTransform.ofStateful(() -> new MethodTransform() {
|
||||
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) {
|
||||
return cm.transform(ClassTransform.transformingMethodBodies((codeB, codeE) -> {
|
||||
return Classfile.of().transform(cm, ClassTransform.transformingMethodBodies((codeB, codeE) -> {
|
||||
switch (codeE) {
|
||||
case InvokeInstruction i -> {
|
||||
codeB.nopInstruction();
|
||||
@ -276,7 +276,7 @@ public class ExampleGallery {
|
||||
}
|
||||
|
||||
public byte[] replaceIntegerConstant(ClassModel cm) {
|
||||
return cm.transform(ClassTransform.transformingMethodBodies((codeB, codeE) -> {
|
||||
return Classfile.of().transform(cm, ClassTransform.transformingMethodBodies((codeB, codeE) -> {
|
||||
switch (codeE) {
|
||||
case ConstantInstruction ci -> {
|
||||
if (ci.constantValue() instanceof Integer i) codeB.constantInstruction(i + 1);
|
||||
|
@ -54,7 +54,7 @@ public class ExperimentalTransformExamples {
|
||||
};
|
||||
|
||||
public byte[] deleteAnnotations(ClassModel cm) {
|
||||
return cm.transform((cb, ce) -> {
|
||||
return Classfile.of().transform(cm, (cb, ce) -> {
|
||||
switch (ce) {
|
||||
case MethodModel m -> cb.transformMethod(m, dropMethodAnnos);
|
||||
case FieldModel f -> cb.transformField(f, dropFieldAnnos);
|
||||
|
@ -53,7 +53,7 @@ public class ModuleExamples {
|
||||
private static final FileSystem JRT = FileSystems.getFileSystem(URI.create("jrt:/"));
|
||||
|
||||
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());
|
||||
|
||||
ModuleAttribute ma = cm.findAttribute(Attributes.MODULE).orElseThrow();
|
||||
@ -78,7 +78,8 @@ public class ModuleExamples {
|
||||
});
|
||||
|
||||
// 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
|
||||
clb.with(RuntimeVisibleAnnotationsAttribute.of(Annotation.of(ClassDesc.ofDescriptor("Ljava/lang/Deprecated;"),
|
||||
@ -87,7 +88,7 @@ public class ModuleExamples {
|
||||
});
|
||||
|
||||
// Examine it
|
||||
ClassModel mm = Classfile.parse(moduleInfo);
|
||||
ClassModel mm = cc.parse(moduleInfo);
|
||||
System.out.println("Is module info?: " + mm.isModuleInfo());
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
* @summary Testing Classfile TransformExamples compilation.
|
||||
* @compile TransformExamples.java
|
||||
*/
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import jdk.internal.classfile.ClassModel;
|
||||
import jdk.internal.classfile.ClassTransform;
|
||||
import jdk.internal.classfile.FieldModel;
|
||||
@ -39,18 +40,18 @@ import jdk.internal.classfile.Attribute;
|
||||
*/
|
||||
public class TransformExamples {
|
||||
public byte[] noop(ClassModel cm) {
|
||||
return cm.transform(ClassTransform.ACCEPT_ALL);
|
||||
return Classfile.of().transform(cm, ClassTransform.ACCEPT_ALL);
|
||||
}
|
||||
|
||||
public byte[] deleteAllMethods(ClassModel cm) {
|
||||
return cm.transform((b, e) -> {
|
||||
return Classfile.of().transform(cm, (b, e) -> {
|
||||
if (!(e instanceof MethodModel))
|
||||
b.with(e);
|
||||
});
|
||||
}
|
||||
|
||||
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("$")))
|
||||
b.with(e);
|
||||
@ -58,14 +59,14 @@ public class TransformExamples {
|
||||
}
|
||||
|
||||
public byte[] deleteAttributes(ClassModel cm) {
|
||||
return cm.transform((b, e) -> {
|
||||
return Classfile.of().transform(cm, (b, e) -> {
|
||||
if (!(e instanceof Attribute))
|
||||
b.with(e);
|
||||
});
|
||||
}
|
||||
|
||||
public byte[] keepMethodsAndFields(ClassModel cm) {
|
||||
return cm.transform((b, e) -> {
|
||||
return Classfile.of().transform(cm, (b, e) -> {
|
||||
if (e instanceof MethodModel || e instanceof FieldModel)
|
||||
b.with(e);
|
||||
});
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user