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 com.sun.tools.javac.util.Names
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||||
import org.jetbrains.kotlin.kapt3.KaptContext
|
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.Type.*
|
import org.jetbrains.org.objectweb.asm.Type.*
|
||||||
import org.jetbrains.org.objectweb.asm.tree.ClassNode
|
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 {
|
fun FqName(internalOrFqName: String): JCTree.JCExpression {
|
||||||
val path = getQualifiedName(internalOrFqName).convertSpecialFqName().split('.')
|
val path = getQualifiedName(internalOrFqName).convertSpecialFqName().split('.')
|
||||||
assert(path.isNotEmpty())
|
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())
|
if (path.size == 1) return SimpleName(path.single())
|
||||||
|
|
||||||
var expr = Select(SimpleName(path[0]), name(path[1]))
|
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 com.sun.tools.javac.tree.TreeMaker
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
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.*
|
||||||
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
|
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
|
||||||
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
|
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.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
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.descriptorUtil.getSuperClassOrAny
|
||||||
|
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
@@ -202,7 +207,7 @@ class ClassFileToSourceStubConverter(
|
|||||||
|
|
||||||
val modifiers = convertModifiers(flags,
|
val modifiers = convertModifiers(flags,
|
||||||
if (isEnum) ElementKind.ENUM else ElementKind.CLASS,
|
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")
|
val isDefaultImpls = clazz.name.endsWith("${descriptor.name.asString()}\$DefaultImpls")
|
||||||
&& isPublic(clazz.access) && isFinal(clazz.access)
|
&& isPublic(clazz.access) && isFinal(clazz.access)
|
||||||
@@ -360,7 +365,8 @@ class ClassFileToSourceStubConverter(
|
|||||||
val origin = kaptContext.origins[field]
|
val origin = kaptContext.origins[field]
|
||||||
val descriptor = origin?.descriptor
|
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 name = getValidIdentifierName(field.name) ?: return null
|
||||||
val type = Type.getType(field.desc)
|
val type = Type.getType(field.desc)
|
||||||
|
|
||||||
@@ -410,7 +416,7 @@ class ClassFileToSourceStubConverter(
|
|||||||
(method.access.toLong() and VISIBILITY_MODIFIERS.inv())
|
(method.access.toLong() and VISIBILITY_MODIFIERS.inv())
|
||||||
else
|
else
|
||||||
method.access.toLong(),
|
method.access.toLong(),
|
||||||
ElementKind.METHOD, packageFqName, visibleAnnotations, method.invisibleAnnotations)
|
ElementKind.METHOD, packageFqName, visibleAnnotations, method.invisibleAnnotations, descriptor.annotations)
|
||||||
|
|
||||||
val asmReturnType = Type.getReturnType(method.desc)
|
val asmReturnType = Type.getReturnType(method.desc)
|
||||||
val jcReturnType = if (isConstructor) null else treeMaker.Type(asmReturnType)
|
val jcReturnType = if (isConstructor) null else treeMaker.Type(asmReturnType)
|
||||||
@@ -432,7 +438,8 @@ class ClassFileToSourceStubConverter(
|
|||||||
ElementKind.PARAMETER,
|
ElementKind.PARAMETER,
|
||||||
packageFqName,
|
packageFqName,
|
||||||
info.visibleAnnotations,
|
info.visibleAnnotations,
|
||||||
info.invisibleAnnotations)
|
info.invisibleAnnotations,
|
||||||
|
Annotations.EMPTY /* TODO */)
|
||||||
|
|
||||||
val name = treeMaker.name(getValidIdentifierName(info.name) ?: "p${index}_" + info.name.hashCode().ushr(1))
|
val name = treeMaker.name(getValidIdentifierName(info.name) ?: "p${index}_" + info.name.hashCode().ushr(1))
|
||||||
val type = treeMaker.Type(info.type)
|
val type = treeMaker.Type(info.type)
|
||||||
@@ -573,21 +580,28 @@ class ClassFileToSourceStubConverter(
|
|||||||
kind: ElementKind,
|
kind: ElementKind,
|
||||||
packageFqName: String,
|
packageFqName: String,
|
||||||
visibleAnnotations: List<AnnotationNode>?,
|
visibleAnnotations: List<AnnotationNode>?,
|
||||||
invisibleAnnotations: List<AnnotationNode>?
|
invisibleAnnotations: List<AnnotationNode>?,
|
||||||
): JCModifiers = convertModifiers(access.toLong(), kind, packageFqName, visibleAnnotations, invisibleAnnotations)
|
descriptorAnnotations: Annotations
|
||||||
|
): JCModifiers = convertModifiers(access.toLong(), kind, packageFqName, visibleAnnotations, invisibleAnnotations, descriptorAnnotations)
|
||||||
|
|
||||||
private fun convertModifiers(
|
private fun convertModifiers(
|
||||||
access: Long,
|
access: Long,
|
||||||
kind: ElementKind,
|
kind: ElementKind,
|
||||||
packageFqName: String,
|
packageFqName: String,
|
||||||
visibleAnnotations: List<AnnotationNode>?,
|
visibleAnnotations: List<AnnotationNode>?,
|
||||||
invisibleAnnotations: List<AnnotationNode>?
|
invisibleAnnotations: List<AnnotationNode>?,
|
||||||
|
descriptorAnnotations: Annotations
|
||||||
): JCModifiers {
|
): 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 ->
|
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()
|
} ?: JavacList.nil()
|
||||||
annotations = invisibleAnnotations?.fold(annotations) { list, anno ->
|
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
|
} ?: annotations
|
||||||
|
|
||||||
val flags = when (kind) {
|
val flags = when (kind) {
|
||||||
@@ -601,7 +615,12 @@ class ClassFileToSourceStubConverter(
|
|||||||
return treeMaker.Modifiers(flags, annotations)
|
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 annotationType = Type.getType(annotation.desc)
|
||||||
val fqName = treeMaker.getQualifiedName(annotationType)
|
val fqName = treeMaker.getQualifiedName(annotationType)
|
||||||
|
|
||||||
@@ -609,15 +628,62 @@ class ClassFileToSourceStubConverter(
|
|||||||
if (BLACKLISTED_ANNOTATIONS.any { fqName.startsWith(it) }) return null
|
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 useSimpleName = '.' in fqName && fqName.substringBeforeLast('.', "") == packageFqName
|
||||||
val name = if (useSimpleName) treeMaker.FqName(fqName.substring(packageFqName!!.length + 1)) else treeMaker.Type(annotationType)
|
val name = if (useSimpleName) treeMaker.FqName(fqName.substring(packageFqName!!.length + 1)) else treeMaker.Type(annotationType)
|
||||||
val values = mapPairedValuesJList<JCExpression>(annotation.values) { key, value ->
|
val values = mapPairedValuesJList<JCExpression>(annotation.values) { key, value ->
|
||||||
val name = getValidIdentifierName(key) ?: return@mapPairedValuesJList null
|
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)
|
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? {
|
private fun convertValueOfPrimitiveTypeOrString(value: Any?): JCExpression? {
|
||||||
return when (value) {
|
return when (value) {
|
||||||
is Char -> treeMaker.Literal(TypeTag.CHAR, value.toInt())
|
is Char -> treeMaker.Literal(TypeTag.CHAR, value.toInt())
|
||||||
|
|||||||
+6
@@ -192,6 +192,12 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("kt19700.kt")
|
||||||
public void testKt19700() throws Exception {
|
public void testKt19700() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/kt19700.kt");
|
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