8306842: Classfile API performance improvements

Reviewed-by: redestad
This commit is contained in:
Adam Sotona 2023-05-18 06:02:47 +00:00
parent 95da499ef2
commit f4f5542f8d
28 changed files with 499 additions and 206 deletions

View File

@ -595,15 +595,7 @@ define SetupRunMicroTestBody
endif
# Set library path for native dependencies
$1_JMH_JVM_ARGS := -Djava.library.path=$$(TEST_IMAGE_DIR)/micro/native \
--add-exports java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED \
--add-exports java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED \
--add-exports java.base/jdk.internal.classfile=ALL-UNNAMED \
--add-exports java.base/jdk.internal.classfile.attribute=ALL-UNNAMED \
--add-exports java.base/jdk.internal.classfile.constantpool=ALL-UNNAMED \
--add-exports java.base/jdk.internal.classfile.instruction=ALL-UNNAMED \
--add-exports java.base/jdk.internal.classfile.components=ALL-UNNAMED \
--add-exports java.base/jdk.internal.classfile.impl=ALL-UNNAMED
$1_JMH_JVM_ARGS := -Djava.library.path=$$(TEST_IMAGE_DIR)/micro/native
ifneq ($$(MICRO_VM_OPTIONS)$$(MICRO_JAVA_OPTIONS), )
$1_JMH_JVM_ARGS += $$(MICRO_VM_OPTIONS) $$(MICRO_JAVA_OPTIONS)

View File

@ -36,6 +36,7 @@ import jdk.internal.classfile.constantpool.Utf8Entry;
import jdk.internal.classfile.impl.BoundAttribute;
import jdk.internal.classfile.impl.TemporaryConstantPool;
import jdk.internal.classfile.impl.UnboundAttribute;
import jdk.internal.classfile.impl.Util;
/**
* Models the {@code EnclosingMethod} attribute {@jvms 4.7.7}, which can appear
@ -81,7 +82,7 @@ public sealed interface EnclosingMethodAttribute
* immediately enclosed by a method or constructor}
*/
default Optional<MethodTypeDesc> enclosingMethodTypeSymbol() {
return enclosingMethodType().map(n -> MethodTypeDesc.ofDescriptor(n.stringValue()));
return enclosingMethod().map(Util::methodTypeSymbol);
}
/**

View File

@ -25,7 +25,7 @@
package jdk.internal.classfile.constantpool;
import jdk.internal.classfile.TypeKind;
import java.lang.constant.ClassDesc;
import jdk.internal.classfile.impl.Util;
import java.lang.constant.ConstantDesc;
import java.lang.constant.DynamicConstantDesc;
@ -50,7 +50,7 @@ public sealed interface ConstantDynamicEntry
default DynamicConstantDesc<?> asSymbol() {
return DynamicConstantDesc.ofNamed(bootstrap().bootstrapMethod().asSymbol(),
name().stringValue(),
ClassDesc.ofDescriptor(type().stringValue()),
Util.fieldTypeSymbol(nameAndType()),
bootstrap().arguments().stream()
.map(LoadableConstantEntry::constantValue)
.toArray(ConstantDesc[]::new));

View File

@ -43,6 +43,8 @@ import jdk.internal.classfile.impl.Options;
import java.lang.constant.ModuleDesc;
import java.lang.constant.PackageDesc;
import jdk.internal.classfile.WritableElement;
import jdk.internal.classfile.impl.AbstractPoolEntry.ClassEntryImpl;
import jdk.internal.classfile.impl.AbstractPoolEntry.NameAndTypeEntryImpl;
import jdk.internal.classfile.impl.SplitConstantPool;
import jdk.internal.classfile.impl.TemporaryConstantPool;
import jdk.internal.classfile.impl.Util;
@ -160,7 +162,9 @@ public sealed interface ConstantPoolBuilder
if (classDesc.isPrimitive()) {
throw new IllegalArgumentException("Cannot be encoded as ClassEntry: " + classDesc.displayName());
}
return classEntry(utf8Entry(classDesc.isArray() ? classDesc.descriptorString() : Util.toInternalName(classDesc)));
ClassEntryImpl ret = (ClassEntryImpl)classEntry(utf8Entry(classDesc.isArray() ? classDesc.descriptorString() : Util.toInternalName(classDesc)));
ret.sym = classDesc;
return ret;
}
/**
@ -233,7 +237,9 @@ public sealed interface ConstantPoolBuilder
* @param type the symbolic descriptor for a field type
*/
default NameAndTypeEntry nameAndTypeEntry(String name, ClassDesc type) {
return nameAndTypeEntry(utf8Entry(name), utf8Entry(type.descriptorString()));
var ret = (NameAndTypeEntryImpl)nameAndTypeEntry(utf8Entry(name), utf8Entry(type.descriptorString()));
ret.typeSym = type;
return ret;
}
/**
@ -246,7 +252,9 @@ public sealed interface ConstantPoolBuilder
* @param type the symbolic descriptor for a method type
*/
default NameAndTypeEntry nameAndTypeEntry(String name, MethodTypeDesc type) {
return nameAndTypeEntry(utf8Entry(name), utf8Entry(type.descriptorString()));
var ret = (NameAndTypeEntryImpl)nameAndTypeEntry(utf8Entry(name), utf8Entry(type.descriptorString()));
ret.typeSym = type;
return ret;
}
/**

View File

@ -26,9 +26,9 @@ package jdk.internal.classfile.constantpool;
import java.lang.constant.ConstantDesc;
import java.lang.constant.DynamicCallSiteDesc;
import java.lang.constant.MethodTypeDesc;
import jdk.internal.classfile.impl.AbstractPoolEntry;
import jdk.internal.classfile.impl.Util;
/**
* Models a constant pool entry for a dynamic call site.
@ -43,7 +43,7 @@ public sealed interface InvokeDynamicEntry
default DynamicCallSiteDesc asSymbol() {
return DynamicCallSiteDesc.of(bootstrap().bootstrapMethod().asSymbol(),
name().stringValue(),
MethodTypeDesc.ofDescriptor(type().stringValue()),
Util.methodTypeSymbol(nameAndType()),
bootstrap().arguments().stream()
.map(LoadableConstantEntry::constantValue)
.toArray(ConstantDesc[]::new));

View File

@ -50,7 +50,5 @@ public sealed interface MethodTypeEntry
/**
* {@return a symbolic descriptor for the method type}
*/
default MethodTypeDesc asSymbol() {
return MethodTypeDesc.ofDescriptor(descriptor().stringValue());
}
MethodTypeDesc asSymbol();
}

