Debugger: Navigate to the right file in MPP projects (#KT-25152, #KT-25147)
This commit is contained in:
Generated
+1
@@ -1,6 +1,7 @@
|
|||||||
<component name="ProjectDictionaryState">
|
<component name="ProjectDictionaryState">
|
||||||
<dictionary name="yan">
|
<dictionary name="yan">
|
||||||
<words>
|
<words>
|
||||||
|
<w>debuggee</w>
|
||||||
<w>deserializes</w>
|
<w>deserializes</w>
|
||||||
<w>impls</w>
|
<w>impls</w>
|
||||||
<w>kapt</w>
|
<w>kapt</w>
|
||||||
|
|||||||
+13
-28
@@ -1,43 +1,28 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2016 JetBrains s.r.o.
|
* 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.
|
||||||
* 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.kapt3
|
package org.jetbrains.kotlin.codegen
|
||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.AbstractClassBuilder
|
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactory
|
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilderMode
|
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||||
import org.jetbrains.org.objectweb.asm.*
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
import org.jetbrains.org.objectweb.asm.tree.*
|
import org.jetbrains.org.objectweb.asm.tree.*
|
||||||
|
|
||||||
internal class Kapt3BuilderFactory : ClassBuilderFactory {
|
class OriginCollectingClassBuilderFactory(private val builderMode: ClassBuilderMode) : ClassBuilderFactory {
|
||||||
internal val compiledClasses = mutableListOf<ClassNode>()
|
val compiledClasses = mutableListOf<ClassNode>()
|
||||||
internal val origins = mutableMapOf<Any, JvmDeclarationOrigin>()
|
val origins = mutableMapOf<Any, JvmDeclarationOrigin>()
|
||||||
|
|
||||||
override fun getClassBuilderMode(): ClassBuilderMode = ClassBuilderMode.KAPT3
|
override fun getClassBuilderMode(): ClassBuilderMode = builderMode
|
||||||
|
|
||||||
override fun newClassBuilder(origin: JvmDeclarationOrigin): AbstractClassBuilder.Concrete {
|
override fun newClassBuilder(origin: JvmDeclarationOrigin): AbstractClassBuilder.Concrete {
|
||||||
val classNode = ClassNode()
|
val classNode = ClassNode()
|
||||||
compiledClasses += classNode
|
compiledClasses += classNode
|
||||||
origins.put(classNode, origin)
|
origins[classNode] = origin
|
||||||
return Kapt3ClassBuilder(classNode)
|
return OriginCollectingClassBuilder(classNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class Kapt3ClassBuilder(val classNode: ClassNode) : AbstractClassBuilder.Concrete(classNode) {
|
private inner class OriginCollectingClassBuilder(val classNode: ClassNode) : AbstractClassBuilder.Concrete(classNode) {
|
||||||
override fun newField(
|
override fun newField(
|
||||||
origin: JvmDeclarationOrigin,
|
origin: JvmDeclarationOrigin,
|
||||||
access: Int,
|
access: Int,
|
||||||
@@ -47,7 +32,7 @@ internal class Kapt3BuilderFactory : ClassBuilderFactory {
|
|||||||
value: Any?
|
value: Any?
|
||||||
): FieldVisitor {
|
): FieldVisitor {
|
||||||
val fieldNode = super.newField(origin, access, name, desc, signature, value) as FieldNode
|
val fieldNode = super.newField(origin, access, name, desc, signature, value) as FieldNode
|
||||||
origins.put(fieldNode, origin)
|
origins[fieldNode] = origin
|
||||||
return fieldNode
|
return fieldNode
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +45,7 @@ internal class Kapt3BuilderFactory : ClassBuilderFactory {
|
|||||||
exceptions: Array<out String>?
|
exceptions: Array<out String>?
|
||||||
): MethodVisitor {
|
): MethodVisitor {
|
||||||
val methodNode = super.newMethod(origin, access, name, desc, signature, exceptions) as MethodNode
|
val methodNode = super.newMethod(origin, access, name, desc, signature, exceptions) as MethodNode
|
||||||
origins.put(methodNode, origin)
|
origins[methodNode] = origin
|
||||||
|
|
||||||
// ASM doesn't read information about local variables for the `abstract` methods so we need to get it manually
|
// ASM doesn't read information about local variables for the `abstract` methods so we need to get it manually
|
||||||
if ((access and Opcodes.ACC_ABSTRACT) != 0 && methodNode.localVariables == null) {
|
if ((access and Opcodes.ACC_ABSTRACT) != 0 && methodNode.localVariables == null) {
|
||||||
@@ -73,7 +58,7 @@ internal class Kapt3BuilderFactory : ClassBuilderFactory {
|
|||||||
|
|
||||||
override fun asBytes(builder: ClassBuilder): ByteArray {
|
override fun asBytes(builder: ClassBuilder): ByteArray {
|
||||||
val classWriter = ClassWriter(0)
|
val classWriter = ClassWriter(0)
|
||||||
(builder as Kapt3ClassBuilder).classNode.accept(classWriter)
|
(builder as OriginCollectingClassBuilder).classNode.accept(classWriter)
|
||||||
return classWriter.toByteArray()
|
return classWriter.toByteArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +94,10 @@ fun PsiElement.nextLeaf(filter: (PsiElement) -> Boolean): PsiElement? {
|
|||||||
return leaf
|
return leaf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T : PsiElement> PsiElement.getParentOfTypes(strict: Boolean = false, vararg parentClasses: Class<out T>): T? {
|
||||||
|
return getParentOfTypesAndPredicate(strict, *parentClasses) { true }
|
||||||
|
}
|
||||||
|
|
||||||
fun <T : PsiElement> PsiElement.getParentOfTypesAndPredicate(
|
fun <T : PsiElement> PsiElement.getParentOfTypesAndPredicate(
|
||||||
strict: Boolean = false, vararg parentClasses: Class<out T>, predicate: (T) -> Boolean
|
strict: Boolean = false, vararg parentClasses: Class<out T>, predicate: (T) -> Boolean
|
||||||
): T? {
|
): T? {
|
||||||
@@ -121,6 +125,14 @@ inline fun <reified T : PsiElement> PsiElement.getParentOfType(strict: Boolean):
|
|||||||
return PsiTreeUtil.getParentOfType(this, T::class.java, strict)
|
return PsiTreeUtil.getParentOfType(this, T::class.java, strict)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : PsiElement, reified V : PsiElement> PsiElement.getParentOfTypes2(): PsiElement? {
|
||||||
|
return PsiTreeUtil.getParentOfType(this, T::class.java, V::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : PsiElement, reified V : PsiElement, reified U : PsiElement> PsiElement.getParentOfTypes3(): PsiElement? {
|
||||||
|
return PsiTreeUtil.getParentOfType(this, T::class.java, V::class.java, U::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
inline fun <reified T : PsiElement> PsiElement.getStrictParentOfType(): T? {
|
inline fun <reified T : PsiElement> PsiElement.getStrictParentOfType(): T? {
|
||||||
return PsiTreeUtil.getParentOfType(this, T::class.java, true)
|
return PsiTreeUtil.getParentOfType(this, T::class.java, true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,10 +76,7 @@ import org.jetbrains.kotlin.idea.conversion.copy.AbstractLiteralKotlinToKotlinCo
|
|||||||
import org.jetbrains.kotlin.idea.conversion.copy.AbstractLiteralTextToKotlinCopyPasteTest
|
import org.jetbrains.kotlin.idea.conversion.copy.AbstractLiteralTextToKotlinCopyPasteTest
|
||||||
import org.jetbrains.kotlin.idea.conversion.copy.AbstractTextJavaToKotlinCopyPasteConversionTest
|
import org.jetbrains.kotlin.idea.conversion.copy.AbstractTextJavaToKotlinCopyPasteConversionTest
|
||||||
import org.jetbrains.kotlin.idea.coverage.AbstractKotlinCoverageOutputFilesTest
|
import org.jetbrains.kotlin.idea.coverage.AbstractKotlinCoverageOutputFilesTest
|
||||||
import org.jetbrains.kotlin.idea.debugger.AbstractBeforeExtractFunctionInsertionTest
|
import org.jetbrains.kotlin.idea.debugger.*
|
||||||
import org.jetbrains.kotlin.idea.debugger.AbstractKotlinSteppingTest
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.AbstractPositionManagerTest
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.AbstractSmartStepIntoTest
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.*
|
import org.jetbrains.kotlin.idea.debugger.evaluate.*
|
||||||
import org.jetbrains.kotlin.idea.decompiler.navigation.AbstractNavigateToDecompiledLibraryTest
|
import org.jetbrains.kotlin.idea.decompiler.navigation.AbstractNavigateToDecompiledLibraryTest
|
||||||
import org.jetbrains.kotlin.idea.decompiler.navigation.AbstractNavigateToLibrarySourceTest
|
import org.jetbrains.kotlin.idea.decompiler.navigation.AbstractNavigateToLibrarySourceTest
|
||||||
@@ -640,6 +637,10 @@ fun main(args: Array<String>) {
|
|||||||
model("debugger/tinyApp/src/evaluate/multipleBreakpoints", testMethod = "doMultipleBreakpointsTest")
|
model("debugger/tinyApp/src/evaluate/multipleBreakpoints", testMethod = "doMultipleBreakpointsTest")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testClass<AbstractFileRankingTest> {
|
||||||
|
model("debugger/fileRanking")
|
||||||
|
}
|
||||||
|
|
||||||
testClass<AbstractStubBuilderTest> {
|
testClass<AbstractStubBuilderTest> {
|
||||||
model("stubs", extension = "kt")
|
model("stubs", extension = "kt")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.intellij.openapi.project.DumbService
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.io.FileUtilRt
|
import com.intellij.openapi.util.io.FileUtilRt
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import com.sun.jdi.Location
|
||||||
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
|
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.idea.KotlinFileTypeFactory
|
import org.jetbrains.kotlin.idea.KotlinFileTypeFactory
|
||||||
@@ -45,13 +46,16 @@ object DebuggerUtils {
|
|||||||
project: Project,
|
project: Project,
|
||||||
scope: GlobalSearchScope,
|
scope: GlobalSearchScope,
|
||||||
className: JvmClassName,
|
className: JvmClassName,
|
||||||
fileName: String): KtFile? {
|
fileName: String,
|
||||||
|
location: Location? = null
|
||||||
|
): KtFile? {
|
||||||
return runReadAction {
|
return runReadAction {
|
||||||
findSourceFileForClass(
|
findSourceFileForClass(
|
||||||
project,
|
project,
|
||||||
listOf(scope, KotlinSourceFilterScope.librarySources(GlobalSearchScope.allScope(project), project)),
|
listOf(scope, KotlinSourceFilterScope.librarySources(GlobalSearchScope.allScope(project), project)),
|
||||||
className,
|
className,
|
||||||
fileName)
|
fileName,
|
||||||
|
location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +63,9 @@ object DebuggerUtils {
|
|||||||
project: Project,
|
project: Project,
|
||||||
scopes: List<GlobalSearchScope>,
|
scopes: List<GlobalSearchScope>,
|
||||||
className: JvmClassName,
|
className: JvmClassName,
|
||||||
fileName: String): KtFile? {
|
fileName: String,
|
||||||
|
location: Location?
|
||||||
|
): KtFile? {
|
||||||
if (!isKotlinSourceFile(fileName)) return null
|
if (!isKotlinSourceFile(fileName)) return null
|
||||||
if (DumbService.getInstance(project).isDumb) return null
|
if (DumbService.getInstance(project).isDumb) return null
|
||||||
|
|
||||||
@@ -84,6 +90,8 @@ object DebuggerUtils {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location?.let { return FileRankingCalculatorForIde.findMostAppropriateSource(filesWithExactName, it) }
|
||||||
|
|
||||||
return filesWithExactName.first()
|
return filesWithExactName.first()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,421 @@
|
|||||||
|
/*
|
||||||
|
* 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.debugger
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.sun.jdi.*
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
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.*
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.FileRankingCalculator.Ranking.Companion.LOW
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.FileRankingCalculator.Ranking.Companion.ZERO
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.FileRankingCalculator.Ranking.Companion.MINOR
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.FileRankingCalculator.Ranking.Companion.NORMAL
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.FileRankingCalculator.Ranking.Companion.MAJOR
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.getLineStartOffset
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.varargParameterPosition
|
||||||
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||||
|
import org.jetbrains.kotlin.utils.keysToMap
|
||||||
|
import kotlin.jvm.internal.FunctionBase
|
||||||
|
import org.jetbrains.org.objectweb.asm.Type as AsmType
|
||||||
|
|
||||||
|
object FileRankingCalculatorForIde : FileRankingCalculator() {
|
||||||
|
override fun analyze(element: KtElement) = element.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class FileRankingCalculator(
|
||||||
|
private val checkClassFqName: Boolean = true,
|
||||||
|
private val strictMode: Boolean = false
|
||||||
|
) {
|
||||||
|
abstract fun analyze(element: KtElement): BindingContext
|
||||||
|
|
||||||
|
fun findMostAppropriateSource(files: Collection<KtFile>, location: Location): KtFile {
|
||||||
|
assert(files.isNotEmpty())
|
||||||
|
|
||||||
|
val fileWithRankings = files.keysToMap { fileRankingSafe(it, location) }
|
||||||
|
val fileWithMaxScore = fileWithRankings.maxBy { it.value }!!
|
||||||
|
|
||||||
|
if (strictMode) {
|
||||||
|
require(fileWithMaxScore.value.value >= 0) { "Max score is negative" }
|
||||||
|
|
||||||
|
// Allow only one element with max ranking
|
||||||
|
require(fileWithRankings.count { it.value == fileWithMaxScore.value } == 1) {
|
||||||
|
"Score is the same for several files"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileWithMaxScore.key
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Ranking(val value: Int) : Comparable<Ranking> {
|
||||||
|
companion object {
|
||||||
|
val LOW = Ranking(-1000)
|
||||||
|
val ZERO = Ranking(0)
|
||||||
|
val MINOR = Ranking(1)
|
||||||
|
val NORMAL = Ranking(5)
|
||||||
|
val MAJOR = Ranking(10)
|
||||||
|
|
||||||
|
fun minor(condition: Boolean) = if (condition) MINOR else ZERO
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun unaryMinus() = Ranking(-value)
|
||||||
|
operator fun plus(other: Ranking) = Ranking(value + other.value)
|
||||||
|
override fun compareTo(other: Ranking) = this.value - other.value
|
||||||
|
override fun toString() = value.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collect(vararg conditions: Any): Ranking {
|
||||||
|
return conditions
|
||||||
|
.map { condition ->
|
||||||
|
when (condition) {
|
||||||
|
is Boolean -> Ranking.minor(condition)
|
||||||
|
is Int -> Ranking(condition)
|
||||||
|
is Ranking -> condition
|
||||||
|
else -> error("Invalid condition type ${condition.javaClass.name}")
|
||||||
|
}
|
||||||
|
}.fold(ZERO) { sum, r -> sum + r }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rankingForClass(clazz: KtClassOrObject, fqName: String, virtualMachine: VirtualMachine): Ranking {
|
||||||
|
val bindingContext = analyze(clazz)
|
||||||
|
val descriptor = bindingContext[BindingContext.CLASS, clazz] ?: return ZERO
|
||||||
|
|
||||||
|
val jdiType = virtualMachine.classesByName(fqName).firstOrNull() ?: run {
|
||||||
|
// Check at least the class name if not found
|
||||||
|
return rankingForClassName(fqName, descriptor, bindingContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rankingForClass(clazz, jdiType)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rankingForClass(clazz: KtClassOrObject, type: ReferenceType): Ranking {
|
||||||
|
val bindingContext = analyze(clazz)
|
||||||
|
val descriptor = bindingContext[BindingContext.CLASS, clazz] ?: return ZERO
|
||||||
|
|
||||||
|
return collect(
|
||||||
|
rankingForClassName(type.name(), descriptor, bindingContext),
|
||||||
|
Ranking.minor(type.isAbstract && descriptor.modality == Modality.ABSTRACT),
|
||||||
|
Ranking.minor(type.isFinal && descriptor.modality == Modality.FINAL),
|
||||||
|
Ranking.minor(type.isStatic && !descriptor.isInner),
|
||||||
|
rankingForVisibility(descriptor, type)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rankingForClassName(fqName: String, descriptor: ClassDescriptor, bindingContext: BindingContext): Ranking {
|
||||||
|
val expectedFqName = makeTypeMapper(bindingContext).mapType(descriptor).className
|
||||||
|
return when {
|
||||||
|
checkClassFqName -> if (expectedFqName == fqName) MAJOR else LOW
|
||||||
|
else -> if (expectedFqName.simpleName() == fqName.simpleName()) MAJOR else LOW
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rankingForMethod(function: KtFunction, method: Method): Ranking {
|
||||||
|
val bindingContext = analyze(function)
|
||||||
|
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, function] as? CallableMemberDescriptor ?: return ZERO
|
||||||
|
|
||||||
|
if (function !is KtConstructor<*> && method.name() != descriptor.name.asString())
|
||||||
|
return LOW
|
||||||
|
|
||||||
|
val typeMapper = makeTypeMapper(bindingContext)
|
||||||
|
|
||||||
|
return collect(
|
||||||
|
method.isConstructor && function is KtConstructor<*>,
|
||||||
|
method.isAbstract && descriptor.modality == Modality.ABSTRACT,
|
||||||
|
method.isFinal && descriptor.modality == Modality.FINAL,
|
||||||
|
method.isVarArgs && descriptor.varargParameterPosition() >= 0,
|
||||||
|
rankingForVisibility(descriptor, method),
|
||||||
|
rankingForType(descriptor.returnType, method.safeReturnType(), typeMapper),
|
||||||
|
rankingForValueParameters(descriptor.valueParameters, method.safeArguments(), typeMapper)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rankingForAccessor(accessor: KtPropertyAccessor, method: Method): Ranking {
|
||||||
|
val methodName = method.name()
|
||||||
|
val expectedPropertyName = accessor.property.name ?: return ZERO
|
||||||
|
|
||||||
|
if (accessor.isSetter) {
|
||||||
|
if (!methodName.startsWith("set") || method.returnType() !is VoidType || method.argumentTypes().size != 1)
|
||||||
|
return -MAJOR
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accessor.isGetter) {
|
||||||
|
if (!methodName.startsWith("get") && !methodName.startsWith("is"))
|
||||||
|
return -MAJOR
|
||||||
|
else if (method.returnType() is VoidType || method.argumentTypes().isNotEmpty())
|
||||||
|
return -NORMAL
|
||||||
|
}
|
||||||
|
|
||||||
|
val actualPropertyName = getPropertyName(methodName, accessor.isSetter)
|
||||||
|
if (expectedPropertyName != actualPropertyName)
|
||||||
|
return -NORMAL
|
||||||
|
|
||||||
|
val bindingContext = analyze(accessor.property)
|
||||||
|
val descriptor = bindingContext[BindingContext.PROPERTY_ACCESSOR, accessor] ?: return ZERO
|
||||||
|
|
||||||
|
val jdiPropertyType = when {
|
||||||
|
accessor.isGetter -> method.safeReturnType()
|
||||||
|
else -> method.safeArguments()?.map { it.type() }?.singleOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
return rankingForType(descriptor.returnType, jdiPropertyType, makeTypeMapper(bindingContext))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPropertyName(accessorMethodName: String, isSetter: Boolean): String {
|
||||||
|
if (isSetter) {
|
||||||
|
return accessorMethodName.drop(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
return accessorMethodName.drop(if (accessorMethodName.startsWith("is")) 2 else 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rankingForProperty(property: KtProperty, method: Method): Ranking {
|
||||||
|
val methodName = method.name()
|
||||||
|
val propertyName = property.name ?: return ZERO
|
||||||
|
|
||||||
|
if (property.isTopLevel && method.name() == "<clinit>") {
|
||||||
|
// For top-level property initializers
|
||||||
|
return MINOR
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!methodName.startsWith("get") && !methodName.startsWith("set"))
|
||||||
|
return -MAJOR
|
||||||
|
|
||||||
|
// boolean is
|
||||||
|
return if (methodName.drop(3) == propertyName.capitalize()) MAJOR else -NORMAL
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rankingForValueParameters(
|
||||||
|
ktParameters: List<ValueParameterDescriptor>,
|
||||||
|
jdiParameters: List<LocalVariable>?,
|
||||||
|
typeMapper: KotlinTypeMapper
|
||||||
|
): Ranking {
|
||||||
|
if (jdiParameters == null)
|
||||||
|
return ZERO
|
||||||
|
|
||||||
|
if (ktParameters.size != jdiParameters.size)
|
||||||
|
return -NORMAL
|
||||||
|
|
||||||
|
return ktParameters.zip(jdiParameters).fold(ZERO) { sum, (ktParameter, jdiParameter) ->
|
||||||
|
sum + rankingForValueParameter(ktParameter, jdiParameter, typeMapper)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rankingForValueParameter(
|
||||||
|
ktParameter: ValueParameterDescriptor,
|
||||||
|
jdiParameter: LocalVariable,
|
||||||
|
typeMapper: KotlinTypeMapper
|
||||||
|
): Ranking {
|
||||||
|
return collect(
|
||||||
|
ktParameter.name.asString() == jdiParameter.name(),
|
||||||
|
rankingForType(ktParameter.type, jdiParameter.type(), typeMapper)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rankingForVisibility(descriptor: DeclarationDescriptorWithVisibility, accessible: Accessible): Ranking {
|
||||||
|
return collect(
|
||||||
|
accessible.isPublic && descriptor.visibility == Visibilities.PUBLIC,
|
||||||
|
accessible.isProtected && descriptor.visibility == Visibilities.PROTECTED,
|
||||||
|
accessible.isPrivate && descriptor.visibility == Visibilities.PRIVATE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rankingForType(ktType: KotlinType?, jdiType: Type?, typeMapper: KotlinTypeMapper): Ranking {
|
||||||
|
if (jdiType == null)
|
||||||
|
return ZERO
|
||||||
|
|
||||||
|
if (ktType == null)
|
||||||
|
return Ranking.minor(jdiType is VoidType)
|
||||||
|
|
||||||
|
val asmType = typeMapper.mapType(ktType)
|
||||||
|
return Ranking.minor(
|
||||||
|
when (jdiType) {
|
||||||
|
is VoidType -> asmType == AsmType.VOID_TYPE
|
||||||
|
is LongType -> asmType == AsmType.LONG_TYPE
|
||||||
|
is DoubleType -> asmType == AsmType.DOUBLE_TYPE
|
||||||
|
is CharType -> asmType == AsmType.CHAR_TYPE
|
||||||
|
is FloatType -> asmType == AsmType.FLOAT_TYPE
|
||||||
|
is ByteType -> asmType == AsmType.BYTE_TYPE
|
||||||
|
is IntegerType -> asmType == AsmType.INT_TYPE
|
||||||
|
is BooleanType -> asmType == AsmType.BOOLEAN_TYPE
|
||||||
|
is ShortType -> asmType == AsmType.SHORT_TYPE
|
||||||
|
is ArrayType -> {
|
||||||
|
asmType.sort == AsmType.ARRAY && KotlinBuiltIns.isArrayOrPrimitiveArray(ktType) && run {
|
||||||
|
val ktElementType = ktType.builtIns.getArrayElementType(ktType)
|
||||||
|
val jdiElementType = jdiType.componentType()
|
||||||
|
rankingForType(ktElementType, jdiElementType, typeMapper) > ZERO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ReferenceType -> asmType.className == jdiType.name()
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fileRankingSafe(file: KtFile, location: Location): Ranking {
|
||||||
|
return try {
|
||||||
|
fileRanking(file, location)
|
||||||
|
} catch (e: AbsentInformationException) {
|
||||||
|
ZERO
|
||||||
|
} catch (e: InternalException) {
|
||||||
|
ZERO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fileRanking(file: KtFile, location: Location): Ranking {
|
||||||
|
val locationLineNumber = location.lineNumber() - 1
|
||||||
|
val lineStartOffset = file.getLineStartOffset(locationLineNumber) ?: return LOW
|
||||||
|
val elementAt = file.findElementAt(lineStartOffset) ?: return ZERO
|
||||||
|
|
||||||
|
var overallRanking = ZERO
|
||||||
|
val method = location.method()
|
||||||
|
|
||||||
|
if (method.isLambda()) {
|
||||||
|
val (className, methodName) = method.getContainingClassAndMethodNameForLambda() ?: return ZERO
|
||||||
|
if (method.isBridge && method.isSynthetic) {
|
||||||
|
// It might be a static lambda field accessor
|
||||||
|
val containingClass = elementAt.getParentOfType<KtClassOrObject>(false) ?: return LOW
|
||||||
|
return rankingForClass(containingClass, className, location.virtualMachine())
|
||||||
|
} else {
|
||||||
|
val containingFunctionLiteral = findFunctionLiteralOnLine(elementAt) ?: return LOW
|
||||||
|
|
||||||
|
val containingCallable = findNonLocalCallableParent(containingFunctionLiteral) ?: return LOW
|
||||||
|
when (containingCallable) {
|
||||||
|
is KtFunction -> if (containingCallable.name == methodName) overallRanking += MAJOR
|
||||||
|
is KtProperty -> if (containingCallable.name == methodName) overallRanking += MAJOR
|
||||||
|
is KtPropertyAccessor -> if (containingCallable.property.name == methodName) overallRanking += MAJOR
|
||||||
|
}
|
||||||
|
|
||||||
|
val containingClass = containingCallable.getParentOfType<KtClassOrObject>(false)
|
||||||
|
if (containingClass != null) {
|
||||||
|
overallRanking += rankingForClass(containingClass, className, location.virtualMachine())
|
||||||
|
}
|
||||||
|
|
||||||
|
return overallRanking
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO support <clinit>
|
||||||
|
if (method.name() == "<init>") {
|
||||||
|
val containingClass = elementAt.getParentOfType<KtClassOrObject>(false) ?: return LOW
|
||||||
|
val constructorOrInitializer =
|
||||||
|
elementAt.getParentOfTypes2<KtConstructor<*>, KtClassInitializer>()?.takeIf { containingClass.isAncestor(it) }
|
||||||
|
?: containingClass.primaryConstructor?.takeIf { it.getLine() == containingClass.getLine() }
|
||||||
|
|
||||||
|
if (constructorOrInitializer == null
|
||||||
|
&& locationLineNumber < containingClass.getLine()
|
||||||
|
&& locationLineNumber > containingClass.lastChild.getLine()
|
||||||
|
) {
|
||||||
|
return LOW
|
||||||
|
}
|
||||||
|
|
||||||
|
overallRanking += rankingForClass(containingClass, location.declaringType())
|
||||||
|
|
||||||
|
if (constructorOrInitializer is KtConstructor<*>)
|
||||||
|
overallRanking += rankingForMethod(constructorOrInitializer, method)
|
||||||
|
} else {
|
||||||
|
val callable = findNonLocalCallableParent(elementAt) ?: return LOW
|
||||||
|
overallRanking += when (callable) {
|
||||||
|
is KtFunction -> rankingForMethod(callable, method)
|
||||||
|
is KtPropertyAccessor -> rankingForAccessor(callable, method)
|
||||||
|
is KtProperty -> rankingForProperty(callable, method)
|
||||||
|
else -> return LOW
|
||||||
|
}
|
||||||
|
|
||||||
|
val containingClass = elementAt.getParentOfType<KtClassOrObject>(false)
|
||||||
|
if (containingClass != null)
|
||||||
|
overallRanking += rankingForClass(containingClass, location.declaringType())
|
||||||
|
}
|
||||||
|
|
||||||
|
return overallRanking
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findFunctionLiteralOnLine(element: PsiElement): KtFunctionLiteral? {
|
||||||
|
val literal = element.getParentOfType<KtFunctionLiteral>(false)
|
||||||
|
if (literal != null) {
|
||||||
|
return literal
|
||||||
|
}
|
||||||
|
|
||||||
|
val callExpression = element.getParentOfType<KtCallExpression>(false) ?: return null
|
||||||
|
|
||||||
|
for (lambdaArgument in callExpression.lambdaArguments) {
|
||||||
|
if (element.getLine() == lambdaArgument.getLine()) {
|
||||||
|
val functionLiteral = lambdaArgument.getLambdaExpression()?.functionLiteral
|
||||||
|
if (functionLiteral != null) {
|
||||||
|
return functionLiteral
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private tailrec fun findNonLocalCallableParent(element: PsiElement): PsiElement? {
|
||||||
|
fun PsiElement.isCallableDeclaration() = this is KtProperty || this is KtFunction || this is KtAnonymousInitializer
|
||||||
|
|
||||||
|
// org.jetbrains.kotlin.psi.KtPsiUtil.isLocal
|
||||||
|
fun PsiElement.isLocalDeclaration(): Boolean {
|
||||||
|
val containingDeclaration = getParentOfType<KtDeclaration>(true)
|
||||||
|
return containingDeclaration is KtCallableDeclaration || containingDeclaration is KtPropertyAccessor
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.isCallableDeclaration() && !element.isLocalDeclaration()) {
|
||||||
|
return element
|
||||||
|
}
|
||||||
|
|
||||||
|
val containingCallable = element.getParentOfTypes3<KtProperty, KtFunction, KtAnonymousInitializer>()
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
if (containingCallable.isLocalDeclaration()) {
|
||||||
|
return findNonLocalCallableParent(containingCallable)
|
||||||
|
}
|
||||||
|
|
||||||
|
return containingCallable
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Method.getContainingClassAndMethodNameForLambda(): Pair<String, String>? {
|
||||||
|
// TODO this breaks nested classes
|
||||||
|
val declaringClass = declaringType() as ClassType
|
||||||
|
val (className, methodName) = declaringClass.name().split('$', limit = 3)
|
||||||
|
.takeIf { it.size == 3 }
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
return Pair(className, methodName)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Method.isLambda(): Boolean {
|
||||||
|
val declaringClass = declaringType() as? ClassType ?: return false
|
||||||
|
|
||||||
|
tailrec fun ClassType.isLambdaClass(): Boolean {
|
||||||
|
if (interfaces().any { it.name() == FunctionBase::class.java.name }) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
val superClass = superclass() ?: return false
|
||||||
|
return superClass.isLambdaClass()
|
||||||
|
}
|
||||||
|
|
||||||
|
return declaringClass.superclass().isLambdaClass()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeTypeMapper(bindingContext: BindingContext): KotlinTypeMapper {
|
||||||
|
return KotlinTypeMapper(bindingContext, ClassBuilderMode.LIGHT_CLASSES, IncompatibleClassTracker.DoNothing, "debugger", false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.simpleName() = substringAfterLast('.').substringAfterLast('$')
|
||||||
|
|
||||||
|
private fun PsiElement.getLine(): Int {
|
||||||
|
return DiagnosticUtils.getLineAndColumnInPsiFile(containingFile, textRange).line
|
||||||
|
}
|
||||||
@@ -121,7 +121,9 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
|||||||
val javaClassName = JvmClassName.byInternalName(defaultInternalName(location))
|
val javaClassName = JvmClassName.byInternalName(defaultInternalName(location))
|
||||||
val project = myDebugProcess.project
|
val project = myDebugProcess.project
|
||||||
|
|
||||||
val defaultPsiFile = DebuggerUtils.findSourceFileForClass(project, sourceSearchScopes, javaClassName, javaSourceFileName)
|
val defaultPsiFile = DebuggerUtils.findSourceFileForClass(
|
||||||
|
project, sourceSearchScopes, javaClassName, javaSourceFileName, location)
|
||||||
|
|
||||||
if (defaultPsiFile != null) {
|
if (defaultPsiFile != null) {
|
||||||
return SourcePosition.createFromLine(defaultPsiFile, 0)
|
return SourcePosition.createFromLine(defaultPsiFile, 0)
|
||||||
}
|
}
|
||||||
@@ -273,7 +275,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
|||||||
|
|
||||||
val project = myDebugProcess.project
|
val project = myDebugProcess.project
|
||||||
|
|
||||||
return DebuggerUtils.findSourceFileForClass(project, sourceSearchScopes, className, sourceName)
|
return DebuggerUtils.findSourceFileForClass(project, sourceSearchScopes, className, sourceName, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun defaultInternalName(location: Location): String {
|
private fun defaultInternalName(location: Location): String {
|
||||||
|
|||||||
@@ -46,11 +46,27 @@ fun Method.safeLocationsOfLine(line: Int): List<Location> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Method.safeVariables(): List<LocalVariable> {
|
fun Method.safeVariables(): List<LocalVariable>? {
|
||||||
return try {
|
return try {
|
||||||
variables()
|
variables()
|
||||||
} catch (e: AbsentInformationException) {
|
} catch (e: AbsentInformationException) {
|
||||||
emptyList()
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Method.safeArguments(): List<LocalVariable>? {
|
||||||
|
return try {
|
||||||
|
arguments()
|
||||||
|
} catch (e: AbsentInformationException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Method.safeReturnType(): Type? {
|
||||||
|
return try {
|
||||||
|
returnType()
|
||||||
|
} catch (e: ClassNotLoadedException) {
|
||||||
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +172,7 @@ private class MockStackFrame(private val location: Location, private val vm: Vir
|
|||||||
|
|
||||||
private fun createVisibleVariables() {
|
private fun createVisibleVariables() {
|
||||||
if (visibleVariables == null) {
|
if (visibleVariables == null) {
|
||||||
val allVariables = location.method().safeVariables()
|
val allVariables = location.method().safeVariables() ?: emptyList()
|
||||||
val map = HashMap<String, LocalVariable>(allVariables.size)
|
val map = HashMap<String, LocalVariable>(allVariables.size)
|
||||||
|
|
||||||
for (allVariable in allVariables) {
|
for (allVariable in allVariables) {
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ class FrameVisitor(val context: EvaluationContextImpl) {
|
|||||||
val declaringTypeName = location?.declaringType()?.name()?.replace('.', '/')?.let { JvmClassName.byInternalName(it) }
|
val declaringTypeName = location?.declaringType()?.name()?.replace('.', '/')?.let { JvmClassName.byInternalName(it) }
|
||||||
|
|
||||||
val sourceFile = if (sourceName != null && declaringTypeName != null) {
|
val sourceFile = if (sourceName != null && declaringTypeName != null) {
|
||||||
DebuggerUtils.findSourceFileForClassIncludeLibrarySources(context.project, scope, declaringTypeName, sourceName)
|
DebuggerUtils.findSourceFileForClassIncludeLibrarySources(context.project, scope, declaringTypeName, sourceName, location)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ class SelectionAwareScopeHighlighter(val editor: Editor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun PsiFile.getLineStartOffset(line: Int): Int? {
|
fun PsiFile.getLineStartOffset(line: Int): Int? {
|
||||||
val doc = PsiDocumentManager.getInstance(project).getDocument(this)
|
val doc = viewProvider.document ?: PsiDocumentManager.getInstance(project).getDocument(this)
|
||||||
if (doc != null && line >= 0 && line < doc.lineCount) {
|
if (doc != null && line >= 0 && line < doc.lineCount) {
|
||||||
val startOffset = doc.getLineStartOffset(line)
|
val startOffset = doc.getLineStartOffset(line)
|
||||||
val element = findElementAt(startOffset) ?: return startOffset
|
val element = findElementAt(startOffset) ?: return startOffset
|
||||||
@@ -331,11 +331,13 @@ fun PsiFile.getLineStartOffset(line: Int): Int? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun PsiFile.getLineEndOffset(line: Int): Int? {
|
fun PsiFile.getLineEndOffset(line: Int): Int? {
|
||||||
return PsiDocumentManager.getInstance(project).getDocument(this)?.getLineEndOffset(line)
|
val document = viewProvider.document ?: PsiDocumentManager.getInstance(project).getDocument(this)
|
||||||
|
return document?.getLineEndOffset(line)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PsiElement.getLineNumber(start: Boolean = true): Int {
|
fun PsiElement.getLineNumber(start: Boolean = true): Int {
|
||||||
return PsiDocumentManager.getInstance(project).getDocument(this.containingFile)?.getLineNumber(if (start) this.startOffset else this.endOffset) ?: 0
|
val document = containingFile.viewProvider.document ?: PsiDocumentManager.getInstance(project).getDocument(containingFile)
|
||||||
|
return document?.getLineNumber(if (start) this.startOffset else this.endOffset) ?: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PsiElement.getLineCount(): Int {
|
fun PsiElement.getLineCount(): Int {
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ class SelectionAwareScopeHighlighter(val editor: Editor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun PsiFile.getLineStartOffset(line: Int): Int? {
|
fun PsiFile.getLineStartOffset(line: Int): Int? {
|
||||||
val doc = PsiDocumentManager.getInstance(project).getDocument(this)
|
val doc = viewProvider.document ?: PsiDocumentManager.getInstance(project).getDocument(this)
|
||||||
if (doc != null && line >= 0 && line < doc.lineCount) {
|
if (doc != null && line >= 0 && line < doc.lineCount) {
|
||||||
val startOffset = doc.getLineStartOffset(line)
|
val startOffset = doc.getLineStartOffset(line)
|
||||||
val element = findElementAt(startOffset) ?: return startOffset
|
val element = findElementAt(startOffset) ?: return startOffset
|
||||||
@@ -331,11 +331,13 @@ fun PsiFile.getLineStartOffset(line: Int): Int? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun PsiFile.getLineEndOffset(line: Int): Int? {
|
fun PsiFile.getLineEndOffset(line: Int): Int? {
|
||||||
return PsiDocumentManager.getInstance(project).getDocument(this)?.getLineEndOffset(line)
|
val document = viewProvider.document ?: PsiDocumentManager.getInstance(project).getDocument(this)
|
||||||
|
return document?.getLineEndOffset(line)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PsiElement.getLineNumber(start: Boolean = true): Int {
|
fun PsiElement.getLineNumber(start: Boolean = true): Int {
|
||||||
return PsiDocumentManager.getInstance(project).getDocument(this.containingFile)?.getLineNumber(if (start) this.startOffset else this.endOffset) ?: 0
|
val document = containingFile.viewProvider.document ?: PsiDocumentManager.getInstance(project).getDocument(containingFile)
|
||||||
|
return document?.getLineNumber(if (start) this.startOffset else this.endOffset) ?: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PsiElement.getLineCount(): Int {
|
fun PsiElement.getLineCount(): Int {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// DO_NOT_CHECK_CLASS_FQNAME
|
||||||
|
|
||||||
|
//FILE: a/a.kt
|
||||||
|
package a
|
||||||
|
|
||||||
|
class A private constructor() {
|
||||||
|
protected fun a() {
|
||||||
|
val a = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//FILE: b/a.kt
|
||||||
|
|
||||||
|
//significant whitespace
|
||||||
|
package b
|
||||||
|
|
||||||
|
class A public constructor() {
|
||||||
|
private fun a() {
|
||||||
|
val a = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
//FILE: a/a.kt
|
||||||
|
class A {
|
||||||
|
init {
|
||||||
|
val a = 5
|
||||||
|
val b = 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//FILE: b/a.kt
|
||||||
|
class B {
|
||||||
|
init {
|
||||||
|
val x = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
// DO_NOT_CHECK_CLASS_FQNAME
|
||||||
|
|
||||||
|
//FILE: a/a.kt
|
||||||
|
package a
|
||||||
|
|
||||||
|
fun block(l: () -> Unit) {}
|
||||||
|
|
||||||
|
class A {
|
||||||
|
fun a() {
|
||||||
|
block {
|
||||||
|
val a = 5
|
||||||
|
block {
|
||||||
|
val b = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//FILE: b/a.kt
|
||||||
|
package b
|
||||||
|
|
||||||
|
import a.block
|
||||||
|
|
||||||
|
class A {
|
||||||
|
fun b() {
|
||||||
|
val g = 5
|
||||||
|
val x = 1
|
||||||
|
block { val y = 2 }
|
||||||
|
block {
|
||||||
|
val a = 5
|
||||||
|
block { block { val x = 4 }}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//FILE: a/a.kt
|
||||||
|
class A(
|
||||||
|
val firstName: String,
|
||||||
|
val lastName: String,
|
||||||
|
val age: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
//FILE: b/a.kt
|
||||||
|
class B(
|
||||||
|
val firstName: String,
|
||||||
|
val lastName: String,
|
||||||
|
val age: Int
|
||||||
|
)
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
//FILE: a/a.kt
|
||||||
|
class A(
|
||||||
|
val firstName: String,
|
||||||
|
val lastName: String,
|
||||||
|
val age: Int
|
||||||
|
) {
|
||||||
|
val c = 1
|
||||||
|
val d = "A"
|
||||||
|
}
|
||||||
|
|
||||||
|
//FILE: b/a.kt
|
||||||
|
class B(
|
||||||
|
val firstName: String,
|
||||||
|
val lastName: String,
|
||||||
|
val age: Int
|
||||||
|
) {
|
||||||
|
init {
|
||||||
|
val a = 5
|
||||||
|
val b = 6
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
//FILE: a/a.kt
|
||||||
|
package foo
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val a by lazy {
|
||||||
|
val a = 5
|
||||||
|
val b = 2
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//FILE: b/a.kt
|
||||||
|
package bar
|
||||||
|
|
||||||
|
class B {
|
||||||
|
val a by lazy {
|
||||||
|
val b = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
//FILE: a/a.kt
|
||||||
|
package foo
|
||||||
|
|
||||||
|
class A {
|
||||||
|
fun a() {
|
||||||
|
val a = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//FILE: b/a.kt
|
||||||
|
package bar
|
||||||
|
|
||||||
|
class A {
|
||||||
|
fun a() {
|
||||||
|
val c = 1
|
||||||
|
val d = 3
|
||||||
|
val g = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
//FILE: a/a.kt
|
||||||
|
package bar
|
||||||
|
|
||||||
|
class A {
|
||||||
|
fun a() {
|
||||||
|
val a = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//FILE: b/a.kt
|
||||||
|
package foo
|
||||||
|
|
||||||
|
class A {
|
||||||
|
fun b() {
|
||||||
|
val c = 1
|
||||||
|
val d = 3
|
||||||
|
val g = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
//FILE: a/a.kt
|
||||||
|
class A {
|
||||||
|
fun a() {
|
||||||
|
System.out.println("a")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//FILE: b/a.kt
|
||||||
|
class B {
|
||||||
|
fun b() {
|
||||||
|
System.out.println("b")
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
//FILE: a/a.kt
|
||||||
|
package a
|
||||||
|
|
||||||
|
val a = run {
|
||||||
|
val a = 5
|
||||||
|
val b = run {
|
||||||
|
val c = 2
|
||||||
|
}
|
||||||
|
5
|
||||||
|
}
|
||||||
|
|
||||||
|
fun x() {
|
||||||
|
println("")
|
||||||
|
}
|
||||||
|
|
||||||
|
//FILE: b/a.kt
|
||||||
|
package b
|
||||||
|
|
||||||
|
val b = 5
|
||||||
|
|
||||||
|
fun y() {
|
||||||
|
println("")
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package org.jetbrains.kotlin.idea.debugger
|
||||||
|
|
||||||
|
import com.sun.jdi.ThreadReference
|
||||||
|
import org.jetbrains.kotlin.codegen.OriginCollectingClassBuilderFactory
|
||||||
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.ClassNode
|
||||||
|
|
||||||
|
abstract class AbstractFileRankingTest : LowLevelDebuggerTestBase() {
|
||||||
|
override fun doTest(
|
||||||
|
options: Set<String>,
|
||||||
|
mainThread: ThreadReference,
|
||||||
|
factory: OriginCollectingClassBuilderFactory,
|
||||||
|
state: GenerationState
|
||||||
|
) {
|
||||||
|
val allKtFiles = factory.origins.mapNotNull { it.value.element?.containingFile as? KtFile }.distinct()
|
||||||
|
fun getKtFiles(name: String) = allKtFiles.filter { it.name == name }
|
||||||
|
|
||||||
|
val doNotCheckClassFqName = "DO_NOT_CHECK_CLASS_FQNAME" in options
|
||||||
|
|
||||||
|
val calculator = object : FileRankingCalculator(checkClassFqName = !doNotCheckClassFqName, strictMode = true) {
|
||||||
|
override fun analyze(element: KtElement) = state.bindingContext
|
||||||
|
}
|
||||||
|
|
||||||
|
val problems = mutableListOf<String>()
|
||||||
|
|
||||||
|
for ((node, origin) in factory.origins) {
|
||||||
|
val classNode = node as? ClassNode ?: continue
|
||||||
|
val expectedFile = origin.element?.containingFile as? KtFile ?: continue
|
||||||
|
val className = classNode.name.replace('/', '.')
|
||||||
|
val jdiClass = mainThread.virtualMachine().classesByName(className).singleOrNull()
|
||||||
|
?: error("Class '$className' was not found in the debuggee process class loader")
|
||||||
|
|
||||||
|
val locations = jdiClass.allLineLocations()
|
||||||
|
assert(locations.isNotEmpty()) { "There are no locations for class $className" }
|
||||||
|
|
||||||
|
val allFilesWithSameName = getKtFiles(expectedFile.name)
|
||||||
|
|
||||||
|
for (location in locations) {
|
||||||
|
val actualFile = calculator.findMostAppropriateSource(allFilesWithSameName, location)
|
||||||
|
if (actualFile != expectedFile) {
|
||||||
|
problems += "Location ${location.sourceName()}:${location.lineNumber() - 1} is associated with a wrong KtFile:\n" +
|
||||||
|
" - expected: ${expectedFile.virtualFilePath}\n" +
|
||||||
|
" - actual: ${actualFile.virtualFilePath}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (problems.isNotEmpty()) {
|
||||||
|
throw AssertionError(buildString {
|
||||||
|
appendln("There were association errors:").appendln()
|
||||||
|
problems.joinTo(this, "\n\n")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* 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.debugger;
|
||||||
|
|
||||||
|
import com.intellij.testFramework.TestDataPath;
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
|
import org.jetbrains.kotlin.test.TargetBackend;
|
||||||
|
import org.jetbrains.kotlin.test.TestMetadata;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
@TestMetadata("idea/testData/debugger/fileRanking")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public class FileRankingTestGenerated extends AbstractFileRankingTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInFileRanking() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/fileRanking"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("differentFlags.kt")
|
||||||
|
public void testDifferentFlags() throws Exception {
|
||||||
|
runTest("idea/testData/debugger/fileRanking/differentFlags.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("init.kt")
|
||||||
|
public void testInit() throws Exception {
|
||||||
|
runTest("idea/testData/debugger/fileRanking/init.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdas.kt")
|
||||||
|
public void testLambdas() throws Exception {
|
||||||
|
runTest("idea/testData/debugger/fileRanking/lambdas.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("multilinePrimaryConstructor.kt")
|
||||||
|
public void testMultilinePrimaryConstructor() throws Exception {
|
||||||
|
runTest("idea/testData/debugger/fileRanking/multilinePrimaryConstructor.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("multilinePrimaryConstructorWithBody.kt")
|
||||||
|
public void testMultilinePrimaryConstructorWithBody() throws Exception {
|
||||||
|
runTest("idea/testData/debugger/fileRanking/multilinePrimaryConstructorWithBody.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyDelegates.kt")
|
||||||
|
public void testPropertyDelegates() throws Exception {
|
||||||
|
runTest("idea/testData/debugger/fileRanking/propertyDelegates.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("sameClassName.kt")
|
||||||
|
public void testSameClassName() throws Exception {
|
||||||
|
runTest("idea/testData/debugger/fileRanking/sameClassName.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("sameClassNameDifferentMethodNames.kt")
|
||||||
|
public void testSameClassNameDifferentMethodNames() throws Exception {
|
||||||
|
runTest("idea/testData/debugger/fileRanking/sameClassNameDifferentMethodNames.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simple.kt")
|
||||||
|
public void testSimple() throws Exception {
|
||||||
|
runTest("idea/testData/debugger/fileRanking/simple.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("topLevel.kt")
|
||||||
|
public void testTopLevel() throws Exception {
|
||||||
|
runTest("idea/testData/debugger/fileRanking/topLevel.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* 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.debugger
|
||||||
|
|
||||||
|
import com.intellij.util.PathUtil
|
||||||
|
import com.intellij.util.SystemProperties
|
||||||
|
import com.sun.jdi.ThreadReference
|
||||||
|
import com.sun.jdi.VirtualMachine
|
||||||
|
import com.sun.tools.jdi.SocketAttachingConnector
|
||||||
|
import org.jetbrains.kotlin.backend.common.output.OutputFile
|
||||||
|
import org.jetbrains.kotlin.codegen.*
|
||||||
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
|
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.ClassNode
|
||||||
|
import org.jetbrains.org.objectweb.asm.Type as AsmType
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.Socket
|
||||||
|
import java.nio.file.Files
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
|
abstract class LowLevelDebuggerTestBase : CodegenTestCase() {
|
||||||
|
private companion object {
|
||||||
|
private const val DEBUG_ADDRESS = "127.0.0.1"
|
||||||
|
private const val DEBUG_PORT = 5115
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>, javaFilesDir: File?) {
|
||||||
|
val javaSources = javaFilesDir?.let { arrayOf(it) } ?: emptyArray()
|
||||||
|
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, *javaSources)
|
||||||
|
|
||||||
|
val options = wholeFile.readLines()
|
||||||
|
.filter { it.matches("^// ?[\\w_]+$".toRegex()) }
|
||||||
|
.mapTo(mutableSetOf()) { it.drop(2).trim() }
|
||||||
|
|
||||||
|
loadMultiFiles(files)
|
||||||
|
val classBuilderFactory = OriginCollectingClassBuilderFactory(ClassBuilderMode.FULL)
|
||||||
|
val generationState = GenerationUtils.compileFiles(myFiles.psiFiles, myEnvironment, classBuilderFactory)
|
||||||
|
classFileFactory = generationState.factory
|
||||||
|
|
||||||
|
val tempDirForTest = Files.createTempDirectory("debuggerTest").toFile()
|
||||||
|
|
||||||
|
try {
|
||||||
|
val classesDir = File(tempDirForTest, "classes").apply {
|
||||||
|
writeMainClass(this)
|
||||||
|
for (classFile in classFileFactory.getClassFiles()) {
|
||||||
|
File(this, classFile.relativePath).mkdirAndWriteBytes(classFile.asByteArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val process = startDebuggeeProcess(classesDir)
|
||||||
|
waitUntil { isPortOpen() }
|
||||||
|
|
||||||
|
val virtualMachine = attachDebugger()
|
||||||
|
|
||||||
|
try {
|
||||||
|
val mainThread = virtualMachine.allThreads().single { it.name() == "main" }
|
||||||
|
waitUntil { areCompiledClassesLoaded(mainThread, classBuilderFactory) }
|
||||||
|
doTest(options, mainThread, classBuilderFactory, generationState)
|
||||||
|
} finally {
|
||||||
|
virtualMachine.exit(0)
|
||||||
|
process.destroy()
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
tempDirForTest.deleteRecursively()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun doTest(
|
||||||
|
options: Set<String>,
|
||||||
|
mainThread: ThreadReference,
|
||||||
|
factory: OriginCollectingClassBuilderFactory,
|
||||||
|
state: GenerationState
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun isPortOpen(): Boolean {
|
||||||
|
return try {
|
||||||
|
Socket(DEBUG_ADDRESS, DEBUG_PORT).close()
|
||||||
|
true
|
||||||
|
} catch (e: IOException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun areCompiledClassesLoaded(mainThread: ThreadReference, factory: OriginCollectingClassBuilderFactory): Boolean {
|
||||||
|
for ((node, _) in factory.origins) {
|
||||||
|
val classNode = node as? ClassNode ?: continue
|
||||||
|
mainThread.virtualMachine().classesByName(classNode.name.replace('/', '.')).firstOrNull() ?: return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startDebuggeeProcess(classesDir: File): Process {
|
||||||
|
val classesToLoad = this.classFileFactory.getClassFiles().joinToString(",") { it.qualifiedName }
|
||||||
|
|
||||||
|
val classpath = listOf(
|
||||||
|
classesDir.absolutePath,
|
||||||
|
PathUtil.getJarPathForClass(Delegates::class.java) // Add Kotlin runtime JAR
|
||||||
|
)
|
||||||
|
|
||||||
|
val command = arrayOf(
|
||||||
|
findJavaExecutable().absolutePath,
|
||||||
|
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$DEBUG_PORT",
|
||||||
|
"-ea",
|
||||||
|
"-classpath", classpath.joinToString(File.pathSeparator),
|
||||||
|
"-D${DebuggerMain.CLASSES_TO_LOAD}=$classesToLoad",
|
||||||
|
DebuggerMain::class.java.name
|
||||||
|
)
|
||||||
|
|
||||||
|
return ProcessBuilder(*command).inheritIO().start()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun attachDebugger(): VirtualMachine {
|
||||||
|
val connector = SocketAttachingConnector()
|
||||||
|
return connector.attach(connector.defaultArguments().toMutableMap().apply {
|
||||||
|
getValue("port").setValue("$DEBUG_PORT")
|
||||||
|
getValue("hostname").setValue(DEBUG_ADDRESS)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findJavaExecutable(): File {
|
||||||
|
val javaBin = File(SystemProperties.getJavaHome(), "bin")
|
||||||
|
return File(javaBin, "java.exe").takeIf { it.exists() }
|
||||||
|
?: File(javaBin, "java").also { assert(it.exists()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeMainClass(classesDir: File) {
|
||||||
|
val mainClassResourceName = DebuggerMain::class.java.name.replace('.', '/') + ".class"
|
||||||
|
val mainClassBytes = javaClass.classLoader.getResource(mainClassResourceName).readBytes()
|
||||||
|
File(classesDir, mainClassResourceName).mkdirAndWriteBytes(mainClassBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val OutputFile.internalName
|
||||||
|
get() = relativePath.substringBeforeLast(".class")
|
||||||
|
|
||||||
|
private val OutputFile.qualifiedName
|
||||||
|
get() = internalName.replace('/', '.')
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun File.mkdirAndWriteBytes(array: ByteArray) {
|
||||||
|
parentFile.mkdirs()
|
||||||
|
writeBytes(array)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun waitUntil(condition: () -> Boolean) {
|
||||||
|
while (!condition()) {
|
||||||
|
Thread.sleep(30)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object DebuggerMain {
|
||||||
|
const val CLASSES_TO_LOAD = "classes.to.load"
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
val lock = Any()
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
System.getProperty(CLASSES_TO_LOAD).split(',').forEach { Class.forName(it) }
|
||||||
|
synchronized(lock) {
|
||||||
|
// Wait until debugger is attached
|
||||||
|
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||||
|
(lock as java.lang.Object).wait()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,8 +28,10 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.OUTPUT
|
|||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
|
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
|
||||||
import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAll
|
import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAll
|
||||||
|
import org.jetbrains.kotlin.codegen.ClassBuilderMode
|
||||||
import org.jetbrains.kotlin.codegen.CompilationErrorHandler
|
import org.jetbrains.kotlin.codegen.CompilationErrorHandler
|
||||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
||||||
|
import org.jetbrains.kotlin.codegen.OriginCollectingClassBuilderFactory
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
@@ -219,7 +221,7 @@ abstract class AbstractKapt3Extension(
|
|||||||
bindingContext: BindingContext,
|
bindingContext: BindingContext,
|
||||||
files: List<KtFile>
|
files: List<KtFile>
|
||||||
): KaptContextForStubGeneration {
|
): KaptContextForStubGeneration {
|
||||||
val builderFactory = Kapt3BuilderFactory()
|
val builderFactory = OriginCollectingClassBuilderFactory(ClassBuilderMode.KAPT3)
|
||||||
|
|
||||||
val targetId = TargetId(
|
val targetId = TargetId(
|
||||||
name = compilerConfiguration[CommonConfigurationKeys.MODULE_NAME] ?: module.name.asString(),
|
name = compilerConfiguration[CommonConfigurationKeys.MODULE_NAME] ?: module.name.asString(),
|
||||||
|
|||||||
+4
-1
@@ -19,8 +19,10 @@ package org.jetbrains.kotlin.kapt3.test
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
|
import org.jetbrains.kotlin.codegen.ClassBuilderMode
|
||||||
import org.jetbrains.kotlin.codegen.CodegenTestCase
|
import org.jetbrains.kotlin.codegen.CodegenTestCase
|
||||||
import org.jetbrains.kotlin.codegen.GenerationUtils
|
import org.jetbrains.kotlin.codegen.GenerationUtils
|
||||||
|
import org.jetbrains.kotlin.codegen.OriginCollectingClassBuilderFactory
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.kapt3.*
|
import org.jetbrains.kotlin.kapt3.*
|
||||||
import org.jetbrains.kotlin.kapt3.AptMode.STUBS_AND_APT
|
import org.jetbrains.kotlin.kapt3.AptMode.STUBS_AND_APT
|
||||||
@@ -140,7 +142,8 @@ abstract class AbstractKotlinKapt3IntegrationTest : CodegenTestCase() {
|
|||||||
try {
|
try {
|
||||||
loadMultiFiles(files)
|
loadMultiFiles(files)
|
||||||
|
|
||||||
GenerationUtils.compileFiles(myFiles.psiFiles, myEnvironment, Kapt3BuilderFactory()).factory
|
val classBuilderFactory = OriginCollectingClassBuilderFactory(ClassBuilderMode.KAPT3)
|
||||||
|
GenerationUtils.compileFiles(myFiles.psiFiles, myEnvironment, classBuilderFactory).factory
|
||||||
|
|
||||||
val actualRaw = kapt3Extension.savedStubs ?: error("Stubs were not saved")
|
val actualRaw = kapt3Extension.savedStubs ?: error("Stubs were not saved")
|
||||||
val actual = StringUtil.convertLineSeparators(actualRaw.trim({ it <= ' ' }))
|
val actual = StringUtil.convertLineSeparators(actualRaw.trim({ it <= ' ' }))
|
||||||
|
|||||||
+2
-4
@@ -29,9 +29,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
|
|||||||
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
|
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||||
import org.jetbrains.kotlin.codegen.CodegenTestCase
|
import org.jetbrains.kotlin.codegen.*
|
||||||
import org.jetbrains.kotlin.codegen.CodegenTestFiles
|
|
||||||
import org.jetbrains.kotlin.codegen.GenerationUtils
|
|
||||||
import org.jetbrains.kotlin.kapt3.*
|
import org.jetbrains.kotlin.kapt3.*
|
||||||
import org.jetbrains.kotlin.kapt3.base.KaptContext
|
import org.jetbrains.kotlin.kapt3.base.KaptContext
|
||||||
import org.jetbrains.kotlin.kapt3.base.KaptPaths
|
import org.jetbrains.kotlin.kapt3.base.KaptPaths
|
||||||
@@ -112,7 +110,7 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
|
|||||||
loadMultiFiles(files)
|
loadMultiFiles(files)
|
||||||
|
|
||||||
val txtFile = File(wholeFile.parentFile, wholeFile.nameWithoutExtension + ".txt")
|
val txtFile = File(wholeFile.parentFile, wholeFile.nameWithoutExtension + ".txt")
|
||||||
val classBuilderFactory = Kapt3BuilderFactory()
|
val classBuilderFactory = OriginCollectingClassBuilderFactory(ClassBuilderMode.KAPT3)
|
||||||
val generationState = GenerationUtils.compileFiles(myFiles.psiFiles, myEnvironment, classBuilderFactory)
|
val generationState = GenerationUtils.compileFiles(myFiles.psiFiles, myEnvironment, classBuilderFactory)
|
||||||
|
|
||||||
val logger = MessageCollectorBackedKaptLogger(isVerbose = true, messageCollector = messageCollector)
|
val logger = MessageCollectorBackedKaptLogger(isVerbose = true, messageCollector = messageCollector)
|
||||||
|
|||||||
Reference in New Issue
Block a user