Kapt: Drop custom annotation stub factory, implement it with AnnotationParser in sun.reflect as done in Javac
(cherry picked from commit 82dcbf3)
This commit is contained in:
committed by
Yan Zhulanow
parent
6eb3d7e002
commit
53921c3f74
@@ -8,7 +8,6 @@
|
|||||||
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
|
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="intellij-core" level="project" />
|
<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="light-classes" />
|
||||||
<orderEntry type="module" module-name="frontend" />
|
<orderEntry type="module" module-name="frontend" />
|
||||||
<orderEntry type="module" module-name="annotation-processing" />
|
<orderEntry type="module" module-name="annotation-processing" />
|
||||||
|
|||||||
+6
-2
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.java.model
|
|||||||
import com.intellij.psi.PsiAnnotationOwner
|
import com.intellij.psi.PsiAnnotationOwner
|
||||||
import com.intellij.psi.PsiClass
|
import com.intellij.psi.PsiClass
|
||||||
import org.jetbrains.kotlin.java.model.elements.JeAnnotationMirror
|
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.AnnotationMirror
|
||||||
import javax.lang.model.element.Element
|
import javax.lang.model.element.Element
|
||||||
import java.lang.reflect.Array as RArray
|
import java.lang.reflect.Array as RArray
|
||||||
@@ -30,6 +30,10 @@ interface JeAnnotationOwner : Element {
|
|||||||
override fun getAnnotationMirrors() = annotationOwner?.annotations?.map { JeAnnotationMirror(it) } ?: emptyList()
|
override fun getAnnotationMirrors() = annotationOwner?.annotations?.map { JeAnnotationMirror(it) } ?: emptyList()
|
||||||
|
|
||||||
override fun <A : Annotation> getAnnotation(annotationClass: Class<A>): A? {
|
override fun <A : Annotation> getAnnotation(annotationClass: Class<A>): A? {
|
||||||
|
if (!annotationClass.isAnnotation) {
|
||||||
|
throw IllegalArgumentException("Not an annotation class: " + annotationClass)
|
||||||
|
}
|
||||||
|
|
||||||
val annotationFqName = annotationClass.canonicalName
|
val annotationFqName = annotationClass.canonicalName
|
||||||
|
|
||||||
val annotation = annotationOwner?.annotations
|
val annotation = annotationOwner?.annotations
|
||||||
@@ -37,7 +41,7 @@ interface JeAnnotationOwner : Element {
|
|||||||
val annotationDeclaration = annotation.nameReferenceElement?.resolve() as? PsiClass ?: return null
|
val annotationDeclaration = annotation.nameReferenceElement?.resolve() as? PsiClass ?: return null
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return createAnnotation(annotation, annotationDeclaration, annotationClass) as? A
|
return KotlinAnnotationProxyMaker(annotation, annotationDeclaration, annotationClass).generate() as? A
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
|||||||
+54
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+178
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
-272
@@ -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)
|
|
||||||
Reference in New Issue
Block a user