Kapt: Drop custom annotation stub factory, implement it with AnnotationParser in sun.reflect as done in Javac

This commit is contained in:
Yan Zhulanow
2016-06-23 20:19:15 +03:00
committed by Yan Zhulanow
parent 0a684aa1d1
commit 82dcbf36e5
5 changed files with 239 additions and 276 deletions

View File

@@ -8,7 +8,6 @@
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="intellij-core" level="project" />
<orderEntry type="library" name="asm" level="project" />
<orderEntry type="module" module-name="light-classes" />
<orderEntry type="module" module-name="frontend" />
<orderEntry type="module" module-name="annotation-processing" />

View File

@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.java.model
import com.intellij.psi.PsiAnnotationOwner
import com.intellij.psi.PsiClass
import org.jetbrains.kotlin.java.model.elements.JeAnnotationMirror
import org.jetbrains.kotlin.java.model.internal.createAnnotation
import org.jetbrains.kotlin.java.model.internal.KotlinAnnotationProxyMaker
import javax.lang.model.element.AnnotationMirror
import javax.lang.model.element.Element
import java.lang.reflect.Array as RArray
@@ -30,14 +30,18 @@ interface JeAnnotationOwner : Element {
override fun getAnnotationMirrors() = annotationOwner?.annotations?.map { JeAnnotationMirror(it) } ?: emptyList()
override fun <A : Annotation> getAnnotation(annotationClass: Class<A>): A? {
if (!annotationClass.isAnnotation) {
throw IllegalArgumentException("Not an annotation class: " + annotationClass)
}
val annotationFqName = annotationClass.canonicalName
val annotation = annotationOwner?.annotations
?.firstOrNull { it.qualifiedName == annotationFqName } ?: return null
val annotationDeclaration = annotation.nameReferenceElement?.resolve() as? PsiClass ?: return null
@Suppress("UNCHECKED_CAST")
return createAnnotation(annotation, annotationDeclaration, annotationClass) as? A
return KotlinAnnotationProxyMaker(annotation, annotationDeclaration, annotationClass).generate() as? A
}
@Suppress("UNCHECKED_CAST")

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.java.model.internal;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiType;
import sun.reflect.annotation.EnumConstantNotPresentExceptionProxy;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public final class AnnotationUtil {
public static final Map<PsiType, Class<?>> ARRAY_TYPES_MAP;
static {
Map<PsiType, Class<?>> classes = new HashMap<PsiType, Class<?>>();
classes.put(PsiType.BYTE, byte.class);
classes.put(PsiType.SHORT, short.class);
classes.put(PsiType.INT, int.class);
classes.put(PsiType.CHAR, char.class);
classes.put(PsiType.BOOLEAN, boolean.class);
classes.put(PsiType.LONG, long.class);
classes.put(PsiType.FLOAT, float.class);
classes.put(PsiType.DOUBLE, double.class);
ARRAY_TYPES_MAP = Collections.unmodifiableMap(classes);
}
public static Object createEnumValue(Class<?> enumClass, String name) {
try {
return Enum.valueOf((Class)enumClass, name);
} catch (IllegalArgumentException ex) {
//noinspection unchecked
return new EnumConstantNotPresentExceptionProxy((Class<Enum<?>>) enumClass, name);
}
}
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.java.model.internal
import com.intellij.psi.*
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotation
import org.jetbrains.kotlin.java.model.types.toJeType
import sun.reflect.annotation.AnnotationParser
import sun.reflect.annotation.ExceptionProxy
import java.io.ObjectInputStream
import java.lang.reflect.Array
import java.lang.reflect.Method
import java.util.*
import javax.lang.model.type.MirroredTypeException
import javax.lang.model.type.MirroredTypesException
import javax.lang.model.type.TypeMirror
class KotlinAnnotationProxyMaker(val annotation: PsiAnnotation, val annotationClass: PsiClass, val annotationType: Class<out Annotation>) {
fun generate(): Any {
return AnnotationParser.annotationForMap(annotationType, getAllValuesForParser(getAllPsiValues()))
}
private fun getAllPsiValues(): List<Triple<PsiAnnotationMethod, PsiAnnotationMemberValue, Method>> {
val values = mutableListOf<Triple<PsiAnnotationMethod, PsiAnnotationMemberValue, Method>>()
for (method in annotationClass.methods) {
val jMethod = try { annotationType.getMethod(method.name) } catch (e : NoSuchMethodException) { continue }
if (method !is PsiAnnotationMethod) continue
if (method.returnType == null) continue
val value = annotation.findAttributeValue(method.name) ?: method.defaultValue ?: continue
values += Triple(method, value, jMethod)
}
return values
}
private fun getAllValuesForParser(values: List<Triple<PsiAnnotationMethod, PsiAnnotationMemberValue, Method>>): Map<String, Any?> {
val parserValues = mutableMapOf<String, Any?>()
val evaluator = JavaPsiFacade.getInstance(annotation.project).constantEvaluationHelper
for ((method, value, jMethod) in values) {
val jReturnType = jMethod.returnType ?: unexpectedType("no return type for ${jMethod.name}")
parserValues.put(method.name, getConstantValue(value, method.returnType!!, jReturnType, evaluator))
}
return parserValues
}
}
private fun getConstantValue(
psiValue: PsiAnnotationMemberValue,
returnType: PsiType,
jReturnType: Class<*>,
evaluator: PsiConstantEvaluationHelper
): Any? {
val manager = psiValue.manager
when {
returnType == PsiType.NULL || returnType == PsiType.VOID -> unexpectedType("void")
jReturnType == String::class.java -> return calculateConstantValue(psiValue, evaluator)
jReturnType == Class::class.java -> {
val type = (psiValue as PsiClassObjectAccessExpression).operand.type.toJeType(manager)
return MirroredTypeExceptionProxy(type)
}
jReturnType.isArray -> {
val jComponentType = jReturnType.componentType ?: unexpectedType("no component type for $jReturnType")
if (returnType !is PsiArrayType) unexpectedType(returnType)
val arrayValues = when (psiValue) {
is PsiArrayInitializerMemberValue -> psiValue.initializers.toList()
else -> listOf(psiValue)
}
if (jComponentType == Class::class.java) {
val typeMirrors = arrayValues.map { (it as PsiClassObjectAccessExpression).operand.type.toJeType(manager) }
return MirroredTypesExceptionProxy(Collections.unmodifiableList(typeMirrors))
} else {
val arr = Array.newInstance(jComponentType, arrayValues.size)
arrayValues.forEachIndexed { i, componentPsiValue ->
val componentValue = getConstantValue(componentPsiValue, returnType.componentType, jComponentType, evaluator)
try { Array.set(arr, i, componentValue) } catch (e: IllegalArgumentException) { return null }
}
return arr
}
}
jReturnType.isEnum -> {
val enumConstant = (psiValue.originalElement as? PsiReference)?.resolve() as? PsiEnumConstant
?: error("$psiValue can not be resolved to enum constant")
return AnnotationUtil.createEnumValue(jReturnType, enumConstant.name)
}
else -> return castPrimitiveValue(returnType, calculateConstantValue(psiValue, evaluator))
}
}
private fun castPrimitiveValue(type: PsiType, value: Any?): Any? = when (type) {
PsiType.BYTE -> byteValue(value)
PsiType.SHORT -> shortValue(value)
PsiType.INT -> intValue(value)
PsiType.CHAR -> charValue(value)
PsiType.BOOLEAN -> booleanValue(value)
PsiType.LONG -> longValue(value)
PsiType.FLOAT -> floatValue(value)
PsiType.DOUBLE -> doubleValue(value)
else -> unexpectedType(type)
}
private fun byteValue(value: Any?): Byte = (value as? Number)?.toByte() ?: 0
private fun intValue(value: Any?): Int = (value as? Number)?.toInt() ?: 0
private fun shortValue(value: Any?): Short = (value as? Number)?.toShort() ?: 0
private fun booleanValue(value: Any?): Boolean = if (value == true) true else false
private fun charValue(value: Any?): Char = value as? Char ?: 0.toChar()
private fun longValue(value: Any?): Long = (value as? Number)?.toLong() ?: 0
private fun floatValue(value: Any?): Float = (value as? Number)?.toFloat() ?: 0f
private fun doubleValue(value: Any?): Double = (value as? Number)?.toDouble() ?: 0.0
private fun calculateConstantValue(value: PsiAnnotationMemberValue?, evaluator: PsiConstantEvaluationHelper) = when (value) {
is PsiLiteral -> value.value
is KtLightAnnotation.LightExpressionValue<*> -> value.getConstantValue()
is PsiExpression -> evaluator.computeConstantExpression(value)
else -> null
}
private fun unexpectedType(type: String): Nothing = error("Unexpected type: $type")
private fun unexpectedType(type: PsiType): Nothing = unexpectedType(type.presentableText)
private class MirroredTypeExceptionProxy(@Transient private var type: TypeMirror?) : ExceptionProxy() {
private val typeString = type.toString()
@Suppress("IMPLICIT_CAST_TO_ANY")
override fun hashCode() = (if (type != null) type else typeString)?.hashCode() ?: 0
override fun equals(other: Any?) = type != null && other is MirroredTypeExceptionProxy && type == other.type
override fun toString() = typeString
override fun generateException() = MirroredTypeException(type)
// Explicitly set all transient fields.
private fun readObject(s: ObjectInputStream) {
s.defaultReadObject()
type = null
}
companion object {
private const val serialVersionUID: Long = 269
}
}
private class MirroredTypesExceptionProxy(@Transient private var types: List<TypeMirror>?) : ExceptionProxy() {
private val typeStrings: String = types.toString()
@Suppress("IMPLICIT_CAST_TO_ANY")
override fun hashCode() = (if (types != null) types else typeStrings)?.hashCode() ?: 0
override fun equals(other: Any?) = types != null && other is MirroredTypesExceptionProxy && types == other.types
override fun toString() = typeStrings
override fun generateException() = MirroredTypesException(types)
// Explicitly set all transient fields.
private fun readObject(s: ObjectInputStream) {
s.defaultReadObject()
types = null
}
companion object {
private const val serialVersionUID: Long = 269
}
}

View File

@@ -1,272 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.java.model.internal
import com.intellij.psi.*
import org.jetbrains.kotlin.asJava.KtLightAnnotation
import org.jetbrains.org.objectweb.asm.ClassWriter
import org.jetbrains.org.objectweb.asm.Opcodes.*
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import java.net.URL
import java.net.URLClassLoader
internal fun createAnnotation(
annotation: PsiAnnotation,
annotationDeclaration: PsiClass,
annotationClass: Class<*>
): Annotation? {
val qualifiedName = annotationDeclaration.qualifiedName ?: return null
val implQualifiedName = "stub." + qualifiedName + ".Impl" + Integer.toHexString(annotation.text.hashCode())
val annotationInternalName = annotationDeclaration.getInternalName()
val implInternalName = implQualifiedName.replace('.', '/')
val bytes = createAnnotationImplementationClass(annotation, annotationDeclaration, annotationInternalName, implInternalName)
return loadClass(implQualifiedName, bytes, annotationClass)?.newInstance() as? Annotation
}
private fun createAnnotationImplementationClass(
annotation: PsiAnnotation,
annotationDeclaration: PsiClass,
annotationInternalName: String,
implInternalName: String
): ByteArray {
return ClassWriter(ClassWriter.COMPUTE_FRAMES or ClassWriter.COMPUTE_MAXS).apply {
visit(V1_6, ACC_SUPER or ACC_PUBLIC, implInternalName, null, "java/lang/Object", arrayOf(annotationInternalName))
with(visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)) {
visitCode()
visitVarInsn(ALOAD, 0)
visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false)
visitInsn(RETURN)
visitMaxs(-1, -1)
visitEnd()
}
with(visitMethod(ACC_PUBLIC, "annotationType", "()Ljava/lang/Class;",
"()Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;", null)) {
visitCode()
visitLdcInsn(Type.getType("L$annotationInternalName;"))
visitInsn(ARETURN)
visitMaxs(-1, -1)
visitEnd()
}
if (annotationDeclaration.allMethods.isNotEmpty()) {
val evaluator = JavaPsiFacade.getInstance(annotation.project).constantEvaluationHelper
for (method in annotationDeclaration.methods) {
if (method !is PsiAnnotationMethod) continue
if (method.returnType == null) continue
val returnType = method.returnType!!
val value = annotation.findAttributeValue(method.name) ?: method.defaultValue
when (returnType) {
PsiType.VOID -> unexpectedType("void")
PsiType.NULL -> unexpectedType("null")
is PsiClassType -> {
val resolvedClass = returnType.resolve()
val resolvedQualifiedName = resolvedClass?.qualifiedName
if (resolvedQualifiedName == "java.lang.String") {
writeStringMethod(method, calculateConstantValue(value, evaluator))
}
else if (resolvedClass != null && resolvedClass.isEnum) {
writeEnumMethod(method, resolvedClass, value)
}
else {
unexpectedType("$resolvedQualifiedName")
}
}
is PsiPrimitiveType -> writeLiteralMethod(method, calculateConstantValue(value, evaluator))
is PsiArrayType -> writeArrayMethod(method, value, evaluator)
else -> unexpectedType(returnType)
}
}
}
visitEnd()
}.toByteArray()
}
private fun ClassWriter.writeEnumMethod(method: PsiAnnotationMethod, enumClass: PsiClass, value: PsiAnnotationMemberValue?) {
val enumInternalName = enumClass.getInternalName()
val enumConstant = (value?.originalElement as? PsiReference)?.resolve() as? PsiEnumConstant ?: return
val mv = visitMethod(ACC_PUBLIC, method.name, "()L$enumInternalName;", null, null)
with (InstructionAdapter(mv)) {
visitCode()
getstatic(enumInternalName, enumConstant.name!!, "L$enumInternalName;")
visitInsn(ARETURN)
visitMaxs(-1 ,-1)
visitEnd()
}
}
private fun ClassWriter.writeStringMethod(method: PsiAnnotationMethod, value: Any?) {
val stringValue = (value as? String) ?: null
val mv = visitMethod(ACC_PUBLIC, method.name, "()Ljava/lang/String;", null, null)
with (InstructionAdapter(mv)) {
visitCode()
if (stringValue != null) {
visitLdcInsn(stringValue)
} else {
visitInsn(ACONST_NULL)
}
visitInsn(ARETURN)
visitMaxs(-1 ,-1)
visitEnd()
}
}
private fun ClassWriter.writeArrayMethod(
method: PsiAnnotationMethod,
value: PsiAnnotationMemberValue?,
evaluator: PsiConstantEvaluationHelper
) {
val componentType = (method.returnType as? PsiArrayType)?.componentType ?: return
val componentAsmType = componentType.toAsmType() ?: return
val initializers = (value as? PsiArrayInitializerMemberValue)?.initializers ?: emptyArray()
val mv = visitMethod(ACC_PUBLIC, method.name, "()[" + componentAsmType.descriptor, null, null)
with (InstructionAdapter(mv)) {
visitCode()
iconst(initializers.size)
newarray(componentAsmType)
initializers.forEachIndexed { index, value ->
val valueObject = calculateConstantValue(value, evaluator)
dup()
iconst(index)
putValue(componentAsmType, valueObject)
astore(componentAsmType)
}
visitInsn(ARETURN)
visitMaxs(-1, -1)
visitEnd()
}
}
private fun calculateConstantValue(value: PsiAnnotationMemberValue?, evaluator: PsiConstantEvaluationHelper) = when (value) {
is PsiLiteral -> value.value
is KtLightAnnotation.LightExpressionValue<*> -> value.getConstantValue()
is PsiExpression -> evaluator.computeConstantExpression(value)
else -> null
}
private fun InstructionAdapter.putValue(type: Type, value: Any?) = when (type) {
Type.DOUBLE_TYPE -> dconst(doubleValue(value))
Type.FLOAT_TYPE -> fconst(floatValue(value))
Type.LONG_TYPE -> lconst(longValue(value))
Type.BYTE_TYPE, Type.SHORT_TYPE, Type.INT_TYPE, Type.CHAR_TYPE, Type.BOOLEAN_TYPE -> iconst(type.castValue(value))
else -> aconst(value)
}
private fun ClassWriter.writeLiteralMethod(method: PsiAnnotationMethod, value: Any?) {
val returnType = method.returnType ?: return
val asmType = returnType.toAsmType() ?: return
val mv = visitMethod(ACC_PUBLIC, method.name, "()" + asmType.descriptor, null, null)
with (InstructionAdapter(mv)) {
visitCode()
putValue(asmType, value)
areturn(asmType)
visitMaxs(-1, -1)
visitEnd()
}
}
private fun PsiType.toAsmType(): Type? = when (this) {
PsiType.BYTE -> Type.BYTE_TYPE
PsiType.SHORT -> Type.SHORT_TYPE
PsiType.INT -> Type.INT_TYPE
PsiType.CHAR -> Type.CHAR_TYPE
PsiType.BOOLEAN -> Type.BOOLEAN_TYPE
PsiType.LONG -> Type.LONG_TYPE
PsiType.FLOAT -> Type.FLOAT_TYPE
PsiType.DOUBLE -> Type.DOUBLE_TYPE
is PsiClassType -> resolve()?.let { Type.getObjectType(it.getInternalName()) }
else -> null
}
private fun Type.castValue(value: Any?): Int = when (this) {
Type.BYTE_TYPE -> byteValue(value)
Type.SHORT_TYPE -> shortValue(value)
Type.INT_TYPE -> intValue(value)
Type.CHAR_TYPE -> charValue(value)
Type.BOOLEAN_TYPE -> booleanValue(value)
else -> unexpectedType(this)
}
private fun PsiClass.getInternalName(): String {
val containingClass = this.containingClass
return if (containingClass != null) {
containingClass.getInternalName() + "$" + this.name
} else {
qualifiedName?.replace('.', '/') ?: error("Invalid class name ($this)")
}
}
private fun byteValue(value: Any?): Int = (value as? Number)?.toByte()?.toInt() ?: 0
private fun intValue(value: Any?): Int = (value as? Number)?.toInt() ?: 0
private fun shortValue(value: Any?): Int = (value as? Number)?.toShort()?.toInt() ?: 0
private fun booleanValue(value: Any?): Int = if (value == true) 1 else 0
private fun charValue(value: Any?): Int = (value as? Char)?.toInt() ?: 0.toChar().toInt()
private fun longValue(value: Any?): Long = (value as? Number)?.toLong() ?: 0
private fun floatValue(value: Any?): Float = (value as? Number)?.toFloat() ?: 0f
private fun doubleValue(value: Any?): Double = (value as? Number)?.toDouble() ?: 0.0
private fun loadClass(fqName: String, bytes: ByteArray, annotationClass: Class<*>): Class<*>? {
class ByteClassLoader(
urls: Array<out URL>?,
parent: ClassLoader?,
val extraClasses: MutableMap<String, ByteArray>,
predefinedClasses: List<Class<*>>
) : URLClassLoader(urls, parent) {
private val predefinedClasses = predefinedClasses.associateBy { it.canonicalName }
override fun findClass(name: String): Class<*>? {
return extraClasses.remove(name)?.let {
defineClass(name, it, 0, it.size)
} ?: predefinedClasses[name] ?: super.findClass(name)
}
}
val classLoader = ByteClassLoader(emptyArray(), annotationClass.classLoader, hashMapOf(fqName to bytes), listOf())
return Class.forName(fqName, false, classLoader)
}
private fun unexpectedType(type: String): Nothing = error("Unexpected type: $type")
private fun unexpectedType(type: PsiType): Nothing = unexpectedType(type.presentableText)
private fun unexpectedType(type: Type): Nothing = unexpectedType(type.descriptor)