diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/kotlin/PackagePartClassUtils.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/kotlin/PackagePartClassUtils.java index 744a647017c..6bb4a4f7f50 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/kotlin/PackagePartClassUtils.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/kotlin/PackagePartClassUtils.java @@ -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(); diff --git a/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt index 771dc2d4e92..6bfe2e38b79 100644 --- a/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt @@ -565,7 +565,7 @@ fun main(args: Array) { testClass(javaClass()) { 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") } diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/libraries/JetClsFile.kt b/idea/idea-analysis/src/org/jetbrains/jet/plugin/libraries/JetClsFile.kt index 0df568e0c19..c9894b44332 100644 --- a/idea/idea-analysis/src/org/jetbrains/jet/plugin/libraries/JetClsFile.kt +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/libraries/JetClsFile.kt @@ -45,4 +45,7 @@ public class JetClsFile(val provider: JetClassFileViewProvider) : ClsFileImpl(pr fun getRenderedDescriptorsToRange(): Map { return provider.decompiledText.renderedDescriptorsToRange } + + override fun getTextLength() = decompiledFile.getTextLength() + override fun getText() = decompiledFile.getText() } diff --git a/idea/src/org/jetbrains/jet/plugin/debugger/JetPositionManager.java b/idea/src/org/jetbrains/jet/plugin/debugger/JetPositionManager.java index f9c145f8a5d..5460ad0ec66 100644 --- a/idea/src/org/jetbrains/jet/plugin/debugger/JetPositionManager.java +++ b/idea/src/org/jetbrains/jet/plugin/debugger/JetPositionManager.java @@ -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 value = myTypeMappers.get(fqName); diff --git a/idea/src/org/jetbrains/jet/plugin/debugger/PositionManagerLibraryClassFileSearcher.kt b/idea/src/org/jetbrains/jet/plugin/debugger/PositionManagerLibraryClassFileSearcher.kt new file mode 100644 index 00000000000..6c53b1c6662 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/debugger/PositionManagerLibraryClassFileSearcher.kt @@ -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()) + 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 = 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()) + .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 { + 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 "" + ) +} diff --git a/idea/testData/debugger/tinyApp/customLibrary/breakpointOnLocalProperty/1/1.kt b/idea/testData/debugger/tinyApp/customLibrary/breakpointOnLocalProperty/1/1.kt new file mode 100644 index 00000000000..10fab1a9ccc --- /dev/null +++ b/idea/testData/debugger/tinyApp/customLibrary/breakpointOnLocalProperty/1/1.kt @@ -0,0 +1,6 @@ +package customLib.breakpointOnLocalProperty + +public fun breakpointOnLocalPropertyFun(): Int { + val a = 1 + return 1 +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/customLibrary/breakpointOnLocalProperty/2/1.kt b/idea/testData/debugger/tinyApp/customLibrary/breakpointOnLocalProperty/2/1.kt new file mode 100644 index 00000000000..691b0be946c --- /dev/null +++ b/idea/testData/debugger/tinyApp/customLibrary/breakpointOnLocalProperty/2/1.kt @@ -0,0 +1,5 @@ +package customLib.breakpointOnLocalProperty + +public fun breakpointOnLocalPropertyFun2(): Int { + return 1 +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/customLibrary/property/1/1.kt b/idea/testData/debugger/tinyApp/customLibrary/property/1/1.kt new file mode 100644 index 00000000000..1acfa65f40d --- /dev/null +++ b/idea/testData/debugger/tinyApp/customLibrary/property/1/1.kt @@ -0,0 +1,4 @@ +package customLib.property + +public val foo: Int = + 1 diff --git a/idea/testData/debugger/tinyApp/customLibrary/property/2/1.kt b/idea/testData/debugger/tinyApp/customLibrary/property/2/1.kt new file mode 100644 index 00000000000..23ddf508adf --- /dev/null +++ b/idea/testData/debugger/tinyApp/customLibrary/property/2/1.kt @@ -0,0 +1,5 @@ +package customLib.property + +public fun someFun(): Int { + return 1 +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/outs/allFilesPresentInLibrary.out b/idea/testData/debugger/tinyApp/outs/allFilesPresentInLibrary.out new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/debugger/tinyApp/outs/customLibClassName.out b/idea/testData/debugger/tinyApp/outs/customLibClassName.out new file mode 100644 index 00000000000..64b36270fa3 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/customLibClassName.out @@ -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 diff --git a/idea/testData/debugger/tinyApp/outs/stdlibDelegatedProperty.out b/idea/testData/debugger/tinyApp/outs/stdlibDelegatedProperty.out new file mode 100644 index 00000000000..979dc1afcc0 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/stdlibDelegatedProperty.out @@ -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 diff --git a/idea/testData/debugger/tinyApp/outs/stdlibRange.out b/idea/testData/debugger/tinyApp/outs/stdlibRange.out new file mode 100644 index 00000000000..10adbf52e0a --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/stdlibRange.out @@ -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 diff --git a/idea/testData/debugger/tinyApp/outs/stdlibSlice.out b/idea/testData/debugger/tinyApp/outs/stdlibSlice.out new file mode 100644 index 00000000000..3bbaaf353ea --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/stdlibSlice.out @@ -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 diff --git a/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/customLibClassName.kt b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/customLibClassName.kt new file mode 100644 index 00000000000..943312bef5d --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/customLibClassName.kt @@ -0,0 +1,24 @@ +package customLibClassName + +fun main(args: Array) { + 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 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/stdlibDelegatedProperty.kt b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/stdlibDelegatedProperty.kt new file mode 100644 index 00000000000..788623d710f --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/stdlibDelegatedProperty.kt @@ -0,0 +1,15 @@ +package stdlibDelegatedProperty + +import kotlin.properties.Delegates + +var prop: Int by Delegates.notNull() + +fun main(args: Array) { + 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; \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/stdlibRange.kt b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/stdlibRange.kt new file mode 100644 index 00000000000..047b03ee0b1 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/stdlibRange.kt @@ -0,0 +1,14 @@ +package stdlibRange + +fun main(args: Array) { + A().rangeTo(A()).contains(A()) +} + +class A: Comparable { + override fun compareTo(other: A) = 0 +} + +// ADDITIONAL_BREAKPOINT: Ranges.kt:override fun contains(item: T): Boolean { + +// EXPRESSION: start <= item +// RESULT: 1: Z \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/stdlibSlice.kt b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/stdlibSlice.kt new file mode 100644 index 00000000000..e71e6af4354 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/library/stdlibSlice.kt @@ -0,0 +1,11 @@ +package stdlibSlice + +fun main(args: Array) { + val c: CharSequence = "CharSequence" + c.slice(0..1) +} + +// ADDITIONAL_BREAKPOINT: StringsJVM.kt:CharSequence.slice(range: IntRange): CharSequence + +// EXPRESSION: range.start +// RESULT: 0: I \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/debugger/KotlinDebuggerTestCase.java b/idea/tests/org/jetbrains/jet/plugin/debugger/KotlinDebuggerTestCase.java index 485eeb488e5..0887d5843d9 100644 --- a/idea/tests/org/jetbrains/jet/plugin/debugger/KotlinDebuggerTestCase.java +++ b/idea/tests/org/jetbrains/jet/plugin/debugger/KotlinDebuggerTestCase.java @@ -210,6 +210,6 @@ public abstract class KotlinDebuggerTestCase extends DescriptorTestCase { @Override protected Sdk getTestProjectJdk() { - return PluginTestCaseBase.jdkFromIdeaHome(); + return PluginTestCaseBase.fullJdk(); } } diff --git a/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt b/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt index ae24a402722..6d8aaf6fba6 100644 --- a/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt +++ b/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt @@ -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())!! @@ -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() 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); diff --git a/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java index ac893ad1498..272d9b8b724 100644 --- a/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java @@ -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; }