blob: 2027d94b2a8efeb9c85fa55f854930974d68f51f [file]
/*
* Copyright (c) 2022, 2025, 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 jdk.internal.classfile.impl;
import java.lang.classfile.*;
import java.lang.classfile.attribute.*;
import java.lang.classfile.constantpool.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import jdk.internal.access.SharedSecrets;
import static java.lang.classfile.Attributes.*;
public abstract sealed class BoundAttribute<T extends Attribute<T>>
extends AbstractElement
implements Attribute<T>, Util.Writable {
static final int NAME_AND_LENGTH_PREFIX = 6;
private final AttributeMapper<T> mapper;
final ClassReaderImpl classReader;
final int payloadStart;
Utf8Entry name;
BoundAttribute(ClassReader classReader, AttributeMapper<T> mapper, int payloadStart) {
this.mapper = mapper;
this.classReader = (ClassReaderImpl)classReader;
this.payloadStart = payloadStart;
}
public int payloadLen() {
return classReader.readInt(payloadStart - 4);
}
@Override
public Utf8Entry attributeName() {
if (name == null) {
name = classReader.readEntry(payloadStart - 6, Utf8Entry.class);
}
return name;
}
@Override
public AttributeMapper<T> attributeMapper() {
return mapper;
}
public byte[] contents() {
return classReader.readBytes(payloadStart, payloadLen());
}
@Override
public void writeTo(DirectClassBuilder builder) {
builder.writeAttribute(this);
}
@Override
public void writeTo(DirectCodeBuilder builder) {
builder.writeAttribute(this);
}
@Override
public void writeTo(DirectMethodBuilder builder) {
builder.writeAttribute(this);
}
@Override
public void writeTo(DirectFieldBuilder builder) {
builder.writeAttribute(this);
}
@Override
@SuppressWarnings("unchecked")
public void writeTo(BufWriterImpl buf) {
if (!buf.canWriteDirect(classReader))
attributeMapper().writeAttribute(buf, (T) this);
else
classReader.copyBytesTo(buf, payloadStart - NAME_AND_LENGTH_PREFIX, payloadLen() + NAME_AND_LENGTH_PREFIX);
}
public ConstantPool constantPool() {
return classReader;
}
@Override
public String toString() {
return String.format("Attribute[name=%s]", mapper.name());
}
<E extends PoolEntry> List<E> readEntryList(int p, Class<E> type) {
int cnt = classReader.readU2(p);
p += 2;
var entries = new Object[cnt];
int end = p + (cnt * 2);
for (int i = 0; p < end; i++, p += 2) {
entries[i] = classReader.readEntry(p, type);
}
return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(entries);
}
public static List<Attribute<?>> readAttributes(AttributedElement enclosing, ClassReader reader, int pos,
Function<Utf8Entry, AttributeMapper<?>> customAttributes) {
int size = reader.readU2(pos);
var filled = new ArrayList<Attribute<?>>(size);
int p = pos + 2;
int cfLen = reader.classfileLength();
for (int i = 0; i < size; ++i) {
Utf8Entry name = reader.readEntry(p, Utf8Entry.class);
int len = reader.readInt(p + 2);
p += 6;
if (len < 0 || len > cfLen - p) {
throw new IllegalArgumentException("attribute " + name.stringValue() + " too big to handle");
}
var mapper = standardAttribute(name);
if (mapper == null) {
mapper = customAttributes.apply(name);
}
if (mapper != null) {
filled.add(Objects.requireNonNull(mapper.readAttribute(enclosing, reader, p)));
} else {
AttributeMapper<UnknownAttribute> fakeMapper = new AttributeMapper<>() {
@Override
public String name() {
return name.stringValue();
}
@Override
public UnknownAttribute readAttribute(AttributedElement enclosing, ClassReader cf, int pos) {
// Will never get called
throw new UnsupportedOperationException();
}
@Override
public void writeAttribute(BufWriter buf, UnknownAttribute attr) {
buf.writeIndex(name);
var cont = attr.contents();
buf.writeInt(cont.length);
buf.writeBytes(cont);
}
@Override
public boolean allowMultiple() {
return true;
}
@Override
public AttributeMapper.AttributeStability stability() {
return AttributeStability.UNKNOWN;
}
};
filled.add(new BoundUnknownAttribute(reader, fakeMapper, p));
}
p += len;
}
return Collections.unmodifiableList(filled);
}
public static final class BoundUnknownAttribute extends BoundAttribute<UnknownAttribute>
implements UnknownAttribute {
public BoundUnknownAttribute(ClassReader cf, AttributeMapper<UnknownAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
}
public static final class BoundStackMapTableAttribute
extends BoundAttribute<StackMapTableAttribute>
implements StackMapTableAttribute {
final MethodModel method;
final LabelContext ctx;
List<StackMapFrameInfo> entries = null;
public BoundStackMapTableAttribute(CodeImpl code, ClassReader cf, AttributeMapper<StackMapTableAttribute> mapper, int pos) {
super(cf, mapper, pos);
method = code.parent().orElseThrow();
ctx = code;
}
@Override
public List<StackMapFrameInfo> entries() {
if (entries == null) {
entries = new StackMapDecoder(classReader, payloadStart, ctx, StackMapDecoder.initFrameLocals(method)).entries();
}
return entries;
}
@Override
public void writeTo(BufWriterImpl buf) {
if (buf.canWriteDirect(classReader) && buf.labelsMatch(ctx)) {
classReader.copyBytesTo(buf, payloadStart - NAME_AND_LENGTH_PREFIX, payloadLen() + NAME_AND_LENGTH_PREFIX);
} else {
attributeMapper().writeAttribute(buf, this);
}
}
}
public static final class BoundSyntheticAttribute extends BoundAttribute<SyntheticAttribute>
implements SyntheticAttribute {
public BoundSyntheticAttribute(ClassReader cf, AttributeMapper<SyntheticAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
}
public static final class BoundLineNumberTableAttribute
extends BoundAttribute<LineNumberTableAttribute>
implements LineNumberTableAttribute {
private List<LineNumberInfo> lineNumbers = null;
public BoundLineNumberTableAttribute(ClassReader cf, AttributeMapper<LineNumberTableAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public List<LineNumberInfo> lineNumbers() {
if (lineNumbers == null) {
int nLn = classReader.readU2(payloadStart);
LineNumberInfo[] elements = new LineNumberInfo[nLn];
int p = payloadStart + 2;
int pEnd = p + (nLn * 4);
for (int i = 0; p < pEnd; p += 4, i++) {
int startPc = classReader.readU2(p);
int lineNumber = classReader.readU2(p + 2);
elements[i] = LineNumberInfo.of(startPc, lineNumber);
}
lineNumbers = List.of(elements);
}
return lineNumbers;
}
}
public static final class BoundCharacterRangeTableAttribute extends BoundAttribute<CharacterRangeTableAttribute> implements CharacterRangeTableAttribute {
private List<CharacterRangeInfo> characterRangeTable = null;
public BoundCharacterRangeTableAttribute(ClassReader cf, AttributeMapper<CharacterRangeTableAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public List<CharacterRangeInfo> characterRangeTable() {
if (characterRangeTable == null) {
int nLn = classReader.readU2(payloadStart);
CharacterRangeInfo[] elements = new CharacterRangeInfo[nLn];
int p = payloadStart + 2;
int pEnd = p + (nLn * 14);
for (int i = 0; p < pEnd; p += 14, i++) {
int startPc = classReader.readU2(p);
int endPc = classReader.readU2(p + 2);
int characterRangeStart = classReader.readInt(p + 4);
int characterRangeEnd = classReader.readInt(p + 8);
int flags = classReader.readU2(p + 12);
elements[i] = CharacterRangeInfo.of(startPc, endPc, characterRangeStart, characterRangeEnd, flags);
}
characterRangeTable = List.of(elements);
}
return characterRangeTable;
}
}
public static final class BoundLocalVariableTableAttribute
extends BoundAttribute<LocalVariableTableAttribute>
implements LocalVariableTableAttribute {
private final CodeImpl codeAttribute;
private List<LocalVariableInfo> localVars = null;
public BoundLocalVariableTableAttribute(AttributedElement enclosing, ClassReader cf, AttributeMapper<LocalVariableTableAttribute> mapper, int pos) {
super(cf, mapper, pos);
if (enclosing instanceof CodeImpl ci) {
this.codeAttribute = ci;
} else {
throw new IllegalArgumentException("Invalid LocalVariableTable attribute location");
}
}
@Override
public List<LocalVariableInfo> localVariables() {
if (localVars == null) {
int cnt = classReader.readU2(payloadStart);
BoundLocalVariable[] elements = new BoundLocalVariable[cnt];
int p = payloadStart + 2;
int pEnd = p + (cnt * 10);
for (int i = 0; p < pEnd; p += 10, i++) {
elements[i] = new BoundLocalVariable(codeAttribute, p);
}
localVars = List.of(elements);
}
return localVars;
}
}
public static final class BoundLocalVariableTypeTableAttribute
extends BoundAttribute<LocalVariableTypeTableAttribute>
implements LocalVariableTypeTableAttribute {
private final CodeImpl codeAttribute;
private List<LocalVariableTypeInfo> localVars = null;
public BoundLocalVariableTypeTableAttribute(AttributedElement enclosing, ClassReader cf, AttributeMapper<LocalVariableTypeTableAttribute> mapper, int pos) {
super(cf, mapper, pos);
if (enclosing instanceof CodeImpl ci) {
this.codeAttribute = ci;
} else {
throw new IllegalArgumentException("Invalid LocalVariableTypeTable attribute location");
}
}
@Override
public List<LocalVariableTypeInfo> localVariableTypes() {
if (localVars == null) {
final int cnt = classReader.readU2(payloadStart);
BoundLocalVariableType[] elements = new BoundLocalVariableType[cnt];
int p = payloadStart + 2;
int pEnd = p + (cnt * 10);
for (int i = 0; p < pEnd; p += 10, i++) {
elements[i] = new BoundLocalVariableType(codeAttribute, p);
}
localVars = List.of(elements);
}
return localVars;
}
}
public static final class BoundMethodParametersAttribute extends BoundAttribute<MethodParametersAttribute>
implements MethodParametersAttribute {
private List<MethodParameterInfo> parameters = null;
public BoundMethodParametersAttribute(ClassReader cf, AttributeMapper<MethodParametersAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public List<MethodParameterInfo> parameters() {
if (parameters == null) {
final int cnt = classReader.readU1(payloadStart);
MethodParameterInfo[] elements = new MethodParameterInfo[cnt];
int p = payloadStart + 1;
int pEnd = p + (cnt * 4);
for (int i = 0; p < pEnd; p += 4, i++) {
Utf8Entry name = classReader.readEntryOrNull(p, Utf8Entry.class);
int accessFlags = classReader.readU2(p + 2);
elements[i] = MethodParameterInfo.of(Optional.ofNullable(name), accessFlags);
}
parameters = List.of(elements);
}
return parameters;
}
}
public static final class BoundModuleHashesAttribute extends BoundAttribute<ModuleHashesAttribute>
implements ModuleHashesAttribute {
private List<ModuleHashInfo> hashes = null;
public BoundModuleHashesAttribute(ClassReader cf, AttributeMapper<ModuleHashesAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public Utf8Entry algorithm() {
return classReader.readEntry(payloadStart, Utf8Entry.class);
}
@Override
public List<ModuleHashInfo> hashes() {
if (hashes == null) {
final int cnt = classReader.readU2(payloadStart + 2);
ModuleHashInfo[] elements = new ModuleHashInfo[cnt];
int p = payloadStart + 4;
//System.err.printf("%5d: ModuleHashesAttr alg = %s, cnt = %d%n", pos, algorithm(), cnt);
for (int i = 0; i < cnt; ++i) {
ModuleEntry module = classReader.readEntry(p, ModuleEntry.class);
int hashLength = classReader.readU2(p + 2);
//System.err.printf("%5d: [%d] module = %s, hashLength = %d%n", p, i, module, hashLength);
p += 4;
elements[i] = ModuleHashInfo.of(module, classReader.readBytes(p, hashLength));
p += hashLength;
}
hashes = List.of(elements);
}
return hashes;
}
}
public static final class BoundRecordAttribute extends BoundAttribute<RecordAttribute>
implements RecordAttribute {
private List<RecordComponentInfo> components = null;
public BoundRecordAttribute(ClassReader cf, AttributeMapper<RecordAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public List<RecordComponentInfo> components() {
if (components == null) {
final int cnt = classReader.readU2(payloadStart);
RecordComponentInfo[] elements = new RecordComponentInfo[cnt];
int p = payloadStart + 2;
for (int i = 0; i < cnt; i++) {
elements[i] = new BoundRecordComponentInfo(classReader, p);
p = classReader.skipAttributeHolder(p + 4);
}
components = List.of(elements);
}
return components;
}
}
public static final class BoundDeprecatedAttribute extends BoundAttribute<DeprecatedAttribute>
implements DeprecatedAttribute {
public BoundDeprecatedAttribute(ClassReader cf, AttributeMapper<DeprecatedAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
}
public static final class BoundSignatureAttribute extends BoundAttribute<SignatureAttribute>
implements SignatureAttribute {
public BoundSignatureAttribute(ClassReader cf, AttributeMapper<SignatureAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public Utf8Entry signature() {
return classReader.readEntry(payloadStart, Utf8Entry.class);
}
}
public static final class BoundSourceFileAttribute extends BoundAttribute<SourceFileAttribute>
implements SourceFileAttribute {
public BoundSourceFileAttribute(ClassReader cf, AttributeMapper<SourceFileAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public Utf8Entry sourceFile() {
return classReader.readEntry(payloadStart, Utf8Entry.class);
}
}
public static final class BoundModuleMainClassAttribute extends BoundAttribute<ModuleMainClassAttribute> implements ModuleMainClassAttribute {
public BoundModuleMainClassAttribute(ClassReader cf, AttributeMapper<ModuleMainClassAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public ClassEntry mainClass() {
return classReader.readEntry(payloadStart, ClassEntry.class);
}
}
public static final class BoundNestHostAttribute extends BoundAttribute<NestHostAttribute>
implements NestHostAttribute {
public BoundNestHostAttribute(ClassReader cf, AttributeMapper<NestHostAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public ClassEntry nestHost() {
return classReader.readEntry(payloadStart, ClassEntry.class);
}
}
public static final class BoundSourceDebugExtensionAttribute extends BoundAttribute<SourceDebugExtensionAttribute>
implements SourceDebugExtensionAttribute {
public BoundSourceDebugExtensionAttribute(ClassReader cf, AttributeMapper<SourceDebugExtensionAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
}
public static final class BoundConstantValueAttribute extends BoundAttribute<ConstantValueAttribute>
implements ConstantValueAttribute {
public BoundConstantValueAttribute(ClassReader cf, AttributeMapper<ConstantValueAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public ConstantValueEntry constant() {
return classReader.readEntry(payloadStart, ConstantValueEntry.class);
}
}
public static final class BoundModuleTargetAttribute extends BoundAttribute<ModuleTargetAttribute>
implements ModuleTargetAttribute {
public BoundModuleTargetAttribute(ClassReader cf, AttributeMapper<ModuleTargetAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public Utf8Entry targetPlatform() {
return classReader.readEntry(payloadStart, Utf8Entry.class);
}
}
public static final class BoundCompilationIDAttribute extends BoundAttribute<CompilationIDAttribute>
implements CompilationIDAttribute {
public BoundCompilationIDAttribute(ClassReader cf, AttributeMapper<CompilationIDAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public Utf8Entry compilationId() {
return classReader.readEntry(payloadStart, Utf8Entry.class);
}
}
public static final class BoundSourceIDAttribute extends BoundAttribute<SourceIDAttribute>
implements SourceIDAttribute {
public BoundSourceIDAttribute(ClassReader cf, AttributeMapper<SourceIDAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public Utf8Entry sourceId() {
return classReader.readEntry(payloadStart, Utf8Entry.class);
}
}
public static final class BoundModuleResolutionAttribute extends BoundAttribute<ModuleResolutionAttribute>
implements ModuleResolutionAttribute {
public BoundModuleResolutionAttribute(ClassReader cf, AttributeMapper<ModuleResolutionAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public int resolutionFlags() {
return classReader.readU2(payloadStart);
}
}
public static final class BoundExceptionsAttribute extends BoundAttribute<ExceptionsAttribute>
implements ExceptionsAttribute {
private List<ClassEntry> exceptions = null;
public BoundExceptionsAttribute(ClassReader cf, AttributeMapper<ExceptionsAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public List<ClassEntry> exceptions() {
if (exceptions == null) {
exceptions = readEntryList(payloadStart, ClassEntry.class);
}
return exceptions;
}
}
public static final class BoundModuleAttribute extends BoundAttribute<ModuleAttribute>
implements ModuleAttribute {
private List<ModuleRequireInfo> requires = null;
private List<ModuleExportInfo> exports = null;
private List<ModuleOpenInfo> opens = null;
private List<ClassEntry> uses = null;
private List<ModuleProvideInfo> provides = null;
public BoundModuleAttribute(ClassReader cf, AttributeMapper<ModuleAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public ModuleEntry moduleName() {
return classReader.readEntry(payloadStart, ModuleEntry.class);
}
@Override
public int moduleFlagsMask() {
return classReader.readU2(payloadStart + 2);
}
@Override
public Optional<Utf8Entry> moduleVersion() {
return Optional.ofNullable(classReader.readEntryOrNull(payloadStart + 4, Utf8Entry.class));
}
@Override
public List<ModuleRequireInfo> requires() {
if (requires == null) {
structure();
}
return requires;
}
@Override
public List<ModuleExportInfo> exports() {
if (exports == null) {
structure();
}
return exports;
}
@Override
public List<ModuleOpenInfo> opens() {
if (opens == null) {
structure();
}
return opens;
}
@Override
public List<ClassEntry> uses() {
if (uses == null) {
structure();
}
return uses;
}
@Override
public List<ModuleProvideInfo> provides() {
if (provides == null) {
structure();
}
return provides;
}
private void structure() {
int p = payloadStart + 8;
{
int cnt = classReader.readU2(payloadStart + 6);
ModuleRequireInfo[] elements = new ModuleRequireInfo[cnt];
int end = p + (cnt * 6);
for (int i = 0; p < end; p += 6, i++) {
elements[i] = ModuleRequireInfo.of(classReader.readEntry(p, ModuleEntry.class),
classReader.readU2(p + 2),
classReader.readEntryOrNull(p + 4, Utf8Entry.class));
}
requires = List.of(elements);
}
{
int cnt = classReader.readU2(p);
p += 2;
ModuleExportInfo[] elements = new ModuleExportInfo[cnt];
for (int i = 0; i < cnt; i++) {
PackageEntry pe = classReader.readEntry(p, PackageEntry.class);
int exportFlags = classReader.readU2(p + 2);
p += 4;
List<ModuleEntry> exportsTo = readEntryList(p, ModuleEntry.class);
p += 2 + exportsTo.size() * 2;
elements[i] = ModuleExportInfo.of(pe, exportFlags, exportsTo);
}
exports = List.of(elements);
}
{
int cnt = classReader.readU2(p);
p += 2;
ModuleOpenInfo[] elements = new ModuleOpenInfo[cnt];
for (int i = 0; i < cnt; i++) {
PackageEntry po = classReader.readEntry(p, PackageEntry.class);
int opensFlags = classReader.readU2(p + 2);
p += 4;
List<ModuleEntry> opensTo = readEntryList(p, ModuleEntry.class);
p += 2 + opensTo.size() * 2;
elements[i] = ModuleOpenInfo.of(po, opensFlags, opensTo);
}
opens = List.of(elements);
}
{
uses = readEntryList(p, ClassEntry.class);
p += 2 + uses.size() * 2;
int cnt = classReader.readU2(p);
p += 2;
ModuleProvideInfo[] elements = new ModuleProvideInfo[cnt];
provides = new ArrayList<>(cnt);
for (int i = 0; i < cnt; i++) {
ClassEntry c = classReader.readEntry(p, ClassEntry.class);
p += 2;
List<ClassEntry> providesWith = readEntryList(p, ClassEntry.class);
p += 2 + providesWith.size() * 2;
elements[i] = ModuleProvideInfo.of(c, providesWith);
}
provides = List.of(elements);
}
}
}
public static final class BoundModulePackagesAttribute extends BoundAttribute<ModulePackagesAttribute>
implements ModulePackagesAttribute {
private List<PackageEntry> packages = null;
public BoundModulePackagesAttribute(ClassReader cf, AttributeMapper<ModulePackagesAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public List<PackageEntry> packages() {
if (packages == null) {
packages = readEntryList(payloadStart, PackageEntry.class);
}
return packages;
}
}
public static final class BoundNestMembersAttribute extends BoundAttribute<NestMembersAttribute>
implements NestMembersAttribute {
private List<ClassEntry> members = null;
public BoundNestMembersAttribute(ClassReader cf, AttributeMapper<NestMembersAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public List<ClassEntry> nestMembers() {
if (members == null) {
members = readEntryList(payloadStart, ClassEntry.class);
}
return members;
}
}
public static final class BoundBootstrapMethodsAttribute extends BoundAttribute<BootstrapMethodsAttribute>
implements BootstrapMethodsAttribute {
private List<BootstrapMethodEntry> bootstraps = null;
private final int size;
public BoundBootstrapMethodsAttribute(ClassReader reader, AttributeMapper<BootstrapMethodsAttribute> mapper, int pos) {
super(reader, mapper, pos);
size = classReader.readU2(pos);
}
@Override
public int bootstrapMethodsSize() {
return size;
}
@Override
public List<BootstrapMethodEntry> bootstrapMethods() {
if (bootstraps == null) {
BootstrapMethodEntry[] bs = new BootstrapMethodEntry[size];
int p = payloadStart + 2;
for (int i = 0; i < size; ++i) {
final var handle = classReader.readEntry(p, AbstractPoolEntry.MethodHandleEntryImpl.class);
final List<LoadableConstantEntry> args = readEntryList(p + 2, LoadableConstantEntry.class);
p += 4 + args.size() * 2;
int hash = BootstrapMethodEntryImpl.computeHashCode(handle, args);
bs[i] = new BootstrapMethodEntryImpl(classReader, i, hash, handle, args);
}
bootstraps = List.of(bs);
}
return bootstraps;
}
}
public static final class BoundInnerClassesAttribute extends BoundAttribute<InnerClassesAttribute>
implements InnerClassesAttribute {
private List<InnerClassInfo> classes;
public BoundInnerClassesAttribute(ClassReader cf, AttributeMapper<InnerClassesAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public List<InnerClassInfo> classes() {
if (classes == null) {
final int cnt = classReader.readU2(payloadStart);
int p = payloadStart + 2;
InnerClassInfo[] elements = new InnerClassInfo[cnt];
for (int i = 0; i < cnt; i++) {
ClassEntry innerClass = classReader.readEntry(p, ClassEntry.class);
var outerClass = classReader.readEntryOrNull(p + 2, ClassEntry.class);
var innerName = classReader.readEntryOrNull(p + 4, Utf8Entry.class);
int flags = classReader.readU2(p + 6);
p += 8;
elements[i] = InnerClassInfo.of(innerClass, Optional.ofNullable(outerClass), Optional.ofNullable(innerName), flags);
}
classes = List.of(elements);
}
return classes;
}
}
public static final class BoundEnclosingMethodAttribute extends BoundAttribute<EnclosingMethodAttribute>
implements EnclosingMethodAttribute {
public BoundEnclosingMethodAttribute(ClassReader cf, AttributeMapper<EnclosingMethodAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public ClassEntry enclosingClass() {
return classReader.readEntry(payloadStart, ClassEntry.class);
}
@Override
public Optional<NameAndTypeEntry> enclosingMethod() {
return Optional.ofNullable(classReader.readEntryOrNull(payloadStart + 2, NameAndTypeEntry.class));
}
}
public static final class BoundAnnotationDefaultAttr
extends BoundAttribute<AnnotationDefaultAttribute>
implements AnnotationDefaultAttribute {
private AnnotationValue annotationValue;
public BoundAnnotationDefaultAttr(ClassReader cf, AttributeMapper<AnnotationDefaultAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public AnnotationValue defaultValue() {
if (annotationValue == null)
annotationValue = AnnotationReader.readElementValue(classReader, payloadStart);
return annotationValue;
}
}
public static final class BoundRuntimeVisibleTypeAnnotationsAttribute extends BoundAttribute<RuntimeVisibleTypeAnnotationsAttribute>
implements RuntimeVisibleTypeAnnotationsAttribute {
private final LabelContext labelContext;
public BoundRuntimeVisibleTypeAnnotationsAttribute(AttributedElement enclosing, ClassReader cf, AttributeMapper<RuntimeVisibleTypeAnnotationsAttribute> mapper, int pos) {
super(cf, mapper, pos);
this.labelContext = (enclosing instanceof LabelContext lc) ? lc : null;
}
@Override
public List<TypeAnnotation> annotations() {
return AnnotationReader.readTypeAnnotations(classReader, payloadStart, labelContext);
}
}
public static final class BoundRuntimeInvisibleTypeAnnotationsAttribute
extends BoundAttribute<RuntimeInvisibleTypeAnnotationsAttribute>
implements RuntimeInvisibleTypeAnnotationsAttribute {
public BoundRuntimeInvisibleTypeAnnotationsAttribute(AttributedElement enclosing, ClassReader cf, AttributeMapper<RuntimeInvisibleTypeAnnotationsAttribute> mapper, int pos) {
super(cf, mapper, pos);
this.labelContext = (enclosing instanceof LabelContext lc) ? lc : null;
}
private final LabelContext labelContext;
@Override
public List<TypeAnnotation> annotations() {
return AnnotationReader.readTypeAnnotations(classReader, payloadStart, labelContext);
}
}
public static final class BoundRuntimeVisibleParameterAnnotationsAttribute
extends BoundAttribute<RuntimeVisibleParameterAnnotationsAttribute>
implements RuntimeVisibleParameterAnnotationsAttribute {
public BoundRuntimeVisibleParameterAnnotationsAttribute(ClassReader cf, AttributeMapper<RuntimeVisibleParameterAnnotationsAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public List<List<Annotation>> parameterAnnotations() {
return AnnotationReader.readParameterAnnotations(classReader, payloadStart);
}
}
public static final class BoundRuntimeInvisibleParameterAnnotationsAttribute
extends BoundAttribute<RuntimeInvisibleParameterAnnotationsAttribute>
implements RuntimeInvisibleParameterAnnotationsAttribute {
public BoundRuntimeInvisibleParameterAnnotationsAttribute(ClassReader cf, AttributeMapper<RuntimeInvisibleParameterAnnotationsAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public List<List<Annotation>> parameterAnnotations() {
return AnnotationReader.readParameterAnnotations(classReader, payloadStart);
}
}
public static final class BoundRuntimeInvisibleAnnotationsAttribute
extends BoundAttribute<RuntimeInvisibleAnnotationsAttribute>
implements RuntimeInvisibleAnnotationsAttribute {
private List<Annotation> inflated;
public BoundRuntimeInvisibleAnnotationsAttribute(ClassReader cf,
int payloadStart) {
super(cf, Attributes.runtimeInvisibleAnnotations(), payloadStart);
}
@Override
public List<Annotation> annotations() {
if (inflated == null)
inflated = AnnotationReader.readAnnotations(classReader, payloadStart);
return inflated;
}
}
public static final class BoundRuntimeVisibleAnnotationsAttribute
extends BoundAttribute<RuntimeVisibleAnnotationsAttribute>
implements RuntimeVisibleAnnotationsAttribute {
private List<Annotation> inflated;
public BoundRuntimeVisibleAnnotationsAttribute(ClassReader cf,
int payloadStart) {
super(cf, Attributes.runtimeVisibleAnnotations(), payloadStart);
}
@Override
public List<Annotation> annotations() {
if (inflated == null)
inflated = AnnotationReader.readAnnotations(classReader, payloadStart);
return inflated;
}
}
public static final class BoundPermittedSubclassesAttribute extends BoundAttribute<PermittedSubclassesAttribute>
implements PermittedSubclassesAttribute {
private List<ClassEntry> permittedSubclasses = null;
public BoundPermittedSubclassesAttribute(ClassReader cf, AttributeMapper<PermittedSubclassesAttribute> mapper, int pos) {
super(cf, mapper, pos);
}
@Override
public List<ClassEntry> permittedSubclasses() {
if (permittedSubclasses == null) {
permittedSubclasses = readEntryList(payloadStart, ClassEntry.class);
}
return permittedSubclasses;
}
}
public abstract static sealed class BoundCodeAttribute
extends BoundAttribute<CodeAttribute>
implements CodeAttribute
permits CodeImpl {
protected final int codeStart;
protected final int codeLength;
protected final int codeEnd;
protected final int attributePos;
protected final int exceptionHandlerPos;
protected final int exceptionHandlerCnt;
protected final MethodModel enclosingMethod;
public BoundCodeAttribute(AttributedElement enclosing,
ClassReader reader,
AttributeMapper<CodeAttribute> mapper,
int payloadStart) {
super(reader, mapper, payloadStart);
this.codeLength = classReader.readInt(payloadStart + 4);
this.enclosingMethod = (MethodModel) enclosing;
this.codeStart = payloadStart + 8;
this.codeEnd = codeStart + codeLength;
this.exceptionHandlerPos = codeEnd;
this.exceptionHandlerCnt = classReader.readU2(exceptionHandlerPos);
this.attributePos = exceptionHandlerPos + 2 + exceptionHandlerCnt * 8;
}
// CodeAttribute
@Override
public int maxStack() {
return classReader.readU2(payloadStart);
}
@Override
public int maxLocals() {
return classReader.readU2(payloadStart + 2);
}
@Override
public int codeLength() {
return codeLength;
}
@Override
public byte[] codeArray() {
return classReader.readBytes(payloadStart + 8, codeLength());
}
}
/**
* {@return the attribute mapper for a standard attribute}
*
* @param name the name of the attribute to find
*/
public static AttributeMapper<?> standardAttribute(Utf8Entry name) {
// critical bootstrap path, so no lambdas nor method handles here
return switch (name.hashCode()) {
case 0x46699ff2 ->
name.equalsString(NAME_ANNOTATION_DEFAULT) ? annotationDefault() : null;
case 0x5208e184 ->
name.equalsString(NAME_BOOTSTRAP_METHODS) ? bootstrapMethods() : null;
case 0xcb60907a ->
name.equalsString(NAME_CHARACTER_RANGE_TABLE) ? characterRangeTable() : null;
case 0x4020220d ->
name.equalsString(NAME_CODE) ? code() : null;
case 0xc20dd1fe ->
name.equalsString(NAME_COMPILATION_ID) ? compilationId() : null;
case 0xcab1940d ->
name.equalsString(NAME_CONSTANT_VALUE) ? constantValue() : null;
case 0x558641d3 ->
name.equalsString(NAME_DEPRECATED) ? deprecated() : null;
case 0x51d443cd ->
name.equalsString(NAME_ENCLOSING_METHOD) ? enclosingMethod() : null;
case 0x687c1624 ->
name.equalsString(NAME_EXCEPTIONS) ? exceptions() : null;
case 0x7adb2910 ->
name.equalsString(NAME_INNER_CLASSES) ? innerClasses() : null;
case 0x653f0551 ->
name.equalsString(NAME_LINE_NUMBER_TABLE) ? lineNumberTable() : null;
case 0x64c75927 ->
name.equalsString(NAME_LOCAL_VARIABLE_TABLE) ? localVariableTable() : null;
case 0x6697f98d ->
name.equalsString(NAME_LOCAL_VARIABLE_TYPE_TABLE) ? localVariableTypeTable() : null;
case 0xdbb0cdcb ->
name.equalsString(NAME_METHOD_PARAMETERS) ? methodParameters() : null;
case 0xc9b0928c ->
name.equalsString(NAME_MODULE) ? module() : null;
case 0x41cd27e8 ->
name.equalsString(NAME_MODULE_HASHES) ? moduleHashes() : null;
case 0x7deb0a13 ->
name.equalsString(NAME_MODULE_MAIN_CLASS) ? moduleMainClass() : null;
case 0x6706ff99 ->
name.equalsString(NAME_MODULE_PACKAGES) ? modulePackages() : null;
case 0x60272858 ->
name.equalsString(NAME_MODULE_RESOLUTION) ? moduleResolution() : null;
case 0x5646d73d ->
name.equalsString(NAME_MODULE_TARGET) ? moduleTarget() : null;
case 0x50336c40 ->
name.equalsString(NAME_NEST_HOST) ? nestHost() : null;
case 0x4735ab81 ->
name.equalsString(NAME_NEST_MEMBERS) ? nestMembers() : null;
case 0x7100d9fe ->
name.equalsString(NAME_PERMITTED_SUBCLASSES) ? permittedSubclasses() : null;
case 0xd1ab5871 ->
name.equalsString(NAME_RECORD) ? record() : null;
case 0x7588550f ->
name.equalsString(NAME_RUNTIME_INVISIBLE_ANNOTATIONS) ? runtimeInvisibleAnnotations() : null;
case 0xcc74da30 ->
name.equalsString(NAME_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS) ? runtimeInvisibleParameterAnnotations() : null;
case 0xf67697f5 ->
name.equalsString(NAME_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS) ? runtimeInvisibleTypeAnnotations() : null;
case 0xe0837d2a ->
name.equalsString(NAME_RUNTIME_VISIBLE_ANNOTATIONS) ? runtimeVisibleAnnotations() : null;
case 0xc945a075 ->
name.equalsString(NAME_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS) ? runtimeVisibleParameterAnnotations() : null;
case 0x611a3a90 ->
name.equalsString(NAME_RUNTIME_VISIBLE_TYPE_ANNOTATIONS) ? runtimeVisibleTypeAnnotations() : null;
case 0xf76fb898 ->
name.equalsString(NAME_SIGNATURE) ? signature() : null;
case 0x6b41b047 ->
name.equalsString(NAME_SOURCE_DEBUG_EXTENSION) ? sourceDebugExtension() : null;
case 0x748c2857 ->
name.equalsString(NAME_SOURCE_FILE) ? sourceFile() : null;
case 0x6bf13a96 ->
name.equalsString(NAME_SOURCE_ID) ? sourceId() : null;
case 0xfa85ee5a ->
name.equalsString(NAME_STACK_MAP_TABLE) ? stackMapTable() : null;
case 0xf2670725 ->
name.equalsString(NAME_SYNTHETIC) ? synthetic() : null;
default -> null;
};
}
}