Support Java symbols in runtime descriptor loading

This commit is contained in:
Alexander Udalov
2014-08-29 18:35:13 +04:00
parent 73e4287aee
commit c90f11b7e6
19 changed files with 4018 additions and 2688 deletions
@@ -1,4 +1,5 @@
//ALLOW_AST_ACCESS //ALLOW_AST_ACCESS
//SKIP_IN_RUNTIME_TEST
package test package test
import java.lang.annotation.* import java.lang.annotation.*
@@ -52,10 +52,7 @@ import org.junit.Assert;
import java.io.File; import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil.*; import static org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil.*;
@@ -82,7 +79,8 @@ public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir {
List<File> javaSources = FileUtil.findFilesByMask(Pattern.compile(".+\\.java"), sourcesDir); List<File> javaSources = FileUtil.findFilesByMask(Pattern.compile(".+\\.java"), sourcesDir);
Pair<PackageViewDescriptor, BindingContext> binaryPackageAndContext = compileJavaAndLoadTestPackageAndBindingContextFromBinary( Pair<PackageViewDescriptor, BindingContext> binaryPackageAndContext = compileJavaAndLoadTestPackageAndBindingContextFromBinary(
javaSources, tmpdir, myTestRootDisposable, ConfigurationKind.JDK_ONLY); javaSources, tmpdir, ConfigurationKind.JDK_ONLY
);
checkJavaPackage(expectedFile, binaryPackageAndContext.first, binaryPackageAndContext.second, DONT_INCLUDE_METHODS_OF_OBJECT); checkJavaPackage(expectedFile, binaryPackageAndContext.first, binaryPackageAndContext.second, DONT_INCLUDE_METHODS_OF_OBJECT);
} }
@@ -110,7 +108,8 @@ public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir {
Assert.assertEquals("test", packageFromSource.getName().asString()); Assert.assertEquals("test", packageFromSource.getName().asString());
PackageViewDescriptor packageFromBinary = LoadDescriptorUtil.loadTestPackageAndBindingContextFromJavaRoot( PackageViewDescriptor packageFromBinary = LoadDescriptorUtil.loadTestPackageAndBindingContextFromJavaRoot(
tmpdir, getTestRootDisposable(), configurationKind).first; tmpdir, getTestRootDisposable(), TestJdkKind.MOCK_JDK, configurationKind
).first;
for (DeclarationDescriptor descriptor : packageFromBinary.getMemberScope().getAllDescriptors()) { for (DeclarationDescriptor descriptor : packageFromBinary.getMemberScope().getAllDescriptors()) {
if (descriptor instanceof ClassDescriptor) { if (descriptor instanceof ClassDescriptor) {
@@ -208,7 +207,8 @@ public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir {
FileUtil.copy(originalJavaFile, new File(testPackageDir, originalJavaFile.getName())); FileUtil.copy(originalJavaFile, new File(testPackageDir, originalJavaFile.getName()));
Pair<PackageViewDescriptor, BindingContext> javaPackageAndContext = loadTestPackageAndBindingContextFromJavaRoot( Pair<PackageViewDescriptor, BindingContext> javaPackageAndContext = loadTestPackageAndBindingContextFromJavaRoot(
tmpdir, getTestRootDisposable(), ConfigurationKind.JDK_ONLY); tmpdir, getTestRootDisposable(), TestJdkKind.MOCK_JDK, ConfigurationKind.JDK_ONLY
);
checkJavaPackage(expectedFile, javaPackageAndContext.first, javaPackageAndContext.second, checkJavaPackage(expectedFile, javaPackageAndContext.first, javaPackageAndContext.second,
DONT_INCLUDE_METHODS_OF_OBJECT.withValidationStrategy(errorTypesAllowed())); DONT_INCLUDE_METHODS_OF_OBJECT.withValidationStrategy(errorTypesAllowed()));
@@ -241,11 +241,22 @@ public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir {
getTestRootDisposable(), ConfigurationKind.JDK_AND_ANNOTATIONS, TestJdkKind.MOCK_JDK); getTestRootDisposable(), ConfigurationKind.JDK_AND_ANNOTATIONS, TestJdkKind.MOCK_JDK);
Pair<PackageViewDescriptor, BindingContext> javaPackageAndContext = compileJavaAndLoadTestPackageAndBindingContextFromBinary( Pair<PackageViewDescriptor, BindingContext> javaPackageAndContext = compileJavaAndLoadTestPackageAndBindingContextFromBinary(
srcFiles, compiledDir, getTestRootDisposable(), ConfigurationKind.ALL); srcFiles, compiledDir, ConfigurationKind.ALL
);
checkJavaPackage(getTxtFile(javaFileName), javaPackageAndContext.first, javaPackageAndContext.second, configuration); checkJavaPackage(getTxtFile(javaFileName), javaPackageAndContext.first, javaPackageAndContext.second, configuration);
} }
@NotNull
private Pair<PackageViewDescriptor, BindingContext> compileJavaAndLoadTestPackageAndBindingContextFromBinary(
@NotNull Collection<File> javaFiles,
@NotNull File outDir,
@NotNull ConfigurationKind configurationKind
) throws IOException {
compileJavaWithAnnotationsJar(javaFiles, outDir);
return loadTestPackageAndBindingContextFromJavaRoot(outDir, myTestRootDisposable, TestJdkKind.MOCK_JDK, configurationKind);
}
private static void checkJavaPackage( private static void checkJavaPackage(
File txtFile, File txtFile,
PackageViewDescriptor javaPackage, PackageViewDescriptor javaPackage,
@@ -80,10 +80,12 @@ public final class LoadDescriptorUtil {
public static Pair<PackageViewDescriptor, BindingContext> loadTestPackageAndBindingContextFromJavaRoot( public static Pair<PackageViewDescriptor, BindingContext> loadTestPackageAndBindingContextFromJavaRoot(
@NotNull File javaRoot, @NotNull File javaRoot,
@NotNull Disposable disposable, @NotNull Disposable disposable,
@NotNull TestJdkKind testJdkKind,
@NotNull ConfigurationKind configurationKind @NotNull ConfigurationKind configurationKind
) { ) {
CompilerConfiguration configuration = JetTestUtils.compilerConfigurationForTests( CompilerConfiguration configuration = JetTestUtils.compilerConfigurationForTests(
configurationKind, TestJdkKind.MOCK_JDK, configurationKind,
testJdkKind,
JetTestUtils.getAnnotationsJar(), JetTestUtils.getAnnotationsJar(),
javaRoot, javaRoot,
new File("compiler/tests") // for @ExpectLoadError annotation new File("compiler/tests") // for @ExpectLoadError annotation
@@ -100,19 +102,7 @@ public final class LoadDescriptorUtil {
return Pair.create(packageView, trace.getBindingContext()); return Pair.create(packageView, trace.getBindingContext());
} }
@NotNull public static void compileJavaWithAnnotationsJar(@NotNull Collection<File> javaFiles, @NotNull File outDir) throws IOException {
public static Pair<PackageViewDescriptor, BindingContext> compileJavaAndLoadTestPackageAndBindingContextFromBinary(
@NotNull Collection<File> javaFiles,
@NotNull File outDir,
@NotNull Disposable disposable,
@NotNull ConfigurationKind configurationKind
)
throws IOException {
compileJavaWithAnnotationsJar(javaFiles, outDir);
return loadTestPackageAndBindingContextFromJavaRoot(outDir, disposable, configurationKind);
}
private static void compileJavaWithAnnotationsJar(@NotNull Collection<File> javaFiles, @NotNull File outDir) throws IOException {
String classPath = ForTestCompileRuntime.runtimeJarForTests() + File.pathSeparator + String classPath = ForTestCompileRuntime.runtimeJarForTests() + File.pathSeparator +
JetTestUtils.getAnnotationsJar().getPath(); JetTestUtils.getAnnotationsJar().getPath();
JetTestUtils.compileJavaFiles(javaFiles, Arrays.asList( JetTestUtils.compileJavaFiles(javaFiles, Arrays.asList(
@@ -16,32 +16,40 @@
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.GenerationUtils import org.jetbrains.kotlin.codegen.GenerationUtils
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorVisitor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
import org.jetbrains.kotlin.jvm.compiler.ExpectedLoadErrorsUtil import org.jetbrains.kotlin.jvm.compiler.ExpectedLoadErrorsUtil
import org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil import org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.structure.reflect.classId
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.load.kotlin.reflect.ReflectKotlinClass import org.jetbrains.kotlin.load.kotlin.reflect.ReflectKotlinClass
import org.jetbrains.kotlin.load.kotlin.reflect.RuntimeModuleData import org.jetbrains.kotlin.load.kotlin.reflect.RuntimeModuleData
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.DescriptorRendererBuilder import org.jetbrains.kotlin.renderer.DescriptorRendererBuilder
import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.scopes.JetScope import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.resolve.scopes.RedeclarationHandler import org.jetbrains.kotlin.resolve.scopes.RedeclarationHandler
import org.jetbrains.kotlin.resolve.scopes.WritableScope.LockLevel import org.jetbrains.kotlin.resolve.scopes.WritableScope
import org.jetbrains.kotlin.resolve.scopes.WritableScopeImpl import org.jetbrains.kotlin.resolve.scopes.WritableScopeImpl
import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.test.JetTestUtils import org.jetbrains.kotlin.test.*
import org.jetbrains.kotlin.test.TestCaseWithTmpdir import org.jetbrains.kotlin.test.JetTestUtils.TestFileFactoryNoModules
import org.jetbrains.kotlin.test.util.DescriptorValidator.ValidationVisitor.errorTypesForbidden
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.Configuration
import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.utils.sure import org.jetbrains.kotlin.utils.sure
import java.io.File import java.io.File
import java.net.URLClassLoader
import java.util.regex.Pattern
public abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdir() { public abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdir() {
class object { class object {
@@ -49,8 +57,10 @@ public abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdi
.setWithDefinedIn(false) .setWithDefinedIn(false)
.setExcludedAnnotationClasses(listOf( .setExcludedAnnotationClasses(listOf(
ExpectedLoadErrorsUtil.ANNOTATION_CLASS_NAME, ExpectedLoadErrorsUtil.ANNOTATION_CLASS_NAME,
// TODO: add these annotations when they are retained at runtime
"kotlin.deprecated", "kotlin.deprecated",
"kotlin.data", "kotlin.data",
"kotlin.inline",
"org.jetbrains.annotations.NotNull", "org.jetbrains.annotations.NotNull",
"org.jetbrains.annotations.Nullable", "org.jetbrains.annotations.Nullable",
"org.jetbrains.annotations.Mutable", "org.jetbrains.annotations.Mutable",
@@ -64,92 +74,122 @@ public abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdi
} }
// NOTE: this test does a dirty hack of text substitution to make all annotations defined in source code retain at runtime. // 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" // Specifically each "annotation class" in Kotlin sources is replaced by "Retention(RUNTIME) annotation class", and the same in Java
protected fun doTest(ktFileName: String) { protected fun doTest(fileName: String) {
val ktFile = File(ktFileName) val file = File(fileName)
val text = FileUtil.loadFile(file, true)
val environment = JetTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(myTestRootDisposable, ConfigurationKind.ALL) if (InTextDirectivesUtils.isDirectiveDefined(text, "SKIP_IN_RUNTIME_TEST")) return
val jetFile = JetTestUtils.createFile(ktFileName, loadFileAddingRuntimeRetention(ktFile), environment.getProject())
val classFileFactory = GenerationUtils.compileFileGetClassFileFactoryForTest(jetFile)
val classLoader = GeneratedClassLoader(classFileFactory, null, ForTestCompileRuntime.runtimeJarForTests().toURI().toURL())
classFileFactory.writeAllTo(tmpdir) compileFile(file, text)
val classLoader = URLClassLoader(array(tmpdir.toURI().toURL()), ForTestCompileRuntime.runtimeJarClassLoader())
val actual = createReflectedPackageView(classLoader)
val expected = LoadDescriptorUtil.loadTestPackageAndBindingContextFromJavaRoot(
tmpdir, getTestRootDisposable(), TestJdkKind.FULL_JDK, ConfigurationKind.ALL
).first
val comparatorConfiguration = Configuration(
/* checkPrimaryConstructors = */ fileName.endsWith(".kt"),
/* checkPropertyAccessors = */ true,
/* includeMethodsOfKotlinAny = */ false,
{ descriptor ->
// Skip annotation constructors because order of their parameters is not retained at runtime
!(descriptor is ConstructorDescriptor && DescriptorUtils.isAnnotationClass(descriptor.getContainingDeclaration()))
},
errorTypesForbidden(), renderer
)
RecursiveDescriptorComparator.validateAndCompareDescriptors(expected, actual, comparatorConfiguration, null)
}
private fun compileFile(file: File, text: String) {
val fileName = file.getName()
when {
fileName.endsWith(".java") -> {
val sources = JetTestUtils.createTestFiles(fileName, text, object : TestFileFactoryNoModules<File>() {
override fun create(fileName: String, text: String, directives: Map<String, String>): File {
val targetFile = File(tmpdir, fileName)
targetFile.writeText(addRuntimeRetentionToJavaSource(text))
return targetFile
}
})
LoadDescriptorUtil.compileJavaWithAnnotationsJar(sources, tmpdir)
}
fileName.endsWith(".kt") -> {
val environment = JetTestUtils.createEnvironmentWithFullJdk(myTestRootDisposable)
val jetFile = JetTestUtils.createFile(file.getPath(), addRuntimeRetentionToKotlinSource(text), environment.getProject())
GenerationUtils.compileFileGetClassFileFactoryForTest(jetFile).writeAllTo(tmpdir)
}
}
}
private fun createReflectedPackageView(classLoader: URLClassLoader): SyntheticPackageViewForTest {
val module = RuntimeModuleData.create(classLoader).module val module = RuntimeModuleData.create(classLoader).module
// Since runtime package view descriptor doesn't support getAllDescriptors(), we construct a synthetic package view here. // Since runtime package view descriptor doesn't support getAllDescriptors(), we construct a synthetic package view here.
// It has in its scope descriptors for all the classes and top level members generated by the compiler // It has in its scope descriptors for all the classes and top level members generated by the compiler
val actual = object : PackageViewDescriptor { val actual = SyntheticPackageViewForTest(module)
val scope = WritableScopeImpl(JetScope.Empty, this, RedeclarationHandler.THROW_EXCEPTION, "runtime descriptor loader test")
override fun getFqName() = LoadDescriptorUtil.TEST_PACKAGE_FQNAME
override fun getMemberScope() = scope
override fun getModule() = module
override fun <R, D> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D): R =
visitor.visitPackageViewDescriptor(this, data)
override fun getContainingDeclaration() = throw UnsupportedOperationException()
override fun getOriginal() = throw UnsupportedOperationException()
override fun substitute(substitutor: TypeSubstitutor) = throw UnsupportedOperationException()
override fun acceptVoid(visitor: DeclarationDescriptorVisitor<Void, Void>?) = throw UnsupportedOperationException()
override fun getAnnotations() = throw UnsupportedOperationException()
override fun getName() = throw UnsupportedOperationException()
}
val scope = actual.getMemberScope() val scope = actual.getMemberScope()
scope.changeLockLevel(LockLevel.BOTH)
for (outputFile in classLoader.getAllGeneratedFiles()) { val generatedPackageDir = File(tmpdir, LoadDescriptorUtil.TEST_PACKAGE_FQNAME.pathSegments().single().asString())
val className = outputFile.relativePath.substringBeforeLast(".class").replace('/', '.').replace('\\', '.') val allClassFiles = FileUtil.findFilesByMask(Pattern.compile(".*\\.class"), generatedPackageDir)
for (classFile in allClassFiles) {
val className = tmpdir.relativePath(classFile).substringBeforeLast(".class").replace('/', '.').replace('\\', '.')
val klass = classLoader.loadClass(className).sure("Couldn't load class $className") val klass = classLoader.loadClass(className).sure("Couldn't load class $className")
val header = ReflectKotlinClass.create(klass)?.getClassHeader()
when (ReflectKotlinClass.create(klass)!!.getClassHeader().kind) { if (header?.kind == KotlinClassHeader.Kind.PACKAGE_FACADE) {
KotlinClassHeader.Kind.PACKAGE_FACADE -> { val packageView = module.getPackage(actual.getFqName()).sure("Couldn't resolve package ${actual.getFqName()}")
val packageView = module.getPackage(actual.getFqName()) ?: error("Couldn't resolve package ${actual.getFqName()}") scope.importScope(packageView.getMemberScope())
for (descriptor in packageView.getMemberScope().getAllDescriptors()) { }
when (descriptor) { else if (header == null ||
is FunctionDescriptor -> scope.addFunctionDescriptor(descriptor) (header.kind == KotlinClassHeader.Kind.CLASS && header.classKind == JvmAnnotationNames.KotlinClass.Kind.CLASS)) {
is PropertyDescriptor -> scope.addPropertyDescriptor(descriptor) // Either a normal Kotlin class or a Java class
} val classDescriptor = module.findClassAcrossModuleDependencies(klass.classId).sure("Couldn't resolve class $className")
} if (DescriptorUtils.isTopLevelDeclaration(classDescriptor)) {
} scope.addClassifierDescriptor(classDescriptor)
KotlinClassHeader.Kind.CLASS -> {
val classDescriptor =
resolveClassByFqNameInModule(module, FqNameUnsafe(className)).sure("Couldn't resolve class $className")
if (classDescriptor.getContainingDeclaration() is PackageFragmentDescriptor) {
scope.addClassifierDescriptor(classDescriptor)
}
} }
} }
} }
return actual
val expected = LoadDescriptorUtil.loadTestPackageAndBindingContextFromJavaRoot(tmpdir, getTestRootDisposable(),
ConfigurationKind.ALL)
val comparatorConfiguration = RecursiveDescriptorComparator.DONT_INCLUDE_METHODS_OF_OBJECT
.checkPrimaryConstructors(true)
.checkPropertyAccessors(true)
.withRenderer(renderer)
RecursiveDescriptorComparator.validateAndCompareDescriptors(expected.first, actual, comparatorConfiguration, null)
} }
private fun loadFileAddingRuntimeRetention(file: File): String { private fun addRuntimeRetentionToKotlinSource(text: String): String {
return file.readText().replace( return text.replace(
"annotation class", "annotation class",
"[java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)] 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 addRuntimeRetentionToJavaSource(text: String): String {
private fun resolveClassByFqNameInModule(module: ModuleDescriptor, fqName: FqNameUnsafe): ClassDescriptor? { return text.replace(
if (fqName.isRoot()) return null "@interface",
"@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @interface"
)
}
if (fqName.isSafe()) { private class SyntheticPackageViewForTest(private val module: ModuleDescriptor) : PackageViewDescriptor {
val topLevel = module.resolveTopLevelClass(fqName.toSafe()) private val scope = WritableScopeImpl(JetScope.Empty, this, RedeclarationHandler.THROW_EXCEPTION, "runtime descriptor loader test")
if (topLevel != null) return topLevel
;{
scope.changeLockLevel(WritableScope.LockLevel.BOTH)
} }
val parent = resolveClassByFqNameInModule(module, fqName.parent()) ?: return null override fun getFqName() = LoadDescriptorUtil.TEST_PACKAGE_FQNAME
return parent.getUnsubstitutedInnerClassesScope().getClassifier(fqName.shortName()) as? ClassDescriptor override fun getMemberScope() = scope
override fun getModule() = module
override fun <R, D> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D): R =
visitor.visitPackageViewDescriptor(this, data)
override fun getContainingDeclaration() = throw UnsupportedOperationException()
override fun getOriginal() = throw UnsupportedOperationException()
override fun substitute(substitutor: TypeSubstitutor) = throw UnsupportedOperationException()
override fun acceptVoid(visitor: DeclarationDescriptorVisitor<Void, Void>?) = throw UnsupportedOperationException()
override fun getAnnotations() = throw UnsupportedOperationException()
override fun getName() = throw UnsupportedOperationException()
} }
} }
@@ -26,7 +26,13 @@ import org.jetbrains.kotlin.name.ClassId
public class ReflectJavaClassFinder(private val classLoader: ClassLoader) : JavaClassFinder { public class ReflectJavaClassFinder(private val classLoader: ClassLoader) : JavaClassFinder {
override fun findClass(classId: ClassId): JavaClass? { override fun findClass(classId: ClassId): JavaClass? {
val klass = classLoader.tryLoadClass(classId.asSingleFqName().asString()) val packageFqName = classId.getPackageFqName()
val relativeClassName = classId.getRelativeClassName().asString().replace('.', '$')
val name =
if (packageFqName.isRoot()) relativeClassName
else packageFqName.asString() + "." + relativeClassName
val klass = classLoader.tryLoadClass(name)
return if (klass != null) ReflectJavaClass(klass) else null return if (klass != null) ReflectJavaClass(klass) else null
} }
@@ -0,0 +1,40 @@
/*
* 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.JavaAnnotation
import org.jetbrains.kotlin.load.java.structure.JavaAnnotationArgument
import org.jetbrains.kotlin.name.Name
import java.lang.reflect.Method
public class ReflectJavaAnnotation(private val annotation: Annotation) : ReflectJavaElement(), JavaAnnotation {
override fun findArgument(name: Name): JavaAnnotationArgument? {
return getArgumentValue(annotation.annotationType().getDeclaredMethod(name.asString()))
}
override fun getArguments(): Collection<JavaAnnotationArgument> {
return annotation.annotationType().getDeclaredMethods().map { getArgumentValue(it) }
}
private fun getArgumentValue(argument: Method): ReflectJavaAnnotationArgument {
return argument.invoke(annotation).let { ReflectJavaAnnotationArgument.create(it, Name.identifier(argument.getName())) }
}
override fun resolve() = ReflectJavaClass(annotation.annotationType())
override fun getClassId() = annotation.annotationType().classId
}
@@ -0,0 +1,69 @@
/*
* 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.*
import org.jetbrains.kotlin.name.Name
abstract class ReflectJavaAnnotationArgument(
override val name: Name?
) : JavaAnnotationArgument {
class object {
fun create(value: Any, name: Name?): ReflectJavaAnnotationArgument {
return when {
value.javaClass.isEnum() -> ReflectJavaEnumValueAnnotationArgument(name, value)
value is Annotation -> ReflectJavaAnnotationAsAnnotationArgument(name, value)
value is Array<Any> -> ReflectJavaArrayAnnotationArgument(name, value)
value is Class<*> -> ReflectJavaClassObjectAnnotationArgument(name, value)
else -> ReflectJavaLiteralAnnotationArgument(name, value)
}
}
}
}
class ReflectJavaLiteralAnnotationArgument(
name: Name?,
override val value: Any
) : ReflectJavaAnnotationArgument(name), JavaLiteralAnnotationArgument
class ReflectJavaArrayAnnotationArgument(
name: Name?,
private val values: Array<Any>
) : ReflectJavaAnnotationArgument(name), JavaArrayAnnotationArgument {
override fun getElements() = values.map { ReflectJavaAnnotationArgument.create(it, null) }
}
class ReflectJavaEnumValueAnnotationArgument(
name: Name?,
private val value: Any
) : ReflectJavaAnnotationArgument(name), JavaEnumValueAnnotationArgument {
override fun resolve() = ReflectJavaField(value.javaClass.getDeclaredField(value.toString()))
}
class ReflectJavaClassObjectAnnotationArgument(
name: Name?,
private val klass: Class<*>
) : ReflectJavaAnnotationArgument(name), JavaClassObjectAnnotationArgument {
override fun getReferencedType(): JavaType = ReflectJavaType.create(klass)
}
class ReflectJavaAnnotationAsAnnotationArgument(
name: Name?,
private val annotation: Annotation
) : ReflectJavaAnnotationArgument(name), JavaAnnotationAsAnnotationArgument {
override fun getAnnotation(): JavaAnnotation = ReflectJavaAnnotation(annotation)
}
@@ -16,9 +16,9 @@
package org.jetbrains.kotlin.load.java.structure.reflect package org.jetbrains.kotlin.load.java.structure.reflect
import java.lang.reflect.GenericArrayType
import org.jetbrains.kotlin.load.java.structure.JavaArrayType import org.jetbrains.kotlin.load.java.structure.JavaArrayType
import java.lang.reflect.Type
public class ReflectJavaArrayType(private val arrayType: GenericArrayType) : ReflectJavaType(), JavaArrayType { public class ReflectJavaArrayType(private val componentType: Type) : ReflectJavaType(), JavaArrayType {
override fun getComponentType() = ReflectJavaType.create(arrayType.getGenericComponentType()!!) override fun getComponentType() = ReflectJavaType.create(componentType)
} }
@@ -16,69 +16,88 @@
package org.jetbrains.kotlin.load.java.structure.reflect package org.jetbrains.kotlin.load.java.structure.reflect
import java.lang.reflect.Modifier
import org.jetbrains.kotlin.load.java.structure.* import org.jetbrains.kotlin.load.java.structure.*
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.emptyOrSingletonList import org.jetbrains.kotlin.utils.emptyOrSingletonList
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.util.Arrays
public class ReflectJavaClass(private val klass: Class<*>) : ReflectJavaElement(), JavaClass { public class ReflectJavaClass(private val klass: Class<*>) : ReflectJavaElement(), JavaClass {
override fun getInnerClasses() = override fun getInnerClasses() = klass.getDeclaredClasses()
klass.getDeclaredClasses() .stream()
.filter { it.getSimpleName().isNotEmpty() } .filterNot {
.map { ReflectJavaClass(it) } // getDeclaredClasses() returns anonymous classes sometimes, for example enums with specialized entries (which are in fact
// anonymous classes) or in case of a special anonymous class created for the synthetic accessor to a private nested class
// constructor accessed from the outer class
it.getSimpleName().isEmpty()
}
.map(::ReflectJavaClass)
.toList()
override fun getFqName(): FqName? { override fun getFqName() = klass.fqName
// TODO: can there be primitive types, arrays?
return getOuterClass()?.getFqName()?.child(Name.identifier(klass.getSimpleName())) ?: FqName(klass.getName())
}
override fun getOuterClass(): JavaClass? { override fun getOuterClass() = klass.getDeclaringClass()?.let(::ReflectJavaClass)
val container = klass.getDeclaringClass()
return if (container != null) ReflectJavaClass(container) else null
}
override fun getSupertypes(): Collection<JavaClassifierType> { override fun getSupertypes(): Collection<JavaClassifierType> {
// TODO: also call getSuperclass() / getInterfaces() for classes without generic signature val supertype = klass.getGenericSuperclass()
val supertypes = emptyOrSingletonList(klass.getGenericSuperclass()) + klass.getGenericInterfaces() val superClassName = (supertype as? Class<*>)?.getName()
return supertypes.map { supertype -> ReflectJavaType.create(supertype) as JavaClassifierType } val supertypes =
(if (superClassName == "java.lang.Object") emptyList() else emptyOrSingletonList(supertype)) +
klass.getGenericInterfaces()
return supertypes.map(::ReflectJavaClassifierType)
} }
override fun getMethods() = klass.getDeclaredMethods().map { method -> ReflectJavaMethod(method) } override fun getMethods() = klass.getDeclaredMethods()
.stream()
.filter { method ->
when {
method.isSynthetic() -> false
isEnum() -> !isEnumValuesOrValueOf(method)
else -> true
}
}
.map(::ReflectJavaMethod)
.toList()
override fun getFields() = klass.getDeclaredFields().map { field -> ReflectJavaField(field) } private fun isEnumValuesOrValueOf(method: Method): Boolean {
return when (method.getName()) {
override fun getConstructors(): Collection<JavaConstructor> { "values" -> method.getParameterTypes().isEmpty()
// TODO "valueOf" -> Arrays.equals(method.getParameterTypes(), array(javaClass<String>()))
return listOf() else -> false
}
} }
override fun getDefaultType() = ReflectJavaClassifierType(klass) override fun getFields() = klass.getDeclaredFields()
.stream()
.filter { field -> !field.isSynthetic() }
.map(::ReflectJavaField)
.toList()
// TODO: drop OriginKind? override fun getConstructors() = klass.getDeclaredConstructors().map(::ReflectJavaConstructor)
override fun getDefaultType(): ReflectJavaClassifierType = throw UnsupportedOperationException()
// TODO: drop OriginKind
override fun getOriginKind() = JavaClass.OriginKind.COMPILED override fun getOriginKind() = JavaClass.OriginKind.COMPILED
override fun createImmediateType(substitutor: JavaTypeSubstitutor): JavaType { override fun createImmediateType(substitutor: JavaTypeSubstitutor): JavaType = throw UnsupportedOperationException()
// TODO
throw UnsupportedOperationException()
}
override fun getName(): Name { override fun getName(): Name = Name.identifier(klass.getSimpleName())
// TODO: can there be primitive types, arrays?
return Name.identifier(klass.getSimpleName())
}
override fun getAnnotations(): Collection<JavaAnnotation> { override fun getAnnotations() = klass.getDeclaredAnnotations().map { ReflectJavaAnnotation(it) }
// TODO
return listOf()
}
override fun findAnnotation(fqName: FqName): JavaAnnotation? { override fun findAnnotation(fqName: FqName): JavaAnnotation? {
// TODO for (annotation in klass.getDeclaredAnnotations()) {
if (annotation.annotationType().fqName == fqName) {
return ReflectJavaAnnotation(annotation)
}
}
return null return null
} }
override fun getTypeParameters() = klass.getTypeParameters().map { ReflectJavaTypeParameter(it!!) } override fun getTypeParameters() = klass.getTypeParameters().map { ReflectJavaTypeParameter(it) }
override fun isInterface() = klass.isInterface() override fun isInterface() = klass.isInterface()
override fun isAnnotationType() = klass.isAnnotation() override fun isAnnotationType() = klass.isAnnotation()
@@ -25,15 +25,18 @@ import java.lang.reflect.Type
import java.lang.reflect.TypeVariable import java.lang.reflect.TypeVariable
public class ReflectJavaClassifierType(val classifierType: Type) : ReflectJavaType(), JavaClassifierType { public class ReflectJavaClassifierType(val classifierType: Type) : ReflectJavaType(), JavaClassifierType {
override fun getClassifier(): JavaClassifier? { private val classifier: JavaClassifier = run {
return when (classifierType) { val type = classifierType
is Class<*> -> ReflectJavaClass(classifierType) when (type) {
is TypeVariable<*> -> ReflectJavaTypeParameter(classifierType) is Class<*> -> ReflectJavaClass(type)
is ParameterizedType -> ReflectJavaClass(classifierType.getRawType() as Class<*>) is TypeVariable<*> -> ReflectJavaTypeParameter(type)
else -> throw IllegalStateException("Not a classifier type (${classifierType.javaClass}): $classifierType") is ParameterizedType -> ReflectJavaClass(type.getRawType() as Class<*>)
} else -> throw IllegalStateException("Not a classifier type (${type.javaClass}): $type")
} : JavaClassifier
} }
override fun getClassifier(): JavaClassifier = classifier
override fun getSubstitutor(): JavaTypeSubstitutor = throw UnsupportedOperationException() override fun getSubstitutor(): JavaTypeSubstitutor = throw UnsupportedOperationException()
override fun getSupertypes(): Collection<JavaClassifierType> = throw UnsupportedOperationException() override fun getSupertypes(): Collection<JavaClassifierType> = throw UnsupportedOperationException()
@@ -0,0 +1,63 @@
/*
* 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.JavaAnnotation
import org.jetbrains.kotlin.load.java.structure.JavaConstructor
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
import org.jetbrains.kotlin.load.java.structure.JavaValueParameter
import org.jetbrains.kotlin.name.FqName
import java.lang.reflect.Constructor
import java.lang.reflect.Modifier
import java.util.Arrays
public class ReflectJavaConstructor(constructor: Constructor<*>) : ReflectJavaMember(constructor), JavaConstructor {
private val constructor: Constructor<*>
get() = member as Constructor<*>
override fun getAnnotations(): Collection<JavaAnnotation> {
// TODO
return listOf()
}
override fun findAnnotation(fqName: FqName): JavaAnnotation? {
// TODO
return null
}
override fun getValueParameters(): List<JavaValueParameter> =
getValueParameters(
dropSynthetic(constructor.getGenericParameterTypes()),
dropSynthetic(constructor.getParameterAnnotations()),
constructor.isVarArgs()
)
// Constructors of inner classes have one additional synthetic parameter
// TODO: test this code with annotations on constructor parameters of enums and inner classes
private inline fun <reified T> dropSynthetic(array: Array<T>): Array<T> {
val klass = constructor.getDeclaringClass()
return if (klass.getDeclaringClass() != null && !Modifier.isStatic(klass.getModifiers())) {
Arrays.copyOfRange(array, 1, array.size())
}
else array
}
override fun getTypeParameters(): List<JavaTypeParameter> {
// TODO
return listOf()
}
}
@@ -20,7 +20,6 @@ import java.lang.reflect.Field
import org.jetbrains.kotlin.load.java.structure.JavaField import org.jetbrains.kotlin.load.java.structure.JavaField
import org.jetbrains.kotlin.load.java.structure.JavaAnnotation import org.jetbrains.kotlin.load.java.structure.JavaAnnotation
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.load.java.structure.JavaType
public class ReflectJavaField(field: Field) : ReflectJavaMember(field), JavaField { public class ReflectJavaField(field: Field) : ReflectJavaMember(field), JavaField {
val field: Field val field: Field
@@ -38,8 +37,5 @@ public class ReflectJavaField(field: Field) : ReflectJavaMember(field), JavaFiel
override fun isEnumEntry() = field.isEnumConstant() override fun isEnumEntry() = field.isEnumConstant()
override fun getType(): JavaType { override fun getType() = ReflectJavaType.create(field.getGenericType()!!)
// TODO
throw UnsupportedOperationException()
}
} }
@@ -16,11 +16,14 @@
package org.jetbrains.kotlin.load.java.structure.reflect 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.load.java.structure.JavaMember
import org.jetbrains.kotlin.load.java.structure.JavaValueParameter
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
import java.lang.reflect.Member
import java.lang.reflect.Modifier
import java.lang.reflect.Type
import java.util.ArrayList
public abstract class ReflectJavaMember(protected val member: Member) : ReflectJavaElement(), JavaMember { 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 getName() = member.getName()?.let { Name.identifier(it) } ?: SpecialNames.NO_NAME_PROVIDED
@@ -32,4 +35,17 @@ public abstract class ReflectJavaMember(protected val member: Member) : ReflectJ
override fun isFinal() = Modifier.isFinal(member.getModifiers()) override fun isFinal() = Modifier.isFinal(member.getModifiers())
override fun getVisibility() = calculateVisibility(member.getModifiers()) override fun getVisibility() = calculateVisibility(member.getModifiers())
protected fun getValueParameters(
parameterTypes: Array<Type>,
parameterAnnotations: Array<Array<Annotation>>,
isVararg: Boolean
): List<JavaValueParameter> {
val result = ArrayList<JavaValueParameter>(parameterTypes.size())
for (i in parameterTypes.indices) {
val isParamVararg = isVararg && i == parameterTypes.lastIndex
result.add(ReflectJavaValueParameter(ReflectJavaType.create(parameterTypes[i]), parameterAnnotations[i], isParamVararg))
}
return result
}
} }
@@ -16,13 +16,12 @@
package org.jetbrains.kotlin.load.java.structure.reflect 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.load.java.structure.JavaAnnotation
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.load.java.structure.JavaMethod
import org.jetbrains.kotlin.load.java.structure.JavaValueParameter
import org.jetbrains.kotlin.load.java.structure.JavaType import org.jetbrains.kotlin.load.java.structure.JavaType
import org.jetbrains.kotlin.load.java.structure.JavaValueParameter
import org.jetbrains.kotlin.name.FqName
import java.lang.reflect.Method
public class ReflectJavaMethod(method: Method) : ReflectJavaMember(method), JavaMethod { public class ReflectJavaMethod(method: Method) : ReflectJavaMember(method), JavaMethod {
private val method: Method private val method: Method
@@ -38,17 +37,8 @@ public class ReflectJavaMethod(method: Method) : ReflectJavaMember(method), Java
return null return null
} }
override fun getValueParameters(): List<JavaValueParameter> { override fun getValueParameters(): List<JavaValueParameter> =
val types = method.getGenericParameterTypes()!! getValueParameters(method.getGenericParameterTypes(), method.getParameterAnnotations(), method.isVarArgs())
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? { override fun getReturnType(): JavaType? {
// TODO // TODO
@@ -33,8 +33,12 @@ public abstract class ReflectJavaType : JavaType {
fun create(reflectType: Type): ReflectJavaType { fun create(reflectType: Type): ReflectJavaType {
return when (reflectType) { return when (reflectType) {
in PRIMITIVE_TYPES -> ReflectJavaPrimitiveType(reflectType as Class<*>) in PRIMITIVE_TYPES -> ReflectJavaPrimitiveType(reflectType as Class<*>)
is Class<*>, is TypeVariable<*>, is ParameterizedType -> ReflectJavaClassifierType(reflectType) is GenericArrayType -> ReflectJavaArrayType(reflectType.getGenericComponentType()!!)
is GenericArrayType -> ReflectJavaArrayType(reflectType) is Class<*> -> {
if (reflectType.isArray()) ReflectJavaArrayType(reflectType.getComponentType()!!)
else ReflectJavaClassifierType(reflectType)
}
is TypeVariable<*>, is ParameterizedType -> ReflectJavaClassifierType(reflectType)
is WildcardType -> ReflectJavaWildcardType(reflectType) is WildcardType -> ReflectJavaWildcardType(reflectType)
else -> throw UnsupportedOperationException("Unsupported type (${reflectType.javaClass}): $reflectType") else -> throw UnsupportedOperationException("Unsupported type (${reflectType.javaClass}): $reflectType")
} }
@@ -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.Constructor
import java.lang.reflect.Method import java.lang.reflect.Method
import java.lang.reflect.TypeVariable import java.lang.reflect.TypeVariable
@@ -35,8 +36,9 @@ public class ReflectJavaTypeParameter(
override fun getOwner(): JavaTypeParameterListOwner? { override fun getOwner(): JavaTypeParameterListOwner? {
val owner = typeVariable.getGenericDeclaration() val owner = typeVariable.getGenericDeclaration()
return when (owner) { return when (owner) {
is Method -> ReflectJavaMethod(owner)
is Class<*> -> ReflectJavaClass(owner) is Class<*> -> ReflectJavaClass(owner)
is Method -> ReflectJavaMethod(owner)
is Constructor<*> -> ReflectJavaConstructor(owner)
else -> throw UnsupportedOperationException("Unsupported type parameter list owner (${owner.javaClass}): $owner") else -> throw UnsupportedOperationException("Unsupported type parameter list owner (${owner.javaClass}): $owner")
} }
} }
@@ -16,13 +16,13 @@
package org.jetbrains.kotlin.load.java.structure.reflect package org.jetbrains.kotlin.load.java.structure.reflect
import java.lang.reflect.Modifier
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.load.java.JavaVisibilities import org.jetbrains.kotlin.load.java.JavaVisibilities
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import java.lang.reflect.Modifier
private fun calculateVisibility(modifiers: Int): Visibility { private fun calculateVisibility(modifiers: Int): Visibility {
return when { return when {
@@ -34,5 +34,8 @@ private fun calculateVisibility(modifiers: Int): Visibility {
} }
} }
val Class<*>.classId: ClassId public val Class<*>.fqName: FqName
get() = classId.asSingleFqName().toSafe()
public val Class<*>.classId: ClassId
get() = getDeclaringClass()?.classId?.createNestedClassId(Name.identifier(getSimpleName())) ?: ClassId.topLevel(FqName(getName())) get() = getDeclaringClass()?.classId?.createNestedClassId(Name.identifier(getSimpleName())) ?: ClassId.topLevel(FqName(getName()))
@@ -221,6 +221,7 @@ fun main(args: Array<String>) {
testClass(javaClass<AbstractJvmRuntimeDescriptorLoaderTest>()) { testClass(javaClass<AbstractJvmRuntimeDescriptorLoaderTest>()) {
model("loadJava/compiledKotlin") model("loadJava/compiledKotlin")
model("loadJava/compiledJava", extension = "java", excludeDirs = listOf("sam", "kotlinSignature/propagation"))
} }
testClass(javaClass<AbstractCompileJavaAgainstKotlinTest>()) { testClass(javaClass<AbstractCompileJavaAgainstKotlinTest>()) {