Debugger: get correct context for breakpoints inside lambdas
This commit is contained in:
@@ -83,18 +83,10 @@ public class CodeInsightUtils {
|
|||||||
parent = parent.getParent();
|
parent = parent.getParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!parent.equals(element1)) {
|
element1 = getTopmostParentInside(element1, parent);
|
||||||
while (!parent.equals(element1.getParent())) {
|
|
||||||
element1 = element1.getParent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (startOffset != element1.getTextRange().getStartOffset()) return PsiElement.EMPTY_ARRAY;
|
if (startOffset != element1.getTextRange().getStartOffset()) return PsiElement.EMPTY_ARRAY;
|
||||||
|
|
||||||
if (!parent.equals(element2)) {
|
element2 = getTopmostParentInside(element2, parent);
|
||||||
while (!parent.equals(element2.getParent())) {
|
|
||||||
element2 = element2.getParent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (endOffset != element2.getTextRange().getEndOffset()) return PsiElement.EMPTY_ARRAY;
|
if (endOffset != element2.getTextRange().getEndOffset()) return PsiElement.EMPTY_ARRAY;
|
||||||
|
|
||||||
List<PsiElement> array = new ArrayList<PsiElement>();
|
List<PsiElement> array = new ArrayList<PsiElement>();
|
||||||
@@ -133,6 +125,47 @@ public class CodeInsightUtils {
|
|||||||
return jetExpression;
|
return jetExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static PsiElement[] findElementsOfClassInRange(@NotNull PsiFile file, int startOffset, int endOffset, Class<? extends PsiElement> aClass) {
|
||||||
|
PsiElement element1 = getElementAtOffsetIgnoreWhitespaceBefore(file, startOffset);
|
||||||
|
PsiElement element2 = getElementAtOffsetIgnoreWhitespaceAfter(file, endOffset);
|
||||||
|
|
||||||
|
if (element1 == null || element2 == null) return PsiElement.EMPTY_ARRAY;
|
||||||
|
|
||||||
|
startOffset = element1.getTextRange().getStartOffset();
|
||||||
|
endOffset = element2.getTextRange().getEndOffset();
|
||||||
|
|
||||||
|
PsiElement parent = PsiTreeUtil.findCommonParent(element1, element2);
|
||||||
|
if (parent == null) return PsiElement.EMPTY_ARRAY;
|
||||||
|
|
||||||
|
element1 = getTopmostParentInside(element1, parent);
|
||||||
|
if (startOffset != element1.getTextRange().getStartOffset()) return PsiElement.EMPTY_ARRAY;
|
||||||
|
|
||||||
|
element2 = getTopmostParentInside(element2, parent);
|
||||||
|
if (endOffset != element2.getTextRange().getEndOffset()) return PsiElement.EMPTY_ARRAY;
|
||||||
|
|
||||||
|
PsiElement stopElement = element2.getNextSibling();
|
||||||
|
List<PsiElement> array = new ArrayList<PsiElement>();
|
||||||
|
for (PsiElement currentElement = element1; currentElement != stopElement; currentElement = currentElement.getNextSibling()) {
|
||||||
|
if (aClass.isInstance(currentElement)) {
|
||||||
|
array.add(currentElement);
|
||||||
|
}
|
||||||
|
array.addAll(PsiTreeUtil.findChildrenOfType(currentElement, aClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
return PsiUtilCore.toPsiElementArray(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static PsiElement getTopmostParentInside(@NotNull PsiElement element, @NotNull PsiElement parent) {
|
||||||
|
if (!parent.equals(element)) {
|
||||||
|
while (!parent.equals(element.getParent())) {
|
||||||
|
element = element.getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static PsiElement getElementAtOffsetIgnoreWhitespaceBefore(@NotNull PsiFile file, int offset) {
|
public static PsiElement getElementAtOffsetIgnoreWhitespaceBefore(@NotNull PsiFile file, int offset) {
|
||||||
PsiElement element = file.findElementAt(offset);
|
PsiElement element = file.findElementAt(offset);
|
||||||
@@ -206,6 +239,15 @@ public class CodeInsightUtils {
|
|||||||
return CharArrayUtil.shiftForward(document.getCharsSequence(), lineStartOffset, " \t");
|
return CharArrayUtil.shiftForward(document.getCharsSequence(), lineStartOffset, " \t");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Integer getEndLineOffset(@NotNull PsiFile file, int line) {
|
||||||
|
Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
|
||||||
|
if (document == null) return null;
|
||||||
|
|
||||||
|
int lineStartOffset = document.getLineEndOffset(line);
|
||||||
|
return CharArrayUtil.shiftBackward(document.getCharsSequence(), lineStartOffset, " \t");
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static PsiElement getTopmostElementAtOffset(@NotNull PsiElement element, int offset) {
|
public static PsiElement getTopmostElementAtOffset(@NotNull PsiElement element, int offset) {
|
||||||
do {
|
do {
|
||||||
|
|||||||
@@ -56,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.codeInsight.CodeInsightUtils;
|
||||||
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies;
|
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;
|
||||||
@@ -93,12 +94,48 @@ public class JetPositionManager implements PositionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (lineNumber >= 0) {
|
if (lineNumber >= 0) {
|
||||||
|
JetFunctionLiteral lambdaIfInside = getLambdaIfInside(location, (JetFile) psiFile, lineNumber);
|
||||||
|
if (lambdaIfInside != null) {
|
||||||
|
return SourcePosition.createFromElement(lambdaIfInside.getBodyExpression().getStatements().get(0));
|
||||||
|
}
|
||||||
return SourcePosition.createFromLine(psiFile, lineNumber);
|
return SourcePosition.createFromLine(psiFile, lineNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NoDataException();
|
throw new NoDataException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JetFunctionLiteral getLambdaIfInside(@NotNull Location location, @NotNull JetFile file, int lineNumber) {
|
||||||
|
String currentLocationFqName = location.declaringType().name();
|
||||||
|
if (currentLocationFqName == null) return null;
|
||||||
|
|
||||||
|
Integer start = CodeInsightUtils.getStartLineOffset(file, lineNumber);
|
||||||
|
Integer end = CodeInsightUtils.getEndLineOffset(file, lineNumber);
|
||||||
|
if (start == null || end == null) return null;
|
||||||
|
|
||||||
|
PsiElement[] literals = CodeInsightUtils.findElementsOfClassInRange(file, start, end, JetFunctionLiteral.class);
|
||||||
|
if (literals == null || literals.length == 0) return null;
|
||||||
|
|
||||||
|
boolean isInLibrary = LibraryUtil.findLibraryEntry(file.getVirtualFile(), file.getProject()) != null;
|
||||||
|
JetTypeMapper typeMapper = !isInLibrary
|
||||||
|
? prepareTypeMapper(file)
|
||||||
|
: createTypeMapperForLibraryFile(file.findElementAt(start), file);
|
||||||
|
|
||||||
|
String currentLocationClassName = JvmClassName.byFqNameWithoutInnerClasses(new FqName(currentLocationFqName)).getInternalName();
|
||||||
|
for (PsiElement literal : literals) {
|
||||||
|
JetFunctionLiteral functionLiteral = (JetFunctionLiteral) literal;
|
||||||
|
if (isInlinedLambda(functionLiteral, typeMapper.getBindingContext())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String internalClassName = getClassNameForElement(literal.getFirstChild(), typeMapper, file, isInLibrary);
|
||||||
|
if (internalClassName.equals(currentLocationClassName)) {
|
||||||
|
return functionLiteral;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private PsiFile getPsiFileByLocation(@NotNull Location location) {
|
private PsiFile getPsiFileByLocation(@NotNull Location location) {
|
||||||
String sourceName;
|
String sourceName;
|
||||||
@@ -142,7 +179,7 @@ public class JetPositionManager implements PositionManager {
|
|||||||
public void run() {
|
public void run() {
|
||||||
JetFile file = (JetFile) sourcePosition.getFile();
|
JetFile file = (JetFile) sourcePosition.getFile();
|
||||||
boolean isInLibrary = LibraryUtil.findLibraryEntry(file.getVirtualFile(), file.getProject()) != null;
|
boolean isInLibrary = LibraryUtil.findLibraryEntry(file.getVirtualFile(), file.getProject()) != null;
|
||||||
JetTypeMapper typeMapper = !isInLibrary ? prepareTypeMapper(file) : createTypeMapperForLibraryFile(sourcePosition);
|
JetTypeMapper typeMapper = !isInLibrary ? prepareTypeMapper(file) : createTypeMapperForLibraryFile(sourcePosition.getElementAt(), file);
|
||||||
result.set(getClassNameForElement(sourcePosition.getElementAt(), typeMapper, file, isInLibrary));
|
result.set(getClassNameForElement(sourcePosition.getElementAt(), typeMapper, file, isInLibrary));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,11 +254,10 @@ public class JetPositionManager implements PositionManager {
|
|||||||
return typeMapper.mapClass(classDescriptor).getInternalName();
|
return typeMapper.mapClass(classDescriptor).getInternalName();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JetTypeMapper createTypeMapperForLibraryFile(@NotNull SourcePosition position) {
|
private static JetTypeMapper createTypeMapperForLibraryFile(@Nullable PsiElement notPositionedElement, @NotNull JetFile file) {
|
||||||
JetElement element = getElementToCreateTypeMapperForLibraryFile(position.getElementAt());
|
JetElement element = getElementToCreateTypeMapperForLibraryFile(notPositionedElement);
|
||||||
ResolveSessionForBodies resolveSession = ResolvePackage.getLazyResolveSession(element);
|
ResolveSessionForBodies resolveSession = ResolvePackage.getLazyResolveSession(element);
|
||||||
|
|
||||||
JetFile file = (JetFile) position.getFile();
|
|
||||||
GenerationState state = new GenerationState(file.getProject(), ClassBuilderFactories.THROW_EXCEPTION,
|
GenerationState state = new GenerationState(file.getProject(), ClassBuilderFactories.THROW_EXCEPTION,
|
||||||
resolveSession.getModuleDescriptor(),
|
resolveSession.getModuleDescriptor(),
|
||||||
resolveSession.resolveToElement(element),
|
resolveSession.resolveToElement(element),
|
||||||
|
|||||||
+52
-7
@@ -77,16 +77,11 @@ fun getFunctionForExtractedFragment(
|
|||||||
|
|
||||||
val originalFile = breakpointFile as JetFile
|
val originalFile = breakpointFile as JetFile
|
||||||
|
|
||||||
val lineStart = CodeInsightUtils.getStartLineOffset(originalFile, breakpointLine)
|
|
||||||
if (lineStart == null) return null
|
|
||||||
|
|
||||||
val tmpFile = originalFile.createTempCopy { it }
|
val tmpFile = originalFile.createTempCopy { it }
|
||||||
tmpFile.skipVisibilityCheck = true
|
tmpFile.skipVisibilityCheck = true
|
||||||
|
|
||||||
val elementAtOffset = tmpFile.findElementAt(lineStart)
|
val contextElement = getExpressionToAddDebugExpressionBefore(tmpFile, codeFragment.getContext(), breakpointLine)
|
||||||
if (elementAtOffset == null) return null
|
if (contextElement == null) return null
|
||||||
|
|
||||||
val contextElement: PsiElement = CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, lineStart) ?: elementAtOffset
|
|
||||||
|
|
||||||
// Don't evaluate smth when breakpoint is on package directive (ex. for package classes)
|
// Don't evaluate smth when breakpoint is on package directive (ex. for package classes)
|
||||||
if (contextElement is JetFile) {
|
if (contextElement is JetFile) {
|
||||||
@@ -137,6 +132,52 @@ private fun addImportsToFile(newImportList: JetImportList?, tmpFile: JetFile) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun JetFile.getElementInCopy(e: PsiElement): PsiElement? {
|
||||||
|
val offset = e.getTextRange()?.getStartOffset()
|
||||||
|
if (offset == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
var elementAt = this.findElementAt(offset)
|
||||||
|
while (elementAt == null || elementAt!!.getTextRange()?.getEndOffset() != e.getTextRange()?.getEndOffset()) {
|
||||||
|
elementAt = elementAt?.getParent()
|
||||||
|
}
|
||||||
|
return elementAt
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getExpressionToAddDebugExpressionBefore(tmpFile: JetFile, contextElement: PsiElement?, line: Int): PsiElement? {
|
||||||
|
if (contextElement == null) {
|
||||||
|
val lineStart = CodeInsightUtils.getStartLineOffset(tmpFile, line)
|
||||||
|
if (lineStart == null) return null
|
||||||
|
|
||||||
|
val elementAtOffset = tmpFile.findElementAt(lineStart)
|
||||||
|
if (elementAtOffset == null) return null
|
||||||
|
|
||||||
|
return CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, lineStart) ?: elementAtOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shouldStop(el: PsiElement?, p: PsiElement?) = p is JetBlockExpression || el is JetDeclaration
|
||||||
|
|
||||||
|
var elementAt = tmpFile.getElementInCopy(contextElement)
|
||||||
|
|
||||||
|
var parent = elementAt?.getParent()
|
||||||
|
if (shouldStop(elementAt, parent)) {
|
||||||
|
return elementAt
|
||||||
|
}
|
||||||
|
|
||||||
|
var parentOfParent = parent?.getParent()
|
||||||
|
|
||||||
|
while (parent != null && parentOfParent != null) {
|
||||||
|
if (shouldStop(parent, parentOfParent)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = parent?.getParent()
|
||||||
|
parentOfParent = parent?.getParent()
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
|
||||||
private fun addDebugExpressionBeforeContextElement(codeFragment: JetCodeFragment, contextElement: PsiElement): JetExpression? {
|
private fun addDebugExpressionBeforeContextElement(codeFragment: JetCodeFragment, contextElement: PsiElement): JetExpression? {
|
||||||
val psiFactory = JetPsiFactory(codeFragment)
|
val psiFactory = JetPsiFactory(codeFragment)
|
||||||
|
|
||||||
@@ -144,6 +185,10 @@ private fun addDebugExpressionBeforeContextElement(codeFragment: JetCodeFragment
|
|||||||
contextElement is JetProperty && !contextElement.isLocal() -> {
|
contextElement is JetProperty && !contextElement.isLocal() -> {
|
||||||
wrapInRunFun(contextElement.getDelegateExpressionOrInitializer()!!)
|
wrapInRunFun(contextElement.getDelegateExpressionOrInitializer()!!)
|
||||||
}
|
}
|
||||||
|
contextElement is JetFunctionLiteral -> {
|
||||||
|
val block = contextElement.getBodyExpression()!!
|
||||||
|
block.getStatements().first ?: block.getLastChild()
|
||||||
|
}
|
||||||
contextElement is JetDeclarationWithBody && !contextElement.hasBlockBody()-> {
|
contextElement is JetDeclarationWithBody && !contextElement.hasBlockBody()-> {
|
||||||
wrapInRunFun(contextElement.getBodyExpression()!!)
|
wrapInRunFun(contextElement.getBodyExpression()!!)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
LineBreakpoint created at callableBug.kt:8
|
||||||
|
!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! callableBug.CallableBugPackage
|
||||||
|
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
callableBug.kt:7
|
||||||
|
Compile bytecode for callable
|
||||||
|
callableBug.kt:7
|
||||||
|
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 inlineLambda.kt:9
|
||||||
|
!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! inlineLambda.InlineLambdaPackage
|
||||||
|
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
inlineLambda.kt:8
|
||||||
|
inlineLambda.kt:8
|
||||||
|
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
LineBreakpoint created at lambdaOnSecondLine.kt:10
|
||||||
|
!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! lambdaOnSecondLine.LambdaOnSecondLinePackage
|
||||||
|
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
lambdaOnSecondLine.kt:9
|
||||||
|
lambdaOnSecondLine.kt:14
|
||||||
|
lambdaOnSecondLine.kt:9
|
||||||
|
Compile bytecode for it
|
||||||
|
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
LineBreakpoint created at oneLineLambda.kt:9
|
||||||
|
!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! oneLineLambda.OneLineLambdaPackage
|
||||||
|
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
oneLineLambda.kt:8
|
||||||
|
oneLineLambda.kt:13
|
||||||
|
oneLineLambda.kt:8
|
||||||
|
Compile bytecode for it
|
||||||
|
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
LineBreakpoint created at twoLambdasOnOneLineFirst.kt:9
|
||||||
|
!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! twoLambdasOnOneLineFirst.TwoLambdasOnOneLineFirstPackage
|
||||||
|
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
twoLambdasOnOneLineFirst.kt:8
|
||||||
|
twoLambdasOnOneLineFirst.kt:13
|
||||||
|
twoLambdasOnOneLineFirst.kt:8
|
||||||
|
Compile bytecode for it
|
||||||
|
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
LineBreakpoint created at twoLambdasOnOneLineSecond.kt:9
|
||||||
|
!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! twoLambdasOnOneLineSecond.TwoLambdasOnOneLineSecondPackage
|
||||||
|
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
twoLambdasOnOneLineSecond.kt:8
|
||||||
|
twoLambdasOnOneLineSecond.kt:15
|
||||||
|
twoLambdasOnOneLineSecond.kt:8
|
||||||
|
twoLambdasOnOneLineSecond.kt:15
|
||||||
|
twoLambdasOnOneLineSecond.kt:8
|
||||||
|
Compile bytecode for it
|
||||||
|
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package callableBug
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val callable = 1
|
||||||
|
array(1, 2).map {
|
||||||
|
it + 1
|
||||||
|
//Breakpoint!
|
||||||
|
}.forEach { it + 2 }
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPRESSION: callable
|
||||||
|
// RESULT: 1: I
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package inlineLambda
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val a = array(1)
|
||||||
|
// EXPRESSION: it
|
||||||
|
// RESULT: Unresolved reference: it
|
||||||
|
// STEP_INTO: 1
|
||||||
|
//Breakpoint!
|
||||||
|
a.map { it * 1 }
|
||||||
|
}
|
||||||
|
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package lambdaOnSecondLine
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val a = A()
|
||||||
|
// EXPRESSION: it
|
||||||
|
// RESULT: 1: I
|
||||||
|
// STEP_INTO: 2
|
||||||
|
a.foo { a }
|
||||||
|
//Breakpoint!
|
||||||
|
.foo { a }
|
||||||
|
}
|
||||||
|
|
||||||
|
class A {
|
||||||
|
fun foo(f: (Int) -> A): A {
|
||||||
|
return f(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package oneLineLambda
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val a = A()
|
||||||
|
// EXPRESSION: it
|
||||||
|
// RESULT: 1: I
|
||||||
|
// STEP_INTO: 2
|
||||||
|
//Breakpoint!
|
||||||
|
a.foo { a }
|
||||||
|
}
|
||||||
|
|
||||||
|
class A {
|
||||||
|
fun foo(f: (Int) -> A): A {
|
||||||
|
return f(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
package twoLambdasOnOneLineFirst
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val a = A()
|
||||||
|
// EXPRESSION: it
|
||||||
|
// RESULT: 1: I
|
||||||
|
// STEP_INTO: 2
|
||||||
|
//Breakpoint!
|
||||||
|
a.foo { a }.foo { a }
|
||||||
|
}
|
||||||
|
|
||||||
|
class A {
|
||||||
|
fun foo(f: (Int) -> A): A {
|
||||||
|
return f(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package twoLambdasOnOneLineSecond
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val a = A()
|
||||||
|
// EXPRESSION: it
|
||||||
|
// RESULT: 2: I
|
||||||
|
// STEP_INTO: 4
|
||||||
|
//Breakpoint!
|
||||||
|
a.foo { counter++; a }.foo { a }
|
||||||
|
}
|
||||||
|
|
||||||
|
var counter = 1
|
||||||
|
|
||||||
|
class A {
|
||||||
|
fun foo(f: (Int) -> A): A {
|
||||||
|
return f(counter)
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-16
@@ -59,7 +59,7 @@ import com.intellij.debugger.DebuggerManagerEx
|
|||||||
import com.intellij.psi.PsiDocumentManager
|
import com.intellij.psi.PsiDocumentManager
|
||||||
import com.intellij.openapi.application.ModalityState
|
import com.intellij.openapi.application.ModalityState
|
||||||
|
|
||||||
public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestCase() {
|
public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestBase() {
|
||||||
private val logger = Logger.getLogger(javaClass<KotlinEvaluateExpressionCache>())!!
|
private val logger = Logger.getLogger(javaClass<KotlinEvaluateExpressionCache>())!!
|
||||||
|
|
||||||
private val appender = object : AppenderSkeleton() {
|
private val appender = object : AppenderSkeleton() {
|
||||||
@@ -73,7 +73,7 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
|
|||||||
private var oldLogLevel: Level? = null
|
private var oldLogLevel: Level? = null
|
||||||
|
|
||||||
override fun setUp() {
|
override fun setUp() {
|
||||||
super<KotlinDebuggerTestCase>.setUp()
|
super.setUp()
|
||||||
|
|
||||||
oldLogLevel = logger.getLevel()
|
oldLogLevel = logger.getLevel()
|
||||||
logger.setLevel(Level.DEBUG)
|
logger.setLevel(Level.DEBUG)
|
||||||
@@ -84,7 +84,7 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
|
|||||||
logger.setLevel(oldLogLevel)
|
logger.setLevel(oldLogLevel)
|
||||||
logger.removeAppender(appender)
|
logger.removeAppender(appender)
|
||||||
|
|
||||||
super<KotlinDebuggerTestCase>.tearDown()
|
super.tearDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun doSingleBreakpointTest(path: String) {
|
fun doSingleBreakpointTest(path: String) {
|
||||||
@@ -102,6 +102,13 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
|
|||||||
|
|
||||||
createDebugProcess(path)
|
createDebugProcess(path)
|
||||||
|
|
||||||
|
val count = InTextDirectivesUtils.getPrefixedInt(fileText, "// STEP_INTO: ") ?: 0
|
||||||
|
if (count > 0) {
|
||||||
|
for (i in 1..count) {
|
||||||
|
onBreakpoint { stepInto() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onBreakpoint {
|
onBreakpoint {
|
||||||
val exceptions = linkedMapOf<String, Throwable>()
|
val exceptions = linkedMapOf<String, Throwable>()
|
||||||
try {
|
try {
|
||||||
@@ -275,19 +282,6 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
|
|||||||
return mainFile.getParentFile()?.listFiles()?.filter { it.name.startsWith(mainFileName) && it.name != mainFileName } ?: Collections.emptyList()
|
return mainFile.getParentFile()?.listFiles()?.filter { it.name.startsWith(mainFileName) && it.name != mainFileName } ?: Collections.emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onBreakpoint(doOnBreakpoint: SuspendContextImpl.() -> Unit) {
|
|
||||||
super.onBreakpoint {
|
|
||||||
super.printContext(it)
|
|
||||||
it.doOnBreakpoint()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun finish() {
|
|
||||||
onBreakpoint {
|
|
||||||
resume(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SuspendContextImpl.evaluate(text: String, codeFragmentKind: CodeFragmentKind, expectedResult: String) {
|
private fun SuspendContextImpl.evaluate(text: String, codeFragmentKind: CodeFragmentKind, expectedResult: String) {
|
||||||
ApplicationManager.getApplication()?.runReadAction {
|
ApplicationManager.getApplication()?.runReadAction {
|
||||||
val sourcePosition = ContextUtil.getSourcePosition(this)
|
val sourcePosition = ContextUtil.getSourcePosition(this)
|
||||||
|
|||||||
+47
-1
@@ -35,7 +35,7 @@ import java.util.regex.Pattern;
|
|||||||
public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluateExpressionTest {
|
public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluateExpressionTest {
|
||||||
@TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint")
|
@TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@InnerTestClasses({SingleBreakpoint.Frame.class})
|
@InnerTestClasses({SingleBreakpoint.Frame.class, SingleBreakpoint.Lambdas.class})
|
||||||
@RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class)
|
@RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class)
|
||||||
public static class SingleBreakpoint extends AbstractKotlinEvaluateExpressionTest {
|
public static class SingleBreakpoint extends AbstractKotlinEvaluateExpressionTest {
|
||||||
@TestMetadata("abstractFunCall.kt")
|
@TestMetadata("abstractFunCall.kt")
|
||||||
@@ -54,6 +54,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
|||||||
doSingleBreakpointTest(fileName);
|
doSingleBreakpointTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callableBug.kt")
|
||||||
|
public void testCallableBug() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/callableBug.kt");
|
||||||
|
doSingleBreakpointTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("classFromAnotherPackage.kt")
|
@TestMetadata("classFromAnotherPackage.kt")
|
||||||
public void testClassFromAnotherPackage() throws Exception {
|
public void testClassFromAnotherPackage() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/classFromAnotherPackage.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/classFromAnotherPackage.kt");
|
||||||
@@ -262,6 +268,46 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class)
|
||||||
|
public static class Lambdas extends AbstractKotlinEvaluateExpressionTest {
|
||||||
|
public void testAllFilesPresentInLambdas() throws Exception {
|
||||||
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineLambda.kt")
|
||||||
|
public void testInlineLambda() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/inlineLambda.kt");
|
||||||
|
doSingleBreakpointTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaOnSecondLine.kt")
|
||||||
|
public void testLambdaOnSecondLine() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/lambdaOnSecondLine.kt");
|
||||||
|
doSingleBreakpointTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("oneLineLambda.kt")
|
||||||
|
public void testOneLineLambda() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/oneLineLambda.kt");
|
||||||
|
doSingleBreakpointTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("twoLambdasOnOneLineFirst.kt")
|
||||||
|
public void testTwoLambdasOnOneLineFirst() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/twoLambdasOnOneLineFirst.kt");
|
||||||
|
doSingleBreakpointTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("twoLambdasOnOneLineSecond.kt")
|
||||||
|
public void testTwoLambdasOnOneLineSecond() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/twoLambdasOnOneLineSecond.kt");
|
||||||
|
doSingleBreakpointTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints")
|
@TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints")
|
||||||
|
|||||||
Reference in New Issue
Block a user