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:
Yan Zhulanow
2017-04-10 22:36:23 +03:00
parent fe3c1aff8b
commit 622430a296
15 changed files with 472 additions and 670 deletions
@@ -56,11 +56,17 @@ class KotlinExtraSteppingFilter : ExtraSteppingFilter {
val settings = DebuggerSettings.getInstance()
if (settings.TRACING_FILTERS_ENABLED) {
val className = positionManager.originalClassNameForPosition(sourcePosition)?.replace('/', '.') ?: return false
for (filter in settings.steppingFilters) {
if (filter.isEnabled) {
if (filter.matches(className)) {
return true
val classNames = positionManager.originalClassNameForPosition(sourcePosition).map { it.replace('/', '.') }
if (classNames.isEmpty()) {
return false
}
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");
* 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.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.project.DumbService
import com.intellij.openapi.roots.libraries.LibraryUtil
import com.intellij.openapi.ui.MessageType
import com.intellij.debugger.engine.DebuggerUtils
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.xdebugger.impl.XDebugSessionImpl
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegen
import org.jetbrains.kotlin.codegen.coroutines.DO_RESUME_METHOD_NAME
import com.sun.jdi.AbsentInformationException
import com.sun.jdi.ReferenceType
import org.jetbrains.kotlin.codegen.binding.CodegenBinding.*
import org.jetbrains.kotlin.codegen.coroutines.containsNonTailSuspensionCalls
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider
import org.jetbrains.kotlin.fileClasses.getFileClassInternalName
import org.jetbrains.kotlin.idea.debugger.breakpoints.getLambdasAtLineIfAny
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.ComputedClassNames.CachedClassNames
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches.ComputedClassNames.NonCachedClassNames
import org.jetbrains.kotlin.idea.search.usagesSearch.isImportUsage
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches.ComputedClassNames.Companion.Cached
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches.ComputedClassNames.Companion.NonCached
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.name.Name
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.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.org.objectweb.asm.Type
import java.util.*
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>) {
fun classNamesForPosition(sourcePosition: SourcePosition, withInlines: Boolean): List<String> {
val element = sourcePosition.readAction { it.elementAt } ?: return emptyList()
val names = classNamesForPosition(element, withInlines)
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)
}
}
internal fun getRelevantElement(element: PsiElement): PsiElement? {
for (elementType in CLASS_ELEMENT_TYPES) {
if (elementType.isInstance(element)) {
return element
}
}
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)
}
// TODO recursive search
return searchResult.flatMap { classNamesForPosition(it, true) }
// Do not copy the array (*elementTypes) if the element is one we look for
return runReadAction { PsiTreeUtil.getNonStrictParentOfType(element, *CLASS_ELEMENT_TYPES) }
}
}
private fun findCrossInlineArguments(argument: KtFunction, parameterDescriptor: ValueParameterDescriptor, context: BindingContext): Set<String> {
return runReadAction {
val source = parameterDescriptor.original.source.getPsi() as? KtParameter
val functionName = source?.ownerFunction?.name
if (functionName != null) {
return@runReadAction setOf(getCrossInlineArgumentClassName(argument, functionName, context))
}
return@runReadAction emptySet()
}
val inlineUsagesSearcher = InlineCallableUsagesSearcher(debugProcess, scopes)
/**
* Returns classes in which the given line number *is* present.
*/
fun getClassesForPosition(position: SourcePosition): List<ReferenceType> = with (debugProcess) {
val lineNumber = position.line
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
val newName = anonymousClassNameForArgument.substringIndex() + InlineCodegenUtil.INLINE_TRANSFORMATION_SUFFIX + "$" + inlineFunctionName
return "$newName$*"
/**
* Returns classes names in JDI format (my.app.App$Nested) in which the given line number *may be* present.
*/
fun getOuterClassNamesForPosition(position: SourcePosition): List<String> {
return doGetClassesForPosition(position).toList()
}
private fun KtElement.containsCrossInlineParameterUsages(context: BindingContext): Collection<ValueParameterDescriptor> {
fun KtElement.hasParameterCall(parameter: KtParameter): Boolean {
return ReferencesSearch.search(parameter).any {
this.textRange.contains(it.element.textRange)
}
}
private fun doGetClassesForPosition(position: SourcePosition): Set<String> {
val relevantElement = runReadAction { getRelevantElement(position.elementAt) }
val inlineFunction = this.parents.firstIsInstanceOrNull<KtNamedFunction>() ?: return emptySet()
val result = getOrComputeClassNames(relevantElement) { element ->
getOuterClassNamesForElement(element)
}.toMutableSet()
val inlineFunctionDescriptor = context[BindingContext.FUNCTION, inlineFunction]
if (inlineFunctionDescriptor == null || !InlineUtil.isInline(inlineFunctionDescriptor)) return emptySet()
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))
}
}
}
}
for (lambda in position.readAction(::getLambdasAtLineIfAny)) {
result += getOrComputeClassNames(lambda) { element ->
getOuterClassNamesForElement(element)
}
}
return result
}
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
@PublishedApi
@Suppress("NON_TAIL_RECURSIVE_CALL")
internal tailrec fun getOuterClassNamesForElement(element: PsiElement?): ComputedClassNames {
if (element == null) return EMPTY
return when (element) {
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 resolvedCall = expr.getResolvedCall(context) ?: return false
return InlineUtil.isInline(resolvedCall.resultingDescriptor)
}
val type = typeMapper.mapClass(descriptor)
if (type.sort != Type.OBJECT) {
return null
}
private inline fun <reified T : PsiElement> PsiElement.typedParent(): T? = parent as? T
private fun String.substringIndex(): String {
if (lastIndexOf("$") < 0) return this
val suffix = substringAfterLast("$")
if (suffix.all(Char::isDigit)) {
return substringBeforeLast("$") + "$"
return type.className
}
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.resolve.inline.InlineUtil
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import java.util.*
import com.intellij.debugger.engine.DebuggerUtils as JDebuggerUtils
class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiRequestPositionManager, PositionManagerEx() {
@@ -140,7 +139,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
val methodName = location.method().name()
return when {
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
} ?: return null
parameterForGetter
@@ -163,7 +162,9 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
val elementAt = file.findElementAt(start) ?: return null
val typeMapper = KotlinDebuggerCaches.getOrCreateTypeMapper(elementAt)
val currentLocationClassName = JvmClassName.byFqNameWithoutInnerClasses(FqName(currentLocationFqName)).internalName
val currentLocationClassName = JvmClassName.byFqNameWithoutInnerClasses(FqName(currentLocationFqName))
.internalName.replace('/', '.')
for (literal in literalsOrFunctions) {
if (InlineUtil.isInlinedArgument(literal, typeMapper.bindingContext, true)) {
if (isInsideInlineArgument(literal, location, myDebugProcess as DebugProcessImpl)) {
@@ -172,7 +173,10 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
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 }) {
return literal
}
@@ -223,32 +227,8 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
override fun getAllClasses(sourcePosition: SourcePosition): List<ReferenceType> {
val psiFile = sourcePosition.file
if (psiFile is KtFile) {
val result = ArrayList<ReferenceType>()
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 (!ProjectRootsUtil.isInProjectOrLibSource(psiFile)) return emptyList()
return DebuggerClassNameProvider(myDebugProcess, scopes).getClassesForPosition(sourcePosition)
}
if (psiFile is ClsFileImpl) {
@@ -263,8 +243,8 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
throw NoDataException.INSTANCE
}
fun originalClassNameForPosition(sourcePosition: SourcePosition): String? {
return DebuggerClassNameProvider(myDebugProcess, scopes).classNamesForPosition(sourcePosition, false).firstOrNull()
fun originalClassNameForPosition(position: SourcePosition): List<String> {
return DebuggerClassNameProvider(myDebugProcess, scopes).getOuterClassNamesForPosition(position)
}
override fun locationsOfLine(type: ReferenceType, position: SourcePosition): List<Location> {
@@ -307,9 +287,9 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
throw NoDataException.INSTANCE
}
return DebuggerClassNameProvider(myDebugProcess, scopes).classNamesForPosition(position, true).mapNotNull {
className ->
myDebugProcess.requestsManager.createClassPrepareRequest(requestor, className.replace('/', '.'))
val classNames = DebuggerClassNameProvider(myDebugProcess, scopes).getOuterClassNamesForPosition(position)
return classNames.mapNotNull { name ->
myDebugProcess.requestsManager.createClassPrepareRequest(requestor, name + "*")
}
}
@@ -120,7 +120,9 @@ class KotlinDebuggerCaches(project: Project) {
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 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)
sealed class ComputedClassNames(val classNames: List<String>, val shouldBeCached: Boolean) {
class CachedClassNames(classNames: List<String>) : ComputedClassNames(classNames, true) {
constructor(className: String?) : this(className.toList())
class ComputedClassNames(val classNames: List<String>, val shouldBeCached: Boolean) {
companion object {
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) {
constructor(className: String?) : this(className.toList())
}
fun distinct() = ComputedClassNames(classNames.distinct(), shouldBeCached)
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
+1 -1
View File
@@ -3,8 +3,8 @@ Run Java
Connected to the target VM
functionReference.kt:11
functionReference.kt:4
functionReference.kt:7
functionReference.kt:11
functionReference.kt:7
functionReference.kt:4
functionReference.kt:5
functionReference.kt:12
+7
View File
@@ -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
+7
View File
@@ -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
+1 -1
View File
@@ -3,8 +3,8 @@ Run Java
Connected to the target VM
propertyReference.kt:11
propertyReference.kt:4
propertyReference.kt:7
propertyReference.kt:11
propertyReference.kt:7
propertyReference.kt:4
propertyReference.kt:5
propertyReference.kt:12
@@ -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);
}
@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")
public void testCoroutine() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/coroutine.kt");
@@ -1064,6 +1070,18 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
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")
public void testManyFilesWithInlineCalls1Dex() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/manyFilesWithInlineCalls1Dex.kt");