Debugger: Fix AbstractPositionManagerTest
MockReferenceType can't be used anymore because the new DebuggerClassNameProvider requires much more from it. The new SmartMockReferenceType implements methods such as nestedTypes() or allLineLocations() properly. Also, as PositionManager can return more than one class, we should check if any of the classes it returned matches the pattern.
This commit is contained in:
@@ -56,11 +56,17 @@ class KotlinExtraSteppingFilter : ExtraSteppingFilter {
|
|||||||
|
|
||||||
val settings = DebuggerSettings.getInstance()
|
val settings = DebuggerSettings.getInstance()
|
||||||
if (settings.TRACING_FILTERS_ENABLED) {
|
if (settings.TRACING_FILTERS_ENABLED) {
|
||||||
val className = positionManager.originalClassNameForPosition(sourcePosition)?.replace('/', '.') ?: return false
|
val classNames = positionManager.originalClassNameForPosition(sourcePosition).map { it.replace('/', '.') }
|
||||||
for (filter in settings.steppingFilters) {
|
if (classNames.isEmpty()) {
|
||||||
if (filter.isEnabled) {
|
return false
|
||||||
if (filter.matches(className)) {
|
}
|
||||||
return true
|
|
||||||
|
for (className in classNames) {
|
||||||
|
for (filter in settings.steppingFilters) {
|
||||||
|
if (filter.isEnabled) {
|
||||||
|
if (filter.matches(className)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2016 JetBrains s.r.o.
|
* Copyright 2010-2015 KtBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -18,415 +18,272 @@ package org.jetbrains.kotlin.idea.debugger
|
|||||||
|
|
||||||
import com.intellij.debugger.SourcePosition
|
import com.intellij.debugger.SourcePosition
|
||||||
import com.intellij.debugger.engine.DebugProcess
|
import com.intellij.debugger.engine.DebugProcess
|
||||||
import com.intellij.openapi.application.ModalityState
|
import com.intellij.debugger.engine.DebuggerUtils
|
||||||
import com.intellij.openapi.application.ex.ApplicationManagerEx
|
|
||||||
import com.intellij.openapi.progress.ProgressManager
|
|
||||||
import com.intellij.openapi.project.DumbService
|
|
||||||
import com.intellij.openapi.roots.libraries.LibraryUtil
|
|
||||||
import com.intellij.openapi.ui.MessageType
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import com.intellij.xdebugger.impl.XDebugSessionImpl
|
import com.sun.jdi.AbsentInformationException
|
||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
import com.sun.jdi.ReferenceType
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegen
|
import org.jetbrains.kotlin.codegen.binding.CodegenBinding.*
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.DO_RESUME_METHOD_NAME
|
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.containsNonTailSuspensionCalls
|
import org.jetbrains.kotlin.codegen.coroutines.containsNonTailSuspensionCalls
|
||||||
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
|
||||||
import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider
|
import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider
|
||||||
import org.jetbrains.kotlin.fileClasses.getFileClassInternalName
|
import org.jetbrains.kotlin.fileClasses.getFileClassInternalName
|
||||||
import org.jetbrains.kotlin.idea.debugger.breakpoints.getLambdasAtLineIfAny
|
import org.jetbrains.kotlin.idea.debugger.breakpoints.getLambdasAtLineIfAny
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches
|
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches.ComputedClassNames
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches.ComputedClassNames.Companion.EMPTY
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches.Companion.getOrComputeClassNames
|
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches.Companion.getOrComputeClassNames
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches.ComputedClassNames.CachedClassNames
|
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches.ComputedClassNames.Companion.Cached
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches.ComputedClassNames.NonCachedClassNames
|
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches.ComputedClassNames.Companion.NonCached
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.isImportUsage
|
|
||||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
import java.util.*
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
|
||||||
|
|
||||||
|
class DebuggerClassNameProvider(
|
||||||
|
private val debugProcess: DebugProcess,
|
||||||
|
scopes: List<GlobalSearchScope>,
|
||||||
|
val findInlineUseSites: Boolean = true,
|
||||||
|
val alwaysReturnLambdaParentClass: Boolean = true
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
internal val CLASS_ELEMENT_TYPES = arrayOf<Class<out PsiElement>>(
|
||||||
|
KtFile::class.java,
|
||||||
|
KtClassOrObject::class.java,
|
||||||
|
KtProperty::class.java,
|
||||||
|
KtNamedFunction::class.java,
|
||||||
|
KtFunctionLiteral::class.java,
|
||||||
|
KtAnonymousInitializer::class.java)
|
||||||
|
|
||||||
class DebuggerClassNameProvider(val myDebugProcess: DebugProcess, val scopes: List<GlobalSearchScope>) {
|
internal fun getRelevantElement(element: PsiElement): PsiElement? {
|
||||||
fun classNamesForPosition(sourcePosition: SourcePosition, withInlines: Boolean): List<String> {
|
for (elementType in CLASS_ELEMENT_TYPES) {
|
||||||
val element = sourcePosition.readAction { it.elementAt } ?: return emptyList()
|
if (elementType.isInstance(element)) {
|
||||||
val names = classNamesForPosition(element, withInlines)
|
return element
|
||||||
|
|
||||||
val lambdas = findLambdas(sourcePosition)
|
|
||||||
if (lambdas.isEmpty()) {
|
|
||||||
return names
|
|
||||||
}
|
|
||||||
|
|
||||||
return names + lambdas
|
|
||||||
}
|
|
||||||
|
|
||||||
fun classNamesForPosition(element: PsiElement?, withInlines: Boolean): List<String> {
|
|
||||||
if (DumbService.getInstance(myDebugProcess.project).isDumb) {
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val baseElement = getElementToCalculateClassName(element) ?: return emptyList()
|
|
||||||
return getOrComputeClassNames(baseElement) { element ->
|
|
||||||
val file = element.readAction { it.containingFile as KtFile }
|
|
||||||
val isInLibrary = LibraryUtil.findLibraryEntry(file.virtualFile, file.project) != null
|
|
||||||
val typeMapper = KotlinDebuggerCaches.getOrCreateTypeMapper(element)
|
|
||||||
|
|
||||||
getInternalClassNameForElement(element, typeMapper, file, isInLibrary, withInlines)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findLambdas(sourcePosition: SourcePosition): Collection<String> {
|
|
||||||
val lambdas = sourcePosition.readAction(::getLambdasAtLineIfAny)
|
|
||||||
return lambdas.flatMap { classNamesForPosition(it, true) }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun getInternalClassNameForElement(
|
|
||||||
element: KtElement,
|
|
||||||
typeMapper: KotlinTypeMapper,
|
|
||||||
file: KtFile,
|
|
||||||
isInLibrary: Boolean,
|
|
||||||
withInlines: Boolean
|
|
||||||
): KotlinDebuggerCaches.ComputedClassNames {
|
|
||||||
val elementOfClassName = element.readAction { getElementToCalculateClassName(it.parent) }
|
|
||||||
when (element) {
|
|
||||||
is KtClassOrObject -> return CachedClassNames(getClassNameForClass(element, typeMapper))
|
|
||||||
is KtSecondaryConstructor -> {
|
|
||||||
val klass = element.readAction { it.getContainingClassOrObject() }
|
|
||||||
return CachedClassNames(getClassNameForClass(klass, typeMapper))
|
|
||||||
}
|
|
||||||
is KtFunction -> {
|
|
||||||
val descriptor = element.readAction { InlineUtil.getInlineArgumentDescriptor(it, typeMapper.bindingContext) }
|
|
||||||
if (descriptor != null) {
|
|
||||||
val classNamesForParent = classNamesForPosition(elementOfClassName, withInlines)
|
|
||||||
if (descriptor.isCrossinline) {
|
|
||||||
return CachedClassNames(classNamesForParent + findCrossInlineArguments(element, descriptor, typeMapper.bindingContext))
|
|
||||||
}
|
|
||||||
return CachedClassNames(classNamesForParent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val crossInlineParameterUsages = element.readAction { it.containsCrossInlineParameterUsages(typeMapper.bindingContext) }
|
|
||||||
if (crossInlineParameterUsages.isNotEmpty()) {
|
|
||||||
return CachedClassNames(classNamesForCrossInlineParameters(crossInlineParameterUsages, typeMapper.bindingContext).toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
when {
|
|
||||||
element is KtFunctionLiteral -> {
|
|
||||||
val asmType = CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, element)
|
|
||||||
val className = asmType.internalName
|
|
||||||
|
|
||||||
val inlineCallClassPatterns = inlineCallClassPatterns(typeMapper, element)
|
|
||||||
val names = listOf(className) + inlineCallClassPatterns
|
|
||||||
|
|
||||||
return CachedClassNames(names)
|
|
||||||
}
|
|
||||||
element is KtAnonymousInitializer -> {
|
|
||||||
// Class-object initializer
|
|
||||||
if (elementOfClassName is KtObjectDeclaration && runReadAction { elementOfClassName.isCompanion() }) {
|
|
||||||
return CachedClassNames(classNamesForPosition(elementOfClassName.parent, withInlines))
|
|
||||||
}
|
|
||||||
return CachedClassNames(classNamesForPosition(elementOfClassName, withInlines))
|
|
||||||
}
|
|
||||||
element is KtPropertyAccessor && (!element.readAction { it.property.isTopLevel } || !isInLibrary) -> {
|
|
||||||
val classOrObject = element.readAction { PsiTreeUtil.getParentOfType(it, KtClassOrObject::class.java) }
|
|
||||||
if (classOrObject != null) {
|
|
||||||
return CachedClassNames(getClassNameForClass(classOrObject, typeMapper))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
element is KtProperty && (!element.readAction { it.isTopLevel } || !isInLibrary) -> {
|
|
||||||
val descriptor = typeMapper.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element) as? PropertyDescriptor ?:
|
|
||||||
return CachedClassNames(classNamesForPosition(elementOfClassName, withInlines))
|
|
||||||
|
|
||||||
return CachedClassNames(getJvmInternalNameForPropertyOwner(typeMapper, descriptor))
|
|
||||||
}
|
|
||||||
element is KtNamedFunction -> {
|
|
||||||
val descriptor = typeMapper.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element)
|
|
||||||
|
|
||||||
val parentInternalName = when {
|
|
||||||
isFunctionWithSuspendStateMachine(descriptor, typeMapper.bindingContext) -> {
|
|
||||||
CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, element).internalName
|
|
||||||
}
|
|
||||||
elementOfClassName is KtClassOrObject -> getClassNameForClass(elementOfClassName, typeMapper)
|
|
||||||
elementOfClassName != null -> {
|
|
||||||
val asmType = CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, element)
|
|
||||||
asmType.internalName
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
getClassNameForFile(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!withInlines) return NonCachedClassNames(parentInternalName)
|
|
||||||
val inlinedCalls = findInlinedCalls(element, typeMapper.bindingContext)
|
|
||||||
if (parentInternalName == null) return CachedClassNames(inlinedCalls)
|
|
||||||
|
|
||||||
val inlineCallPatterns = inlineCallClassPatterns(typeMapper, element)
|
|
||||||
|
|
||||||
return CachedClassNames((listOf(parentInternalName) + inlinedCalls + inlineCallPatterns).filterNotNull())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CachedClassNames(getClassNameForFile(file))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isFunctionWithSuspendStateMachine(descriptor: DeclarationDescriptor?, bindingContext: BindingContext): Boolean {
|
|
||||||
return descriptor is SimpleFunctionDescriptor && descriptor.isSuspend && descriptor.containsNonTailSuspensionCalls(bindingContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun inlineCallClassPatterns(typeMapper: KotlinTypeMapper, element: KtElement): List<String> {
|
|
||||||
val context = typeMapper.bindingContext
|
|
||||||
|
|
||||||
val inlineCall = runReadAction {
|
|
||||||
val parentCallsWithLambdas = element.parents.map {
|
|
||||||
val ktCallExpression: KtCallExpression =
|
|
||||||
when (it) {
|
|
||||||
is KtFunctionLiteral -> {
|
|
||||||
val lambdaExpression = it.parent as? KtLambdaExpression
|
|
||||||
// call(param, { <it> })
|
|
||||||
lambdaExpression?.typedParent<KtValueArgument>()?.typedParent<KtValueArgumentList>()?.typedParent<KtCallExpression>() ?:
|
|
||||||
|
|
||||||
// call { <it> }
|
|
||||||
lambdaExpression?.typedParent<KtLambdaArgument>()?.typedParent<KtCallExpression>()
|
|
||||||
}
|
|
||||||
|
|
||||||
is KtNamedFunction -> {
|
|
||||||
// call(fun () {})
|
|
||||||
it.typedParent<KtValueArgument>()?.typedParent<KtValueArgumentList>()?.typedParent<KtCallExpression>()
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> null
|
|
||||||
} ?: return@map null
|
|
||||||
|
|
||||||
ktCallExpression to (it as KtElement)
|
|
||||||
}.toList()
|
|
||||||
|
|
||||||
parentCallsWithLambdas.lastOrNull {
|
|
||||||
it != null && isInlineCall(context, it.component1())
|
|
||||||
}?.first
|
|
||||||
} ?: return emptyList()
|
|
||||||
|
|
||||||
val lexicalScope = runReadAction { inlineCall.getResolutionScope(context) } ?: return emptyList()
|
|
||||||
val baseClassName = classNamesForPosition(inlineCall, false).firstOrNull() ?: return emptyList()
|
|
||||||
|
|
||||||
val resolvedCall = runReadAction { inlineCall.getResolvedCall(context) } ?: return emptyList()
|
|
||||||
val inlineFunctionName = resolvedCall.resultingDescriptor.name
|
|
||||||
|
|
||||||
val ownerDescriptor = lexicalScope.ownerDescriptor
|
|
||||||
val ownerDeclaration = if (ownerDescriptor is DeclarationDescriptor) DescriptorToSourceUtils.getSourceFromDescriptor(ownerDescriptor) else null
|
|
||||||
|
|
||||||
val ownerDescriptorName =
|
|
||||||
if (isFunctionWithSuspendStateMachine(ownerDescriptor, typeMapper.bindingContext) ||
|
|
||||||
(ownerDescriptor is CallableDescriptor && ownerDeclaration is KtElement && CoroutineCodegen.shouldCreateByLambda(ownerDescriptor, ownerDeclaration))) {
|
|
||||||
Name.identifier(DO_RESUME_METHOD_NAME)
|
|
||||||
}
|
|
||||||
else if (ownerDescriptor is PropertyDescriptor && lexicalScope.kind == LexicalScopeKind.PROPERTY_INITIALIZER_OR_DELEGATE &&
|
|
||||||
ownerDescriptor.containingDeclaration is ClassDescriptor) {
|
|
||||||
(ownerDescriptor.containingDeclaration as ClassDescriptor).constructors.first().name
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ownerDescriptor.name
|
|
||||||
}
|
|
||||||
|
|
||||||
val ownerJvmName = if (ownerDescriptorName.isSpecial) InlineCodegenUtil.SPECIAL_TRANSFORMATION_NAME else ownerDescriptorName.asString()
|
|
||||||
val mangledInternalClassName =
|
|
||||||
baseClassName + "$" + ownerJvmName + InlineCodegenUtil.INLINE_CALL_TRANSFORMATION_SUFFIX + "$" + inlineFunctionName
|
|
||||||
|
|
||||||
return listOf("$mangledInternalClassName*")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getClassNameForClass(klass: KtClassOrObject, typeMapper: KotlinTypeMapper) = klass.readAction { getJvmInternalNameForImpl(typeMapper, it) }
|
|
||||||
private fun getClassNameForFile(file: KtFile) = file.readAction { NoResolveFileClassesProvider.getFileClassInternalName(it) }
|
|
||||||
|
|
||||||
private val TYPES_TO_CALCULATE_CLASSNAME: Array<Class<out KtElement>> =
|
|
||||||
arrayOf(KtClass::class.java,
|
|
||||||
KtObjectDeclaration::class.java,
|
|
||||||
KtEnumEntry::class.java,
|
|
||||||
KtFunctionLiteral::class.java,
|
|
||||||
KtNamedFunction::class.java,
|
|
||||||
KtPropertyAccessor::class.java,
|
|
||||||
KtProperty::class.java,
|
|
||||||
KtClassInitializer::class.java,
|
|
||||||
KtSecondaryConstructor::class.java)
|
|
||||||
|
|
||||||
private fun getElementToCalculateClassName(notPositionedElement: PsiElement?): KtElement? {
|
|
||||||
if (notPositionedElement?.let { it::class.java } as Class<*> in TYPES_TO_CALCULATE_CLASSNAME) return notPositionedElement as KtElement
|
|
||||||
|
|
||||||
return readAction { PsiTreeUtil.getParentOfType(notPositionedElement, *TYPES_TO_CALCULATE_CLASSNAME) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getJvmInternalNameForPropertyOwner(typeMapper: KotlinTypeMapper, descriptor: PropertyDescriptor): String {
|
|
||||||
return descriptor.readAction {
|
|
||||||
typeMapper.mapOwner(
|
|
||||||
if (JvmAbi.isPropertyWithBackingFieldInOuterClass(it)) it.containingDeclaration else it
|
|
||||||
).internalName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getJvmInternalNameForImpl(typeMapper: KotlinTypeMapper, ktClass: KtClassOrObject): String? {
|
|
||||||
val classDescriptor = typeMapper.bindingContext.get<PsiElement, ClassDescriptor>(BindingContext.CLASS, ktClass) ?: return null
|
|
||||||
|
|
||||||
if (ktClass is KtClass && ktClass.isInterface()) {
|
|
||||||
return typeMapper.mapDefaultImpls(classDescriptor).internalName
|
|
||||||
}
|
|
||||||
|
|
||||||
return typeMapper.mapClass(classDescriptor).internalName
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findInlinedCalls(function: KtNamedFunction, context: BindingContext): List<String> {
|
|
||||||
if (!InlineUtil.isInline(context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, function))) {
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val searchResult = hashSetOf<KtElement>()
|
|
||||||
val functionName = function.readAction { it.name }
|
|
||||||
|
|
||||||
val task = Runnable {
|
|
||||||
ReferencesSearch.search(function, getScopeForInlineFunctionUsages(function)).forEach {
|
|
||||||
if (!it.readAction { it.isImportUsage() }) {
|
|
||||||
val usage = (it.element as? KtElement)?.let { getElementToCalculateClassName(it) }
|
|
||||||
if (usage != null) {
|
|
||||||
searchResult.add(usage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isSuccess = true
|
// Do not copy the array (*elementTypes) if the element is one we look for
|
||||||
val applicationEx = ApplicationManagerEx.getApplicationEx()
|
return runReadAction { PsiTreeUtil.getNonStrictParentOfType(element, *CLASS_ELEMENT_TYPES) }
|
||||||
if (!applicationEx.isUnitTestMode && (!applicationEx.holdsReadLock() || applicationEx.isDispatchThread)) {
|
|
||||||
applicationEx.invokeAndWait(
|
|
||||||
{
|
|
||||||
isSuccess = ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
|
||||||
task,
|
|
||||||
"Compute class names for function $functionName",
|
|
||||||
true,
|
|
||||||
myDebugProcess.project)
|
|
||||||
}, ModalityState.NON_MODAL)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Pooled thread with read lock. Can't invoke task under UI progress, so call it directly.
|
|
||||||
task.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isSuccess) {
|
|
||||||
XDebugSessionImpl.NOTIFICATION_GROUP.createNotification(
|
|
||||||
"Debugger can skip some executions of $functionName method, because the computation of class names was interrupted", MessageType.WARNING
|
|
||||||
).notify(myDebugProcess.project)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO recursive search
|
|
||||||
return searchResult.flatMap { classNamesForPosition(it, true) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findCrossInlineArguments(argument: KtFunction, parameterDescriptor: ValueParameterDescriptor, context: BindingContext): Set<String> {
|
val inlineUsagesSearcher = InlineCallableUsagesSearcher(debugProcess, scopes)
|
||||||
return runReadAction {
|
|
||||||
val source = parameterDescriptor.original.source.getPsi() as? KtParameter
|
/**
|
||||||
val functionName = source?.ownerFunction?.name
|
* Returns classes in which the given line number *is* present.
|
||||||
if (functionName != null) {
|
*/
|
||||||
return@runReadAction setOf(getCrossInlineArgumentClassName(argument, functionName, context))
|
fun getClassesForPosition(position: SourcePosition): List<ReferenceType> = with (debugProcess) {
|
||||||
}
|
val lineNumber = position.line
|
||||||
return@runReadAction emptySet()
|
|
||||||
}
|
return doGetClassesForPosition(position)
|
||||||
|
.flatMap { className -> virtualMachineProxy.classesByName(className) }
|
||||||
|
.flatMap { referenceType -> findTargetClasses(referenceType, lineNumber) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCrossInlineArgumentClassName(argument: KtFunction, inlineFunctionName: String, context: BindingContext): String {
|
/**
|
||||||
val anonymousClassNameForArgument = CodegenBinding.asmTypeForAnonymousClass(context, argument).internalName
|
* Returns classes names in JDI format (my.app.App$Nested) in which the given line number *may be* present.
|
||||||
val newName = anonymousClassNameForArgument.substringIndex() + InlineCodegenUtil.INLINE_TRANSFORMATION_SUFFIX + "$" + inlineFunctionName
|
*/
|
||||||
return "$newName$*"
|
fun getOuterClassNamesForPosition(position: SourcePosition): List<String> {
|
||||||
|
return doGetClassesForPosition(position).toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtElement.containsCrossInlineParameterUsages(context: BindingContext): Collection<ValueParameterDescriptor> {
|
private fun doGetClassesForPosition(position: SourcePosition): Set<String> {
|
||||||
fun KtElement.hasParameterCall(parameter: KtParameter): Boolean {
|
val relevantElement = runReadAction { getRelevantElement(position.elementAt) }
|
||||||
return ReferencesSearch.search(parameter).any {
|
|
||||||
this.textRange.contains(it.element.textRange)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val inlineFunction = this.parents.firstIsInstanceOrNull<KtNamedFunction>() ?: return emptySet()
|
val result = getOrComputeClassNames(relevantElement) { element ->
|
||||||
|
getOuterClassNamesForElement(element)
|
||||||
|
}.toMutableSet()
|
||||||
|
|
||||||
val inlineFunctionDescriptor = context[BindingContext.FUNCTION, inlineFunction]
|
for (lambda in position.readAction(::getLambdasAtLineIfAny)) {
|
||||||
if (inlineFunctionDescriptor == null || !InlineUtil.isInline(inlineFunctionDescriptor)) return emptySet()
|
result += getOrComputeClassNames(lambda) { element ->
|
||||||
|
getOuterClassNamesForElement(element)
|
||||||
return inlineFunctionDescriptor.valueParameters
|
|
||||||
.filter { it.isCrossinline }
|
|
||||||
.mapNotNull {
|
|
||||||
val psiParameter = it.source.getPsi() as? KtParameter
|
|
||||||
if (psiParameter != null && this@containsCrossInlineParameterUsages.hasParameterCall(psiParameter))
|
|
||||||
it
|
|
||||||
else
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun classNamesForCrossInlineParameters(usedParameters: Collection<ValueParameterDescriptor>, context: BindingContext): Set<String> {
|
|
||||||
// We could calculate className only for one of parameters, because we add '*' to match all crossInlined parameter calls
|
|
||||||
val parameter = usedParameters.first()
|
|
||||||
val result = hashSetOf<String>()
|
|
||||||
val inlineFunction = parameter.containingDeclaration.source.getPsi() as? KtNamedFunction ?: return emptySet()
|
|
||||||
|
|
||||||
ReferencesSearch.search(inlineFunction, getScopeForInlineFunctionUsages(inlineFunction)).forEach {
|
|
||||||
runReadAction {
|
|
||||||
if (!it.isImportUsage()) {
|
|
||||||
val call = (it.element as? KtExpression)?.let { KtPsiUtil.getParentCallIfPresent(it) }
|
|
||||||
if (call != null) {
|
|
||||||
val resolvedCall = call.getResolvedCall(context)
|
|
||||||
val argument = resolvedCall?.valueArguments?.entries?.firstOrNull { it.key.original == parameter }?.value
|
|
||||||
if (argument != null) {
|
|
||||||
val argumentExpression = getArgumentExpression(argument.arguments.first())
|
|
||||||
if (argumentExpression is KtFunction) {
|
|
||||||
result.add(getCrossInlineArgumentClassName(argumentExpression, inlineFunction.name!!, context))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getScopeForInlineFunctionUsages(inlineFunction: KtNamedFunction): GlobalSearchScope {
|
@PublishedApi
|
||||||
val virtualFile = runReadAction { inlineFunction.containingFile.virtualFile }
|
@Suppress("NON_TAIL_RECURSIVE_CALL")
|
||||||
if (virtualFile != null && ProjectRootsUtil.isLibraryFile(myDebugProcess.project, virtualFile)) {
|
internal tailrec fun getOuterClassNamesForElement(element: PsiElement?): ComputedClassNames {
|
||||||
return GlobalSearchScope.union(scopes.toTypedArray())
|
if (element == null) return EMPTY
|
||||||
}
|
|
||||||
else {
|
return when (element) {
|
||||||
return myDebugProcess.searchScope
|
is KtFile -> {
|
||||||
|
val fileClassName = runReadAction { NoResolveFileClassesProvider.getFileClassInternalName(element) }.toJdiName()
|
||||||
|
ComputedClassNames.Cached(fileClassName)
|
||||||
|
}
|
||||||
|
is KtClassOrObject -> {
|
||||||
|
val enclosingElementForLocal = runReadAction { KtPsiUtil.getEnclosingElementForLocalDeclaration(element) }
|
||||||
|
if (enclosingElementForLocal != null) { // A local class
|
||||||
|
getOuterClassNamesForElement(enclosingElementForLocal)
|
||||||
|
}
|
||||||
|
else if (runReadAction { element.isObjectLiteral() }) {
|
||||||
|
getOuterClassNamesForElement(element.relevantParentInReadAction)
|
||||||
|
}
|
||||||
|
else { // Guaranteed to be non-local class or object
|
||||||
|
element.readAction {
|
||||||
|
if (it is KtClass && runReadAction { it.isInterface() }) {
|
||||||
|
val name = getNameForNonLocalClass(it)
|
||||||
|
|
||||||
|
if (name != null)
|
||||||
|
Cached(listOf(name, name + JvmAbi.DEFAULT_IMPLS_SUFFIX))
|
||||||
|
else
|
||||||
|
ComputedClassNames.EMPTY
|
||||||
|
} else {
|
||||||
|
getNameForNonLocalClass(it)?.let { ComputedClassNames.Cached(it) } ?: ComputedClassNames.EMPTY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is KtProperty -> {
|
||||||
|
// inline top level property!
|
||||||
|
if (runReadAction { element.isTopLevel }) {
|
||||||
|
return getOuterClassNamesForElement(element.relevantParentInReadAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
val enclosingElementForLocal = runReadAction { KtPsiUtil.getEnclosingElementForLocalDeclaration(element) }
|
||||||
|
if (enclosingElementForLocal != null) {
|
||||||
|
return getOuterClassNamesForElement(enclosingElementForLocal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle non-local property
|
||||||
|
val containingClassOrFile = runReadAction {
|
||||||
|
PsiTreeUtil.getParentOfType(element, KtFile::class.java, KtClassOrObject::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (containingClassOrFile is KtObjectDeclaration && runReadAction { containingClassOrFile.isCompanion() }) {
|
||||||
|
// Properties from the companion object can be placed in the companion object's containing class
|
||||||
|
return (getOuterClassNamesForElement(containingClassOrFile.relevantParentInReadAction) +
|
||||||
|
getOuterClassNamesForElement(containingClassOrFile)).distinct()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (containingClassOrFile != null)
|
||||||
|
getOuterClassNamesForElement(containingClassOrFile)
|
||||||
|
else
|
||||||
|
getOuterClassNamesForElement(element.relevantParentInReadAction)
|
||||||
|
}
|
||||||
|
is KtNamedFunction -> {
|
||||||
|
val typeMapper = KotlinDebuggerCaches.getOrCreateTypeMapper(element)
|
||||||
|
val descriptor = typeMapper.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element)
|
||||||
|
|
||||||
|
val classNamesOfContainingDeclaration = getOuterClassNamesForElement(element.relevantParentInReadAction)
|
||||||
|
|
||||||
|
val nonInlineClasses: ComputedClassNames
|
||||||
|
if (runReadAction { element.name == null || element.isLocal } || isFunctionWithSuspendStateMachine(descriptor, typeMapper.bindingContext)) {
|
||||||
|
nonInlineClasses = classNamesOfContainingDeclaration + ComputedClassNames.Cached(
|
||||||
|
asmTypeForAnonymousClass(typeMapper.bindingContext, element).internalName.toJdiName())
|
||||||
|
} else {
|
||||||
|
nonInlineClasses = classNamesOfContainingDeclaration
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!findInlineUseSites) {
|
||||||
|
return NonCached(nonInlineClasses.classNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
val inlineCallSiteClasses = inlineUsagesSearcher.findInlinedCalls(
|
||||||
|
element,
|
||||||
|
KotlinDebuggerCaches.getOrCreateTypeMapper(element).bindingContext
|
||||||
|
) { this.getOuterClassNamesForElement(it) }
|
||||||
|
|
||||||
|
nonInlineClasses + inlineCallSiteClasses
|
||||||
|
}
|
||||||
|
is KtAnonymousInitializer -> {
|
||||||
|
val initializerOwner = runReadAction { element.containingDeclaration }
|
||||||
|
|
||||||
|
if (initializerOwner is KtObjectDeclaration && runReadAction { initializerOwner.isCompanion() }) {
|
||||||
|
return getOuterClassNamesForElement(runReadAction { initializerOwner.containingClassOrObject })
|
||||||
|
}
|
||||||
|
|
||||||
|
getOuterClassNamesForElement(initializerOwner)
|
||||||
|
}
|
||||||
|
is KtFunctionLiteral -> {
|
||||||
|
val typeMapper = KotlinDebuggerCaches.getOrCreateTypeMapper(element)
|
||||||
|
|
||||||
|
val nonInlinedLambdaClassName = runReadAction {
|
||||||
|
asmTypeForAnonymousClass(typeMapper.bindingContext, element).internalName.toJdiName()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!alwaysReturnLambdaParentClass && !InlineUtil.isInlinedArgument(element, typeMapper.bindingContext, true)) {
|
||||||
|
return ComputedClassNames.Cached(nonInlinedLambdaClassName)
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputedClassNames.Cached(nonInlinedLambdaClassName) + getOuterClassNamesForElement(element.relevantParentInReadAction)
|
||||||
|
}
|
||||||
|
else -> getOuterClassNamesForElement(element.relevantParentInReadAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getArgumentExpression(it: ValueArgument) = (it.getArgumentExpression() as? KtLambdaExpression)?.functionLiteral ?: it.getArgumentExpression()
|
private fun getNameForNonLocalClass(nonLocalClassOrObject: KtClassOrObject): String? {
|
||||||
}
|
val typeMapper = KotlinDebuggerCaches.getOrCreateTypeMapper(nonLocalClassOrObject)
|
||||||
|
val descriptor = typeMapper.bindingContext[BindingContext.CLASS, nonLocalClassOrObject] ?: return null
|
||||||
|
|
||||||
private fun isInlineCall(context: BindingContext, expr: KtCallExpression): Boolean {
|
val type = typeMapper.mapClass(descriptor)
|
||||||
val resolvedCall = expr.getResolvedCall(context) ?: return false
|
if (type.sort != Type.OBJECT) {
|
||||||
return InlineUtil.isInline(resolvedCall.resultingDescriptor)
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <reified T : PsiElement> PsiElement.typedParent(): T? = parent as? T
|
return type.className
|
||||||
|
|
||||||
private fun String.substringIndex(): String {
|
|
||||||
if (lastIndexOf("$") < 0) return this
|
|
||||||
|
|
||||||
val suffix = substringAfterLast("$")
|
|
||||||
if (suffix.all(Char::isDigit)) {
|
|
||||||
return substringBeforeLast("$") + "$"
|
|
||||||
}
|
}
|
||||||
return this
|
|
||||||
|
private fun isFunctionWithSuspendStateMachine(descriptor: DeclarationDescriptor?, bindingContext: BindingContext): Boolean {
|
||||||
|
return descriptor is SimpleFunctionDescriptor && descriptor.isSuspend && descriptor.containsNonTailSuspensionCalls(bindingContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val PsiElement.relevantParentInReadAction
|
||||||
|
get() = runReadAction { getRelevantElement(this.parent) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.toJdiName() = replace('/', '.')
|
||||||
|
|
||||||
|
private fun DebugProcess.findTargetClasses(outerClass: ReferenceType, lineAt: Int): List<ReferenceType> {
|
||||||
|
val vmProxy = virtualMachineProxy
|
||||||
|
if (!outerClass.isPrepared) return emptyList()
|
||||||
|
|
||||||
|
val targetClasses = ArrayList<ReferenceType>(1)
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (location in outerClass.allLineLocations()) {
|
||||||
|
val locationLine = location.lineNumber() - 1
|
||||||
|
if (locationLine < 0) {
|
||||||
|
// such locations are not correspond to real lines in code
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val method = location.method()
|
||||||
|
if (method == null || DebuggerUtils.isSynthetic(method) || method.isBridge) {
|
||||||
|
// skip synthetic methods
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lineAt == locationLine) {
|
||||||
|
targetClasses += outerClass
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The same line number may appear in different classes so we have to scan nested classes as well.
|
||||||
|
// For example, in the next example line 3 appears in both Foo and Foo$Companion.
|
||||||
|
|
||||||
|
/* class Foo {
|
||||||
|
companion object {
|
||||||
|
val a = Foo() /* line 3 */
|
||||||
|
}
|
||||||
|
} */
|
||||||
|
|
||||||
|
val nestedTypes = vmProxy.nestedTypes(outerClass)
|
||||||
|
for (nested in nestedTypes) {
|
||||||
|
targetClasses += findTargetClasses(nested, lineAt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (_: AbsentInformationException) {}
|
||||||
|
|
||||||
|
return targetClasses
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.idea.debugger
|
||||||
|
|
||||||
|
import com.intellij.debugger.engine.DebugProcess
|
||||||
|
import com.intellij.openapi.application.ModalityState
|
||||||
|
import com.intellij.openapi.application.ex.ApplicationManagerEx
|
||||||
|
import com.intellij.openapi.progress.ProgressManager
|
||||||
|
import com.intellij.openapi.ui.MessageType
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
|
import com.intellij.xdebugger.impl.XDebugSessionImpl
|
||||||
|
import org.jetbrains.kotlin.idea.search.usagesSearch.isImportUsage
|
||||||
|
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.DebuggerClassNameProvider.Companion.getRelevantElement
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches.ComputedClassNames
|
||||||
|
|
||||||
|
class InlineCallableUsagesSearcher(val myDebugProcess: DebugProcess, val scopes: List<GlobalSearchScope>) {
|
||||||
|
fun findInlinedCalls(function: KtNamedFunction, context: BindingContext, transformer: (PsiElement) -> ComputedClassNames): ComputedClassNames {
|
||||||
|
if (!InlineUtil.isInline(context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, function))) {
|
||||||
|
return ComputedClassNames.EMPTY
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val searchResult = hashSetOf<PsiElement>()
|
||||||
|
val functionName = runReadAction { function.name }
|
||||||
|
|
||||||
|
val task = Runnable {
|
||||||
|
ReferencesSearch.search(function, getScopeForInlineFunctionUsages(function)).forEach {
|
||||||
|
if (!runReadAction { it.isImportUsage() }) {
|
||||||
|
val usage = (it.element as? KtElement)?.let(::getRelevantElement)
|
||||||
|
if (usage != null) {
|
||||||
|
searchResult.add(usage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isSuccess = true
|
||||||
|
val applicationEx = ApplicationManagerEx.getApplicationEx()
|
||||||
|
if (!applicationEx.isUnitTestMode && (!applicationEx.holdsReadLock() || applicationEx.isDispatchThread)) {
|
||||||
|
applicationEx.invokeAndWait(
|
||||||
|
{
|
||||||
|
isSuccess = ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||||
|
task,
|
||||||
|
"Compute class names for function $functionName",
|
||||||
|
true,
|
||||||
|
myDebugProcess.project)
|
||||||
|
}, ModalityState.NON_MODAL)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Pooled thread with read lock. Can't invoke task under UI progress, so call it directly.
|
||||||
|
task.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isSuccess) {
|
||||||
|
XDebugSessionImpl.NOTIFICATION_GROUP.createNotification(
|
||||||
|
"Debugger can skip some executions of $functionName method, because the computation of class names was interrupted", MessageType.WARNING
|
||||||
|
).notify(myDebugProcess.project)
|
||||||
|
}
|
||||||
|
|
||||||
|
val results = searchResult.map { transformer(it) }
|
||||||
|
return ComputedClassNames(results.flatMap { it.classNames }, shouldBeCached = results.all { it.shouldBeCached })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getScopeForInlineFunctionUsages(inlineFunction: KtNamedFunction): GlobalSearchScope {
|
||||||
|
val virtualFile = runReadAction { inlineFunction.containingFile.virtualFile }
|
||||||
|
if (virtualFile != null && ProjectRootsUtil.isLibraryFile(myDebugProcess.project, virtualFile)) {
|
||||||
|
return GlobalSearchScope.union(scopes.toTypedArray())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return myDebugProcess.searchScope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,7 +55,6 @@ import org.jetbrains.kotlin.psi.KtFile
|
|||||||
import org.jetbrains.kotlin.psi.KtFunction
|
import org.jetbrains.kotlin.psi.KtFunction
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import java.util.*
|
|
||||||
import com.intellij.debugger.engine.DebuggerUtils as JDebuggerUtils
|
import com.intellij.debugger.engine.DebuggerUtils as JDebuggerUtils
|
||||||
|
|
||||||
class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiRequestPositionManager, PositionManagerEx() {
|
class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiRequestPositionManager, PositionManagerEx() {
|
||||||
@@ -140,7 +139,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
|||||||
val methodName = location.method().name()
|
val methodName = location.method().name()
|
||||||
return when {
|
return when {
|
||||||
JvmAbi.isGetterName(methodName) -> {
|
JvmAbi.isGetterName(methodName) -> {
|
||||||
val parameterForGetter = contextElement.primaryConstructor?.valueParameters?.firstOrNull() {
|
val parameterForGetter = contextElement.primaryConstructor?.valueParameters?.firstOrNull {
|
||||||
it.hasValOrVar() && it.name != null && JvmAbi.getterName(it.name!!) == methodName
|
it.hasValOrVar() && it.name != null && JvmAbi.getterName(it.name!!) == methodName
|
||||||
} ?: return null
|
} ?: return null
|
||||||
parameterForGetter
|
parameterForGetter
|
||||||
@@ -163,7 +162,9 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
|||||||
val elementAt = file.findElementAt(start) ?: return null
|
val elementAt = file.findElementAt(start) ?: return null
|
||||||
val typeMapper = KotlinDebuggerCaches.getOrCreateTypeMapper(elementAt)
|
val typeMapper = KotlinDebuggerCaches.getOrCreateTypeMapper(elementAt)
|
||||||
|
|
||||||
val currentLocationClassName = JvmClassName.byFqNameWithoutInnerClasses(FqName(currentLocationFqName)).internalName
|
val currentLocationClassName = JvmClassName.byFqNameWithoutInnerClasses(FqName(currentLocationFqName))
|
||||||
|
.internalName.replace('/', '.')
|
||||||
|
|
||||||
for (literal in literalsOrFunctions) {
|
for (literal in literalsOrFunctions) {
|
||||||
if (InlineUtil.isInlinedArgument(literal, typeMapper.bindingContext, true)) {
|
if (InlineUtil.isInlinedArgument(literal, typeMapper.bindingContext, true)) {
|
||||||
if (isInsideInlineArgument(literal, location, myDebugProcess as DebugProcessImpl)) {
|
if (isInsideInlineArgument(literal, location, myDebugProcess as DebugProcessImpl)) {
|
||||||
@@ -172,7 +173,10 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val internalClassNames = DebuggerClassNameProvider(myDebugProcess, scopes).classNamesForPosition(literal.firstChild, true)
|
val internalClassNames = DebuggerClassNameProvider(myDebugProcess, scopes, alwaysReturnLambdaParentClass = false)
|
||||||
|
.getOuterClassNamesForElement(literal.firstChild)
|
||||||
|
.classNames
|
||||||
|
|
||||||
if (internalClassNames.any { it == currentLocationClassName }) {
|
if (internalClassNames.any { it == currentLocationClassName }) {
|
||||||
return literal
|
return literal
|
||||||
}
|
}
|
||||||
@@ -223,32 +227,8 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
|||||||
override fun getAllClasses(sourcePosition: SourcePosition): List<ReferenceType> {
|
override fun getAllClasses(sourcePosition: SourcePosition): List<ReferenceType> {
|
||||||
val psiFile = sourcePosition.file
|
val psiFile = sourcePosition.file
|
||||||
if (psiFile is KtFile) {
|
if (psiFile is KtFile) {
|
||||||
val result = ArrayList<ReferenceType>()
|
if (!ProjectRootsUtil.isInProjectOrLibSource(psiFile)) return emptyList()
|
||||||
|
return DebuggerClassNameProvider(myDebugProcess, scopes).getClassesForPosition(sourcePosition)
|
||||||
if (!ProjectRootsUtil.isInProjectOrLibSource(psiFile)) return result
|
|
||||||
|
|
||||||
val names = DebuggerClassNameProvider(myDebugProcess, scopes).classNamesForPosition(sourcePosition, true)
|
|
||||||
for (name in names) {
|
|
||||||
result.addAll(myDebugProcess.virtualMachineProxy.classesByName(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
val allClasses = runReadAction { myDebugProcess.getAllClassesAtLine(sourcePosition) }
|
|
||||||
// TEMP START
|
|
||||||
if (result.isNotEmpty()) {
|
|
||||||
val r1 = result.toSet().toList().sorted()
|
|
||||||
val r2 = allClasses.toSet().toList().sorted()
|
|
||||||
for ((i, r11) in r1.withIndex()) {
|
|
||||||
if (i >= r2.size) {
|
|
||||||
println("MIS: $r11")
|
|
||||||
}
|
|
||||||
else if (r11 != r2[i]) {
|
|
||||||
println("OLD: $r11")
|
|
||||||
println("NEW: ${r2[i]}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TEMP END
|
|
||||||
return allClasses
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (psiFile is ClsFileImpl) {
|
if (psiFile is ClsFileImpl) {
|
||||||
@@ -263,8 +243,8 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
|||||||
throw NoDataException.INSTANCE
|
throw NoDataException.INSTANCE
|
||||||
}
|
}
|
||||||
|
|
||||||
fun originalClassNameForPosition(sourcePosition: SourcePosition): String? {
|
fun originalClassNameForPosition(position: SourcePosition): List<String> {
|
||||||
return DebuggerClassNameProvider(myDebugProcess, scopes).classNamesForPosition(sourcePosition, false).firstOrNull()
|
return DebuggerClassNameProvider(myDebugProcess, scopes).getOuterClassNamesForPosition(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun locationsOfLine(type: ReferenceType, position: SourcePosition): List<Location> {
|
override fun locationsOfLine(type: ReferenceType, position: SourcePosition): List<Location> {
|
||||||
@@ -307,9 +287,9 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
|||||||
throw NoDataException.INSTANCE
|
throw NoDataException.INSTANCE
|
||||||
}
|
}
|
||||||
|
|
||||||
return DebuggerClassNameProvider(myDebugProcess, scopes).classNamesForPosition(position, true).mapNotNull {
|
val classNames = DebuggerClassNameProvider(myDebugProcess, scopes).getOuterClassNamesForPosition(position)
|
||||||
className ->
|
return classNames.mapNotNull { name ->
|
||||||
myDebugProcess.requestsManager.createClassPrepareRequest(requestor, className.replace('/', '.'))
|
myDebugProcess.requestsManager.createClassPrepareRequest(requestor, name + "*")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,9 @@ class KotlinDebuggerCaches(project: Project) {
|
|||||||
return newCompiledData
|
return newCompiledData
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : PsiElement> getOrComputeClassNames(psiElement: T, create: (T) -> ComputedClassNames): List<String> {
|
fun <T : PsiElement> getOrComputeClassNames(psiElement: T?, create: (T) -> ComputedClassNames): List<String> {
|
||||||
|
if (psiElement == null) return Collections.emptyList()
|
||||||
|
|
||||||
val cache = getInstance(runReadAction { psiElement.project })
|
val cache = getInstance(runReadAction { psiElement.project })
|
||||||
|
|
||||||
val classNamesCache = cache.cachedClassNames.value
|
val classNamesCache = cache.cachedClassNames.value
|
||||||
@@ -233,14 +235,20 @@ class KotlinDebuggerCaches(project: Project) {
|
|||||||
|
|
||||||
data class Parameter(val callText: String, val type: KotlinType, val value: Value? = null)
|
data class Parameter(val callText: String, val type: KotlinType, val value: Value? = null)
|
||||||
|
|
||||||
sealed class ComputedClassNames(val classNames: List<String>, val shouldBeCached: Boolean) {
|
class ComputedClassNames(val classNames: List<String>, val shouldBeCached: Boolean) {
|
||||||
class CachedClassNames(classNames: List<String>) : ComputedClassNames(classNames, true) {
|
companion object {
|
||||||
constructor(className: String?) : this(className.toList())
|
val EMPTY = ComputedClassNames.Cached(emptyList())
|
||||||
|
|
||||||
|
fun Cached(classNames: List<String>) = ComputedClassNames(classNames, true)
|
||||||
|
fun Cached(className: String) = ComputedClassNames(Collections.singletonList(className), true)
|
||||||
|
|
||||||
|
fun NonCached(classNames: List<String>) = ComputedClassNames(classNames, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
class NonCachedClassNames(classNames: List<String>) : ComputedClassNames(classNames, false) {
|
fun distinct() = ComputedClassNames(classNames.distinct(), shouldBeCached)
|
||||||
constructor(className: String?) : this(className.toList())
|
|
||||||
}
|
operator fun plus(other: ComputedClassNames) = ComputedClassNames(
|
||||||
|
classNames + other.classNames, shouldBeCached && other.shouldBeCached)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,249 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2015 KtBrains 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.idea.debugger
|
|
||||||
|
|
||||||
import com.intellij.debugger.SourcePosition
|
|
||||||
import com.intellij.debugger.engine.DebugProcess
|
|
||||||
import com.intellij.debugger.engine.DebuggerUtils
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
|
||||||
import com.sun.jdi.AbsentInformationException
|
|
||||||
import com.sun.jdi.ReferenceType
|
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
|
||||||
import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider
|
|
||||||
import org.jetbrains.kotlin.fileClasses.getFileClassInternalName
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.breakpoints.getLambdasAtLineIfAny
|
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral
|
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
|
||||||
|
|
||||||
fun DebugProcess.getAllClassesAtLine(position: SourcePosition): List<ReferenceType> {
|
|
||||||
val result = hashSetOf<ReferenceType>()
|
|
||||||
|
|
||||||
result.addAll(getAllClassesAtElement(position.elementAt, position.line))
|
|
||||||
|
|
||||||
getLambdasAtLineIfAny(position).forEach {
|
|
||||||
result.addAll(getAllClassesAtElement(it, position.line))
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun DebugProcess.getAllClassesAtElement(elementAt: PsiElement, lineAt: Int): List<ReferenceType> {
|
|
||||||
|
|
||||||
var depthToLocalOrAnonymousClass = 0
|
|
||||||
|
|
||||||
fun calc(element: KtElement?): String? {
|
|
||||||
when (element) {
|
|
||||||
null -> return null
|
|
||||||
is KtClassOrObject -> {
|
|
||||||
if (element.isLocal) {
|
|
||||||
depthToLocalOrAnonymousClass++
|
|
||||||
return calc(element.getElementToCalculateClassName())
|
|
||||||
}
|
|
||||||
|
|
||||||
return element.getNameForNonAnonymousClass()
|
|
||||||
}
|
|
||||||
is KtFunctionLiteral -> {
|
|
||||||
if (!isInlinedLambda(element)) {
|
|
||||||
depthToLocalOrAnonymousClass++
|
|
||||||
}
|
|
||||||
return calc(element.getElementToCalculateClassName())
|
|
||||||
}
|
|
||||||
is KtClassInitializer -> {
|
|
||||||
val parent = element.getElementToCalculateClassName()
|
|
||||||
|
|
||||||
if (parent is KtObjectDeclaration && parent.isCompanion()) {
|
|
||||||
// Companion-object initializer
|
|
||||||
return calc(parent.getElementToCalculateClassName())
|
|
||||||
}
|
|
||||||
|
|
||||||
return calc(parent)
|
|
||||||
}
|
|
||||||
is KtPropertyAccessor -> {
|
|
||||||
return calc(element.getClassOfFile())
|
|
||||||
}
|
|
||||||
is KtProperty -> {
|
|
||||||
if (element.isTopLevel || element.isLocal) {
|
|
||||||
return calc(element.getElementToCalculateClassName())
|
|
||||||
}
|
|
||||||
|
|
||||||
val containingClass = element.getClassOfFile()
|
|
||||||
if (containingClass is KtObjectDeclaration && containingClass.isCompanion()) {
|
|
||||||
// Properties from companion object are moved into class
|
|
||||||
val descriptor = element.resolveToDescriptor() as PropertyDescriptor
|
|
||||||
if (AsmUtil.isPropertyWithBackingFieldCopyInOuterClass(descriptor)) {
|
|
||||||
return calc(containingClass.getElementToCalculateClassName())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return calc(containingClass)
|
|
||||||
}
|
|
||||||
is KtSecondaryConstructor -> {
|
|
||||||
return calc(element.getElementToCalculateClassName())
|
|
||||||
}
|
|
||||||
is KtNamedFunction -> {
|
|
||||||
if (element.name == null) {
|
|
||||||
val descriptor = element.readAction { InlineUtil.getInlineArgumentDescriptor(it, element.analyze()) }
|
|
||||||
if (descriptor == null || descriptor.isCrossinline) {
|
|
||||||
depthToLocalOrAnonymousClass++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (element.isLocal) {
|
|
||||||
depthToLocalOrAnonymousClass++
|
|
||||||
}
|
|
||||||
val parent = element.getElementToCalculateClassName()
|
|
||||||
if (parent is KtClassInitializer) {
|
|
||||||
// TODO BUG? anonymous functions from companion object constructor should be inner class of companion object, not class
|
|
||||||
return calc(parent.getElementToCalculateClassName())
|
|
||||||
}
|
|
||||||
|
|
||||||
return calc(parent)
|
|
||||||
}
|
|
||||||
is KtFile -> {
|
|
||||||
return NoResolveFileClassesProvider.getFileClassInternalName(element)
|
|
||||||
}
|
|
||||||
else -> throw IllegalStateException("Unsupported container ${element.javaClass}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val elementToCalcClassName = elementAt.getElementToCalculateClassName(true)
|
|
||||||
val className = calc(elementToCalcClassName) ?: return emptyList()
|
|
||||||
|
|
||||||
if (depthToLocalOrAnonymousClass == 0) {
|
|
||||||
return virtualMachineProxy.classesByName(className)
|
|
||||||
}
|
|
||||||
|
|
||||||
// the name is a parent class for a local or anonymous class
|
|
||||||
val outers = virtualMachineProxy.classesByName(className)
|
|
||||||
return outers.map { findNested(it, 0, depthToLocalOrAnonymousClass, elementAt, lineAt) }.filterNotNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KtClassOrObject.getNameForNonAnonymousClass(addTraitImplSuffix: Boolean = true): String? {
|
|
||||||
if (isLocal) return null
|
|
||||||
if (this.isObjectLiteral()) return null
|
|
||||||
|
|
||||||
val name = name ?: return null
|
|
||||||
|
|
||||||
val parentClass = PsiTreeUtil.getParentOfType(this, KtClassOrObject::class.java, true)
|
|
||||||
if (parentClass != null) {
|
|
||||||
val shouldAddTraitImplSuffix = !(parentClass is KtClass && this is KtObjectDeclaration && this.isCompanion())
|
|
||||||
val parentName = parentClass.getNameForNonAnonymousClass(shouldAddTraitImplSuffix)
|
|
||||||
if (parentName == null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return parentName + "$" + name
|
|
||||||
}
|
|
||||||
|
|
||||||
val className = if (addTraitImplSuffix && this is KtClass && this.isInterface()) name + JvmAbi.DEFAULT_IMPLS_SUFFIX else name
|
|
||||||
|
|
||||||
val packageFqName = this.containingKtFile.packageFqName
|
|
||||||
return if (packageFqName.isRoot) className else packageFqName.asString() + "." + className
|
|
||||||
}
|
|
||||||
|
|
||||||
private val TYPES_TO_CALCULATE_CLASSNAME: Array<Class<out KtElement>> =
|
|
||||||
arrayOf(KtFile::class.java,
|
|
||||||
KtClass::class.java,
|
|
||||||
KtObjectDeclaration::class.java,
|
|
||||||
KtEnumEntry::class.java,
|
|
||||||
KtFunctionLiteral::class.java,
|
|
||||||
KtNamedFunction::class.java,
|
|
||||||
KtPropertyAccessor::class.java,
|
|
||||||
KtProperty::class.java,
|
|
||||||
KtClassInitializer::class.java,
|
|
||||||
KtSecondaryConstructor::class.java)
|
|
||||||
|
|
||||||
private fun PsiElement?.getElementToCalculateClassName(withItSelf: Boolean = false): KtElement? {
|
|
||||||
if (withItSelf && this?.let { it::class.java } as Class<*> in TYPES_TO_CALCULATE_CLASSNAME) return this as KtElement
|
|
||||||
|
|
||||||
return readAction { PsiTreeUtil.getParentOfType(this, *TYPES_TO_CALCULATE_CLASSNAME) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun PsiElement.getClassOfFile(): KtElement? {
|
|
||||||
return PsiTreeUtil.getParentOfType(this, KtFile::class.java, KtClassOrObject::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun DebugProcess.findNested(
|
|
||||||
fromClass: ReferenceType,
|
|
||||||
currentDepth: Int,
|
|
||||||
requiredDepth: Int,
|
|
||||||
elementAt: PsiElement,
|
|
||||||
lineAt: Int
|
|
||||||
): ReferenceType? {
|
|
||||||
val vmProxy = virtualMachineProxy
|
|
||||||
if (fromClass.isPrepared) {
|
|
||||||
try {
|
|
||||||
if (currentDepth < requiredDepth) {
|
|
||||||
val nestedTypes = vmProxy.nestedTypes(fromClass)
|
|
||||||
for (nested in nestedTypes) {
|
|
||||||
val found = findNested(nested, currentDepth + 1, requiredDepth, elementAt, lineAt)
|
|
||||||
if (found != null) {
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
for (location in fromClass.allLineLocations()) {
|
|
||||||
val locationLine = location.lineNumber() - 1
|
|
||||||
if (locationLine <= 0) {
|
|
||||||
// such locations are not correspond to real lines in code
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val method = location.method()
|
|
||||||
if (method == null || DebuggerUtils.isSynthetic(method) || method.isBridge) {
|
|
||||||
// skip synthetic methods
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lineAt == locationLine) {
|
|
||||||
// TODO: compare elementAt, can be significant for lambdas
|
|
||||||
// val candidatePosition = KotlinPositionManager(this).getSourcePosition(location)
|
|
||||||
// if (candidatePosition?.elementAt == elementAt) {
|
|
||||||
return fromClass
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ignored: AbsentInformationException) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isInlinedLambda(functionLiteral: KtFunctionLiteral): Boolean {
|
|
||||||
val functionLiteralExpression = functionLiteral.parent
|
|
||||||
|
|
||||||
var parent = functionLiteralExpression.parent
|
|
||||||
|
|
||||||
while (parent is KtParenthesizedExpression || parent is KtBinaryExpressionWithTypeRHS || parent is KtLabeledExpression) {
|
|
||||||
parent = parent.parent
|
|
||||||
}
|
|
||||||
|
|
||||||
while (parent is ValueArgument || parent is KtValueArgumentList) {
|
|
||||||
parent = parent.parent
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parent !is KtElement) return false
|
|
||||||
|
|
||||||
return InlineUtil.isInlinedArgument(functionLiteral, functionLiteral.analyze(), true)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
LineBreakpoint created at anonymousFunAsParamDefaultValue.kt:6
|
||||||
|
LineBreakpoint created at anonymousFunAsParamDefaultValue.kt:10
|
||||||
|
Run Java
|
||||||
|
Connected to the target VM
|
||||||
|
anonymousFunAsParamDefaultValue.kt:6
|
||||||
|
anonymousFunAsParamDefaultValue.kt:10
|
||||||
|
Disconnected from the target VM
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
@@ -3,8 +3,8 @@ Run Java
|
|||||||
Connected to the target VM
|
Connected to the target VM
|
||||||
functionReference.kt:11
|
functionReference.kt:11
|
||||||
functionReference.kt:4
|
functionReference.kt:4
|
||||||
functionReference.kt:7
|
|
||||||
functionReference.kt:11
|
functionReference.kt:11
|
||||||
|
functionReference.kt:7
|
||||||
functionReference.kt:4
|
functionReference.kt:4
|
||||||
functionReference.kt:5
|
functionReference.kt:5
|
||||||
functionReference.kt:12
|
functionReference.kt:12
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
LineBreakpoint created at kt15823.kt:13
|
||||||
|
Run Java
|
||||||
|
Connected to the target VM
|
||||||
|
kt15823.kt:13
|
||||||
|
Disconnected from the target VM
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
LineBreakpoint created at kt17295.kt:9
|
||||||
|
Run Java
|
||||||
|
Connected to the target VM
|
||||||
|
kt17295.kt:9
|
||||||
|
Disconnected from the target VM
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
@@ -3,8 +3,8 @@ Run Java
|
|||||||
Connected to the target VM
|
Connected to the target VM
|
||||||
propertyReference.kt:11
|
propertyReference.kt:11
|
||||||
propertyReference.kt:4
|
propertyReference.kt:4
|
||||||
propertyReference.kt:7
|
|
||||||
propertyReference.kt:11
|
propertyReference.kt:11
|
||||||
|
propertyReference.kt:7
|
||||||
propertyReference.kt:4
|
propertyReference.kt:4
|
||||||
propertyReference.kt:5
|
propertyReference.kt:5
|
||||||
propertyReference.kt:12
|
propertyReference.kt:12
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package anonymousFunAsParamDefaultValue
|
||||||
|
|
||||||
|
fun foo(a: (String) -> String = fun(b: String): String {
|
||||||
|
listOf("").map {
|
||||||
|
//Breakpoint!
|
||||||
|
val a = it.length
|
||||||
|
}
|
||||||
|
|
||||||
|
//Breakpoint!
|
||||||
|
return b
|
||||||
|
}) {
|
||||||
|
a("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RESUME: 2
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package kt15823
|
||||||
|
|
||||||
|
object Some {
|
||||||
|
val collection = mutableListOf<() -> Unit>()
|
||||||
|
|
||||||
|
inline fun inlineWithReified(crossinline lambda: () -> Unit) {
|
||||||
|
collection.add({ lambda() })
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
inlineWithReified {
|
||||||
|
//Breakpoint!
|
||||||
|
foo() // Will marked as (X), and never hit, but executes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {}
|
||||||
|
|
||||||
|
fun magic() {
|
||||||
|
collection.forEach { it.invoke() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
Some.magic()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RESUME: 1
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package kt17295
|
||||||
|
|
||||||
|
import java.lang.Thread.sleep
|
||||||
|
import kotlin.concurrent.timer
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
timer("Repeating println", period=100) {
|
||||||
|
//Breakpoint!
|
||||||
|
foo()
|
||||||
|
System.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {}
|
||||||
|
|
||||||
|
// RESUME: 1
|
||||||
@@ -1010,6 +1010,12 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/custom"), Pattern.compile("^([^.]+)\\.kt$"), TargetBackend.ANY, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/custom"), Pattern.compile("^([^.]+)\\.kt$"), TargetBackend.ANY, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("anonymousFunAsParamDefaultValue.kt")
|
||||||
|
public void testAnonymousFunAsParamDefaultValue() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/anonymousFunAsParamDefaultValue.kt");
|
||||||
|
doCustomTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("coroutine.kt")
|
@TestMetadata("coroutine.kt")
|
||||||
public void testCoroutine() throws Exception {
|
public void testCoroutine() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/coroutine.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/coroutine.kt");
|
||||||
@@ -1064,6 +1070,18 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
|
|||||||
doCustomTest(fileName);
|
doCustomTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt15823.kt")
|
||||||
|
public void testKt15823() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/kt15823.kt");
|
||||||
|
doCustomTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt17295.kt")
|
||||||
|
public void testKt17295() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/kt17295.kt");
|
||||||
|
doCustomTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("manyFilesWithInlineCalls1Dex.kt")
|
@TestMetadata("manyFilesWithInlineCalls1Dex.kt")
|
||||||
public void testManyFilesWithInlineCalls1Dex() throws Exception {
|
public void testManyFilesWithInlineCalls1Dex() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/manyFilesWithInlineCalls1Dex.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/manyFilesWithInlineCalls1Dex.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user