Kapt3: Fix parameter names in JvmOverloads-generated methods (KT-15024)

This commit is contained in:
Yan Zhulanow
2016-11-29 22:04:53 +03:00
committed by Yan Zhulanow
parent 77153f0926
commit 3c77242da2
13 changed files with 217 additions and 3 deletions
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmOverloadsAnnotation
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
/**
@@ -36,6 +37,12 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
* parameter values substituted.
*/
class DefaultParameterValueSubstitutor(val state: GenerationState) {
private companion object {
// rename -> JvmOverloads
private val ANNOTATION_TYPE_DESCRIPTOR_FOR_JVMOVERLOADS_GENERATED_METHODS: String =
Type.getObjectType("synthetic/kotlin/jvm/GeneratedByJvmOverloads").descriptor
}
/**
* If all of the parameters of the specified constructor declare default values,
* generates a no-argument constructor that passes default values for all arguments.
@@ -135,6 +142,10 @@ class DefaultParameterValueSubstitutor(val state: GenerationState) {
AnnotationCodegen.forMethod(mv, memberCodegen, typeMapper).genAnnotations(functionDescriptor, signature.returnType)
if (state.classBuilderMode == ClassBuilderMode.KAPT3) {
mv.visitAnnotation(ANNOTATION_TYPE_DESCRIPTOR_FOR_JVMOVERLOADS_GENERATED_METHODS, /* visible = */ false)
}
remainingParameters.withIndex().forEach {
val annotationCodegen = AnnotationCodegen.forParameter(it.index, mv, memberCodegen, typeMapper)
annotationCodegen.genAnnotations(it.value, signature.valueParameters[it.index].asmType)
@@ -63,6 +63,7 @@ class ClassFileToSourceStubConverter(
private val BLACKLISTED_ANNOTATIONS = listOf(
"java.lang.Deprecated", "kotlin.Deprecated", // Deprecated annotations
"java.lang.Synthetic",
"synthetic.kotlin.jvm.GeneratedByJvmOverloads", // kapt3-related annotation for marking JvmOverloads-generated methods
"java.lang.annotation.", // Java annotations
"org.jetbrains.annotations.", // Nullable/NotNull, ReadOnly, Mutable
"kotlin.jvm.", "kotlin.Metadata" // Kotlin annotations from runtime
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.kapt3.stubs
import org.jetbrains.kotlin.kapt3.util.isAbstract
import org.jetbrains.kotlin.kapt3.util.isEnum
import org.jetbrains.kotlin.kapt3.util.isJvmOverloadsGenerated
import org.jetbrains.kotlin.kapt3.util.isStatic
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.AnnotationNode
@@ -36,6 +37,7 @@ internal fun MethodNode.getParametersInfo(containingClass: ClassNode): List<Para
val localVariables = this.localVariables ?: emptyList()
val parameters = this.parameters ?: emptyList()
val isStatic = isStatic(access)
val isJvmOverloads = this.isJvmOverloadsGenerated()
// First and second parameters in enum constructors are synthetic, we should ignore them
val isEnumConstructor = (name == "<init>") && containingClass.isEnum()
@@ -53,7 +55,14 @@ internal fun MethodNode.getParametersInfo(containingClass: ClassNode): List<Para
else
null
name = name ?: localVariables.getOrNull(index + (if (isStatic) 0 else 1))?.name
val localVariableIndexOffset = when {
isStatic -> 0
isJvmOverloads -> 0
else -> 1
}
// @JvmOverloads constructors and ordinary methods don't have "this" local variable
name = name ?: localVariables.getOrNull(index + localVariableIndexOffset)?.name
?: "p${index - startParameterIndex}"
// Property setters has bad parameter names
@@ -17,6 +17,8 @@
package org.jetbrains.kotlin.kapt3.util
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.AnnotationNode
import org.jetbrains.org.objectweb.asm.tree.ClassNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
@@ -30,4 +32,18 @@ internal fun ClassNode.isEnum() = (access and Opcodes.ACC_ENUM) != 0
internal fun ClassNode.isAnnotation() = (access and Opcodes.ACC_ANNOTATION) != 0
internal fun MethodNode.isVarargs() = (access and Opcodes.ACC_VARARGS) != 0
internal fun <T> List<T>?.isNullOrEmpty() = this == null || this.isEmpty()
internal fun <T> List<T>?.isNullOrEmpty() = this == null || this.isEmpty()
internal fun MethodNode.isJvmOverloadsGenerated(): Boolean {
return (invisibleAnnotations?.any { it.isJvmOverloadsGenerated() } ?: false)
|| (visibleAnnotations?.any { it.isJvmOverloadsGenerated() } ?: false)
}
// Constant from DefaultParameterValueSubstitutor can't be used in Maven build because of ProGuard
// rename this as well
private val ANNOTATION_TYPE_DESCRIPTOR_FOR_JVMOVERLOADS_GENERATED_METHODS: String =
Type.getObjectType("synthetic/kotlin/jvm/GeneratedByJvmOverloads").descriptor
private fun AnnotationNode.isJvmOverloadsGenerated(): Boolean {
return this.desc == ANNOTATION_TYPE_DESCRIPTOR_FOR_JVMOVERLOADS_GENERATED_METHODS
}
@@ -102,6 +102,12 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
doTest(fileName);
}
@TestMetadata("jvmOverloads.kt")
public void testJvmOverloads() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/jvmOverloads.kt");
doTest(fileName);
}
@TestMetadata("jvmStatic.kt")
public void testJvmStatic() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/jvmStatic.kt");
@@ -17,6 +17,8 @@
package org.jetbrains.kotlin.kapt3.test
import org.junit.Test
import javax.lang.model.element.ElementKind
import javax.lang.model.element.ExecutableElement
class KotlinKapt3IntegrationTests : AbstractKotlinKapt3IntegrationTest() {
@Test
@@ -36,4 +38,30 @@ class KotlinKapt3IntegrationTests : AbstractKotlinKapt3IntegrationTest() {
assertEquals("firstValue", options["firstKey"])
assertTrue("secondKey" in options)
}
@Test
fun testOverloads() = test("Overloads", "test.MyAnnotation") { set, roundEnv, env ->
assertEquals(1, set.size)
val annotatedElements = roundEnv.getElementsAnnotatedWith(set.single())
assertEquals(1, annotatedElements.size)
val constructors = annotatedElements
.first()
.enclosedElements
.filter { it.kind == ElementKind.CONSTRUCTOR }
.map { it as ExecutableElement }
.sortedBy { it.parameters.size }
assertEquals(2, constructors.size)
assertEquals(2, constructors[0].parameters.size)
assertEquals(3, constructors[1].parameters.size)
assertEquals("int", constructors[0].parameters[0].asType().toString())
assertEquals("long", constructors[0].parameters[1].asType().toString())
assertEquals("int", constructors[1].parameters[0].asType().toString())
assertEquals("long", constructors[1].parameters[1].asType().toString())
assertEquals("java.lang.String", constructors[1].parameters[2].asType().toString())
assertEquals("someInt", constructors[0].parameters[0].simpleName.toString())
assertEquals("someLong", constructors[0].parameters[1].simpleName.toString())
assertEquals("someInt", constructors[1].parameters[0].simpleName.toString())
assertEquals("someLong", constructors[1].parameters[1].simpleName.toString())
assertEquals("someString", constructors[1].parameters[2].simpleName.toString())
}
}
@@ -36,6 +36,12 @@ public class KotlinKaptContextTestGenerated extends AbstractKotlinKaptContextTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/kapt3/testData/kotlinRunner"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("Overloads.kt")
public void testOverloads() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/kotlinRunner/Overloads.kt");
doTest(fileName);
}
@TestMetadata("Simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/kotlinRunner/Simple.kt");
+17
View File
@@ -0,0 +1,17 @@
class State @JvmOverloads constructor(
val someInt: Int,
val someLong: Long,
val someString: String = ""
)
class State2 @JvmOverloads constructor(
@JvmField val someInt: Int,
@JvmField val someLong: Long = 2,
@JvmField val someString: String = ""
) {
@JvmOverloads
fun test(someInt: Int, someLong: Long = 1, someString: String = "A"): Int = 5
fun someMethod(str: String) {}
fun methodWithoutArgs() {}
}
+64
View File
@@ -0,0 +1,64 @@
public final class State {
private final int someInt = 0;
private final long someLong = 0L;
private final java.lang.String someString = null;
public final int getSomeInt() {
return 0;
}
public final long getSomeLong() {
return 0L;
}
public final java.lang.String getSomeString() {
return null;
}
public State(int someInt, long someLong, java.lang.String someString) {
super();
}
public State(int someInt, long someLong) {
super();
}
}
////////////////////
public final class State2 {
public final int someInt = 0;
public final long someLong = 0L;
public final java.lang.String someString = null;
public final int test(int someInt, long someLong, java.lang.String someString) {
return 0;
}
public final int test(int someInt, long someLong) {
return 0;
}
public final int test(int someInt) {
return 0;
}
public final void someMethod(java.lang.String str) {
}
public final void methodWithoutArgs() {
}
public State2(int someInt, long someLong, java.lang.String someString) {
super();
}
public State2(int someInt, long someLong) {
super();
}
public State2(int someInt) {
super();
}
}
+1 -1
View File
@@ -143,7 +143,7 @@ public final class Modifiers {
return null;
}
public final java.lang.String overloads(java.lang.String p0) {
public final java.lang.String overloads(java.lang.String a) {
return null;
}
+42
View File
@@ -0,0 +1,42 @@
package test;
public abstract @interface MyAnnotation {
}
////////////////////
package test;
@MyAnnotation()
public final class State {
private final int someInt = 0;
private final long someLong = 0L;
private final java.lang.String someString = null;
public final int getSomeInt() {
return 0;
}
public final long getSomeLong() {
return 0L;
}
public final java.lang.String getSomeString() {
return null;
}
public State(int someInt, long someLong, java.lang.String someString) {
super();
}
public State(int someInt, long someLong) {
super();
}
}
////////////////////
package error;
public final class NonExistentClass {
}
+10
View File
@@ -0,0 +1,10 @@
package test
internal annotation class MyAnnotation
@MyAnnotation
internal class State @JvmOverloads constructor(
val someInt: Int,
val someLong: Long,
val someString: String = ""
)
+4
View File
@@ -0,0 +1,4 @@
StateMyAnnotation.java:
package generated;
class StateMyAnnotation {}