View File

@ -424,7 +424,7 @@ public abstract sealed class AbstractInstruction
@Override
public int count() {
return Util.parameterSlots(type().stringValue());
return Util.parameterSlots(Util.methodTypeSymbol(method().nameAndType()));
}
@Override
@ -1059,7 +1059,7 @@ public abstract sealed class AbstractInstruction
@Override
public int count() {
return op == Opcode.INVOKEINTERFACE
? Util.parameterSlots(methodEntry.nameAndType().type().stringValue()) + 1
? Util.parameterSlots(Util.methodTypeSymbol(methodEntry.nameAndType())) + 1
: 0;
}

View File

@ -25,6 +25,7 @@
package jdk.internal.classfile.impl;
import java.lang.constant.*;
import java.lang.invoke.TypeDescriptor;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@ -163,6 +164,10 @@ public abstract sealed class AbstractPoolEntry {
}
Utf8EntryImpl(ConstantPool cpm, int index, String s) {
this(cpm, index, s, hashString(s.hashCode()));
}
Utf8EntryImpl(ConstantPool cpm, int index, String s, int hash) {
super(cpm, Classfile.TAG_UTF8, index, 0);
this.rawBytes = null;
this.offset = 0;
@ -170,7 +175,7 @@ public abstract sealed class AbstractPoolEntry {
this.state = State.STRING;
this.stringValue = s;
this.charLen = s.length();
this.hash = hashString(s.hashCode());
this.hash = hash;
}
Utf8EntryImpl(ConstantPool cpm, int index, Utf8EntryImpl u) {
@ -556,18 +561,30 @@ public abstract sealed class AbstractPoolEntry {
public static final class ClassEntryImpl extends AbstractNamedEntry implements ClassEntry {
public ClassDesc sym = null;
ClassEntryImpl(ConstantPool cpm, int index, Utf8EntryImpl name) {
super(cpm, Classfile.TAG_CLASS, index, name);
}
@Override
public ClassEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.classEntry(ref1);
if (cp.canWriteDirect(constantPool)) {
return this;
} else {
ClassEntryImpl ret = (ClassEntryImpl)cp.classEntry(ref1);
ret.sym = sym;
return ret;
}
}
@Override
public ClassDesc asSymbol() {
return Util.toClassDesc(asInternalName());
var sym = this.sym;
if (sym != null) {
return sym;
}
return this.sym = Util.toClassDesc(asInternalName());
}
@Override
@ -637,6 +654,8 @@ public abstract sealed class AbstractPoolEntry {
public static final class NameAndTypeEntryImpl extends AbstractRefsEntry<Utf8EntryImpl, Utf8EntryImpl>
implements NameAndTypeEntry {
public TypeDescriptor typeSym = null;
NameAndTypeEntryImpl(ConstantPool cpm, int index, Utf8EntryImpl name, Utf8EntryImpl type) {
super(cpm, Classfile.TAG_NAMEANDTYPE, index, name, type);
}
@ -651,9 +670,31 @@ public abstract sealed class AbstractPoolEntry {
return ref2;
}
public ClassDesc fieldTypeSymbol() {
if (typeSym instanceof ClassDesc cd) {
return cd;
} else {
return (ClassDesc)(typeSym = ClassDesc.ofDescriptor(ref2.stringValue()));
}
}
public MethodTypeDesc methodTypeSymbol() {
if (typeSym instanceof MethodTypeDesc mtd) {
return mtd;
} else {
return (MethodTypeDesc)(typeSym = MethodTypeDesc.ofDescriptor(ref2.stringValue()));
}
}
@Override
public NameAndTypeEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.nameAndTypeEntry(ref1, ref2);
if (cp.canWriteDirect(constantPool)) {
return this;
} else {
var ret = (NameAndTypeEntryImpl)cp.nameAndTypeEntry(ref1, ref2);
ret.typeSym = typeSym;
return ret;
}
}
@Override
@ -918,6 +959,8 @@ public abstract sealed class AbstractPoolEntry {
extends AbstractRefEntry<Utf8EntryImpl>
implements MethodTypeEntry {
public MethodTypeDesc sym = null;
MethodTypeEntryImpl(ConstantPool cpm, int index, Utf8EntryImpl descriptor) {
super(cpm, Classfile.TAG_METHODTYPE, index, descriptor);
}
@ -929,7 +972,22 @@ public abstract sealed class AbstractPoolEntry {
@Override
public MethodTypeEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.methodTypeEntry(ref1);
if (cp.canWriteDirect(constantPool)) {
return this;
} else {
var ret = (MethodTypeEntryImpl)cp.methodTypeEntry(ref1);
ret.sym = sym;
return ret;
}
}
@Override
public MethodTypeDesc asSymbol() {
var sym = this.sym;
if (sym != null) {
return sym;
}
return this.sym = MethodTypeDesc.ofDescriptor(descriptor().stringValue());
}
@Override

View File

@ -60,7 +60,7 @@ public final class BufferedCodeBuilder
this.endLabel = new LabelImpl(this, -1);
this.original = original;
this.methodInfo = methodInfo;
this.maxLocals = Util.maxLocals(methodInfo.methodFlags(), methodInfo.methodType().stringValue());
this.maxLocals = Util.maxLocals(methodInfo.methodFlags(), methodInfo.methodTypeSymbol());
if (original != null)
this.maxLocals = Math.max(this.maxLocals, original.maxLocals());

View File

@ -24,6 +24,7 @@
*/
package jdk.internal.classfile.impl;
import java.lang.constant.MethodTypeDesc;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@ -51,6 +52,7 @@ public final class BufferedMethodBuilder
private AccessFlags flags;
private final MethodModel original;
private int[] parameterSlots;
MethodTypeDesc mDesc;
public BufferedMethodBuilder(SplitConstantPool constantPool,
Utf8Entry nameInfo,
@ -91,6 +93,18 @@ public final class BufferedMethodBuilder
return desc;
}
@Override
public MethodTypeDesc methodTypeSymbol() {
if (mDesc == null) {
if (original instanceof MethodInfo mi) {
mDesc = mi.methodTypeSymbol();
} else {
mDesc = MethodTypeDesc.ofDescriptor(methodType().stringValue());
}
}
return mDesc;
}
@Override
public int methodFlags() {
return flags.flagsMask();
@ -99,7 +113,7 @@ public final class BufferedMethodBuilder
@Override
public int parameterSlot(int paramNo) {
if (parameterSlots == null)
parameterSlots = Util.parseParameterSlots(methodFlags(), methodType().stringValue());
parameterSlots = Util.parseParameterSlots(methodFlags(), methodTypeSymbol());
return parameterSlots[paramNo];
}
@ -158,6 +172,11 @@ public final class BufferedMethodBuilder
return desc;
}
@Override
public MethodTypeDesc methodTypeSymbol() {
return BufferedMethodBuilder.this.methodTypeSymbol();
}
@Override
public int methodFlags() {
return flags.flagsMask();

View File

@ -130,7 +130,7 @@ public final class DirectCodeBuilder
: new BufWriterImpl(constantPool);
this.startLabel = new LabelImpl(this, 0);
this.endLabel = new LabelImpl(this, -1);
this.topLocal = Util.maxLocals(methodInfo.methodFlags(), methodInfo.methodType().stringValue());
this.topLocal = Util.maxLocals(methodInfo.methodFlags(), methodInfo.methodTypeSymbol());
if (original != null)
this.topLocal = Math.max(this.topLocal, original.maxLocals());
}

View File

@ -25,6 +25,7 @@
package jdk.internal.classfile.impl;
import java.lang.constant.MethodTypeDesc;
import java.util.function.Consumer;
import jdk.internal.classfile.BufWriter;
@ -46,6 +47,7 @@ public final class DirectMethodBuilder
final Utf8Entry desc;
int flags;
int[] parameterSlots;
MethodTypeDesc mDesc;
public DirectMethodBuilder(SplitConstantPool constantPool,
Utf8Entry nameInfo,
@ -77,6 +79,18 @@ public final class DirectMethodBuilder
return desc;
}
@Override
public MethodTypeDesc methodTypeSymbol() {
if (mDesc == null) {
if (original instanceof MethodInfo mi) {
mDesc = mi.methodTypeSymbol();
} else {
mDesc = MethodTypeDesc.ofDescriptor(methodType().stringValue());
}
}
return mDesc;
}
@Override
public int methodFlags() {
return flags;
@ -85,7 +99,7 @@ public final class DirectMethodBuilder
@Override
public int parameterSlot(int paramNo) {
if (parameterSlots == null)
parameterSlots = Util.parseParameterSlots(methodFlags(), methodType().stringValue());
parameterSlots = Util.parseParameterSlots(methodFlags(), methodTypeSymbol());
return parameterSlots[paramNo];
}

View File

@ -24,6 +24,7 @@
*/
package jdk.internal.classfile.impl;
import java.lang.constant.MethodTypeDesc;
import jdk.internal.classfile.*;
import jdk.internal.classfile.constantpool.Utf8Entry;
@ -39,6 +40,7 @@ public final class MethodImpl
private final int startPos, endPos, attributesPos;
private List<Attribute<?>> attributes;
private int[] parameterSlots;
private MethodTypeDesc mDesc;
public MethodImpl(ClassReader reader, int startPos, int endPos, int attrStart) {
this.reader = reader;
@ -70,6 +72,14 @@ public final class MethodImpl
return reader.readUtf8Entry(startPos + 4);
}
@Override
public MethodTypeDesc methodTypeSymbol() {
if (mDesc == null) {
mDesc = MethodTypeDesc.ofDescriptor(methodType().stringValue());
}
return mDesc;
}
@Override
public int methodFlags() {
return reader.readU2(startPos);
@ -78,7 +88,7 @@ public final class MethodImpl
@Override
public int parameterSlot(int paramNo) {
if (parameterSlots == null)
parameterSlots = Util.parseParameterSlots(methodFlags(), methodType().stringValue());
parameterSlots = Util.parseParameterSlots(methodFlags(), methodTypeSymbol());
return parameterSlots[paramNo];
}

View File

@ -24,6 +24,7 @@
*/
package jdk.internal.classfile.impl;
import java.lang.constant.MethodTypeDesc;
import jdk.internal.classfile.constantpool.Utf8Entry;
import static jdk.internal.classfile.Classfile.ACC_STATIC;
@ -31,6 +32,7 @@ import static jdk.internal.classfile.Classfile.ACC_STATIC;
public interface MethodInfo {
Utf8Entry methodName();
Utf8Entry methodType();
MethodTypeDesc methodTypeSymbol();
int methodFlags();
default int receiverSlot() {

View File

@ -103,6 +103,7 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
this.parentSize = 0;
this.parentBsmSize = 0;
this.options = options;
this.doneFullScan = true;
}
public SplitConstantPool(ClassReader parent) {
@ -380,8 +381,9 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
@Override
public AbstractPoolEntry.Utf8EntryImpl utf8Entry(String s) {
var ce = tryFindUtf8(AbstractPoolEntry.hashString(s.hashCode()), s);
return ce == null ? internalAdd(new AbstractPoolEntry.Utf8EntryImpl(this, size, s)) : ce;
int hash = AbstractPoolEntry.hashString(s.hashCode());
var ce = tryFindUtf8(hash, s);
return ce == null ? internalAdd(new AbstractPoolEntry.Utf8EntryImpl(this, size, s, hash)) : ce;
}
AbstractPoolEntry.Utf8EntryImpl maybeCloneUtf8Entry(Utf8Entry entry) {
@ -459,7 +461,9 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
@Override
public MethodTypeEntry methodTypeEntry(MethodTypeDesc descriptor) {
return methodTypeEntry(utf8Entry(descriptor.descriptorString()));
var ret = (AbstractPoolEntry.MethodTypeEntryImpl)methodTypeEntry(utf8Entry(descriptor.descriptorString()));
ret.sym = descriptor;
return ret;
}
@Override

View File

@ -44,7 +44,7 @@ public final class StackCounter {
dcb,
buf.thisClass().asSymbol(),
dcb.methodInfo.methodName().stringValue(),
MethodTypeDesc.ofDescriptor(dcb.methodInfo.methodType().stringValue()),
dcb.methodInfo.methodTypeSymbol(),
(dcb.methodInfo.methodFlags() & ACC_STATIC) != 0,
dcb.bytecodesBufWriter.asByteBuffer().slice(0, dcb.bytecodesBufWriter.size()),
dcb.constantPool,
@ -106,7 +106,7 @@ public final class StackCounter {
for (var h : handlers) map.put(labelContext.labelToBci(h.handler), 1);
maxLocals = isStatic ? 0 : 1;
for (var cd : methodDesc.parameterList()) {
maxLocals += TypeKind.from(cd).slotSize();
maxLocals += Util.slotSize(cd);
}
bcs = new RawBytecodeHelper(bytecode);
visited = new BitSet(bcs.endBci);

View File

@ -63,31 +63,30 @@ public class StackMapDecoder {
static List<VerificationTypeInfo> initFrameLocals(MethodModel method) {
return initFrameLocals(method.parent().orElseThrow().thisClass(),
method.methodName().stringValue(),
method.methodType().stringValue(),
method.methodTypeSymbol(),
method.flags().has(AccessFlag.STATIC));
}
public static List<VerificationTypeInfo> initFrameLocals(ClassEntry thisClass, String methodName, String methodType, boolean isStatic) {
var mdesc = MethodTypeDesc.ofDescriptor(methodType);
public static List<VerificationTypeInfo> initFrameLocals(ClassEntry thisClass, String methodName, MethodTypeDesc methodType, boolean isStatic) {
VerificationTypeInfo vtis[];
int i = 0;
if (!isStatic) {
vtis = new VerificationTypeInfo[mdesc.parameterCount() + 1];
vtis = new VerificationTypeInfo[methodType.parameterCount() + 1];
if ("<init>".equals(methodName) && !ConstantDescs.CD_Object.equals(thisClass.asSymbol())) {
vtis[i++] = SimpleVerificationTypeInfo.ITEM_UNINITIALIZED_THIS;
} else {
vtis[i++] = new StackMapDecoder.ObjectVerificationTypeInfoImpl(thisClass);
}
} else {
vtis = new VerificationTypeInfo[mdesc.parameterCount()];
vtis = new VerificationTypeInfo[methodType.parameterCount()];
}
for(var arg : mdesc.parameterList()) {
vtis[i++] = switch (arg.descriptorString()) {
case "I", "S", "C" ,"B", "Z" -> SimpleVerificationTypeInfo.ITEM_INTEGER;
case "J" -> SimpleVerificationTypeInfo.ITEM_LONG;
case "F" -> SimpleVerificationTypeInfo.ITEM_FLOAT;
case "D" -> SimpleVerificationTypeInfo.ITEM_DOUBLE;
case "V" -> throw new IllegalArgumentException("Illegal method argument type: " + arg);
for(var arg : methodType.parameterList()) {
vtis[i++] = switch (arg.descriptorString().charAt(0)) {
case 'I', 'S', 'C' ,'B', 'Z' -> SimpleVerificationTypeInfo.ITEM_INTEGER;
case 'J' -> SimpleVerificationTypeInfo.ITEM_LONG;
case 'F' -> SimpleVerificationTypeInfo.ITEM_FLOAT;
case 'D' -> SimpleVerificationTypeInfo.ITEM_DOUBLE;
case 'V' -> throw new IllegalArgumentException("Illegal method argument type: " + arg);
default -> new StackMapDecoder.ObjectVerificationTypeInfoImpl(TemporaryConstantPool.INSTANCE.classEntry(arg));
};
}
@ -100,7 +99,7 @@ public class StackMapDecoder {
var mi = dcb.methodInfo();
var prevLocals = StackMapDecoder.initFrameLocals(buf.thisClass(),
mi.methodName().stringValue(),
mi.methodType().stringValue(),
mi.methodTypeSymbol(),
(mi.methodFlags() & ACC_STATIC) != 0);
int prevOffset = -1;
var map = new TreeMap<Integer, StackMapFrameInfo>();

View File

@ -149,7 +149,7 @@ public final class StackMapGenerator {
dcb,
buf.thisClass().asSymbol(),
dcb.methodInfo.methodName().stringValue(),
MethodTypeDesc.ofDescriptor(dcb.methodInfo.methodType().stringValue()),
dcb.methodInfo.methodTypeSymbol(),
(dcb.methodInfo.methodFlags() & ACC_STATIC) != 0,
dcb.bytecodesBufWriter.asByteBuffer().slice(0, dcb.bytecodesBufWriter.size()),
dcb.constantPool,
@ -181,6 +181,8 @@ public final class StackMapGenerator {
Type.BOOLEAN_ARRAY_TYPE, Type.CHAR_ARRAY_TYPE, Type.FLOAT_ARRAY_TYPE, Type.DOUBLE_ARRAY_TYPE,
Type.BYTE_ARRAY_TYPE, Type.SHORT_ARRAY_TYPE, Type.INT_ARRAY_TYPE, Type.LONG_ARRAY_TYPE};
static record RawExceptionCatch(int start, int end, int handler, Type catchType) {}
private final Type thisType;
private final String methodName;
private final MethodTypeDesc methodDesc;
@ -188,7 +190,8 @@ public final class StackMapGenerator {
private final SplitConstantPool cp;
private final boolean isStatic;
private final LabelContext labelContext;
private final List<AbstractPseudoInstruction.ExceptionCatchImpl> exceptionTable;
private final List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers;
private final List<RawExceptionCatch> rawHandlers;
private final ClassHierarchyImpl classHierarchy;
private final boolean patchDeadCode;
private List<Frame> frames;
@ -226,7 +229,8 @@ public final class StackMapGenerator {
this.bytecode = bytecode;
this.cp = cp;
this.labelContext = labelContext;
this.exceptionTable = handlers;
this.handlers = handlers;
this.rawHandlers = new ArrayList<>(handlers.size());
this.classHierarchy = new ClassHierarchyImpl(cp.options().classHierarchyResolver);
this.patchDeadCode = cp.options().patchCode;
this.currentFrame = new Frame(classHierarchy);
@ -249,19 +253,25 @@ public final class StackMapGenerator {
return maxStack;
}
private int getFrameIndexFromOffset(int offset) {
int i = 0;
for (; i < frames.size(); i++) {
if (frames.get(i).offset == offset) {
return i;
}
private Frame getFrame(int offset) {
//binary search over frames ordered by offset
int low = 0;
int high = frames.size() - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
var f = frames.get(mid);
if (f.offset < offset)
low = mid + 1;
else if (f.offset > offset)
high = mid - 1;
else
return f;
}
return i;
return null;
}
private void checkJumpTarget(Frame frame, int target) {
int index = getFrameIndexFromOffset(target);
frame.checkAssignableTo(frames.get(index));
frame.checkAssignableTo(getFrame(target));
}
private int exMin, exMax;
@ -276,11 +286,18 @@ public final class StackMapGenerator {
private void generate() {
exMin = bytecode.capacity();
exMax = -1;
for (var exhandler : exceptionTable) {
for (var exhandler : handlers) {
int start_pc = labelContext.labelToBci(exhandler.tryStart());
int end_pc = labelContext.labelToBci(exhandler.tryEnd());
if (start_pc < exMin) exMin = start_pc;
if (end_pc > exMax) exMax = end_pc;
int handler_pc = labelContext.labelToBci(exhandler.handler());
if (start_pc >= 0 && end_pc >= 0 && end_pc > start_pc && handler_pc >= 0) {
if (start_pc < exMin) exMin = start_pc;
if (end_pc > exMax) exMax = end_pc;
var catchType = exhandler.catchType();
rawHandlers.add(new RawExceptionCatch(start_pc, end_pc, handler_pc,
catchType.isPresent() ? cpIndexToType(catchType.get().index(), cp)
: Type.THROWABLE_TYPE));
}
}
BitSet frameOffsets = detectFrameOffsets();
int framesCount = frameOffsets.cardinality();
@ -318,7 +335,7 @@ public final class StackMapGenerator {
}
private void removeRangeFromExcTable(int rangeStart, int rangeEnd) {
var it = exceptionTable.listIterator();
var it = handlers.listIterator();
while (it.hasNext()) {
var e = it.next();
int handlerStart = labelContext.labelToBci(e.tryStart());
@ -379,10 +396,6 @@ public final class StackMapGenerator {
return Type.referenceType(((ClassEntry)cp.entryByIndex(index)).asSymbol());
}
private static boolean isDoubleSlot(ClassDesc desc) {
return CD_double.equals(desc) || CD_long.equals(desc);
}
private void processMethod() {
currentFrame.setLocalsFromArg(methodName, methodDesc, isStatic, thisType);
currentFrame.stackSize = 0;
@ -651,17 +664,15 @@ public final class StackMapGenerator {
}
private void processExceptionHandlerTargets(int bci, boolean this_uninit) {
for (var exhandler : exceptionTable) {
if (bci >= labelContext.labelToBci(exhandler.tryStart()) && bci < labelContext.labelToBci(exhandler.tryEnd())) {
for (var ex : rawHandlers) {
if (bci == ex.start || (currentFrame.localsChanged && bci > ex.start && bci < ex.end)) {
int flags = currentFrame.flags;
if (this_uninit) flags |= FLAG_THIS_UNINIT;
Frame newFrame = currentFrame.frameInExceptionHandler(flags);
var catchType = exhandler.catchType();
newFrame.pushStack(catchType.isPresent() ? cpIndexToType(catchType.get().index(), cp) : Type.THROWABLE_TYPE);
int handler = labelContext.labelToBci(exhandler.handler());
if (handler != -1) checkJumpTarget(newFrame, handler);
Frame newFrame = currentFrame.frameInExceptionHandler(flags, ex.catchType);
checkJumpTarget(newFrame, ex.handler);
}
}
currentFrame.localsChanged = false;
}
private void processLdc(int index) {
@ -732,13 +743,13 @@ public final class StackMapGenerator {
}
private void processFieldInstructions(RawBytecodeHelper bcs) {
var desc = ClassDesc.ofDescriptor(((MemberRefEntry)cp.entryByIndex(bcs.getIndexU2())).nameAndType().type().stringValue());
var desc = Util.fieldTypeSymbol(((MemberRefEntry)cp.entryByIndex(bcs.getIndexU2())).nameAndType());
switch (bcs.rawCode) {
case GETSTATIC ->
currentFrame.pushStack(desc);
case PUTSTATIC -> {
currentFrame.popStack();
if (isDoubleSlot(desc)) currentFrame.popStack();
if (Util.isDoubleSlot(desc)) currentFrame.popStack();
}
case GETFIELD -> {
currentFrame.popStack();
@ -747,7 +758,7 @@ public final class StackMapGenerator {
case PUTFIELD -> {
currentFrame.popStack();
currentFrame.popStack();
if (isDoubleSlot(desc)) currentFrame.popStack();
if (Util.isDoubleSlot(desc)) currentFrame.popStack();
}
default -> throw new AssertionError("Should not reach here");
}
@ -759,36 +770,9 @@ public final class StackMapGenerator {
var cpe = cp.entryByIndex(index);
var nameAndType = opcode == INVOKEDYNAMIC ? ((DynamicConstantPoolEntry)cpe).nameAndType() : ((MemberRefEntry)cpe).nameAndType();
String invokeMethodName = nameAndType.name().stringValue();
var mDesc = nameAndType.type().stringValue();
//faster counting of method descriptor argument slots instead of full parsing
int nargs = 0, pos = 0, descLen = mDesc.length();
if (descLen < 3 || mDesc.charAt(0) != '(')
throw new IllegalArgumentException("Bad method descriptor: " + mDesc);
char ch;
while (++pos < descLen && (ch = mDesc.charAt(pos)) != ')') {
switch (ch) {
case '[' -> {
nargs++;
while (++pos < descLen && mDesc.charAt(pos) == '[');
if (mDesc.charAt(pos) == 'L')
while (++pos < descLen && mDesc.charAt(pos) != ';');
}
case 'D', 'J' -> nargs += 2;
case 'B', 'C', 'F', 'I', 'S', 'Z' -> nargs++;
case 'L' -> {
nargs++;
while (++pos < descLen && mDesc.charAt(pos) != ';');
}
default ->
throw new IllegalArgumentException("Bad method descriptor: " + mDesc);
}
}
if (++pos >= descLen)
throw new IllegalArgumentException("Bad method descriptor: " + mDesc);
var mDesc = Util.methodTypeSymbol(nameAndType);
int bci = bcs.bci;
currentFrame.decStack(nargs);
currentFrame.decStack(Util.parameterSlots(mDesc));
if (opcode != INVOKESTATIC && opcode != INVOKEDYNAMIC) {
if (OBJECT_INITIALIZER_NAME.equals(invokeMethodName)) {
Type type = currentFrame.popStack();
@ -813,7 +797,7 @@ public final class StackMapGenerator {
currentFrame.popStack();
}
}
currentFrame.pushStack(ClassDesc.ofDescriptor(mDesc.substring(pos)));
currentFrame.pushStack(mDesc.returnType());
return thisUninit;
}
@ -944,8 +928,8 @@ public final class StackMapGenerator {
} catch (IllegalArgumentException iae) {
generatorError("Detected branch target out of bytecode range", bci);
}
for (var exhandler : exceptionTable) try {
offsets.set(labelContext.labelToBci(exhandler.handler()));
for (var exhandler : rawHandlers) try {
offsets.set(exhandler.handler());
} catch (IllegalArgumentException iae) {
if (!cp.options().filterDeadLabels)
generatorError("Detected exception handler out of bytecode range");
@ -960,6 +944,7 @@ public final class StackMapGenerator {
int flags;
int frameMaxStack = 0, frameMaxLocals = 0;
boolean dirty = false;
boolean localsChanged = false;
private final ClassHierarchyImpl classHierarchy;
@ -989,16 +974,16 @@ public final class StackMapGenerator {
}
Frame pushStack(ClassDesc desc) {
return switch (desc.descriptorString()) {
case "J" ->
return switch (desc.descriptorString().charAt(0)) {
case 'J' ->
pushStack(Type.LONG_TYPE, Type.LONG2_TYPE);
case "D" ->
case 'D' ->
pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE);
case "I", "Z", "B", "C", "S" ->
case 'I', 'Z', 'B', 'C', 'S' ->
pushStack(Type.INTEGER_TYPE);
case "F" ->
case 'F' ->
pushStack(Type.FLOAT_TYPE);
case "V" ->
case 'V' ->
this;
default ->
pushStack(Type.referenceType(desc));
@ -1029,8 +1014,8 @@ public final class StackMapGenerator {
return this;
}
Frame frameInExceptionHandler(int flags) {
return new Frame(offset, flags, localsSize, 0, locals, new Type[] {Type.TOP_TYPE}, classHierarchy);
Frame frameInExceptionHandler(int flags, Type excType) {
return new Frame(offset, flags, localsSize, 1, locals, new Type[] {excType}, classHierarchy);
}
void initializeObject(Type old_object, Type new_object) {
@ -1038,6 +1023,7 @@ public final class StackMapGenerator {
for (i = 0; i < localsSize; i++) {
if (locals[i].equals(old_object)) {
locals[i] = new_object;
localsChanged = true;
}
}
for (i = 0; i < stackSize; i++) {
@ -1077,6 +1063,7 @@ public final class StackMapGenerator {
private void setLocalRawInternal(int index, Type type) {
checkLocal(index);
localsChanged |= !type.equals(locals[index]);
locals[index] = type;
}
@ -1095,18 +1082,18 @@ public final class StackMapGenerator {
var desc = methodDesc.parameterType(i);
if (desc.isClassOrInterface() || desc.isArray()) {
setLocalRawInternal(localsSize++, Type.referenceType(desc));
} else switch (desc.descriptorString()) {
case "J" -> {
} else switch (desc.descriptorString().charAt(0)) {
case 'J' -> {
setLocalRawInternal(localsSize++, Type.LONG_TYPE);
setLocalRawInternal(localsSize++, Type.LONG2_TYPE);
}
case "D" -> {
case 'D' -> {
setLocalRawInternal(localsSize++, Type.DOUBLE_TYPE);
setLocalRawInternal(localsSize++, Type.DOUBLE2_TYPE);
}
case "I", "Z", "B", "C", "S" ->
case 'I', 'Z', 'B', 'C', 'S' ->
setLocalRawInternal(localsSize++, Type.INTEGER_TYPE);
case "F" ->
case 'F' ->
setLocalRawInternal(localsSize++, Type.FLOAT_TYPE);
default -> throw new AssertionError("Should not reach here");
}
@ -1123,6 +1110,7 @@ public final class StackMapGenerator {
checkStack(src.stackSize - 1);
if (src.stackSize > 0) System.arraycopy(src.stack, 0, stack, 0, src.stackSize);
flags = src.flags;
localsChanged = true;
}
void checkAssignableTo(Frame target) {

View File

@ -25,6 +25,7 @@
package jdk.internal.classfile.impl;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.util.AbstractList;
import java.util.BitSet;
import java.util.Collection;
@ -35,6 +36,7 @@ import java.util.function.Function;
import jdk.internal.classfile.Opcode;
import jdk.internal.classfile.constantpool.ClassEntry;
import jdk.internal.classfile.constantpool.ModuleEntry;
import jdk.internal.classfile.constantpool.NameAndTypeEntry;
import java.lang.constant.ModuleDesc;
import jdk.internal.classfile.impl.TemporaryConstantPool;
import java.lang.reflect.AccessFlag;
@ -56,61 +58,29 @@ public class Util {
return "[" + s;
}
public static BitSet findParams(String type) {
BitSet bs = new BitSet();
if (type.charAt(0) != '(')
throw new IllegalArgumentException();
loop: for (int i = 1; i < type.length(); ++i) {
switch (type.charAt(i)) {
case '[':
bs.set(i);
while (type.charAt(++i) == '[')
;
if (type.charAt(i) == 'L') {
while (type.charAt(++i) != ';')
;
}
break;
case ')':
break loop;
default:
bs.set(i);
if (type.charAt(i) == 'L') {
while (type.charAt(++i) != ';')
;
}
}
}
return bs;
}
@SuppressWarnings("fallthrough")
public static int parameterSlots(String type) {
BitSet bs = findParams(type);
public static int parameterSlots(MethodTypeDesc mDesc) {
int count = 0;
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) {
count += (type.charAt(i) == 'J' || type.charAt(i) == 'D') ? 2 : 1;
for (int i = 0; i < mDesc.parameterCount(); i++) {
count += slotSize(mDesc.parameterType(i));
}
return count;
}
public static int[] parseParameterSlots(int flags, String type) {
BitSet bs = findParams(type);
int[] result = new int[bs.cardinality()];
int index = 0;
public static int[] parseParameterSlots(int flags, MethodTypeDesc mDesc) {
int[] result = new int[mDesc.parameterCount()];
int count = ((flags & ACC_STATIC) != 0) ? 0 : 1;
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) {
result[index++] = count;
count += (type.charAt(i) == 'J' || type.charAt(i) == 'D') ? 2 : 1;
for (int i = 0; i < result.length; i++) {
result[i] = count;
count += slotSize(mDesc.parameterType(i));
}
return result;
}
public static int maxLocals(int flags, String type) {
BitSet bs = findParams(type);
public static int maxLocals(int flags, MethodTypeDesc mDesc) {
int count = ((flags & ACC_STATIC) != 0) ? 0 : 1;
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1))
count += (type.charAt(i) == 'J' || type.charAt(i) == 'D') ? 2 : 1;
for (int i = 0; i < mDesc.parameterCount(); i++) {
count += slotSize(mDesc.parameterType(i));
}
return count;
}
@ -187,7 +157,7 @@ public class Util {
public static List<ClassEntry> entryList(List<? extends ClassDesc> list) {
var result = new Object[list.size()]; // null check
for (int i = 0; i < result.length; i++) {
result[i] = TemporaryConstantPool.INSTANCE.classEntry(TemporaryConstantPool.INSTANCE.utf8Entry(toInternalName(list.get(i))));
result[i] = TemporaryConstantPool.INSTANCE.classEntry(list.get(i));
}
return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArrayNullsAllowed(result);
}
@ -231,4 +201,25 @@ public class Util {
public static boolean has(AccessFlag.Location location, int flagsMask, AccessFlag flag) {
return (flag.mask() & flagsMask) == flag.mask() && flag.locations().contains(location);
}
public static ClassDesc fieldTypeSymbol(NameAndTypeEntry nat) {
return ((AbstractPoolEntry.NameAndTypeEntryImpl)nat).fieldTypeSymbol();
}
public static MethodTypeDesc methodTypeSymbol(NameAndTypeEntry nat) {
return ((AbstractPoolEntry.NameAndTypeEntryImpl)nat).methodTypeSymbol();
}
public static int slotSize(ClassDesc desc) {
return switch (desc.descriptorString().charAt(0)) {
case 'V' -> 0;
case 'D','J' -> 2;
default -> 1;
};
}
public static boolean isDoubleSlot(ClassDesc desc) {
char ch = desc.descriptorString().charAt(0);
return ch == 'D' || ch == 'J';
}
}

View File

@ -35,6 +35,7 @@ import jdk.internal.classfile.constantpool.FieldRefEntry;
import jdk.internal.classfile.constantpool.NameAndTypeEntry;
import jdk.internal.classfile.constantpool.Utf8Entry;
import jdk.internal.classfile.impl.AbstractInstruction;
import jdk.internal.classfile.impl.AbstractPoolEntry;
import jdk.internal.classfile.impl.TemporaryConstantPool;
import jdk.internal.classfile.impl.Util;
@ -76,7 +77,7 @@ public sealed interface FieldInstruction extends Instruction
* {@return a symbolic descriptor for the type of the field}
*/
default ClassDesc typeSymbol() {
return ClassDesc.ofDescriptor(type().stringValue());
return Util.fieldTypeSymbol(field().nameAndType());
}
/**

View File

@ -37,6 +37,7 @@ import jdk.internal.classfile.constantpool.InvokeDynamicEntry;
import jdk.internal.classfile.constantpool.LoadableConstantEntry;
import jdk.internal.classfile.constantpool.Utf8Entry;
import jdk.internal.classfile.impl.AbstractInstruction;
import jdk.internal.classfile.impl.AbstractPoolEntry;
import jdk.internal.classfile.impl.Util;
/**
@ -69,7 +70,7 @@ public sealed interface InvokeDynamicInstruction extends Instruction
* {@return the invocation type of the call site, as a symbolic descriptor}
*/
default MethodTypeDesc typeSymbol() {
return MethodTypeDesc.ofDescriptor(type().stringValue());
return Util.methodTypeSymbol(invokedynamic().nameAndType());
}
/**

View File

@ -37,6 +37,7 @@ import jdk.internal.classfile.constantpool.MethodRefEntry;
import jdk.internal.classfile.constantpool.NameAndTypeEntry;
import jdk.internal.classfile.constantpool.Utf8Entry;
import jdk.internal.classfile.impl.AbstractInstruction;
import jdk.internal.classfile.impl.AbstractPoolEntry;
import jdk.internal.classfile.impl.TemporaryConstantPool;
import jdk.internal.classfile.impl.Util;
@ -89,7 +90,7 @@ public sealed interface InvokeInstruction extends Instruction
* {@return a symbolic descriptor for the method type}
*/
default MethodTypeDesc typeSymbol() {
return MethodTypeDesc.ofDescriptor(type().stringValue());
return Util.methodTypeSymbol(method().nameAndType());
}

View File

@ -28,6 +28,7 @@
* @summary Testing Classfile Util.
* @run junit UtilTest
*/
import java.lang.constant.MethodTypeDesc;
import jdk.internal.classfile.impl.Util;
import org.junit.jupiter.api.Test;
@ -37,36 +38,30 @@ import static org.junit.jupiter.api.Assertions.*;
* UtilTest
*/
class UtilTest {
@Test
void testFindParams() {
assertEquals(Util.findParams("(IIII)V").cardinality(), 4);
assertEquals(Util.findParams("([I[I[I[I)V").cardinality(), 4);
assertEquals(Util.findParams("(IJLFoo;IJ)V").cardinality(), 5);
assertEquals(Util.findParams("([[[[I)V").cardinality(), 1);
assertEquals(Util.findParams("([[[[LFoo;)V").cardinality(), 1);
assertEquals(Util.findParams("([I[LFoo;)V").cardinality(), 2);
assertEquals(Util.findParams("()V").cardinality(), 0);
}
@Test
void testParameterSlots() {
assertEquals(Util.parameterSlots("(IIII)V"), 4);
assertEquals(Util.parameterSlots("([I[I[I[I)V"), 4);
assertEquals(Util.parameterSlots("(IJLFoo;IJ)V"), 7);
assertEquals(Util.parameterSlots("([[[[I)V"), 1);
assertEquals(Util.parameterSlots("([[[[LFoo;)V"), 1);
assertEquals(Util.parameterSlots("([I[LFoo;)V"), 2);
assertEquals(Util.parameterSlots("()V"), 0);
assertEquals(Util.parameterSlots("(I)V"), 1);
assertEquals(Util.parameterSlots("(S)V"), 1);
assertEquals(Util.parameterSlots("(C)V"), 1);
assertEquals(Util.parameterSlots("(B)V"), 1);
assertEquals(Util.parameterSlots("(Z)V"), 1);
assertEquals(Util.parameterSlots("(F)V"), 1);
assertEquals(Util.parameterSlots("(LFoo;)V"), 1);
assertEquals(Util.parameterSlots("(J)V"), 2);
assertEquals(Util.parameterSlots("(D)V"), 2);
assertEquals(Util.parameterSlots("([J)V"), 1);
assertEquals(Util.parameterSlots("([D)V"), 1);
assertSlots("(IIII)V", 4);
assertSlots("([I[I[I[I)V", 4);
assertSlots("(IJLFoo;IJ)V", 7);
assertSlots("([[[[I)V", 1);
assertSlots("([[[[LFoo;)V", 1);
assertSlots("([I[LFoo;)V", 2);
assertSlots("()V", 0);
assertSlots("(I)V", 1);
assertSlots("(S)V", 1);
assertSlots("(C)V", 1);
assertSlots("(B)V", 1);
assertSlots("(Z)V", 1);
assertSlots("(F)V", 1);
assertSlots("(LFoo;)V", 1);
assertSlots("(J)V", 2);
assertSlots("(D)V", 2);
assertSlots("([J)V", 1);
assertSlots("([D)V", 1);
}
private void assertSlots(String methodDesc, int slots) {
assertEquals(Util.parameterSlots(MethodTypeDesc.ofDescriptor(methodDesc)), slots);
}
}

View File

@ -45,7 +45,16 @@ import org.openjdk.jmh.annotations.Warmup;
*/
@Warmup(iterations = 2)
@Measurement(iterations = 4)
@Fork(1)
@Fork(value = 1, jvmArgsAppend = {
"--enable-preview",
"--add-exports", "java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.attribute=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.constantpool=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.instruction=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.components=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.impl=ALL-UNNAMED"})
@State(Scope.Benchmark)
public class AbstractCorpusBenchmark {
protected byte[][] classes;

View File

@ -32,7 +32,7 @@ import java.nio.ByteBuffer;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import jdk.internal.classfile.Classfile;
import jdk.internal.classfile.ClassReader;
@ -55,7 +55,14 @@ import org.openjdk.jmh.annotations.Warmup;
@BenchmarkMode(Mode.Throughput)
@State(Scope.Benchmark)
@Fork(1)
@Fork(value = 1, jvmArgsAppend = {
"--enable-preview",
"--add-exports", "java.base/jdk.internal.classfile=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.attribute=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.constantpool=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.instruction=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.components=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.impl=ALL-UNNAMED"})
@Warmup(iterations = 2)
@Measurement(iterations = 10)
public class GenerateStackMaps {
@ -75,7 +82,7 @@ public class GenerateStackMaps {
@Setup(Level.Trial)
public void setup() throws IOException {
data = new LinkedList<>();
data = new ArrayList<>();
Files.walk(FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java")).forEach(p -> {
if (Files.isRegularFile(p) && p.toString().endsWith(".class")) try {
var clm = Classfile.parse(p);

View File

@ -0,0 +1,115 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. 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 org.openjdk.bench.jdk.classfile;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
import jdk.internal.classfile.ClassModel;
import jdk.internal.classfile.Classfile;
import jdk.internal.classfile.ClassTransform;
import jdk.internal.classfile.instruction.*;
import org.openjdk.jmh.annotations.*;
@BenchmarkMode(Mode.Throughput)
@State(Scope.Benchmark)
@Fork(value = 1, jvmArgsAppend = {
"--enable-preview",
"--add-exports", "java.base/jdk.internal.classfile=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.constantpool=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.instruction=ALL-UNNAMED"})
@Warmup(iterations = 2)
@Measurement(iterations = 4)
public class RebuildMethodBodies {
List<ClassModel> shared, unshared;
Iterator<ClassModel> it1, it2;
@Setup(Level.Trial)
public void setup() throws IOException {
shared = new ArrayList<>();
unshared = new ArrayList<>();
Files.walk(FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java")).forEach(p -> {
if (Files.isRegularFile(p) && p.toString().endsWith(".class")) try {
var clm = Classfile.parse(p,
Classfile.Option.constantPoolSharing(true),
Classfile.Option.processDebug(false),
Classfile.Option.processLineNumbers(false));
shared.add(clm);
transform(clm); //dry run to expand model and symbols
clm = Classfile.parse(p,
Classfile.Option.constantPoolSharing(false),
Classfile.Option.processDebug(false),
Classfile.Option.processLineNumbers(false));
unshared.add(clm);
transform(clm); //dry run to expand model and symbols
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
@Benchmark
public void shared() {
if (it1 == null || !it1.hasNext())
it1 = shared.iterator();
//model and symbols were already expanded, so benchmark is focused more on builder performance
transform(it1.next());
}
@Benchmark
public void unshared() {
if (it2 == null || !it2.hasNext())
it2 = unshared.iterator();
//model and symbols were already expanded, so benchmark is focused more on builder performance
transform(it2.next());
}
private static void transform(ClassModel clm) {
clm.transform(ClassTransform.transformingMethodBodies((cob, coe) -> {
switch (coe) {
case FieldInstruction i ->
cob.fieldInstruction(i.opcode(), i.owner().asSymbol(), i.name().stringValue(), i.typeSymbol());
case InvokeDynamicInstruction i ->
cob.invokedynamic(i.invokedynamic().asSymbol());
case InvokeInstruction i ->
cob.invokeInstruction(i.opcode(), i.owner().asSymbol(), i.name().stringValue(), i.typeSymbol(), i.isInterface());
case NewMultiArrayInstruction i ->
cob.multianewarray(i.arrayType().asSymbol(), i.dimensions());
case NewObjectInstruction i ->
cob.new_(i.className().asSymbol());
case NewReferenceArrayInstruction i ->
cob.anewarray(i.componentType().asSymbol());
case TypeCheckInstruction i ->
cob.typeCheckInstruction(i.opcode(), i.type().asSymbol());
default -> cob.with(coe);
}
}));
}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. 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 org.openjdk.bench.jdk.classfile;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
import jdk.internal.classfile.ClassModel;
import jdk.internal.classfile.Classfile;
import jdk.internal.classfile.components.ClassPrinter;
import org.openjdk.jmh.annotations.*;
@BenchmarkMode(Mode.Throughput)
@State(Scope.Benchmark)
@Fork(value = 1, jvmArgsAppend = {
"--enable-preview",
"--add-exports", "java.base/jdk.internal.classfile=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.components=ALL-UNNAMED"})
@Warmup(iterations = 3)
@Measurement(iterations = 4)
public class RepeatedModelTraversal {
List<ClassModel> models;
Iterator<ClassModel> it;
@Setup(Level.Trial)
public void setup() throws IOException {
models = new ArrayList<>();
Files.walk(FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java/util")).forEach(p -> {
if (Files.isRegularFile(p) && p.toString().endsWith(".class")) try {
var clm = Classfile.parse(p);
models.add(clm);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
@Benchmark
public void traverseModel() {
if (it == null || !it.hasNext())
it = models.iterator();
ClassPrinter.toTree(it.next(), ClassPrinter.Verbosity.TRACE_ALL);
}
}

View File

@ -65,7 +65,16 @@ import static jdk.internal.org.objectweb.asm.Opcodes.V12;
*/
@Warmup(iterations = 3)
@Measurement(iterations = 5)
@Fork(1)
@Fork(value = 1, jvmArgsAppend = {
"--enable-preview",
"--add-exports", "java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.attribute=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.constantpool=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.instruction=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.components=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.classfile.impl=ALL-UNNAMED"})
public class Write {
static String checkFileAsm = "/tmp/asw/MyClass.class";
static String checkFileBc = "/tmp/byw/MyClass.class";