Skip to content

Commit 2e26337

Browse files
rcosta358codex
andcommitted
Resolve inherited public constants in field reads
Co-authored-by: Codex <noreply@openai.com>
1 parent 0472025 commit 2e26337

4 files changed

Lines changed: 82 additions & 4 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package testSuite.classes.inherited_constant_correct;
2+
3+
import liquidjava.specification.ExternalRefinementsFor;
4+
import liquidjava.specification.Refinement;
5+
import liquidjava.specification.StateRefinement;
6+
import liquidjava.specification.StateSet;
7+
8+
@StateSet({"start", "explicit"})
9+
@ExternalRefinementsFor("javax.imageio.ImageWriteParam")
10+
public interface ImageWriteParamRefinements {
11+
@StateRefinement(to = "mode == 2 ? explicit(this) : start(this)")
12+
void setCompressionMode(@Refinement("_ >= 0 && _ <= 3") int mode);
13+
14+
@StateRefinement(from = "explicit(this)")
15+
void setCompressionQuality(@Refinement("_ >= 0.0 && _ <= 1.0") float quality);
16+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package testSuite.classes.inherited_constant_correct;
2+
3+
import javax.imageio.ImageIO;
4+
import javax.imageio.ImageWriteParam;
5+
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
6+
7+
public class Repro {
8+
public static void main(String[] args) {
9+
ImageWriteParam p = ImageIO.getImageWritersByFormatName("jpeg").next().getDefaultWriteParam();
10+
p.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT);
11+
p.setCompressionQuality(0.5f);
12+
}
13+
}

‎liquidjava-verifier/src/main/java/liquidjava/utils/StaticConstants.java‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ private StaticConstants() {
3030
*
3131
* <p>
3232
* Tries the source AST first ({@link CtLiteral} initializer in Spoon's model), then reflection via
33-
* {@link CtFieldReference#getActualField()} + {@link #readStaticFinal}. Returns {@code null} if the field isn't
34-
* static-final, has a non-literal initializer, or any lookup step fails.
33+
* {@link Class#getField(String)} + {@link #readStaticFinal} so inherited public constants are included. Returns
34+
* {@code null} if the field isn't static-final, has a non-literal initializer, or any lookup step fails.
3535
*
3636
* @see #resolve(String, String, CtElement) sibling for refinement-string {@code Type.CONST} references
3737
*/
@@ -43,8 +43,8 @@ public static Object resolve(CtFieldReference<?> ref) {
4343
if (v != null)
4444
return v;
4545
try {
46-
return ref.getActualField()instanceof Field jf ? readStaticFinal(jf) : null;
47-
} catch (RuntimeException | LinkageError ignored) {
46+
return readStaticFinal(ref.getDeclaringType().getActualClass().getField(ref.getSimpleName()));
47+
} catch (NoSuchFieldException | RuntimeException | LinkageError ignored) {
4848
// Spoon throws SpoonClassNotFoundException; reflection can throw LinkageError. Fall through.
4949
return null;
5050
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package liquidjava.utils;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertNull;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
import spoon.Launcher;
9+
import spoon.reflect.code.CtFieldRead;
10+
import spoon.reflect.visitor.filter.TypeFilter;
11+
12+
class StaticConstantsTest {
13+
@Test
14+
void resolvesInheritedLibraryConstant() {
15+
assertEquals(2, resolveRead(
16+
"class Repro { int mode = javax.imageio.plugins.jpeg.JPEGImageWriteParam.MODE_EXPLICIT; }"));
17+
}
18+
19+
@Test
20+
void resolvesInheritedInterfaceConstant() {
21+
assertEquals(0, resolveRead("class Repro { int alignment = javax.swing.JLabel.CENTER; }"));
22+
}
23+
24+
@Test
25+
void resolvesDeclaredLibraryConstant() {
26+
assertEquals(2, resolveRead("class Repro { int mode = javax.imageio.ImageWriteParam.MODE_EXPLICIT; }"));
27+
}
28+
29+
@Test
30+
void resolvesPrivateSourceConstant() {
31+
assertEquals(3, resolveRead("class Repro { private static final int LIMIT = 3; int value = LIMIT; }"));
32+
}
33+
34+
@Test
35+
void rejectsMutableSourceField() {
36+
assertNull(resolveRead("class Repro { static int LIMIT = 3; int value = LIMIT; }"));
37+
}
38+
39+
@Test
40+
void rejectsInstanceSourceField() {
41+
assertNull(resolveRead("class Repro { final int LIMIT = 3; int value = LIMIT; }"));
42+
}
43+
44+
private static Object resolveRead(String source) {
45+
var type = Launcher.parseClass(source);
46+
CtFieldRead<?> read = type.getElements(new TypeFilter<>(CtFieldRead.class)).get(0);
47+
return StaticConstants.resolve(read.getVariable());
48+
}
49+
}

0 commit comments

Comments
 (0)