javac-wrapper: identifier resolver

This commit is contained in:
baratynskiy
2017-08-03 11:22:32 +03:00
committed by Alexander Baratynskiy
parent 01883a41cb
commit 4f180e1292
30 changed files with 1256 additions and 192 deletions
@@ -41,14 +41,15 @@ import com.sun.tools.javac.util.Context
import com.sun.tools.javac.util.Log
import com.sun.tools.javac.util.Names
import com.sun.tools.javac.util.Options
import org.jetbrains.kotlin.javac.resolve.ClassifierResolver
import org.jetbrains.kotlin.javac.resolve.IdentifierResolver
import org.jetbrains.kotlin.javac.resolve.KotlinClassifiersCache
import org.jetbrains.kotlin.javac.resolve.classId
import org.jetbrains.kotlin.javac.wrappers.symbols.SymbolBasedClass
import org.jetbrains.kotlin.javac.wrappers.symbols.SymbolBasedClassifierType
import org.jetbrains.kotlin.javac.wrappers.symbols.SymbolBasedPackage
import org.jetbrains.kotlin.javac.wrappers.trees.*
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.load.java.structure.*
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.psi.KtFile
import java.io.Closeable
@@ -67,7 +68,7 @@ class JavacWrapper(
jvmClasspathRoots: List<File>,
bootClasspath: List<File>?,
sourcePath: List<File>?,
val kotlinSupertypeResolver: KotlinSupertypeResolver,
val kotlinResolver: JavacWrapperKotlinResolver,
private val compileJava: Boolean,
private val outputDirectory: File?,
private val context: Context
@@ -174,6 +175,7 @@ class JavacWrapper(
}
val classifierResolver = ClassifierResolver(this)
private val identifierResolver = IdentifierResolver(this)
private val kotlinClassifiersCache = KotlinClassifiersCache(if (javaFiles.isNotEmpty()) kotlinFiles else emptyList(), this)
private val symbolBasedPackagesCache = hashMapOf<String, SymbolBasedPackage?>()
@@ -281,6 +283,9 @@ class JavacWrapper(
fun resolve(treePath: TreePath): JavaClassifier? =
classifierResolver.resolve(treePath)
fun resolveField(treePath: TreePath, containingClass: JavaClass): JavaField? =
identifierResolver.resolve(treePath, containingClass)
fun toVirtualFile(javaFileObject: JavaFileObject): VirtualFile? =
javaFileObject.toUri().let { uri ->
if (uri.scheme == "jar") {
@@ -16,9 +16,12 @@
package org.jetbrains.kotlin.javac
import org.jetbrains.kotlin.load.java.structure.JavaField
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtClassOrObject
interface KotlinSupertypeResolver {
interface JavacWrapperKotlinResolver {
fun resolveSupertypes(classOrObject: KtClassOrObject): List<ClassId>
fun findField(classOrObject: KtClassOrObject, name: String): JavaField?
}
@@ -14,15 +14,14 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.javac.wrappers.trees
package org.jetbrains.kotlin.javac.resolve
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.javac.wrappers.trees.TreeBasedClass
import org.jetbrains.kotlin.javac.wrappers.trees.TreeBasedTypeParameter
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.JavaClassifier
import org.jetbrains.kotlin.name.ClassId
@@ -111,80 +110,14 @@ class ClassifierResolver(private val javac: JavacWrapper) {
}
private abstract class Scope(protected val javac: JavacWrapper,
protected val treePath: TreePath) {
protected val treePath: TreePath) {
protected val helper = ResolveHelper(javac, 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) {
@@ -195,8 +128,8 @@ private class GlobalScope(javac: JavacWrapper, treePath: TreePath) : Scope(javac
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)
return helper.findJavaOrKotlinClass(classId("java.lang", name))?.let { javaClass ->
helper.getJavaClassFromPathSegments(javaClass, pathSegments)
}
}
@@ -204,18 +137,18 @@ private class GlobalScope(javac: JavacWrapper, treePath: TreePath) : Scope(javac
pathSegments.forEachIndexed { index, _ ->
if (index != 0) {
val packageFqName = pathSegments.take(index).joinToString(separator = ".")
findPackage(packageFqName)?.let { pack ->
helper.findPackage(packageFqName)?.let { pack ->
val className = pathSegments.drop(index)
findJavaOrKotlinClass(ClassId(pack, Name.identifier(className.first())))?.let { javaClass ->
return getJavaClassFromPathSegments(javaClass, className)
helper.findJavaOrKotlinClass(ClassId(pack, Name.identifier(className.first())))?.let { javaClass ->
return helper.getJavaClassFromPathSegments(javaClass, className)
}
}
}
}
// try to find in <root>
return findJavaOrKotlinClass(classId("", pathSegments.first()))?.let { javaClass ->
getJavaClassFromPathSegments(javaClass, pathSegments)
return helper.findJavaOrKotlinClass(classId("", pathSegments.first()))?.let { javaClass ->
helper.getJavaClassFromPathSegments(javaClass, pathSegments)
}
}
@@ -229,11 +162,11 @@ private class ImportOnDemandScope(javac: JavacWrapper,
override fun findClass(name: String, pathSegments: List<String>): JavaClassifier? {
asteriskImports()
.mapNotNullTo(hashSetOf()) { findImport("$it$name".split(".")) }
.mapNotNullTo(hashSetOf()) { helper.findImport("$it$name".split(".")) }
.takeIf { it.isNotEmpty() }
?.let {
return it.singleOrNull()?.let { javaClass ->
getJavaClassFromPathSegments(javaClass, pathSegments)
helper.getJavaClassFromPathSegments(javaClass, pathSegments)
}
}
@@ -259,9 +192,9 @@ private class PackageScope(javac: JavacWrapper,
get() = ImportOnDemandScope(javac, treePath)
override fun findClass(name: String, pathSegments: List<String>): JavaClassifier? {
findJavaOrKotlinClass(classId(treePath.compilationUnit.packageName?.toString() ?: "", name))
helper.findJavaOrKotlinClass(classId(treePath.compilationUnit.packageName?.toString() ?: "", name))
?.let { javaClass ->
return getJavaClassFromPathSegments(javaClass, pathSegments)
return helper.getJavaClassFromPathSegments(javaClass, pathSegments)
}
return parent.findClass(name, pathSegments)
@@ -281,8 +214,8 @@ private class SingleTypeImportScope(javac: JavacWrapper,
imports.singleOrNull() ?: return null
return findImport(imports.first().split("."))
?.let { javaClass -> getJavaClassFromPathSegments(javaClass, pathSegments) }
return helper.findImport(imports.first().split("."))
?.let { javaClass -> helper.getJavaClassFromPathSegments(javaClass, pathSegments) }
}
private fun imports(firstSegment: String) =
@@ -310,7 +243,7 @@ private class CurrentClassAndInnerScope(javac: JavacWrapper,
?.find { typeParameter -> typeParameter.name == identifier }
?.let { typeParameter -> return typeParameter }
it.findInnerOrNested(identifier)?.let { javaClass -> return getJavaClassFromPathSegments(javaClass, pathSegments) }
helper.findInnerOrNested(it, identifier)?.let { javaClass -> return helper.getJavaClassFromPathSegments(javaClass, pathSegments) }
if (it.name == identifier && pathSegments.size == 1) return it
}
@@ -0,0 +1,162 @@
/*
* 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.resolve
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.JavaField
import org.jetbrains.kotlin.name.Name
class IdentifierResolver(private val javac: JavacWrapper) {
fun resolve(treePath: TreePath, containingClass: JavaClass): JavaField? {
val leaf = treePath.leaf
if (leaf is JCTree.JCIdent) {
val fieldName = Name.identifier(leaf.name.toString())
return CurrentClassAndInnerFieldScope(javac, treePath).findField(containingClass, fieldName)
}
else if (leaf is JCTree.JCFieldAccess) {
val javaClassTreePath = javac.getTreePath(leaf.selected, treePath.compilationUnit)
val javaClass = javac.resolve(javaClassTreePath) as? JavaClass ?: return null
if (javaClass is MockKotlinClassifier) {
return javaClass.findField(leaf.name.toString())
}
val fieldName = Name.identifier(leaf.name.toString())
return CurrentClassAndInnerFieldScope(javac, treePath, null).findField(javaClass, fieldName)
}
return null
}
}
private abstract class FieldScope(protected val javac: JavacWrapper,
protected val treePath: TreePath) {
protected val helper = ResolveHelper(javac, treePath)
abstract val parent: FieldScope?
abstract fun findField(javaClass: JavaClass, name: Name): JavaField?
protected fun JavaClass.findFieldIncludingSupertypes(name: Name, checkedSupertypes: HashSet<JavaClass> = hashSetOf()): JavaField? {
fields.find { it.name == name }?.let {
checkedSupertypes.addAll(collectAllSupertypes())
return it
}
return supertypes
.mapNotNull {
val classifier = it.classifier as? JavaClass
if (classifier !in checkedSupertypes) {
classifier?.findFieldIncludingSupertypes(name, checkedSupertypes)
}
else null
}.singleOrNull()
}
}
private class StaticImportOnDemandFieldScope(javac: JavacWrapper,
treePath: TreePath) : FieldScope(javac, treePath) {
override val parent: FieldScope?
get() = null
override fun findField(javaClass: JavaClass, name: Name): JavaField? {
val foundFields = hashSetOf<JavaField>()
staticAsteriskImports().forEach { import ->
val pathSegments = import.split(".")
val importedClass = helper.findImport(pathSegments)
if (importedClass is MockKotlinClassifier) {
return importedClass.findField(name.asString())
}
importedClass?.findFieldIncludingSupertypes(name)?.let { foundFields.add(it) }
}
return foundFields.singleOrNull()
}
private fun staticAsteriskImports() =
(treePath.compilationUnit as JCTree.JCCompilationUnit).imports
.filter { it.staticImport }
.mapNotNull {
val fqName = it.qualifiedIdentifier.toString()
if (fqName.endsWith("*")) {
fqName.dropLast(2)
}
else null
}
}
private class StaticImportFieldScope(javac: JavacWrapper,
treePath: TreePath) : FieldScope(javac, treePath) {
override val parent: FieldScope
get() = StaticImportOnDemandFieldScope(javac, treePath)
override fun findField(javaClass: JavaClass, name: Name): JavaField? {
val staticImports = staticImports(name.asString()).toSet().takeIf { it.isNotEmpty() }
?: return parent.findField(javaClass, name)
val import = staticImports.singleOrNull() ?: return null
val pathSegments = import.split(".").dropLast(1)
val importedClass = helper.findImport(pathSegments)
if (importedClass is MockKotlinClassifier) {
return importedClass.findField(name.asString())
}
return importedClass?.findFieldIncludingSupertypes(name)
}
private fun staticImports(fieldName: String) =
(treePath.compilationUnit as JCTree.JCCompilationUnit).imports
.filter { it.staticImport }
.mapNotNull {
val import = it.qualifiedIdentifier as? JCTree.JCFieldAccess
val importedField = import?.name?.toString()
if (importedField == fieldName) {
import.toString()
}
else null
}
}
private class CurrentClassAndInnerFieldScope(javac: JavacWrapper,
treePath: TreePath,
override val parent: FieldScope? = StaticImportFieldScope(javac, treePath)) : FieldScope(javac, treePath) {
override fun findField(javaClass: JavaClass, name: Name): JavaField? {
javaClass.enclosingClasses().forEach {
it.findFieldIncludingSupertypes(name)?.let { return it }
}
return parent?.findField(javaClass, name)
}
private fun JavaClass.enclosingClasses(): List<JavaClass> = arrayListOf<JavaClass>().also { classes ->
classes.add(this)
outerClass?.let { classes.addAll(it.enclosingClasses()) }
}
}
@@ -14,12 +14,15 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.javac
package org.jetbrains.kotlin.javac.resolve
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiField
import com.intellij.psi.PsiLiteralExpression
import com.intellij.psi.search.SearchScope
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.javac.JavacWrapper
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.JavaVisibilities
import org.jetbrains.kotlin.load.java.structure.*
@@ -85,15 +88,6 @@ class MockKotlinClassifier(val classId: ClassId,
override val fqName: FqName
get() = classId.asSingleFqName()
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() = when (classOrObject.visibilityModifierType()) {
null, KtTokens.PUBLIC_KEYWORD -> Visibilities.PUBLIC
@@ -102,11 +96,8 @@ class MockKotlinClassifier(val classId: ClassId,
else -> JavaVisibilities.PACKAGE_VISIBILITY
}
override val typeParameters: List<JavaTypeParameter>
get() = throw UnsupportedOperationException("Should not be called")
override val supertypes: Collection<JavaClassifierType>
get() = javac.kotlinSupertypeResolver.resolveSupertypes(classOrObject)
get() = javac.kotlinResolver.resolveSupertypes(classOrObject)
.mapNotNull { javac.getKotlinClassifier(it) ?: javac.findClass(it) }
.map { MockKotlinClassifierType(it) }
@@ -118,53 +109,17 @@ class MockKotlinClassifier(val classId: ClassId,
}
}
override val outerClass: JavaClass?
get() = throw UnsupportedOperationException("Should not be called")
override val isInterface: Boolean
get() = throw UnsupportedOperationException("Should not be called")
override val isAnnotationType: Boolean
get() = throw UnsupportedOperationException("Should not be called")
override val isEnum: Boolean
get() = throw UnsupportedOperationException("Should not be called")
override val lightClassOriginKind
get() = LightClassOriginKind.SOURCE
override val virtualFile: VirtualFile?
get() = null
override val methods: Collection<JavaMethod>
get() = throw UnsupportedOperationException("Should not be called")
override val fields: Collection<JavaField>
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")
override val name
get() = fqName.shortNameOrSpecial()
override val annotations
get() = throw UnsupportedOperationException("Should not be called")
override val isDeprecatedInJavaDoc: Boolean
get() = throw UnsupportedOperationException("Should not be called")
override fun isFromSourceCodeInScope(scope: SearchScope) = true
override fun findAnnotation(fqName: FqName) =
throw UnsupportedOperationException("Should not be called")
override val innerClassNames
get() = innerClasses.map(JavaClass::name)
@@ -177,36 +132,62 @@ class MockKotlinClassifier(val classId: ClassId,
val hasTypeParameters: Boolean
get() = typeParametersNumber > 0
fun findField(name: String) = javac.kotlinResolver.findField(classOrObject, name)
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 typeParameters: List<JavaTypeParameter>
get() = throw UnsupportedOperationException("Should not be called")
override val outerClass: JavaClass?
get() = throw UnsupportedOperationException("Should not be called")
override val isInterface: Boolean
get() = throw UnsupportedOperationException("Should not be called")
override val isAnnotationType: Boolean
get() = throw UnsupportedOperationException("Should not be called")
override val isEnum: Boolean
get() = throw UnsupportedOperationException("Should not be called")
override val methods: Collection<JavaMethod>
get() = throw UnsupportedOperationException("Should not be called")
override val fields: Collection<JavaField>
get() = throw UnsupportedOperationException("Should not be called")
override val constructors: Collection<JavaConstructor>
get() = throw UnsupportedOperationException("Should not be called")
override val annotations
get() = throw UnsupportedOperationException("Should not be called")
override val isDeprecatedInJavaDoc: Boolean
get() = throw UnsupportedOperationException("Should not be called")
override fun findAnnotation(fqName: FqName) =
throw UnsupportedOperationException("Should not be called")
}
class MockKotlinClassifierType(override val classifier: JavaClassifier) : JavaClassifierType {
override val typeArguments: List<JavaType>
get() = throw UnsupportedOperationException("Should not be called")
override val isRaw: Boolean
get() = throw UnsupportedOperationException("Should not be called")
override val annotations: Collection<JavaAnnotation>
get() = throw UnsupportedOperationException("Should not be called")
override val classifierQualifiedName: String
get() = throw UnsupportedOperationException("Should not be called")
override val presentableText: String
get() = throw UnsupportedOperationException("Should not be called")
override fun findAnnotation(fqName: FqName) =
throw UnsupportedOperationException("Should not be called")
override val isDeprecatedInJavaDoc: Boolean
get() = throw UnsupportedOperationException("Should not be called")
}
class MockKotlinField(private val property: KtProperty) : JavaField {
class MockKotlinField(private val psiField: PsiField) : JavaField {
override val initializerValue: Any?
get() = (psiField.initializer as? PsiLiteralExpression)?.value
override val name: Name
get() = property.nameAsSafeName
get() = throw UnsupportedOperationException("Should not be called")
override val annotations: Collection<JavaAnnotation>
get() = throw UnsupportedOperationException("Should not be called")
override val isDeprecatedInJavaDoc: Boolean
@@ -225,16 +206,8 @@ class MockKotlinField(private val property: KtProperty) : JavaField {
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
return initializer.text.toIntOrNull() ?: initializer.text
}
override val hasConstantNotNullInitializer: Boolean
get() = throw UnsupportedOperationException("Should not be called")
override fun findAnnotation(fqName: FqName) = throw UnsupportedOperationException("Should not be called")
}
@@ -0,0 +1,101 @@
/*
* 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.resolve
import com.sun.source.util.TreePath
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.javac.JavacWrapper
import org.jetbrains.kotlin.javac.wrappers.trees.computeClassId
import org.jetbrains.kotlin.load.java.JavaVisibilities
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
internal class ResolveHelper(private val javac: JavacWrapper,
private val treePath: TreePath) {
fun getJavaClassFromPathSegments(javaClass: JavaClass,
pathSegments: List<String>) =
if (pathSegments.size == 1) {
javaClass
}
else {
javaClass.findInnerOrNested(pathSegments.drop(1))
}
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
}
fun findJavaOrKotlinClass(classId: ClassId) = javac.findClass(classId) ?: javac.getKotlinClassifier(classId)
fun findInnerOrNested(javaClass: JavaClass, name: Name, checkedSupertypes: HashSet<JavaClass> = hashSetOf()): JavaClass? {
javaClass.findVisibleInnerOrNestedClass(name)?.let {
checkedSupertypes.addAll(javaClass.collectAllSupertypes())
return it
}
return javaClass.supertypes
.mapNotNull {
(it.classifier as? JavaClass)?.let { supertype ->
if (supertype !in checkedSupertypes) {
findInnerOrNested(supertype, name, checkedSupertypes)
} else null
}
}.singleOrNull()
}
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.findInnerOrNested(pathSegments: List<String>): JavaClass? =
pathSegments.fold(this) { javaClass, it -> findInnerOrNested(javaClass, Name.identifier(it)) ?: return null }
}
fun JavaClass.collectAllSupertypes(): Set<JavaClass> =
hashSetOf(this).apply {
supertypes.mapNotNull { it.classifier as? JavaClass }.forEach { addAll(it.collectAllSupertypes()) }
}
@@ -87,10 +87,8 @@ class ValueCalculator(private val containingClass: JavaClass,
}
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.JCIdent,
is JCTree.JCFieldAccess -> javac.resolveField(javac.getTreePath(expr, treePath.compilationUnit), containingClass)?.initializerValue
is JCTree.JCBinary -> binaryInitializerValue(expr)
is JCTree.JCParens -> getValue(expr.expr)
is JCTree.JCUnary -> unaryInitializerValue(expr)
@@ -98,16 +96,6 @@ class ValueCalculator(private val containingClass: JavaClass,
}
}
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) {
@@ -21,7 +21,7 @@ import com.sun.tools.javac.code.BoundKind
import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.javac.JavacWrapper
import org.jetbrains.kotlin.javac.MockKotlinClassifier
import org.jetbrains.kotlin.javac.resolve.MockKotlinClassifier
import org.jetbrains.kotlin.load.java.structure.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType