diff --git a/java/java-analysis-api/src/com/intellij/codeInspection/AbstractBaseUastLocalInspectionTool.java b/java/java-analysis-api/src/com/intellij/codeInspection/AbstractBaseUastLocalInspectionTool.java index 5decdd8f623e..50381e6643ec 100644 --- a/java/java-analysis-api/src/com/intellij/codeInspection/AbstractBaseUastLocalInspectionTool.java +++ b/java/java-analysis-api/src/com/intellij/codeInspection/AbstractBaseUastLocalInspectionTool.java @@ -11,23 +11,20 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.uast.*; import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor; -import java.util.Arrays; -import java.util.List; - public abstract class AbstractBaseUastLocalInspectionTool extends LocalInspectionTool { private static final Condition PROBLEM_ELEMENT_CONDITION = Conditions.and(Conditions.instanceOf(PsiFile.class, PsiClass.class, PsiMethod.class, PsiField.class), Conditions.notInstanceOf(PsiTypeParameter.class)); - private final List> myUElementsTypesHint; + private final Class[] myUElementsTypesHint; protected AbstractBaseUastLocalInspectionTool() { - myUElementsTypesHint = Arrays.asList(UFile.class, UClass.class, UMethod.class, UField.class); + this(UFile.class, UClass.class, UMethod.class, UField.class); } protected AbstractBaseUastLocalInspectionTool(Class... uElementsTypesHint) { - myUElementsTypesHint = Arrays.asList(uElementsTypesHint); + myUElementsTypesHint = uElementsTypesHint; } /** diff --git a/java/java-analysis-api/src/com/intellij/uast/UastHintedVisitorAdapter.kt b/java/java-analysis-api/src/com/intellij/uast/UastHintedVisitorAdapter.kt index a2c5af6ea3bb..8cd8e46049da 100644 --- a/java/java-analysis-api/src/com/intellij/uast/UastHintedVisitorAdapter.kt +++ b/java/java-analysis-api/src/com/intellij/uast/UastHintedVisitorAdapter.kt @@ -11,7 +11,7 @@ import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor open class UastHintedVisitorAdapter(private val plugin: UastLanguagePlugin, private val visitor: AbstractUastNonRecursiveVisitor, private val directOnly: Boolean, - private val uElementTypesHint: List> + private val uElementTypesHint: Array> ) : PsiElementVisitor() { override fun visitElement(element: PsiElement) { @@ -27,7 +27,7 @@ open class UastHintedVisitorAdapter(private val plugin: UastLanguagePlugin, @JvmOverloads fun create(language: Language, visitor: AbstractUastNonRecursiveVisitor, - uElementTypesHint: List>, + uElementTypesHint: Array>, directOnly: Boolean = true): PsiElementVisitor { val uastLanguagePlugin = UastLanguagePlugin.byLanguage(language) ?: return EMPTY_VISITOR return UastHintedVisitorAdapter(uastLanguagePlugin, visitor, directOnly, uElementTypesHint) diff --git a/uast/uast-common/src/org/jetbrains/uast/UastContext.kt b/uast/uast-common/src/org/jetbrains/uast/UastContext.kt index 075e583d702e..f4eecf2bcc44 100644 --- a/uast/uast-common/src/org/jetbrains/uast/UastContext.kt +++ b/uast/uast-common/src/org/jetbrains/uast/UastContext.kt @@ -116,7 +116,7 @@ fun PsiElement?.toUElement(cls: Class): T? = fun PsiElement?.toUElementOfExpectedTypes(vararg clss: Class): T? = this?.let { ServiceManager.getService(project, UastContext::class.java) - .convertElementWithParent(this, if (clss.isNotEmpty()) clss.toList() else DEFAULT_TYPES_LIST) as T? + .convertElementWithParent(this, if (clss.isNotEmpty()) clss else DEFAULT_TYPES_LIST) as T? } @@ -150,7 +150,7 @@ fun PsiElement?.getUastParentOfType(cls: Class, strict: Bo inline fun PsiElement?.getUastParentOfType(strict: Boolean = false): T? = getUastParentOfType(T::class.java, strict) @JvmField -val DEFAULT_TYPES_LIST: List> = listOf(UElement::class.java) +val DEFAULT_TYPES_LIST: Array> = arrayOf(UElement::class.java) @JvmField -val DEFAULT_EXPRESSION_TYPES_LIST: List> = listOf(UExpression::class.java) +val DEFAULT_EXPRESSION_TYPES_LIST: Array> = arrayOf(UExpression::class.java) diff --git a/uast/uast-common/src/org/jetbrains/uast/UastLanguagePlugin.kt b/uast/uast-common/src/org/jetbrains/uast/UastLanguagePlugin.kt index ac82337d6261..529f393c65c9 100644 --- a/uast/uast-common/src/org/jetbrains/uast/UastLanguagePlugin.kt +++ b/uast/uast-common/src/org/jetbrains/uast/UastLanguagePlugin.kt @@ -106,7 +106,7 @@ interface UastLanguagePlugin { fun isExpressionValueUsed(element: UExpression): Boolean @JvmDefault - fun convertElementWithParent(element: PsiElement, requiredTypes: List>): T? = + fun convertElementWithParent(element: PsiElement, requiredTypes: Array>): T? = when { requiredTypes.isEmpty() -> convertElementWithParent(element, null) requiredTypes.size == 1 -> convertElementWithParent(element, requiredTypes.single()) @@ -116,7 +116,7 @@ interface UastLanguagePlugin { @JvmDefault - fun convertToAlternatives(element: PsiElement, requiredTypes: List>): Sequence = + fun convertToAlternatives(element: PsiElement, requiredTypes: Array>): Sequence = sequenceOf(convertElementWithParent(element, requiredTypes)).filterNotNull() } diff --git a/uast/uast-common/src/org/jetbrains/uast/internal/UElementToPsiElementMapping.kt b/uast/uast-common/src/org/jetbrains/uast/internal/UElementToPsiElementMapping.kt index 2482a2c8e0f8..0ab055c60f1b 100644 --- a/uast/uast-common/src/org/jetbrains/uast/internal/UElementToPsiElementMapping.kt +++ b/uast/uast-common/src/org/jetbrains/uast/internal/UElementToPsiElementMapping.kt @@ -9,28 +9,26 @@ class UElementToPsiElementMapping(val baseMapping: Map, Clas constructor(vararg mapping: Pair, ClassSet>) : this(mapOf(*mapping)) - private val internalMapping = ConcurrentHashMap, List>() + private val internalMapping = ConcurrentHashMap, ClassSet>() - private operator fun get(uCls: Class): List { + operator fun get(uCls: Class): ClassSet { internalMapping[uCls]?.let { return it } - var result = mutableListOf() + val applicableClassSets = mutableListOf() for ((key, set) in baseMapping.entries) { - if (uCls.isAssignableFrom(key)) result.add(set) - } - - if (result.size > MERGING_CLASS_SET_LIMIT) { - result = mutableListOf(ClassSet(*result.flatMap { it.initialClasses.toList() }.toTypedArray())) + if (uCls.isAssignableFrom(key)) applicableClassSets.add(set) } + val result = mergeClassSets(applicableClassSets) internalMapping[uCls] = result return result } - fun canConvert(psiCls: Class, targets: List>): Boolean { + + fun canConvert(psiCls: Class, targets: Array>): Boolean { for (target in targets) { - if (this[target].any { it.contains(psiCls) }) + if (this[target].contains(psiCls)) return true } return false @@ -39,17 +37,30 @@ class UElementToPsiElementMapping(val baseMapping: Map, Clas } -private const val MERGING_CLASS_SET_LIMIT = 5 +private const val SIMPLE_CLASS_SET_LIMIT = 5 + +private fun mergeClassSets(result: List) = result.singleOrNull() ?: ClassSet( + *result.flatMap { it.initialClasses.asIterable() }.toTypedArray()) class ClassSet(vararg val initialClasses: Class<*>) { - private val internalMapping = ConcurrentHashMap, Boolean>().apply { - for (initialClass in initialClasses) { - this[initialClass] = true - } + private val isSimple = initialClasses.size <= SIMPLE_CLASS_SET_LIMIT + + private lateinit var internalMapping: ConcurrentHashMap, Boolean> + + init { + if (!isSimple) + internalMapping = ConcurrentHashMap, Boolean>().apply { + for (initialClass in initialClasses) { + this[initialClass] = true + } + } } + fun contains(cls: Class<*>): Boolean = - internalMapping[cls] ?: initialClasses.any { it.isAssignableFrom(cls) }.also { internalMapping[cls] = it } + if (isSimple) anyAssignable(cls) else internalMapping[cls] ?: anyAssignable(cls).also { internalMapping[cls] = it } + + private fun anyAssignable(cls: Class<*>): Boolean = initialClasses.any { it.isAssignableFrom(cls) } } \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/JavaUastLanguagePlugin.kt b/uast/uast-java/src/org/jetbrains/uast/java/JavaUastLanguagePlugin.kt index fc4ada3697ee..7f7ad1eec347 100644 --- a/uast/uast-java/src/org/jetbrains/uast/java/JavaUastLanguagePlugin.kt +++ b/uast/uast-java/src/org/jetbrains/uast/java/JavaUastLanguagePlugin.kt @@ -18,7 +18,6 @@ package org.jetbrains.uast.java import com.intellij.lang.Language import com.intellij.lang.java.JavaLanguage -import com.intellij.openapi.util.registry.Registry import com.intellij.psi.* import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl import com.intellij.psi.util.PsiTreeUtil @@ -29,8 +28,6 @@ import org.jetbrains.uast.java.expressions.JavaUSynchronizedExpression class JavaUastLanguagePlugin : UastLanguagePlugin { - private val checkCanConvert = Registry.`is`("uast.java.use.psi.type.precheck") - override val priority: Int = 0 override fun isFileSupported(fileName: String): Boolean = fileName.endsWith(".java", ignoreCase = true) @@ -99,20 +96,19 @@ class JavaUastLanguagePlugin : UastLanguagePlugin { } @Suppress("UNCHECKED_CAST") - fun convertElement(element: PsiElement, parent: UElement?, requiredTypes: List>): T? { + fun convertElement(element: PsiElement, parent: UElement?, requiredTypes: Array>): T? { val nonEmptyRequiredTypes = requiredTypes.nonEmptyOr(DEFAULT_TYPES_LIST) - if (checkCanConvert && !canConvert(element.javaClass, nonEmptyRequiredTypes)) return null - + if (!canConvert(element.javaClass, requiredTypes)) return null return (convertDeclaration(element, parent, nonEmptyRequiredTypes) ?: JavaConverter.convertPsiElement(element, parent, nonEmptyRequiredTypes)) as? T } - override fun convertElementWithParent(element: PsiElement, requiredTypes: List>): T? { + override fun convertElementWithParent(element: PsiElement, requiredTypes: Array>): T? { return convertElement(element, null, requiredTypes) } - override fun convertToAlternatives(element: PsiElement, requiredTypes: List>) = when (element) { - is `PsiMethodCallExpression` -> + override fun convertToAlternatives(element: PsiElement, requiredTypes: Array>) = when (element) { + is PsiMethodCallExpression -> JavaConverter.psiMethodCallConversionAlternatives(element, null, requiredTypes.nonEmptyOr(DEFAULT_EXPRESSION_TYPES_LIST)) as Sequence @@ -121,7 +117,7 @@ class JavaUastLanguagePlugin : UastLanguagePlugin { private fun convertDeclaration(element: PsiElement, givenParent: UElement?, - requiredType: List>): UElement? { + requiredType: Array>): UElement? { fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? { return fun(): UElement? { return ctor(element as P, givenParent) @@ -155,15 +151,15 @@ internal inline fun Class<*>?.el(f: () -> UElement? return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null } -internal inline fun List>.el(f: () -> UElement?): UElement? { +internal inline fun Array>.el(f: () -> UElement?): UElement? { return if (isAssignableFrom(ActualT::class.java)) f() else null } -internal inline fun List>.expr(f: () -> UExpression?): UExpression? { +internal inline fun Array>.expr(f: () -> UExpression?): UExpression? { return if (isAssignableFrom(ActualT::class.java)) f() else null } -internal fun List>.isAssignableFrom(cls: Class<*>) = any { it.isAssignableFrom(cls) } +internal fun Array>.isAssignableFrom(cls: Class<*>) = any { it.isAssignableFrom(cls) } internal object JavaConverter { @@ -181,7 +177,7 @@ internal object JavaConverter { internal fun convertPsiElement(el: PsiElement, givenParent: UElement?, - requiredType: List> = DEFAULT_TYPES_LIST): UElement? { + requiredType: Array> = DEFAULT_TYPES_LIST): UElement? { fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? { return fun(): UElement? { @@ -214,7 +210,7 @@ internal object JavaConverter { internal fun convertReference(reference: PsiJavaCodeReferenceElement, givenParent: UElement?, - requiredType: List> = DEFAULT_TYPES_LIST): UExpression? { + requiredType: Array> = DEFAULT_TYPES_LIST): UExpression? { return with(requiredType) { if (reference.isQualified) { expr { JavaUQualifiedReferenceExpression(reference, givenParent) } @@ -228,7 +224,7 @@ internal object JavaConverter { internal fun convertExpression(el: PsiExpression, givenParent: UElement?, - requiredType: List> = DEFAULT_EXPRESSION_TYPES_LIST): UExpression? { + requiredType: Array> = DEFAULT_EXPRESSION_TYPES_LIST): UExpression? { fun

build(ctor: (P, UElement?) -> UExpression): () -> UExpression? { return fun(): UExpression? { return ctor(el as P, givenParent) @@ -271,7 +267,7 @@ internal object JavaConverter { internal fun psiMethodCallConversionAlternatives(element: PsiMethodCallExpression, givenParent: UElement?, - requiredTypes: List>): Sequence { + requiredTypes: Array>): Sequence { if (element.methodExpression.qualifierExpression == null) { return sequenceOf(requiredTypes.expr { JavaUCallExpression(element, givenParent) }).filterNotNull() } @@ -293,7 +289,7 @@ internal object JavaConverter { internal fun convertStatement(el: PsiStatement, givenParent: UElement?, - requiredType: List> = DEFAULT_EXPRESSION_TYPES_LIST): UExpression? { + requiredType: Array> = DEFAULT_EXPRESSION_TYPES_LIST): UExpression? { fun

build(ctor: (P, UElement?) -> UExpression): () -> UExpression? { return fun(): UExpression? { return ctor(el as P, givenParent) @@ -373,8 +369,9 @@ internal object JavaConverter { } } -private fun expressionTypes(requiredType: Class?) = requiredType?.let { listOf(it) } ?: DEFAULT_EXPRESSION_TYPES_LIST +private fun expressionTypes(requiredType: Class?) = requiredType?.let { arrayOf(it) } ?: DEFAULT_EXPRESSION_TYPES_LIST -private fun elementTypes(requiredType: Class?) = requiredType?.let { listOf(it) } ?: DEFAULT_TYPES_LIST +private fun elementTypes(requiredType: Class?) = requiredType?.let { arrayOf(it) } ?: DEFAULT_TYPES_LIST -private fun List>.nonEmptyOr(default: List>) = takeIf { it.isNotEmpty() } ?: default \ No newline at end of file +private fun Array>.nonEmptyOr(default: Array>) = takeIf { it.isNotEmpty() } + ?: default \ No newline at end of file diff --git a/uast/uast-java/src/org/jetbrains/uast/java/psiElementMapping.kt b/uast/uast-java/src/org/jetbrains/uast/java/psiElementMapping.kt index 6b9462725f58..5c6b66ecca05 100644 --- a/uast/uast-java/src/org/jetbrains/uast/java/psiElementMapping.kt +++ b/uast/uast-java/src/org/jetbrains/uast/java/psiElementMapping.kt @@ -1,15 +1,30 @@ // Copyright 2000-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 file. package org.jetbrains.uast.java +import com.intellij.openapi.util.registry.Registry import com.intellij.psi.* import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl import org.jetbrains.uast.* import org.jetbrains.uast.internal.ClassSet import org.jetbrains.uast.internal.UElementToPsiElementMapping +private val checkCanConvert = Registry.`is`("uast.java.use.psi.type.precheck") -internal fun canConvert(psiCls: Class, targets: List>) = - conversionMapping.canConvert(psiCls, targets) +internal fun canConvert(psiCls: Class, targets: Array>): Boolean { + if (!checkCanConvert) return true + + if (targets.size == 1) { + // checking the most popular cases before looking up in hashtable + when (targets.single()) { + UElement::class.java -> uElementClassSet.contains(psiCls) + ULiteralExpression::class.java -> uLiteralClassSet.contains(psiCls) + UCallExpression::class.java -> uCallClassSet.contains(psiCls) + } + } + + + return conversionMapping.canConvert(psiCls, targets) +} private val conversionMapping = UElementToPsiElementMapping( UClass::class.java to ClassSet(PsiClass::class.java), @@ -83,3 +98,9 @@ private val conversionMapping = UElementToPsiElementMapping( UExpression::class.java to ClassSet(PsiExpressionStatement::class.java), USwitchClauseExpression::class.java to ClassSet(PsiSwitchLabelStatementBase::class.java) ) + +val uElementClassSet = ClassSet(*conversionMapping.baseMapping.flatMap { it.value.initialClasses.asIterable() }.toTypedArray()) + +val uLiteralClassSet: ClassSet = conversionMapping[ULiteralExpression::class.java] + +val uCallClassSet: ClassSet = conversionMapping[UCallExpression::class.java] diff --git a/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaUastApiTest.kt b/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaUastApiTest.kt index 46fea364a8e0..b0d3442cb3cc 100644 --- a/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaUastApiTest.kt +++ b/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaUastApiTest.kt @@ -84,21 +84,21 @@ class JavaUastApiTest : AbstractJavaUastTest() { val javaUastLanguagePlugin = UastLanguagePlugin.byLanguage(callExpression.language)!! - javaUastLanguagePlugin.convertToAlternatives(callExpression, listOf(UCallExpression::class.java)).let { + javaUastLanguagePlugin.convertToAlternatives(callExpression, arrayOf(UCallExpression::class.java)).let { assertEquals("format(\"q\")", it.joinToString(transform = UExpression::asRenderString)) } - javaUastLanguagePlugin.convertToAlternatives(callExpression, listOf(UQualifiedReferenceExpression::class.java, - UCallExpression::class.java)).let { + javaUastLanguagePlugin.convertToAlternatives(callExpression, arrayOf(UQualifiedReferenceExpression::class.java, + UCallExpression::class.java)).let { assertEquals("String.format(\"q\"), format(\"q\")", it.joinToString(transform = UExpression::asRenderString)) } - javaUastLanguagePlugin.convertToAlternatives(callExpression, listOf(UCallExpression::class.java, - UQualifiedReferenceExpression::class.java)).let { + javaUastLanguagePlugin.convertToAlternatives(callExpression, arrayOf(UCallExpression::class.java, + UQualifiedReferenceExpression::class.java)).let { assertEquals("format(\"q\"), String.format(\"q\")", it.joinToString(transform = UExpression::asRenderString)) } - javaUastLanguagePlugin.convertToAlternatives(callExpression, listOf(UExpression::class.java)).let { + javaUastLanguagePlugin.convertToAlternatives(callExpression, arrayOf(UExpression::class.java)).let { assertEquals("String.format(\"q\"), format(\"q\")", it.joinToString(transform = UExpression::asRenderString)) }