Skip to content

Commit f608918

Browse files
liachasotona
authored andcommittedMay 30, 2024
8332614: Type-checked ConstantPool.entryByIndex and ClassReader.readEntryOrNull
Reviewed-by: asotona
1 parent 1b04f64 commit f608918

15 files changed

+230
-90
lines changed
 

‎src/java.base/share/classes/java/lang/classfile/ClassReader.java

+25-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -76,17 +76,14 @@ public sealed interface ClassReader extends ConstantPool
7676

7777
// Constant pool
7878

79-
/**
80-
* {@return the UTF8 constant pool entry at the given index of the constant
81-
* pool} The given index must correspond to a valid constant pool index
82-
* whose slot holds a UTF8 constant.
83-
* @param index the index into the constant pool
84-
*/
85-
Utf8Entry utf8EntryByIndex(int index);
86-
8779
/**
8880
* {@return the constant pool entry whose index is given at the specified
8981
* offset within the classfile}
82+
*
83+
* @apiNote
84+
* If only a particular type of entry is expected, use {@link #readEntry(
85+
* int, Class) readEntry(int, Class)}.
86+
*
9087
* @param offset the offset of the index within the classfile
9188
* @throws ConstantPoolException if the index is out of range of the
9289
* constant pool size, or zero
@@ -108,12 +105,31 @@ public sealed interface ClassReader extends ConstantPool
108105
* {@return the constant pool entry whose index is given at the specified
109106
* offset within the classfile, or null if the index at the specified
110107
* offset is zero}
108+
*
109+
* @apiNote
110+
* If only a particular type of entry is expected, use {@link #readEntryOrNull(
111+
* int, Class) readEntryOrNull(int, Class)}.
112+
*
111113
* @param offset the offset of the index within the classfile
112114
* @throws ConstantPoolException if the index is out of range of the
113115
* constant pool size
114116
*/
115117
PoolEntry readEntryOrNull(int offset);
116118

119+
/**
120+
* {@return the constant pool entry of a given type whose index is given
121+
* at the specified offset within the classfile, or null if the index at
122+
* the specified offset is zero}
123+
*
124+
* @param <T> the entry type
125+
* @param offset the offset of the index within the classfile
126+
* @param cls the entry type
127+
* @throws ConstantPoolException if the index is out of range of the
128+
* constant pool size, or zero, or the entry is not of the given type
129+
* @since 24
130+
*/
131+
<T extends PoolEntry> T readEntryOrNull(int offset, Class<T> cls);
132+
117133
/**
118134
* {@return the UTF8 entry whose index is given at the specified
119135
* offset within the classfile}

‎src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPool.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -46,6 +46,10 @@ public sealed interface ConstantPool extends Iterable<PoolEntry>
4646
/**
4747
* {@return the entry at the specified index}
4848
*
49+
* @apiNote
50+
* If only a particular type of entry is expected, use {@link #entryByIndex(
51+
* int, Class) entryByIndex(int, Class)}.
52+
*
4953
* @param index the index within the pool of the desired entry
5054
* @throws ConstantPoolException if the index is out of range of the
5155
* constant pool, or is considered unusable
@@ -57,6 +61,18 @@ public sealed interface ConstantPool extends Iterable<PoolEntry>
5761
*/
5862
int size();
5963

64+
/**
65+
* {@return the entry of a given type at the specified index}
66+
*
67+
* @param <T> the entry type
68+
* @param index the index within the pool of the desired entry
69+
* @param cls the entry type
70+
* @throws ConstantPoolException if the index is out of range of the
71+
* constant pool, or the entry is not of the given type
72+
* @since 24
73+
*/
74+
<T extends PoolEntry> T entryByIndex(int index, Class<T> cls);
75+
6076
/**
6177
* {@return an iterator over pool entries}
6278
*/

