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