io.github.dmlloyd.classfile.impl.SplitConstantPool Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jdk-classfile-preview Show documentation
Show all versions of jdk-classfile-preview Show documentation
An unofficial backport of the JDK Classfile API to Java 17
/*
* Copyright (c) 2022, 2024, 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 io.github.dmlloyd.classfile.impl;
import java.lang.constant.ConstantDesc;
import java.lang.constant.MethodTypeDesc;
import java.util.Arrays;
import java.util.List;
import io.github.dmlloyd.classfile.Attributes;
import io.github.dmlloyd.classfile.ClassReader;
import io.github.dmlloyd.classfile.ClassFile;
import io.github.dmlloyd.classfile.constantpool.ClassEntry;
import io.github.dmlloyd.classfile.constantpool.ConstantDynamicEntry;
import io.github.dmlloyd.classfile.constantpool.ConstantPoolBuilder;
import io.github.dmlloyd.classfile.constantpool.ConstantPool;
import io.github.dmlloyd.classfile.BootstrapMethodEntry;
import io.github.dmlloyd.classfile.attribute.BootstrapMethodsAttribute;
import io.github.dmlloyd.classfile.constantpool.*;
import java.util.Objects;
import static io.github.dmlloyd.classfile.ClassFile.TAG_CLASS;
import static io.github.dmlloyd.classfile.ClassFile.TAG_CONSTANTDYNAMIC;
import static io.github.dmlloyd.classfile.ClassFile.TAG_DOUBLE;
import static io.github.dmlloyd.classfile.ClassFile.TAG_FIELDREF;
import static io.github.dmlloyd.classfile.ClassFile.TAG_FLOAT;
import static io.github.dmlloyd.classfile.ClassFile.TAG_INTEGER;
import static io.github.dmlloyd.classfile.ClassFile.TAG_INTERFACEMETHODREF;
import static io.github.dmlloyd.classfile.ClassFile.TAG_INVOKEDYNAMIC;
import static io.github.dmlloyd.classfile.ClassFile.TAG_LONG;
import static io.github.dmlloyd.classfile.ClassFile.TAG_METHODHANDLE;
import static io.github.dmlloyd.classfile.ClassFile.TAG_METHODREF;
import static io.github.dmlloyd.classfile.ClassFile.TAG_METHODTYPE;
import static io.github.dmlloyd.classfile.ClassFile.TAG_MODULE;
import static io.github.dmlloyd.classfile.ClassFile.TAG_NAMEANDTYPE;
import static io.github.dmlloyd.classfile.ClassFile.TAG_PACKAGE;
import static io.github.dmlloyd.classfile.ClassFile.TAG_STRING;
public final class SplitConstantPool implements ConstantPoolBuilder {
private final ClassReaderImpl parent;
private final int parentSize, parentBsmSize;
private int size, bsmSize;
private PoolEntry[] myEntries;
private BootstrapMethodEntryImpl[] myBsmEntries;
private boolean doneFullScan;
private EntryMap map;
private EntryMap bsmMap;
public SplitConstantPool() {
this.size = 1;
this.bsmSize = 0;
this.myEntries = new PoolEntry[1024];
this.myBsmEntries = new BootstrapMethodEntryImpl[8];
this.parent = null;
this.parentSize = 0;
this.parentBsmSize = 0;
this.doneFullScan = true;
}
public SplitConstantPool(ClassReader parent) {
this.parent = (ClassReaderImpl) parent;
this.parentSize = parent.size();
this.parentBsmSize = parent.bootstrapMethodCount();
this.size = parentSize;
this.bsmSize = parentBsmSize;
this.myEntries = new PoolEntry[8];
this.myBsmEntries = new BootstrapMethodEntryImpl[8];
this.doneFullScan = true;
}
@Override
public int size() {
return size;
}
@Override
public int bootstrapMethodCount() {
return bsmSize;
}
@Override
public PoolEntry entryByIndex(int index) {
if (index <= 0 || index >= size()) {
throw new ConstantPoolException("Bad CP index: " + index);
}
PoolEntry pe = (index < parentSize)
? parent.entryByIndex(index)
: myEntries[index - parentSize];
if (pe == null) {
throw new ConstantPoolException("Unusable CP index: " + index);
}
return pe;
}
@Override
public T entryByIndex(int index, Class cls) {
Objects.requireNonNull(cls);
return ClassReaderImpl.checkType(entryByIndex(index), index, cls);
}
@Override
public BootstrapMethodEntryImpl bootstrapMethodEntry(int index) {
if (index < 0 || index >= bootstrapMethodCount()) {
throw new ConstantPoolException("Bad BSM index: " + index);
}
return (index < parentBsmSize)
? parent.bootstrapMethodEntry(index)
: myBsmEntries[index - parentBsmSize];
}
@Override
public boolean canWriteDirect(ConstantPool other) {
return this == other || parent == other;
}
public boolean writeBootstrapMethods(BufWriterImpl buf) {
if (bsmSize == 0)
return false;
int pos = buf.size();
if (parent != null && parentBsmSize != 0) {
parent.writeBootstrapMethods(buf);
for (int i = parentBsmSize; i < bsmSize; i++)
bootstrapMethodEntry(i).writeTo(buf);
int attrLen = buf.size() - pos;
buf.patchInt(pos + 2, 4, attrLen - 6);
buf.patchInt(pos + 6, 2, bsmSize);
}
else {
UnboundAttribute a
= new UnboundAttribute.AdHocAttribute<>(Attributes.bootstrapMethods()) {
@Override
public void writeBody(BufWriterImpl b) {
buf.writeU2(bsmSize);
for (int i = 0; i < bsmSize; i++)
bootstrapMethodEntry(i).writeTo(buf);
}
};
a.writeTo(buf);
}
return true;
}
void writeTo(BufWriterImpl buf) {
int writeFrom = 1;
if (size() >= 65536) {
throw new IllegalArgumentException(String.format("Constant pool is too large %d", size()));
}
buf.writeU2(size());
if (parent != null && buf.constantPool().canWriteDirect(this)) {
parent.writeConstantPoolEntries(buf);
writeFrom = parent.size();
}
for (int i = writeFrom; i < size(); ) {
var info = (AbstractPoolEntry) entryByIndex(i);
info.writeTo(buf);
i += info.width();
}
}
private EntryMap map() {
if (map == null) {
map = new EntryMap<>(Math.max(size, 1024), .75f) {
@Override
protected PoolEntry fetchElement(int index) {
return entryByIndex(index);
}
};
// Doing a full scan here yields fall-off-the-cliff performance results,
// especially if we only need a few entries that are already
// inflated (such as attribute names).
// So we inflate the map with whatever we've got from the parent, and
// later, if we miss, we do a one-time full inflation before creating
// a new entry.
for (int i=1; i bsmMap() {
if (bsmMap == null) {
bsmMap = new EntryMap<>(Math.max(bsmSize, 16), .75f) {
@Override
protected BootstrapMethodEntryImpl fetchElement(int index) {
return bootstrapMethodEntry(index);
}
};
for (int i=0; i E internalAdd(E cpi) {
return internalAdd(cpi, cpi.hashCode());
}
private E internalAdd(E cpi, int hash) {
int newIndex = size-parentSize;
if (newIndex + 2 > myEntries.length) {
myEntries = Arrays.copyOf(myEntries, 2 * newIndex, PoolEntry[].class);
}
myEntries[newIndex] = cpi;
size += cpi.width();
map().put(hash, cpi.index());
return cpi;
}
private BootstrapMethodEntryImpl internalAdd(BootstrapMethodEntryImpl bsm, int hash) {
int newIndex = bsmSize-parentBsmSize;
if (newIndex + 2 > myBsmEntries.length) {
myBsmEntries = Arrays.copyOf(myBsmEntries, 2 * newIndex, BootstrapMethodEntryImpl[].class);
}
myBsmEntries[newIndex] = bsm;
bsmSize += 1;
bsmMap().put(hash, bsm.index);
return bsm;
}
private IntegerEntry findIntEntry(int val) {
int hash = AbstractPoolEntry.hash1(TAG_INTEGER, Integer.hashCode(val));
EntryMap map = map();
for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
PoolEntry e = map.getElementByToken(token);
if (e.tag() == TAG_INTEGER
&& e instanceof AbstractPoolEntry.IntegerEntryImpl ce
&& ce.intValue() == val)
return ce;
}
if (!doneFullScan) {
fullScan();
return findIntEntry(val);
}
return null;
}
private LongEntry findLongEntry(long val) {
int hash = AbstractPoolEntry.hash1(TAG_LONG, Long.hashCode(val));
EntryMap map = map();
for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
PoolEntry e = map.getElementByToken(token);
if (e.tag() == TAG_LONG
&& e instanceof AbstractPoolEntry.LongEntryImpl ce
&& ce.longValue() == val)
return ce;
}
if (!doneFullScan) {
fullScan();
return findLongEntry(val);
}
return null;
}
private FloatEntry findFloatEntry(float val) {
int hash = AbstractPoolEntry.hash1(TAG_FLOAT, Float.hashCode(val));
EntryMap map = map();
for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
PoolEntry e = map.getElementByToken(token);
if (e.tag() == TAG_FLOAT
&& e instanceof AbstractPoolEntry.FloatEntryImpl ce
&& ce.floatValue() == val)
return ce;
}
if (!doneFullScan) {
fullScan();
return findFloatEntry(val);
}
return null;
}
private DoubleEntry findDoubleEntry(double val) {
int hash = AbstractPoolEntry.hash1(TAG_DOUBLE, Double.hashCode(val));
EntryMap map = map();
for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
PoolEntry e = map.getElementByToken(token);
if (e.tag() == TAG_DOUBLE
&& e instanceof AbstractPoolEntry.DoubleEntryImpl ce
&& ce.doubleValue() == val)
return ce;
}
if (!doneFullScan) {
fullScan();
return findDoubleEntry(val);
}
return null;
}
private AbstractPoolEntry findEntry(int tag, T ref1) {
// invariant: canWriteDirect(ref1.constantPool())
int hash = AbstractPoolEntry.hash1(tag, ref1.index());
EntryMap map = map();
for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
PoolEntry e = map.getElementByToken(token);
if (e.tag() == tag
&& e instanceof AbstractPoolEntry.AbstractRefEntry> re
&& re.ref1 == ref1)
return re;
}
if (!doneFullScan) {
fullScan();
return findEntry(tag, ref1);
}
return null;
}
private
AbstractPoolEntry findEntry(int tag, T ref1, U ref2) {
// invariant: canWriteDirect(ref1.constantPool()), canWriteDirect(ref2.constantPool())
int hash = AbstractPoolEntry.hash2(tag, ref1.index(), ref2.index());
EntryMap map = map();
for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
PoolEntry e = map.getElementByToken(token);
if (e.tag() == tag
&& e instanceof AbstractPoolEntry.AbstractRefsEntry, ?> re
&& re.ref1 == ref1
&& re.ref2 == ref2) {
return re;
}
}
if (!doneFullScan) {
fullScan();
return findEntry(tag, ref1, ref2);
}
return null;
}
private AbstractPoolEntry.Utf8EntryImpl tryFindUtf8(int hash, String target) {
EntryMap map = map();
for (int token = map.firstToken(hash); token != -1;
token = map.nextToken(hash, token)) {
PoolEntry e = map.getElementByToken(token);
if (e.tag() == ClassFile.TAG_UTF8
&& e instanceof AbstractPoolEntry.Utf8EntryImpl ce
&& ce.hashCode() == hash
&& target.equals(ce.stringValue()))
return ce;
}
if (!doneFullScan) {
fullScan();
return tryFindUtf8(hash, target);
}
return null;
}
private AbstractPoolEntry.Utf8EntryImpl tryFindUtf8(int hash, AbstractPoolEntry.Utf8EntryImpl target) {
EntryMap map = map();
for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
PoolEntry e = map.getElementByToken(token);
if (e.tag() == ClassFile.TAG_UTF8
&& e instanceof AbstractPoolEntry.Utf8EntryImpl ce
&& target.equalsUtf8(ce))
return ce;
}
if (!doneFullScan) {
fullScan();
return tryFindUtf8(hash, target);
}
return null;
}
@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;
}
AbstractPoolEntry.Utf8EntryImpl maybeCloneUtf8Entry(Utf8Entry entry) {
AbstractPoolEntry.Utf8EntryImpl e = (AbstractPoolEntry.Utf8EntryImpl) entry;
if (e.constantPool == this || e.constantPool == parent)
return e;
AbstractPoolEntry.Utf8EntryImpl ce = tryFindUtf8(e.hashCode(), e);
return ce == null ? internalAdd(new AbstractPoolEntry.Utf8EntryImpl(this, size, e)) : ce;
}
@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;
}
@Override
public PackageEntry packageEntry(Utf8Entry nameEntry) {
AbstractPoolEntry.Utf8EntryImpl ne = maybeCloneUtf8Entry(nameEntry);
var e = (AbstractPoolEntry.PackageEntryImpl) findEntry(TAG_PACKAGE, ne);
return e == null ? internalAdd(new AbstractPoolEntry.PackageEntryImpl(this, size, ne)) : e;
}
@Override
public ModuleEntry moduleEntry(Utf8Entry nameEntry) {
AbstractPoolEntry.Utf8EntryImpl ne = maybeCloneUtf8Entry(nameEntry);
var e = (AbstractPoolEntry.ModuleEntryImpl) findEntry(TAG_MODULE, ne);
return e == null ? internalAdd(new AbstractPoolEntry.ModuleEntryImpl(this, size, ne)) : e;
}
@Override
public AbstractPoolEntry.NameAndTypeEntryImpl nameAndTypeEntry(Utf8Entry nameEntry, Utf8Entry typeEntry) {
AbstractPoolEntry.Utf8EntryImpl ne = maybeCloneUtf8Entry(nameEntry);
AbstractPoolEntry.Utf8EntryImpl te = maybeCloneUtf8Entry(typeEntry);
var e = (AbstractPoolEntry.NameAndTypeEntryImpl) findEntry(TAG_NAMEANDTYPE, ne, te);
return e == null ? internalAdd(new AbstractPoolEntry.NameAndTypeEntryImpl(this, size, ne, te)) : e;
}
@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 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 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 e = (AbstractPoolEntry.InterfaceMethodRefEntryImpl) findEntry(TAG_INTERFACEMETHODREF, oe, ne);
return e == null ? internalAdd(new AbstractPoolEntry.InterfaceMethodRefEntryImpl(this, size, oe, ne)) : e;
}
@Override
public MethodTypeEntry methodTypeEntry(MethodTypeDesc descriptor) {
var ret = (AbstractPoolEntry.MethodTypeEntryImpl)methodTypeEntry(utf8Entry(descriptor.descriptorString()));
ret.sym = descriptor;
return ret;
}
@Override
public MethodTypeEntry methodTypeEntry(Utf8Entry descriptor) {
AbstractPoolEntry.Utf8EntryImpl de = maybeCloneUtf8Entry(descriptor);
var e = (AbstractPoolEntry.MethodTypeEntryImpl) findEntry(TAG_METHODTYPE, de);
return e == null ? internalAdd(new AbstractPoolEntry.MethodTypeEntryImpl(this, size, de)) : e;
}
@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()));
};
}
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)) {
PoolEntry e = map1.getElementByToken(token);
if (e.tag() == TAG_METHODHANDLE
&& e instanceof AbstractPoolEntry.MethodHandleEntryImpl ce
&& ce.kind() == refKind && ce.reference() == reference)
return ce;
}
if (!doneFullScan) {
fullScan();
return methodHandleEntry(refKind, reference);
}
return internalAdd(new AbstractPoolEntry.MethodHandleEntryImpl(this, size,
hash, refKind, (AbstractPoolEntry.AbstractMemberRefEntry) reference), hash);
}
@Override
public InvokeDynamicEntry invokeDynamicEntry(BootstrapMethodEntry bootstrapMethodEntry,
NameAndTypeEntry nameAndType) {
if (!canWriteDirect(bootstrapMethodEntry.constantPool()))
bootstrapMethodEntry = bsmEntry(bootstrapMethodEntry.bootstrapMethod(),
bootstrapMethodEntry.arguments());
if (!canWriteDirect(nameAndType.constantPool()))
nameAndType = nameAndTypeEntry(nameAndType.name(), nameAndType.type());
int hash = AbstractPoolEntry.hash2(TAG_INVOKEDYNAMIC,
bootstrapMethodEntry.bsmIndex(), nameAndType.index());
EntryMap map1 = map();
for (int token = map1.firstToken(hash); token != -1; token = map1.nextToken(hash, token)) {
PoolEntry e = map1.getElementByToken(token);
if (e.tag() == TAG_INVOKEDYNAMIC
&& e instanceof AbstractPoolEntry.InvokeDynamicEntryImpl ce
&& ce.bootstrap() == bootstrapMethodEntry && ce.nameAndType() == nameAndType)
return ce;
}
if (!doneFullScan) {
fullScan();
return invokeDynamicEntry(bootstrapMethodEntry, nameAndType);
}
AbstractPoolEntry.InvokeDynamicEntryImpl ce =
new AbstractPoolEntry.InvokeDynamicEntryImpl(this, size, hash,
(BootstrapMethodEntryImpl) bootstrapMethodEntry,
(AbstractPoolEntry.NameAndTypeEntryImpl) nameAndType);
internalAdd(ce, hash);
return ce;
}
@Override
public ConstantDynamicEntry constantDynamicEntry(BootstrapMethodEntry bootstrapMethodEntry,
NameAndTypeEntry nameAndType) {
if (!canWriteDirect(bootstrapMethodEntry.constantPool()))
bootstrapMethodEntry = bsmEntry(bootstrapMethodEntry.bootstrapMethod(),
bootstrapMethodEntry.arguments());
if (!canWriteDirect(nameAndType.constantPool()))
nameAndType = nameAndTypeEntry(nameAndType.name(), nameAndType.type());
int hash = AbstractPoolEntry.hash2(TAG_CONSTANTDYNAMIC,
bootstrapMethodEntry.bsmIndex(), nameAndType.index());
EntryMap map1 = map();
for (int token = map1.firstToken(hash); token != -1; token = map1.nextToken(hash, token)) {
PoolEntry e = map1.getElementByToken(token);
if (e.tag() == TAG_CONSTANTDYNAMIC
&& e instanceof AbstractPoolEntry.ConstantDynamicEntryImpl ce
&& ce.bootstrap() == bootstrapMethodEntry && ce.nameAndType() == nameAndType)
return ce;
}
if (!doneFullScan) {
fullScan();
return constantDynamicEntry(bootstrapMethodEntry, nameAndType);
}
AbstractPoolEntry.ConstantDynamicEntryImpl ce =
new AbstractPoolEntry.ConstantDynamicEntryImpl(this, size, hash,
(BootstrapMethodEntryImpl) bootstrapMethodEntry,
(AbstractPoolEntry.NameAndTypeEntryImpl) nameAndType);
internalAdd(ce, hash);
return ce;
}
@Override
public IntegerEntry intEntry(int value) {
var e = findIntEntry(value);
return e == null ? internalAdd(new AbstractPoolEntry.IntegerEntryImpl(this, size, value)) : e;
}
@Override
public FloatEntry floatEntry(float value) {
var e = findFloatEntry(value);
return e == null ? internalAdd(new AbstractPoolEntry.FloatEntryImpl(this, size, value)) : e;
}
@Override
public LongEntry longEntry(long value) {
var e = findLongEntry(value);
return e == null ? internalAdd(new AbstractPoolEntry.LongEntryImpl(this, size, value)) : e;
}
@Override
public DoubleEntry doubleEntry(double value) {
var e = findDoubleEntry(value);
return e == null ? internalAdd(new AbstractPoolEntry.DoubleEntryImpl(this, size, value)) : e;
}
@Override
public StringEntry stringEntry(Utf8Entry utf8) {
AbstractPoolEntry.Utf8EntryImpl ue = maybeCloneUtf8Entry(utf8);
var e = (AbstractPoolEntry.StringEntryImpl) findEntry(TAG_STRING, ue);
return e == null ? internalAdd(new AbstractPoolEntry.StringEntryImpl(this, size, ue)) : e;
}
@Override
public BootstrapMethodEntry bsmEntry(MethodHandleEntry methodReference,
List arguments) {
if (!canWriteDirect(methodReference.constantPool()))
methodReference = methodHandleEntry(methodReference.kind(), methodReference.reference());
for (LoadableConstantEntry a : arguments) {
if (!canWriteDirect(a.constantPool())) {
// copy args list
LoadableConstantEntry[] arr = arguments.toArray(new LoadableConstantEntry[0]);
for (int i = 0; i < arr.length; i++)
arr[i] = AbstractPoolEntry.maybeClone(this, arr[i]);
arguments = List.of(arr);
break;
}
}
AbstractPoolEntry.MethodHandleEntryImpl mre = (AbstractPoolEntry.MethodHandleEntryImpl) methodReference;
int hash = BootstrapMethodEntryImpl.computeHashCode(mre, arguments);
EntryMap map = bsmMap();
for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
BootstrapMethodEntryImpl e = map.getElementByToken(token);
if (e.bootstrapMethod() == mre && e.arguments().equals(arguments)) {
return e;
}
}
BootstrapMethodEntryImpl ne = new BootstrapMethodEntryImpl(this, bsmSize, hash, mre, arguments);
return internalAdd(ne, hash);
}
}