Uast: support internal class names in Java and method signatures in Java and Kotlin

This commit is contained in:
Yan Zhulanow
2016-03-22 19:44:22 +03:00
parent 5bc31112c1
commit 3dc091d92c
5 changed files with 111 additions and 3 deletions
@@ -28,7 +28,8 @@ interface UClass : UDeclaration, UFqNamed, UModifierOwner, UAnnotated {
val companions: List<UClass>
val internalName: String?
open val internalName: String?
get() = null
val superTypes: List<UType>
val declarations: List<UDeclaration>
@@ -25,6 +25,9 @@ interface UFunction : UDeclaration, UModifierOwner, UAnnotated {
val body: UExpression
val visibility: UastVisibility
open val bytecodeDescriptor: String?
get() = null
fun getSuperFunctions(context: UastContext): List<UFunction>
override fun traverse(handler: UastHandler) {
@@ -15,7 +15,10 @@
*/
package org.jetbrains.uast.java
import com.intellij.ide.util.JavaAnonymousClassesHelper
import com.intellij.psi.*
import com.intellij.psi.util.ClassUtil
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.PsiTypesUtil
import org.jetbrains.uast.*
import org.jetbrains.uast.kinds.UastClassKind
@@ -59,7 +62,7 @@ class JavaUClass(
override val isAnonymous: Boolean
get() = psi is PsiAnonymousClass
override val internalName = null
override val internalName by lz { getInternalName(psi) }
override val superTypes by lz {
psi.extendsListTypes.map { JavaConverter.convert(it, this) } + psi.implementsListTypes.map { JavaConverter.convert(it, this) }
@@ -98,6 +101,61 @@ class JavaUClass(
return isSubClassOf(psi, name)
}
private companion object {
/* Primarily copied from IntellijLintUtils and ClassContext classes from the Android IDEA plugin */
private fun getInternalName(psiClass: PsiClass): String? {
if (psiClass is PsiAnonymousClass) {
val parent = PsiTreeUtil.getParentOfType(psiClass, PsiClass::class.java)
if (parent != null) {
val internalName = getInternalName(parent) ?: return null
return internalName + JavaAnonymousClassesHelper.getName(psiClass)
}
}
var sig = ClassUtil.getJVMClassName(psiClass)
if (sig == null) {
val qualifiedName = psiClass.qualifiedName
if (qualifiedName != null) {
return getInternalName(qualifiedName)
}
return null
}
else if (sig.indexOf('.') != -1) {
// Workaround -- ClassUtil doesn't treat this correctly!
// .replace('.', '/');
sig = getInternalName(sig)
}
return sig
}
private fun getInternalName(fqcn: String): String {
if (fqcn.indexOf('.') == -1) {
return fqcn
}
// If class name contains $, it's not an ambiguous inner class name.
if (fqcn.indexOf('$') != -1) {
return fqcn.replace('.', '/')
}
// Let's assume that components that start with Caps are class names.
val sb = StringBuilder(fqcn.length)
var prev: String? = null
for (part in fqcn.split('.')) {
if (prev != null && !prev.isEmpty()) {
if (Character.isUpperCase(prev[0])) {
sb.append('$')
}
else {
sb.append('/')
}
}
sb.append(part)
prev = part
}
return sb.toString()
}
}
}
private class JavaUAnonymousClassConstructor(
@@ -15,7 +15,10 @@
*/
package org.jetbrains.uast.java
import com.intellij.psi.PsiArrayType
import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiType
import org.jetbrains.uast.*
import org.jetbrains.uast.psi.PsiElementBacked
@@ -53,10 +56,37 @@ class JavaUFunction(
override val visibility: UastVisibility
get() = psi.getVisibility()
override val body by lz { JavaConverter.convertOrEmpty(psi.body, this) }
override val bytecodeDescriptor by lz { getDescriptor(psi) }
override fun getSuperFunctions(context: UastContext): List<UFunction> {
return psi.findSuperMethods().map { context.convert(it) as? UFunction }.filterNotNull()
}
private companion object {
fun getDescriptor(psi: PsiMethod): String? {
val parameterTypes = psi.parameterList.parameters.map {
renderType(it.type) ?: return null
}
val returnType = renderType(psi.returnType) ?: return null
return parameterTypes.joinToString("", "(", ")") + returnType
}
fun renderType(type: PsiType?): String? = when (type) {
null -> null
PsiType.CHAR -> "C"
PsiType.DOUBLE -> "D"
PsiType.FLOAT -> "F"
PsiType.INT -> "I"
PsiType.LONG -> "J"
PsiType.SHORT -> "S"
PsiType.BOOLEAN -> "Z"
PsiType.VOID -> "V"
is PsiArrayType -> renderType(type.componentType)?.let { "[$it" }
is PsiClassType -> type.resolve()?.qualifiedName?.let { "L$it;" }
else -> null
}
}
}
@@ -16,11 +16,16 @@
package org.jetbrains.kotlin.uast
import org.jetbrains.kotlin.codegen.ClassBuilderMode
import org.jetbrains.kotlin.codegen.state.IncompatibleClassTracker
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -45,6 +50,14 @@ abstract class KotlinAbstractUFunction : KotlinAbstractUElement(), UFunction, Ps
}.filterNotNull()
}
override val bytecodeDescriptor by lz {
val bindingContext = psi.analyze(BodyResolveMode.PARTIAL)
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, psi] as? FunctionDescriptor ?: return@lz null
val typeMapper = KotlinTypeMapper(BindingContext.EMPTY, ClassBuilderMode.LIGHT_CLASSES, NoResolveFileClassesProvider, null,
IncompatibleClassTracker.DoNothing, JvmAbi.DEFAULT_MODULE_NAME)
typeMapper.mapAsmMethod(descriptor).descriptor
}
override fun hasModifier(modifier: UastModifier) = psi.hasModifier(modifier)
override val annotations by lz { psi.getUastAnnotations(this) }
@@ -56,6 +69,9 @@ class KotlinConstructorUFunction(
override val psi: KtConstructor<*>,
override val parent: UElement
) : KotlinAbstractUFunction(), PsiElementBacked {
override val name: String
get() = "<init>"
override val nameElement by lz {
val constructorKeyword = psi.getConstructorKeyword()?.let { KotlinDumbUElement(it, this) }
constructorKeyword ?: this.getContainingFunction()?.nameElement