8332614: Type-checked ConstantPool.entryByIndex and ClassReader.readEntryOrNull

Reviewed-by: asotona
This commit is contained in:
Chen Liang 2024-05-30 11:51:49 +00:00 committed by Adam Sotona
parent 1b04f6487c
commit f608918df3
15 changed files with 230 additions and 90 deletions

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* 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
@ -76,17 +76,14 @@ public sealed interface ClassReader extends ConstantPool
// Constant pool
/**
* {@return the UTF8 constant pool entry at the given index of the constant
* pool} The given index must correspond to a valid constant pool index
* whose slot holds a UTF8 constant.
* @param index the index into the constant pool
*/
Utf8Entry utf8EntryByIndex(int index);
/**
* {@return the constant pool entry whose index is given at the specified
* offset within the classfile}
*
* @apiNote
* If only a particular type of entry is expected, use {@link #readEntry(
* int, Class) readEntry(int, Class)}.
*
* @param offset the offset of the index within the classfile
* @throws ConstantPoolException if the index is out of range of the
* constant pool size, or zero
@ -108,12 +105,31 @@ public sealed interface ClassReader extends ConstantPool
* {@return the constant pool entry whose index is given at the specified
* offset within the classfile, or null if the index at the specified
* offset is zero}
*
* @apiNote
* If only a particular type of entry is expected, use {@link #readEntryOrNull(
* int, Class) readEntryOrNull(int, Class)}.
*
* @param offset the offset of the index within the classfile
* @throws ConstantPoolException if the index is out of range of the
* constant pool size
*/
PoolEntry readEntryOrNull(int offset);
/**
* {@return the constant pool entry of a given type whose index is given
* at the specified offset within the classfile, or null if the index at
* the specified offset is zero}
*
* @param <T> the entry type
* @param offset the offset of the index within the classfile
* @param cls the entry type
* @throws ConstantPoolException if the index is out of range of the
* constant pool size, or zero, or the entry is not of the given type
* @since 24
*/
<T extends PoolEntry> T readEntryOrNull(int offset, Class<T> cls);
/**
* {@return the UTF8 entry whose index is given at the specified
* offset within the classfile}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* 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
@ -46,6 +46,10 @@ public sealed interface ConstantPool extends Iterable<PoolEntry>
/**
* {@return the entry at the specified index}
*
* @apiNote
* If only a particular type of entry is expected, use {@link #entryByIndex(
* int, Class) entryByIndex(int, Class)}.
*
* @param index the index within the pool of the desired entry
* @throws ConstantPoolException if the index is out of range of the
* constant pool, or is considered unusable
@ -57,6 +61,18 @@ public sealed interface ConstantPool extends Iterable<PoolEntry>
*/
int size();
/**
* {@return the entry of a given type at the specified index}
*
* @param <T> the entry type
* @param index the index within the pool of the desired entry
* @param cls the entry type
* @throws ConstantPoolException if the index is out of range of the
* constant pool, or the entry is not of the given type
* @since 24
*/
<T extends PoolEntry> T entryByIndex(int index, Class<T> cls);
/**
* {@return an iterator over pool entries}
*/

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* 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
@ -46,7 +46,7 @@ public class AbstractBoundLocalVariable
public Utf8Entry name() {
if (nameEntry == null)
nameEntry = (Utf8Entry) code.constantPool().entryByIndex(nameIndex());
nameEntry = code.constantPool().entryByIndex(nameIndex(), Utf8Entry.class);
return nameEntry;
}
@ -56,7 +56,7 @@ public class AbstractBoundLocalVariable
protected Utf8Entry secondaryEntry() {
if (secondaryEntry == null)
secondaryEntry = (Utf8Entry) code.constantPool().entryByIndex(secondaryIndex());
secondaryEntry = code.constantPool().entryByIndex(secondaryIndex(), Utf8Entry.class);
return secondaryEntry;
}

