diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 62ad51cd1b6..770b81af8ac 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -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) diff --git a/src/java.base/share/classes/jdk/internal/classfile/attribute/EnclosingMethodAttribute.java b/src/java.base/share/classes/jdk/internal/classfile/attribute/EnclosingMethodAttribute.java index fc4c33d1a35..505c2f2b590 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/attribute/EnclosingMethodAttribute.java +++ b/src/java.base/share/classes/jdk/internal/classfile/attribute/EnclosingMethodAttribute.java @@ -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 enclosingMethodTypeSymbol() { - return enclosingMethodType().map(n -> MethodTypeDesc.ofDescriptor(n.stringValue())); + return enclosingMethod().map(Util::methodTypeSymbol); } /** diff --git a/src/java.base/share/classes/jdk/internal/classfile/constantpool/ConstantDynamicEntry.java b/src/java.base/share/classes/jdk/internal/classfile/constantpool/ConstantDynamicEntry.java index 39a6aeba5ee..d4d26986454 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/constantpool/ConstantDynamicEntry.java +++ b/src/java.base/share/classes/jdk/internal/classfile/constantpool/ConstantDynamicEntry.java @@ -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)); diff --git a/src/java.base/share/classes/jdk/internal/classfile/constantpool/ConstantPoolBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/constantpool/ConstantPoolBuilder.java index df7a0d746e8..a757624834c 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/constantpool/ConstantPoolBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/constantpool/ConstantPoolBuilder.java @@ -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; } /** diff --git a/src/java.base/share/classes/jdk/internal/classfile/constantpool/InvokeDynamicEntry.java b/src/java.base/share/classes/jdk/internal/classfile/constantpool/InvokeDynamicEntry.java index 54a9d53b540..ffa5c601e8a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/constantpool/InvokeDynamicEntry.java +++ b/src/java.base/share/classes/jdk/internal/classfile/constantpool/InvokeDynamicEntry.java @@ -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)); diff --git a/src/java.base/share/classes/jdk/internal/classfile/constantpool/MethodTypeEntry.java b/src/java.base/share/classes/jdk/internal/classfile/constantpool/MethodTypeEntry.java index 8e1a256fa74..a66e9bc779b 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/constantpool/MethodTypeEntry.java +++ b/src/java.base/share/classes/jdk/internal/classfile/constantpool/MethodTypeEntry.java @@ -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(); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java index 10dd19c4b16..fa729ae961a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java @@ -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; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java index 296f5bce833..3b011920415 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java @@ -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 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 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 diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java index f9da397c707..77c5b6ac57a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java @@ -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()); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java index f147a04c144..de6042ef096 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java @@ -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(); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java index f8c26d77f13..5b2378e0a7a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java @@ -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()); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java index 4e10e319299..84be7429d8a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java @@ -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]; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java index b96b4a6a3ce..b5e0f0336c3 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java @@ -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> 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]; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/MethodInfo.java b/src/java.base/share/classes/jdk/internal/classfile/impl/MethodInfo.java index f7229ed0182..45bec1ef67f 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/MethodInfo.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/MethodInfo.java @@ -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() { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java index d2f214ef822..9fc90a24448 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java @@ -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 diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java index cf5a5fb9b0c..e753580afbd 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java @@ -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); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java index bd4ef3d131d..6772c7d0108 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java @@ -63,31 +63,30 @@ public class StackMapDecoder { static List 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 initFrameLocals(ClassEntry thisClass, String methodName, String methodType, boolean isStatic) { - var mdesc = MethodTypeDesc.ofDescriptor(methodType); + public static List 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 ("".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(); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java index 921e326ac4d..7881be27ba2 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java @@ -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 exceptionTable; + private final List handlers; + private final List rawHandlers; private final ClassHierarchyImpl classHierarchy; private final boolean patchDeadCode; private List 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) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java index 21891d093b7..c66b097cfca 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java @@ -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 entryList(List 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'; + } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/instruction/FieldInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/instruction/FieldInstruction.java index e30e5684f00..ff502c432ed 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/instruction/FieldInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/instruction/FieldInstruction.java @@ -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()); } /** diff --git a/src/java.base/share/classes/jdk/internal/classfile/instruction/InvokeDynamicInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/instruction/InvokeDynamicInstruction.java index 5230b0a3d85..77262353391 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/instruction/InvokeDynamicInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/instruction/InvokeDynamicInstruction.java @@ -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()); } /** diff --git a/src/java.base/share/classes/jdk/internal/classfile/instruction/InvokeInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/instruction/InvokeInstruction.java index a76f2b3ddaf..dd97a6cd2f1 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/instruction/InvokeInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/instruction/InvokeInstruction.java @@ -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()); } diff --git a/test/jdk/jdk/classfile/UtilTest.java b/test/jdk/jdk/classfile/UtilTest.java index 421aa5cf513..1ce8c27dd30 100644 --- a/test/jdk/jdk/classfile/UtilTest.java +++ b/test/jdk/jdk/classfile/UtilTest.java @@ -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); } } diff --git a/test/micro/org/openjdk/bench/jdk/classfile/AbstractCorpusBenchmark.java b/test/micro/org/openjdk/bench/jdk/classfile/AbstractCorpusBenchmark.java index 310aa6f7a17..ce6ebd593f7 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/AbstractCorpusBenchmark.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/AbstractCorpusBenchmark.java @@ -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; diff --git a/test/micro/org/openjdk/bench/jdk/classfile/GenerateStackMaps.java b/test/micro/org/openjdk/bench/jdk/classfile/GenerateStackMaps.java index e856a1890cf..dc8a8e49229 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/GenerateStackMaps.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/GenerateStackMaps.java @@ -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); diff --git a/test/micro/org/openjdk/bench/jdk/classfile/RebuildMethodBodies.java b/test/micro/org/openjdk/bench/jdk/classfile/RebuildMethodBodies.java new file mode 100644 index 00000000000..b55521f69c0 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/classfile/RebuildMethodBodies.java @@ -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 shared, unshared; + Iterator 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); + } + })); + } +} diff --git a/test/micro/org/openjdk/bench/jdk/classfile/RepeatedModelTraversal.java b/test/micro/org/openjdk/bench/jdk/classfile/RepeatedModelTraversal.java new file mode 100644 index 00000000000..f69cf8fa606 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/classfile/RepeatedModelTraversal.java @@ -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 models; + Iterator 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); + } +} diff --git a/test/micro/org/openjdk/bench/jdk/classfile/Write.java b/test/micro/org/openjdk/bench/jdk/classfile/Write.java index d3d171828f6..96f4217d077 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/Write.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/Write.java @@ -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";