Skip to content

Commit 3ccd2f7

Browse files
committedOct 31, 2024
8342458: More consistent constant instruction handling
Reviewed-by: asotona
1 parent 29ae265 commit 3ccd2f7

File tree

3 files changed

+145
-75
lines changed

3 files changed

+145
-75
lines changed
 

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

+8-2
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,8 @@ public void writeTo(DirectCodeBuilder writer) {
673673
if (writer.canWriteDirect(code.constantPool()))
674674
super.writeTo(writer);
675675
else
676-
writer.writeLoadConstant(op, constantEntry());
676+
// We have writer.canWriteDirect(constantEntry().constantPool()) == false
677+
writer.writeAdaptLoadConstant(op, constantEntry());
677678
}
678679

679680
@Override
@@ -1346,7 +1347,12 @@ public ConstantDesc constantValue() {
13461347

13471348
@Override
13481349
public void writeTo(DirectCodeBuilder writer) {
1349-
writer.writeLoadConstant(op, constant);
1350+
var constant = this.constant;
1351+
if (writer.canWriteDirect(constant.constantPool()))
1352+
// Allows writing ldc_w small index constants upon user request
1353+
writer.writeDirectLoadConstant(op, constant);
1354+
else
1355+
writer.writeAdaptLoadConstant(op, constant);
13501356
}
13511357

13521358
@Override

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

+17-10
Original file line numberDiff line numberDiff line change
@@ -670,16 +670,22 @@ public void writeArgumentConstant(Opcode opcode, int value) {
670670
}
671671
}
672672

