Kapt: Do not inline R.resType.resName constants in annotations (KT-18791)
This commit is contained in:
@@ -27,6 +27,7 @@ import com.sun.tools.javac.util.Name
|
||||
import com.sun.tools.javac.util.Names
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.kapt3.KaptContext
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.Type.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.ClassNode
|
||||
@@ -45,6 +46,12 @@ class KaptTreeMaker(context: Context, private val kaptContext: KaptContext<*>) :
|
||||
fun FqName(internalOrFqName: String): JCTree.JCExpression {
|
||||
val path = getQualifiedName(internalOrFqName).convertSpecialFqName().split('.')
|
||||
assert(path.isNotEmpty())
|
||||
return FqName(path)
|
||||
}
|
||||
|
||||
fun FqName(fqName: FqName) = FqName(fqName.pathSegments().map { it.asString() })
|
||||
|
||||
private fun FqName(path: List<String>): JCTree.JCExpression {
|
||||
if (path.size == 1) return SimpleName(path.single())
|
||||
|
||||
var expr = Select(SimpleName(path[0]), name(path[1]))
|
||||
|
||||
+77
-11
@@ -25,6 +25,8 @@ import com.sun.tools.javac.tree.JCTree.*
|
||||
import com.sun.tools.javac.tree.TreeMaker
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.kapt3.*
|
||||
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
|
||||
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
|
||||
@@ -32,7 +34,10 @@ import org.jetbrains.kotlin.kapt3.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
@@ -202,7 +207,7 @@ class ClassFileToSourceStubConverter(
|
||||
|
||||
val modifiers = convertModifiers(flags,
|
||||
if (isEnum) ElementKind.ENUM else ElementKind.CLASS,
|
||||
packageFqName, clazz.visibleAnnotations, clazz.invisibleAnnotations)
|
||||
packageFqName, clazz.visibleAnnotations, clazz.invisibleAnnotations, descriptor.annotations)
|
||||
|
||||
val isDefaultImpls = clazz.name.endsWith("${descriptor.name.asString()}\$DefaultImpls")
|
||||
&& isPublic(clazz.access) && isFinal(clazz.access)
|
||||
@@ -360,7 +365,8 @@ class ClassFileToSourceStubConverter(
|
||||
val origin = kaptContext.origins[field]
|
||||
val descriptor = origin?.descriptor
|
||||
|
||||
val modifiers = convertModifiers(field.access, ElementKind.FIELD, packageFqName, field.visibleAnnotations, field.invisibleAnnotations)
|
||||
val modifiers = convertModifiers(field.access, ElementKind.FIELD, packageFqName,
|
||||
field.visibleAnnotations, field.invisibleAnnotations, descriptor?.annotations ?: Annotations.EMPTY)
|
||||
val name = getValidIdentifierName(field.name) ?: return null
|
||||
val type = Type.getType(field.desc)
|
||||
|
||||
@@ -410,7 +416,7 @@ class ClassFileToSourceStubConverter(
|
||||
(method.access.toLong() and VISIBILITY_MODIFIERS.inv())
|
||||
else
|
||||
method.access.toLong(),
|
||||
ElementKind.METHOD, packageFqName, visibleAnnotations, method.invisibleAnnotations)
|
||||
ElementKind.METHOD, packageFqName, visibleAnnotations, method.invisibleAnnotations, descriptor.annotations)
|
||||
|
||||
val asmReturnType = Type.getReturnType(method.desc)
|
||||
val jcReturnType = if (isConstructor) null else treeMaker.Type(asmReturnType)
|
||||
@@ -432,7 +438,8 @@ class ClassFileToSourceStubConverter(
|
||||
ElementKind.PARAMETER,
|
||||
packageFqName,
|
||||
info.visibleAnnotations,
|
||||
info.invisibleAnnotations)
|
||||
info.invisibleAnnotations,
|
||||
Annotations.EMPTY /* TODO */)
|
||||
|
||||
val name = treeMaker.name(getValidIdentifierName(info.name) ?: "p${index}_" + info.name.hashCode().ushr(1))
|
||||
val type = treeMaker.Type(info.type)
|
||||
@@ -573,21 +580,28 @@ class ClassFileToSourceStubConverter(
|
||||
kind: ElementKind,
|
||||
packageFqName: String,
|
||||
visibleAnnotations: List<AnnotationNode>?,
|
||||
invisibleAnnotations: List<AnnotationNode>?
|
||||
): JCModifiers = convertModifiers(access.toLong(), kind, packageFqName, visibleAnnotations, invisibleAnnotations)
|
||||
invisibleAnnotations: List<AnnotationNode>?,
|
||||
descriptorAnnotations: Annotations
|
||||
): JCModifiers = convertModifiers(access.toLong(), kind, packageFqName, visibleAnnotations, invisibleAnnotations, descriptorAnnotations)
|
||||
|
||||
private fun convertModifiers(
|
||||
access: Long,
|
||||
kind: ElementKind,
|
||||
packageFqName: String,
|
||||
visibleAnnotations: List<AnnotationNode>?,
|
||||
invisibleAnnotations: List<AnnotationNode>?
|
||||
invisibleAnnotations: List<AnnotationNode>?,
|
||||
descriptorAnnotations: Annotations
|
||||
): JCModifiers {
|
||||
fun findDescriptorAnnotation(anno: AnnotationNode): AnnotationDescriptor? {
|
||||
val annoFqName = treeMaker.getQualifiedName(Type.getType(anno.desc))
|
||||
return descriptorAnnotations.findAnnotation(FqName(annoFqName))
|
||||
}
|
||||
|
||||
var annotations = visibleAnnotations?.fold(JavacList.nil<JCAnnotation>()) { list, anno ->
|
||||
convertAnnotation(anno, packageFqName)?.let { list.prepend(it) } ?: list
|
||||
convertAnnotation(anno, packageFqName, findDescriptorAnnotation(anno))?.let { list.prepend(it) } ?: list
|
||||
} ?: JavacList.nil()
|
||||
annotations = invisibleAnnotations?.fold(annotations) { list, anno ->
|
||||
convertAnnotation(anno, packageFqName)?.let { list.prepend(it) } ?: list
|
||||
convertAnnotation(anno, packageFqName, findDescriptorAnnotation(anno))?.let { list.prepend(it) } ?: list
|
||||
} ?: annotations
|
||||
|
||||
val flags = when (kind) {
|
||||
@@ -601,7 +615,12 @@ class ClassFileToSourceStubConverter(
|
||||
return treeMaker.Modifiers(flags, annotations)
|
||||
}
|
||||
|
||||
private fun convertAnnotation(annotation: AnnotationNode, packageFqName: String? = "", filtered: Boolean = true): JCAnnotation? {
|
||||
private fun convertAnnotation(
|
||||
annotation: AnnotationNode,
|
||||
packageFqName: String? = "",
|
||||
annotationDescriptor: AnnotationDescriptor? = null,
|
||||
filtered: Boolean = true
|
||||
): JCAnnotation? {
|
||||
val annotationType = Type.getType(annotation.desc)
|
||||
val fqName = treeMaker.getQualifiedName(annotationType)
|
||||
|
||||
@@ -609,15 +628,62 @@ class ClassFileToSourceStubConverter(
|
||||
if (BLACKLISTED_ANNOTATIONS.any { fqName.startsWith(it) }) return null
|
||||
}
|
||||
|
||||
val ktAnnotation = (annotationDescriptor?.source as? PsiSourceElement)?.psi as? KtAnnotationEntry
|
||||
val argMapping = ktAnnotation?.calleeExpression
|
||||
?.getResolvedCall(kaptContext.bindingContext)?.valueArguments
|
||||
?.mapKeys { it.key.name.asString() }
|
||||
|
||||
fun getKtElementsForArgument(name: String): List<KtExpression?>? {
|
||||
val args = argMapping?.get(name) ?: return null
|
||||
return args.arguments.map { it.getArgumentExpression() }
|
||||
}
|
||||
|
||||
val useSimpleName = '.' in fqName && fqName.substringBeforeLast('.', "") == packageFqName
|
||||
val name = if (useSimpleName) treeMaker.FqName(fqName.substring(packageFqName!!.length + 1)) else treeMaker.Type(annotationType)
|
||||
val values = mapPairedValuesJList<JCExpression>(annotation.values) { key, value ->
|
||||
val name = getValidIdentifierName(key) ?: return@mapPairedValuesJList null
|
||||
treeMaker.Assign(treeMaker.SimpleName(name), convertLiteralExpression(value))
|
||||
val convertedExpression = convertLiteralExpression(value)
|
||||
|
||||
fun assign(expr: JCExpression) = treeMaker.Assign(treeMaker.SimpleName(name), expr)
|
||||
|
||||
if (value is Int) { // This may be a resource identifier
|
||||
val ktExpression = getKtElementsForArgument(name)?.singleOrNull()
|
||||
tryParseReferenceToAndroidResource(ktExpression)?.let { return@mapPairedValuesJList assign(it) }
|
||||
}
|
||||
|
||||
assign(convertedExpression)
|
||||
}
|
||||
|
||||
return treeMaker.Annotation(name, values)
|
||||
}
|
||||
|
||||
private fun tryParseReferenceToAndroidResource(expression: KtExpression?): JCExpression? {
|
||||
val bindingContext = kaptContext.bindingContext
|
||||
|
||||
val expressionToResolve = when (expression) {
|
||||
is KtDotQualifiedExpression -> expression.selectorExpression
|
||||
else -> expression
|
||||
}
|
||||
|
||||
val resolvedCall = expressionToResolve.getResolvedCall(bindingContext) ?: return null
|
||||
val fqName = resolvedCall.resultingDescriptor.fqNameOrNull() ?: return null
|
||||
val jcExpression = treeMaker.FqName(fqName)
|
||||
|
||||
return if (doesLookLikeAndroidIdentifier(jcExpression)) jcExpression else null
|
||||
}
|
||||
|
||||
private fun doesLookLikeAndroidIdentifier(expression: JCExpression): Boolean {
|
||||
// 'expression' here is always a fqName, so we can forget about imports here
|
||||
val rId = (expression as? JCFieldAccess)?.selected ?: return false
|
||||
val r = (rId as? JCFieldAccess)?.selected ?: return false
|
||||
|
||||
return when {
|
||||
r is JCIdent && r.name.toString() == "R" -> true
|
||||
r is JCFieldAccess && r.name.toString() == "R" -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertValueOfPrimitiveTypeOrString(value: Any?): JCExpression? {
|
||||
return when (value) {
|
||||
is Char -> treeMaker.Literal(TypeTag.CHAR, value.toInt())
|
||||
|
||||
+6
@@ -192,6 +192,12 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt18791.kt")
|
||||
public void testKt18791() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/kt18791.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt19700.kt")
|
||||
public void testKt19700() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/kt19700.kt");
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
//FILE: lib/R.java
|
||||
package lib;
|
||||
|
||||
public class R {
|
||||
public static class id {
|
||||
public final static int textView = 100;
|
||||
}
|
||||
}
|
||||
|
||||
//FILE: app/R.java
|
||||
package app;
|
||||
|
||||
public class R {
|
||||
public static class layout {
|
||||
public final static int mainActivity = 100;
|
||||
}
|
||||
}
|
||||
|
||||
//FILE: app/B.java
|
||||
package app;
|
||||
|
||||
public class B {
|
||||
public static class id {
|
||||
public final static int textView = 200;
|
||||
}
|
||||
}
|
||||
|
||||
//FILE: test.kt
|
||||
package app
|
||||
|
||||
import lib.R as LibR
|
||||
import lib.R.id.textView
|
||||
|
||||
annotation class Bind(val id: Int)
|
||||
|
||||
class MyActivity {
|
||||
@Bind(LibR.id.textView)
|
||||
fun foo() {}
|
||||
|
||||
@Bind(lib.R.id.textView)
|
||||
fun foo2() {}
|
||||
|
||||
@Bind(app.R.layout.mainActivity)
|
||||
fun foo3() {}
|
||||
|
||||
@Bind(R.layout.mainActivity)
|
||||
fun foo4() {}
|
||||
|
||||
@Bind(B.id.textView)
|
||||
fun notResource() {}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package app;
|
||||
|
||||
@kotlin.Metadata()
|
||||
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||
public abstract @interface Bind {
|
||||
|
||||
public abstract int id();
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
package app;
|
||||
|
||||
@kotlin.Metadata()
|
||||
public final class MyActivity {
|
||||
|
||||
@Bind(id = lib.R.id.textView)
|
||||
public final void foo() {
|
||||
}
|
||||
|
||||
@Bind(id = lib.R.id.textView)
|
||||
public final void foo2() {
|
||||
}
|
||||
|
||||
@Bind(id = app.R.layout.mainActivity)
|
||||
public final void foo3() {
|
||||
}
|
||||
|
||||
@Bind(id = app.R.layout.mainActivity)
|
||||
public final void foo4() {
|
||||
}
|
||||
|
||||
@Bind(id = 200)
|
||||
public final void notResource() {
|
||||
}
|
||||
|
||||
public MyActivity() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user