@ -687,10 +687,10 @@ public abstract sealed class AbstractInstruction
@Override
public LoadableConstantEntry constantEntry() {
return (LoadableConstantEntry)
code.classReader.entryByIndex(op == Opcode.LDC
return code.classReader.entryByIndex(op == Opcode.LDC
? code.classReader.readU1(pos + 1)
: code.classReader.readU2(pos + 1));
: code.classReader.readU2(pos + 1),
LoadableConstantEntry.class);
}
@Override

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* 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
@ -127,7 +127,7 @@ class AnnotationReader {
}
private static Annotation readAnnotation(ClassReader classReader, int p) {
Utf8Entry annotationClass = classReader.utf8EntryByIndex(classReader.readU2(p));
Utf8Entry annotationClass = classReader.entryByIndex(classReader.readU2(p), Utf8Entry.class);
p += 2;
List<AnnotationElement> elems = readAnnotationElementValuePairs(classReader, p);
return new AnnotationImpl(annotationClass, elems);

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* 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
@ -632,7 +632,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
for (int i = 0; p < end; p += 6, i++) {
elements[i] = ModuleRequireInfo.of(classReader.readModuleEntry(p),
classReader.readU2(p + 2),
(Utf8Entry) classReader.readEntryOrNull(p + 4));
classReader.readEntryOrNull(p + 4, Utf8Entry.class));
}
requires = List.of(elements);
}
@ -771,15 +771,9 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
int p = payloadStart + 2;
InnerClassInfo[] elements = new InnerClassInfo[cnt];
for (int i = 0; i < cnt; i++) {
ClassEntry innerClass = classReader.readClassEntry(p); // TODO FIXME
int outerClassIndex = classReader.readU2(p + 2);
ClassEntry outerClass = outerClassIndex == 0
? null
: (ClassEntry) classReader.entryByIndex(outerClassIndex);
int innerNameIndex = classReader.readU2(p + 4);
Utf8Entry innerName = innerNameIndex == 0
? null
: (Utf8Entry) classReader.entryByIndex(innerNameIndex);
ClassEntry innerClass = classReader.readClassEntry(p);
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);
@ -803,7 +797,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
@Override
public Optional<NameAndTypeEntry> enclosingMethod() {
return Optional.ofNullable((NameAndTypeEntry) classReader.readEntryOrNull(payloadStart + 2));
return Optional.ofNullable(classReader.readEntryOrNull(payloadStart + 2, NameAndTypeEntry.class));
}
}

