mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
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
113 changed files with 1602 additions and 1190 deletions
|
@ -1590,8 +1590,8 @@ public final class Module implements AnnotatedElement {
|
|||
*/
|
||||
private Class<?> loadModuleInfoClass(InputStream in) throws IOException {
|
||||
final String MODULE_INFO = "module-info";
|
||||
byte[] bytes = Classfile.parse(in.readAllBytes(),
|
||||
Classfile.Option.constantPoolSharing(false)).transform((clb, cle) -> {
|
||||
var cc = Classfile.of(Classfile.ConstantPoolSharingOption.NEW_POOL);
|
||||
byte[] bytes = cc.transform(cc.parse(in.readAllBytes()), (clb, cle) -> {
|
||||
switch (cle) {
|
||||
case AccessFlags af -> clb.withFlags(AccessFlag.INTERFACE,
|
||||
AccessFlag.ABSTRACT, AccessFlag.SYNTHETIC);
|
||||
|
|
|
@ -48,10 +48,8 @@ import static java.lang.constant.ConstantDescs.CD_Object;
|
|||
public interface ClassHierarchyResolver {
|
||||
|
||||
/**
|
||||
* Returns a default instance of {@linkplain ClassHierarchyResolver}
|
||||
* that reads from system class loader with
|
||||
* {@link ClassLoader#getSystemResourceAsStream(String)} and falls
|
||||
* back to reflection if a class is not found.
|
||||
* Returns a default instance of {@linkplain ClassHierarchyResolver} that
|
||||
* gets {@link ClassHierarchyInfo} from system class loader with reflection.
|
||||
*/
|
||||
static ClassHierarchyResolver defaultResolver() {
|
||||
return ClassHierarchyImpl.DEFAULT_RESOLVER;
|
||||
|
|
|
@ -72,25 +72,6 @@ public sealed interface ClassModel
|
|||
/** {@return the interfaces implemented by this class} */
|
||||
List<ClassEntry> interfaces();
|
||||
|
||||
/**
|
||||
* Transform this classfile into a new classfile with the aid of a
|
||||
* {@link ClassTransform}. The transform will receive each element of
|
||||
* this class, as well as a {@link ClassBuilder} for building the new class.
|
||||
* The transform is free to preserve, remove, or replace elements as it
|
||||
* sees fit.
|
||||
*
|
||||
* @implNote
|
||||
* <p>This method behaves as if:
|
||||
* {@snippet lang=java :
|
||||
* Classfile.build(thisClass(), ConstantPoolBuilder.of(this),
|
||||
* b -> b.transform(this, transform));
|
||||
* }
|
||||
*
|
||||
* @param transform the transform
|
||||
* @return the bytes of the new class
|
||||
*/
|
||||
byte[] transform(ClassTransform transform);
|
||||
|
||||
/** {@return whether this class is a module descriptor} */
|
||||
boolean isModuleInfo();
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ public sealed interface ClassReader extends ConstantPool
|
|||
|
||||
/**
|
||||
* {@return the table of custom attribute mappers} This is derived from
|
||||
* the processing option {@link Classfile.Option#attributeMapper(Function)}.
|
||||
* the processing option {@link Classfile.AttributeMapperOption}.
|
||||
*/
|
||||
Function<Utf8Entry, AttributeMapper<?>> customAttributes();
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -32,7 +32,7 @@ import jdk.internal.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
|
|||
/**
|
||||
* A transformation on streams of elements. Transforms are used during
|
||||
* transformation of classfile entities; a transform is provided to a method like
|
||||
* {@link ClassModel#transform(ClassTransform)}, and the elements of the class,
|
||||
* {@link Classfile#transform(ClassModel, ClassTransform)}, and the elements of the class,
|
||||
* along with a builder, are presented to the transform.
|
||||
*
|
||||
* <p>The subtypes of {@linkplain
|
||||
|
|
|
@ -39,7 +39,7 @@ import jdk.internal.classfile.impl.AbstractPseudoInstruction;
|
|||
* between instructions and labels. Pseudo-instructions are delivered as part
|
||||
* of the element stream of a {@link CodeModel}. Delivery of some
|
||||
* pseudo-instructions can be disabled by modifying the value of classfile
|
||||
* options (e.g., {@link Classfile.Option#processDebug(boolean)}).
|
||||
* options (e.g., {@link Classfile.DebugElementsOption}).
|
||||
*/
|
||||
public sealed interface PseudoInstruction
|
||||
extends CodeElement
|
||||
|
|
|
@ -67,7 +67,7 @@ public sealed interface ConstantValueAttribute
|
|||
case Long l -> TemporaryConstantPool.INSTANCE.longEntry(l);
|
||||
case Double d -> TemporaryConstantPool.INSTANCE.doubleEntry(d);
|
||||
case String s -> TemporaryConstantPool.INSTANCE.stringEntry(s);
|
||||
default -> throw new IllegalArgumentException("Invalid ConstantValueAtrtibute value: " + value);
|
||||
default -> throw new IllegalArgumentException("Invalid ConstantValueAttribute value: " + value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ import jdk.internal.classfile.impl.UnboundAttribute;
|
|||
* the code table and line numbers in the source file.
|
||||
* Delivered as a {@link jdk.internal.classfile.instruction.LineNumber} when traversing the
|
||||
* elements of a {@link jdk.internal.classfile.CodeModel}, according to the setting of the
|
||||
* {@link jdk.internal.classfile.Classfile.Option#processLineNumbers(boolean)} option.
|
||||
* {@link jdk.internal.classfile.Classfile.LineNumbersOption} option.
|
||||
*/
|
||||
public sealed interface LineNumberTableAttribute
|
||||
extends Attribute<LineNumberTableAttribute>
|
||||
|
|
|
@ -36,7 +36,7 @@ import java.util.List;
|
|||
* variables.
|
||||
* Delivered as a {@link jdk.internal.classfile.instruction.LocalVariable} when traversing the
|
||||
* elements of a {@link jdk.internal.classfile.CodeModel}, according to the setting of the
|
||||
* {@link jdk.internal.classfile.Classfile.Option#processDebug(boolean)} option.
|
||||
* {@link jdk.internal.classfile.Classfile.DebugElementsOption} option.
|
||||
*/
|
||||
public sealed interface LocalVariableTableAttribute
|
||||
extends Attribute<LocalVariableTableAttribute>
|
||||
|
|
|
@ -37,7 +37,7 @@ import java.util.List;
|
|||
* variables.
|
||||
* Delivered as a {@link jdk.internal.classfile.instruction.LocalVariable} when traversing the
|
||||
* elements of a {@link jdk.internal.classfile.CodeModel}, according to the setting of the
|
||||
* {@link jdk.internal.classfile.Classfile.Option#processLineNumbers(boolean)} option.
|
||||
* {@link jdk.internal.classfile.Classfile.LineNumbersOption} option.
|
||||
*/
|
||||
public sealed interface LocalVariableTypeTableAttribute
|
||||
extends Attribute<LocalVariableTypeTableAttribute>
|
||||
|
|
|
@ -96,11 +96,11 @@ public sealed interface ClassRemapper extends ClassTransform permits ClassRemapp
|
|||
|
||||
/**
|
||||
* Remaps the whole ClassModel into a new class file, including the class name.
|
||||
* @param context Classfile context
|
||||
* @param clm class model to re-map
|
||||
* @return re-mapped class file bytes
|
||||
*/
|
||||
default byte[] remapClass(ClassModel clm) {
|
||||
return Classfile.build(map(clm.thisClass().asSymbol()),
|
||||
clb -> clm.forEachElement(resolve(clb).consumer()));
|
||||
default byte[] remapClass(Classfile context, ClassModel clm) {
|
||||
return context.transform(clm, map(clm.thisClass().asSymbol()), this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import java.util.ArrayDeque;
|
|||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import jdk.internal.classfile.ClassModel;
|
||||
import jdk.internal.classfile.ClassTransform;
|
||||
import jdk.internal.classfile.CodeModel;
|
||||
|
@ -101,9 +102,9 @@ class PackageSnippets {
|
|||
// @start region="singleClassRemap"
|
||||
var classRemapper = ClassRemapper.of(
|
||||
Map.of(ClassDesc.of("Foo"), ClassDesc.of("Bar")));
|
||||
|
||||
var cc = Classfile.of();
|
||||
for (var classModel : allMyClasses) {
|
||||
byte[] newBytes = classRemapper.remapClass(classModel);
|
||||
byte[] newBytes = classRemapper.remapClass(cc, classModel);
|
||||
|
||||
}
|
||||
// @end
|
||||
|
@ -113,9 +114,9 @@ class PackageSnippets {
|
|||
// @start region="allPackageRemap"
|
||||
var classRemapper = ClassRemapper.of(cd ->
|
||||
ClassDesc.ofDescriptor(cd.descriptorString().replace("Lcom/oldpackage/", "Lcom/newpackage/")));
|
||||
|
||||
var cc = Classfile.of();
|
||||
for (var classModel : allMyClasses) {
|
||||
byte[] newBytes = classRemapper.remapClass(classModel);
|
||||
byte[] newBytes = classRemapper.remapClass(cc, classModel);
|
||||
|
||||
}
|
||||
// @end
|
||||
|
@ -123,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) -> {
|
||||
|
|
|
@ -39,7 +39,7 @@ import jdk.internal.classfile.ClassBuilder;
|
|||
import jdk.internal.classfile.ClassModel;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import jdk.internal.classfile.impl.ClassReaderImpl;
|
||||
import jdk.internal.classfile.impl.Options;
|
||||
import jdk.internal.classfile.impl.ClassfileImpl;
|
||||
import java.lang.constant.ModuleDesc;
|
||||
import java.lang.constant.PackageDesc;
|
||||
import jdk.internal.classfile.WritableElement;
|
||||
|
@ -63,29 +63,22 @@ public sealed interface ConstantPoolBuilder
|
|||
permits SplitConstantPool, TemporaryConstantPool {
|
||||
|
||||
/**
|
||||
* {@return a new constant pool builder} The new constant pool builder
|
||||
* will inherit the classfile processing options of the specified class.
|
||||
* If the processing options include {@link Classfile.Option#constantPoolSharing(boolean)},
|
||||
* (the default) the new constant pool builder will be also be pre-populated with the
|
||||
* contents of the constant pool associated with the class reader.
|
||||
* {@return a new constant pool builder} The new constant pool builder will
|
||||
* be pre-populated with the contents of the constant pool associated with
|
||||
* the class reader.
|
||||
*
|
||||
* @param classModel the class to copy from
|
||||
*/
|
||||
static ConstantPoolBuilder of(ClassModel classModel) {
|
||||
ClassReaderImpl reader = (ClassReaderImpl) classModel.constantPool();
|
||||
return reader.options().cpSharing
|
||||
? new SplitConstantPool(reader)
|
||||
: new SplitConstantPool(reader.options());
|
||||
return new SplitConstantPool((ClassReaderImpl) classModel.constantPool());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a new constant pool builder} The new constant pool builder
|
||||
* will be empty and have the specified classfile processing options.
|
||||
*
|
||||
* @param options the processing options
|
||||
* will be empty.
|
||||
*/
|
||||
static ConstantPoolBuilder of(Collection<Classfile.Option> options) {
|
||||
return new SplitConstantPool(new Options(options));
|
||||
static ConstantPoolBuilder of() {
|
||||
return new SplitConstantPool();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,11 +30,13 @@ import jdk.internal.classfile.Attribute;
|
|||
|
||||
public class AbstractDirectBuilder<M> {
|
||||
protected final SplitConstantPool constantPool;
|
||||
protected final ClassfileImpl context;
|
||||
protected final AttributeHolder attributes = new AttributeHolder();
|
||||
protected M original;
|
||||
|
||||
public AbstractDirectBuilder(SplitConstantPool constantPool) {
|
||||
public AbstractDirectBuilder(SplitConstantPool constantPool, ClassfileImpl context) {
|
||||
this.constantPool = constantPool;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public SplitConstantPool constantPool() {
|
||||
|
|
|
@ -30,6 +30,8 @@ import jdk.internal.classfile.constantpool.*;
|
|||
import java.lang.constant.ConstantDesc;
|
||||
import java.util.List;
|
||||
|
||||
import static jdk.internal.classfile.Classfile.*;
|
||||
|
||||
public final class AnnotationImpl implements Annotation {
|
||||
private final Utf8Entry className;
|
||||
private final List<AnnotationElement> elements;
|
||||
|
@ -113,7 +115,7 @@ public final class AnnotationImpl implements Annotation {
|
|||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 's';
|
||||
return AEV_STRING;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -127,7 +129,7 @@ public final class AnnotationImpl implements Annotation {
|
|||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'D';
|
||||
return AEV_DOUBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -141,7 +143,7 @@ public final class AnnotationImpl implements Annotation {
|
|||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'F';
|
||||
return AEV_FLOAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -155,7 +157,7 @@ public final class AnnotationImpl implements Annotation {
|
|||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'J';
|
||||
return AEV_LONG;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -169,7 +171,7 @@ public final class AnnotationImpl implements Annotation {
|
|||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'I';
|
||||
return AEV_INT;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -183,7 +185,7 @@ public final class AnnotationImpl implements Annotation {
|
|||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'S';
|
||||
return AEV_SHORT;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -197,7 +199,7 @@ public final class AnnotationImpl implements Annotation {
|
|||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'C';
|
||||
return AEV_CHAR;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -211,7 +213,7 @@ public final class AnnotationImpl implements Annotation {
|
|||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'B';
|
||||
return AEV_BYTE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -225,7 +227,7 @@ public final class AnnotationImpl implements Annotation {
|
|||
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'Z';
|
||||
return AEV_BOOLEAN;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -243,7 +245,7 @@ public final class AnnotationImpl implements Annotation {
|
|||
|
||||
@Override
|
||||
public char tag() {
|
||||
return '[';
|
||||
return AEV_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -258,7 +260,7 @@ public final class AnnotationImpl implements Annotation {
|
|||
implements AnnotationValue.OfEnum {
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'e';
|
||||
return AEV_ENUM;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -274,7 +276,7 @@ public final class AnnotationImpl implements Annotation {
|
|||
implements AnnotationValue.OfAnnotation {
|
||||
@Override
|
||||
public char tag() {
|
||||
return '@';
|
||||
return AEV_ANNOTATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -289,7 +291,7 @@ public final class AnnotationImpl implements Annotation {
|
|||
implements AnnotationValue.OfClass {
|
||||
@Override
|
||||
public char tag() {
|
||||
return 'c';
|
||||
return AEV_CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -58,19 +58,19 @@ class AnnotationReader {
|
|||
char tag = (char) classReader.readU1(p);
|
||||
++p;
|
||||
return switch (tag) {
|
||||
case 'B' -> new AnnotationImpl.OfByteImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case 'C' -> new AnnotationImpl.OfCharacterImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case 'D' -> new AnnotationImpl.OfDoubleImpl((DoubleEntry)classReader.readEntry(p));
|
||||
case 'F' -> new AnnotationImpl.OfFloatImpl((FloatEntry)classReader.readEntry(p));
|
||||
case 'I' -> new AnnotationImpl.OfIntegerImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case 'J' -> new AnnotationImpl.OfLongImpl((LongEntry)classReader.readEntry(p));
|
||||
case 'S' -> new AnnotationImpl.OfShortImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case 'Z' -> new AnnotationImpl.OfBooleanImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case 's' -> new AnnotationImpl.OfStringImpl(classReader.readUtf8Entry(p));
|
||||
case 'e' -> new AnnotationImpl.OfEnumImpl(classReader.readUtf8Entry(p), classReader.readUtf8Entry(p + 2));
|
||||
case 'c' -> new AnnotationImpl.OfClassImpl(classReader.readUtf8Entry(p));
|
||||
case '@' -> new AnnotationImpl.OfAnnotationImpl(readAnnotation(classReader, p));
|
||||
case '[' -> {
|
||||
case AEV_BYTE -> new AnnotationImpl.OfByteImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case AEV_CHAR -> new AnnotationImpl.OfCharacterImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case AEV_DOUBLE -> new AnnotationImpl.OfDoubleImpl((DoubleEntry)classReader.readEntry(p));
|
||||
case AEV_FLOAT -> new AnnotationImpl.OfFloatImpl((FloatEntry)classReader.readEntry(p));
|
||||
case AEV_INT -> new AnnotationImpl.OfIntegerImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case AEV_LONG -> new AnnotationImpl.OfLongImpl((LongEntry)classReader.readEntry(p));
|
||||
case AEV_SHORT -> new AnnotationImpl.OfShortImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case AEV_BOOLEAN -> new AnnotationImpl.OfBooleanImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case AEV_STRING -> new AnnotationImpl.OfStringImpl(classReader.readUtf8Entry(p));
|
||||
case AEV_ENUM -> new AnnotationImpl.OfEnumImpl(classReader.readUtf8Entry(p), classReader.readUtf8Entry(p + 2));
|
||||
case AEV_CLASS -> new AnnotationImpl.OfClassImpl(classReader.readUtf8Entry(p));
|
||||
case AEV_ANNOTATION -> new AnnotationImpl.OfAnnotationImpl(readAnnotation(classReader, p));
|
||||
case AEV_ARRAY -> {
|
||||
int numValues = classReader.readU2(p);
|
||||
p += 2;
|
||||
var values = new Object[numValues];
|
||||
|
|
|
@ -144,7 +144,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
|
|||
}
|
||||
if (mapper != null) {
|
||||
filled[i] = mapper.readAttribute(enclosing, reader, p);
|
||||
} else if (((ClassReaderImpl)reader).options().processUnknownAttributes) {
|
||||
} else if (((ClassReaderImpl)reader).context().unknownAttributesOption() == Classfile.UnknownAttributesOption.PASS_UNKNOWN_ATTRIBUTES) {
|
||||
AttributeMapper<UnknownAttribute> fakeMapper = new AttributeMapper<>() {
|
||||
@Override
|
||||
public String name() {
|
||||
|
|
|
@ -39,22 +39,24 @@ import jdk.internal.classfile.constantpool.PoolEntry;
|
|||
public final class BufWriterImpl implements BufWriter {
|
||||
|
||||
private final ConstantPoolBuilder constantPool;
|
||||
private final ClassfileImpl context;
|
||||
private LabelContext labelContext;
|
||||
private final ClassEntry thisClass;
|
||||
private final int majorVersion;
|
||||
byte[] elems;
|
||||
int offset = 0;
|
||||
|
||||
public BufWriterImpl(ConstantPoolBuilder constantPool) {
|
||||
this(constantPool, 64, null, 0);
|
||||
public BufWriterImpl(ConstantPoolBuilder constantPool, ClassfileImpl context) {
|
||||
this(constantPool, context, 64, null, 0);
|
||||
}
|
||||
|
||||
public BufWriterImpl(ConstantPoolBuilder constantPool, int initialSize) {
|
||||
this(constantPool, initialSize, null, 0);
|
||||
public BufWriterImpl(ConstantPoolBuilder constantPool, ClassfileImpl context, int initialSize) {
|
||||
this(constantPool, context, initialSize, null, 0);
|
||||
}
|
||||
|
||||
public BufWriterImpl(ConstantPoolBuilder constantPool, int initialSize, ClassEntry thisClass, int majorVersion) {
|
||||
public BufWriterImpl(ConstantPoolBuilder constantPool, ClassfileImpl context, int initialSize, ClassEntry thisClass, int majorVersion) {
|
||||
this.constantPool = constantPool;
|
||||
this.context = context;
|
||||
elems = new byte[initialSize];
|
||||
this.thisClass = thisClass;
|
||||
this.majorVersion = majorVersion;
|
||||
|
@ -85,6 +87,10 @@ public final class BufWriterImpl implements BufWriter {
|
|||
return majorVersion;
|
||||
}
|
||||
|
||||
public ClassfileImpl context() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeU1(int x) {
|
||||
writeIntBytes(1, x);
|
||||
|
|
|
@ -45,6 +45,7 @@ import java.util.function.Consumer;
|
|||
public final class BufferedCodeBuilder
|
||||
implements TerminalCodeBuilder, LabelContext {
|
||||
private final SplitConstantPool constantPool;
|
||||
private final ClassfileImpl context;
|
||||
private final List<CodeElement> elements = new ArrayList<>();
|
||||
private final LabelImpl startLabel, endLabel;
|
||||
private final CodeModel original;
|
||||
|
@ -54,8 +55,10 @@ public final class BufferedCodeBuilder
|
|||
|
||||
public BufferedCodeBuilder(MethodInfo methodInfo,
|
||||
SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
CodeModel original) {
|
||||
this.constantPool = constantPool;
|
||||
this.context = context;
|
||||
this.startLabel = new LabelImpl(this, -1);
|
||||
this.endLabel = new LabelImpl(this, -1);
|
||||
this.original = original;
|
||||
|
@ -204,7 +207,7 @@ public final class BufferedCodeBuilder
|
|||
}
|
||||
|
||||
public void writeTo(BufWriter buf) {
|
||||
DirectCodeBuilder.build(methodInfo, cb -> elements.forEach(cb), constantPool, null).writeTo(buf);
|
||||
DirectCodeBuilder.build(methodInfo, cb -> elements.forEach(cb), constantPool, context, null).writeTo(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -36,6 +36,7 @@ import jdk.internal.classfile.constantpool.Utf8Entry;
|
|||
public final class BufferedFieldBuilder
|
||||
implements TerminalFieldBuilder {
|
||||
private final SplitConstantPool constantPool;
|
||||
private final ClassfileImpl context;
|
||||
private final Utf8Entry name;
|
||||
private final Utf8Entry desc;
|
||||
private final List<FieldElement> elements = new ArrayList<>();
|
||||
|
@ -43,10 +44,12 @@ public final class BufferedFieldBuilder
|
|||
private final FieldModel original;
|
||||
|
||||
public BufferedFieldBuilder(SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
Utf8Entry name,
|
||||
Utf8Entry type,
|
||||
FieldModel original) {
|
||||
this.constantPool = constantPool;
|
||||
this.context = context;
|
||||
this.name = name;
|
||||
this.desc = type;
|
||||
this.flags = AccessFlags.ofField();
|
||||
|
@ -119,7 +122,7 @@ public final class BufferedFieldBuilder
|
|||
|
||||
@Override
|
||||
public void writeTo(BufWriter buf) {
|
||||
DirectFieldBuilder fb = new DirectFieldBuilder(constantPool, name, desc, null);
|
||||
DirectFieldBuilder fb = new DirectFieldBuilder(constantPool, context, name, desc, null);
|
||||
elements.forEach(fb);
|
||||
fb.writeTo(buf);
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ public final class BufferedMethodBuilder
|
|||
implements TerminalMethodBuilder, MethodInfo {
|
||||
private final List<MethodElement> elements;
|
||||
private final SplitConstantPool constantPool;
|
||||
private final ClassfileImpl context;
|
||||
private final Utf8Entry name;
|
||||
private final Utf8Entry desc;
|
||||
private AccessFlags flags;
|
||||
|
@ -55,11 +56,13 @@ public final class BufferedMethodBuilder
|
|||
MethodTypeDesc mDesc;
|
||||
|
||||
public BufferedMethodBuilder(SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
Utf8Entry nameInfo,
|
||||
Utf8Entry typeInfo,
|
||||
MethodModel original) {
|
||||
this.elements = new ArrayList<>();
|
||||
this.constantPool = constantPool;
|
||||
this.context = context;
|
||||
this.name = nameInfo;
|
||||
this.desc = typeInfo;
|
||||
this.flags = AccessFlags.ofMethod();
|
||||
|
@ -119,21 +122,21 @@ public final class BufferedMethodBuilder
|
|||
|
||||
@Override
|
||||
public MethodBuilder withCode(Consumer<? super CodeBuilder> handler) {
|
||||
return with(new BufferedCodeBuilder(this, constantPool, null)
|
||||
return with(new BufferedCodeBuilder(this, constantPool, context, null)
|
||||
.run(handler)
|
||||
.toModel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodBuilder transformCode(CodeModel code, CodeTransform transform) {
|
||||
BufferedCodeBuilder builder = new BufferedCodeBuilder(this, constantPool, code);
|
||||
BufferedCodeBuilder builder = new BufferedCodeBuilder(this, constantPool, context, code);
|
||||
builder.transform(code, transform);
|
||||
return with(builder.toModel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedCodeBuilder bufferedCodeBuilder(CodeModel original) {
|
||||
return new BufferedCodeBuilder(this, constantPool, original);
|
||||
return new BufferedCodeBuilder(this, constantPool, context, original);
|
||||
}
|
||||
|
||||
public BufferedMethodBuilder run(Consumer<? super MethodBuilder> handler) {
|
||||
|
@ -204,7 +207,7 @@ public final class BufferedMethodBuilder
|
|||
|
||||
@Override
|
||||
public void writeTo(BufWriter buf) {
|
||||
DirectMethodBuilder mb = new DirectMethodBuilder(constantPool, name, desc, methodFlags(), null);
|
||||
DirectMethodBuilder mb = new DirectMethodBuilder(constantPool, context, name, desc, methodFlags(), null);
|
||||
elements.forEach(mb);
|
||||
mb.writeTo(buf);
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ public final class ChainedClassBuilder
|
|||
|
||||
@Override
|
||||
public ClassBuilder withField(Utf8Entry name, Utf8Entry descriptor, Consumer<? super FieldBuilder> handler) {
|
||||
return downstream.with(new BufferedFieldBuilder(terminal.constantPool,
|
||||
return downstream.with(new BufferedFieldBuilder(terminal.constantPool, terminal.context,
|
||||
name, descriptor, null)
|
||||
.run(handler)
|
||||
.toModel());
|
||||
|
@ -68,7 +68,7 @@ public final class ChainedClassBuilder
|
|||
|
||||
@Override
|
||||
public ClassBuilder transformField(FieldModel field, FieldTransform transform) {
|
||||
BufferedFieldBuilder builder = new BufferedFieldBuilder(terminal.constantPool,
|
||||
BufferedFieldBuilder builder = new BufferedFieldBuilder(terminal.constantPool, terminal.context,
|
||||
field.fieldName(), field.fieldType(),
|
||||
field);
|
||||
builder.transform(field, transform);
|
||||
|
@ -78,7 +78,7 @@ public final class ChainedClassBuilder
|
|||
@Override
|
||||
public ClassBuilder withMethod(Utf8Entry name, Utf8Entry descriptor, int flags,
|
||||
Consumer<? super MethodBuilder> handler) {
|
||||
return downstream.with(new BufferedMethodBuilder(terminal.constantPool,
|
||||
return downstream.with(new BufferedMethodBuilder(terminal.constantPool, terminal.context,
|
||||
name, descriptor, null)
|
||||
.run(handler)
|
||||
.toModel());
|
||||
|
@ -86,7 +86,7 @@ public final class ChainedClassBuilder
|
|||
|
||||
@Override
|
||||
public ClassBuilder transformMethod(MethodModel method, MethodTransform transform) {
|
||||
BufferedMethodBuilder builder = new BufferedMethodBuilder(terminal.constantPool,
|
||||
BufferedMethodBuilder builder = new BufferedMethodBuilder(terminal.constantPool, terminal.context,
|
||||
method.methodName(), method.methodType(), method);
|
||||
builder.transform(method, transform);
|
||||
return downstream.with(builder.toModel());
|
||||
|
|
|
@ -32,14 +32,13 @@ import java.lang.constant.ClassDesc;
|
|||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import jdk.internal.classfile.ClassHierarchyResolver;
|
||||
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
import static jdk.internal.classfile.Classfile.*;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Class hierarchy resolution framework is answering questions about classes assignability, common classes ancestor and whether the class represents an interface.
|
||||
|
@ -52,15 +51,8 @@ public final class ClassHierarchyImpl {
|
|||
static final ClassHierarchyResolver.ClassHierarchyInfo OBJECT_INFO = new ClassHierarchyInfoImpl(null, false);
|
||||
}
|
||||
|
||||
public static final ClassHierarchyResolver DEFAULT_RESOLVER = ClassHierarchyResolver
|
||||
.ofResourceParsing(ResourceParsingClassHierarchyResolver.SYSTEM_STREAM_PROVIDER)
|
||||
.orElse(new ClassLoadingClassHierarchyResolver(ClassLoadingClassHierarchyResolver.SYSTEM_CLASS_PROVIDER))
|
||||
.cached(new Supplier<>() {
|
||||
@Override
|
||||
public Map<ClassDesc, ClassHierarchyResolver.ClassHierarchyInfo> get() {
|
||||
return new ConcurrentHashMap<>();
|
||||
}
|
||||
});
|
||||
public static final ClassHierarchyResolver DEFAULT_RESOLVER =
|
||||
new ClassLoadingClassHierarchyResolver(ClassLoadingClassHierarchyResolver.SYSTEM_CLASS_PROVIDER);
|
||||
|
||||
private final ClassHierarchyResolver resolver;
|
||||
|
||||
|
@ -69,7 +61,10 @@ public final class ClassHierarchyImpl {
|
|||
* @param classHierarchyResolver <code>ClassHierarchyInfoResolver</code> instance
|
||||
*/
|
||||
public ClassHierarchyImpl(ClassHierarchyResolver classHierarchyResolver) {
|
||||
this.resolver = classHierarchyResolver;
|
||||
requireNonNull(classHierarchyResolver);
|
||||
this.resolver = classHierarchyResolver instanceof CachedClassHierarchyResolver
|
||||
? classHierarchyResolver
|
||||
: classHierarchyResolver.cached();
|
||||
}
|
||||
|
||||
private ClassHierarchyInfoImpl resolve(ClassDesc classDesc) {
|
||||
|
|
|
@ -63,9 +63,8 @@ public final class ClassImpl
|
|||
private List<Attribute<?>> attributes;
|
||||
private List<ClassEntry> interfaces;
|
||||
|
||||
public ClassImpl(byte[] cfbytes,
|
||||
Collection<Classfile.Option> options) {
|
||||
this.reader = new ClassReaderImpl(cfbytes, options);
|
||||
public ClassImpl(byte[] cfbytes, ClassfileImpl context) {
|
||||
this.reader = new ClassReaderImpl(cfbytes, context);
|
||||
ClassReaderImpl reader = (ClassReaderImpl) this.reader;
|
||||
int p = reader.interfacesPos;
|
||||
int icnt = reader.readU2(p);
|
||||
|
@ -94,6 +93,10 @@ public final class ClassImpl
|
|||
reader.setContainedClass(this);
|
||||
}
|
||||
|
||||
public int classfileLength() {
|
||||
return reader.classfileLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessFlags flags() {
|
||||
return AccessFlags.ofClass(reader.flags());
|
||||
|
@ -169,20 +172,6 @@ public final class ClassImpl
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] transform(ClassTransform transform) {
|
||||
ConstantPoolBuilder constantPool = ConstantPoolBuilder.of(this);
|
||||
return Classfile.build(thisClass(), constantPool,
|
||||
new Consumer<ClassBuilder>() {
|
||||
@Override
|
||||
public void accept(ClassBuilder builder) {
|
||||
((DirectClassBuilder) builder).setOriginal(ClassImpl.this);
|
||||
((DirectClassBuilder) builder).setSizeHint(reader.classfileLength());
|
||||
builder.transform(ClassImpl.this, transform);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FieldModel> fields() {
|
||||
return fields;
|
||||
|
|
|
@ -77,7 +77,7 @@ public final class ClassReaderImpl
|
|||
private final int constantPoolCount;
|
||||
private final int[] cpOffset;
|
||||
|
||||
final Options options;
|
||||
final ClassfileImpl context;
|
||||
final int interfacesPos;
|
||||
final PoolEntry[] cp;
|
||||
|
||||
|
@ -86,11 +86,11 @@ public final class ClassReaderImpl
|
|||
private BootstrapMethodsAttribute bootstrapMethodsAttribute;
|
||||
|
||||
ClassReaderImpl(byte[] classfileBytes,
|
||||
Collection<Classfile.Option> options) {
|
||||
ClassfileImpl context) {
|
||||
this.buffer = classfileBytes;
|
||||
this.classfileLength = classfileBytes.length;
|
||||
this.options = new Options(options);
|
||||
this.attributeMapper = this.options.attributeMapper;
|
||||
this.context = context;
|
||||
this.attributeMapper = this.context.attributeMapperOption().attributeMapper();
|
||||
if (classfileLength < 4 || readInt(0) != 0xCAFEBABE) {
|
||||
throw new IllegalArgumentException("Bad magic number");
|
||||
}
|
||||
|
@ -134,8 +134,8 @@ public final class ClassReaderImpl
|
|||
this.interfacesPos = p;
|
||||
}
|
||||
|
||||
public Options options() {
|
||||
return options;
|
||||
public ClassfileImpl context() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.classfile.impl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jdk.internal.classfile.AttributeMapper;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import jdk.internal.classfile.Classfile.*;
|
||||
import jdk.internal.classfile.ClassBuilder;
|
||||
import jdk.internal.classfile.ClassHierarchyResolver;
|
||||
import jdk.internal.classfile.ClassModel;
|
||||
import jdk.internal.classfile.ClassTransform;
|
||||
import jdk.internal.classfile.constantpool.ClassEntry;
|
||||
import jdk.internal.classfile.constantpool.ConstantPoolBuilder;
|
||||
import jdk.internal.classfile.constantpool.Utf8Entry;
|
||||
|
||||
public record ClassfileImpl(StackMapsOption stackMapsOption,
|
||||
DebugElementsOption debugElementsOption,
|
||||
LineNumbersOption lineNumbersOption,
|
||||
UnknownAttributesOption unknownAttributesOption,
|
||||
ConstantPoolSharingOption constantPoolSharingOption,
|
||||
ShortJumpsOption shortJumpsOption,
|
||||
DeadCodeOption deadCodeOption,
|
||||
DeadLabelsOption deadLabelsOption,
|
||||
ClassHierarchyResolverOption classHierarchyResolverOption,
|
||||
AttributeMapperOption attributeMapperOption) implements Classfile {
|
||||
|
||||
public static final ClassfileImpl DEFAULT_CONTEXT = new ClassfileImpl(
|
||||
StackMapsOption.STACK_MAPS_WHEN_REQUIRED,
|
||||
DebugElementsOption.PASS_DEBUG,
|
||||
LineNumbersOption.PASS_LINE_NUMBERS,
|
||||
UnknownAttributesOption.PASS_UNKNOWN_ATTRIBUTES,
|
||||
ConstantPoolSharingOption.SHARED_POOL,
|
||||
ShortJumpsOption.FIX_SHORT_JUMPS,
|
||||
DeadCodeOption.PATCH_DEAD_CODE,
|
||||
DeadLabelsOption.FAIL_ON_DEAD_LABELS,
|
||||
new ClassHierarchyResolverOptionImpl(ClassHierarchyResolver.defaultResolver()),
|
||||
new AttributeMapperOptionImpl(new Function<>() {
|
||||
@Override
|
||||
public AttributeMapper<?> apply(Utf8Entry k) {
|
||||
return null;
|
||||
}
|
||||
}));
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public ClassfileImpl withOptions(Option... options) {
|
||||
var smo = stackMapsOption;
|
||||
var deo = debugElementsOption;
|
||||
var lno = lineNumbersOption;
|
||||
var uao = unknownAttributesOption;
|
||||
var cpso = constantPoolSharingOption;
|
||||
var sjo = shortJumpsOption;
|
||||
var dco = deadCodeOption;
|
||||
var dlo = deadLabelsOption;
|
||||
var chro = classHierarchyResolverOption;
|
||||
var amo = attributeMapperOption;
|
||||
for (var o : options) {
|
||||
switch (o) {
|
||||
case StackMapsOption oo -> smo = oo;
|
||||
case DebugElementsOption oo -> deo = oo;
|
||||
case LineNumbersOption oo -> lno = oo;
|
||||
case UnknownAttributesOption oo -> uao = oo;
|
||||
case ConstantPoolSharingOption oo -> cpso = oo;
|
||||
case ShortJumpsOption oo -> sjo = oo;
|
||||
case DeadCodeOption oo -> dco = oo;
|
||||
case DeadLabelsOption oo -> dlo = oo;
|
||||
case ClassHierarchyResolverOption oo -> chro = oo;
|
||||
case AttributeMapperOption oo -> amo = oo;
|
||||
}
|
||||
}
|
||||
return new ClassfileImpl(smo, deo, lno, uao, cpso, sjo, dco, dlo, chro, amo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassModel parse(byte[] bytes) {
|
||||
return new ClassImpl(bytes, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] build(ClassEntry thisClassEntry,
|
||||
ConstantPoolBuilder constantPool,
|
||||
Consumer<? super ClassBuilder> handler) {
|
||||
thisClassEntry = AbstractPoolEntry.maybeClone(constantPool, thisClassEntry);
|
||||
DirectClassBuilder builder = new DirectClassBuilder((SplitConstantPool)constantPool, this, thisClassEntry);
|
||||
handler.accept(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] transform(ClassModel model, ClassEntry newClassName, ClassTransform transform) {
|
||||
ConstantPoolBuilder constantPool = constantPoolSharingOption() == ConstantPoolSharingOption.SHARED_POOL
|
||||
? ConstantPoolBuilder.of(model)
|
||||
: ConstantPoolBuilder.of();
|
||||
return build(newClassName, constantPool,
|
||||
new Consumer<ClassBuilder>() {
|
||||
@Override
|
||||
public void accept(ClassBuilder builder) {
|
||||
((DirectClassBuilder) builder).setOriginal((ClassImpl)model);
|
||||
((DirectClassBuilder) builder).setSizeHint(((ClassImpl)model).classfileLength());
|
||||
builder.transform((ClassImpl)model, transform);
|
||||
}
|
||||
});
|
||||
}
|
||||
public record AttributeMapperOptionImpl(Function<Utf8Entry, AttributeMapper<?>> attributeMapper)
|
||||
implements AttributeMapperOption {
|
||||
}
|
||||
|
||||
public record ClassHierarchyResolverOptionImpl(ClassHierarchyResolver classHierarchyResolver)
|
||||
implements ClassHierarchyResolverOption {
|
||||
}
|
||||
}
|
|
@ -118,7 +118,7 @@ public final class CodeImpl
|
|||
if (!inflated) {
|
||||
if (labels == null)
|
||||
labels = new LabelImpl[codeLength + 1];
|
||||
if (((ClassReaderImpl)classReader).options().processLineNumbers)
|
||||
if (((ClassReaderImpl)classReader).context().lineNumbersOption() == Classfile.LineNumbersOption.PASS_LINE_NUMBERS)
|
||||
inflateLineNumbers();
|
||||
inflateJumpTargets();
|
||||
inflateTypeAnnotations();
|
||||
|
@ -150,6 +150,7 @@ public final class CodeImpl
|
|||
}
|
||||
},
|
||||
(SplitConstantPool)buf.constantPool(),
|
||||
((BufWriterImpl)buf).context(),
|
||||
null).writeTo(buf);
|
||||
}
|
||||
}
|
||||
|
@ -166,7 +167,7 @@ public final class CodeImpl
|
|||
inflateMetadata();
|
||||
boolean doLineNumbers = (lineNumbers != null);
|
||||
generateCatchTargets(consumer);
|
||||
if (((ClassReaderImpl)classReader).options().processDebug)
|
||||
if (((ClassReaderImpl)classReader).context().debugElementsOption() == Classfile.DebugElementsOption.PASS_DEBUG)
|
||||
generateDebugElements(consumer);
|
||||
for (int pos=codeStart; pos<codeEnd; ) {
|
||||
if (labels[pos - codeStart] != null)
|
||||
|
|
|
@ -61,14 +61,15 @@ public final class DirectClassBuilder
|
|||
private int sizeHint;
|
||||
|
||||
public DirectClassBuilder(SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
ClassEntry thisClass) {
|
||||
super(constantPool);
|
||||
super(constantPool, context);
|
||||
this.thisClassEntry = AbstractPoolEntry.maybeClone(constantPool, thisClass);
|
||||
this.flags = Classfile.DEFAULT_CLASS_FLAGS;
|
||||
this.superclassEntry = null;
|
||||
this.interfaceEntries = Collections.emptyList();
|
||||
this.majorVersion = Classfile.LATEST_MAJOR_VERSION;
|
||||
this.minorVersion = Classfile.LATEST_MINOR_VERSION;
|
||||
this.majorVersion = Classfile.latestMajorVersion();
|
||||
this.minorVersion = Classfile.latestMinorVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -81,13 +82,13 @@ public final class DirectClassBuilder
|
|||
public ClassBuilder withField(Utf8Entry name,
|
||||
Utf8Entry descriptor,
|
||||
Consumer<? super FieldBuilder> handler) {
|
||||
return withField(new DirectFieldBuilder(constantPool, name, descriptor, null)
|
||||
return withField(new DirectFieldBuilder(constantPool, context, name, descriptor, null)
|
||||
.run(handler));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassBuilder transformField(FieldModel field, FieldTransform transform) {
|
||||
DirectFieldBuilder builder = new DirectFieldBuilder(constantPool, field.fieldName(),
|
||||
DirectFieldBuilder builder = new DirectFieldBuilder(constantPool, context, field.fieldName(),
|
||||
field.fieldType(), field);
|
||||
builder.transform(field, transform);
|
||||
return withField(builder);
|
||||
|
@ -98,13 +99,13 @@ public final class DirectClassBuilder
|
|||
Utf8Entry descriptor,
|
||||
int flags,
|
||||
Consumer<? super MethodBuilder> handler) {
|
||||
return withMethod(new DirectMethodBuilder(constantPool, name, descriptor, flags, null)
|
||||
return withMethod(new DirectMethodBuilder(constantPool, context, name, descriptor, flags, null)
|
||||
.run(handler));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassBuilder transformMethod(MethodModel method, MethodTransform transform) {
|
||||
DirectMethodBuilder builder = new DirectMethodBuilder(constantPool, method.methodName(),
|
||||
DirectMethodBuilder builder = new DirectMethodBuilder(constantPool, context, method.methodName(),
|
||||
method.methodType(),
|
||||
method.flags().flagsMask(),
|
||||
method);
|
||||
|
@ -141,7 +142,7 @@ public final class DirectClassBuilder
|
|||
this.flags = flags;
|
||||
}
|
||||
|
||||
void setSizeHint(int sizeHint) {
|
||||
public void setSizeHint(int sizeHint) {
|
||||
this.sizeHint = sizeHint;
|
||||
}
|
||||
|
||||
|
@ -166,8 +167,8 @@ public final class DirectClassBuilder
|
|||
|
||||
// We maintain two writers, and then we join them at the end
|
||||
int size = sizeHint == 0 ? 256 : sizeHint;
|
||||
BufWriter head = new BufWriterImpl(constantPool, size);
|
||||
BufWriterImpl tail = new BufWriterImpl(constantPool, size, thisClassEntry, majorVersion);
|
||||
BufWriter head = new BufWriterImpl(constantPool, context, size);
|
||||
BufWriterImpl tail = new BufWriterImpl(constantPool, context, size, thisClassEntry, majorVersion);
|
||||
|
||||
// The tail consists of fields and methods, and attributes
|
||||
// This should trigger all the CP/BSM mutation
|
||||
|
|
|
@ -41,6 +41,7 @@ import jdk.internal.classfile.Classfile;
|
|||
import jdk.internal.classfile.CodeBuilder;
|
||||
import jdk.internal.classfile.CodeElement;
|
||||
import jdk.internal.classfile.CodeModel;
|
||||
import jdk.internal.classfile.Instruction;
|
||||
import jdk.internal.classfile.Label;
|
||||
import jdk.internal.classfile.Opcode;
|
||||
import jdk.internal.classfile.TypeKind;
|
||||
|
@ -101,14 +102,15 @@ public final class DirectCodeBuilder
|
|||
public static Attribute<CodeAttribute> build(MethodInfo methodInfo,
|
||||
Consumer<? super CodeBuilder> handler,
|
||||
SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
CodeModel original) {
|
||||
DirectCodeBuilder cb;
|
||||
try {
|
||||
handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, original, false));
|
||||
handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, false));
|
||||
cb.buildContent();
|
||||
} catch (LabelOverflowException loe) {
|
||||
if (constantPool.options().fixJumps) {
|
||||
handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, original, true));
|
||||
if (context.shortJumpsOption() == Classfile.ShortJumpsOption.FIX_SHORT_JUMPS) {
|
||||
handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, true));
|
||||
cb.buildContent();
|
||||
}
|
||||
else
|
||||
|
@ -119,15 +121,16 @@ public final class DirectCodeBuilder
|
|||
|
||||
private DirectCodeBuilder(MethodInfo methodInfo,
|
||||
SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
CodeModel original,
|
||||
boolean transformFwdJumps) {
|
||||
super(constantPool);
|
||||
super(constantPool, context);
|
||||
setOriginal(original);
|
||||
this.methodInfo = methodInfo;
|
||||
this.transformFwdJumps = transformFwdJumps;
|
||||
this.transformBackJumps = constantPool.options().fixJumps;
|
||||
bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, cai.codeLength())
|
||||
: new BufWriterImpl(constantPool);
|
||||
this.transformBackJumps = context.shortJumpsOption() == Classfile.ShortJumpsOption.FIX_SHORT_JUMPS;
|
||||
bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, context, cai.codeLength())
|
||||
: new BufWriterImpl(constantPool, context);
|
||||
this.startLabel = new LabelImpl(this, 0);
|
||||
this.endLabel = new LabelImpl(this, -1);
|
||||
this.topLocal = Util.maxLocals(methodInfo.methodFlags(), methodInfo.methodTypeSymbol());
|
||||
|
@ -196,7 +199,7 @@ public final class DirectCodeBuilder
|
|||
int endPc = labelToBci(h.tryEnd());
|
||||
int handlerPc = labelToBci(h.handler());
|
||||
if (startPc == -1 || endPc == -1 || handlerPc == -1) {
|
||||
if (constantPool.options().filterDeadLabels) {
|
||||
if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) {
|
||||
handlersSize--;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unbound label in exception handler");
|
||||
|
@ -220,7 +223,7 @@ public final class DirectCodeBuilder
|
|||
// Backfill branches for which Label didn't have position yet
|
||||
processDeferredLabels();
|
||||
|
||||
if (constantPool.options().processDebug) {
|
||||
if (context.debugElementsOption() == Classfile.DebugElementsOption.PASS_DEBUG) {
|
||||
if (!characterRanges.isEmpty()) {
|
||||
Attribute<?> a = new UnboundAttribute.AdHocAttribute<>(Attributes.CHARACTER_RANGE_TABLE) {
|
||||
|
||||
|
@ -233,7 +236,7 @@ public final class DirectCodeBuilder
|
|||
var start = labelToBci(cr.startScope());
|
||||
var end = labelToBci(cr.endScope());
|
||||
if (start == -1 || end == -1) {
|
||||
if (constantPool.options().filterDeadLabels) {
|
||||
if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) {
|
||||
crSize--;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unbound label in character range");
|
||||
|
@ -262,7 +265,7 @@ public final class DirectCodeBuilder
|
|||
b.writeU2(lvSize);
|
||||
for (LocalVariable l : localVariables) {
|
||||
if (!l.writeTo(b)) {
|
||||
if (constantPool.options().filterDeadLabels) {
|
||||
if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) {
|
||||
lvSize--;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unbound label in local variable type");
|
||||
|
@ -285,7 +288,7 @@ public final class DirectCodeBuilder
|
|||
b.writeU2(localVariableTypes.size());
|
||||
for (LocalVariableType l : localVariableTypes) {
|
||||
if (!l.writeTo(b)) {
|
||||
if (constantPool.options().filterDeadLabels) {
|
||||
if (context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS) {
|
||||
lvtSize--;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unbound label in local variable type");
|
||||
|
@ -305,6 +308,44 @@ public final class DirectCodeBuilder
|
|||
}
|
||||
|
||||
content = new UnboundAttribute.AdHocAttribute<>(Attributes.CODE) {
|
||||
|
||||
private void writeCounters(boolean codeMatch, BufWriterImpl buf) {
|
||||
if (codeMatch) {
|
||||
buf.writeU2(original.maxStack());
|
||||
buf.writeU2(original.maxLocals());
|
||||
} else {
|
||||
StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf);
|
||||
buf.writeU2(cntr.maxStack());
|
||||
buf.writeU2(cntr.maxLocals());
|
||||
}
|
||||
}
|
||||
|
||||
private void generateStackMaps(BufWriterImpl buf) throws IllegalArgumentException {
|
||||
//new instance of generator immediately calculates maxStack, maxLocals, all frames,
|
||||
// patches dead bytecode blocks and removes them from exception table
|
||||
StackMapGenerator gen = StackMapGenerator.of(DirectCodeBuilder.this, buf);
|
||||
attributes.withAttribute(gen.stackMapTableAttribute());
|
||||
buf.writeU2(gen.maxStack());
|
||||
buf.writeU2(gen.maxLocals());
|
||||
}
|
||||
|
||||
private void tryGenerateStackMaps(boolean codeMatch, BufWriterImpl buf) {
|
||||
if (buf.getMajorVersion() >= Classfile.JAVA_6_VERSION) {
|
||||
try {
|
||||
generateStackMaps(buf);
|
||||
} catch (IllegalArgumentException e) {
|
||||
//failover following JVMS-4.10
|
||||
if (buf.getMajorVersion() == Classfile.JAVA_6_VERSION) {
|
||||
writeCounters(codeMatch, buf);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
writeCounters(codeMatch, buf);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBody(BufWriter b) {
|
||||
BufWriterImpl buf = (BufWriterImpl) b;
|
||||
|
@ -318,52 +359,29 @@ public final class DirectCodeBuilder
|
|||
methodInfo.methodName().stringValue(),
|
||||
methodInfo.methodTypeSymbol().displayDescriptor()));
|
||||
}
|
||||
int maxStack, maxLocals;
|
||||
Attribute<? extends StackMapTableAttribute> stackMapAttr;
|
||||
boolean canReuseStackmaps = codeAndExceptionsMatch(codeLength);
|
||||
|
||||
if (!constantPool.options().generateStackmaps) {
|
||||
StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf);
|
||||
maxStack = cntr.maxStack();
|
||||
maxLocals = cntr.maxLocals();
|
||||
stackMapAttr = null;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -42,10 +42,11 @@ public final class DirectFieldBuilder
|
|||
private int flags;
|
||||
|
||||
public DirectFieldBuilder(SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
Utf8Entry name,
|
||||
Utf8Entry type,
|
||||
FieldModel original) {
|
||||
super(constantPool);
|
||||
super(constantPool, context);
|
||||
setOriginal(original);
|
||||
this.name = name;
|
||||
this.desc = type;
|
||||
|
|
|
@ -50,11 +50,12 @@ public final class DirectMethodBuilder
|
|||
MethodTypeDesc mDesc;
|
||||
|
||||
public DirectMethodBuilder(SplitConstantPool constantPool,
|
||||
ClassfileImpl context,
|
||||
Utf8Entry nameInfo,
|
||||
Utf8Entry typeInfo,
|
||||
int flags,
|
||||
MethodModel original) {
|
||||
super(constantPool);
|
||||
super(constantPool, context);
|
||||
setOriginal(original);
|
||||
this.name = nameInfo;
|
||||
this.desc = typeInfo;
|
||||
|
@ -105,7 +106,7 @@ public final class DirectMethodBuilder
|
|||
|
||||
@Override
|
||||
public BufferedCodeBuilder bufferedCodeBuilder(CodeModel original) {
|
||||
return new BufferedCodeBuilder(this, constantPool, original);
|
||||
return new BufferedCodeBuilder(this, constantPool, context, original);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -116,7 +117,7 @@ public final class DirectMethodBuilder
|
|||
|
||||
private MethodBuilder withCode(CodeModel original,
|
||||
Consumer<? super CodeBuilder> handler) {
|
||||
var cb = DirectCodeBuilder.build(this, handler, constantPool, original);
|
||||
var cb = DirectCodeBuilder.build(this, handler, constantPool, context, original);
|
||||
writeAttribute(cb);
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.internal.classfile.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
import jdk.internal.classfile.AttributeMapper;
|
||||
import jdk.internal.classfile.ClassHierarchyResolver;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import jdk.internal.classfile.constantpool.Utf8Entry;
|
||||
|
||||
import static jdk.internal.classfile.ClassHierarchyResolver.defaultResolver;
|
||||
|
||||
public class Options {
|
||||
|
||||
public enum Key {
|
||||
GENERATE_STACK_MAPS, PROCESS_DEBUG, PROCESS_LINE_NUMBERS, PROCESS_UNKNOWN_ATTRIBUTES,
|
||||
CP_SHARING, FIX_SHORT_JUMPS, PATCH_DEAD_CODE, HIERARCHY_RESOLVER, ATTRIBUTE_MAPPER,
|
||||
FILTER_DEAD_LABELS;
|
||||
}
|
||||
|
||||
public record OptionValue(Key key, Object value) implements Classfile.Option { }
|
||||
|
||||
public Boolean generateStackmaps = true;
|
||||
public Boolean processDebug = true;
|
||||
public Boolean processLineNumbers = true;
|
||||
public Boolean processUnknownAttributes = true;
|
||||
public Boolean cpSharing = true;
|
||||
public Boolean fixJumps = true;
|
||||
public Boolean patchCode = true;
|
||||
public Boolean filterDeadLabels = false;
|
||||
public ClassHierarchyResolver classHierarchyResolver = defaultResolver();
|
||||
public Function<Utf8Entry, AttributeMapper<?>> attributeMapper = new Function<>() {
|
||||
@Override
|
||||
public AttributeMapper<?> apply(Utf8Entry k) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Options(Collection<Classfile.Option> options) {
|
||||
for (var o : options) {
|
||||
var ov = ((OptionValue)o);
|
||||
var v = ov.value();
|
||||
switch (ov.key()) {
|
||||
case GENERATE_STACK_MAPS -> generateStackmaps = (Boolean) v;
|
||||
case PROCESS_DEBUG -> processDebug = (Boolean) v;
|
||||
case PROCESS_LINE_NUMBERS -> processLineNumbers = (Boolean) v;
|
||||
case PROCESS_UNKNOWN_ATTRIBUTES -> processUnknownAttributes = (Boolean) v;
|
||||
case CP_SHARING -> cpSharing = (Boolean) v;
|
||||
case FIX_SHORT_JUMPS -> fixJumps = (Boolean) v;
|
||||
case PATCH_DEAD_CODE -> patchCode = (Boolean) v;
|
||||
case HIERARCHY_RESOLVER -> classHierarchyResolver = (ClassHierarchyResolver) v;
|
||||
case ATTRIBUTE_MAPPER -> attributeMapper = (Function<Utf8Entry, AttributeMapper<?>>) v;
|
||||
case FILTER_DEAD_LABELS -> filterDeadLabels = (Boolean) v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,7 +27,6 @@ package jdk.internal.classfile.impl;
|
|||
import java.lang.constant.ConstantDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.internal.classfile.Attribute;
|
||||
|
@ -81,7 +80,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
|
|||
|
||||
private final ClassReaderImpl parent;
|
||||
private final int parentSize, parentBsmSize;
|
||||
final Options options;
|
||||
|
||||
private int size, bsmSize;
|
||||
private PoolEntry[] myEntries;
|
||||
|
@ -91,10 +89,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
|
|||
private EntryMap<BootstrapMethodEntryImpl> bsmMap;
|
||||
|
||||
public SplitConstantPool() {
|
||||
this(new Options(Collections.emptyList()));
|
||||
}
|
||||
|
||||
public SplitConstantPool(Options options) {
|
||||
this.size = 1;
|
||||
this.bsmSize = 0;
|
||||
this.myEntries = new PoolEntry[1024];
|
||||
|
@ -102,12 +96,10 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
|
|||
this.parent = null;
|
||||
this.parentSize = 0;
|
||||
this.parentBsmSize = 0;
|
||||
this.options = options;
|
||||
this.doneFullScan = true;
|
||||
}
|
||||
|
||||
public SplitConstantPool(ClassReader parent) {
|
||||
this.options = ((ClassReaderImpl) parent).options;
|
||||
this.parent = (ClassReaderImpl) parent;
|
||||
this.parentSize = parent.entryCount();
|
||||
this.parentBsmSize = parent.bootstrapMethodCount();
|
||||
|
@ -117,18 +109,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
|
|||
this.myBsmEntries = new BootstrapMethodEntryImpl[8];
|
||||
}
|
||||
|
||||
//clone constructor for internal purposes
|
||||
SplitConstantPool(SplitConstantPool cloneFrom, Options options) {
|
||||
this.options = options;
|
||||
this.parent = cloneFrom.parent;
|
||||
this.parentSize = cloneFrom.parentSize;
|
||||
this.parentBsmSize = cloneFrom.parentBsmSize;
|
||||
this.size = cloneFrom.size;
|
||||
this.bsmSize = cloneFrom.bsmSize;
|
||||
this.myEntries = Arrays.copyOf(cloneFrom.myEntries, cloneFrom.myEntries.length);
|
||||
this.myBsmEntries = Arrays.copyOf(cloneFrom.myBsmEntries, cloneFrom.myBsmEntries.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int entryCount() {
|
||||
return size;
|
||||
|
@ -153,10 +133,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
|
|||
: myBsmEntries[index - parentBsmSize];
|
||||
}
|
||||
|
||||
public Options options() {
|
||||
return options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWriteDirect(ConstantPool other) {
|
||||
return this == other || parent == other;
|
||||
|
|
|
@ -153,6 +153,7 @@ public final class StackMapGenerator {
|
|||
(dcb.methodInfo.methodFlags() & ACC_STATIC) != 0,
|
||||
dcb.bytecodesBufWriter.asByteBuffer().slice(0, dcb.bytecodesBufWriter.size()),
|
||||
dcb.constantPool,
|
||||
dcb.context,
|
||||
dcb.handlers);
|
||||
}
|
||||
|
||||
|
@ -194,6 +195,7 @@ public final class StackMapGenerator {
|
|||
private final List<RawExceptionCatch> rawHandlers;
|
||||
private final ClassHierarchyImpl classHierarchy;
|
||||
private final boolean patchDeadCode;
|
||||
private final boolean filterDeadLabels;
|
||||
private List<Frame> frames;
|
||||
private final Frame currentFrame;
|
||||
private int maxStack, maxLocals;
|
||||
|
@ -221,6 +223,7 @@ public final class StackMapGenerator {
|
|||
boolean isStatic,
|
||||
ByteBuffer bytecode,
|
||||
SplitConstantPool cp,
|
||||
ClassfileImpl context,
|
||||
List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) {
|
||||
this.thisType = Type.referenceType(thisClass);
|
||||
this.methodName = methodName;
|
||||
|
@ -231,8 +234,9 @@ public final class StackMapGenerator {
|
|||
this.labelContext = labelContext;
|
||||
this.handlers = handlers;
|
||||
this.rawHandlers = new ArrayList<>(handlers.size());
|
||||
this.classHierarchy = new ClassHierarchyImpl(cp.options().classHierarchyResolver);
|
||||
this.patchDeadCode = cp.options().patchCode;
|
||||
this.classHierarchy = new ClassHierarchyImpl(context.classHierarchyResolverOption().classHierarchyResolver());
|
||||
this.patchDeadCode = context.deadCodeOption() == Classfile.DeadCodeOption.PATCH_DEAD_CODE;
|
||||
this.filterDeadLabels = context.deadLabelsOption() == Classfile.DeadLabelsOption.DROP_DEAD_LABELS;
|
||||
this.currentFrame = new Frame(classHierarchy);
|
||||
generate();
|
||||
}
|
||||
|
@ -832,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;
|
||||
|
|
|
@ -37,7 +37,7 @@ import jdk.internal.classfile.impl.BoundCharacterRange;
|
|||
* A pseudo-instruction which models a single entry in the
|
||||
* {@link CharacterRangeTableAttribute}. Delivered as a {@link CodeElement}
|
||||
* during traversal of the elements of a {@link CodeModel}, according to
|
||||
* the setting of the {@link Classfile.Option#processDebug(boolean)} option.
|
||||
* the setting of the {@link Classfile.DebugElementsOption} option.
|
||||
*/
|
||||
public sealed interface CharacterRange extends PseudoInstruction
|
||||
permits AbstractPseudoInstruction.UnboundCharacterRange, BoundCharacterRange {
|
||||
|
|
|
@ -136,7 +136,7 @@ public sealed interface ConstantInstruction extends Instruction {
|
|||
static ArgumentConstantInstruction ofArgument(Opcode op, int value) {
|
||||
Util.checkKind(op, Opcode.Kind.CONSTANT);
|
||||
if (op != Opcode.BIPUSH && op != Opcode.SIPUSH)
|
||||
throw new IllegalArgumentException(String.format("Wrong opcode specified; found %s, expected BIPUSH or SIPUSH", op, op.kind()));
|
||||
throw new IllegalArgumentException(String.format("Wrong opcode specified; found %s, expected BIPUSH or SIPUSH", op));
|
||||
return new AbstractInstruction.UnboundArgumentConstantInstruction(op, value);
|
||||
}
|
||||
|
||||
|
@ -150,7 +150,7 @@ public sealed interface ConstantInstruction extends Instruction {
|
|||
static LoadConstantInstruction ofLoad(Opcode op, LoadableConstantEntry constant) {
|
||||
Util.checkKind(op, Opcode.Kind.CONSTANT);
|
||||
if (op != Opcode.LDC && op != Opcode.LDC_W && op != Opcode.LDC2_W)
|
||||
throw new IllegalArgumentException(String.format("Wrong opcode specified; found %s, expected LDC, LDC_W or LDC2_W", op, op.kind()));
|
||||
throw new IllegalArgumentException(String.format("Wrong opcode specified; found %s, expected LDC, LDC_W or LDC2_W", op));
|
||||
return new AbstractInstruction.UnboundLoadConstantInstruction(op, constant);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ import jdk.internal.classfile.impl.LineNumberImpl;
|
|||
* A pseudo-instruction which models a single entry in the
|
||||
* {@link LineNumberTableAttribute}. Delivered as a {@link CodeElement}
|
||||
* during traversal of the elements of a {@link CodeModel}, according to
|
||||
* the setting of the {@link Classfile.Option#processLineNumbers(boolean)} option.
|
||||
* the setting of the {@link Classfile.LineNumbersOption} option.
|
||||
*
|
||||
* @see PseudoInstruction
|
||||
*/
|
||||
|
|
|
@ -42,7 +42,7 @@ import jdk.internal.classfile.impl.TemporaryConstantPool;
|
|||
* A pseudo-instruction which models a single entry in the
|
||||
* {@link LocalVariableTableAttribute}. Delivered as a {@link CodeElement}
|
||||
* during traversal of the elements of a {@link CodeModel}, according to
|
||||
* the setting of the {@link Classfile.Option#processDebug(boolean)} option.
|
||||
* the setting of the {@link Classfile.DebugElementsOption} option.
|
||||
*
|
||||
* @see PseudoInstruction
|
||||
*/
|
||||
|
|
|
@ -41,7 +41,7 @@ import jdk.internal.classfile.impl.TemporaryConstantPool;
|
|||
* A pseudo-instruction which models a single entry in the {@link
|
||||
* LocalVariableTypeTableAttribute}. Delivered as a {@link CodeElement} during
|
||||
* traversal of the elements of a {@link CodeModel}, according to the setting of
|
||||
* the {@link Classfile.Option#processDebug(boolean)} option.
|
||||
* the {@link Classfile.DebugElementsOption} option.
|
||||
*/
|
||||
public sealed interface LocalVariableType extends PseudoInstruction
|
||||
permits AbstractPseudoInstruction.UnboundLocalVariableType, BoundLocalVariableType {
|
||||
|
|
|
@ -33,10 +33,10 @@
|
|||
* <h2>Reading classfiles</h2>
|
||||
* The main class for reading classfiles is {@link jdk.internal.classfile.ClassModel}; we
|
||||
* convert bytes into a {@link jdk.internal.classfile.ClassModel} with {@link
|
||||
* jdk.internal.classfile.Classfile#parse(byte[], jdk.internal.classfile.Classfile.Option[])}:
|
||||
* jdk.internal.classfile.Classfile#parse(byte[])}:
|
||||
* <p>
|
||||
* {@snippet lang=java :
|
||||
* ClassModel cm = Classfile.parse(bytes);
|
||||
* ClassModel cm = Classfile.of().parse(bytes);
|
||||
* }
|
||||
* <p>
|
||||
* There are several additional overloads of {@code parse} that let you specify
|
||||
|
@ -164,31 +164,30 @@
|
|||
* <p>
|
||||
* For nonstandard attributes, user-provided attribute mappers can be specified
|
||||
* through the use of the {@link
|
||||
* jdk.internal.classfile.Classfile.Option#attributeMapper(java.util.function.Function)}}
|
||||
* jdk.internal.classfile.Classfile.AttributeMapperOption#of(java.util.function.Function)}}
|
||||
* classfile option. Implementations of custom attributes should extend {@link
|
||||
* jdk.internal.classfile.CustomAttribute}.
|
||||
*
|
||||
* <h3>Options</h3>
|
||||
* <p>
|
||||
* {@link jdk.internal.classfile.Classfile#parse(byte[], jdk.internal.classfile.Classfile.Option[])}
|
||||
* accepts a list of options. {@link jdk.internal.classfile.Classfile.Option} exports some
|
||||
* static boolean options, as well as factories for more complex options,
|
||||
* {@link jdk.internal.classfile.Classfile#of(jdk.internal.classfile.Classfile.Option[])}
|
||||
* accepts a list of options. {@link jdk.internal.classfile.Classfile.Option} is a base interface
|
||||
* for some statically enumerated options, as well as factories for more complex options,
|
||||
* including:
|
||||
* <ul>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.Option#generateStackmap(boolean)}
|
||||
* -- generate stackmaps (default is true)</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.Option#processDebug(boolean)}
|
||||
* -- processing of debug information, such as local variable metadata (default is true) </li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.Option#processLineNumbers(boolean)}
|
||||
* -- processing of line numbers (default is true) </li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.Option#processUnknownAttributes(boolean)}
|
||||
* -- processing of unrecognized attributes (default is true)</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.Option#constantPoolSharing(boolean)}}
|
||||
* -- share constant pool when transforming (default is true)</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.Option#classHierarchyResolver(jdk.internal.classfile.ClassHierarchyResolver)}
|
||||
* -- specify a custom class hierarchy
|
||||
* resolver used by stack map generation</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.Option#attributeMapper(java.util.function.Function)}
|
||||
* <li>{@link jdk.internal.classfile.Classfile.StackMapsOption}
|
||||
* -- generate stackmaps (default is {@code STACK_MAPS_WHEN_REQUIRED})</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.DebugElementsOption}
|
||||
* -- processing of debug information, such as local variable metadata (default is {@code PASS_DEBUG}) </li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.LineNumbersOption}
|
||||
* -- processing of line numbers (default is {@code PASS_LINE_NUMBERS}) </li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.UnknownAttributesOption}
|
||||
* -- processing of unrecognized attributes (default is {@code PASS_UNKNOWN_ATTRIBUTES})</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.ConstantPoolSharingOption}}
|
||||
* -- share constant pool when transforming (default is {@code SHARED_POOL})</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.ClassHierarchyResolverOption#of(jdk.internal.classfile.ClassHierarchyResolver)}
|
||||
* -- specify a custom class hierarchy resolver used by stack map generation</li>
|
||||
* <li>{@link jdk.internal.classfile.Classfile.AttributeMapperOption#of(java.util.function.Function)}
|
||||
* -- specify format of custom attributes</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
|
@ -304,7 +303,8 @@
|
|||
* <p>
|
||||
* and then transform the classfile:
|
||||
* {@snippet lang=java :
|
||||
* byte[] newBytes = Classfile.parse(bytes).transform(ct);
|
||||
* var cc = Classfile.of();
|
||||
* byte[] newBytes = cc.transform(cc.parse(bytes), ct);
|
||||
* }
|
||||
* <p>
|
||||
* This is much more concise (and less error-prone) than the equivalent
|
||||
|
@ -322,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.
|
||||
*
|
||||
|
|
|
@ -30,6 +30,7 @@ import java.util.HashSet;
|
|||
import java.util.Set;
|
||||
|
||||
import java.lang.reflect.AccessFlag;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
@ -62,7 +63,7 @@ import jdk.internal.classfile.instruction.StoreInstruction;
|
|||
class PackageSnippets {
|
||||
void enumerateFieldsMethods1(byte[] bytes) {
|
||||
// @start region="enumerateFieldsMethods1"
|
||||
ClassModel cm = Classfile.parse(bytes);
|
||||
ClassModel cm = Classfile.of().parse(bytes);
|
||||
for (FieldModel fm : cm.fields())
|
||||
System.out.printf("Field %s%n", fm.fieldName().stringValue());
|
||||
for (MethodModel mm : cm.methods())
|
||||
|
@ -72,7 +73,7 @@ class PackageSnippets {
|
|||
|
||||
void enumerateFieldsMethods2(byte[] bytes) {
|
||||
// @start region="enumerateFieldsMethods2"
|
||||
ClassModel cm = Classfile.parse(bytes);
|
||||
ClassModel cm = Classfile.of().parse(bytes);
|
||||
for (ClassElement ce : cm) {
|
||||
switch (ce) {
|
||||
case MethodModel mm -> System.out.printf("Method %s%n", mm.methodName().stringValue());
|
||||
|
@ -85,7 +86,7 @@ class PackageSnippets {
|
|||
|
||||
void gatherDependencies1(byte[] bytes) {
|
||||
// @start region="gatherDependencies1"
|
||||
ClassModel cm = Classfile.parse(bytes);
|
||||
ClassModel cm = Classfile.of().parse(bytes);
|
||||
Set<ClassDesc> dependencies = new HashSet<>();
|
||||
|
||||
for (ClassElement ce : cm) {
|
||||
|
@ -108,7 +109,7 @@ class PackageSnippets {
|
|||
|
||||
void gatherDependencies2(byte[] bytes) {
|
||||
// @start region="gatherDependencies2"
|
||||
ClassModel cm = Classfile.parse(bytes);
|
||||
ClassModel cm = Classfile.of().parse(bytes);
|
||||
Set<ClassDesc> dependencies =
|
||||
cm.elementStream()
|
||||
.flatMap(ce -> ce instanceof MethodModel mm ? mm.elementStream() : Stream.empty())
|
||||
|
@ -126,7 +127,7 @@ class PackageSnippets {
|
|||
|
||||
void writeHelloWorld() {
|
||||
// @start region="helloWorld"
|
||||
byte[] bytes = Classfile.build(ClassDesc.of("Hello"), cb -> {
|
||||
byte[] bytes = Classfile.of().build(ClassDesc.of("Hello"), cb -> {
|
||||
cb.withFlags(AccessFlag.PUBLIC);
|
||||
cb.withMethod("<init>", MethodTypeDesc.of(ConstantDescs.CD_void), Classfile.ACC_PUBLIC,
|
||||
mb -> mb.withCode(
|
||||
|
@ -152,8 +153,8 @@ class PackageSnippets {
|
|||
|
||||
void stripDebugMethods1(byte[] bytes) {
|
||||
// @start region="stripDebugMethods1"
|
||||
ClassModel classModel = Classfile.parse(bytes);
|
||||
byte[] newBytes = Classfile.build(classModel.thisClass().asSymbol(),
|
||||
ClassModel classModel = Classfile.of().parse(bytes);
|
||||
byte[] newBytes = Classfile.of().build(classModel.thisClass().asSymbol(),
|
||||
classBuilder -> {
|
||||
for (ClassElement ce : classModel) {
|
||||
if (!(ce instanceof MethodModel mm
|
||||
|
@ -170,7 +171,8 @@ class PackageSnippets {
|
|||
if (!(element instanceof MethodModel mm && mm.methodName().stringValue().startsWith("debug")))
|
||||
builder.with(element);
|
||||
};
|
||||
byte[] newBytes = Classfile.parse(bytes).transform(ct);
|
||||
var cc = Classfile.of();
|
||||
byte[] newBytes = cc.transform(cc.parse(bytes), ct);
|
||||
// @end
|
||||
}
|
||||
|
||||
|
@ -202,7 +204,7 @@ class PackageSnippets {
|
|||
|
||||
void fooToBarUnrolled(ClassModel classModel) {
|
||||
// @start region="fooToBarUnrolled"
|
||||
byte[] newBytes = Classfile.build(classModel.thisClass().asSymbol(),
|
||||
byte[] newBytes = Classfile.of().build(classModel.thisClass().asSymbol(),
|
||||
classBuilder -> {
|
||||
for (ClassElement ce : classModel) {
|
||||
if (ce instanceof MethodModel mm) {
|
||||
|
@ -234,7 +236,7 @@ class PackageSnippets {
|
|||
|
||||
void codeRelabeling(ClassModel classModel) {
|
||||
// @start region="codeRelabeling"
|
||||
byte[] newBytes = classModel.transform(
|
||||
byte[] newBytes = Classfile.of().transform(classModel,
|
||||
ClassTransform.transformingMethodBodies(
|
||||
CodeTransform.ofStateful(CodeRelabeler::of)));
|
||||
// @end
|
||||
|
@ -244,11 +246,11 @@ class PackageSnippets {
|
|||
byte[] classInstrumentation(ClassModel target, ClassModel instrumentor, Predicate<MethodModel> instrumentedMethodsFilter) {
|
||||
var instrumentorCodeMap = instrumentor.methods().stream()
|
||||
.filter(instrumentedMethodsFilter)
|
||||
.collect(Collectors.toMap(mm -> mm.methodName().stringValue() + mm.methodType().stringValue(), mm -> mm.code().orElse(null)));
|
||||
.collect(Collectors.toMap(mm -> mm.methodName().stringValue() + mm.methodType().stringValue(), mm -> mm.code().orElseThrow()));
|
||||
var targetFieldNames = target.fields().stream().map(f -> f.fieldName().stringValue()).collect(Collectors.toSet());
|
||||
var targetMethods = target.methods().stream().map(m -> m.methodName().stringValue() + m.methodType().stringValue()).collect(Collectors.toSet());
|
||||
var instrumentorClassRemapper = ClassRemapper.of(Map.of(instrumentor.thisClass().asSymbol(), target.thisClass().asSymbol()));
|
||||
return target.transform(
|
||||
return Classfile.of().transform(target,
|
||||
ClassTransform.transformingMethods(
|
||||
instrumentedMethodsFilter,
|
||||
(mb, me) -> {
|
||||
|
@ -266,13 +268,13 @@ class PackageSnippets {
|
|||
&& mm.methodType().stringValue().equals(inv.type().stringValue())) {
|
||||
|
||||
//store stacked method parameters into locals
|
||||
var storeStack = new LinkedList<StoreInstruction>();
|
||||
var storeStack = new ArrayDeque<StoreInstruction>();
|
||||
int slot = 0;
|
||||
if (!mm.flags().has(AccessFlag.STATIC))
|
||||
storeStack.add(StoreInstruction.of(TypeKind.ReferenceType, slot++));
|
||||
storeStack.push(StoreInstruction.of(TypeKind.ReferenceType, slot++));
|
||||
for (var pt : mm.methodTypeSymbol().parameterList()) {
|
||||
var tk = TypeKind.from(pt);
|
||||
storeStack.addFirst(StoreInstruction.of(tk, slot));
|
||||
storeStack.push(StoreInstruction.of(tk, slot));
|
||||
slot += tk.slotSize();
|
||||
}
|
||||
storeStack.forEach(codeBuilder::with);
|
||||
|
|
|
@ -186,7 +186,7 @@ public class BindingSpecializer {
|
|||
private static byte[] specializeHelper(MethodType leafType, MethodType callerMethodType,
|
||||
CallingSequence callingSequence, ABIDescriptor abi) {
|
||||
String className = callingSequence.forDowncall() ? CLASS_NAME_DOWNCALL : CLASS_NAME_UPCALL;
|
||||
byte[] bytes = Classfile.build(ClassDesc.ofInternalName(className), clb -> {
|
||||
byte[] bytes = Classfile.of().build(ClassDesc.ofInternalName(className), clb -> {
|
||||
clb.withFlags(ACC_PUBLIC + ACC_FINAL + ACC_SUPER);
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withVersion(CLASSFILE_VERSION, 0);
|
||||
|
@ -207,7 +207,7 @@ public class BindingSpecializer {
|
|||
}
|
||||
|
||||
if (PERFORM_VERIFICATION) {
|
||||
List<VerifyError> errors = Classfile.parse(bytes).verify(null);
|
||||
List<VerifyError> errors = Classfile.of().parse(bytes).verify(null);
|
||||
if (!errors.isEmpty()) {
|
||||
errors.forEach(System.err::println);
|
||||
throw new IllegalStateException("Verification error(s)");
|
||||
|
|
|
@ -150,9 +150,10 @@ public final class ModuleInfoExtender {
|
|||
* be discarded.
|
||||
*/
|
||||
public byte[] toByteArray() throws IOException {
|
||||
var cm = Classfile.parse(in.readAllBytes());
|
||||
var cc = Classfile.of();
|
||||
var cm = cc.parse(in.readAllBytes());
|
||||
Version v = ModuleInfoExtender.this.version;
|
||||
return cm.transform(ClassTransform.endHandler(clb -> {
|
||||
return cc.transform(cm, ClassTransform.endHandler(clb -> {
|
||||
// ModuleMainClass attribute
|
||||
if (mainClass != null) {
|
||||
clb.with(ModuleMainClassAttribute.of(ClassDesc.of(mainClass)));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue