Initial support of annotation loading at runtime
In order to locate an annotated entity, we need to implement almost the whole Java element model (which will be used anyway for Java descriptor loading)
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
// SKIP_IN_RUNTIME_TEST because there's no stable way to determine if a field is initialized with a non-null value in runtime
|
||||||
|
|
||||||
package test;
|
package test;
|
||||||
|
|
||||||
public interface StringConstantInParam {
|
public interface StringConstantInParam {
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
|
// SKIP_IN_RUNTIME_TEST because there's no stable way to determine if a field is initialized with a non-null value in runtime
|
||||||
|
|
||||||
package test;
|
package test;
|
||||||
|
|
||||||
public class StaticFinal {
|
public class StaticFinal {
|
||||||
public static final String foo = "aaa";
|
public static final String publicNonNull = "aaa";
|
||||||
|
public static final String publicNull = null;
|
||||||
|
static final String packageNonNull = "bbb";
|
||||||
|
static final String packageNull = null;
|
||||||
|
private static final String privateNonNull = "bbb";
|
||||||
|
private static final String privateNull = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,10 @@ public open class StaticFinal {
|
|||||||
public constructor StaticFinal()
|
public constructor StaticFinal()
|
||||||
|
|
||||||
// Static members
|
// Static members
|
||||||
public final val foo: kotlin.String = "aaa"
|
public/*package*/ final val packageNonNull: kotlin.String = "bbb"
|
||||||
|
public/*package*/ final val packageNull: kotlin.String!
|
||||||
|
private final val privateNonNull: kotlin.String = "bbb"
|
||||||
|
private final val privateNull: kotlin.String!
|
||||||
|
public final val publicNonNull: kotlin.String = "aaa"
|
||||||
|
public final val publicNull: kotlin.String!
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-5
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.jvm.runtime
|
package org.jetbrains.kotlin.jvm.runtime
|
||||||
|
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
|
||||||
import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAllTo
|
import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAllTo
|
||||||
import org.jetbrains.kotlin.codegen.GeneratedClassLoader
|
import org.jetbrains.kotlin.codegen.GeneratedClassLoader
|
||||||
import org.jetbrains.kotlin.codegen.GenerationUtils
|
import org.jetbrains.kotlin.codegen.GenerationUtils
|
||||||
@@ -64,11 +63,13 @@ public abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdi
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: this test does a dirty hack of text substitution to make all annotations defined in source code retain at runtime.
|
||||||
|
// Specifically each "annotation class" is replaced by "Retention(RUNTIME) annotation class"
|
||||||
protected fun doTest(ktFileName: String) {
|
protected fun doTest(ktFileName: String) {
|
||||||
val ktFile = File(ktFileName)
|
val ktFile = File(ktFileName)
|
||||||
|
|
||||||
val environment = JetTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(myTestRootDisposable, ConfigurationKind.ALL)
|
val environment = JetTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(myTestRootDisposable, ConfigurationKind.ALL)
|
||||||
val jetFile = JetTestUtils.createFile(ktFileName, FileUtil.loadFile(ktFile, true), environment.getProject())
|
val jetFile = JetTestUtils.createFile(ktFileName, loadFileAddingRuntimeRetention(ktFile), environment.getProject())
|
||||||
val classFileFactory = GenerationUtils.compileFileGetClassFileFactoryForTest(jetFile)
|
val classFileFactory = GenerationUtils.compileFileGetClassFileFactoryForTest(jetFile)
|
||||||
val classLoader = GeneratedClassLoader(classFileFactory, null, ForTestCompileRuntime.runtimeJarForTests().toURI().toURL())
|
val classLoader = GeneratedClassLoader(classFileFactory, null, ForTestCompileRuntime.runtimeJarForTests().toURI().toURL())
|
||||||
|
|
||||||
@@ -103,7 +104,7 @@ public abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdi
|
|||||||
|
|
||||||
val klass = classLoader.loadClass(className).sure("Couldn't load class $className")
|
val klass = classLoader.loadClass(className).sure("Couldn't load class $className")
|
||||||
|
|
||||||
when (ReflectKotlinClass(klass).getClassHeader().kind) {
|
when (ReflectKotlinClass.create(klass)!!.getClassHeader().kind) {
|
||||||
KotlinClassHeader.Kind.PACKAGE_FACADE -> {
|
KotlinClassHeader.Kind.PACKAGE_FACADE -> {
|
||||||
val packageView = module.getPackage(actual.getFqName()) ?: error("Couldn't resolve package ${actual.getFqName()}")
|
val packageView = module.getPackage(actual.getFqName()) ?: error("Couldn't resolve package ${actual.getFqName()}")
|
||||||
for (descriptor in packageView.getMemberScope().getAllDescriptors()) {
|
for (descriptor in packageView.getMemberScope().getAllDescriptors()) {
|
||||||
@@ -125,13 +126,21 @@ public abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdi
|
|||||||
|
|
||||||
val expected = LoadDescriptorUtil.loadTestPackageAndBindingContextFromJavaRoot(tmpdir, getTestRootDisposable(),
|
val expected = LoadDescriptorUtil.loadTestPackageAndBindingContextFromJavaRoot(tmpdir, getTestRootDisposable(),
|
||||||
ConfigurationKind.ALL)
|
ConfigurationKind.ALL)
|
||||||
val configuration = RecursiveDescriptorComparator.DONT_INCLUDE_METHODS_OF_OBJECT
|
val comparatorConfiguration = RecursiveDescriptorComparator.DONT_INCLUDE_METHODS_OF_OBJECT
|
||||||
.checkPrimaryConstructors(true)
|
.checkPrimaryConstructors(true)
|
||||||
.checkPropertyAccessors(true)
|
.checkPropertyAccessors(true)
|
||||||
.withRenderer(renderer)
|
.withRenderer(renderer)
|
||||||
RecursiveDescriptorComparator.validateAndCompareDescriptors(expected.first, actual, configuration, null)
|
RecursiveDescriptorComparator.validateAndCompareDescriptors(expected.first, actual, comparatorConfiguration, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadFileAddingRuntimeRetention(file: File): String {
|
||||||
|
return file.readText().replace(
|
||||||
|
"annotation class",
|
||||||
|
"[java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)] annotation class"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolves not only top level classes, but also nested classes, including class objects and classes nested within them
|
||||||
private fun resolveClassByFqNameInModule(module: ModuleDescriptor, fqName: FqNameUnsafe): ClassDescriptor? {
|
private fun resolveClassByFqNameInModule(module: ModuleDescriptor, fqName: FqNameUnsafe): ClassDescriptor? {
|
||||||
if (fqName.isRoot()) return null
|
if (fqName.isRoot()) return null
|
||||||
|
|
||||||
|
|||||||
+2
@@ -18,11 +18,13 @@ package org.jetbrains.kotlin.load.java.structure;
|
|||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.annotations.ReadOnly;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface JavaMethod extends JavaMember, JavaTypeParameterListOwner {
|
public interface JavaMethod extends JavaMember, JavaTypeParameterListOwner {
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@ReadOnly
|
||||||
List<JavaValueParameter> getValueParameters();
|
List<JavaValueParameter> getValueParameters();
|
||||||
|
|
||||||
boolean hasAnnotationParameterDefaultValue();
|
boolean hasAnnotationParameterDefaultValue();
|
||||||
|
|||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 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.load.java.structure.reflect
|
||||||
|
|
||||||
|
import java.lang.reflect.GenericArrayType
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaArrayType
|
||||||
|
|
||||||
|
public class ReflectJavaArrayType(private val arrayType: GenericArrayType) : ReflectJavaType(), JavaArrayType {
|
||||||
|
override fun getComponentType() = ReflectJavaType.create(arrayType.getGenericComponentType()!!)
|
||||||
|
}
|
||||||
+7
-13
@@ -23,10 +23,10 @@ import org.jetbrains.kotlin.name.Name
|
|||||||
import org.jetbrains.kotlin.utils.emptyOrSingletonList
|
import org.jetbrains.kotlin.utils.emptyOrSingletonList
|
||||||
|
|
||||||
public class ReflectJavaClass(private val klass: Class<*>) : ReflectJavaElement(), JavaClass {
|
public class ReflectJavaClass(private val klass: Class<*>) : ReflectJavaElement(), JavaClass {
|
||||||
override fun getInnerClasses(): Collection<JavaClass> {
|
override fun getInnerClasses() =
|
||||||
// TODO
|
klass.getDeclaredClasses()
|
||||||
return listOf()
|
.filter { it.getSimpleName().isNotEmpty() }
|
||||||
}
|
.map { ReflectJavaClass(it) }
|
||||||
|
|
||||||
override fun getFqName(): FqName? {
|
override fun getFqName(): FqName? {
|
||||||
// TODO: can there be primitive types, arrays?
|
// TODO: can there be primitive types, arrays?
|
||||||
@@ -41,18 +41,12 @@ public class ReflectJavaClass(private val klass: Class<*>) : ReflectJavaElement(
|
|||||||
override fun getSupertypes(): Collection<JavaClassifierType> {
|
override fun getSupertypes(): Collection<JavaClassifierType> {
|
||||||
// TODO: also call getSuperclass() / getInterfaces() for classes without generic signature
|
// TODO: also call getSuperclass() / getInterfaces() for classes without generic signature
|
||||||
val supertypes = emptyOrSingletonList(klass.getGenericSuperclass()) + klass.getGenericInterfaces()
|
val supertypes = emptyOrSingletonList(klass.getGenericSuperclass()) + klass.getGenericInterfaces()
|
||||||
return supertypes.map { supertype -> ReflectJavaClassifierType(supertype) }
|
return supertypes.map { supertype -> ReflectJavaType.create(supertype) as JavaClassifierType }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMethods(): Collection<JavaMethod> {
|
override fun getMethods() = klass.getDeclaredMethods().map { method -> ReflectJavaMethod(method) }
|
||||||
// TODO
|
|
||||||
return listOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getFields(): Collection<JavaField> {
|
override fun getFields() = klass.getDeclaredFields().map { field -> ReflectJavaField(field) }
|
||||||
// TODO
|
|
||||||
return listOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getConstructors(): Collection<JavaConstructor> {
|
override fun getConstructors(): Collection<JavaConstructor> {
|
||||||
// TODO
|
// TODO
|
||||||
|
|||||||
+4
-3
@@ -29,9 +29,8 @@ public class ReflectJavaClassifierType(val classifierType: Type) : ReflectJavaTy
|
|||||||
return when (classifierType) {
|
return when (classifierType) {
|
||||||
is Class<*> -> ReflectJavaClass(classifierType)
|
is Class<*> -> ReflectJavaClass(classifierType)
|
||||||
is TypeVariable<*> -> ReflectJavaTypeParameter(classifierType)
|
is TypeVariable<*> -> ReflectJavaTypeParameter(classifierType)
|
||||||
// TODO
|
|
||||||
is ParameterizedType -> ReflectJavaClass(classifierType.getRawType() as Class<*>)
|
is ParameterizedType -> ReflectJavaClass(classifierType.getRawType() as Class<*>)
|
||||||
else -> throw UnsupportedOperationException("Unsupported type (${classifierType.javaClass}): $classifierType")
|
else -> throw IllegalStateException("Not a classifier type (${classifierType.javaClass}): $classifierType")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +43,9 @@ public class ReflectJavaClassifierType(val classifierType: Type) : ReflectJavaTy
|
|||||||
override fun isRaw(): Boolean = with(classifierType) { this is Class<*> && getTypeParameters().isNotEmpty() }
|
override fun isRaw(): Boolean = with(classifierType) { this is Class<*> && getTypeParameters().isNotEmpty() }
|
||||||
|
|
||||||
override fun getTypeArguments(): List<JavaType> {
|
override fun getTypeArguments(): List<JavaType> {
|
||||||
// TODO
|
if (classifierType is ParameterizedType) {
|
||||||
|
return classifierType.getActualTypeArguments()!!.map { ReflectJavaType.create(it) }
|
||||||
|
}
|
||||||
return listOf()
|
return listOf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 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.load.java.structure.reflect
|
||||||
|
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaField
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaAnnotation
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaType
|
||||||
|
|
||||||
|
public class ReflectJavaField(field: Field) : ReflectJavaMember(field), JavaField {
|
||||||
|
val field: Field
|
||||||
|
get() = member as Field
|
||||||
|
|
||||||
|
override fun getAnnotations(): Collection<JavaAnnotation> {
|
||||||
|
// TODO
|
||||||
|
return listOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findAnnotation(fqName: FqName): JavaAnnotation? {
|
||||||
|
// TODO
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isEnumEntry() = field.isEnumConstant()
|
||||||
|
|
||||||
|
override fun getType(): JavaType {
|
||||||
|
// TODO
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 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.load.java.structure.reflect
|
||||||
|
|
||||||
|
import java.lang.reflect.Member
|
||||||
|
import java.lang.reflect.Modifier
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaMember
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.name.SpecialNames
|
||||||
|
|
||||||
|
public abstract class ReflectJavaMember(protected val member: Member) : ReflectJavaElement(), JavaMember {
|
||||||
|
override fun getName() = member.getName()?.let { Name.identifier(it) } ?: SpecialNames.NO_NAME_PROVIDED
|
||||||
|
|
||||||
|
override fun getContainingClass() = ReflectJavaClass(member.getDeclaringClass())
|
||||||
|
|
||||||
|
override fun isAbstract() = Modifier.isAbstract(member.getModifiers())
|
||||||
|
override fun isStatic() = Modifier.isStatic(member.getModifiers())
|
||||||
|
override fun isFinal() = Modifier.isFinal(member.getModifiers())
|
||||||
|
|
||||||
|
override fun getVisibility() = calculateVisibility(member.getModifiers())
|
||||||
|
}
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 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.load.java.structure.reflect
|
||||||
|
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
import java.util.ArrayList
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaMethod
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaAnnotation
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaValueParameter
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaType
|
||||||
|
|
||||||
|
public class ReflectJavaMethod(method: Method) : ReflectJavaMember(method), JavaMethod {
|
||||||
|
private val method: Method
|
||||||
|
get() = member as Method
|
||||||
|
|
||||||
|
override fun getAnnotations(): Collection<JavaAnnotation> {
|
||||||
|
// TODO
|
||||||
|
return listOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findAnnotation(fqName: FqName): JavaAnnotation? {
|
||||||
|
// TODO
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getValueParameters(): List<JavaValueParameter> {
|
||||||
|
val types = method.getGenericParameterTypes()!!
|
||||||
|
val annotations = method.getParameterAnnotations()
|
||||||
|
val result = ArrayList<JavaValueParameter>()
|
||||||
|
val methodIsVararg = method.isVarArgs()
|
||||||
|
for (i in types.indices) {
|
||||||
|
val isVararg = methodIsVararg && i == types.lastIndex
|
||||||
|
result.add(ReflectJavaValueParameter(ReflectJavaType.create(types[i]), annotations[i], isVararg))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getReturnType(): JavaType? {
|
||||||
|
// TODO
|
||||||
|
return ReflectJavaType.create(method.getGenericReturnType()!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasAnnotationParameterDefaultValue() = method.getDefaultValue() != null
|
||||||
|
|
||||||
|
override fun getTypeParameters() = method.getTypeParameters().map { ReflectJavaTypeParameter(it) }
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 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.load.java.structure.reflect
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaPrimitiveType
|
||||||
|
|
||||||
|
public class ReflectJavaPrimitiveType(private val klass: Class<*>): ReflectJavaType(), JavaPrimitiveType {
|
||||||
|
override fun getCanonicalText() = klass.getName()
|
||||||
|
}
|
||||||
+20
-1
@@ -16,9 +16,28 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.load.java.structure.reflect
|
package org.jetbrains.kotlin.load.java.structure.reflect
|
||||||
|
|
||||||
import org.jetbrains.kotlin.load.java.structure.JavaType
|
|
||||||
import org.jetbrains.kotlin.load.java.structure.JavaArrayType
|
import org.jetbrains.kotlin.load.java.structure.JavaArrayType
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaType
|
||||||
|
import java.lang.reflect.*
|
||||||
|
|
||||||
|
private val PRIMITIVE_TYPES = setOf(
|
||||||
|
java.lang.Integer.TYPE, java.lang.Character.TYPE, java.lang.Byte.TYPE, java.lang.Long.TYPE,
|
||||||
|
java.lang.Short.TYPE, java.lang.Boolean.TYPE, java.lang.Double.TYPE, java.lang.Float.TYPE,
|
||||||
|
java.lang.Void.TYPE
|
||||||
|
)
|
||||||
|
|
||||||
public abstract class ReflectJavaType : JavaType {
|
public abstract class ReflectJavaType : JavaType {
|
||||||
override fun createArrayType(): JavaArrayType = throw UnsupportedOperationException()
|
override fun createArrayType(): JavaArrayType = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
class object {
|
||||||
|
fun create(reflectType: Type): ReflectJavaType {
|
||||||
|
return when (reflectType) {
|
||||||
|
in PRIMITIVE_TYPES -> ReflectJavaPrimitiveType(reflectType as Class<*>)
|
||||||
|
is Class<*>, is TypeVariable<*>, is ParameterizedType -> ReflectJavaClassifierType(reflectType)
|
||||||
|
is GenericArrayType -> ReflectJavaArrayType(reflectType)
|
||||||
|
is WildcardType -> ReflectJavaWildcardType(reflectType)
|
||||||
|
else -> throw UnsupportedOperationException("Unsupported type (${reflectType.javaClass}): $reflectType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-2
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.load.java.structure.JavaType
|
|||||||
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
|
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
|
||||||
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner
|
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import java.lang.reflect.Method
|
||||||
import java.lang.reflect.TypeVariable
|
import java.lang.reflect.TypeVariable
|
||||||
|
|
||||||
public class ReflectJavaTypeParameter(
|
public class ReflectJavaTypeParameter(
|
||||||
@@ -32,8 +33,12 @@ public class ReflectJavaTypeParameter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getOwner(): JavaTypeParameterListOwner? {
|
override fun getOwner(): JavaTypeParameterListOwner? {
|
||||||
// TODO
|
val owner = typeVariable.getGenericDeclaration()
|
||||||
throw UnsupportedOperationException()
|
return when (owner) {
|
||||||
|
is Method -> ReflectJavaMethod(owner)
|
||||||
|
is Class<*> -> ReflectJavaClass(owner)
|
||||||
|
else -> throw UnsupportedOperationException("Unsupported type parameter list owner (${owner.javaClass}): $owner")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getType(): JavaType {
|
override fun getType(): JavaType {
|
||||||
|
|||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 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.load.java.structure.reflect
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaValueParameter
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaAnnotation
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
|
||||||
|
public class ReflectJavaValueParameter(
|
||||||
|
private val returnType: ReflectJavaType,
|
||||||
|
private val annotations: Array<Annotation>,
|
||||||
|
private val isVararg: Boolean
|
||||||
|
) : ReflectJavaElement(), JavaValueParameter {
|
||||||
|
override fun getAnnotations(): Collection<JavaAnnotation> {
|
||||||
|
// TODO
|
||||||
|
return listOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findAnnotation(fqName: FqName): JavaAnnotation? {
|
||||||
|
// TODO
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getName() = null // TODO: use ParameterNames on JDK 8
|
||||||
|
override fun getType() = returnType
|
||||||
|
override fun isVararg() = isVararg
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 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.load.java.structure.reflect
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaWildcardType
|
||||||
|
import java.lang.reflect.WildcardType
|
||||||
|
|
||||||
|
public class ReflectJavaWildcardType(private val wildcardType: WildcardType): ReflectJavaType(), JavaWildcardType {
|
||||||
|
override fun getBound(): ReflectJavaType? {
|
||||||
|
val upperBounds = wildcardType.getUpperBounds()
|
||||||
|
val lowerBounds = wildcardType.getLowerBounds()
|
||||||
|
if (upperBounds.size() > 1 || lowerBounds.size() > 1) {
|
||||||
|
throw UnsupportedOperationException("Wildcard types with many bounds are not yet supported: $wildcardType")
|
||||||
|
}
|
||||||
|
return when {
|
||||||
|
lowerBounds.size() == 1 -> ReflectJavaType.create(lowerBounds.single())
|
||||||
|
upperBounds.size() == 1 -> upperBounds.single().let { ub -> if (ub != javaClass<Any>()) ReflectJavaType.create(ub) else null }
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isExtends() = wildcardType.getUpperBounds().firstOrNull() != javaClass<Any>()
|
||||||
|
|
||||||
|
override fun getTypeProvider() = ReflectJavaTypeProvider
|
||||||
|
}
|
||||||
+185
-20
@@ -16,40 +16,205 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.load.kotlin.reflect
|
package org.jetbrains.kotlin.load.kotlin.reflect
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.reflect.classId
|
||||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
|
||||||
import org.jetbrains.kotlin.load.java.structure.reflect.classId
|
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.ReadKotlinClassHeaderAnnotationVisitor
|
import org.jetbrains.kotlin.load.kotlin.header.ReadKotlinClassHeaderAnnotationVisitor
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import java.lang.reflect.Constructor
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
|
||||||
public class ReflectKotlinClass(private val klass: Class<*>) : KotlinJvmBinaryClass {
|
suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||||
private val classHeader: KotlinClassHeader
|
private val TYPES_ELIGIBLE_FOR_SIMPLE_VISIT = setOf(
|
||||||
|
// Primitives
|
||||||
|
javaClass<java.lang.Integer>(), javaClass<java.lang.Character>(), javaClass<java.lang.Byte>(), javaClass<java.lang.Long>(),
|
||||||
|
javaClass<java.lang.Short>(), javaClass<java.lang.Boolean>(), javaClass<java.lang.Double>(), javaClass<java.lang.Float>(),
|
||||||
|
// Arrays of primitives
|
||||||
|
javaClass<IntArray>(), javaClass<CharArray>(), javaClass<ByteArray>(), javaClass<LongArray>(),
|
||||||
|
javaClass<ShortArray>(), javaClass<BooleanArray>(), javaClass<DoubleArray>(), javaClass<FloatArray>(),
|
||||||
|
// Others
|
||||||
|
javaClass<Class<*>>(), javaClass<String>()
|
||||||
|
)
|
||||||
|
|
||||||
{
|
public class ReflectKotlinClass private(
|
||||||
val headerReader = ReadKotlinClassHeaderAnnotationVisitor()
|
private val klass: Class<*>,
|
||||||
loadClassAnnotations(headerReader)
|
private val classHeader: KotlinClassHeader
|
||||||
val header = headerReader.createHeader()
|
) : KotlinJvmBinaryClass {
|
||||||
|
default object Factory {
|
||||||
if (header == null) {
|
public fun create(klass: Class<*>): ReflectKotlinClass? {
|
||||||
// This exception must be caught in the caller
|
val headerReader = ReadKotlinClassHeaderAnnotationVisitor()
|
||||||
throw NotAKotlinClass()
|
ReflectClassStructure.loadClassAnnotations(klass, headerReader)
|
||||||
|
return ReflectKotlinClass(klass, headerReader.createHeader() ?: return null)
|
||||||
}
|
}
|
||||||
|
|
||||||
classHeader = header
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class NotAKotlinClass : RuntimeException()
|
override fun getClassId() = klass.classId
|
||||||
|
|
||||||
override fun getClassId(): ClassId = klass.classId
|
override fun getClassHeader() = classHeader
|
||||||
|
|
||||||
override fun loadClassAnnotations(visitor: KotlinJvmBinaryClass.AnnotationVisitor) {
|
override fun loadClassAnnotations(visitor: KotlinJvmBinaryClass.AnnotationVisitor) {
|
||||||
// TODO
|
ReflectClassStructure.loadClassAnnotations(klass, visitor)
|
||||||
visitor.visitEnd()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitMembers(visitor: KotlinJvmBinaryClass.MemberVisitor) {
|
override fun visitMembers(visitor: KotlinJvmBinaryClass.MemberVisitor) {
|
||||||
// TODO
|
ReflectClassStructure.visitMembers(klass, visitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object ReflectClassStructure {
|
||||||
|
fun loadClassAnnotations(klass: Class<*>, visitor: KotlinJvmBinaryClass.AnnotationVisitor) {
|
||||||
|
for (annotation in klass.getDeclaredAnnotations()) {
|
||||||
|
processAnnotation(visitor, annotation)
|
||||||
|
}
|
||||||
|
visitor.visitEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getClassHeader() = classHeader
|
fun visitMembers(klass: Class<*>, memberVisitor: KotlinJvmBinaryClass.MemberVisitor) {
|
||||||
|
loadMethodAnnotations(klass, memberVisitor)
|
||||||
|
loadConstructorAnnotations(klass, memberVisitor)
|
||||||
|
loadFieldAnnotations(klass, memberVisitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadMethodAnnotations(klass: Class<*>, memberVisitor: KotlinJvmBinaryClass.MemberVisitor) {
|
||||||
|
for (method in klass.getDeclaredMethods()) {
|
||||||
|
val visitor = memberVisitor.visitMethod(Name.identifier(method.getName()), SignatureSerializer.methodDesc(method)) ?: continue
|
||||||
|
|
||||||
|
for (annotation in method.getDeclaredAnnotations()) {
|
||||||
|
processAnnotation(visitor, annotation)
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((parameterIndex, annotations) in method.getParameterAnnotations().withIndex()) {
|
||||||
|
for (annotation in annotations) {
|
||||||
|
val annotationType = annotation.annotationType()
|
||||||
|
visitor.visitParameterAnnotation(parameterIndex, annotationType.classId)?.let {
|
||||||
|
processAnnotationArguments(it, annotation, annotationType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visitor.visitEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadConstructorAnnotations(klass: Class<*>, memberVisitor: KotlinJvmBinaryClass.MemberVisitor) {
|
||||||
|
for (constructor in klass.getDeclaredConstructors()) {
|
||||||
|
// TODO: load annotations on constructors
|
||||||
|
val visitor = memberVisitor.visitMethod(Name.special("<init>"), SignatureSerializer.constructorDesc(constructor)) ?: continue
|
||||||
|
|
||||||
|
// Constructors of enums have 2 additional synthetic parameters
|
||||||
|
// TODO: the similar logic should probably be present for annotations on parameters of inner class constructors
|
||||||
|
val shift = if (klass.isEnum()) 2 else 0
|
||||||
|
for ((parameterIndex, annotations) in constructor.getParameterAnnotations().withIndex()) {
|
||||||
|
for (annotation in annotations) {
|
||||||
|
val annotationType = annotation.annotationType()
|
||||||
|
visitor.visitParameterAnnotation(parameterIndex + shift, annotationType.classId)?.let {
|
||||||
|
processAnnotationArguments(it, annotation, annotationType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadFieldAnnotations(klass: Class<*>, memberVisitor: KotlinJvmBinaryClass.MemberVisitor) {
|
||||||
|
for (field in klass.getDeclaredFields()) {
|
||||||
|
val visitor = memberVisitor.visitField(Name.identifier(field.getName()), SignatureSerializer.fieldDesc(field), null) ?: continue
|
||||||
|
|
||||||
|
for (annotation in field.getDeclaredAnnotations()) {
|
||||||
|
processAnnotation(visitor, annotation)
|
||||||
|
}
|
||||||
|
|
||||||
|
visitor.visitEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processAnnotation(visitor: KotlinJvmBinaryClass.AnnotationVisitor, annotation: Annotation) {
|
||||||
|
val annotationType = annotation.annotationType()
|
||||||
|
visitor.visitAnnotation(annotationType.classId)?.let {
|
||||||
|
processAnnotationArguments(it, annotation, annotationType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processAnnotationArguments(
|
||||||
|
visitor: KotlinJvmBinaryClass.AnnotationArgumentVisitor,
|
||||||
|
annotation: Annotation,
|
||||||
|
annotationType: Class<*>
|
||||||
|
) {
|
||||||
|
for (method in annotationType.getDeclaredMethods()) {
|
||||||
|
processAnnotationArgumentValue(visitor, Name.identifier(method.getName()), method(annotation))
|
||||||
|
}
|
||||||
|
visitor.visitEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processAnnotationArgumentValue(visitor: KotlinJvmBinaryClass.AnnotationArgumentVisitor, name: Name, value: Any) {
|
||||||
|
val clazz = value.javaClass
|
||||||
|
when {
|
||||||
|
clazz in TYPES_ELIGIBLE_FOR_SIMPLE_VISIT -> {
|
||||||
|
visitor.visit(name, value)
|
||||||
|
}
|
||||||
|
javaClass<Enum<*>>().isAssignableFrom(clazz) -> {
|
||||||
|
visitor.visitEnum(name, clazz.classId, Name.identifier(value.toString()))
|
||||||
|
}
|
||||||
|
javaClass<Annotation>().isAssignableFrom(clazz) -> {
|
||||||
|
// TODO: support values of annotation types
|
||||||
|
throw UnsupportedOperationException("Values of annotation types are not yet supported in Kotlin reflection: $value")
|
||||||
|
}
|
||||||
|
clazz.isArray() -> {
|
||||||
|
val elementVisitor = visitor.visitArray(name) ?: return
|
||||||
|
val componentType = clazz.getComponentType()
|
||||||
|
if (javaClass<Enum<*>>().isAssignableFrom(componentType)) {
|
||||||
|
val componentClassName = componentType.classId
|
||||||
|
for (element in value as Array<*>) {
|
||||||
|
elementVisitor.visitEnum(componentClassName, Name.identifier(element.toString()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (element in value as Array<*>) {
|
||||||
|
elementVisitor.visit(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elementVisitor.visitEnd()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
throw UnsupportedOperationException("Unsupported annotation argument value ($clazz): $value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object SignatureSerializer {
|
||||||
|
fun methodDesc(method: Method): String {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
sb.append("(")
|
||||||
|
for (parameterType in method.getParameterTypes()) {
|
||||||
|
sb.append(typeDesc(parameterType))
|
||||||
|
}
|
||||||
|
sb.append(")")
|
||||||
|
sb.append(typeDesc(method.getReturnType()))
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun constructorDesc(constructor: Constructor<*>): String {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
sb.append("(")
|
||||||
|
for (parameterType in constructor.getParameterTypes()) {
|
||||||
|
sb.append(typeDesc(parameterType))
|
||||||
|
}
|
||||||
|
sb.append(")V")
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fieldDesc(field: Field): String {
|
||||||
|
return typeDesc(field.getType())
|
||||||
|
}
|
||||||
|
|
||||||
|
suppress("UNCHECKED_CAST")
|
||||||
|
fun typeDesc(clazz: Class<*>): String {
|
||||||
|
if (clazz == Void.TYPE) return "V";
|
||||||
|
// This is a clever exploitation of a format returned by Class.getName(): for arrays, it's almost an internal name,
|
||||||
|
// but with '.' instead of '/'
|
||||||
|
// TODO: ensure there are tests on arrays of nested classes, multi-dimensional arrays, etc.
|
||||||
|
val arrayClass = java.lang.reflect.Array.newInstance(clazz as Class<Any>, 0).javaClass
|
||||||
|
return arrayClass.getName().substring(1).replace('.', '/')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-6
@@ -24,12 +24,7 @@ import org.jetbrains.kotlin.name.ClassId
|
|||||||
|
|
||||||
public class ReflectKotlinClassFinder(private val classLoader: ClassLoader) : KotlinClassFinder {
|
public class ReflectKotlinClassFinder(private val classLoader: ClassLoader) : KotlinClassFinder {
|
||||||
private fun findKotlinClass(fqName: String): KotlinJvmBinaryClass? {
|
private fun findKotlinClass(fqName: String): KotlinJvmBinaryClass? {
|
||||||
return classLoader.tryLoadClass(fqName)?.let { try {
|
return classLoader.tryLoadClass(fqName)?.let { ReflectKotlinClass.create(it) }
|
||||||
ReflectKotlinClass(it)
|
|
||||||
}
|
|
||||||
catch (e: ReflectKotlinClass.NotAKotlinClass) {
|
|
||||||
null
|
|
||||||
} }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findKotlinClass(classId: ClassId) = findKotlinClass(classId.toRuntimeFqName())
|
override fun findKotlinClass(classId: ClassId) = findKotlinClass(classId.toRuntimeFqName())
|
||||||
|
|||||||
Reference in New Issue
Block a user