@ -28,6 +28,7 @@ package jdk.internal.classfile.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
@ -158,8 +159,7 @@ public final class ClassReaderImpl
@Override
public Optional<ClassEntry> superclassEntry() {
if (superclass == null) {
int scIndex = readU2(thisClassPos + 2);
superclass = Optional.ofNullable(scIndex == 0 ? null : (ClassEntry) entryByIndex(scIndex));
superclass = Optional.ofNullable(readEntryOrNull(thisClassPos + 2, ClassEntry.class));
}
return superclass;
}
@ -338,10 +338,42 @@ public final class ClassReaderImpl
// Constantpool
@Override
public PoolEntry entryByIndex(int index) {
return entryByIndex(index, 0, 0xff);
return entryByIndex(index, PoolEntry.class);
}
private PoolEntry entryByIndex(int index, int lowerBoundTag, int upperBoundTag) {
private static boolean checkTag(int tag, Class<?> cls) {
var type = switch (tag) {
// JVMS Table 4.4-B. Constant pool tags
case TAG_UTF8 -> AbstractPoolEntry.Utf8EntryImpl.class;
case TAG_INTEGER -> AbstractPoolEntry.IntegerEntryImpl.class;
case TAG_FLOAT -> AbstractPoolEntry.FloatEntryImpl.class;
case TAG_LONG -> AbstractPoolEntry.LongEntryImpl.class;
case TAG_DOUBLE -> AbstractPoolEntry.DoubleEntryImpl.class;
case TAG_CLASS -> AbstractPoolEntry.ClassEntryImpl.class;
case TAG_STRING -> AbstractPoolEntry.StringEntryImpl.class;
case TAG_FIELDREF -> AbstractPoolEntry.FieldRefEntryImpl.class;
case TAG_METHODREF -> AbstractPoolEntry.MethodRefEntryImpl.class;
case TAG_INTERFACEMETHODREF -> AbstractPoolEntry.InterfaceMethodRefEntryImpl.class;
case TAG_NAMEANDTYPE -> AbstractPoolEntry.NameAndTypeEntryImpl.class;
case TAG_METHODHANDLE -> AbstractPoolEntry.MethodHandleEntryImpl.class;
case TAG_METHODTYPE -> AbstractPoolEntry.MethodTypeEntryImpl.class;
case TAG_CONSTANTDYNAMIC -> AbstractPoolEntry.ConstantDynamicEntryImpl.class;
case TAG_INVOKEDYNAMIC -> AbstractPoolEntry.InvokeDynamicEntryImpl.class;
case TAG_MODULE -> AbstractPoolEntry.ModuleEntryImpl.class;
case TAG_PACKAGE -> AbstractPoolEntry.PackageEntryImpl.class;
default -> null;
};
return type != null && cls.isAssignableFrom(type);
}
static <T extends PoolEntry> T checkType(PoolEntry e, int index, Class<T> cls) {
if (cls.isInstance(e)) return cls.cast(e);
throw new ConstantPoolException("Not a " + cls.getSimpleName() + " at index: " + index);
}
@Override
public <T extends PoolEntry> T entryByIndex(int index, Class<T> cls) {
Objects.requireNonNull(cls);
if (index <= 0 || index >= constantPoolCount) {
throw new ConstantPoolException("Bad CP index: " + index);
}
@ -352,9 +384,9 @@ public final class ClassReaderImpl
throw new ConstantPoolException("Unusable CP index: " + index);
}
int tag = readU1(offset);
if (tag < lowerBoundTag || tag > upperBoundTag) {
if (!checkTag(tag, cls)) {
throw new ConstantPoolException(
"Bad tag (" + tag + ") at index (" + index + ") position (" + offset + ")");
"Bad tag (" + tag + ") at index (" + index + ") position (" + offset + "), expected " + cls.getSimpleName());
}
final int q = offset + 1;
info = switch (tag) {
@ -374,7 +406,7 @@ public final class ClassReaderImpl
case TAG_NAMEANDTYPE -> new AbstractPoolEntry.NameAndTypeEntryImpl(this, index, (AbstractPoolEntry.Utf8EntryImpl) readUtf8Entry(q),
(AbstractPoolEntry.Utf8EntryImpl) readUtf8Entry(q + 2));
case TAG_METHODHANDLE -> new AbstractPoolEntry.MethodHandleEntryImpl(this, index, readU1(q),
readEntry(q + 1, AbstractPoolEntry.AbstractMemberRefEntry.class, TAG_FIELDREF, TAG_INTERFACEMETHODREF));
readEntry(q + 1, AbstractPoolEntry.AbstractMemberRefEntry.class));
case TAG_METHODTYPE -> new AbstractPoolEntry.MethodTypeEntryImpl(this, index, (AbstractPoolEntry.Utf8EntryImpl) readUtf8Entry(q));
case TAG_CONSTANTDYNAMIC -> new AbstractPoolEntry.ConstantDynamicEntryImpl(this, index, readU2(q), (AbstractPoolEntry.NameAndTypeEntryImpl) readNameAndTypeEntry(q + 2));
case TAG_INVOKEDYNAMIC -> new AbstractPoolEntry.InvokeDynamicEntryImpl(this, index, readU2(q), (AbstractPoolEntry.NameAndTypeEntryImpl) readNameAndTypeEntry(q + 2));
@ -385,15 +417,7 @@ public final class ClassReaderImpl
};
cp[index] = info;
}
return info;
}
@Override
public AbstractPoolEntry.Utf8EntryImpl utf8EntryByIndex(int index) {
if (entryByIndex(index, TAG_UTF8, TAG_UTF8) instanceof AbstractPoolEntry.Utf8EntryImpl utf8) {
return utf8;
}
throw new ConstantPoolException("Not a UTF8 - index: " + index);
return checkType(info, index, cls);
}
public int skipAttributeHolder(int offset) {
@ -418,17 +442,8 @@ public final class ClassReaderImpl
@Override
public <T extends PoolEntry> T readEntry(int pos, Class<T> cls) {
return readEntry(pos, cls, 0, 0xff);
}
private <T extends PoolEntry> T readEntry(int pos, Class<T> cls, int expectedTag) {
return readEntry(pos, cls, expectedTag, expectedTag);
}
private <T extends PoolEntry> T readEntry(int pos, Class<T> cls, int lowerBoundTag, int upperBoundTag) {
var e = entryByIndex(readU2(pos), lowerBoundTag, upperBoundTag);
if (cls.isInstance(e)) return cls.cast(e);
throw new ConstantPoolException("Not a " + cls.getSimpleName() + " at index: " + readU2(pos));
Objects.requireNonNull(cls);
return entryByIndex(readU2(pos), cls);
}
@Override
@ -440,44 +455,49 @@ public final class ClassReaderImpl
return entryByIndex(index);
}
@Override
public <T extends PoolEntry> T readEntryOrNull(int offset, Class<T> cls) {
Objects.requireNonNull(cls);
int index = readU2(offset);
if (index == 0) {
return null;
}
return entryByIndex(index, cls);
}
@Override
public Utf8Entry readUtf8Entry(int pos) {
int index = readU2(pos);
return utf8EntryByIndex(index);
return readEntry(pos, Utf8Entry.class);
}
@Override
public Utf8Entry readUtf8EntryOrNull(int pos) {
int index = readU2(pos);
if (index == 0) {
return null;
}
return utf8EntryByIndex(index);
return readEntryOrNull(pos, Utf8Entry.class);
}
@Override
public ModuleEntry readModuleEntry(int pos) {
return readEntry(pos, ModuleEntry.class, TAG_MODULE);
return readEntry(pos, ModuleEntry.class);
}
@Override
public PackageEntry readPackageEntry(int pos) {
return readEntry(pos, PackageEntry.class, TAG_PACKAGE);
return readEntry(pos, PackageEntry.class);
}
@Override
public ClassEntry readClassEntry(int pos) {
return readEntry(pos, ClassEntry.class, TAG_CLASS);
return readEntry(pos, ClassEntry.class);
}
@Override
public NameAndTypeEntry readNameAndTypeEntry(int pos) {
return readEntry(pos, NameAndTypeEntry.class, TAG_NAMEANDTYPE);
return readEntry(pos, NameAndTypeEntry.class);
}
@Override
public MethodHandleEntry readMethodHandleEntry(int pos) {
return readEntry(pos, MethodHandleEntry.class, TAG_METHODHANDLE);
return readEntry(pos, MethodHandleEntry.class);
}
@Override