‎src/java.base/share/classes/jdk/internal/classfile/impl/AbstractBoundLocalVariable.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -46,7 +46,7 @@ protected int nameIndex() {
4646

4747
public Utf8Entry name() {
4848
if (nameEntry == null)
49-
nameEntry = (Utf8Entry) code.constantPool().entryByIndex(nameIndex());
49+
nameEntry = code.constantPool().entryByIndex(nameIndex(), Utf8Entry.class);
5050
return nameEntry;
5151
}
5252

@@ -56,7 +56,7 @@ protected int secondaryIndex() {
5656

5757
protected Utf8Entry secondaryEntry() {
5858
if (secondaryEntry == null)
59-
secondaryEntry = (Utf8Entry) code.constantPool().entryByIndex(secondaryIndex());
59+
secondaryEntry = code.constantPool().entryByIndex(secondaryIndex(), Utf8Entry.class);
6060
return secondaryEntry;
6161
}
6262

‎src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -687,10 +687,10 @@ public BoundLoadConstantInstruction(Opcode op, CodeImpl code, int pos) {
687687

688688
@Override
689689
public LoadableConstantEntry constantEntry() {
690-
return (LoadableConstantEntry)
691-
code.classReader.entryByIndex(op == Opcode.LDC
690+
return code.classReader.entryByIndex(op == Opcode.LDC
692691
? code.classReader.readU1(pos + 1)
693-
: code.classReader.readU2(pos + 1));
692+
: code.classReader.readU2(pos + 1),
693+
LoadableConstantEntry.class);
694694
}
695695

696696
@Override

‎src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -127,7 +127,7 @@ private static int skipElementValue(ClassReader classReader, int p) {
127127
}
128128

129129
private static Annotation readAnnotation(ClassReader classReader, int p) {
130-
Utf8Entry annotationClass = classReader.utf8EntryByIndex(classReader.readU2(p));
130+
Utf8Entry annotationClass = classReader.entryByIndex(classReader.readU2(p), Utf8Entry.class);
131131
p += 2;
132132
List<AnnotationElement> elems = readAnnotationElementValuePairs(classReader, p);
133133
return new AnnotationImpl(annotationClass, elems);

‎src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java

+6-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -632,7 +632,7 @@ private void structure() {
632632
for (int i = 0; p < end; p += 6, i++) {
633633
elements[i] = ModuleRequireInfo.of(classReader.readModuleEntry(p),
634634
classReader.readU2(p + 2),
635-
(Utf8Entry) classReader.readEntryOrNull(p + 4));
635+
classReader.readEntryOrNull(p + 4, Utf8Entry.class));
636636
}
637637
requires = List.of(elements);
638638
}
@@ -771,15 +771,9 @@ public List<InnerClassInfo> classes() {
771771
int p = payloadStart + 2;
772772
InnerClassInfo[] elements = new InnerClassInfo[cnt];
773773
for (int i = 0; i < cnt; i++) {
774-
ClassEntry innerClass = classReader.readClassEntry(p); // TODO FIXME
775-
int outerClassIndex = classReader.readU2(p + 2);
776-
ClassEntry outerClass = outerClassIndex == 0
777-
? null
778-
: (ClassEntry) classReader.entryByIndex(outerClassIndex);
779-
int innerNameIndex = classReader.readU2(p + 4);
780-
Utf8Entry innerName = innerNameIndex == 0
781-
? null
782-
: (Utf8Entry) classReader.entryByIndex(innerNameIndex);
774+
ClassEntry innerClass = classReader.readClassEntry(p);
775+
var outerClass = classReader.readEntryOrNull(p + 2, ClassEntry.class);
776+
var innerName = classReader.readEntryOrNull(p + 4, Utf8Entry.class);
783777
int flags = classReader.readU2(p + 6);
784778
p += 8;
785779
elements[i] = InnerClassInfo.of(innerClass, Optional.ofNullable(outerClass), Optional.ofNullable(innerName), flags);
@@ -803,7 +797,7 @@ public ClassEntry enclosingClass() {
803797

804798
@Override
805799
public Optional<NameAndTypeEntry> enclosingMethod() {
806-
return Optional.ofNullable((NameAndTypeEntry) classReader.readEntryOrNull(payloadStart + 2));
800+
return Optional.ofNullable(classReader.readEntryOrNull(payloadStart + 2, NameAndTypeEntry.class));
807801
}
808802
}
809803

‎src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java

+59-39
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.ArrayList;
2929
import java.util.Arrays;
3030
import java.util.List;
31+
import java.util.Objects;
3132
import java.util.Optional;
3233
import java.util.function.Function;
3334

@@ -158,8 +159,7 @@ public ClassEntry thisClassEntry() {
158159
@Override
159160
public Optional<ClassEntry> superclassEntry() {
160161
if (superclass == null) {
161-
int scIndex = readU2(thisClassPos + 2);
162-
superclass = Optional.ofNullable(scIndex == 0 ? null : (ClassEntry) entryByIndex(scIndex));
162+
superclass = Optional.ofNullable(readEntryOrNull(thisClassPos + 2, ClassEntry.class));
163163
}
164164
return superclass;
165165
}
@@ -338,10 +338,42 @@ void writeConstantPoolEntries(BufWriter buf) {
338338
// Constantpool
339339
@Override
340340
public PoolEntry entryByIndex(int index) {
341-
return entryByIndex(index, 0, 0xff);
341+
return entryByIndex(index, PoolEntry.class);
342+
}
343+
344+
private static boolean checkTag(int tag, Class<?> cls) {
345+
var type = switch (tag) {
346+
// JVMS Table 4.4-B. Constant pool tags
347+
case TAG_UTF8 -> AbstractPoolEntry.Utf8EntryImpl.class;
348+
case TAG_INTEGER -> AbstractPoolEntry.IntegerEntryImpl.class;
349+
case TAG_FLOAT -> AbstractPoolEntry.FloatEntryImpl.class;
350+
case TAG_LONG -> AbstractPoolEntry.LongEntryImpl.class;
351+
case TAG_DOUBLE -> AbstractPoolEntry.DoubleEntryImpl.class;
352+
case TAG_CLASS -> AbstractPoolEntry.ClassEntryImpl.class;
353+
case TAG_STRING -> AbstractPoolEntry.StringEntryImpl.class;
354+
case TAG_FIELDREF -> AbstractPoolEntry.FieldRefEntryImpl.class;
355+
case TAG_METHODREF -> AbstractPoolEntry.MethodRefEntryImpl.class;
356+
case TAG_INTERFACEMETHODREF -> AbstractPoolEntry.InterfaceMethodRefEntryImpl.class;
357+
case TAG_NAMEANDTYPE -> AbstractPoolEntry.NameAndTypeEntryImpl.class;
358+
case TAG_METHODHANDLE -> AbstractPoolEntry.MethodHandleEntryImpl.class;
359+
case TAG_METHODTYPE -> AbstractPoolEntry.MethodTypeEntryImpl.class;
360+
case TAG_CONSTANTDYNAMIC -> AbstractPoolEntry.ConstantDynamicEntryImpl.class;
361+
case TAG_INVOKEDYNAMIC -> AbstractPoolEntry.InvokeDynamicEntryImpl.class;
362+
case TAG_MODULE -> AbstractPoolEntry.ModuleEntryImpl.class;
363+
case TAG_PACKAGE -> AbstractPoolEntry.PackageEntryImpl.class;
364+
default -> null;
365+
};
366+
return type != null && cls.isAssignableFrom(type);
367+
}
368+
369+
static <T extends PoolEntry> T checkType(PoolEntry e, int index, Class<T> cls) {
370+
if (cls.isInstance(e)) return cls.cast(e);
371+
throw new ConstantPoolException("Not a " + cls.getSimpleName() + " at index: " + index);
342372
}
343373

344-
private PoolEntry entryByIndex(int index, int lowerBoundTag, int upperBoundTag) {
374+
@Override
375+
public <T extends PoolEntry> T entryByIndex(int index, Class<T> cls) {
376+
Objects.requireNonNull(cls);
345377
if (index <= 0 || index >= constantPoolCount) {
346378
throw new ConstantPoolException("Bad CP index: " + index);
347379
}
@@ -352,9 +384,9 @@ private PoolEntry entryByIndex(int index, int lowerBoundTag, int upperBoundTag)
352384
throw new ConstantPoolException("Unusable CP index: " + index);
353385
}
354386
int tag = readU1(offset);
355-
if (tag < lowerBoundTag || tag > upperBoundTag) {
387+
if (!checkTag(tag, cls)) {
356388
throw new ConstantPoolException(
357-
"Bad tag (" + tag + ") at index (" + index + ") position (" + offset + ")");
389+
"Bad tag (" + tag + ") at index (" + index + ") position (" + offset + "), expected " + cls.getSimpleName());
358390
}
359391
final int q = offset + 1;
360392
info = switch (tag) {
@@ -374,7 +406,7 @@ private PoolEntry entryByIndex(int index, int lowerBoundTag, int upperBoundTag)
374406
case TAG_NAMEANDTYPE -> new AbstractPoolEntry.NameAndTypeEntryImpl(this, index, (AbstractPoolEntry.Utf8EntryImpl) readUtf8Entry(q),
375407
(AbstractPoolEntry.Utf8EntryImpl) readUtf8Entry(q + 2));
376408
case TAG_METHODHANDLE -> new AbstractPoolEntry.MethodHandleEntryImpl(this, index, readU1(q),
377-
readEntry(q + 1, AbstractPoolEntry.AbstractMemberRefEntry.class, TAG_FIELDREF, TAG_INTERFACEMETHODREF));
409+
readEntry(q + 1, AbstractPoolEntry.AbstractMemberRefEntry.class));
378410
case TAG_METHODTYPE -> new AbstractPoolEntry.MethodTypeEntryImpl(this, index, (AbstractPoolEntry.Utf8EntryImpl) readUtf8Entry(q));
379411
case TAG_CONSTANTDYNAMIC -> new AbstractPoolEntry.ConstantDynamicEntryImpl(this, index, readU2(q), (AbstractPoolEntry.NameAndTypeEntryImpl) readNameAndTypeEntry(q + 2));
380412
case TAG_INVOKEDYNAMIC -> new AbstractPoolEntry.InvokeDynamicEntryImpl(this, index, readU2(q), (AbstractPoolEntry.NameAndTypeEntryImpl) readNameAndTypeEntry(q + 2));
@@ -385,15 +417,7 @@ private PoolEntry entryByIndex(int index, int lowerBoundTag, int upperBoundTag)
385417
};
386418
cp[index] = info;
387419
}
388-
return info;
389-
}
390-
391-
@Override
392-
public AbstractPoolEntry.Utf8EntryImpl utf8EntryByIndex(int index) {
393-
if (entryByIndex(index, TAG_UTF8, TAG_UTF8) instanceof AbstractPoolEntry.Utf8EntryImpl utf8) {
394-
return utf8;
395-
}
396-
throw new ConstantPoolException("Not a UTF8 - index: " + index);
420+
return checkType(info, index, cls);
397421
}
398422

399423
public int skipAttributeHolder(int offset) {
@@ -418,17 +442,8 @@ public PoolEntry readEntry(int pos) {
418442

419443
@Override
420444
public <T extends PoolEntry> T readEntry(int pos, Class<T> cls) {
421-
return readEntry(pos, cls, 0, 0xff);
422-
}
423-
424-
private <T extends PoolEntry> T readEntry(int pos, Class<T> cls, int expectedTag) {
425-
return readEntry(pos, cls, expectedTag, expectedTag);
426-
}
427-
428-
private <T extends PoolEntry> T readEntry(int pos, Class<T> cls, int lowerBoundTag, int upperBoundTag) {
429-
var e = entryByIndex(readU2(pos), lowerBoundTag, upperBoundTag);
430-
if (cls.isInstance(e)) return cls.cast(e);
431-
throw new ConstantPoolException("Not a " + cls.getSimpleName() + " at index: " + readU2(pos));
445+
Objects.requireNonNull(cls);
446+
return entryByIndex(readU2(pos), cls);
432447
}
433448

434449
@Override
@@ -440,44 +455,49 @@ public PoolEntry readEntryOrNull(int pos) {
440455
return entryByIndex(index);
441456
}
442457

458+
@Override
459+
public <T extends PoolEntry> T readEntryOrNull(int offset, Class<T> cls) {
460+
Objects.requireNonNull(cls);
461+
int index = readU2(offset);
462+
if (index == 0) {
463+
return null;
464+
}
465+
return entryByIndex(index, cls);
466+
}
467+
443468
@Override
444469
public Utf8Entry readUtf8Entry(int pos) {
445-
int index = readU2(pos);
446-
return utf8EntryByIndex(index);
470+
return readEntry(pos, Utf8Entry.class);
447471
}
448472

449473
@Override
450474
public Utf8Entry readUtf8EntryOrNull(int pos) {
451-
int index = readU2(pos);
452-
if (index == 0) {
453-
return null;
454-
}
455-
return utf8EntryByIndex(index);
475+
return readEntryOrNull(pos, Utf8Entry.class);
456476
}
457477

458478
@Override
459479
public ModuleEntry readModuleEntry(int pos) {
460-
return readEntry(pos, ModuleEntry.class, TAG_MODULE);
480+
return readEntry(pos, ModuleEntry.class);
461481
}
462482

463483
@Override
464484
public PackageEntry readPackageEntry(int pos) {
465-
return readEntry(pos, PackageEntry.class, TAG_PACKAGE);
485+
return readEntry(pos, PackageEntry.class);
466486
}
467487

468488
@Override
469489
public ClassEntry readClassEntry(int pos) {
470-
return readEntry(pos, ClassEntry.class, TAG_CLASS);
490+
return readEntry(pos, ClassEntry.class);
471491
}
472492

473493
@Override
474494
public NameAndTypeEntry readNameAndTypeEntry(int pos) {
475-
return readEntry(pos, NameAndTypeEntry.class, TAG_NAMEANDTYPE);
495+
return readEntry(pos, NameAndTypeEntry.class);
476496
}
477497

478498
@Override
479499
public MethodHandleEntry readMethodHandleEntry(int pos) {
480-
return readEntry(pos, MethodHandleEntry.class, TAG_METHODHANDLE);
500+
return readEntry(pos, MethodHandleEntry.class);
481501
}
482502

483503
@Override

‎src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ public List<ExceptionCatch> exceptionHandlers() {
199199
public void accept(int s, int e, int h, int c) {
200200
ClassEntry catchTypeEntry = c == 0
201201
? null
202-
: (ClassEntry) constantPool().entryByIndex(c);
202+
: constantPool().entryByIndex(c, ClassEntry.class);
203203
exceptionTable.add(new AbstractPseudoInstruction.ExceptionCatchImpl(getLabel(h), getLabel(s), getLabel(e), catchTypeEntry));
204204
}
205205
});
@@ -337,7 +337,7 @@ private void generateCatchTargets(Consumer<CodeElement> consumer) {
337337
public void accept(int s, int e, int h, int c) {
338338
ClassEntry catchType = c == 0
339339
? null
340-
: (ClassEntry) classReader.entryByIndex(c);
340+
: classReader.entryByIndex(c, ClassEntry.class);
341341
consumer.accept(new AbstractPseudoInstruction.ExceptionCatchImpl(getLabel(h), getLabel(s), getLabel(e), catchType));
342342
}
343343
});

‎src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -28,7 +28,6 @@
2828
import java.lang.constant.MethodTypeDesc;
2929
import java.util.Arrays;
3030
import java.util.List;
31-
import java.util.Objects;
3231

3332
import java.lang.classfile.Attribute;
3433
import java.lang.classfile.Attributes;
@@ -38,6 +37,7 @@
3837
import java.lang.classfile.BufWriter;
3938
import java.lang.classfile.attribute.BootstrapMethodsAttribute;
4039
import java.lang.classfile.constantpool.*;
40+
import java.util.Objects;
4141

4242
import static java.lang.classfile.ClassFile.TAG_CLASS;
4343
import static java.lang.classfile.ClassFile.TAG_CONSTANTDYNAMIC;
@@ -114,6 +114,12 @@ public PoolEntry entryByIndex(int index) {
114114
return pe;
115115
}
116116

117+
@Override
118+
public <T extends PoolEntry> T entryByIndex(int index, Class<T> cls) {
119+
Objects.requireNonNull(cls);
120+
return ClassReaderImpl.checkType(entryByIndex(index), index, cls);
121+
}
122+
117123
@Override
118124
public BootstrapMethodEntryImpl bootstrapMethodEntry(int index) {
119125
if (index < 0 || index >= bootstrapMethodCount()) {

‎src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ public StackCounter(LabelContext labelContext,
296296
next();
297297
}
298298
case GETSTATIC, PUTSTATIC, GETFIELD, PUTFIELD -> {
299-
var tk = TypeKind.fromDescriptor(((MemberRefEntry)cp.entryByIndex(bcs.getIndexU2())).nameAndType().type().stringValue());
299+
var tk = TypeKind.fromDescriptor(cp.entryByIndex(bcs.getIndexU2(), MemberRefEntry.class).nameAndType().type());
300300
switch (bcs.rawCode) {
301301
case GETSTATIC ->
302302
addStackSlot(tk.slotSize());
@@ -368,7 +368,7 @@ private void processLdc(int index) {
368368
case TAG_DOUBLE, TAG_LONG ->
369369
addStackSlot(+2);
370370
case TAG_CONSTANTDYNAMIC ->
371-
addStackSlot(((ConstantDynamicEntry)cp.entryByIndex(index)).typeKind().slotSize());
371+
addStackSlot(cp.entryByIndex(index, ConstantDynamicEntry.class).typeKind().slotSize());
372372
default ->
373373
throw error("CP entry #%d %s is not loadable constant".formatted(index, cp.entryByIndex(index).tag()));
374374
}

‎src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -231,7 +231,7 @@ private VerificationTypeInfo readVerificationTypeInfo() {
231231
case VT_LONG -> SimpleVerificationTypeInfo.ITEM_LONG;
232232
case VT_NULL -> SimpleVerificationTypeInfo.ITEM_NULL;
233233
case VT_UNINITIALIZED_THIS -> SimpleVerificationTypeInfo.ITEM_UNINITIALIZED_THIS;
234-
case VT_OBJECT -> new ObjectVerificationTypeInfoImpl((ClassEntry)classReader.entryByIndex(u2()));
234+
case VT_OBJECT -> new ObjectVerificationTypeInfoImpl(classReader.entryByIndex(u2(), ClassEntry.class));
235235
case VT_UNINITIALIZED -> new UninitializedVerificationTypeInfoImpl(ctx.getLabel(u2()));
236236
default -> throw new IllegalArgumentException("Invalid verification type tag: " + tag);
237237
};

‎src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,8 @@
2525
*/
2626
package jdk.internal.classfile.impl;
2727

28+
import java.lang.classfile.constantpool.InvokeDynamicEntry;
29+
import java.lang.classfile.constantpool.NameAndTypeEntry;
2830
import java.lang.constant.ClassDesc;
2931
import static java.lang.constant.ConstantDescs.*;
3032
import java.lang.constant.MethodTypeDesc;
@@ -397,7 +399,7 @@ public void writeBody(BufWriter b) {
397399
}
398400

399401
private static Type cpIndexToType(int index, ConstantPoolBuilder cp) {
400-
return Type.referenceType(((ClassEntry)cp.entryByIndex(index)).asSymbol());
402+
return Type.referenceType(cp.entryByIndex(index, ClassEntry.class).asSymbol());
401403
}
402404

403405
private void processMethod() {
@@ -700,7 +702,7 @@ private void processLdc(int index) {
700702
case TAG_METHODTYPE ->
701703
currentFrame.pushStack(Type.METHOD_TYPE);
702704
case TAG_CONSTANTDYNAMIC ->
703-
currentFrame.pushStack(((ConstantDynamicEntry)cp.entryByIndex(index)).asSymbol().constantType());
705+
currentFrame.pushStack(cp.entryByIndex(index, ConstantDynamicEntry.class).asSymbol().constantType());
704706
default ->
705707
throw generatorError("CP entry #%d %s is not loadable constant".formatted(index, cp.entryByIndex(index).tag()));
706708
}
@@ -747,7 +749,7 @@ private void processSwitch(RawBytecodeHelper bcs) {
747749
}
748750

749751
private void processFieldInstructions(RawBytecodeHelper bcs) {
750-
var desc = Util.fieldTypeSymbol(((MemberRefEntry)cp.entryByIndex(bcs.getIndexU2())).nameAndType());
752+
var desc = Util.fieldTypeSymbol(cp.entryByIndex(bcs.getIndexU2(), MemberRefEntry.class).nameAndType());
751753
switch (bcs.rawCode) {
752754
case GETSTATIC ->
753755
currentFrame.pushStack(desc);
@@ -771,8 +773,9 @@ private void processFieldInstructions(RawBytecodeHelper bcs) {
771773
private boolean processInvokeInstructions(RawBytecodeHelper bcs, boolean inTryBlock, boolean thisUninit) {
772774
int index = bcs.getIndexU2();
773775
int opcode = bcs.rawCode;
774-
var cpe = cp.entryByIndex(index);
775-
var nameAndType = opcode == INVOKEDYNAMIC ? ((DynamicConstantPoolEntry)cpe).nameAndType() : ((MemberRefEntry)cpe).nameAndType();
776+
var nameAndType = opcode == INVOKEDYNAMIC
777+
? cp.entryByIndex(index, InvokeDynamicEntry.class).nameAndType()
778+
: cp.entryByIndex(index, MemberRefEntry.class).nameAndType();
776779
String invokeMethodName = nameAndType.name().stringValue();
777780
var mDesc = Util.methodTypeSymbol(nameAndType);
778781
int bci = bcs.bci;

‎src/java.base/share/classes/jdk/internal/classfile/impl/TemporaryConstantPool.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -170,6 +170,11 @@ public int size() {
170170
throw new UnsupportedOperationException();
171171
}
172172

173+
@Override
174+
public <T extends PoolEntry> T entryByIndex(int index, Class<T> cls) {
175+
throw new UnsupportedOperationException();
176+
}
177+
173178
@Override
174179
public BootstrapMethodEntry bootstrapMethodEntry(int index) {
175180
throw new UnsupportedOperationException();

‎src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationWrapper.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -166,11 +166,11 @@ int entryCount() {
166166
}
167167

168168
String classNameAt(int index) {
169-
return ((ClassEntry)cp.entryByIndex(index)).asInternalName();
169+
return cp.entryByIndex(index, ClassEntry.class).asInternalName();
170170
}
171171

172172
String dynamicConstantSignatureAt(int index) {
173-
return ((DynamicConstantPoolEntry)cp.entryByIndex(index)).type().stringValue();
173+
return cp.entryByIndex(index, DynamicConstantPoolEntry.class).type().stringValue();
174174
}
175175

176176
int tagAt(int index) {
@@ -192,7 +192,7 @@ String refSignatureAt(int index) {
192192
}
193193

194194
int refClassIndexAt(int index) {
195-
return ((MemberRefEntry)cp.entryByIndex(index)).owner().index();
195+
return cp.entryByIndex(index, MemberRefEntry.class).owner().index();
196196
}
197197
}
198198
}

‎test/jdk/jdk/classfile/AttributesTest.java

+82-2
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,23 @@
2323

2424
/*
2525
* @test
26-
* @bug 8331291
27-
* @summary Testing Attributes API.
26+
* @bug 8331291 8332614
27+
* @summary Testing Attributes API and ClassReader.
2828
* @run junit AttributesTest
2929
*/
3030
import java.lang.classfile.AttributeMapper;
31+
import java.lang.classfile.AttributedElement;
3132
import java.lang.classfile.Attributes;
33+
import java.lang.classfile.BufWriter;
34+
import java.lang.classfile.ClassFile;
35+
import java.lang.classfile.ClassReader;
36+
import java.lang.classfile.CustomAttribute;
37+
import java.lang.classfile.constantpool.ConstantPoolException;
38+
import java.lang.classfile.constantpool.InvokeDynamicEntry;
3239
import java.lang.classfile.constantpool.Utf8Entry;
40+
import java.lang.constant.ClassDesc;
3341
import java.lang.reflect.Field;
42+
3443
import org.junit.jupiter.api.Test;
3544

3645
import static org.junit.jupiter.api.Assertions.*;
@@ -52,4 +61,75 @@ void testAttributesMapping() throws Exception {
5261
}
5362
}
5463
}
64+
65+
private static final String TEST_ATTRIBUTE_NAME = "org.openjdk.classfile.test";
66+
private static final AttributeMapper<TestAttribute> TEST_MAPPER = new AttributeMapper<>() {
67+
@Override
68+
public String name() {
69+
return TEST_ATTRIBUTE_NAME;
70+
}
71+
72+
@Override
73+
public TestAttribute readAttribute(AttributedElement enclosing, ClassReader cf, int pos) {
74+
int cpPos = pos - 6; // Attribute Name Utf8
75+
// Test valid pos/index - NPE
76+
assertThrows(NullPointerException.class, () -> cf.readEntry(cpPos, null));
77+
assertThrows(NullPointerException.class, () -> cf.readEntryOrNull(cpPos, null));
78+
assertThrows(NullPointerException.class, () -> cf.entryByIndex(1, null));
79+
80+
// Test valid pos/index - incorrect type
81+
assertThrows(ConstantPoolException.class, () -> cf.readEntry(cpPos, InvokeDynamicEntry.class));
82+
assertThrows(ConstantPoolException.class, () -> cf.readEntryOrNull(cpPos, InvokeDynamicEntry.class));
83+
assertThrows(ConstantPoolException.class, () -> cf.entryByIndex(1, InvokeDynamicEntry.class));
84+
85+
// Passing tests
86+
var utf8 = cf.readEntry(cpPos, Utf8Entry.class);
87+
assertSame(utf8, cf.readEntryOrNull(cpPos, Utf8Entry.class));
88+
89+
// Test invalid pos/index - NPE thrown before CPE
90+
assertThrows(NullPointerException.class, () -> cf.readEntry(-1, null));
91+
assertThrows(NullPointerException.class, () -> cf.readEntryOrNull(-1, null));
92+
assertThrows(NullPointerException.class, () -> cf.entryByIndex(-1, null));
93+
94+
return new TestAttribute(true);
95+
}
96+
97+
@Override
98+
public void writeAttribute(BufWriter buf, TestAttribute attr) {
99+
buf.writeIndex(buf.constantPool().utf8Entry(name()));
100+
buf.writeInt(0);
101+
}
102+
103+
@Override
104+
public AttributeStability stability() {
105+
return AttributeStability.STATELESS;
106+
}
107+
};
108+
109+
private static final class TestAttribute extends CustomAttribute<TestAttribute> {
110+
final boolean fromMapper;
111+
112+
TestAttribute(boolean fromMapper) {
113+
super(TEST_MAPPER);
114+
this.fromMapper = fromMapper;
115+
}
116+
}
117+
118+
@Test
119+
void testClassReader() throws Exception {
120+
var cf = ClassFile.of(ClassFile.AttributeMapperOption.of(utf8 -> {
121+
if (utf8.equalsString(TEST_ATTRIBUTE_NAME)) {
122+
return TEST_MAPPER;
123+
}
124+
return null;
125+
}));
126+
127+
var cd = ClassDesc.of("Testing");
128+
var bytes = cf.build(cd, clb -> clb
129+
.with(new TestAttribute(false)));
130+
assertTrue(cf.parse(bytes)
131+
.findAttribute(TEST_MAPPER)
132+
.orElseThrow()
133+
.fromMapper);
134+
}
55135
}

0 commit comments

Comments
 (0)
Please sign in to comment.