Debugger: breakpoints in library source files
This commit is contained in:
+6
-1
@@ -69,10 +69,15 @@ public class PackagePartClassUtils {
|
||||
|
||||
@NotNull
|
||||
public static String getPackagePartInternalName(@NotNull JetFile file) {
|
||||
FqName fqName = getPackagePartFqName(getPackageClassFqName(file.getPackageFqName()), file.getVirtualFile());
|
||||
FqName fqName = getPackagePartFqName(file);
|
||||
return JvmClassName.byFqNameWithoutInnerClasses(fqName).getInternalName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static FqName getPackagePartFqName(@NotNull JetFile file) {
|
||||
return getPackagePartFqName(getPackageClassFqName(file.getPackageFqName()), file.getVirtualFile());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static FqName getPackagePartFqName(@NotNull DeserializedCallableMemberDescriptor callable) {
|
||||
FqName packageFqName = ((PackageFragmentDescriptor) callable.getContainingDeclaration()).getFqName();
|
||||
|
||||
@@ -565,7 +565,7 @@ fun main(args: Array<String>) {
|
||||
|
||||
testClass(javaClass<AbstractKotlinEvaluateExpressionTest>()) {
|
||||
model("debugger/tinyApp/src/evaluate/singleBreakpoint", testMethod = "doSingleBreakpointTest")
|
||||
model("debugger/tinyApp/src/evaluate/multipleBreakpoints", testMethod = "doMultipleBreakpointsTest")
|
||||
model("debugger/tinyApp/src/evaluate/multipleBreakpoints", testMethod = "doMultipleBreakpointsTest", recursive = true)
|
||||
model("debugger/tinyApp/src/evaluate/frame", testMethod = "doSingleBreakpointTest")
|
||||
}
|
||||
|
||||
|
||||
@@ -45,4 +45,7 @@ public class JetClsFile(val provider: JetClassFileViewProvider) : ClsFileImpl(pr
|
||||
fun getRenderedDescriptorsToRange(): Map<String, TextRange> {
|
||||
return provider.decompiledText.renderedDescriptorsToRange
|
||||
}
|
||||
|
||||
override fun getTextLength() = decompiledFile.getTextLength()
|
||||
override fun getText() = decompiledFile.getText()
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.jetbrains.jet.plugin.debugger;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.intellij.debugger.NoDataException;
|
||||
import com.intellij.debugger.PositionManager;
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
@@ -23,6 +25,7 @@ import com.intellij.debugger.engine.DebugProcess;
|
||||
import com.intellij.debugger.requests.ClassPrepareRequestor;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.libraries.LibraryUtil;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
@@ -53,6 +56,7 @@ import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||
import org.jetbrains.jet.lang.types.lang.InlineStrategy;
|
||||
import org.jetbrains.jet.lang.types.lang.InlineUtil;
|
||||
import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage;
|
||||
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies;
|
||||
import org.jetbrains.jet.plugin.util.DebuggerUtils;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
|
||||
@@ -137,9 +141,9 @@ public class JetPositionManager implements PositionManager {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void run() {
|
||||
JetFile file = (JetFile) sourcePosition.getFile();
|
||||
JetTypeMapper typeMapper = prepareTypeMapper(file);
|
||||
|
||||
result.set(getClassNameForElement(sourcePosition.getElementAt(), typeMapper, file));
|
||||
boolean isInLibrary = LibraryUtil.findLibraryEntry(file.getVirtualFile(), file.getProject()) != null;
|
||||
JetTypeMapper typeMapper = !isInLibrary ? prepareTypeMapper(file) : createTypeMapperForLibraryFile(sourcePosition);
|
||||
result.set(getClassNameForElement(sourcePosition.getElementAt(), typeMapper, file, isInLibrary));
|
||||
}
|
||||
|
||||
});
|
||||
@@ -148,7 +152,12 @@ public class JetPositionManager implements PositionManager {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static String getClassNameForElement(@Nullable PsiElement notPositionedElement, @NotNull JetTypeMapper typeMapper, @NotNull JetFile file) {
|
||||
public static String getClassNameForElement(
|
||||
@Nullable PsiElement notPositionedElement,
|
||||
@NotNull JetTypeMapper typeMapper,
|
||||
@NotNull JetFile file,
|
||||
boolean isInLibrary
|
||||
) {
|
||||
PsiElement element = PsiTreeUtil.getParentOfType(notPositionedElement, JetClassOrObject.class, JetFunctionLiteral.class, JetNamedFunction.class);
|
||||
|
||||
if (element instanceof JetClassOrObject) {
|
||||
@@ -156,7 +165,7 @@ public class JetPositionManager implements PositionManager {
|
||||
}
|
||||
else if (element instanceof JetFunctionLiteral) {
|
||||
if (isInlinedLambda((JetFunctionLiteral) element, typeMapper.getBindingContext())) {
|
||||
return getClassNameForElement(element.getParent(), typeMapper, file);
|
||||
return getClassNameForElement(element.getParent(), typeMapper, file, isInLibrary);
|
||||
} else {
|
||||
Type asmType = asmTypeForAnonymousClass(typeMapper.getBindingContext(), ((JetFunctionLiteral) element));
|
||||
return asmType.getInternalName();
|
||||
@@ -173,10 +182,22 @@ public class JetPositionManager implements PositionManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (isInLibrary) {
|
||||
JetElement elementAtForLibraryFile = getElementToCreateTypeMapperForLibraryFile(notPositionedElement);
|
||||
assert elementAtForLibraryFile != null : "Couldn't find element at breakpoint for library file " + file.getName()
|
||||
+ (notPositionedElement == null ? "" : ", notPositionedElement = " + JetPsiUtil.getElementTextWithContext( (JetElement) notPositionedElement));
|
||||
return DebuggerPackage.findPackagePartInternalNameForLibraryFile(elementAtForLibraryFile);
|
||||
}
|
||||
|
||||
return PackagePartClassUtils.getPackagePartInternalName(file);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private static JetElement getElementToCreateTypeMapperForLibraryFile(@Nullable PsiElement notPositionedElement) {
|
||||
return PsiTreeUtil.getParentOfType(notPositionedElement, JetElement.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getJvmInternalNameForImpl(JetTypeMapper typeMapper, JetClassOrObject jetClass) {
|
||||
ClassDescriptor classDescriptor = typeMapper.getBindingContext().get(BindingContext.CLASS, jetClass);
|
||||
@@ -191,6 +212,20 @@ public class JetPositionManager implements PositionManager {
|
||||
return typeMapper.mapClass(classDescriptor).getInternalName();
|
||||
}
|
||||
|
||||
private static JetTypeMapper createTypeMapperForLibraryFile(@NotNull SourcePosition position) {
|
||||
JetElement element = getElementToCreateTypeMapperForLibraryFile(position.getElementAt());
|
||||
ResolveSessionForBodies resolveSession = ResolvePackage.getLazyResolveSession(element);
|
||||
|
||||
JetFile file = (JetFile) position.getFile();
|
||||
GenerationState state = new GenerationState(file.getProject(), ClassBuilderFactories.THROW_EXCEPTION,
|
||||
resolveSession.getModuleDescriptor(),
|
||||
resolveSession.resolveToElement(element),
|
||||
Collections.singletonList(file)
|
||||
);
|
||||
state.beforeCompile();
|
||||
return state.getTypeMapper();
|
||||
}
|
||||
|
||||
private JetTypeMapper prepareTypeMapper(final JetFile file) {
|
||||
final FqName fqName = file.getPackageFqName();
|
||||
CachedValue<JetTypeMapper> value = myTypeMappers.get(fqName);
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2010-2014 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.jet.plugin.debugger
|
||||
|
||||
import com.intellij.psi.search.FilenameIndex
|
||||
import org.jetbrains.jet.lang.resolve.kotlin.PackagePartClassUtils
|
||||
import org.jetbrains.jet.lang.psi.JetElement
|
||||
import org.jetbrains.jet.descriptors.serialization.JavaProtoBuf
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import org.jetbrains.jet.plugin.caches.resolve.getLazyResolveSession
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
||||
import com.intellij.openapi.roots.libraries.LibraryUtil
|
||||
import com.intellij.openapi.module.impl.scopes.LibraryScope
|
||||
import com.intellij.openapi.roots.LibraryOrderEntry
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.jet.lang.psi.JetDeclaration
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedCallableMemberDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.jet.lang.psi.JetNamedDeclaration
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
import com.intellij.openapi.module.impl.scopes.JdkScope
|
||||
import com.intellij.openapi.roots.JdkOrderEntry
|
||||
import org.jetbrains.jet.lang.psi.JetPsiUtil
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmClassName
|
||||
|
||||
private val LOG = Logger.getInstance("org.jetbrains.jet.plugin.debugger")
|
||||
|
||||
/**
|
||||
* This method finds class name for top-level declaration in source file attached to library.
|
||||
* The problem is that package-part class file has hash in className and it depends on machine where library was built,
|
||||
* so we couldn't predict it.
|
||||
* 1. find all .class files with package-part prefix, if there is one - return it
|
||||
* 2. find all descriptors with same signature, if there is one - return it
|
||||
* 3. else -> return null, because it means that there is more than one function with same signature in project
|
||||
*/
|
||||
fun findPackagePartInternalNameForLibraryFile(elementAt: JetElement): String? {
|
||||
val topLevelDeclaration = PsiTreeUtil.getTopmostParentOfType(elementAt, javaClass<JetDeclaration>())
|
||||
if (topLevelDeclaration == null) {
|
||||
reportError(elementAt, null)
|
||||
return null
|
||||
}
|
||||
|
||||
val packagePartFile = findPackagePartFileNamesForElement(topLevelDeclaration).singleOrNull()
|
||||
if (packagePartFile != null) return packagePartFile
|
||||
|
||||
val resolveSession = topLevelDeclaration.getLazyResolveSession()
|
||||
|
||||
val descriptor = resolveSession.resolveToDescriptor(topLevelDeclaration)
|
||||
if (descriptor !is CallableDescriptor) return null
|
||||
|
||||
val packageFqName = topLevelDeclaration.getContainingJetFile().getPackageFqName()
|
||||
val packageDescriptor = resolveSession.getModuleDescriptor().getPackage(packageFqName)
|
||||
if (packageDescriptor == null) {
|
||||
reportError(topLevelDeclaration, descriptor)
|
||||
return null
|
||||
}
|
||||
|
||||
val descFromSourceText = render(descriptor)
|
||||
|
||||
val descriptors: Collection<CallableDescriptor> = when (descriptor) {
|
||||
is FunctionDescriptor -> packageDescriptor.getMemberScope().getFunctions(descriptor.getName())
|
||||
is PropertyDescriptor -> packageDescriptor.getMemberScope().getProperties(descriptor.getName())
|
||||
else -> {
|
||||
reportError(topLevelDeclaration, descriptor)
|
||||
listOf()
|
||||
}
|
||||
}
|
||||
|
||||
val deserializedDescriptor = descriptors
|
||||
.filterIsInstance(javaClass<DeserializedCallableMemberDescriptor>())
|
||||
.filter { render(it) == descFromSourceText }
|
||||
.singleOrNull()
|
||||
|
||||
if (deserializedDescriptor == null) {
|
||||
reportError(topLevelDeclaration, descriptor)
|
||||
return null
|
||||
}
|
||||
|
||||
val proto = deserializedDescriptor.proto
|
||||
if (proto.hasExtension(JavaProtoBuf.implClassName)) {
|
||||
val name = deserializedDescriptor.nameResolver.getName(proto.getExtension(JavaProtoBuf.implClassName)!!)
|
||||
return JvmClassName.byFqNameWithoutInnerClasses(packageFqName.child(name)).getInternalName()
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun findPackagePartFileNamesForElement(elementAt: JetElement): List<String> {
|
||||
val project = elementAt.getProject()
|
||||
val file = elementAt.getContainingJetFile()
|
||||
|
||||
val packagePartName = PackagePartClassUtils.getPackagePartFqName(file).shortName().asString()
|
||||
val packagePartNameWoHash = packagePartName.substring(0, packagePartName.lastIndexOf("-"))
|
||||
|
||||
val libraryEntry = LibraryUtil.findLibraryEntry(file.getVirtualFile(), project)
|
||||
val scope = if (libraryEntry is LibraryOrderEntry){
|
||||
LibraryScope(project, libraryEntry.getLibrary() ?: throw AssertionError("Cannot find library for file ${file.getVirtualFile()?.getPath()}"))
|
||||
}
|
||||
else {
|
||||
JdkScope(project, libraryEntry as JdkOrderEntry)
|
||||
}
|
||||
|
||||
val packagePartFiles = FilenameIndex.getAllFilesByExt(project, "class", scope)
|
||||
.filter { it.getName().startsWith(packagePartNameWoHash) }
|
||||
.map {
|
||||
val packageFqName = file.getPackageFqName()
|
||||
if (packageFqName.isRoot()) {
|
||||
it.getNameWithoutExtension()
|
||||
} else {
|
||||
"${packageFqName.asString()}.${it.getNameWithoutExtension()}"
|
||||
}
|
||||
}
|
||||
return packagePartFiles
|
||||
}
|
||||
|
||||
private fun render(desc: DeclarationDescriptor) = DescriptorRenderer.FQ_NAMES_IN_TYPES.render(desc)
|
||||
|
||||
private fun reportError(element: JetElement, descriptor: CallableDescriptor?) {
|
||||
LOG.error("Couldn't calculate class name for element in library scope:\n" +
|
||||
JetPsiUtil.getElementTextWithContext(element) +
|
||||
if (descriptor != null) "\ndescriptor = ${render(descriptor)}" else ""
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package customLib.breakpointOnLocalProperty
|
||||
|
||||
public fun breakpointOnLocalPropertyFun(): Int {
|
||||
val a = 1
|
||||
return 1
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package customLib.breakpointOnLocalProperty
|
||||
|
||||
public fun breakpointOnLocalPropertyFun2(): Int {
|
||||
return 1
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package customLib.property
|
||||
|
||||
public val foo: Int =
|
||||
1
|
||||
@@ -0,0 +1,5 @@
|
||||
package customLib.property
|
||||
|
||||
public fun someFun(): Int {
|
||||
return 1
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
LineBreakpoint created at 1.kt:3
|
||||
LineBreakpoint created at 1.kt:3
|
||||
LineBreakpoint created at 1.kt:3
|
||||
LineBreakpoint created at 1.kt:3
|
||||
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! customLibClassName.CustomLibClassNamePackage
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
1.kt:3
|
||||
Compile bytecode for 1 + 1
|
||||
1.kt:3
|
||||
Compile bytecode for 1 + 2
|
||||
1.kt:3
|
||||
Compile bytecode for 1 + 3
|
||||
1.kt:3
|
||||
Compile bytecode for 1 + 4
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -0,0 +1,8 @@
|
||||
LineBreakpoint created at Delegation.kt:48
|
||||
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! stdlibDelegatedProperty.StdlibDelegatedPropertyPackage
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
Delegation.kt:48
|
||||
Compile bytecode for value.toString()
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -0,0 +1,8 @@
|
||||
LineBreakpoint created at Ranges.kt:7
|
||||
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! stdlibRange.StdlibRangePackage
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
Ranges.kt:7
|
||||
Compile bytecode for start <= item
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -0,0 +1,8 @@
|
||||
LineBreakpoint created at StringsJVM.kt:163
|
||||
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! stdlibSlice.StdlibSlicePackage
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
StringsJVM.kt:163
|
||||
Compile bytecode for range.start
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package customLibClassName
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
customLib.oneFunSameFileName.oneFunSameFileNameFun()
|
||||
customLib.twoFunDifferentSignature.twoFunDifferentSignatureFun()
|
||||
customLib.property.foo
|
||||
customLib.breakpointOnLocalProperty.breakpointOnLocalPropertyFun()
|
||||
}
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: 1.kt:public fun oneFunSameFileNameFun(): Int {
|
||||
// EXPRESSION: 1 + 1
|
||||
// RESULT: 2: I
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: 1.kt:public fun twoFunDifferentSignatureFun(): Int {
|
||||
// EXPRESSION: 1 + 2
|
||||
// RESULT: 3: I
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: 1.kt:public val foo: Int =
|
||||
// EXPRESSION: 1 + 3
|
||||
// RESULT: 4: I
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: 1.kt:public fun breakpointOnLocalPropertyFun(): Int {
|
||||
// EXPRESSION: 1 + 4
|
||||
// RESULT: 5: I
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package stdlibDelegatedProperty
|
||||
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
var prop: Int by Delegates.notNull()
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
prop = 3
|
||||
val a = prop
|
||||
}
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: Delegation.kt:public override fun set(thisRef: Any?, desc: PropertyMetadata, value: T) {
|
||||
|
||||
// EXPRESSION: value.toString()
|
||||
// RESULT: "3": Ljava/lang/String;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package stdlibRange
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
A().rangeTo(A()).contains(A())
|
||||
}
|
||||
|
||||
class A: Comparable<A> {
|
||||
override fun compareTo(other: A) = 0
|
||||
}
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: Ranges.kt:override fun contains(item: T): Boolean {
|
||||
|
||||
// EXPRESSION: start <= item
|
||||
// RESULT: 1: Z
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package stdlibSlice
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val c: CharSequence = "CharSequence"
|
||||
c.slice(0..1)
|
||||
}
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: StringsJVM.kt:CharSequence.slice(range: IntRange): CharSequence
|
||||
|
||||
// EXPRESSION: range.start
|
||||
// RESULT: 0: I
|
||||
@@ -210,6 +210,6 @@ public abstract class KotlinDebuggerTestCase extends DescriptorTestCase {
|
||||
|
||||
@Override
|
||||
protected Sdk getTestProjectJdk() {
|
||||
return PluginTestCaseBase.jdkFromIdeaHome();
|
||||
return PluginTestCaseBase.fullJdk();
|
||||
}
|
||||
}
|
||||
|
||||
+49
-2
@@ -53,7 +53,11 @@ import com.intellij.debugger.ui.impl.watch.ThisDescriptorImpl
|
||||
import com.intellij.debugger.ui.tree.FieldDescriptor
|
||||
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.util.Computable
|
||||
import com.intellij.psi.search.FilenameIndex
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.debugger.DebuggerManagerEx
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
|
||||
public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestCase() {
|
||||
private val logger = Logger.getLogger(javaClass<KotlinEvaluateExpressionCache>())!!
|
||||
@@ -87,6 +91,8 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
|
||||
val file = File(path)
|
||||
val fileText = FileUtil.loadFile(file, true)
|
||||
|
||||
createAdditionalBreakpoints(fileText)
|
||||
|
||||
val shouldPrintFrame = InTextDirectivesUtils.isDirectiveDefined(fileText, "// PRINT_FRAME")
|
||||
|
||||
val expressions = loadTestDirectivesPairs(fileText, "// EXPRESSION: ", "// RESULT: ")
|
||||
@@ -127,10 +133,15 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
|
||||
}
|
||||
|
||||
fun doMultipleBreakpointsTest(path: String) {
|
||||
val expressions = loadTestDirectivesPairs(FileUtil.loadFile(File(path), true), "// EXPRESSION: ", "// RESULT: ")
|
||||
val file = File(path)
|
||||
val fileText = FileUtil.loadFile(file, true)
|
||||
|
||||
createAdditionalBreakpoints(fileText)
|
||||
|
||||
createDebugProcess(path)
|
||||
|
||||
val expressions = loadTestDirectivesPairs(fileText, "// EXPRESSION: ", "// RESULT: ")
|
||||
|
||||
val exceptions = linkedMapOf<String, Throwable>()
|
||||
for ((expression, expected) in expressions) {
|
||||
mayThrow(exceptions, expression) {
|
||||
@@ -150,6 +161,42 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun createAdditionalBreakpoints(fileText: String) {
|
||||
val breakpoints = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// ADDITIONAL_BREAKPOINT: ")
|
||||
for (breakpoint in breakpoints) {
|
||||
val position = breakpoint.split(".kt:")
|
||||
assert(position.size == 2, "Couldn't parse position from test directive: directive = ${breakpoint}")
|
||||
createBreakpoint(position[0], position[1])
|
||||
}
|
||||
}
|
||||
|
||||
private fun createBreakpoint(fileName: String, lineMarker: String) {
|
||||
val project = getProject()!!
|
||||
val sourceFiles = FilenameIndex.getAllFilesByExt(project, "kt").filter {
|
||||
it.getName().contains(fileName) &&
|
||||
it.contentsToByteArray().toString("UTF-8").contains(lineMarker)
|
||||
}
|
||||
|
||||
assert(sourceFiles.size() == 1, "One source file should be found: name = $fileName, sourceFiles = $sourceFiles")
|
||||
|
||||
val runnable = Runnable() {
|
||||
val psiSourceFile = PsiManager.getInstance(project).findFile(sourceFiles.first())!!
|
||||
|
||||
val breakpointManager = DebuggerManagerEx.getInstanceEx(project)?.getBreakpointManager()!!
|
||||
val document = PsiDocumentManager.getInstance(project).getDocument(psiSourceFile)!!
|
||||
|
||||
val index = psiSourceFile.getText()!!.indexOf(lineMarker)
|
||||
val lineNumber = document.getLineNumber(index) + 1
|
||||
|
||||
val breakpoint = breakpointManager.addLineBreakpoint(document, lineNumber)
|
||||
if (breakpoint != null) {
|
||||
println("LineBreakpoint created at " + psiSourceFile.getName() + ":" + lineNumber, ProcessOutputTypes.SYSTEM);
|
||||
}
|
||||
}
|
||||
|
||||
DebuggerInvocationUtil.invokeAndWait(project, runnable, ModalityState.defaultModalityState())
|
||||
}
|
||||
|
||||
private fun SuspendContextImpl.printFrame() {
|
||||
val tree = FrameVariablesTree(getProject()!!)
|
||||
Disposer.register(getTestRootDisposable()!!, tree);
|
||||
|
||||
+36
-1
@@ -143,6 +143,7 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints")
|
||||
@InnerTestClasses({MultipleBreakpoints.Library.class})
|
||||
public static class MultipleBreakpoints extends AbstractKotlinEvaluateExpressionTest {
|
||||
public void testAllFilesPresentInMultipleBreakpoints() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
@@ -178,6 +179,40 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
||||
doMultipleBreakpointsTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/withoutBodyTypeParameters.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library")
|
||||
public static class Library extends AbstractKotlinEvaluateExpressionTest {
|
||||
public void testAllFilesPresentInLibrary() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("customLibClassName.kt")
|
||||
public void testCustomLibClassName() throws Exception {
|
||||
doMultipleBreakpointsTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/customLibClassName.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stdlibDelegatedProperty.kt")
|
||||
public void testStdlibDelegatedProperty() throws Exception {
|
||||
doMultipleBreakpointsTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/stdlibDelegatedProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stdlibRange.kt")
|
||||
public void testStdlibRange() throws Exception {
|
||||
doMultipleBreakpointsTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/stdlibRange.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stdlibSlice.kt")
|
||||
public void testStdlibSlice() throws Exception {
|
||||
doMultipleBreakpointsTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/stdlibSlice.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Test innerSuite() {
|
||||
TestSuite suite = new TestSuite("MultipleBreakpoints");
|
||||
suite.addTestSuite(MultipleBreakpoints.class);
|
||||
suite.addTestSuite(Library.class);
|
||||
return suite;
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/frame")
|
||||
@@ -261,7 +296,7 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
||||
public static Test suite() {
|
||||
TestSuite suite = new TestSuite("KotlinEvaluateExpressionTestGenerated");
|
||||
suite.addTestSuite(SingleBreakpoint.class);
|
||||
suite.addTestSuite(MultipleBreakpoints.class);
|
||||
suite.addTest(MultipleBreakpoints.innerSuite());
|
||||
suite.addTestSuite(Frame.class);
|
||||
return suite;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user