8308899: Introduce Classfile context and improve Classfile options

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -118,7 +118,7 @@ public final class CodeImpl
if (!inflated) {
if (labels == null)
labels = new LabelImpl[codeLength + 1];
if (((ClassReaderImpl)classReader).options().processLineNumbers)
if (((ClassReaderImpl)classReader).context().lineNumbersOption() == Classfile.LineNumbersOption.PASS_LINE_NUMBERS)
inflateLineNumbers();
inflateJumpTargets();
inflateTypeAnnotations();
@ -150,6 +150,7 @@ public final class CodeImpl
}
},
(SplitConstantPool)buf.constantPool(),
((BufWriterImpl)buf).context(),
null).writeTo(buf);
}
}
@ -166,7 +167,7 @@ public final class CodeImpl
inflateMetadata();
boolean doLineNumbers = (lineNumbers != null);
generateCatchTargets(consumer);
if (((ClassReaderImpl)classReader).options().processDebug)
if (((ClassReaderImpl)classReader).context().debugElementsOption() == Classfile.DebugElementsOption.PASS_DEBUG)
generateDebugElements(consumer);
for (int pos=codeStart; pos<codeEnd; ) {
if (labels[pos - codeStart] != null)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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