Debugger: breakpoints in library source files
This commit is contained in:
+6
-1
@@ -69,10 +69,15 @@ public class PackagePartClassUtils {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static String getPackagePartInternalName(@NotNull JetFile file) {
|
public static String getPackagePartInternalName(@NotNull JetFile file) {
|
||||||
FqName fqName = getPackagePartFqName(getPackageClassFqName(file.getPackageFqName()), file.getVirtualFile());
|
FqName fqName = getPackagePartFqName(file);
|
||||||
return JvmClassName.byFqNameWithoutInnerClasses(fqName).getInternalName();
|
return JvmClassName.byFqNameWithoutInnerClasses(fqName).getInternalName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static FqName getPackagePartFqName(@NotNull JetFile file) {
|
||||||
|
return getPackagePartFqName(getPackageClassFqName(file.getPackageFqName()), file.getVirtualFile());
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static FqName getPackagePartFqName(@NotNull DeserializedCallableMemberDescriptor callable) {
|
public static FqName getPackagePartFqName(@NotNull DeserializedCallableMemberDescriptor callable) {
|
||||||
FqName packageFqName = ((PackageFragmentDescriptor) callable.getContainingDeclaration()).getFqName();
|
FqName packageFqName = ((PackageFragmentDescriptor) callable.getContainingDeclaration()).getFqName();
|
||||||
|
|||||||
@@ -565,7 +565,7 @@ fun main(args: Array<String>) {
|
|||||||
|
|
||||||
testClass(javaClass<AbstractKotlinEvaluateExpressionTest>()) {
|
testClass(javaClass<AbstractKotlinEvaluateExpressionTest>()) {
|
||||||
model("debugger/tinyApp/src/evaluate/singleBreakpoint", testMethod = "doSingleBreakpointTest")
|
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")
|
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> {
|
fun getRenderedDescriptorsToRange(): Map<String, TextRange> {
|
||||||
return provider.decompiledText.renderedDescriptorsToRange
|
return provider.decompiledText.renderedDescriptorsToRange
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getTextLength() = decompiledFile.getTextLength()
|
||||||
|
override fun getText() = decompiledFile.getText()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.jetbrains.jet.plugin.debugger;
|
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.NoDataException;
|
||||||
import com.intellij.debugger.PositionManager;
|
import com.intellij.debugger.PositionManager;
|
||||||
import com.intellij.debugger.SourcePosition;
|
import com.intellij.debugger.SourcePosition;
|
||||||
@@ -23,6 +25,7 @@ import com.intellij.debugger.engine.DebugProcess;
|
|||||||
import com.intellij.debugger.requests.ClassPrepareRequestor;
|
import com.intellij.debugger.requests.ClassPrepareRequestor;
|
||||||
import com.intellij.openapi.application.ApplicationManager;
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.openapi.roots.libraries.LibraryUtil;
|
||||||
import com.intellij.openapi.util.Ref;
|
import com.intellij.openapi.util.Ref;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.PsiFile;
|
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.InlineStrategy;
|
||||||
import org.jetbrains.jet.lang.types.lang.InlineUtil;
|
import org.jetbrains.jet.lang.types.lang.InlineUtil;
|
||||||
import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage;
|
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.jet.plugin.util.DebuggerUtils;
|
||||||
import org.jetbrains.org.objectweb.asm.Type;
|
import org.jetbrains.org.objectweb.asm.Type;
|
||||||
|
|
||||||
@@ -137,9 +141,9 @@ public class JetPositionManager implements PositionManager {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void run() {
|
public void run() {
|
||||||
JetFile file = (JetFile) sourcePosition.getFile();
|
JetFile file = (JetFile) sourcePosition.getFile();
|
||||||
JetTypeMapper typeMapper = prepareTypeMapper(file);
|
boolean isInLibrary = LibraryUtil.findLibraryEntry(file.getVirtualFile(), file.getProject()) != null;
|
||||||
|
JetTypeMapper typeMapper = !isInLibrary ? prepareTypeMapper(file) : createTypeMapperForLibraryFile(sourcePosition);
|
||||||
result.set(getClassNameForElement(sourcePosition.getElementAt(), typeMapper, file));
|
result.set(getClassNameForElement(sourcePosition.getElementAt(), typeMapper, file, isInLibrary));
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -148,7 +152,12 @@ public class JetPositionManager implements PositionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@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);
|
PsiElement element = PsiTreeUtil.getParentOfType(notPositionedElement, JetClassOrObject.class, JetFunctionLiteral.class, JetNamedFunction.class);
|
||||||
|
|
||||||
if (element instanceof JetClassOrObject) {
|
if (element instanceof JetClassOrObject) {
|
||||||
@@ -156,7 +165,7 @@ public class JetPositionManager implements PositionManager {
|
|||||||
}
|
}
|
||||||
else if (element instanceof JetFunctionLiteral) {
|
else if (element instanceof JetFunctionLiteral) {
|
||||||
if (isInlinedLambda((JetFunctionLiteral) element, typeMapper.getBindingContext())) {
|
if (isInlinedLambda((JetFunctionLiteral) element, typeMapper.getBindingContext())) {
|
||||||
return getClassNameForElement(element.getParent(), typeMapper, file);
|
return getClassNameForElement(element.getParent(), typeMapper, file, isInLibrary);
|
||||||
} else {
|
} else {
|
||||||
Type asmType = asmTypeForAnonymousClass(typeMapper.getBindingContext(), ((JetFunctionLiteral) element));
|
Type asmType = asmTypeForAnonymousClass(typeMapper.getBindingContext(), ((JetFunctionLiteral) element));
|
||||||
return asmType.getInternalName();
|
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);
|
return PackagePartClassUtils.getPackagePartInternalName(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static JetElement getElementToCreateTypeMapperForLibraryFile(@Nullable PsiElement notPositionedElement) {
|
||||||
|
return PsiTreeUtil.getParentOfType(notPositionedElement, JetElement.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static String getJvmInternalNameForImpl(JetTypeMapper typeMapper, JetClassOrObject jetClass) {
|
private static String getJvmInternalNameForImpl(JetTypeMapper typeMapper, JetClassOrObject jetClass) {
|
||||||
ClassDescriptor classDescriptor = typeMapper.getBindingContext().get(BindingContext.CLASS, jetClass);
|
ClassDescriptor classDescriptor = typeMapper.getBindingContext().get(BindingContext.CLASS, jetClass);
|
||||||
@@ -191,6 +212,20 @@ public class JetPositionManager implements PositionManager {
|
|||||||
return typeMapper.mapClass(classDescriptor).getInternalName();
|
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) {
|
private JetTypeMapper prepareTypeMapper(final JetFile file) {
|
||||||
final FqName fqName = file.getPackageFqName();
|
final FqName fqName = file.getPackageFqName();
|
||||||
CachedValue<JetTypeMapper> value = myTypeMappers.get(fqName);
|
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
|
@Override
|
||||||
protected Sdk getTestProjectJdk() {
|
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.tree.FieldDescriptor
|
||||||
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl
|
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
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() {
|
public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestCase() {
|
||||||
private val logger = Logger.getLogger(javaClass<KotlinEvaluateExpressionCache>())!!
|
private val logger = Logger.getLogger(javaClass<KotlinEvaluateExpressionCache>())!!
|
||||||
@@ -87,6 +91,8 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
|
|||||||
val file = File(path)
|
val file = File(path)
|
||||||
val fileText = FileUtil.loadFile(file, true)
|
val fileText = FileUtil.loadFile(file, true)
|
||||||
|
|
||||||
|
createAdditionalBreakpoints(fileText)
|
||||||
|
|
||||||
val shouldPrintFrame = InTextDirectivesUtils.isDirectiveDefined(fileText, "// PRINT_FRAME")
|
val shouldPrintFrame = InTextDirectivesUtils.isDirectiveDefined(fileText, "// PRINT_FRAME")
|
||||||
|
|
||||||
val expressions = loadTestDirectivesPairs(fileText, "// EXPRESSION: ", "// RESULT: ")
|
val expressions = loadTestDirectivesPairs(fileText, "// EXPRESSION: ", "// RESULT: ")
|
||||||
@@ -127,10 +133,15 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun doMultipleBreakpointsTest(path: String) {
|
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)
|
createDebugProcess(path)
|
||||||
|
|
||||||
|
val expressions = loadTestDirectivesPairs(fileText, "// EXPRESSION: ", "// RESULT: ")
|
||||||
|
|
||||||
val exceptions = linkedMapOf<String, Throwable>()
|
val exceptions = linkedMapOf<String, Throwable>()
|
||||||
for ((expression, expected) in expressions) {
|
for ((expression, expected) in expressions) {
|
||||||
mayThrow(exceptions, expression) {
|
mayThrow(exceptions, expression) {
|
||||||
@@ -150,6 +161,42 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
|
|||||||
finish()
|
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() {
|
private fun SuspendContextImpl.printFrame() {
|
||||||
val tree = FrameVariablesTree(getProject()!!)
|
val tree = FrameVariablesTree(getProject()!!)
|
||||||
Disposer.register(getTestRootDisposable()!!, tree);
|
Disposer.register(getTestRootDisposable()!!, tree);
|
||||||
|
|||||||
+36
-1
@@ -143,6 +143,7 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
|||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints")
|
@TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints")
|
||||||
|
@InnerTestClasses({MultipleBreakpoints.Library.class})
|
||||||
public static class MultipleBreakpoints extends AbstractKotlinEvaluateExpressionTest {
|
public static class MultipleBreakpoints extends AbstractKotlinEvaluateExpressionTest {
|
||||||
public void testAllFilesPresentInMultipleBreakpoints() throws Exception {
|
public void testAllFilesPresentInMultipleBreakpoints() throws Exception {
|
||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints"), Pattern.compile("^(.+)\\.kt$"), true);
|
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");
|
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")
|
@TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/frame")
|
||||||
@@ -261,7 +296,7 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
|||||||
public static Test suite() {
|
public static Test suite() {
|
||||||
TestSuite suite = new TestSuite("KotlinEvaluateExpressionTestGenerated");
|
TestSuite suite = new TestSuite("KotlinEvaluateExpressionTestGenerated");
|
||||||
suite.addTestSuite(SingleBreakpoint.class);
|
suite.addTestSuite(SingleBreakpoint.class);
|
||||||
suite.addTestSuite(MultipleBreakpoints.class);
|
suite.addTest(MultipleBreakpoints.innerSuite());
|
||||||
suite.addTestSuite(Frame.class);
|
suite.addTestSuite(Frame.class);
|
||||||
return suite;
|
return suite;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user