8338546: Speed up ConstantPoolBuilder::classEntry(ClassDesc)

Reviewed-by: asotona, redestad
This commit is contained in:
Chen Liang 2024-09-24 14:28:05 +00:00
parent 279086d4ce
commit caa751c561
7 changed files with 647 additions and 95 deletions

View file

@ -86,14 +86,24 @@ public abstract sealed class AbstractPoolEntry {
return stringHash | NON_ZERO;
}
public static Utf8Entry rawUtf8EntryFromStandardAttributeName(String name) {
//assuming standard attribute names are all US_ASCII
var raw = name.getBytes(StandardCharsets.US_ASCII);
return new Utf8EntryImpl(null, 0, raw, 0, raw.length);
static int hashClassFromUtf8(boolean isArray, Utf8EntryImpl content) {
int hash = content.contentHash();
return hashClassFromDescriptor(isArray ? hash : Util.descriptorStringHash(content.length(), hash));
}
static int hashClassFromDescriptor(int descriptorHash) {
return hash1(ClassFile.TAG_CLASS, descriptorHash);
}
static boolean isArrayDescriptor(Utf8EntryImpl cs) {
// Do not throw out-of-bounds for empty strings
return !cs.isEmpty() && cs.charAt(0) == '[';
}
@SuppressWarnings("unchecked")
public static <T extends PoolEntry> T maybeClone(ConstantPoolBuilder cp, T entry) {
if (cp.canWriteDirect(entry.constantPool()))
return entry;
return (T)((AbstractPoolEntry)entry).clone(cp);
}
@ -146,7 +156,7 @@ public abstract sealed class AbstractPoolEntry {
private final int offset;
private final int rawLen;
// Set in any state other than RAW
private @Stable int hash;
private @Stable int contentHash;
private @Stable int charLen;
// Set in CHAR state
private @Stable char[] chars;
@ -165,10 +175,10 @@ public abstract sealed class AbstractPoolEntry {
}
Utf8EntryImpl(ConstantPool cpm, int index, String s) {
this(cpm, index, s, hashString(s.hashCode()));
this(cpm, index, s, s.hashCode());
}
Utf8EntryImpl(ConstantPool cpm, int index, String s, int hash) {
Utf8EntryImpl(ConstantPool cpm, int index, String s, int contentHash) {
super(cpm, index, 0);
this.rawBytes = null;
this.offset = 0;
@ -176,7 +186,7 @@ public abstract sealed class AbstractPoolEntry {
this.state = State.STRING;
this.stringValue = s;
this.charLen = s.length();
this.hash = hash;
this.contentHash = contentHash;
}
Utf8EntryImpl(ConstantPool cpm, int index, Utf8EntryImpl u) {
@ -185,7 +195,7 @@ public abstract sealed class AbstractPoolEntry {
this.offset = u.offset;
this.rawLen = u.rawLen;
this.state = u.state;
this.hash = u.hash;
this.contentHash = u.contentHash;
this.charLen = u.charLen;
this.chars = u.chars;
this.stringValue = u.stringValue;
@ -236,7 +246,7 @@ public abstract sealed class AbstractPoolEntry {
int singleBytes = JLA.countPositives(rawBytes, offset, rawLen);
int hash = ArraysSupport.hashCodeOfUnsigned(rawBytes, offset, singleBytes, 0);
if (singleBytes == rawLen) {
this.hash = hashString(hash);
this.contentHash = hash;
charLen = rawLen;
state = State.BYTE;
}
@ -294,7 +304,7 @@ public abstract sealed class AbstractPoolEntry {
throw malformedInput(px);
}
}
this.hash = hashString(hash);
this.contentHash = hash;
charLen = chararr_count;
this.chars = chararr;
state = State.CHAR;
@ -307,8 +317,6 @@ public abstract sealed class AbstractPoolEntry {
@Override
public Utf8EntryImpl clone(ConstantPoolBuilder cp) {
if (cp.canWriteDirect(constantPool))
return this;
return (state == State.STRING && rawBytes == null)
? (Utf8EntryImpl) cp.utf8Entry(stringValue)
: ((SplitConstantPool) cp).maybeCloneUtf8Entry(this);
@ -316,9 +324,13 @@ public abstract sealed class AbstractPoolEntry {
@Override
public int hashCode() {
return hashString(contentHash());
}
int contentHash() {
if (state == State.RAW)
inflate();
return hash;
return contentHash;
}
@Override
@ -389,6 +401,38 @@ public abstract sealed class AbstractPoolEntry {
return stringValue().equals(u.stringValue());
}
/**
* Returns if this utf8 entry's content equals a substring
* of {@code s} obtained as {@code s.substring(start, end - start)}.
* This check avoids a substring allocation.
*/
public boolean equalsRegion(String s, int start, int end) {
// start and end values trusted
if (state == State.RAW)
inflate();
int len = charLen;
if (len != end - start)
return false;
var sv = stringValue;
if (sv != null) {
return sv.regionMatches(0, s, start, len);
}
var chars = this.chars;
if (chars != null) {
for (int i = 0; i < len; i++)
if (chars[i] != s.charAt(start + i))
return false;
} else {
var bytes = this.rawBytes;
for (int i = 0; i < len; i++)
if (bytes[offset + i] != s.charAt(start + i))
return false;
}
return true;
}
@Override
public boolean equalsString(String s) {
if (state == State.RAW)
@ -397,7 +441,7 @@ public abstract sealed class AbstractPoolEntry {
case STRING:
return stringValue.equals(s);
case CHAR:
if (charLen != s.length() || hash != hashString(s.hashCode()))
if (charLen != s.length() || contentHash != s.hashCode())
return false;
for (int i=0; i<charLen; i++)
if (chars[i] != s.charAt(i))
@ -406,7 +450,7 @@ public abstract sealed class AbstractPoolEntry {
state = State.STRING;
return true;
case BYTE:
if (rawLen != s.length() || hash != hashString(s.hashCode()))
if (rawLen != s.length() || contentHash != s.hashCode())
return false;
for (int i=0; i<rawLen; i++)
if (rawBytes[offset+i] != s.charAt(i))
@ -519,7 +563,8 @@ public abstract sealed class AbstractPoolEntry {
public static final class ClassEntryImpl extends AbstractNamedEntry implements ClassEntry {
public ClassDesc sym = null;
public @Stable ClassDesc sym;
private @Stable int hash;
ClassEntryImpl(ConstantPool cpm, int index, Utf8EntryImpl name) {
super(cpm, TAG_CLASS, index, name);
@ -530,15 +575,15 @@ public abstract sealed class AbstractPoolEntry {
return TAG_CLASS;
}
ClassEntryImpl(ConstantPool cpm, int index, Utf8EntryImpl name, int hash, ClassDesc sym) {
super(cpm, ClassFile.TAG_CLASS, index, name);
this.hash = hash;
this.sym = sym;
}
@Override
public ClassEntry clone(ConstantPoolBuilder cp) {
if (cp.canWriteDirect(constantPool)) {
return this;
} else {
ClassEntryImpl ret = (ClassEntryImpl)cp.classEntry(ref1);
ret.sym = sym;
return ret;
}
return ((SplitConstantPool) cp).cloneClassEntry(this);
}
@Override
@ -547,19 +592,42 @@ public abstract sealed class AbstractPoolEntry {
if (sym != null) {
return sym;
}
return this.sym = Util.toClassDesc(asInternalName());
if (isArrayDescriptor(ref1)) {
sym = ref1.fieldTypeSymbol(); // array, symbol already available
} else {
sym = ClassDesc.ofInternalName(asInternalName()); // class or interface
}
return this.sym = sym;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (o instanceof ClassEntryImpl cce) {
return cce.name().equals(this.name());
} else if (o instanceof ClassEntry c) {
return c.asSymbol().equals(this.asSymbol());
if (o instanceof ClassEntryImpl other) {
return equalsEntry(other);
}
return false;
}
boolean equalsEntry(ClassEntryImpl other) {
var tsym = this.sym;
var osym = other.sym;
if (tsym != null && osym != null) {
return tsym.equals(osym);
}
return ref1.equalsUtf8(other.ref1);
}
@Override
public int hashCode() {
var hash = this.hash;
if (hash != 0)
return hash;
return this.hash = hashClassFromUtf8(isArrayDescriptor(ref1), ref1);
}
}
public static final class PackageEntryImpl extends AbstractNamedEntry implements PackageEntry {
@ -575,7 +643,7 @@ public abstract sealed class AbstractPoolEntry {
@Override
public PackageEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.packageEntry(ref1);
return cp.packageEntry(ref1);
}
@Override
@ -606,7 +674,7 @@ public abstract sealed class AbstractPoolEntry {
@Override
public ModuleEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.moduleEntry(ref1);
return cp.moduleEntry(ref1);
}
@Override
@ -648,9 +716,6 @@ public abstract sealed class AbstractPoolEntry {
@Override
public NameAndTypeEntry clone(ConstantPoolBuilder cp) {
if (cp.canWriteDirect(constantPool)) {
return this;
}
return cp.nameAndTypeEntry(ref1, ref2);
}
@ -715,7 +780,7 @@ public abstract sealed class AbstractPoolEntry {
@Override
public FieldRefEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.fieldRefEntry(ref1, ref2);
return cp.fieldRefEntry(ref1, ref2);
}
}
@ -733,7 +798,7 @@ public abstract sealed class AbstractPoolEntry {
@Override
public MethodRefEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.methodRefEntry(ref1, ref2);
return cp.methodRefEntry(ref1, ref2);
}
}
@ -751,7 +816,7 @@ public abstract sealed class AbstractPoolEntry {
@Override
public InterfaceMethodRefEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.interfaceMethodRefEntry(ref1, ref2);
return cp.interfaceMethodRefEntry(ref1, ref2);
}
}
@ -847,7 +912,7 @@ public abstract sealed class AbstractPoolEntry {
@Override
public InvokeDynamicEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.invokeDynamicEntry(bootstrap(), nameAndType());
return cp.invokeDynamicEntry(bootstrap(), nameAndType());
}
}
@ -872,7 +937,7 @@ public abstract sealed class AbstractPoolEntry {
@Override
public ConstantDynamicEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.constantDynamicEntry(bootstrap(), nameAndType());
return cp.constantDynamicEntry(bootstrap(), nameAndType());
}
}
@ -929,7 +994,7 @@ public abstract sealed class AbstractPoolEntry {
@Override
public MethodHandleEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.methodHandleEntry(refKind, reference);
return cp.methodHandleEntry(refKind, reference);
}
@Override
@ -969,9 +1034,6 @@ public abstract sealed class AbstractPoolEntry {
@Override
public MethodTypeEntry clone(ConstantPoolBuilder cp) {
if (cp.canWriteDirect(constantPool)) {
return this;
}
return cp.methodTypeEntry(ref1);
}
@ -1020,7 +1082,7 @@ public abstract sealed class AbstractPoolEntry {
@Override
public StringEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.stringEntry(ref1);
return cp.stringEntry(ref1);
}
@Override
@ -1063,7 +1125,7 @@ public abstract sealed class AbstractPoolEntry {
@Override
public IntegerEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.intEntry(val);
return cp.intEntry(val);
}
@Override
@ -1109,7 +1171,7 @@ public abstract sealed class AbstractPoolEntry {
@Override
public FloatEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.floatEntry(val);
return cp.floatEntry(val);
}
@Override
@ -1159,7 +1221,7 @@ public abstract sealed class AbstractPoolEntry {
@Override
public LongEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.longEntry(val);
return cp.longEntry(val);
}
@Override
@ -1209,7 +1271,7 @@ public abstract sealed class AbstractPoolEntry {
@Override
public DoubleEntry clone(ConstantPoolBuilder cp) {
return cp.canWriteDirect(constantPool) ? this : cp.doubleEntry(val);
return cp.doubleEntry(val);
}
@Override

View file

@ -37,6 +37,8 @@ import java.lang.classfile.attribute.BootstrapMethodsAttribute;
import java.lang.classfile.constantpool.*;
import java.util.Objects;
import jdk.internal.constant.ConstantUtils;
import static java.lang.classfile.ClassFile.TAG_CLASS;
import static java.lang.classfile.ClassFile.TAG_CONSTANTDYNAMIC;
import static java.lang.classfile.ClassFile.TAG_DOUBLE;
@ -371,7 +373,6 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
PoolEntry e = entryByIndex(map.getIndexByToken(token));
if (e.tag() == ClassFile.TAG_UTF8
&& e instanceof AbstractPoolEntry.Utf8EntryImpl ce
&& ce.hashCode() == hash
&& target.equals(ce.stringValue()))
return ce;
}
@ -398,25 +399,111 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
return null;
}
private AbstractPoolEntry.Utf8EntryImpl tryFindUtf8OfRegion(int hash, String target, int start, int end) {
EntryMap map = map();
while (true) {
for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
PoolEntry e = entryByIndex(map.getIndexByToken(token));
if (e.tag() == ClassFile.TAG_UTF8
&& e instanceof AbstractPoolEntry.Utf8EntryImpl ce
&& ce.equalsRegion(target, start, end))
return ce;
}
if (!doneFullScan) {
fullScan();
continue;
}
return null;
}
}
private AbstractPoolEntry.ClassEntryImpl tryFindClassOrInterface(int hash, ClassDesc cd) {
while (true) {
EntryMap map = map();
for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
PoolEntry e = entryByIndex(map.getIndexByToken(token));
if (e.tag() == TAG_CLASS
&& e instanceof AbstractPoolEntry.ClassEntryImpl ce) {
var esym = ce.sym;
if (esym != null) {
if (cd.equals(esym)) {
return ce; // definite match
}
continue; // definite mismatch
}
// no symbol available
var desc = cd.descriptorString();
if (ce.ref1.equalsRegion(desc, 1, desc.length() - 1)) {
// definite match, propagate symbol
ce.sym = cd;
return ce;
}
// definite mismatch
}
}
if (!doneFullScan) {
fullScan();
continue;
}
return null;
}
}
private AbstractPoolEntry.ClassEntryImpl classEntryForClassOrInterface(ClassDesc cd) {
var desc = cd.descriptorString();
int hash = AbstractPoolEntry.hashClassFromDescriptor(desc.hashCode());
var ce = tryFindClassOrInterface(hash, cd);
if (ce != null)
return ce;
var utfHash = Util.internalNameHash(desc);
var utf = tryFindUtf8OfRegion(AbstractPoolEntry.hashString(utfHash), desc, 1, desc.length() - 1);
if (utf == null)
utf = internalAdd(new AbstractPoolEntry.Utf8EntryImpl(this, size, ConstantUtils.dropFirstAndLastChar(desc), utfHash));
return internalAdd(new AbstractPoolEntry.ClassEntryImpl(this, size, utf, hash, cd));
}
private AbstractPoolEntry.ClassEntryImpl tryFindClassEntry(int hash, AbstractPoolEntry.Utf8EntryImpl utf8) {
EntryMap map = map();
for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
PoolEntry e = entryByIndex(map.getIndexByToken(token));
if (e.tag() == ClassFile.TAG_CLASS
&& e instanceof AbstractPoolEntry.ClassEntryImpl ce
&& ce.ref1.equalsUtf8(utf8))
return ce;
}
if (!doneFullScan) {
fullScan();
return tryFindClassEntry(hash, utf8);
}
return null;
}
@Override
public Utf8Entry utf8Entry(ClassDesc desc) {
public AbstractPoolEntry.Utf8EntryImpl utf8Entry(ClassDesc desc) {
var utf8 = utf8Entry(desc.descriptorString());
utf8.typeSym = desc;
if (utf8.typeSym == null)
utf8.typeSym = desc;
return utf8;
}
@Override
public Utf8Entry utf8Entry(MethodTypeDesc desc) {
var utf8 = utf8Entry(desc.descriptorString());
utf8.typeSym = desc;
if (utf8.typeSym == null)
utf8.typeSym = desc;
return utf8;
}
@Override
public AbstractPoolEntry.Utf8EntryImpl utf8Entry(String s) {
int hash = AbstractPoolEntry.hashString(s.hashCode());
var ce = tryFindUtf8(hash, s);
return ce == null ? internalAdd(new AbstractPoolEntry.Utf8EntryImpl(this, size, s, hash)) : ce;
int contentHash = s.hashCode();
var ce = tryFindUtf8(AbstractPoolEntry.hashString(contentHash), s);
return ce == null ? internalAdd(new AbstractPoolEntry.Utf8EntryImpl(this, size, s, contentHash)) : ce;
}
AbstractPoolEntry.Utf8EntryImpl maybeCloneUtf8Entry(Utf8Entry entry) {
@ -429,9 +516,37 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
@Override
public AbstractPoolEntry.ClassEntryImpl classEntry(Utf8Entry nameEntry) {
AbstractPoolEntry.Utf8EntryImpl ne = maybeCloneUtf8Entry(nameEntry);
var e = (AbstractPoolEntry.ClassEntryImpl) findEntry(TAG_CLASS, ne);
return e == null ? internalAdd(new AbstractPoolEntry.ClassEntryImpl(this, size, ne)) : e;
var ne = maybeCloneUtf8Entry(nameEntry);
return classEntry(ne, AbstractPoolEntry.isArrayDescriptor(ne));
}
AbstractPoolEntry.ClassEntryImpl classEntry(AbstractPoolEntry.Utf8EntryImpl ne, boolean isArray) {
int hash = AbstractPoolEntry.hashClassFromUtf8(isArray, ne);
var e = tryFindClassEntry(hash, ne);
return e == null ? internalAdd(new AbstractPoolEntry.ClassEntryImpl(this, size, ne, hash,
isArray && ne.typeSym instanceof ClassDesc cd ? cd : null)) : e;
}
@Override
public ClassEntry classEntry(ClassDesc cd) {
if (cd.isClassOrInterface()) { // implicit null check
return classEntryForClassOrInterface(cd);
}
if (cd.isArray()) {
return classEntry(utf8Entry(cd), true);
}
throw new IllegalArgumentException("Cannot be encoded as ClassEntry: " + cd.displayName());
}
AbstractPoolEntry.ClassEntryImpl cloneClassEntry(AbstractPoolEntry.ClassEntryImpl e) {
var ce = tryFindClassEntry(e.hashCode(), e.ref1);
if (ce != null) {
return ce;
}
var utf8 = maybeCloneUtf8Entry(e.ref1); // call order matters
return internalAdd(new AbstractPoolEntry.ClassEntryImpl(this, size,
utf8, e.hashCode(), e.sym));
}
@Override
@ -458,36 +573,24 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
@Override
public FieldRefEntry fieldRefEntry(ClassEntry owner, NameAndTypeEntry nameAndType) {
AbstractPoolEntry.ClassEntryImpl oe = (AbstractPoolEntry.ClassEntryImpl) owner;
AbstractPoolEntry.NameAndTypeEntryImpl ne = (AbstractPoolEntry.NameAndTypeEntryImpl) nameAndType;
if (!canWriteDirect(oe.constantPool))
oe = classEntry(owner.name());
if (!canWriteDirect(ne.constantPool))
ne = nameAndTypeEntry(nameAndType.name(), nameAndType.type());
var oe = AbstractPoolEntry.maybeClone(this, (AbstractPoolEntry.ClassEntryImpl) owner);
var ne = AbstractPoolEntry.maybeClone(this, (AbstractPoolEntry.NameAndTypeEntryImpl) nameAndType);
var e = (AbstractPoolEntry.FieldRefEntryImpl) findEntry(TAG_FIELDREF, oe, ne);
return e == null ? internalAdd(new AbstractPoolEntry.FieldRefEntryImpl(this, size, oe, ne)) : e;
}
@Override
public MethodRefEntry methodRefEntry(ClassEntry owner, NameAndTypeEntry nameAndType) {
AbstractPoolEntry.ClassEntryImpl oe = (AbstractPoolEntry.ClassEntryImpl) owner;
AbstractPoolEntry.NameAndTypeEntryImpl ne = (AbstractPoolEntry.NameAndTypeEntryImpl) nameAndType;
if (!canWriteDirect(oe.constantPool))
oe = classEntry(owner.name());
if (!canWriteDirect(ne.constantPool))
ne = nameAndTypeEntry(nameAndType.name(), nameAndType.type());
var oe = AbstractPoolEntry.maybeClone(this, (AbstractPoolEntry.ClassEntryImpl) owner);
var ne = AbstractPoolEntry.maybeClone(this, (AbstractPoolEntry.NameAndTypeEntryImpl) nameAndType);
var e = (AbstractPoolEntry.MethodRefEntryImpl) findEntry(TAG_METHODREF, oe, ne);
return e == null ? internalAdd(new AbstractPoolEntry.MethodRefEntryImpl(this, size, oe, ne)) : e;
}
@Override
public InterfaceMethodRefEntry interfaceMethodRefEntry(ClassEntry owner, NameAndTypeEntry nameAndType) {
AbstractPoolEntry.ClassEntryImpl oe = (AbstractPoolEntry.ClassEntryImpl) owner;
AbstractPoolEntry.NameAndTypeEntryImpl ne = (AbstractPoolEntry.NameAndTypeEntryImpl) nameAndType;
if (!canWriteDirect(oe.constantPool))
oe = classEntry(owner.name());
if (!canWriteDirect(ne.constantPool))
ne = nameAndTypeEntry(nameAndType.name(), nameAndType.type());
var oe = AbstractPoolEntry.maybeClone(this, (AbstractPoolEntry.ClassEntryImpl) owner);
var ne = AbstractPoolEntry.maybeClone(this, (AbstractPoolEntry.NameAndTypeEntryImpl) nameAndType);
var e = (AbstractPoolEntry.InterfaceMethodRefEntryImpl) findEntry(TAG_INTERFACEMETHODREF, oe, ne);
return e == null ? internalAdd(new AbstractPoolEntry.InterfaceMethodRefEntryImpl(this, size, oe, ne)) : e;
}
@ -506,15 +609,7 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
@Override
public MethodHandleEntry methodHandleEntry(int refKind, MemberRefEntry reference) {
if (!canWriteDirect(reference.constantPool())) {
reference = switch (reference.tag()) {
case TAG_FIELDREF -> fieldRefEntry(reference.owner(), reference.nameAndType());
case TAG_METHODREF -> methodRefEntry(reference.owner(), reference.nameAndType());
case TAG_INTERFACEMETHODREF -> interfaceMethodRefEntry(reference.owner(), reference.nameAndType());
default -> throw new IllegalArgumentException(String.format("Bad tag %d", reference.tag()));
};
}
reference = AbstractPoolEntry.maybeClone(this, reference);
int hash = AbstractPoolEntry.hash2(TAG_METHODHANDLE, refKind, reference.index());
EntryMap map1 = map();
for (int token = map1.firstToken(hash); token != -1; token = map1.nextToken(hash, token)) {
@ -538,8 +633,7 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
if (!canWriteDirect(bootstrapMethodEntry.constantPool()))
bootstrapMethodEntry = bsmEntry(bootstrapMethodEntry.bootstrapMethod(),
bootstrapMethodEntry.arguments());
if (!canWriteDirect(nameAndType.constantPool()))
nameAndType = nameAndTypeEntry(nameAndType.name(), nameAndType.type());
nameAndType = AbstractPoolEntry.maybeClone(this, nameAndType);
int hash = AbstractPoolEntry.hash2(TAG_INVOKEDYNAMIC,
bootstrapMethodEntry.bsmIndex(), nameAndType.index());
EntryMap map1 = map();
@ -569,8 +663,7 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
if (!canWriteDirect(bootstrapMethodEntry.constantPool()))
bootstrapMethodEntry = bsmEntry(bootstrapMethodEntry.bootstrapMethod(),
bootstrapMethodEntry.arguments());
if (!canWriteDirect(nameAndType.constantPool()))
nameAndType = nameAndTypeEntry(nameAndType.name(), nameAndType.type());
nameAndType = AbstractPoolEntry.maybeClone(this, nameAndType);
int hash = AbstractPoolEntry.hash2(TAG_CONSTANTDYNAMIC,
bootstrapMethodEntry.bsmIndex(), nameAndType.index());
EntryMap map1 = map();
@ -628,8 +721,7 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
@Override
public BootstrapMethodEntry bsmEntry(MethodHandleEntry methodReference,
List<LoadableConstantEntry> arguments) {
if (!canWriteDirect(methodReference.constantPool()))
methodReference = methodHandleEntry(methodReference.kind(), methodReference.reference());
methodReference = AbstractPoolEntry.maybeClone(this, methodReference);
for (LoadableConstantEntry a : arguments) {
if (!canWriteDirect(a.constantPool())) {
// copy args list

View file

@ -50,6 +50,7 @@ import java.lang.classfile.constantpool.NameAndTypeEntry;
import java.lang.constant.ModuleDesc;
import java.lang.reflect.AccessFlag;
import jdk.internal.access.SharedSecrets;
import jdk.internal.vm.annotation.Stable;
import static java.lang.classfile.ClassFile.ACC_STATIC;
import java.lang.classfile.attribute.CodeAttribute;
@ -337,4 +338,82 @@ public class Util {
interface WritableLocalVariable {
boolean writeLocalTo(BufWriterImpl buf);
}
/**
* Returns the hash code of an internal name given the class or interface L descriptor.
*/
public static int internalNameHash(String desc) {
if (desc.length() > 0xffff)
throw new IllegalArgumentException("String too long: ".concat(Integer.toString(desc.length())));
return (desc.hashCode() - pow31(desc.length() - 1) * 'L' - ';') * INVERSE_31;
}
/**
* Returns the hash code of a class or interface L descriptor given the internal name.
*/
public static int descriptorStringHash(int length, int hash) {
if (length > 0xffff)
throw new IllegalArgumentException("String too long: ".concat(Integer.toString(length)));
return 'L' * pow31(length + 1) + hash * 31 + ';';
}
// k is at most 65536, length of Utf8 entry + 1
public static int pow31(int k) {
int r = 1;
// calculate the power contribution from index-th octal digit
// from least to most significant (right to left)
// e.g. decimal 26=octal 32, power(26)=powerOctal(2,0)*powerOctal(3,1)
for (int i = 0; i < SIGNIFICANT_OCTAL_DIGITS; i++) {
r *= powerOctal(k & 7, i);
k >>= 3;
}
return r;
}
// The inverse of 31 in Z/2^32Z* modulo group, a * INVERSE_31 * 31 = a
static final int INVERSE_31 = 0xbdef7bdf;
// k is at most 65536 = octal 200000, only consider 6 octal digits
// Note: 31 powers repeat beyond 1 << 27, only 9 octal digits matter
static final int SIGNIFICANT_OCTAL_DIGITS = 6;
// for base k, storage is k * log_k(N)=k/ln(k) * ln(N)
// k = 2 or 4 is better for space at the cost of more multiplications
/**
* The code below is as if:
* {@snippet lang=java :
* int[] powers = new int[7 * SIGNIFICANT_OCTAL_DIGITS];
*
* for (int i = 1, k = 31; i <= 7; i++, k *= 31) {
* int t = powers[powersIndex(i, 0)] = k;
* for (int j = 1; j < SIGNIFICANT_OCTAL_DIGITS; j++) {
* t *= t;
* t *= t;
* t *= t;
* powers[powersIndex(i, j)] = t;
* }
* }
* }
* This is converted to explicit initialization to avoid bootstrap overhead.
* Validated in UtilTest.
*/
static final @Stable int[] powers = new int[] {
0x0000001f, 0x000003c1, 0x0000745f, 0x000e1781, 0x01b4d89f, 0x34e63b41, 0x67e12cdf,
0x94446f01, 0x50a9de01, 0x84304d01, 0x7dd7bc01, 0x8ca02b01, 0xff899a01, 0x25940901,
0x4dbf7801, 0xe3bef001, 0xc1fe6801, 0xe87de001, 0x573d5801, 0x0e3cd001, 0x0d7c4801,
0x54fbc001, 0xb9f78001, 0x2ef34001, 0xb3ef0001, 0x48eac001, 0xede68001, 0xa2e24001,
0x67de0001, 0xcfbc0001, 0x379a0001, 0x9f780001, 0x07560001, 0x6f340001, 0xd7120001,
0x3ef00001, 0x7de00001, 0xbcd00001, 0xfbc00001, 0x3ab00001, 0x79a00001, 0xb8900001,
};
static int powersIndex(int digit, int index) {
return (digit - 1) + index * 7;
}
// (31 ^ digit) ^ (8 * index) = 31 ^ (digit * (8 ^ index))
// digit: 0 - 7
// index: 0 - SIGNIFICANT_OCTAL_DIGITS - 1
private static int powerOctal(int digit, int index) {
return digit == 0 ? 1 : powers[powersIndex(digit, index)];
}
}