8306842: Classfile API performance improvements
Reviewed-by: redestad
This commit is contained in:
parent
95da499ef2
commit
f4f5542f8d
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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>();
|
||||
|
@ -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) {
|
||||
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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";
|
||||
|
Loading…
Reference in New Issue
Block a user