add ultra-light classes/members that work without backend in simple cases

This commit is contained in:
peter
2018-10-25 17:58:05 +02:00
parent 387efc3791
commit ebc998d710
48 changed files with 2502 additions and 98 deletions
@@ -0,0 +1,125 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.perf
import com.intellij.psi.*
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.junit.Assert
@TestOnly
object UltraLightChecker {
fun checkClassEquivalence(file: KtFile) {
for (ktClass in allClasses(file)) {
checkClassEquivalence(ktClass)
}
}
fun allClasses(file: KtFile): List<KtClassOrObject> =
SyntaxTraverser.psiTraverser(file).filter(KtClassOrObject::class.java).toList()
fun checkClassEquivalence(ktClass: KtClassOrObject): KtUltraLightClass? {
val gold = KtLightClassForSourceDeclaration.create(ktClass)
val ultraLightClass = LightClassGenerationSupport.getInstance(ktClass.project).createUltraLightClass(ktClass) ?: return null
if (gold != null) {
Assert.assertFalse(gold.javaClass.name.contains("Ultra"))
}
val goldText = gold?.renderClass().orEmpty()
val ultraText = ultraLightClass.renderClass()
if (goldText != ultraText) {
Assert.assertEquals(
"// Classic implementation:\n$goldText",
"// Ultra-light implementation:\n$ultraText"
)
}
return ultraLightClass
}
private fun PsiAnnotation.renderAnnotation() =
"@" + qualifiedName + "(" + parameterList.attributes.joinToString { it.name + "=" + (it.value?.text ?: "?") } + ")"
private fun PsiModifierListOwner.renderModifiers(): String {
val buffer = StringBuilder()
for (annotation in annotations) {
buffer.append(annotation.renderAnnotation())
buffer.append(if (this is PsiParameter) " " else "\n")
}
for (modifier in PsiModifier.MODIFIERS.filter(::hasModifierProperty)) {
buffer.append(modifier).append(" ")
}
return buffer.toString()
}
private fun PsiType.renderType() = getCanonicalText(true)
private fun PsiReferenceList?.renderRefList(keyword: String): String {
if (this == null || this.referencedTypes.isEmpty()) return ""
return " " + keyword + " " + referencedTypes.joinToString { it.renderType() }
}
private fun PsiVariable.renderVar(): String {
var result = this.renderModifiers() + type.renderType() + " " + name
if (this is PsiParameter && this.isVarArgs) {
result += " /* vararg */"
}
computeConstantValue()?.let { result += " /* constant value $it */" }
return result
}
private fun PsiTypeParameterListOwner.renderTypeParams() =
if (typeParameters.isEmpty()) ""
else "<" + typeParameters.joinToString {
val bounds =
if (it.extendsListTypes.isNotEmpty())
" extends " + it.extendsListTypes.joinToString(" & ", transform = { it.renderType() })
else ""
it.name!! + bounds
} + "> "
private fun PsiMethod.renderMethod() =
renderModifiers() +
(if (isVarArgs) "/* vararg */ " else "") +
renderTypeParams() +
(returnType?.renderType() ?: "") + " " +
name +
"(" + parameterList.parameters.joinToString { it.renderModifiers() + it.type.renderType() } + ")" +
(this as? PsiAnnotationMethod)?.defaultValue?.let { " default " + it.text }.orEmpty() +
throwsList.referencedTypes.let { thrownTypes ->
if (thrownTypes.isEmpty()) ""
else " throws " + thrownTypes.joinToString { it.renderType() }
} +
";"
private fun PsiClass.renderClass(): String {
val classWord = when {
isAnnotationType -> "@interface"
isInterface -> "interface"
isEnum -> "enum"
else -> "class"
}
return renderModifiers() +
classWord + " " +
name + " /* " + qualifiedName + "*/" +
renderTypeParams() +
extendsList.renderRefList("extends") +
implementsList.renderRefList("implements") +
" {\n" +
(if (isEnum) fields.filterIsInstance<PsiEnumConstant>().joinToString(",\n") { it.name } + ";\n\n" else "") +
fields.filterNot { it is PsiEnumConstant }.map { it.renderVar().prependIndent(" ") + ";\n\n" }.sorted().joinToString("") +
methods.map { it.renderMethod().prependIndent(" ") + "\n\n" }.sorted().joinToString("") +
innerClasses.map { "class ${it.name} ...\n\n".prependIndent(" ") }.sorted().joinToString("") +
"}"
}
}