javac-wrapper: refactoring, fixes and tests
This commit is contained in:
committed by
Alexander Baratynskiy
parent
8494e54608
commit
01883a41cb
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.javac
|
||||
|
||||
import com.sun.tools.javac.main.Option
|
||||
import com.sun.tools.javac.util.Options
|
||||
import java.util.regex.Pattern
|
||||
|
||||
@@ -25,6 +26,8 @@ object JavacOptionsMapper {
|
||||
arguments.forEach { options.putOption(it) }
|
||||
}
|
||||
|
||||
fun setUTF8Encoding(options: Options) = options.put(Option.ENCODING, "UTF8")
|
||||
|
||||
private val optionPattern = Pattern.compile("\\s+")
|
||||
|
||||
private fun Options.putOption(option: String) =
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.sun.tools.javac.jvm.ClassReader
|
||||
import com.sun.tools.javac.main.JavaCompiler
|
||||
import com.sun.tools.javac.model.JavacElements
|
||||
import com.sun.tools.javac.model.JavacTypes
|
||||
import com.sun.tools.javac.tree.DCTree
|
||||
import com.sun.tools.javac.tree.JCTree
|
||||
import com.sun.tools.javac.util.Context
|
||||
import com.sun.tools.javac.util.Log
|
||||
@@ -48,10 +49,7 @@ import org.jetbrains.kotlin.load.java.structure.JavaAnnotation
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClassifier
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaPackage
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.isSubpackageOf
|
||||
import org.jetbrains.kotlin.name.parentOrNull
|
||||
import org.jetbrains.kotlin.name.*
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
@@ -67,6 +65,9 @@ class JavacWrapper(
|
||||
kotlinFiles: Collection<KtFile>,
|
||||
arguments: Array<String>?,
|
||||
jvmClasspathRoots: List<File>,
|
||||
bootClasspath: List<File>?,
|
||||
sourcePath: List<File>?,
|
||||
val kotlinSupertypeResolver: KotlinSupertypeResolver,
|
||||
private val compileJava: Boolean,
|
||||
private val outputDirectory: File?,
|
||||
private val context: Context
|
||||
@@ -88,7 +89,7 @@ class JavacWrapper(
|
||||
}
|
||||
|
||||
val JAVA_LANG_ENUM by lazy {
|
||||
createCommonClassifierType(CommonClassNames.JAVA_LANG_ENUM)
|
||||
findClassInSymbols(CommonClassNames.JAVA_LANG_ENUM)
|
||||
}
|
||||
|
||||
val JAVA_LANG_ANNOTATION_ANNOTATION by lazy {
|
||||
@@ -96,7 +97,10 @@ class JavacWrapper(
|
||||
}
|
||||
|
||||
init {
|
||||
arguments?.toList()?.let { JavacOptionsMapper.map(Options.instance(context), it) }
|
||||
Options.instance(context).let { options ->
|
||||
JavacOptionsMapper.setUTF8Encoding(options)
|
||||
arguments?.toList()?.let { JavacOptionsMapper.map(options, it) }
|
||||
}
|
||||
}
|
||||
|
||||
private val javac = object : JavaCompiler(context) {
|
||||
@@ -106,9 +110,18 @@ class JavacWrapper(
|
||||
private val fileManager = context[JavaFileManager::class.java] as JavacFileManager
|
||||
|
||||
init {
|
||||
// keep javadoc comments
|
||||
javac.keepComments = true
|
||||
// use rt.jar instead of lib/ct.sym
|
||||
fileManager.setSymbolFileEnabled(false)
|
||||
fileManager.setLocation(StandardLocation.CLASS_PATH, jvmClasspathRoots)
|
||||
bootClasspath?.let {
|
||||
val cp = fileManager.getLocation(StandardLocation.PLATFORM_CLASS_PATH) + jvmClasspathRoots
|
||||
fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, it)
|
||||
fileManager.setLocation(StandardLocation.CLASS_PATH, cp)
|
||||
} ?: fileManager.setLocation(StandardLocation.CLASS_PATH, jvmClasspathRoots)
|
||||
sourcePath?.let {
|
||||
fileManager.setLocation(StandardLocation.SOURCE_PATH, sourcePath)
|
||||
}
|
||||
}
|
||||
|
||||
private val names = Names.instance(context)
|
||||
@@ -119,25 +132,36 @@ class JavacWrapper(
|
||||
private val fileObjects = fileManager.getJavaFileObjectsFromFiles(javaFiles).toJavacList()
|
||||
private val compilationUnits: JavacList<JCTree.JCCompilationUnit> = fileObjects.map(javac::parse).toJavacList()
|
||||
|
||||
private val javaClasses = compilationUnits
|
||||
.flatMap { unit ->
|
||||
unit.typeDecls.flatMap { classDecl ->
|
||||
TreeBasedClass(classDecl as JCTree.JCClassDecl,
|
||||
trees.getPath(unit, classDecl),
|
||||
this,
|
||||
unit.sourceFile).withInnerClasses()
|
||||
}
|
||||
}
|
||||
.associateBy(JavaClass::fqName)
|
||||
private val treeBasedJavaClasses = hashMapOf<ClassId, TreeBasedClass>()
|
||||
|
||||
private val javaClassesAssociatedByClassId =
|
||||
javaClasses.values.associateBy { it.computeClassId() }
|
||||
private val javaClassDeclarations = compilationUnits.flatMap { unit ->
|
||||
unit.typeDecls.map { classDeclaration ->
|
||||
val packageName = unit.packageName?.toString() ?: ""
|
||||
val className = (classDeclaration as JCTree.JCClassDecl).simpleName.toString()
|
||||
val classId = classId(packageName, className)
|
||||
classId to Pair(classDeclaration, unit)
|
||||
}
|
||||
}.toMap()
|
||||
|
||||
private fun getTreeBasedClass(classId: ClassId): TreeBasedClass? {
|
||||
if (treeBasedJavaClasses.containsKey(classId)) {
|
||||
return treeBasedJavaClasses[classId]
|
||||
}
|
||||
|
||||
val (classDeclaration, unit) = javaClassDeclarations[classId] ?: return null
|
||||
val treeBasedClass = TreeBasedClass(classDeclaration,
|
||||
trees.getPath(unit, classDeclaration),
|
||||
this,
|
||||
unit.sourceFile)
|
||||
|
||||
return treeBasedClass.apply { treeBasedJavaClasses[classId] = this }
|
||||
}
|
||||
|
||||
private val javaPackages = compilationUnits
|
||||
.mapNotNullTo(hashSetOf()) { unit ->
|
||||
.mapTo(hashSetOf<TreeBasedPackage>()) { unit ->
|
||||
unit.packageName?.toString()?.let { packageName ->
|
||||
TreeBasedPackage(packageName, this, unit.sourcefile)
|
||||
}
|
||||
} ?: TreeBasedPackage("<root>", this, unit.sourcefile)
|
||||
}
|
||||
.associateBy(TreeBasedPackage::fqName)
|
||||
|
||||
@@ -146,12 +170,11 @@ class JavacWrapper(
|
||||
it.sourceFile.isNameCompatible("package-info", JavaFileObject.Kind.SOURCE) &&
|
||||
it.packageName != null
|
||||
}.associateBy({ FqName(it.packageName!!.toString()) }) { compilationUnit ->
|
||||
compilationUnit.packageAnnotations.map { TreeBasedAnnotation(it, compilationUnit, this) }
|
||||
}
|
||||
compilationUnit.packageAnnotations.map { TreeBasedAnnotation(it, getTreePath(it, compilationUnit), this) }
|
||||
}
|
||||
|
||||
val classifierResolver = ClassifierResolver(this)
|
||||
private val kotlinClassifiersCache = KotlinClassifiersCache(if (javaFiles.isNotEmpty()) kotlinFiles else emptyList(), this)
|
||||
private val treePathResolverCache = TreePathResolverCache(this)
|
||||
private val symbolBasedClassesCache = hashMapOf<String, SymbolBasedClass?>()
|
||||
private val symbolBasedPackagesCache = hashMapOf<String, SymbolBasedPackage?>()
|
||||
|
||||
fun compile(outDir: File? = null): Boolean = with(javac) {
|
||||
@@ -173,20 +196,20 @@ class JavacWrapper(
|
||||
javac.close()
|
||||
}
|
||||
|
||||
fun findClass(fqName: FqName, scope: GlobalSearchScope = EverythingGlobalScope()): JavaClass? {
|
||||
javaClasses[fqName]?.let { javaClass ->
|
||||
javaClass.virtualFile?.let { if (it in scope) return javaClass }
|
||||
}
|
||||
|
||||
findClassInSymbols(fqName.asString())?.let { javaClass ->
|
||||
javaClass.virtualFile?.let { if (it in scope) return javaClass }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun findClass(classId: ClassId, scope: GlobalSearchScope = EverythingGlobalScope()): JavaClass? {
|
||||
javaClassesAssociatedByClassId[classId]?.let { javaClass ->
|
||||
if (classId.isNestedClass) {
|
||||
val pathSegments = classId.relativeClassName.pathSegments().map { it.asString() }
|
||||
val outerClassId = ClassId(classId.packageFqName, Name.identifier(pathSegments.first()))
|
||||
var outerClass = findClass(outerClassId, scope) ?: return null
|
||||
|
||||
pathSegments.drop(1).forEach {
|
||||
outerClass = outerClass.findInnerClass(Name.identifier(it)) ?: return null
|
||||
}
|
||||
|
||||
return outerClass
|
||||
}
|
||||
|
||||
getTreeBasedClass(classId)?.let { javaClass ->
|
||||
javaClass.virtualFile?.let { if (it in scope) return javaClass }
|
||||
}
|
||||
|
||||
@@ -202,7 +225,7 @@ class JavacWrapper(
|
||||
return null
|
||||
}
|
||||
|
||||
fun findPackage(fqName: FqName, scope: GlobalSearchScope): JavaPackage? {
|
||||
fun findPackage(fqName: FqName, scope: GlobalSearchScope = EverythingGlobalScope()): JavaPackage? {
|
||||
javaPackages[fqName]?.let { javaPackage ->
|
||||
javaPackage.virtualFile?.let { file ->
|
||||
if (file in scope) return javaPackage
|
||||
@@ -224,9 +247,9 @@ class JavacWrapper(
|
||||
packageSourceAnnotations[fqName] ?: emptyList()
|
||||
|
||||
fun findClassesFromPackage(fqName: FqName): List<JavaClass> =
|
||||
javaClasses
|
||||
.filterKeys { it?.parentOrNull() == fqName }
|
||||
.flatMap { it.value.withInnerClasses() } +
|
||||
javaClassDeclarations
|
||||
.filterKeys { it.packageFqName == fqName }
|
||||
.map { getTreeBasedClass(it.key)!! } +
|
||||
elements.getPackageElement(fqName.asString())
|
||||
?.members()
|
||||
?.elements
|
||||
@@ -235,8 +258,9 @@ class JavacWrapper(
|
||||
.orEmpty()
|
||||
|
||||
fun knownClassNamesInPackage(fqName: FqName): Set<String> =
|
||||
javaClasses.filterKeys { it?.parentOrNull() == fqName }
|
||||
.mapTo(hashSetOf()) { it.value.name.asString() } +
|
||||
javaClassDeclarations
|
||||
.filterKeys { it.packageFqName == fqName }
|
||||
.mapTo(hashSetOf()) { it.key.shortClassName.asString() } +
|
||||
elements.getPackageElement(fqName.asString())
|
||||
?.members_field
|
||||
?.elements
|
||||
@@ -247,15 +271,15 @@ class JavacWrapper(
|
||||
fun getTreePath(tree: JCTree, compilationUnit: CompilationUnitTree): TreePath =
|
||||
trees.getPath(compilationUnit, tree)
|
||||
|
||||
fun getKotlinClassifier(fqName: FqName): JavaClass? =
|
||||
kotlinClassifiersCache.getKotlinClassifier(fqName)
|
||||
fun getKotlinClassifier(classId: ClassId): JavaClass? =
|
||||
kotlinClassifiersCache.getKotlinClassifier(classId)
|
||||
|
||||
fun isDeprecated(element: Element) = elements.isDeprecated(element)
|
||||
|
||||
fun isDeprecated(typeMirror: TypeMirror) = isDeprecated(types.asElement(typeMirror))
|
||||
|
||||
fun resolve(treePath: TreePath): JavaClassifier? =
|
||||
treePathResolverCache.resolve(treePath)
|
||||
classifierResolver.resolve(treePath)
|
||||
|
||||
fun toVirtualFile(javaFileObject: JavaFileObject): VirtualFile? =
|
||||
javaFileObject.toUri().let { uri ->
|
||||
@@ -267,18 +291,23 @@ class JavacWrapper(
|
||||
}
|
||||
}
|
||||
|
||||
fun hasKotlinPackage(fqName: FqName) =
|
||||
if (kotlinClassifiersCache.hasPackage(fqName)) {
|
||||
fqName
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
|
||||
fun isDeprecatedInJavaDoc(treePath: TreePath) =
|
||||
(trees.getDocCommentTree(treePath) as? DCTree.DCDocComment)?.comment?.isDeprecated ?: false
|
||||
|
||||
private inline fun <reified T> Iterable<T>.toJavacList() = JavacList.from(this)
|
||||
|
||||
private fun findClassInSymbols(fqName: String): SymbolBasedClass? {
|
||||
if (symbolBasedClassesCache.containsKey(fqName)) return symbolBasedClassesCache[fqName]
|
||||
|
||||
elements.getTypeElement(fqName)?.let { symbol ->
|
||||
SymbolBasedClass(symbol, this, symbol.classfile)
|
||||
}.let { symbolBasedClass ->
|
||||
symbolBasedClassesCache[fqName] = symbolBasedClass
|
||||
return symbolBasedClass
|
||||
}
|
||||
}
|
||||
private fun findClassInSymbols(fqName: String): SymbolBasedClass? =
|
||||
elements.getTypeElement(fqName)?.let { symbol ->
|
||||
SymbolBasedClass(symbol, this, symbol.classfile)
|
||||
}
|
||||
|
||||
private fun findPackageInSymbols(fqName: String): SymbolBasedPackage? {
|
||||
if (symbolBasedPackagesCache.containsKey(fqName)) return symbolBasedPackagesCache[fqName]
|
||||
@@ -322,12 +351,9 @@ class JavacWrapper(
|
||||
|
||||
}
|
||||
|
||||
private fun TreeBasedClass.withInnerClasses(): List<TreeBasedClass> =
|
||||
listOf(this) + innerClasses.values.flatMap { it.withInnerClasses() }
|
||||
|
||||
private fun Symbol.PackageSymbol.findClass(name: String): SymbolBasedClass? {
|
||||
val nameParts = name.replace("$", ".").split(".")
|
||||
var symbol = members_field.getElementsByName(names.fromString(nameParts.first()))
|
||||
var symbol = members_field?.getElementsByName(names.fromString(nameParts.first()))
|
||||
?.firstOrNull() as? Symbol.ClassSymbol ?: return null
|
||||
if (nameParts.size > 1) {
|
||||
symbol.complete()
|
||||
@@ -340,4 +366,4 @@ class JavacWrapper(
|
||||
return symbol.let { SymbolBasedClass(it, this@JavacWrapper, it.classfile) }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -18,51 +18,73 @@ package org.jetbrains.kotlin.javac
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.load.java.JavaVisibilities
|
||||
import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.javac.wrappers.trees.find
|
||||
import org.jetbrains.kotlin.javac.wrappers.trees.findInner
|
||||
import org.jetbrains.kotlin.javac.wrappers.trees.tryToResolveByFqName
|
||||
import org.jetbrains.kotlin.javac.wrappers.trees.tryToResolveInJavaLang
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.VirtualFileBoundJavaClass
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
|
||||
|
||||
class KotlinClassifiersCache(sourceFiles: Collection<KtFile>,
|
||||
private val javac: JavacWrapper) {
|
||||
|
||||
private val kotlinClasses: Map<FqName?, KtClassOrObject?> = sourceFiles.flatMap { ktFile ->
|
||||
ktFile.collectDescendantsOfType<KtClassOrObject>().map { it.fqName to it } +
|
||||
(ktFile.javaFileFacadeFqName to null)
|
||||
}.toMap()
|
||||
private val kotlinPackages = hashSetOf<FqName>()
|
||||
private val kotlinClasses: Map<ClassId?, KtClassOrObject?> =
|
||||
sourceFiles.flatMap { ktFile ->
|
||||
kotlinPackages.add(ktFile.packageFqName)
|
||||
ktFile.declarations
|
||||
.filterIsInstance<KtClassOrObject>()
|
||||
.map { it.computeClassId() to it }
|
||||
}.toMap()
|
||||
|
||||
private val classifiers = hashMapOf<FqName, JavaClass>()
|
||||
private val classifiers = hashMapOf<ClassId, JavaClass>()
|
||||
|
||||
fun getKotlinClassifier(fqName: FqName) = classifiers[fqName] ?: createClassifier(fqName)
|
||||
fun getKotlinClassifier(classId: ClassId) = classifiers[classId] ?: createClassifier(classId)
|
||||
|
||||
private fun createClassifier(fqName: FqName): JavaClass? {
|
||||
if (!kotlinClasses.containsKey(fqName)) return null
|
||||
val kotlinClassifier = kotlinClasses[fqName] ?: return null
|
||||
fun createMockKotlinClassifier(classifier: KtClassOrObject,
|
||||
classId: ClassId) = MockKotlinClassifier(classId,
|
||||
classifier,
|
||||
this,
|
||||
javac)
|
||||
.apply { classifiers[classId] = this }
|
||||
|
||||
return MockKotlinClassifier(fqName,
|
||||
kotlinClassifier,
|
||||
kotlinClassifier.typeParameters.isNotEmpty(),
|
||||
javac)
|
||||
.apply { classifiers[fqName] = this }
|
||||
fun hasPackage(packageFqName: FqName) = kotlinPackages.contains(packageFqName)
|
||||
|
||||
private fun createClassifier(classId: ClassId): JavaClass? {
|
||||
if (classId.isNestedClass) {
|
||||
classifiers[classId]?.let { return it }
|
||||
val pathSegments = classId.relativeClassName.pathSegments().map { it.asString() }
|
||||
val outerClassId = ClassId(classId.packageFqName, Name.identifier(pathSegments.first()))
|
||||
var outerClass: JavaClass = kotlinClasses[outerClassId]?.let { createMockKotlinClassifier(it, outerClassId) } ?: return null
|
||||
|
||||
pathSegments.drop(1).forEach {
|
||||
outerClass = outerClass.findInnerClass(Name.identifier(it)) ?: return null
|
||||
}
|
||||
|
||||
return outerClass.apply { classifiers[classId] = this }
|
||||
}
|
||||
|
||||
val kotlinClassifier = kotlinClasses[classId] ?: return null
|
||||
|
||||
return createMockKotlinClassifier(kotlinClassifier, classId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MockKotlinClassifier(override val fqName: FqName,
|
||||
class MockKotlinClassifier(val classId: ClassId,
|
||||
private val classOrObject: KtClassOrObject,
|
||||
val hasTypeParameters: Boolean,
|
||||
private val cache: KotlinClassifiersCache,
|
||||
private val javac: JavacWrapper) : VirtualFileBoundJavaClass {
|
||||
|
||||
override val fqName: FqName
|
||||
get() = classId.asSingleFqName()
|
||||
|
||||
override val isAbstract: Boolean
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
|
||||
@@ -73,32 +95,26 @@ class MockKotlinClassifier(override val fqName: FqName,
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
|
||||
override val visibility: Visibility
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
get() = when (classOrObject.visibilityModifierType()) {
|
||||
null, KtTokens.PUBLIC_KEYWORD -> Visibilities.PUBLIC
|
||||
KtTokens.PRIVATE_KEYWORD -> Visibilities.PRIVATE
|
||||
KtTokens.PROTECTED_KEYWORD -> Visibilities.PROTECTED
|
||||
else -> JavaVisibilities.PACKAGE_VISIBILITY
|
||||
}
|
||||
|
||||
override val typeParameters: List<JavaTypeParameter>
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
|
||||
override val supertypes: Collection<JavaClassifierType>
|
||||
get() = classOrObject.superTypeListEntries
|
||||
.map { superTypeListEntry ->
|
||||
val userType = superTypeListEntry.typeAsUserType
|
||||
arrayListOf<String>().apply {
|
||||
userType?.referencedName?.let { add(it) }
|
||||
var qualifier = userType?.qualifier
|
||||
while (qualifier != null) {
|
||||
qualifier.referencedName?.let { add(it) }
|
||||
qualifier = qualifier.qualifier
|
||||
}
|
||||
}.reversed().joinToString(separator = ".") { it }
|
||||
}
|
||||
.mapNotNull { resolveSupertype(it, classOrObject, javac) }
|
||||
get() = javac.kotlinSupertypeResolver.resolveSupertypes(classOrObject)
|
||||
.mapNotNull { javac.getKotlinClassifier(it) ?: javac.findClass(it) }
|
||||
.map { MockKotlinClassifierType(it) }
|
||||
|
||||
val innerClasses: Collection<JavaClass>
|
||||
get() = classOrObject.declarations.filterIsInstance<KtClassOrObject>()
|
||||
.mapNotNull { nestedClassOrObject ->
|
||||
nestedClassOrObject.fqName?.let {
|
||||
javac.getKotlinClassifier(it)
|
||||
nestedClassOrObject.computeClassId()?.let {
|
||||
cache.createMockKotlinClassifier(nestedClassOrObject, it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +140,13 @@ class MockKotlinClassifier(override val fqName: FqName,
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
|
||||
override val fields: Collection<JavaField>
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
get() = classOrObject.declarations
|
||||
.filterIsInstance<KtProperty>()
|
||||
.map(::MockKotlinField) + classOrObject.companionObjects.flatMap {
|
||||
it.declarations
|
||||
.filterIsInstance<KtProperty>()
|
||||
.map(::MockKotlinField)
|
||||
}
|
||||
|
||||
override val constructors: Collection<JavaConstructor>
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
@@ -149,6 +171,12 @@ class MockKotlinClassifier(override val fqName: FqName,
|
||||
override fun findInnerClass(name: Name) =
|
||||
innerClasses.find { it.name == name }
|
||||
|
||||
val typeParametersNumber: Int
|
||||
get() = classOrObject.typeParameters.size
|
||||
|
||||
val hasTypeParameters: Boolean
|
||||
get() = typeParametersNumber > 0
|
||||
|
||||
}
|
||||
|
||||
class MockKotlinClassifierType(override val classifier: JavaClassifier) : JavaClassifierType {
|
||||
@@ -176,88 +204,39 @@ class MockKotlinClassifierType(override val classifier: JavaClassifier) : JavaCl
|
||||
|
||||
}
|
||||
|
||||
private fun resolveSupertype(name: String,
|
||||
classOrObject: KtClassOrObject,
|
||||
javac: JavacWrapper): JavaClass? {
|
||||
val nameParts = name.split(".")
|
||||
val ktFile = classOrObject.containingKtFile
|
||||
class MockKotlinField(private val property: KtProperty) : JavaField {
|
||||
override val name: Name
|
||||
get() = property.nameAsSafeName
|
||||
override val annotations: Collection<JavaAnnotation>
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
override val isDeprecatedInJavaDoc: Boolean
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
override val isAbstract: Boolean
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
override val isStatic: Boolean
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
override val isFinal: Boolean
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
override val visibility: Visibility
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
override val containingClass: JavaClass
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
override val isEnumEntry: Boolean
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
override val type: JavaType
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
override val initializerValue: Any?
|
||||
get() {
|
||||
if (!property.hasModifier(KtTokens.CONST_KEYWORD)) return null
|
||||
val initializer = property.initializer ?: return null
|
||||
|
||||
tryToResolveInner(name, classOrObject, javac, nameParts)?.let { return it }
|
||||
ktFile.tryToResolvePackageClass(name, javac, nameParts)?.let { return it }
|
||||
tryToResolveByFqName(name, javac)?.let { return it }
|
||||
ktFile.tryToResolveSingleTypeImport(name, javac, nameParts)?.let { return it }
|
||||
ktFile.tryToResolveTypeImportOnDemand(name, javac, nameParts)?.let { return it }
|
||||
tryToResolveInJavaLang(name, javac)?.let { return it }
|
||||
return initializer.text.toIntOrNull() ?: initializer.text
|
||||
}
|
||||
override val hasConstantNotNullInitializer: Boolean
|
||||
get() = throw UnsupportedOperationException("Should not be called")
|
||||
|
||||
return null
|
||||
override fun findAnnotation(fqName: FqName) = throw UnsupportedOperationException("Should not be called")
|
||||
}
|
||||
|
||||
private fun tryToResolveInner(name: String,
|
||||
classOrObject: KtClassOrObject,
|
||||
javac: JavacWrapper,
|
||||
nameParts: List<String>) =
|
||||
classOrObject.containingClassOrObject?.let { containingClass ->
|
||||
containingClass.fqName?.let {
|
||||
javac.findClass(it) ?: javac.getKotlinClassifier(it)
|
||||
}
|
||||
}?.findInner(name, javac, nameParts)
|
||||
|
||||
private fun KtFile.tryToResolvePackageClass(name: String,
|
||||
javac: JavacWrapper,
|
||||
nameParts: List<String> = emptyList()): JavaClass? {
|
||||
return if (nameParts.size > 1) {
|
||||
find(FqName("${packageFqName.asString()}.${nameParts.first()}"), javac, nameParts)
|
||||
}
|
||||
else {
|
||||
javac.findClass(FqName("${packageFqName.asString()}.$name"))
|
||||
?: javac.getKotlinClassifier(FqName("${packageFqName.asString()}.$name"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtFile.tryToResolveSingleTypeImport(name: String,
|
||||
javac: JavacWrapper,
|
||||
nameParts: List<String> = emptyList()): JavaClass? {
|
||||
if (nameParts.size > 1) {
|
||||
val foundImports = importDirectives.filter { it.text.endsWith(".${nameParts.first()}") }
|
||||
foundImports.forEach { importDirective ->
|
||||
importDirective.importedFqName?.let { importedFqName ->
|
||||
find(importedFqName, javac, nameParts)?.let { importedClass ->
|
||||
return importedClass
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
else {
|
||||
return importDirectives.find { importDirective ->
|
||||
importDirective.text.endsWith(".$name")
|
||||
}?.let { importDirective ->
|
||||
importDirective.importedFqName?.let { fqName ->
|
||||
javac.findClass(fqName) ?: javac.getKotlinClassifier(fqName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtFile.tryToResolveTypeImportOnDemand(name: String,
|
||||
javac: JavacWrapper,
|
||||
nameParts: List<String> = emptyList()): JavaClass? {
|
||||
val packagesWithAsterisk = importDirectives.filter { it.text.endsWith("*") }
|
||||
|
||||
if (nameParts.size > 1) {
|
||||
packagesWithAsterisk.forEach { importDirective ->
|
||||
find(FqName("${importDirective.importedFqName?.asString()}.${nameParts.first()}"),
|
||||
javac,
|
||||
nameParts)?.let { return it }
|
||||
}
|
||||
return null
|
||||
}
|
||||
else {
|
||||
packagesWithAsterisk.forEach { importDirective ->
|
||||
val fqName = "${importDirective.importedFqName?.asString()}.$name".let(::FqName)
|
||||
javac.findClass(fqName)?.let { return it } ?: javac.getKotlinClassifier(fqName)?.let { return it }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
private fun KtClassOrObject.computeClassId(): ClassId? =
|
||||
containingClassOrObject?.computeClassId()?.createNestedClassId(nameAsSafeName) ?: fqName?.let { ClassId.topLevel(it) }
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.javac
|
||||
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
|
||||
interface KotlinSupertypeResolver {
|
||||
fun resolveSupertypes(classOrObject: KtClassOrObject): List<ClassId>
|
||||
}
|
||||
+12
-2
@@ -31,6 +31,7 @@ import javax.lang.model.element.TypeElement
|
||||
import javax.lang.model.element.VariableElement
|
||||
import javax.lang.model.type.NoType
|
||||
import javax.lang.model.type.TypeKind
|
||||
import javax.lang.model.type.TypeMirror
|
||||
import javax.tools.JavaFileObject
|
||||
|
||||
class SymbolBasedClass(
|
||||
@@ -61,10 +62,11 @@ class SymbolBasedClass(
|
||||
get() = FqName(element.qualifiedName.toString())
|
||||
|
||||
override val supertypes: Collection<JavaClassifierType>
|
||||
get() = element.interfaces.toMutableList()
|
||||
get() = arrayListOf<TypeMirror>()
|
||||
.apply {
|
||||
element.superclass.takeIf { it !is NoType }?.let(this::add)
|
||||
}
|
||||
.apply { addAll(element.interfaces) }
|
||||
.mapTo(arrayListOf()) { SymbolBasedClassifierType(it, javac) }
|
||||
.apply {
|
||||
if (isEmpty() && element.qualifiedName.toString() != CommonClassNames.JAVA_LANG_OBJECT) {
|
||||
@@ -97,9 +99,17 @@ class SymbolBasedClass(
|
||||
|
||||
override val methods: Collection<JavaMethod>
|
||||
get() = element.enclosedElements
|
||||
.filter { it.kind == ElementKind.METHOD }
|
||||
.filter { it.kind == ElementKind.METHOD && !isEnumValuesOrValueOf(it as ExecutableElement)}
|
||||
.map { SymbolBasedMethod(it as ExecutableElement, javac) }
|
||||
|
||||
private fun isEnumValuesOrValueOf(method: ExecutableElement): Boolean {
|
||||
return isEnum && when (method.simpleName.toString()) {
|
||||
"values" -> method.parameters.isEmpty()
|
||||
"valueOf" -> method.parameters.let { it.size == 1 && it.first().asType().toString() == "java.lang.String" }
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override val fields: Collection<JavaField>
|
||||
get() = element.enclosedElements
|
||||
.filter { it.kind.isField && Name.isValidIdentifier(it.simpleName.toString()) }
|
||||
|
||||
+4
-3
@@ -32,7 +32,7 @@ sealed class SymbolBasedAnnotationArgument(
|
||||
companion object {
|
||||
fun create(value: Any, name: Name, javac: JavacWrapper): JavaAnnotationArgument = when (value) {
|
||||
is AnnotationMirror -> SymbolBasedAnnotationAsAnnotationArgument(value, name, javac)
|
||||
is VariableElement -> SymbolBasedReferenceAnnotationArgument(value, javac)
|
||||
is VariableElement -> SymbolBasedReferenceAnnotationArgument(value, name, javac)
|
||||
is TypeMirror -> SymbolBasedClassObjectAnnotationArgument(value, name, javac)
|
||||
is Collection<*> -> arrayAnnotationArguments(value, name, javac)
|
||||
is AnnotationValue -> create(value.value, name, javac)
|
||||
@@ -59,10 +59,11 @@ class SymbolBasedAnnotationAsAnnotationArgument(
|
||||
|
||||
class SymbolBasedReferenceAnnotationArgument(
|
||||
val element: VariableElement,
|
||||
name: Name,
|
||||
javac: JavacWrapper
|
||||
) : SymbolBasedAnnotationArgument(Name.identifier(element.simpleName.toString()), javac), JavaEnumValueAnnotationArgument {
|
||||
) : SymbolBasedAnnotationArgument(name, javac), JavaEnumValueAnnotationArgument {
|
||||
override val entryName: Name?
|
||||
get() = Name.identifier(element.simpleName.toString())
|
||||
get() = Name.identifier(name)
|
||||
|
||||
override fun resolve() = SymbolBasedField(element, javac)
|
||||
|
||||
|
||||
+17
-6
@@ -82,18 +82,29 @@ class SymbolBasedClassifierType<out T : TypeMirror>(
|
||||
}
|
||||
|
||||
override val typeArguments: List<JavaType>
|
||||
get() = if (typeMirror.kind == TypeKind.DECLARED) {
|
||||
(typeMirror as DeclaredType).typeArguments.map { create(it, javac) }
|
||||
}
|
||||
else {
|
||||
emptyList()
|
||||
get() {
|
||||
if (typeMirror.kind != TypeKind.DECLARED) return emptyList()
|
||||
|
||||
val arguments = arrayListOf<JavaType>()
|
||||
var type = typeMirror as DeclaredType
|
||||
var staticType = false
|
||||
|
||||
while (!staticType) {
|
||||
if (type.asElement().isStatic) {
|
||||
staticType = true
|
||||
}
|
||||
arguments.addAll(type.typeArguments.map { create(it, javac) })
|
||||
type = type.enclosingType as? DeclaredType ?: return arguments
|
||||
}
|
||||
|
||||
return arguments
|
||||
}
|
||||
|
||||
override val isRaw: Boolean
|
||||
get() = when {
|
||||
typeMirror !is DeclaredType -> false
|
||||
(typeMirror.asElement() as TypeElement).typeParameters.isEmpty() -> false
|
||||
else -> typeMirror.typeArguments.isEmpty()
|
||||
else -> typeMirror.typeArguments.isEmpty() || (typeMirror.asElement() as TypeElement).typeParameters.size != typeMirror.typeArguments.size
|
||||
}
|
||||
|
||||
override val classifierQualifiedName: String
|
||||
|
||||
@@ -62,7 +62,7 @@ internal fun TypeElement.computeClassId(): ClassId? {
|
||||
internal fun ExecutableElement.valueParameters(javac: JavacWrapper): List<JavaValueParameter> =
|
||||
parameters.mapIndexed { index, parameter ->
|
||||
SymbolBasedValueParameter(parameter,
|
||||
parameter.simpleName.toString(),
|
||||
if (!parameter.simpleName.contentEquals("arg$index")) parameter.simpleName.toString() else "p$index",
|
||||
index == parameters.lastIndex && isVarArgs,
|
||||
javac)
|
||||
}
|
||||
|
||||
+348
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.javac.wrappers.trees
|
||||
|
||||
import com.sun.source.tree.Tree
|
||||
import com.sun.source.util.TreePath
|
||||
import com.sun.tools.javac.tree.JCTree
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.javac.JavacWrapper
|
||||
import org.jetbrains.kotlin.javac.MockKotlinClassifier
|
||||
import org.jetbrains.kotlin.load.java.JavaVisibilities
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClassifier
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class ClassifierResolver(private val javac: JavacWrapper) {
|
||||
|
||||
private val cache = hashMapOf<Tree, JavaClassifier?>()
|
||||
private val beingResolved = hashSetOf<Tree>()
|
||||
|
||||
fun resolve(treePath: TreePath): JavaClassifier? = with (treePath) {
|
||||
if (cache.containsKey(leaf)) return cache[leaf]
|
||||
if (treePath.leaf in beingResolved) return null
|
||||
beingResolved(treePath.leaf)
|
||||
|
||||
return tryToResolve().apply {
|
||||
cache[leaf] = this
|
||||
removeBeingResolved(treePath.leaf)
|
||||
}
|
||||
}
|
||||
|
||||
// to avoid StackOverflow when there are cyclic dependencies
|
||||
private fun beingResolved(tree: Tree) {
|
||||
if (tree is JCTree.JCTypeApply) {
|
||||
beingResolved(tree.clazz)
|
||||
}
|
||||
if (tree is JCTree.JCFieldAccess) {
|
||||
beingResolved.add(tree)
|
||||
beingResolved(tree.selected)
|
||||
}
|
||||
else beingResolved.add(tree)
|
||||
}
|
||||
|
||||
private fun removeBeingResolved(tree: Tree) {
|
||||
if (tree is JCTree.JCTypeApply) {
|
||||
beingResolved(tree.clazz)
|
||||
}
|
||||
if (tree is JCTree.JCFieldAccess) {
|
||||
beingResolved.remove(tree)
|
||||
beingResolved(tree.selected)
|
||||
}
|
||||
else beingResolved.remove(tree)
|
||||
}
|
||||
|
||||
private fun pathSegments(path: String): List<String> {
|
||||
val pathSegments = arrayListOf<String>()
|
||||
var numberOfBrackets = 0
|
||||
var builder = StringBuilder()
|
||||
path.forEach { char ->
|
||||
when (char) {
|
||||
'<' -> numberOfBrackets++
|
||||
'>' -> numberOfBrackets--
|
||||
'.' -> {
|
||||
if (numberOfBrackets == 0) {
|
||||
pathSegments.add(builder.toString())
|
||||
builder = StringBuilder()
|
||||
}
|
||||
}
|
||||
'@' -> {}
|
||||
else -> if (numberOfBrackets == 0) builder.append(char)
|
||||
}
|
||||
}
|
||||
|
||||
return pathSegments.apply { add(builder.toString()) }
|
||||
}
|
||||
|
||||
private fun TreePath.tryToResolve(): JavaClassifier? {
|
||||
val pathSegments = pathSegments(leaf.toString())
|
||||
|
||||
return tryToGetTypeParameterFromMethod()?.let { return it } ?:
|
||||
createResolutionScope(this).findClass(pathSegments.first(), pathSegments)
|
||||
}
|
||||
|
||||
private fun TreePath.tryToGetTypeParameterFromMethod(): TreeBasedTypeParameter? =
|
||||
(find { it is JCTree.JCMethodDecl } as? JCTree.JCMethodDecl)
|
||||
?.typarams?.find { it.name.toString() == leaf.toString() }
|
||||
?.let {
|
||||
TreeBasedTypeParameter(it,
|
||||
javac.getTreePath(it, compilationUnit),
|
||||
javac)
|
||||
}
|
||||
|
||||
private fun createResolutionScope(treePath: TreePath): Scope = CurrentClassAndInnerScope(javac, treePath)
|
||||
|
||||
}
|
||||
|
||||
private abstract class Scope(protected val javac: JavacWrapper,
|
||||
protected val treePath: TreePath) {
|
||||
|
||||
abstract val parent: Scope?
|
||||
|
||||
abstract fun findClass(name: String, pathSegments: List<String>): JavaClassifier?
|
||||
|
||||
protected fun getJavaClassFromPathSegments(javaClass: JavaClass,
|
||||
pathSegments: List<String>) =
|
||||
if (pathSegments.size == 1) {
|
||||
javaClass
|
||||
}
|
||||
else {
|
||||
javaClass.findInnerOrNested(pathSegments.drop(1))
|
||||
}
|
||||
|
||||
protected fun findImport(pathSegments: List<String>): JavaClass? {
|
||||
pathSegments.forEachIndexed { index, _ ->
|
||||
if (index == pathSegments.lastIndex) return null
|
||||
val packageFqName = pathSegments.dropLast(index + 1).joinToString(separator = ".")
|
||||
findPackage(packageFqName)?.let { pack ->
|
||||
val className = pathSegments.takeLast(index + 1)
|
||||
return findJavaOrKotlinClass(ClassId(pack, Name.identifier(className.first())))?.let { javaClass ->
|
||||
getJavaClassFromPathSegments(javaClass, className)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
protected fun findJavaOrKotlinClass(classId: ClassId) = javac.findClass(classId) ?: javac.getKotlinClassifier(classId)
|
||||
|
||||
protected fun JavaClass.findInnerOrNested(name: Name, checkedSupertypes: HashSet<JavaClass> = hashSetOf()): JavaClass? {
|
||||
findVisibleInnerOrNestedClass(name)?.let {
|
||||
checkedSupertypes.addAll(collectAllSupertypes())
|
||||
return it
|
||||
}
|
||||
|
||||
return supertypes
|
||||
.mapNotNull {
|
||||
(it.classifier as? JavaClass)?.let { supertype ->
|
||||
if (supertype !in checkedSupertypes) {
|
||||
supertype.findInnerOrNested(name, checkedSupertypes)
|
||||
} else null
|
||||
}
|
||||
}.singleOrNull()
|
||||
}
|
||||
|
||||
protected fun findPackage(packageName: String): FqName? {
|
||||
val fqName = if (packageName.isNotBlank()) FqName(packageName) else FqName.ROOT
|
||||
javac.hasKotlinPackage(fqName)?.let { return it }
|
||||
|
||||
return javac.findPackage(fqName)?.fqName
|
||||
}
|
||||
|
||||
private fun JavaClass.findVisibleInnerOrNestedClass(name: Name) = findInnerClass(name)?.let { innerOrNestedClass ->
|
||||
when (innerOrNestedClass.visibility) {
|
||||
Visibilities.PRIVATE -> null
|
||||
JavaVisibilities.PACKAGE_VISIBILITY -> {
|
||||
val classId = (innerOrNestedClass as? MockKotlinClassifier)?.classId ?: innerOrNestedClass.computeClassId()
|
||||
if (classId?.packageFqName?.asString() == (treePath.compilationUnit.packageName?.toString() ?: "")) innerOrNestedClass else null
|
||||
}
|
||||
else -> innerOrNestedClass
|
||||
}
|
||||
}
|
||||
|
||||
private fun JavaClass.collectAllSupertypes(): Set<JavaClass> =
|
||||
hashSetOf(this).apply {
|
||||
supertypes.mapNotNull { it.classifier as? JavaClass }.forEach { addAll(it.collectAllSupertypes()) }
|
||||
}
|
||||
|
||||
private fun JavaClass.findInnerOrNested(pathSegments: List<String>): JavaClass? =
|
||||
pathSegments.fold(this) { javaClass, it -> javaClass.findInnerOrNested(Name.identifier(it)) ?: return null }
|
||||
|
||||
}
|
||||
|
||||
private class GlobalScope(javac: JavacWrapper, treePath: TreePath) : Scope(javac, treePath) {
|
||||
|
||||
override val parent: Scope?
|
||||
get() = null
|
||||
|
||||
override fun findClass(name: String, pathSegments: List<String>): JavaClass? {
|
||||
findByFqName(pathSegments)?.let { return it }
|
||||
|
||||
return findJavaOrKotlinClass(classId("java.lang", name))?.let { javaClass ->
|
||||
getJavaClassFromPathSegments(javaClass, pathSegments)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findByFqName(pathSegments: List<String>): JavaClass? {
|
||||
pathSegments.forEachIndexed { index, _ ->
|
||||
if (index != 0) {
|
||||
val packageFqName = pathSegments.take(index).joinToString(separator = ".")
|
||||
findPackage(packageFqName)?.let { pack ->
|
||||
val className = pathSegments.drop(index)
|
||||
findJavaOrKotlinClass(ClassId(pack, Name.identifier(className.first())))?.let { javaClass ->
|
||||
return getJavaClassFromPathSegments(javaClass, className)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try to find in <root>
|
||||
return findJavaOrKotlinClass(classId("", pathSegments.first()))?.let { javaClass ->
|
||||
getJavaClassFromPathSegments(javaClass, pathSegments)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ImportOnDemandScope(javac: JavacWrapper,
|
||||
treePath: TreePath) : Scope(javac, treePath) {
|
||||
|
||||
override val parent: Scope
|
||||
get() = GlobalScope(javac, treePath)
|
||||
|
||||
override fun findClass(name: String, pathSegments: List<String>): JavaClassifier? {
|
||||
asteriskImports()
|
||||
.mapNotNullTo(hashSetOf()) { findImport("$it$name".split(".")) }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?.let {
|
||||
return it.singleOrNull()?.let { javaClass ->
|
||||
getJavaClassFromPathSegments(javaClass, pathSegments)
|
||||
}
|
||||
}
|
||||
|
||||
return parent.findClass(name, pathSegments)
|
||||
}
|
||||
|
||||
private fun asteriskImports() =
|
||||
treePath.compilationUnit.imports
|
||||
.mapNotNull {
|
||||
val fqName = it.qualifiedIdentifier.toString()
|
||||
if (fqName.endsWith("*")) {
|
||||
fqName.dropLast(1)
|
||||
}
|
||||
else null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class PackageScope(javac: JavacWrapper,
|
||||
treePath: TreePath) : Scope(javac, treePath) {
|
||||
|
||||
override val parent: Scope
|
||||
get() = ImportOnDemandScope(javac, treePath)
|
||||
|
||||
override fun findClass(name: String, pathSegments: List<String>): JavaClassifier? {
|
||||
findJavaOrKotlinClass(classId(treePath.compilationUnit.packageName?.toString() ?: "", name))
|
||||
?.let { javaClass ->
|
||||
return getJavaClassFromPathSegments(javaClass, pathSegments)
|
||||
}
|
||||
|
||||
return parent.findClass(name, pathSegments)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class SingleTypeImportScope(javac: JavacWrapper,
|
||||
treePath: TreePath) : Scope(javac, treePath) {
|
||||
|
||||
override val parent: Scope
|
||||
get() = PackageScope(javac, treePath)
|
||||
|
||||
override fun findClass(name: String, pathSegments: List<String>): JavaClassifier? {
|
||||
val imports = imports(name).toSet().takeIf { it.isNotEmpty() }
|
||||
?: return parent.findClass(name, pathSegments)
|
||||
|
||||
imports.singleOrNull() ?: return null
|
||||
|
||||
return findImport(imports.first().split("."))
|
||||
?.let { javaClass -> getJavaClassFromPathSegments(javaClass, pathSegments) }
|
||||
}
|
||||
|
||||
private fun imports(firstSegment: String) =
|
||||
(treePath.compilationUnit as JCTree.JCCompilationUnit).imports
|
||||
.mapNotNull {
|
||||
val fqName = it.qualifiedIdentifier.toString()
|
||||
if (fqName.endsWith(".$firstSegment")) {
|
||||
fqName
|
||||
}
|
||||
else null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class CurrentClassAndInnerScope(javac: JavacWrapper,
|
||||
treePath: TreePath) : Scope(javac, treePath) {
|
||||
|
||||
override val parent: Scope
|
||||
get() = SingleTypeImportScope(javac, treePath)
|
||||
|
||||
override fun findClass(name: String, pathSegments: List<String>): JavaClassifier? {
|
||||
val identifier = Name.identifier(name)
|
||||
treePath.enclosingClasses.forEach {
|
||||
(it as? TreeBasedClass)?.typeParameters
|
||||
?.find { typeParameter -> typeParameter.name == identifier }
|
||||
?.let { typeParameter -> return typeParameter }
|
||||
|
||||
it.findInnerOrNested(identifier)?.let { javaClass -> return getJavaClassFromPathSegments(javaClass, pathSegments) }
|
||||
|
||||
if (it.name == identifier && pathSegments.size == 1) return it
|
||||
}
|
||||
|
||||
return parent.findClass(name, pathSegments)
|
||||
}
|
||||
|
||||
private val TreePath.enclosingClasses: List<JavaClass>
|
||||
get() {
|
||||
val outerClasses = filterIsInstance<JCTree.JCClassDecl>()
|
||||
.dropWhile { it.extending == leaf || leaf in it.implementing }
|
||||
.asReversed()
|
||||
.map { it.simpleName.toString() }
|
||||
|
||||
val packageName = compilationUnit.packageName?.toString() ?: ""
|
||||
val outermostClassName = outerClasses.firstOrNull() ?: return emptyList()
|
||||
|
||||
val outermostClassId = classId(packageName, outermostClassName)
|
||||
var outermostClass = javac.findClass(outermostClassId) ?: return emptyList()
|
||||
|
||||
val classes = arrayListOf<JavaClass>()
|
||||
classes.add(outermostClass)
|
||||
|
||||
for (it in outerClasses.drop(1)) {
|
||||
outermostClass = outermostClass.findInnerClass(Name.identifier(it))
|
||||
?: throw AssertionError("Couldn't find a class ($it) that is surely defined in ${outermostClass.fqName?.asString()}")
|
||||
classes.add(outermostClass)
|
||||
}
|
||||
|
||||
return classes.reversed()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun classId(packageName: String = "", className: String) = ClassId(FqName(packageName), Name.identifier(className))
|
||||
+117
-18
@@ -16,38 +16,137 @@
|
||||
|
||||
package org.jetbrains.kotlin.javac.wrappers.trees
|
||||
|
||||
import com.sun.source.tree.CompilationUnitTree
|
||||
import com.sun.source.util.TreePath
|
||||
import com.sun.tools.javac.tree.JCTree
|
||||
import org.jetbrains.kotlin.javac.JavacWrapper
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaAnnotation
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaAnnotationArgument
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaElement
|
||||
import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class TreeBasedAnnotation(
|
||||
private val annotation: JCTree.JCAnnotation,
|
||||
private val compilationUnit: CompilationUnitTree,
|
||||
private val javac: JavacWrapper
|
||||
val annotation: JCTree.JCAnnotation,
|
||||
val treePath: TreePath,
|
||||
val javac: JavacWrapper
|
||||
) : JavaElement, JavaAnnotation {
|
||||
|
||||
constructor(
|
||||
annotation: JCTree.JCAnnotation,
|
||||
treePath: TreePath,
|
||||
javac: JavacWrapper
|
||||
) : this(annotation, treePath.compilationUnit, javac)
|
||||
|
||||
override val arguments: Collection<JavaAnnotationArgument>
|
||||
get() = annotation.arguments.map { TreeBasedAnnotationArgument(Name.identifier(it.toString())) }
|
||||
get() = createAnnotationArguments(this, javac)
|
||||
|
||||
override val classId: ClassId?
|
||||
get() = resolve()?.computeClassId()
|
||||
get() = resolve()?.computeClassId() ?: ClassId.topLevel(FqName(annotation.annotationType.toString().substringAfter("@")))
|
||||
|
||||
override fun resolve() =
|
||||
javac.resolve(TreePath.getPath(compilationUnit, annotation.annotationType)) as? JavaClass
|
||||
javac.resolve(TreePath.getPath(treePath.compilationUnit, annotation.annotationType)) as? JavaClass
|
||||
|
||||
}
|
||||
|
||||
class TreeBasedAnnotationArgument(override val name: Name) : JavaAnnotationArgument, JavaElement
|
||||
sealed class TreeBasedAnnotationArgument(override val name: Name,
|
||||
val javac: JavacWrapper) : JavaAnnotationArgument, JavaElement
|
||||
|
||||
class TreeBasedLiteralAnnotationArgument(name: Name,
|
||||
override val value: Any?,
|
||||
javac: JavacWrapper) : TreeBasedAnnotationArgument(name, javac), JavaLiteralAnnotationArgument
|
||||
|
||||
class TreeBasedReferenceAnnotationArgument(name: Name,
|
||||
private val treePath: TreePath,
|
||||
private val field: JCTree.JCFieldAccess,
|
||||
javac: JavacWrapper) : TreeBasedAnnotationArgument(name, javac), JavaEnumValueAnnotationArgument {
|
||||
|
||||
override fun resolve(): JavaField? {
|
||||
val newTreePath = javac.getTreePath(field.selected, treePath.compilationUnit)
|
||||
val javaClass = javac.resolve(newTreePath) as? JavaClass ?: return null
|
||||
val fieldName = field.name.toString().let { Name.identifier(it) }
|
||||
|
||||
return javaClass.fields.find { it.name == fieldName }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TreeBasedArrayAnnotationArgument(val args: List<JavaAnnotationArgument>,
|
||||
name: Name,
|
||||
javac: JavacWrapper): TreeBasedAnnotationArgument(name, javac), JavaArrayAnnotationArgument {
|
||||
override fun getElements() = args
|
||||
|
||||
}
|
||||
|
||||
class TreeBasedJavaClassObjectAnnotationArgument(private val type: JCTree.JCExpression,
|
||||
name: Name,
|
||||
private val treePath: TreePath,
|
||||
javac: JavacWrapper): TreeBasedAnnotationArgument(name, javac), JavaClassObjectAnnotationArgument {
|
||||
|
||||
override fun getReferencedType(): JavaType =
|
||||
TreeBasedType.create(type, javac.getTreePath(type, treePath.compilationUnit), javac, emptyList())
|
||||
|
||||
}
|
||||
|
||||
class TreeBasedAnnotationAsAnnotationArgument(private val annotation: JCTree.JCAnnotation,
|
||||
name: Name,
|
||||
private val treePath: TreePath,
|
||||
javac: JavacWrapper): TreeBasedAnnotationArgument(name, javac), JavaAnnotationAsAnnotationArgument {
|
||||
override fun getAnnotation(): JavaAnnotation =
|
||||
TreeBasedAnnotation(annotation, javac.getTreePath(annotation, treePath.compilationUnit), javac )
|
||||
|
||||
}
|
||||
|
||||
private fun createAnnotationArguments(annotation: TreeBasedAnnotation,
|
||||
javac: JavacWrapper): Collection<JavaAnnotationArgument> {
|
||||
val arguments = annotation.annotation.arguments
|
||||
val javaClass = annotation.resolve() ?: return emptyList()
|
||||
val methods = javaClass.methods
|
||||
|
||||
if (arguments.size != methods.size) return emptyList()
|
||||
|
||||
return methods.mapIndexedNotNull { index, it ->
|
||||
createAnnotationArgument(arguments[index], it.name, annotation.treePath, javac, annotation)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAnnotationArgument(argument: JCTree.JCExpression,
|
||||
name: Name,
|
||||
treePath: TreePath,
|
||||
javac: JavacWrapper,
|
||||
annotation: TreeBasedAnnotation): JavaAnnotationArgument? =
|
||||
when (argument) {
|
||||
is JCTree.JCLiteral -> TreeBasedLiteralAnnotationArgument(name, argument.value, javac)
|
||||
is JCTree.JCFieldAccess -> {
|
||||
if (argument.name.contentEquals("class")) {
|
||||
TreeBasedJavaClassObjectAnnotationArgument(argument.selected, name, treePath, javac)
|
||||
} else {
|
||||
TreeBasedReferenceAnnotationArgument(name, treePath, argument, javac)
|
||||
}
|
||||
}
|
||||
is JCTree.JCAssign -> createAnnotationArgument(argument.rhs, name, treePath, javac, annotation)
|
||||
is JCTree.JCNewArray -> arrayAnnotationArguments(argument.elems, name, treePath, javac, annotation)
|
||||
is JCTree.JCAnnotation -> TreeBasedAnnotationAsAnnotationArgument(argument, name, treePath, javac)
|
||||
is JCTree.JCParens -> createAnnotationArgument(argument.expr, name, treePath, javac, annotation)
|
||||
is JCTree.JCBinary -> resolveArgumentValue(argument, annotation, name, treePath, javac)
|
||||
is JCTree.JCUnary -> resolveArgumentValue(argument, annotation, name, treePath, javac)
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun resolveArgumentValue(argument: JCTree.JCExpression,
|
||||
annotation: TreeBasedAnnotation,
|
||||
name: Name,
|
||||
treePath: TreePath,
|
||||
javac: JavacWrapper): JavaAnnotationArgument? {
|
||||
val containingAnnotation = annotation.resolve()
|
||||
val type = containingAnnotation?.methods?.find { it.name == name }?.returnType ?: return null
|
||||
val calculator = ValueCalculator(containingAnnotation, javac, treePath, type)
|
||||
|
||||
return calculator.getValue(argument)?.let { TreeBasedLiteralAnnotationArgument(name, it, javac) }
|
||||
}
|
||||
|
||||
private fun arrayAnnotationArguments(values: List<JCTree.JCExpression>,
|
||||
name: Name,
|
||||
treePath: TreePath,
|
||||
javac: JavacWrapper,
|
||||
annotation: TreeBasedAnnotation): JavaArrayAnnotationArgument =
|
||||
values.mapNotNull {
|
||||
if (it is JCTree.JCNewArray) {
|
||||
arrayAnnotationArguments(it.elems, name, treePath, javac, annotation)
|
||||
}
|
||||
else {
|
||||
createAnnotationArgument(it, name, treePath, javac, annotation)
|
||||
}
|
||||
}.let { TreeBasedArrayAnnotationArgument(it, name, javac) }
|
||||
+66
-27
@@ -50,54 +50,52 @@ class TreeBasedClass(
|
||||
annotations.find { it.classId?.asSingleFqName() == fqName }
|
||||
|
||||
override val isDeprecatedInJavaDoc: Boolean
|
||||
get() = false
|
||||
get() = javac.isDeprecatedInJavaDoc(treePath)
|
||||
|
||||
override val isAbstract: Boolean
|
||||
get() = tree.modifiers.isAbstract || (isAnnotationType && methods.any { it.isAbstract })
|
||||
get() = tree.modifiers.isAbstract || ((isAnnotationType || isEnum) && methods.any { it.isAbstract })
|
||||
|
||||
override val isStatic: Boolean
|
||||
get() = (outerClass?.isInterface ?: false) || tree.modifiers.isStatic
|
||||
get() = isEnum || isInterface || (outerClass?.isInterface ?: false) || tree.modifiers.isStatic
|
||||
|
||||
override val isFinal: Boolean
|
||||
get() = tree.modifiers.isFinal
|
||||
get() = isEnum || tree.modifiers.isFinal
|
||||
|
||||
override val visibility: Visibility
|
||||
get() = if (outerClass?.isInterface ?: false) PUBLIC else tree.modifiers.visibility
|
||||
get() = if (outerClass?.isInterface == true) PUBLIC else tree.modifiers.visibility
|
||||
|
||||
override val typeParameters: List<JavaTypeParameter>
|
||||
get() = tree.typeParameters.map { parameter ->
|
||||
TreeBasedTypeParameter(parameter, TreePath(treePath, parameter), javac)
|
||||
}
|
||||
|
||||
override val fqName: FqName =
|
||||
treePath.reversed()
|
||||
.filterIsInstance<JCTree.JCClassDecl>()
|
||||
.joinToString(
|
||||
separator = ".",
|
||||
prefix = "${treePath.compilationUnit.packageName}.",
|
||||
transform = JCTree.JCClassDecl::name
|
||||
)
|
||||
.let(::FqName)
|
||||
override val fqName: FqName
|
||||
get() = treePath.reversed()
|
||||
.filterIsInstance<JCTree.JCClassDecl>()
|
||||
.joinToString(
|
||||
separator = ".",
|
||||
transform = JCTree.JCClassDecl::name
|
||||
)
|
||||
.let { treePath.compilationUnit.packageName?.let { packageName -> FqName("$packageName.$it") } ?: FqName.topLevel(Name.identifier(it))}
|
||||
|
||||
override val supertypes: Collection<JavaClassifierType>
|
||||
get() = arrayListOf<JavaClassifierType>().apply {
|
||||
fun JCTree.mapToJavaClassifierType() = when {
|
||||
this is JCTree.JCTypeApply -> TreeBasedGenericClassifierType(this, TreePath(treePath, this), javac)
|
||||
this is JCTree.JCExpression -> TreeBasedNonGenericClassifierType(this, TreePath(treePath, this), javac)
|
||||
else -> null
|
||||
}
|
||||
|
||||
get() = arrayListOf<JavaClassifierType>().also { list ->
|
||||
if (isEnum) {
|
||||
javac.JAVA_LANG_ENUM?.let(this::add)
|
||||
createEnumSupertype(this, javac).let { list.add(it) }
|
||||
} else if (isAnnotationType) {
|
||||
javac.JAVA_LANG_ANNOTATION_ANNOTATION?.let(this::add)
|
||||
javac.JAVA_LANG_ANNOTATION_ANNOTATION?.let { list.add(it) }
|
||||
}
|
||||
|
||||
tree.implementing?.mapNotNull { it.mapToJavaClassifierType() }?.let(this::addAll)
|
||||
tree.extending?.let { it.mapToJavaClassifierType()?.let(this::add) }
|
||||
tree.extending?.let {
|
||||
(TreeBasedType.create(it, javac.getTreePath(it, treePath.compilationUnit), javac, emptyList()) as? JavaClassifierType)
|
||||
?.let { list.add(it) }
|
||||
}
|
||||
tree.implementing?.mapNotNull {
|
||||
TreeBasedType.create(it, javac.getTreePath(it, treePath.compilationUnit), javac, emptyList()) as? JavaClassifierType
|
||||
}?.let { list.addAll(it) }
|
||||
|
||||
if (isEmpty()) {
|
||||
javac.JAVA_LANG_OBJECT?.let(this::add)
|
||||
if (list.isEmpty()) {
|
||||
javac.JAVA_LANG_OBJECT?.let { list.add(it) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,3 +153,44 @@ class TreeBasedClass(
|
||||
override fun findInnerClass(name: Name) = innerClasses[name]
|
||||
|
||||
}
|
||||
|
||||
private fun createEnumSupertype(javaClass: JavaClass,
|
||||
javac: JavacWrapper) = object : JavaClassifierType {
|
||||
override val classifier: JavaClassifier?
|
||||
get() = javac.JAVA_LANG_ENUM
|
||||
|
||||
override val typeArguments: List<JavaType>
|
||||
get() = listOf(TypeArgument())
|
||||
|
||||
override val isRaw: Boolean
|
||||
get() = false
|
||||
override val annotations: Collection<JavaAnnotation>
|
||||
get() = emptyList()
|
||||
override val classifierQualifiedName: String
|
||||
get() = (classifier as? JavaClass)?.fqName?.asString() ?: ""
|
||||
override val presentableText: String
|
||||
get() = classifierQualifiedName
|
||||
override val isDeprecatedInJavaDoc: Boolean
|
||||
get() = false
|
||||
override fun findAnnotation(fqName: FqName) = null
|
||||
|
||||
private inner class TypeArgument : JavaClassifierType {
|
||||
override val classifier: JavaClassifier?
|
||||
get() = javaClass
|
||||
override val typeArguments: List<JavaType>
|
||||
get() = emptyList()
|
||||
override val isRaw: Boolean
|
||||
get() = false
|
||||
override val annotations: Collection<JavaAnnotation>
|
||||
get() = emptyList()
|
||||
override val classifierQualifiedName: String
|
||||
get() = javaClass.fqName!!.asString()
|
||||
override val presentableText: String
|
||||
get() = classifierQualifiedName
|
||||
override val isDeprecatedInJavaDoc: Boolean
|
||||
get() = false
|
||||
override fun findAnnotation(fqName: FqName) = null
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+106
-6
@@ -18,12 +18,15 @@ package org.jetbrains.kotlin.javac.wrappers.trees
|
||||
|
||||
import com.sun.source.util.TreePath
|
||||
import com.sun.tools.javac.code.Flags
|
||||
import com.sun.tools.javac.code.TypeTag
|
||||
import com.sun.tools.javac.tree.JCTree
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.javac.JavacWrapper
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaField
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaPrimitiveType
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaType
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
@@ -53,19 +56,16 @@ class TreeBasedField(
|
||||
get() = tree.modifiers.flags and Flags.ENUM.toLong() != 0L
|
||||
|
||||
override val type: JavaType
|
||||
get() = TreeBasedType.create(tree.getType(), treePath, javac)
|
||||
get() = TreeBasedType.create(tree.getType(), treePath, javac, annotations)
|
||||
|
||||
override val initializerValue: Any?
|
||||
get() = tree.init?.let { initExpr ->
|
||||
if (hasConstantNotNullInitializer && initExpr is JCTree.JCLiteral) {
|
||||
initExpr.value
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (hasConstantNotNullInitializer) ValueCalculator(containingClass, javac, treePath, type).getValue(initExpr) else null
|
||||
}
|
||||
|
||||
override val hasConstantNotNullInitializer: Boolean
|
||||
get() = tree.init?.let {
|
||||
if (it is JCTree.JCLiteral && it.value == null) return false
|
||||
val type = this.type
|
||||
|
||||
isFinal && ((type is TreeBasedPrimitiveType) ||
|
||||
@@ -73,4 +73,104 @@ class TreeBasedField(
|
||||
type.classifierQualifiedName == "java.lang.String"))
|
||||
} ?: false
|
||||
|
||||
}
|
||||
|
||||
class ValueCalculator(private val containingClass: JavaClass,
|
||||
private val javac: JavacWrapper,
|
||||
private val treePath: TreePath,
|
||||
private val type: JavaType) {
|
||||
fun getValue(expr: JCTree.JCExpression): Any? {
|
||||
return when (expr) {
|
||||
is JCTree.JCLiteral -> {
|
||||
if (expr.typetag == TypeTag.BOOLEAN) {
|
||||
expr.value != 0
|
||||
}
|
||||
else expr.value
|
||||
}
|
||||
is JCTree.JCIdent -> containingClass.fields
|
||||
.find { it.name == expr.name.toString().let { Name.identifier(it) } }
|
||||
?.initializerValue
|
||||
is JCTree.JCFieldAccess -> fieldAccessValue(expr)
|
||||
is JCTree.JCBinary -> binaryInitializerValue(expr)
|
||||
is JCTree.JCParens -> getValue(expr.expr)
|
||||
is JCTree.JCUnary -> unaryInitializerValue(expr)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun fieldAccessValue(value: JCTree.JCFieldAccess): Any? {
|
||||
val newTreePath = javac.getTreePath(value.selected, treePath.compilationUnit)
|
||||
val javaClass = javac.resolve(newTreePath) as? JavaClass ?: return null
|
||||
val fieldName = value.name.toString().let { Name.identifier(it) }
|
||||
|
||||
return javaClass.fields
|
||||
.find { it.name == fieldName }
|
||||
?.initializerValue
|
||||
}
|
||||
|
||||
private fun unaryInitializerValue(value: JCTree.JCUnary): Any? {
|
||||
val argValue = getValue(value.arg)
|
||||
return when (value.tag) {
|
||||
JCTree.Tag.COMPL -> (argValue as? Int)?.inv()
|
||||
JCTree.Tag.NOT -> (argValue as? Boolean)?.let { !it }
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun binaryInitializerValue(value: JCTree.JCBinary): Any? {
|
||||
val lhsValue = getValue(value.lhs) ?: return null
|
||||
val rhsValue = getValue(value.rhs) ?: return null
|
||||
|
||||
return calculateValue(lhsValue, rhsValue, value.tag)
|
||||
}
|
||||
|
||||
private fun calculateValue(lhsValue: Any?, rhsValue: Any?, opcode: JCTree.Tag): Any? {
|
||||
if (lhsValue is String && opcode == JCTree.Tag.PLUS) return lhsValue + rhsValue
|
||||
|
||||
if (lhsValue is Boolean && rhsValue is Boolean) {
|
||||
return when (opcode) {
|
||||
JCTree.Tag.AND -> lhsValue && rhsValue
|
||||
JCTree.Tag.OR -> lhsValue || rhsValue
|
||||
JCTree.Tag.EQ -> lhsValue == rhsValue
|
||||
JCTree.Tag.NE -> lhsValue != rhsValue
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
val l = (lhsValue as? Number)?.toInt() ?: return null
|
||||
val r = (rhsValue as? Number)?.toInt() ?: return null
|
||||
return when (opcode) {
|
||||
JCTree.Tag.PLUS -> getExpressionType(l + r)
|
||||
JCTree.Tag.MINUS -> getExpressionType(l - r)
|
||||
JCTree.Tag.MUL -> getExpressionType(l * r)
|
||||
JCTree.Tag.DIV -> getExpressionType(l / r)
|
||||
JCTree.Tag.MOD -> getExpressionType(l % r)
|
||||
JCTree.Tag.SR -> getExpressionType(l shr r)
|
||||
JCTree.Tag.SL -> getExpressionType(l shl r)
|
||||
JCTree.Tag.BITAND -> getExpressionType(l and r)
|
||||
JCTree.Tag.BITOR -> getExpressionType(l or r)
|
||||
JCTree.Tag.BITXOR -> getExpressionType(l xor r)
|
||||
JCTree.Tag.USR -> getExpressionType(l ushr r)
|
||||
JCTree.Tag.EQ -> l == r
|
||||
JCTree.Tag.NE -> l != r
|
||||
JCTree.Tag.LT -> l < r
|
||||
JCTree.Tag.LE -> l <= r
|
||||
JCTree.Tag.GT -> l > r
|
||||
JCTree.Tag.GE -> l >= r
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getExpressionType(expression: Int): Any? {
|
||||
val type = type as? JavaPrimitiveType ?: return null
|
||||
return when (type.type) {
|
||||
PrimitiveType.DOUBLE -> expression.toDouble()
|
||||
PrimitiveType.INT -> expression
|
||||
PrimitiveType.FLOAT -> expression.toFloat()
|
||||
PrimitiveType.LONG -> expression.toLong()
|
||||
PrimitiveType.SHORT -> expression.toShort()
|
||||
PrimitiveType.BYTE -> expression.toByte()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -32,7 +32,7 @@ abstract class TreeBasedMember<out T : JCTree>(
|
||||
) : TreeBasedElement<T>(tree, treePath, javac), JavaMember {
|
||||
|
||||
override val isDeprecatedInJavaDoc: Boolean
|
||||
get() = false
|
||||
get() = javac.isDeprecatedInJavaDoc(treePath)
|
||||
|
||||
override val annotations: Collection<JavaAnnotation> by lazy {
|
||||
tree.annotations().map { TreeBasedAnnotation(it, treePath, javac) }
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ class TreeBasedMethod(
|
||||
get() = tree.parameters.map { TreeBasedValueParameter(it, TreePath(treePath, it), javac) }
|
||||
|
||||
override val returnType: JavaType
|
||||
get() = TreeBasedType.create(tree.returnType, treePath, javac)
|
||||
get() = TreeBasedType.create(tree.returnType, treePath, javac, annotations)
|
||||
|
||||
override val hasAnnotationParameterDefaultValue: Boolean
|
||||
get() = tree.defaultValue != null
|
||||
|
||||
+13
-6
@@ -42,15 +42,22 @@ class TreeBasedTypeParameter(
|
||||
annotations.firstOrNull { it.classId?.asSingleFqName() == fqName }
|
||||
|
||||
override val isDeprecatedInJavaDoc: Boolean
|
||||
get() = false
|
||||
get() = javac.isDeprecatedInJavaDoc(treePath)
|
||||
|
||||
override val upperBounds: Collection<JavaClassifierType>
|
||||
get() = tree.bounds.mapNotNull {
|
||||
when (it) {
|
||||
is JCTree.JCTypeApply -> TreeBasedGenericClassifierType(it, TreePath(treePath, it), javac)
|
||||
is JCTree.JCIdent -> TreeBasedNonGenericClassifierType(it, TreePath(treePath, it), javac)
|
||||
else -> null
|
||||
}
|
||||
TreeBasedType.create(it, TreePath(treePath, it), javac, emptyList()) as? JavaClassifierType
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is TreeBasedTypeParameter) return false
|
||||
return other.name == name && other.upperBounds == upperBounds
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = name.hashCode()
|
||||
upperBounds.forEach { result = 37 * result + it.hashCode() }
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -40,13 +40,13 @@ class TreeBasedValueParameter(
|
||||
annotations.find { it.classId?.asSingleFqName() == fqName }
|
||||
|
||||
override val isDeprecatedInJavaDoc: Boolean
|
||||
get() = false
|
||||
get() = javac.isDeprecatedInJavaDoc(treePath)
|
||||
|
||||
override val name: Name
|
||||
get() = Name.identifier(tree.name.toString())
|
||||
|
||||
override val type: JavaType
|
||||
get() = TreeBasedType.create(tree.getType(), treePath, javac)
|
||||
get() = TreeBasedType.create(tree.getType(), treePath, javac, annotations)
|
||||
|
||||
override val isVararg: Boolean
|
||||
get() = tree.modifiers.flags and Flags.VARARGS != 0L
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.javac.wrappers.trees
|
||||
|
||||
import com.sun.source.tree.Tree
|
||||
import com.sun.source.util.TreePath
|
||||
import com.sun.tools.javac.tree.JCTree
|
||||
import org.jetbrains.kotlin.javac.JavacWrapper
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClassifier
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
class TreePathResolverCache(private val javac: JavacWrapper) {
|
||||
|
||||
private val cache = hashMapOf<Tree, JavaClassifier?>()
|
||||
|
||||
fun resolve(treePath: TreePath): JavaClassifier? = with(treePath) {
|
||||
if (cache.containsKey(leaf)) return cache[leaf]
|
||||
|
||||
return tryToGetClassifier().apply { cache[leaf] = this }
|
||||
}
|
||||
|
||||
private fun TreePath.tryToGetClassifier(): JavaClassifier? {
|
||||
val name = leaf.toString().substringBefore("<").substringAfter("@")
|
||||
val nameParts = name.split(".")
|
||||
|
||||
with(compilationUnit as JCTree.JCCompilationUnit) {
|
||||
tryToResolveInner(name, javac, nameParts)?.let { return it }
|
||||
tryToResolvePackageClass(name, javac, nameParts)?.let { return it }
|
||||
tryToResolveByFqName(name, javac)?.let { return it }
|
||||
tryToResolveSingleTypeImport(name, javac, nameParts)?.let { return it }
|
||||
tryToResolveTypeImportOnDemand(name, javac, nameParts)?.let { return it }
|
||||
tryToResolveInJavaLang(name, javac)?.let { return it }
|
||||
}
|
||||
|
||||
return tryToResolveTypeParameter(javac)
|
||||
}
|
||||
|
||||
private fun TreePath.tryToResolveInner(
|
||||
name: String,
|
||||
javac: JavacWrapper,
|
||||
nameParts: List<String> = emptyList()
|
||||
): JavaClass? = findEnclosingClasses(javac)?.forEach { javaClass ->
|
||||
javaClass.findInner(name, javac, nameParts)?.let { inner -> return inner }
|
||||
}.let { return null }
|
||||
|
||||
private fun TreePath.findEnclosingClasses(javac: JavacWrapper) =
|
||||
filterIsInstance<JCTree.JCClassDecl>()
|
||||
.filter { it.extending != leaf && !it.implementing.contains(leaf) }
|
||||
.reversed()
|
||||
.joinToString(separator = ".", prefix = "${compilationUnit.packageName}.") { it.simpleName }
|
||||
.let { javac.findClass(FqName(it)) }
|
||||
?.let {
|
||||
arrayListOf(it).apply {
|
||||
var enclosingClass = it.outerClass
|
||||
while (enclosingClass != null) {
|
||||
add(enclosingClass)
|
||||
enclosingClass = enclosingClass.outerClass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun JCTree.JCCompilationUnit.tryToResolveSingleTypeImport(
|
||||
name: String,
|
||||
javac: JavacWrapper,
|
||||
nameParts: List<String> = emptyList()
|
||||
): JavaClass? {
|
||||
nameParts.size.takeIf { it > 1 }?.let {
|
||||
imports.filter { it.qualifiedIdentifier.toString().endsWith(".${nameParts.first()}") }
|
||||
.forEach { import ->
|
||||
find(FqName("${import.qualifiedIdentifier}"), javac, nameParts)?.let { javaClass ->
|
||||
return javaClass
|
||||
}
|
||||
}
|
||||
.let { return null }
|
||||
}
|
||||
|
||||
return imports
|
||||
.find { it.qualifiedIdentifier.toString().endsWith(".$name") }
|
||||
?.let { import ->
|
||||
FqName(import.qualifiedIdentifier.toString()).let { fqName ->
|
||||
javac.findClass(fqName) ?: javac.getKotlinClassifier(fqName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun JCTree.JCCompilationUnit.tryToResolvePackageClass(
|
||||
name: String,
|
||||
javac: JavacWrapper,
|
||||
nameParts: List<String> = emptyList()
|
||||
): JavaClass? {
|
||||
return nameParts.size.takeIf { it > 1 }?.let {
|
||||
find(FqName("$packageName.${nameParts.first()}"), javac, nameParts)
|
||||
}
|
||||
?: javac.findClass(FqName("$packageName.$name"))
|
||||
?: javac.getKotlinClassifier(FqName("$packageName.$name"))
|
||||
}
|
||||
|
||||
private fun JCTree.JCCompilationUnit.tryToResolveTypeImportOnDemand(
|
||||
name: String,
|
||||
javac: JavacWrapper,
|
||||
nameParts: List<String> = emptyList()
|
||||
): JavaClass? {
|
||||
with(imports.filter { it.qualifiedIdentifier.toString().endsWith("*") }) {
|
||||
nameParts.size.takeIf { it > 1 }
|
||||
?.let {
|
||||
forEach { pack ->
|
||||
find(FqName("${pack.qualifiedIdentifier.toString().substringBefore("*")}${nameParts.first()}"),
|
||||
javac,
|
||||
nameParts)?.let { return it }
|
||||
}.let { return null }
|
||||
}
|
||||
|
||||
this.forEach {
|
||||
val fqName = "${it.qualifiedIdentifier.toString().substringBefore("*")}$name".let(::FqName)
|
||||
(javac.findClass(fqName) ?: javac.getKotlinClassifier(fqName))?.let { return it }
|
||||
}.let { return null }
|
||||
}
|
||||
}
|
||||
|
||||
private fun TreePath.tryToResolveTypeParameter(javac: JavacWrapper) =
|
||||
flatMap {
|
||||
when (it) {
|
||||
is JCTree.JCClassDecl -> it.typarams
|
||||
is JCTree.JCMethodDecl -> it.typarams
|
||||
else -> emptyList<JCTree.JCTypeParameter>()
|
||||
}
|
||||
}
|
||||
.find { it.toString().substringBefore(" ") == leaf.toString() }
|
||||
?.let {
|
||||
TreeBasedTypeParameter(it,
|
||||
javac.getTreePath(it, compilationUnit),
|
||||
javac)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun JavaClass.findInner(name: String,
|
||||
javac: JavacWrapper,
|
||||
nameParts: List<String> = emptyList()) : JavaClass? {
|
||||
nameParts.size.takeIf { it > 1 }?.let {
|
||||
return find(FqName("${fqName!!.asString()}.${nameParts[0]}"), javac, nameParts)
|
||||
}
|
||||
|
||||
if (name == this.fqName?.shortName()?.asString()) return this
|
||||
|
||||
with(FqName("${fqName!!.asString()}.$name")) {
|
||||
javac.findClass(this)?.let { return it }
|
||||
javac.getKotlinClassifier(this)?.let { return it }
|
||||
}
|
||||
|
||||
supertypes.mapNotNull { it.classifier as? JavaClass }
|
||||
.forEach { javaClass ->
|
||||
javaClass.findInner(name, javac)?.let { inner -> return inner }
|
||||
}.let { return null }
|
||||
}
|
||||
|
||||
fun tryToResolveByFqName(name: String,
|
||||
javac: JavacWrapper) = with(FqName(name)) {
|
||||
javac.findClass(this) ?: javac.getKotlinClassifier(this)
|
||||
}
|
||||
|
||||
fun tryToResolveInJavaLang(name: String,
|
||||
javac: JavacWrapper) = javac.findClass(FqName("java.lang.$name"))
|
||||
|
||||
|
||||
fun find(fqName: FqName,
|
||||
javac: JavacWrapper,
|
||||
nameParts: List<String>): JavaClass? {
|
||||
val initial = with(fqName) {
|
||||
javac.findClass(this)
|
||||
?: javac.getKotlinClassifier(this)
|
||||
?: return null
|
||||
}
|
||||
|
||||
nameParts.drop(1).fold(initial) {
|
||||
javaClass, namePart -> javaClass.findInner(namePart, javac) ?: return null
|
||||
}.let { return it }
|
||||
}
|
||||
+118
-31
@@ -30,23 +30,31 @@ import javax.lang.model.type.TypeKind
|
||||
abstract class TreeBasedType<out T : JCTree>(
|
||||
val tree: T,
|
||||
val treePath: TreePath,
|
||||
val javac: JavacWrapper
|
||||
val javac: JavacWrapper,
|
||||
override val annotations: Collection<JavaAnnotation>
|
||||
) : JavaType, JavaAnnotationOwner {
|
||||
|
||||
companion object {
|
||||
fun <Type : JCTree> create(tree: Type, treePath: TreePath, javac: JavacWrapper) = when (tree) {
|
||||
is JCTree.JCPrimitiveTypeTree -> TreeBasedPrimitiveType(tree, TreePath(treePath, tree), javac)
|
||||
is JCTree.JCArrayTypeTree -> TreeBasedArrayType(tree, TreePath(treePath, tree), javac)
|
||||
is JCTree.JCWildcard -> TreeBasedWildcardType(tree, TreePath(treePath, tree), javac)
|
||||
is JCTree.JCTypeApply -> TreeBasedGenericClassifierType(tree, TreePath(treePath, tree), javac)
|
||||
is JCTree.JCExpression -> TreeBasedNonGenericClassifierType(tree, TreePath(treePath, tree), javac)
|
||||
else -> throw UnsupportedOperationException("Unsupported type: $tree")
|
||||
fun create(tree: JCTree, treePath: TreePath,
|
||||
javac: JavacWrapper, annotations: Collection<JavaAnnotation>): JavaType {
|
||||
val applicableAnnotations = annotations.filterTypeAnnotations()
|
||||
return when (tree) {
|
||||
is JCTree.JCPrimitiveTypeTree -> TreeBasedPrimitiveType(tree, javac.getTreePath(tree, treePath.compilationUnit), javac, applicableAnnotations)
|
||||
is JCTree.JCArrayTypeTree -> TreeBasedArrayType(tree, javac.getTreePath(tree, treePath.compilationUnit), javac, applicableAnnotations)
|
||||
is JCTree.JCWildcard -> TreeBasedWildcardType(tree, javac.getTreePath(tree, treePath.compilationUnit), javac, applicableAnnotations)
|
||||
is JCTree.JCTypeApply -> TreeBasedGenericClassifierType(tree, javac.getTreePath(tree, treePath.compilationUnit), javac, applicableAnnotations)
|
||||
is JCTree.JCAnnotatedType -> {
|
||||
val underlyingType = tree.underlyingType
|
||||
val newAnnotations = tree.annotations
|
||||
.map { TreeBasedAnnotation(it, javac.getTreePath(it, treePath.compilationUnit), javac) }
|
||||
create(underlyingType, javac.getTreePath(underlyingType, treePath.compilationUnit), javac, newAnnotations)
|
||||
}
|
||||
is JCTree.JCExpression -> TreeBasedNonGenericClassifierType(tree, javac.getTreePath(tree, treePath.compilationUnit), javac, applicableAnnotations)
|
||||
else -> throw UnsupportedOperationException("Unsupported type: $tree")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val annotations: Collection<JavaAnnotation>
|
||||
get() = emptyList()
|
||||
|
||||
override val isDeprecatedInJavaDoc: Boolean
|
||||
get() = false
|
||||
|
||||
@@ -63,8 +71,9 @@ abstract class TreeBasedType<out T : JCTree>(
|
||||
class TreeBasedPrimitiveType(
|
||||
tree: JCTree.JCPrimitiveTypeTree,
|
||||
treePath: TreePath,
|
||||
javac: JavacWrapper
|
||||
) : TreeBasedType<JCTree.JCPrimitiveTypeTree>(tree, treePath, javac), JavaPrimitiveType {
|
||||
javac: JavacWrapper,
|
||||
annotations: Collection<JavaAnnotation>
|
||||
) : TreeBasedType<JCTree.JCPrimitiveTypeTree>(tree, treePath, javac, annotations), JavaPrimitiveType {
|
||||
|
||||
override val type: PrimitiveType?
|
||||
get() = if (tree.primitiveTypeKind == TypeKind.VOID) {
|
||||
@@ -79,22 +88,24 @@ class TreeBasedPrimitiveType(
|
||||
class TreeBasedArrayType(
|
||||
tree: JCTree.JCArrayTypeTree,
|
||||
treePath: TreePath,
|
||||
javac: JavacWrapper
|
||||
) : TreeBasedType<JCTree.JCArrayTypeTree>(tree, treePath, javac), JavaArrayType {
|
||||
javac: JavacWrapper,
|
||||
annotations: Collection<JavaAnnotation>
|
||||
) : TreeBasedType<JCTree.JCArrayTypeTree>(tree, treePath, javac, annotations), JavaArrayType {
|
||||
|
||||
override val componentType: JavaType
|
||||
get() = create(tree.elemtype, treePath, javac)
|
||||
get() = create(tree.elemtype, treePath, javac, annotations)
|
||||
|
||||
}
|
||||
|
||||
class TreeBasedWildcardType(
|
||||
tree: JCTree.JCWildcard,
|
||||
treePath: TreePath,
|
||||
javac: JavacWrapper
|
||||
) : TreeBasedType<JCTree.JCWildcard>(tree, treePath, javac), JavaWildcardType {
|
||||
javac: JavacWrapper,
|
||||
annotations: Collection<JavaAnnotation>
|
||||
) : TreeBasedType<JCTree.JCWildcard>(tree, treePath, javac, annotations), JavaWildcardType {
|
||||
|
||||
override val bound: JavaType?
|
||||
get() = tree.bound?.let { create(it, treePath, javac) }
|
||||
get() = tree.bound?.let { create(it, treePath, javac, annotations) }
|
||||
|
||||
override val isExtends: Boolean
|
||||
get() = tree.kind.kind == BoundKind.EXTENDS
|
||||
@@ -104,18 +115,47 @@ class TreeBasedWildcardType(
|
||||
sealed class TreeBasedClassifierType<out T : JCTree>(
|
||||
tree: T,
|
||||
treePath: TreePath,
|
||||
javac: JavacWrapper
|
||||
) : TreeBasedType<T>(tree, treePath, javac), JavaClassifierType {
|
||||
javac: JavacWrapper,
|
||||
annotations: Collection<JavaAnnotation>
|
||||
) : TreeBasedType<T>(tree, treePath, javac, annotations), JavaClassifierType {
|
||||
|
||||
override val classifier: JavaClassifier?
|
||||
get() = javac.resolve(treePath)
|
||||
|
||||
override val classifierQualifiedName: String
|
||||
get() = (classifier as? JavaClass)?.fqName?.asString() ?: treePath.leaf.toString()
|
||||
get() = (classifier as? JavaClass)?.fqName?.asString() ?: treePath.leaf.toString().substringBefore("<")
|
||||
|
||||
override val presentableText: String
|
||||
get() = classifierQualifiedName
|
||||
|
||||
override val typeArguments: List<JavaType>
|
||||
get() {
|
||||
var tree: JCTree = tree
|
||||
if (tree is JCTree.JCTypeApply) {
|
||||
tree = tree.clazz
|
||||
}
|
||||
if (tree is JCTree.JCFieldAccess) {
|
||||
val enclosingType = TreeBasedType.create(tree.selected, treePath, javac, annotations)
|
||||
return (enclosingType as? JavaClassifierType)?.typeArguments ?: emptyList()
|
||||
}
|
||||
else {
|
||||
val classifier = classifier as? JavaClass ?: return emptyList()
|
||||
if (classifier is MockKotlinClassifier || classifier.isStatic) return emptyList()
|
||||
|
||||
return arrayListOf<JavaClass>().apply {
|
||||
var outer = classifier.outerClass
|
||||
var staticType = false
|
||||
while (outer != null && !staticType) {
|
||||
if (outer.isStatic) {
|
||||
staticType = true
|
||||
}
|
||||
add(outer)
|
||||
outer = outer.outerClass
|
||||
}
|
||||
}.flatMap { it.typeParameters.map(::TreeBasedTypeParameterType) }
|
||||
}
|
||||
}
|
||||
|
||||
private val typeParameter: JCTree.JCTypeParameter?
|
||||
get() = treePath.flatMap {
|
||||
when (it) {
|
||||
@@ -128,15 +168,36 @@ sealed class TreeBasedClassifierType<out T : JCTree>(
|
||||
|
||||
}
|
||||
|
||||
class TreeBasedNonGenericClassifierType(
|
||||
tree: JCTree.JCExpression,
|
||||
treePath: TreePath,
|
||||
javac: JavacWrapper
|
||||
) : TreeBasedClassifierType<JCTree.JCExpression>(tree, treePath, javac) {
|
||||
class TreeBasedTypeParameterType(override val classifier: JavaTypeParameter) : JavaClassifierType {
|
||||
|
||||
override val typeArguments: List<JavaType>
|
||||
get() = emptyList()
|
||||
|
||||
override val isRaw: Boolean
|
||||
get() = false
|
||||
|
||||
override val annotations: Collection<JavaAnnotation>
|
||||
get() = classifier.annotations.filterTypeAnnotations()
|
||||
|
||||
override val classifierQualifiedName: String
|
||||
get() = classifier.name.asString()
|
||||
|
||||
override val presentableText: String
|
||||
get() = classifierQualifiedName
|
||||
|
||||
override fun findAnnotation(fqName: FqName) = annotations.find { it.classId?.asSingleFqName() == fqName }
|
||||
|
||||
override val isDeprecatedInJavaDoc: Boolean
|
||||
get() = false
|
||||
}
|
||||
|
||||
class TreeBasedNonGenericClassifierType(
|
||||
tree: JCTree.JCExpression,
|
||||
treePath: TreePath,
|
||||
javac: JavacWrapper,
|
||||
annotations: Collection<JavaAnnotation>
|
||||
) : TreeBasedClassifierType<JCTree.JCExpression>(tree, treePath, javac, annotations) {
|
||||
|
||||
override val isRaw: Boolean
|
||||
get() = (classifier as? MockKotlinClassifier)?.hasTypeParameters
|
||||
?: (classifier as? JavaClass)?.typeParameters?.isNotEmpty()
|
||||
@@ -147,13 +208,39 @@ class TreeBasedNonGenericClassifierType(
|
||||
class TreeBasedGenericClassifierType(
|
||||
tree: JCTree.JCTypeApply,
|
||||
treePath: TreePath,
|
||||
javac: JavacWrapper
|
||||
) : TreeBasedClassifierType<JCTree.JCTypeApply>(tree, treePath, javac) {
|
||||
javac: JavacWrapper,
|
||||
annotations: Collection<JavaAnnotation>
|
||||
) : TreeBasedClassifierType<JCTree.JCTypeApply>(tree, treePath, javac, annotations) {
|
||||
|
||||
override val classifier: JavaClassifier?
|
||||
get() {
|
||||
val newTree = tree.clazz
|
||||
return if (newTree is JCTree.JCAnnotatedType) {
|
||||
javac.resolve(javac.getTreePath(newTree.underlyingType, treePath.compilationUnit))
|
||||
}
|
||||
else super.classifier
|
||||
}
|
||||
|
||||
override val annotations: Collection<JavaAnnotation>
|
||||
get() {
|
||||
val newTree = tree.clazz
|
||||
return (newTree as? JCTree.JCAnnotatedType)?.annotations?.map { TreeBasedAnnotation(it, javac.getTreePath(it, treePath.compilationUnit), javac) }
|
||||
?.toMutableList<JavaAnnotation>()
|
||||
?.apply { addAll(super.annotations) }
|
||||
?: super.annotations
|
||||
}
|
||||
|
||||
override val typeArguments: List<JavaType>
|
||||
get() = tree.arguments.map { create(it, treePath, javac) }
|
||||
get() = tree.arguments.map { create(it, treePath, javac, emptyList()) }
|
||||
.toMutableList()
|
||||
.apply { addAll(super.typeArguments) }
|
||||
|
||||
override val isRaw: Boolean
|
||||
get() = false
|
||||
get() = classifier.let {
|
||||
when (it) {
|
||||
is MockKotlinClassifier -> tree.arguments.size != it.typeParametersNumber
|
||||
else -> tree.arguments.size != (classifier as? JavaClass)?.typeParameters?.size
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,9 +19,15 @@ package org.jetbrains.kotlin.javac.wrappers.trees
|
||||
import com.sun.tools.javac.tree.JCTree
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.javac.wrappers.symbols.SymbolBasedArrayAnnotationArgument
|
||||
import org.jetbrains.kotlin.javac.wrappers.symbols.SymbolBasedField
|
||||
import org.jetbrains.kotlin.javac.wrappers.symbols.SymbolBasedReferenceAnnotationArgument
|
||||
import org.jetbrains.kotlin.load.java.JavaVisibilities
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaAnnotation
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import javax.lang.model.element.Modifier
|
||||
|
||||
internal val JCTree.JCModifiers.isAbstract: Boolean
|
||||
@@ -58,4 +64,38 @@ internal fun JCTree.annotations(): Collection<JCTree.JCAnnotation> = when (this)
|
||||
} ?: emptyList<JCTree.JCAnnotation>()
|
||||
|
||||
fun JavaClass.computeClassId(): ClassId? =
|
||||
outerClass?.computeClassId()?.createNestedClassId(name) ?: fqName?.let { ClassId.topLevel(it) }
|
||||
outerClass?.computeClassId()?.createNestedClassId(name) ?: fqName?.let { ClassId.topLevel(it) }
|
||||
|
||||
fun Collection<JavaAnnotation>.filterTypeAnnotations(): Collection<JavaAnnotation> {
|
||||
val filteredAnnotations = arrayListOf<JavaAnnotation>()
|
||||
for (annotation in this) {
|
||||
val annotationClass = annotation.resolve()
|
||||
val targetAnnotation = annotationClass?.annotations?.find { it.classId == ClassId(FqName("java.lang.annotation"), Name.identifier("Target")) } ?: continue
|
||||
val elementTypeArg = targetAnnotation.arguments.firstOrNull() ?: continue
|
||||
|
||||
when (elementTypeArg) {
|
||||
is SymbolBasedArrayAnnotationArgument -> {
|
||||
elementTypeArg.args.find { (it as? SymbolBasedReferenceAnnotationArgument)?.element?.simpleName?.contentEquals("TYPE_USE") ?: false }
|
||||
?.let { filteredAnnotations.add(annotation) }
|
||||
}
|
||||
is SymbolBasedReferenceAnnotationArgument -> {
|
||||
elementTypeArg.element.simpleName.takeIf { it.contentEquals("TYPE_USE") }
|
||||
?.let { filteredAnnotations.add(annotation) }
|
||||
}
|
||||
is TreeBasedArrayAnnotationArgument -> {
|
||||
elementTypeArg.args.find {
|
||||
val field = (it as? TreeBasedReferenceAnnotationArgument)?.resolve() as? SymbolBasedField
|
||||
field?.element?.simpleName?.contentEquals("TYPE_USE") ?: false
|
||||
}?.let { filteredAnnotations.add(annotation) }
|
||||
}
|
||||
is TreeBasedReferenceAnnotationArgument -> {
|
||||
(elementTypeArg.resolve() as? SymbolBasedField)?.let { field ->
|
||||
field.element.simpleName.takeIf { it.contentEquals("TYPE_USE") }
|
||||
?.let { filteredAnnotations.add(annotation) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filteredAnnotations
|
||||
}
|
||||
Reference in New Issue
Block a user