Skip to content

Commit c65a182

Browse files
olesya.subbotinamrjameshamilton
olesya.subbotina
authored andcommitted
Fix handling of primitive type classes
In cases when metadata contained `@Serializer(forClass = int.class)` `int.class` part was treated as a class element value, and previously, in the refactored method, its internal className was calculated to be null as primitive class names were not taken into account apparently. It led to stacktrace like this: ``` java.lang.NullPointerException: Cannot invoke "String.length()" because "className" is null at com.guardsquare.proguard.kotlin.printer.internal.Context.className(Context.java:83) at com.guardsquare.proguard.kotlin.printer.internal.AnnotationPrinter.visitClassElementValue(AnnotationPrinter.java:138) ... ``` This diff adds a primitive type class check to have the name derived correctly.
1 parent 41c8a82 commit c65a182

File tree

2 files changed

+78
-4
lines changed

2 files changed

+78
-4
lines changed

kmp-library/src/main/java/com/guardsquare/proguard/kotlin/printer/internal/AnnotationPrinter.java

+23-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99

1010
import com.guardsquare.proguard.kotlin.printer.KotlinMetadataPrinter;
1111
import com.guardsquare.proguard.kotlin.printer.internal.visitor.ConstantToStringVisitor;
12+
import java.util.HashMap;
13+
import java.util.Map;
1214
import proguard.classfile.Clazz;
15+
import proguard.classfile.TypeConstants;
1316
import proguard.classfile.attribute.Attribute;
1417
import proguard.classfile.attribute.annotation.Annotation;
1518
import proguard.classfile.attribute.annotation.AnnotationElementValue;
@@ -40,7 +43,18 @@ public class AnnotationPrinter
4043
private final KotlinMetadataPrinter printer;
4144
private final boolean inline;
4245
private int level = 0;
43-
46+
private static final Map<Character, String> primitiveTypeToClass = new HashMap<>();
47+
48+
static {
49+
primitiveTypeToClass.put(TypeConstants.BOOLEAN, "Boolean::class");
50+
primitiveTypeToClass.put(TypeConstants.BYTE, "Byte::class");
51+
primitiveTypeToClass.put(TypeConstants.CHAR, "Char::class");
52+
primitiveTypeToClass.put(TypeConstants.SHORT, "Short::class");
53+
primitiveTypeToClass.put(TypeConstants.INT, "Int::class");
54+
primitiveTypeToClass.put(TypeConstants.FLOAT, "Float::class");
55+
primitiveTypeToClass.put(TypeConstants.LONG, "Long::class");
56+
primitiveTypeToClass.put(TypeConstants.DOUBLE, "Double::class");
57+
}
4458

4559
public AnnotationPrinter(KotlinMetadataPrinter printer)
4660
{
@@ -130,15 +144,20 @@ public void visitEnumConstantElementValue(Clazz clazz,
130144
printer.print("." + enumConstantElementValue.getConstantName(clazz));
131145
}
132146

133-
134147
@Override
135148
public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
136149
{
137150
visitAnyElementValue(clazz, annotation, classElementValue);
138-
printer.print(printer.getContext().className(ClassUtil.internalClassNameFromType(classElementValue.getClassName(clazz)), "."));
151+
String internalClassName;
152+
String className = classElementValue.getClassName(clazz);
153+
if (ClassUtil.isInternalPrimitiveType(className)) {
154+
internalClassName = primitiveTypeToClass.get(className.charAt(0));
155+
} else {
156+
internalClassName = ClassUtil.internalClassNameFromType(className);
157+
}
158+
printer.print(printer.getContext().className(internalClassName, "."));
139159
}
140160

141-
142161
@Override
143162
public void visitAnnotationElementValue(Clazz clazz,
144163
Annotation annotation,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import com.guardsquare.proguard.kotlin.printer.KotlinMetadataPrinter
2+
import io.kotest.core.spec.style.FunSpec
3+
import io.kotest.matchers.shouldBe
4+
import proguard.classfile.kotlin.visitor.ReferencedKotlinMetadataVisitor
5+
import proguard.testutils.ClassPoolBuilder
6+
import proguard.testutils.KotlinSource
7+
8+
class PrimitiveInAnnotationTest : FunSpec({
9+
test("Primitive type class in annotation") {
10+
val (programClassPool, _) = ClassPoolBuilder.fromSource(
11+
KotlinSource(
12+
"Test.kt",
13+
"""
14+
import kotlin.reflect.KClass
15+
16+
@Target(AnnotationTarget.CLASS)
17+
@Retention(AnnotationRetention.RUNTIME)
18+
annotation class Serializer(val forClass: KClass<*>)
19+
20+
@Serializer(forClass = Int::class)
21+
class Test {
22+
var myInt: Int = 0
23+
}
24+
""".trimIndent()
25+
),
26+
)
27+
28+
programClassPool.classesAccept(
29+
ReferencedKotlinMetadataVisitor(
30+
KotlinMetadataPrinter(
31+
programClassPool
32+
)
33+
)
34+
)
35+
36+
val testKtMetadata = programClassPool.getClass("Test").processingInfo as String
37+
38+
testKtMetadata.trimEnd() shouldBe """
39+
/**
40+
* Kotlin class (metadata version 1.8.0).
41+
* From Java class: Test
42+
*/
43+
@Serializer(forClass = Int::class)
44+
class Test {
45+
46+
// Properties
47+
48+
var myInt: Int
49+
// backing field: int myInt
50+
get // getter method: public final int getMyInt()
51+
set() // setter method: public final void setMyInt(int)
52+
}
53+
""".trimIndent()
54+
}
55+
})

0 commit comments

Comments
 (0)