673-
public void writeLoadConstant(Opcode opcode, LoadableConstantEntry value) {
674-
// Make sure Long and Double have LDC2_W and
675-
// rewrite to _W if index is >= 256
676-
int index = AbstractPoolEntry.maybeClone(constantPool, value).index();
677-
if (value instanceof LongEntry || value instanceof DoubleEntry) {
678-
opcode = Opcode.LDC2_W;
679-
} else if (index >= 256)
680-
opcode = Opcode.LDC_W;
673+
// value may not be writable to this constant pool
674+
public void writeAdaptLoadConstant(Opcode opcode, LoadableConstantEntry value) {
675+
var pe = AbstractPoolEntry.maybeClone(constantPool, value);
676+
int index = pe.index();
677+
if (pe != value && opcode != Opcode.LDC2_W) {
678+
// rewrite ldc/ldc_w if external entry; ldc2_w never needs rewrites
679+
opcode = index <= 0xFF ? Opcode.LDC : Opcode.LDC_W;
680+
}
681681

682-
assert !opcode.isWide();
682+
writeDirectLoadConstant(opcode, pe);
683+
}
684+
685+
// the loadable entry is writable to this constant pool
686+
public void writeDirectLoadConstant(Opcode opcode, LoadableConstantEntry pe) {
687+
assert !opcode.isWide() && canWriteDirect(pe.constantPool());
688+
int index = pe.index();
683689
if (opcode.sizeIfFixed() == 3) {
684690
bytecodesBufWriter.writeU1U2(opcode.bytecode(), index);
685691
} else {
@@ -1654,7 +1660,8 @@ public CodeBuilder lconst_1() {
16541660

16551661
@Override
16561662
public CodeBuilder ldc(LoadableConstantEntry entry) {
1657-
writeLoadConstant(BytecodeHelpers.ldcOpcode(entry), entry);
1663+
var direct = AbstractPoolEntry.maybeClone(constantPool, entry);
1664+
writeDirectLoadConstant(BytecodeHelpers.ldcOpcode(direct), direct);
16581665
return this;
16591666
}
16601667

‎test/jdk/jdk/classfile/LDCTest.java

+120-63
Original file line numberDiff line numberDiff line change
@@ -23,88 +23,145 @@
2323

2424
/*
2525
* @test
26+
* @bug 8342458
27+
* @library /test/lib
2628
* @summary Testing ClassFile LDC instructions.
2729
* @run junit LDCTest
2830
*/
29-
import java.lang.constant.ClassDesc;
3031

31-
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
32-
import static java.lang.classfile.ClassFile.ACC_STATIC;
33-
import static java.lang.constant.ConstantDescs.*;
34-
import java.lang.constant.MethodTypeDesc;
35-
36-
import java.lang.classfile.*;
32+
import java.lang.classfile.Attributes;
33+
import java.lang.classfile.ClassFile;
34+
import java.lang.classfile.Instruction;
35+
import java.lang.classfile.MethodModel;
36+
import java.lang.classfile.attribute.CodeAttribute;
3737
import java.lang.classfile.constantpool.ConstantPoolBuilder;
38+
import java.lang.classfile.constantpool.LongEntry;
3839
import java.lang.classfile.constantpool.StringEntry;
40+
import java.lang.classfile.instruction.ConstantInstruction;
41+
import java.lang.constant.ClassDesc;
42+
import java.lang.constant.DirectMethodHandleDesc;
43+
import java.lang.constant.DynamicConstantDesc;
44+
import java.lang.constant.MethodHandleDesc;
45+
import java.lang.constant.MethodTypeDesc;
3946
import java.lang.reflect.AccessFlag;
40-
import static org.junit.jupiter.api.Assertions.*;
47+
import java.util.List;
48+
49+
import jdk.test.lib.ByteCodeLoader;
4150
import org.junit.jupiter.api.Test;
42-
import static helpers.TestConstants.MTD_VOID;
51+
52+
import static java.lang.classfile.ClassFile.*;
4353
import static java.lang.classfile.Opcode.*;
44-
import java.lang.classfile.instruction.ConstantInstruction;
54+
import static java.lang.constant.ConstantDescs.*;
55+
import static org.junit.jupiter.api.Assertions.*;
4556

4657
class LDCTest {
4758
@Test
48-
void testLDCisConvertedToLDCW() throws Exception {
49-
var cc = ClassFile.of();
50-
byte[] bytes = cc.build(ClassDesc.of("MyClass"), cb -> {
51-
cb.withFlags(AccessFlag.PUBLIC);
52-
cb.withVersion(52, 0);
53-
cb.withMethod("<init>", MethodTypeDesc.of(CD_void), 0, mb -> mb
54-
.withCode(codeb -> codeb.aload(0)
55-
.invokespecial(CD_Object, "<init>", MTD_VOID, false)
56-
.return_()
57-
)
58-
)
59+
void loadConstantGeneralTest() throws Exception {
60+
var otherCp = ConstantPoolBuilder.of();
61+
var narrowString131 = otherCp.stringEntry("string131");
62+
assertTrue(narrowString131.index() <= 0xFF);
63+
for (int i = 0; i < 0xFF; i++) {
64+
var unused = otherCp.intEntry(i);
65+
}
66+
var wideString0 = otherCp.stringEntry("string0");
67+
assertTrue(wideString0.index() > 0xFF);
5968

60-
.withMethod("main", MethodTypeDesc.of(CD_void, CD_String.arrayType()),
61-
ACC_PUBLIC | ACC_STATIC,
62-
mb -> mb.withCode(c0 -> {
63-
ConstantPoolBuilder cpb = cb.constantPool();
64-
for (int i = 0; i <= 256/2 + 2; i++) { // two entries per String
65-
StringEntry s = cpb.stringEntry("string" + i);
66-
}
67-
c0.ldc("string0")
68-
.ldc("string131")
69-
.ldc("string50")
70-
.loadConstant(-0.0f)
71-
.loadConstant(-0.0d)
72-
//non-LDC test cases
73-
.loadConstant(0.0f)
74-
.loadConstant(0.0d)
75-
.return_();
76-
}));
77-
});
69+
var cc = ClassFile.of();
70+
var cd = ClassDesc.of("MyClass");
71+
MethodTypeDesc bsmType = MethodTypeDesc.of(CD_double, CD_MethodHandles_Lookup, CD_String, CD_Class);
72+
byte[] bytes = cc.build(cd, cb -> cb
73+
.withFlags(AccessFlag.PUBLIC)
74+
.withVersion(JAVA_11_VERSION, 0) // condy support required
75+
.withMethodBody("bsm", bsmType, ACC_STATIC, cob -> cob
76+
.dconst_1()
77+
.dreturn())
78+
.withMethodBody("main", MethodTypeDesc.of(CD_void, CD_String.arrayType()),
79+
ACC_PUBLIC | ACC_STATIC, c0 -> {
80+
ConstantPoolBuilder cpb = cb.constantPool();
81+
LongEntry l42 = cpb.longEntry(42);
82+
assertTrue(l42.index() <= 0xFF);
83+
for (int i = 0; i <= 256 / 2 + 2; i++) { // two entries per String
84+
StringEntry s = cpb.stringEntry("string" + i);
85+
}
86+
var wideCondy = cpb.constantDynamicEntry(DynamicConstantDesc.of(MethodHandleDesc.ofMethod(
87+
DirectMethodHandleDesc.Kind.STATIC, cd, "bsm", bsmType)));
88+
assertTrue(wideCondy.index() > 0xFF);
89+
var s0 = cpb.stringEntry("string0");
90+
assertTrue(s0.index() <= 0xFF);
91+
// use line number to match case numbers; pop ensures verification passes
92+
c0.ldc("string0").pop() // regular ldc
93+
.ldc(wideString0).pop() // adaption - narrowed
94+
.with(ConstantInstruction.ofLoad(LDC, wideString0)).pop() // adaption
95+
.with(ConstantInstruction.ofLoad(LDC_W, wideString0)).pop() // adaption - narrowed
96+
.with(ConstantInstruction.ofLoad(LDC_W, s0)).pop() // explicit ldc_w - local
97+
.ldc("string131").pop() // ldc_w
98+
.ldc(narrowString131).pop() // adaption - widened
99+
.with(ConstantInstruction.ofLoad(LDC, narrowString131)).pop() // adaption - widened
100+
.with(ConstantInstruction.ofLoad(LDC_W, narrowString131)).pop() // adaption
101+
.ldc("string50").pop()
102+
.ldc(l42).pop2() // long cases
103+
.loadConstant(l42.longValue()).pop2()
104+
.loadConstant(Long.valueOf(l42.longValue())).pop2()
105+
.loadConstant(-0.0f).pop() // floating cases
106+
.loadConstant(-0.0d).pop2()
107+
.loadConstant(0.0f).pop() // intrinsic cases
108+
.loadConstant(0.0d).pop2()
109+
.ldc(wideCondy).pop2() // no wrong "widening" of condy
110+
.return_();
111+
}));
78112

79-
var model = cc.parse(bytes);
80-
var code = model.elementStream()
81-
.filter(e -> e instanceof MethodModel)
82-
.map(e -> (MethodModel) e)
83-
.filter(e -> e.methodName().stringValue().equals("main"))
84-
.flatMap(MethodModel::elementStream)
85-
.filter(e -> e instanceof CodeModel)
86-
.map(e -> (CodeModel) e)
113+
var cm = cc.parse(bytes);
114+
var code = cm.elementStream()
115+
.<CodeAttribute>mapMulti((ce, sink) -> {
116+
if (ce instanceof MethodModel mm && mm.methodName().equalsString("main")) {
117+
sink.accept(mm.findAttribute(Attributes.code()).orElseThrow());
118+
}
119+
})
87120
.findFirst()
88121
.orElseThrow();
89-
var opcodes = code.elementList().stream()
90-
.filter(e -> e instanceof Instruction)
91-
.map(e -> (Instruction)e)
92-
.toList();
122+
var instructions = code.elementList().stream()
123+
.<ConstantInstruction>mapMulti((ce, sink) -> {
124+
if (ce instanceof ConstantInstruction i) {
125+
sink.accept(i);
126+
}
127+
})
128+
.toList();
93129

94-
assertEquals(opcodes.size(), 8);
95-
assertEquals(opcodes.get(0).opcode(), LDC);
96-
assertEquals(opcodes.get(1).opcode(), LDC_W);
97-
assertEquals(opcodes.get(2).opcode(), LDC);
130+
assertIterableEquals(List.of(
131+
LDC, // string0
132+
LDC,
133+
LDC,
134+
LDC,
135+
LDC_W,
136+
LDC_W, // string131
137+
LDC_W,
138+
LDC_W,
139+
LDC_W,
140+
LDC, // string50
141+
LDC2_W, // long cases
142+
LDC2_W,
143+
LDC2_W,
144+
LDC_W, // floating cases
145+
LDC2_W,
146+
FCONST_0, // intrinsic cases
147+
DCONST_0,
148+
LDC2_W // wide condy
149+
), instructions.stream().map(Instruction::opcode).toList());
150+
151+
int longCaseStart = 10;
152+
for (int longCaseIndex = longCaseStart; longCaseIndex < longCaseStart + 3; longCaseIndex++) {
153+
var message = "Case " + longCaseIndex;
154+
assertEquals(42, (long) instructions.get(longCaseIndex).constantValue(), message);
155+
}
156+
157+
int floatingCaseStart = longCaseStart + 3;
98158
assertEquals(
99-
Float.floatToRawIntBits((float)((ConstantInstruction)opcodes.get(3)).constantValue()),
159+
Float.floatToRawIntBits((float) instructions.get(floatingCaseStart).constantValue()),
100160
Float.floatToRawIntBits(-0.0f));
101161
assertEquals(
102-
Double.doubleToRawLongBits((double)((ConstantInstruction)opcodes.get(4)).constantValue()),
162+
Double.doubleToRawLongBits((double) instructions.get(floatingCaseStart + 1).constantValue()),
103163
Double.doubleToRawLongBits(-0.0d));
104-
assertEquals(opcodes.get(5).opcode(), FCONST_0);
105-
assertEquals(opcodes.get(6).opcode(), DCONST_0);
106-
assertEquals(opcodes.get(7).opcode(), RETURN);
107-
}
108164

109-
// TODO test for explicit LDC_W?
110-
}
165+
assertDoesNotThrow(() -> ByteCodeLoader.load("MyClass", bytes), "Invalid LDC bytecode generated");
166+
}
167+
}

0 commit comments

Comments
 (0)
Please sign in to comment.