@ -199,7 +199,7 @@ public final class CodeImpl
public void accept(int s, int e, int h, int c) {
ClassEntry catchTypeEntry = c == 0
? null
: (ClassEntry) constantPool().entryByIndex(c);
: constantPool().entryByIndex(c, ClassEntry.class);
exceptionTable.add(new AbstractPseudoInstruction.ExceptionCatchImpl(getLabel(h), getLabel(s), getLabel(e), catchTypeEntry));
}
});
@ -337,7 +337,7 @@ public final class CodeImpl
public void accept(int s, int e, int h, int c) {
ClassEntry catchType = c == 0
? null
: (ClassEntry) classReader.entryByIndex(c);
: classReader.entryByIndex(c, ClassEntry.class);
consumer.accept(new AbstractPseudoInstruction.ExceptionCatchImpl(getLabel(h), getLabel(s), getLabel(e), catchType));
}
});

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* 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
@ -28,7 +28,6 @@ import java.lang.constant.ConstantDesc;
import java.lang.constant.MethodTypeDesc;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.lang.classfile.Attribute;
import java.lang.classfile.Attributes;
@ -38,6 +37,7 @@ import java.lang.classfile.BootstrapMethodEntry;
import java.lang.classfile.BufWriter;
import java.lang.classfile.attribute.BootstrapMethodsAttribute;
import java.lang.classfile.constantpool.*;
import java.util.Objects;
import static java.lang.classfile.ClassFile.TAG_CLASS;
import static java.lang.classfile.ClassFile.TAG_CONSTANTDYNAMIC;
@ -114,6 +114,12 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
return pe;
}
@Override
public <T extends PoolEntry> T entryByIndex(int index, Class<T> cls) {
Objects.requireNonNull(cls);
return ClassReaderImpl.checkType(entryByIndex(index), index, cls);
}
@Override
public BootstrapMethodEntryImpl bootstrapMethodEntry(int index) {
if (index < 0 || index >= bootstrapMethodCount()) {

@ -296,7 +296,7 @@ public final class StackCounter {
next();
}
case GETSTATIC, PUTSTATIC, GETFIELD, PUTFIELD -> {
var tk = TypeKind.fromDescriptor(((MemberRefEntry)cp.entryByIndex(bcs.getIndexU2())).nameAndType().type().stringValue());
var tk = TypeKind.fromDescriptor(cp.entryByIndex(bcs.getIndexU2(), MemberRefEntry.class).nameAndType().type());
switch (bcs.rawCode) {
case GETSTATIC ->
addStackSlot(tk.slotSize());
@ -368,7 +368,7 @@ public final class StackCounter {
case TAG_DOUBLE, TAG_LONG ->
addStackSlot(+2);
case TAG_CONSTANTDYNAMIC ->
addStackSlot(((ConstantDynamicEntry)cp.entryByIndex(index)).typeKind().slotSize());
addStackSlot(cp.entryByIndex(index, ConstantDynamicEntry.class).typeKind().slotSize());
default ->
throw error("CP entry #%d %s is not loadable constant".formatted(index, cp.entryByIndex(index).tag()));
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* 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
@ -231,7 +231,7 @@ public class StackMapDecoder {
case VT_LONG -> SimpleVerificationTypeInfo.ITEM_LONG;
case VT_NULL -> SimpleVerificationTypeInfo.ITEM_NULL;
case VT_UNINITIALIZED_THIS -> SimpleVerificationTypeInfo.ITEM_UNINITIALIZED_THIS;
case VT_OBJECT -> new ObjectVerificationTypeInfoImpl((ClassEntry)classReader.entryByIndex(u2()));
case VT_OBJECT -> new ObjectVerificationTypeInfoImpl(classReader.entryByIndex(u2(), ClassEntry.class));
case VT_UNINITIALIZED -> new UninitializedVerificationTypeInfoImpl(ctx.getLabel(u2()));
default -> throw new IllegalArgumentException("Invalid verification type tag: " + tag);
};

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* 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
@ -25,6 +25,8 @@
*/
package jdk.internal.classfile.impl;
import java.lang.classfile.constantpool.InvokeDynamicEntry;
import java.lang.classfile.constantpool.NameAndTypeEntry;
import java.lang.constant.ClassDesc;
import static java.lang.constant.ConstantDescs.*;
import java.lang.constant.MethodTypeDesc;
@ -397,7 +399,7 @@ public final class StackMapGenerator {
}
private static Type cpIndexToType(int index, ConstantPoolBuilder cp) {
return Type.referenceType(((ClassEntry)cp.entryByIndex(index)).asSymbol());
return Type.referenceType(cp.entryByIndex(index, ClassEntry.class).asSymbol());
}
private void processMethod() {
@ -700,7 +702,7 @@ public final class StackMapGenerator {
case TAG_METHODTYPE ->
currentFrame.pushStack(Type.METHOD_TYPE);
case TAG_CONSTANTDYNAMIC ->
currentFrame.pushStack(((ConstantDynamicEntry)cp.entryByIndex(index)).asSymbol().constantType());
currentFrame.pushStack(cp.entryByIndex(index, ConstantDynamicEntry.class).asSymbol().constantType());
default ->
throw generatorError("CP entry #%d %s is not loadable constant".formatted(index, cp.entryByIndex(index).tag()));
}
@ -747,7 +749,7 @@ public final class StackMapGenerator {
}
private void processFieldInstructions(RawBytecodeHelper bcs) {
var desc = Util.fieldTypeSymbol(((MemberRefEntry)cp.entryByIndex(bcs.getIndexU2())).nameAndType());
var desc = Util.fieldTypeSymbol(cp.entryByIndex(bcs.getIndexU2(), MemberRefEntry.class).nameAndType());
switch (bcs.rawCode) {
case GETSTATIC ->
currentFrame.pushStack(desc);
@ -771,8 +773,9 @@ public final class StackMapGenerator {
private boolean processInvokeInstructions(RawBytecodeHelper bcs, boolean inTryBlock, boolean thisUninit) {
int index = bcs.getIndexU2();
int opcode = bcs.rawCode;
var cpe = cp.entryByIndex(index);
var nameAndType = opcode == INVOKEDYNAMIC ? ((DynamicConstantPoolEntry)cpe).nameAndType() : ((MemberRefEntry)cpe).nameAndType();
var nameAndType = opcode == INVOKEDYNAMIC
? cp.entryByIndex(index, InvokeDynamicEntry.class).nameAndType()
: cp.entryByIndex(index, MemberRefEntry.class).nameAndType();
String invokeMethodName = nameAndType.name().stringValue();
var mDesc = Util.methodTypeSymbol(nameAndType);
int bci = bcs.bci;

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* 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
@ -170,6 +170,11 @@ public final class TemporaryConstantPool implements ConstantPoolBuilder {
throw new UnsupportedOperationException();
}
@Override
public <T extends PoolEntry> T entryByIndex(int index, Class<T> cls) {
throw new UnsupportedOperationException();
}
@Override
public BootstrapMethodEntry bootstrapMethodEntry(int index) {
throw new UnsupportedOperationException();

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* 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
@ -166,11 +166,11 @@ public final class VerificationWrapper {
}
String classNameAt(int index) {
return ((ClassEntry)cp.entryByIndex(index)).asInternalName();
return cp.entryByIndex(index, ClassEntry.class).asInternalName();
}
String dynamicConstantSignatureAt(int index) {
return ((DynamicConstantPoolEntry)cp.entryByIndex(index)).type().stringValue();
return cp.entryByIndex(index, DynamicConstantPoolEntry.class).type().stringValue();
}
int tagAt(int index) {
@ -192,7 +192,7 @@ public final class VerificationWrapper {
}
int refClassIndexAt(int index) {
return ((MemberRefEntry)cp.entryByIndex(index)).owner().index();
return cp.entryByIndex(index, MemberRefEntry.class).owner().index();
}
}
}

@ -23,14 +23,23 @@
/*
* @test
* @bug 8331291
* @summary Testing Attributes API.
* @bug 8331291 8332614
* @summary Testing Attributes API and ClassReader.
* @run junit AttributesTest
*/
import java.lang.classfile.AttributeMapper;
import java.lang.classfile.AttributedElement;
import java.lang.classfile.Attributes;
import java.lang.classfile.BufWriter;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassReader;
import java.lang.classfile.CustomAttribute;
import java.lang.classfile.constantpool.ConstantPoolException;
import java.lang.classfile.constantpool.InvokeDynamicEntry;
import java.lang.classfile.constantpool.Utf8Entry;
import java.lang.constant.ClassDesc;
import java.lang.reflect.Field;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@ -52,4 +61,75 @@ class AttributesTest {
}
}
}
private static final String TEST_ATTRIBUTE_NAME = "org.openjdk.classfile.test";
private static final AttributeMapper<TestAttribute> TEST_MAPPER = new AttributeMapper<>() {
@Override
public String name() {
return TEST_ATTRIBUTE_NAME;
}
@Override
public TestAttribute readAttribute(AttributedElement enclosing, ClassReader cf, int pos) {
int cpPos = pos - 6; // Attribute Name Utf8
// Test valid pos/index - NPE
assertThrows(NullPointerException.class, () -> cf.readEntry(cpPos, null));
assertThrows(NullPointerException.class, () -> cf.readEntryOrNull(cpPos, null));
assertThrows(NullPointerException.class, () -> cf.entryByIndex(1, null));
// Test valid pos/index - incorrect type
assertThrows(ConstantPoolException.class, () -> cf.readEntry(cpPos, InvokeDynamicEntry.class));
assertThrows(ConstantPoolException.class, () -> cf.readEntryOrNull(cpPos, InvokeDynamicEntry.class));
assertThrows(ConstantPoolException.class, () -> cf.entryByIndex(1, InvokeDynamicEntry.class));
// Passing tests
var utf8 = cf.readEntry(cpPos, Utf8Entry.class);
assertSame(utf8, cf.readEntryOrNull(cpPos, Utf8Entry.class));
// Test invalid pos/index - NPE thrown before CPE
assertThrows(NullPointerException.class, () -> cf.readEntry(-1, null));
assertThrows(NullPointerException.class, () -> cf.readEntryOrNull(-1, null));
assertThrows(NullPointerException.class, () -> cf.entryByIndex(-1, null));
return new TestAttribute(true);
}
@Override
public void writeAttribute(BufWriter buf, TestAttribute attr) {
buf.writeIndex(buf.constantPool().utf8Entry(name()));
buf.writeInt(0);
}
@Override
public AttributeStability stability() {
return AttributeStability.STATELESS;
}
};
private static final class TestAttribute extends CustomAttribute<TestAttribute> {
final boolean fromMapper;
TestAttribute(boolean fromMapper) {
super(TEST_MAPPER);
this.fromMapper = fromMapper;
}
}
@Test
void testClassReader() throws Exception {
var cf = ClassFile.of(ClassFile.AttributeMapperOption.of(utf8 -> {
if (utf8.equalsString(TEST_ATTRIBUTE_NAME)) {
return TEST_MAPPER;
}
return null;
}));
var cd = ClassDesc.of("Testing");
var bytes = cf.build(cd, clb -> clb
.with(new TestAttribute(false)));
assertTrue(cf.parse(bytes)
.findAttribute(TEST_MAPPER)
.orElseThrow()
.fromMapper);
}
}