Debugger: allow to evaluate kotlin expressions in java files

#KT-7549 Fixed

 If the context is inside PsiJavaFile, get list of all local variables available in current frame (this logic was removed in 29778311e8 01.12.2015 Drop unnecesary logic about additional context for lambda in debugger). For each variable create KtProperty inside top-level KtFunction in KtFile and set it as contextElement for KtCodeFragment. This file should have all imports from PsiJavaFile. We do not create properties on top-level because they will be highlighted as top-level vals/vars.
This commit is contained in:
Natalia Ukhorskaya
2016-08-01 18:34:24 +03:00
parent d823d6f4ab
commit 6a04cd39b2
19 changed files with 352 additions and 26 deletions
+1
View File
@@ -245,6 +245,7 @@ Using 'this' as function argument in constructor of non-final class
- [`KT-13059`](https://youtrack.jetbrains.com/issue/KT-13059) Fix error stepping on *Step Over* action in the end of while block - [`KT-13059`](https://youtrack.jetbrains.com/issue/KT-13059) Fix error stepping on *Step Over* action in the end of while block
- [`KT-13037`](https://youtrack.jetbrains.com/issue/KT-13037) Fix possible deadlock in debugger in 2016.1 and exception in 2016.2 - [`KT-13037`](https://youtrack.jetbrains.com/issue/KT-13037) Fix possible deadlock in debugger in 2016.1 and exception in 2016.2
- [`KT-12651`](https://youtrack.jetbrains.com/issue/KT-12651) Fix exception in evaluate expression when bad identifier is used for marking object - [`KT-12651`](https://youtrack.jetbrains.com/issue/KT-12651) Fix exception in evaluate expression when bad identifier is used for marking object
- [`KT-7549`](https://youtrack.jetbrains.com/issue/KT-13037) Allow to evaluate kotlin expressions in Java files
### JS ### JS
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.psi package org.jetbrains.kotlin.psi
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.psi.* import com.intellij.psi.*
@@ -25,6 +26,7 @@ import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.IElementType
import com.intellij.testFramework.LightVirtualFile import com.intellij.testFramework.LightVirtualFile
import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.check import org.jetbrains.kotlin.utils.addToStdlib.check
import java.util.* import java.util.*
@@ -41,6 +43,10 @@ abstract class KtCodeFragment(
private var viewProvider = super.getViewProvider() as SingleRootFileViewProvider private var viewProvider = super.getViewProvider() as SingleRootFileViewProvider
private var imports = LinkedHashSet<String>() private var imports = LinkedHashSet<String>()
private val fakeContextForJavaFile: PsiElement? by lazy {
this.getCopyableUserData(FAKE_CONTEXT_FOR_JAVA_FILE)?.invoke()
}
init { init {
getViewProvider().forceCachedPsi(this) getViewProvider().forceCachedPsi(this)
init(TokenType.CODE_FRAGMENT, elementType) init(TokenType.CODE_FRAGMENT, elementType)
@@ -67,7 +73,15 @@ abstract class KtCodeFragment(
override fun isValid() = true override fun isValid() = true
override fun getContext() = context override fun getContext(): PsiElement? {
if (fakeContextForJavaFile != null) return fakeContextForJavaFile
if (context !is KtElement) {
LOG.warn("CodeFragment with non-kotlin context should have fakeContextForJavaFile set: \noriginalContext = ${context?.getElementTextWithContext()}")
return null
}
return context
}
override fun getResolveScope() = context?.resolveScope ?: super.getResolveScope() override fun getResolveScope() = context?.resolveScope ?: super.getResolveScope()
@@ -171,6 +185,8 @@ abstract class KtCodeFragment(
companion object { companion object {
val IMPORT_SEPARATOR: String = "," val IMPORT_SEPARATOR: String = ","
val RUNTIME_TYPE_EVALUATOR: Key<Function1<KtExpression, KotlinType?>> = Key.create("RUNTIME_TYPE_EVALUATOR") val RUNTIME_TYPE_EVALUATOR: Key<Function1<KtExpression, KotlinType?>> = Key.create("RUNTIME_TYPE_EVALUATOR")
val ADDITIONAL_CONTEXT_FOR_LAMBDA: Key<Function0<KtElement?>> = Key.create("ADDITIONAL_CONTEXT_FOR_LAMBDA") val FAKE_CONTEXT_FOR_JAVA_FILE: Key<Function0<KtElement>> = Key.create("FAKE_CONTEXT_FOR_JAVA_FILE")
private val LOG = Logger.getInstance(KtCodeFragment::class.java)
} }
} }
@@ -20,6 +20,7 @@ import com.intellij.debugger.DebuggerManagerEx
import com.intellij.debugger.engine.evaluation.CodeFragmentFactory import com.intellij.debugger.engine.evaluation.CodeFragmentFactory
import com.intellij.debugger.engine.evaluation.CodeFragmentKind import com.intellij.debugger.engine.evaluation.CodeFragmentKind
import com.intellij.debugger.engine.evaluation.TextWithImports import com.intellij.debugger.engine.evaluation.TextWithImports
import com.intellij.debugger.engine.events.DebuggerCommandImpl
import com.intellij.debugger.impl.DebuggerContextImpl import com.intellij.debugger.impl.DebuggerContextImpl
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl
import com.intellij.ide.highlighter.JavaFileType import com.intellij.ide.highlighter.JavaFileType
@@ -37,9 +38,7 @@ import com.intellij.util.concurrency.Semaphore
import com.intellij.xdebugger.XDebuggerManager import com.intellij.xdebugger.XDebuggerManager
import com.intellij.xdebugger.impl.XDebugSessionImpl import com.intellij.xdebugger.impl.XDebugSessionImpl
import com.intellij.xdebugger.impl.ui.tree.ValueMarkup import com.intellij.xdebugger.impl.ui.tree.ValueMarkup
import com.sun.jdi.ArrayReference import com.sun.jdi.*
import com.sun.jdi.PrimitiveValue
import com.sun.jdi.Value
import org.jetbrains.annotations.TestOnly import org.jetbrains.annotations.TestOnly
import org.jetbrains.eval4j.jdi.asValue import org.jetbrains.eval4j.jdi.asValue
import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.asJava.classes.KtLightClass
@@ -51,6 +50,7 @@ import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
import org.jetbrains.kotlin.idea.refactoring.j2kText import org.jetbrains.kotlin.idea.refactoring.j2kText
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.versions.getKotlinJvmRuntimeMarkerClass
import org.jetbrains.kotlin.j2k.AfterConversionPass import org.jetbrains.kotlin.j2k.AfterConversionPass
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
@@ -117,9 +117,86 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
} }
}) })
if (contextElement != null && contextElement !is KtElement) {
codeFragment.putCopyableUserData(KtCodeFragment.FAKE_CONTEXT_FOR_JAVA_FILE, {
val emptyFile = createFakeFileWithJavaContextElement("", contextElement)
val debuggerContext = DebuggerManagerEx.getInstanceEx(project).context
val debuggerSession = debuggerContext.debuggerSession
if ((debuggerSession == null || debuggerContext.suspendContext == null) && !ApplicationManager.getApplication().isUnitTestMode) {
LOG.warn("Couldn't create fake context element for java file, debugger isn't paused on breakpoint")
return@putCopyableUserData emptyFile
}
// TODO: 'this' is unavailable
val visibleVariables = getVisibleLocalVariables(contextElement, debuggerContext)
if (visibleVariables == null) {
LOG.warn("Couldn't get a list of local variables for ${debuggerContext.sourcePosition.file.name}:${debuggerContext.sourcePosition.line}")
return@putCopyableUserData emptyFile
}
val fakeFunctionText = StringBuilder().apply {
append("fun _java_locals_debug_fun_() {\n")
val psiNameHelper = PsiNameHelper.getInstance(project)
visibleVariables.forEach {
val variable = it.key
val variableName = variable.name()
if (!psiNameHelper.isIdentifier(variableName)) return@forEach
val kotlinProperty = createKotlinProperty(project, variableName, variable.type().name(), it.value) ?: return@forEach
append("$kotlinProperty\n")
}
append("val _debug_context_val = 1\n")
append("}")
}.toString()
val fakeFile = createFakeFileWithJavaContextElement(fakeFunctionText, contextElement)
val fakeFunction = fakeFile.declarations.firstOrNull() as? KtFunction
val fakeContext = (fakeFunction?.bodyExpression as? KtBlockExpression)?.statements?.lastOrNull()
return@putCopyableUserData wrapContextIfNeeded(project, contextElement, fakeContext) ?: emptyFile
})
}
return codeFragment return codeFragment
} }
private fun getVisibleLocalVariables(contextElement: PsiElement?, debuggerContext: DebuggerContextImpl): Map<LocalVariable, Value>? {
val semaphore = Semaphore()
semaphore.down()
var visibleVariables: Map<LocalVariable, Value>? = null
val worker = object : DebuggerCommandImpl() {
override fun action() {
try {
val frame = if (ApplicationManager.getApplication().isUnitTestMode)
contextElement?.getCopyableUserData(DEBUG_CONTEXT_FOR_TESTS)?.frameProxy?.stackFrame
else
debuggerContext.frameProxy?.stackFrame
visibleVariables = frame?.let { it.getValues(it.visibleVariables()) } ?: emptyMap<LocalVariable, Value>()
}
catch(ignored: AbsentInformationException) {
// Debug info unavailable
}
finally {
semaphore.up()
}
}
}
debuggerContext.debugProcess?.managerThread?.invoke(worker)
for (i in 0..50) {
if (semaphore.waitFor(20)) break
}
return visibleVariables
}
private fun initImports(imports: String?): String? { private fun initImports(imports: String?): String? {
if (imports != null && !imports.isEmpty()) { if (imports != null && !imports.isEmpty()) {
return imports.split(KtCodeFragment.IMPORT_SEPARATOR) return imports.split(KtCodeFragment.IMPORT_SEPARATOR)
@@ -142,8 +219,11 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
return import return import
} }
private fun getWrappedContextElement(project: Project, context: PsiElement?) private fun getWrappedContextElement(project: Project, context: PsiElement?): PsiElement? {
= wrapContextIfNeeded(project, context, getContextElement(context)) val newContext = getContextElement(context)
if (newContext !is KtElement) return newContext
return wrapContextIfNeeded(project, context, newContext)
}
override fun createPresentationCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment { override fun createPresentationCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment {
val kotlinCodeFragment = createCodeFragment(item, context, project) val kotlinCodeFragment = createCodeFragment(item, context, project)
@@ -190,11 +270,16 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
} }
override fun isContextAccepted(contextElement: PsiElement?): Boolean { override fun isContextAccepted(contextElement: PsiElement?): Boolean {
if (contextElement is PsiCodeBlock) { return when {
// PsiCodeBlock -> DummyHolder -> originalElement // PsiCodeBlock -> DummyHolder -> originalElement
return isContextAccepted(contextElement.context?.context) contextElement is PsiCodeBlock -> isContextAccepted(contextElement.context?.context)
contextElement == null -> false
contextElement.language == KotlinFileType.INSTANCE.language -> true
contextElement.language == JavaFileType.INSTANCE.language -> {
getKotlinJvmRuntimeMarkerClass(contextElement.project, contextElement.resolveScope) != null
}
else -> false
} }
return contextElement?.language == KotlinFileType.INSTANCE.language
} }
override fun getFileType() = KotlinFileType.INSTANCE override fun getFileType() = KotlinFileType.INSTANCE
@@ -206,7 +291,7 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
val DEBUG_LABEL_SUFFIX: String = "_DebugLabel" val DEBUG_LABEL_SUFFIX: String = "_DebugLabel"
@TestOnly val DEBUG_CONTEXT_FOR_TESTS: Key<DebuggerContextImpl> = Key.create("DEBUG_CONTEXT_FOR_TESTS") @TestOnly val DEBUG_CONTEXT_FOR_TESTS: Key<DebuggerContextImpl> = Key.create("DEBUG_CONTEXT_FOR_TESTS")
fun getContextElement(elementAt: PsiElement?): KtElement? { fun getContextElement(elementAt: PsiElement?): PsiElement? {
if (elementAt == null) return null if (elementAt == null) return null
if (elementAt is PsiCodeBlock) { if (elementAt is PsiCodeBlock) {
@@ -218,6 +303,7 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
} }
val containingFile = elementAt.containingFile val containingFile = elementAt.containingFile
if (containingFile is PsiJavaFile) return elementAt
if (containingFile !is KtFile) return null if (containingFile !is KtFile) return null
// elementAt can be PsiWhiteSpace when codeFragment is created from line start offset (in case of first opening EE window) // elementAt can be PsiWhiteSpace when codeFragment is created from line start offset (in case of first opening EE window)
@@ -300,11 +386,27 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
return createWrappingContext(text, labels, newContext, project) return createWrappingContext(text, labels, newContext, project)
} }
private fun createFakeFileWithJavaContextElement(funWithLocalVariables: String, javaContext: PsiElement): KtFile {
val javaFile = javaContext.containingFile as? PsiJavaFile
val sb = StringBuilder()
javaFile?.packageName?.check { !it.isBlank() }?.let {
sb.append("package ").append(it.quoteIfNeeded()).append("\n")
}
javaFile?.importList?.let { sb.append(it.text).append("\n") }
sb.append(funWithLocalVariables)
return KtPsiFactory(javaContext.project).createAnalyzableFile("fakeFileForJavaContextInDebugger.kt", sb.toString(), javaContext)
}
// internal for test // internal for test
fun createWrappingContext( fun createWrappingContext(
newFragmentText: String, newFragmentText: String,
labels: Map<String, Value>, labels: Map<String, Value>,
originalContext: PsiElement?, originalContext: KtElement?,
project: Project project: Project
): KtElement? { ): KtElement? {
val codeFragment = KtPsiFactory(project).createBlockCodeFragment(newFragmentText, originalContext) val codeFragment = KtPsiFactory(project).createBlockCodeFragment(newFragmentText, originalContext)
@@ -318,6 +420,6 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
} }
}) })
return getContextElement(codeFragment.findElementAt(codeFragment.text.length - 1)) return getContextElement(codeFragment.findElementAt(codeFragment.text.length - 1)) as? KtElement
} }
} }
@@ -92,20 +92,18 @@ object KotlinEvaluationBuilder: EvaluatorBuilder {
return EvaluatorBuilderImpl.getInstance()!!.build(codeFragment, position) return EvaluatorBuilderImpl.getInstance()!!.build(codeFragment, position)
} }
val file = position.file
if (file !is KtFile) {
throw EvaluateExceptionUtil.createEvaluateException("Couldn't evaluate kotlin expression in non-kotlin context")
}
if (position.line < 0) { if (position.line < 0) {
throw EvaluateExceptionUtil.createEvaluateException("Couldn't evaluate kotlin expression at $position") throw EvaluateExceptionUtil.createEvaluateException("Couldn't evaluate kotlin expression at $position")
} }
val document = PsiDocumentManager.getInstance(file.project).getDocument(file) val file = position.file
if (document == null || document.lineCount < position.line) { if (file is KtFile) {
throw EvaluateExceptionUtil.createEvaluateException( val document = PsiDocumentManager.getInstance(file.project).getDocument(file)
"Couldn't evaluate kotlin expression: breakpoint is placed outside the file. " + if (document == null || document.lineCount < position.line) {
"It may happen when you've changed source file after starting a debug process.") throw EvaluateExceptionUtil.createEvaluateException(
"Couldn't evaluate kotlin expression: breakpoint is placed outside the file. " +
"It may happen when you've changed source file after starting a debug process.")
}
} }
if (codeFragment.context !is KtElement) { if (codeFragment.context !is KtElement) {
@@ -82,7 +82,7 @@ fun getFunctionForExtractedFragment(
} }
fun generateFunction(): ExtractionResult? { fun generateFunction(): ExtractionResult? {
val originalFile = breakpointFile as KtFile val originalFile = codeFragment.getContextContainingFile() ?: return null
val newDebugExpressions = addDebugExpressionIntoTmpFileForExtractFunction(originalFile, codeFragment, breakpointLine) val newDebugExpressions = addDebugExpressionIntoTmpFileForExtractFunction(originalFile, codeFragment, breakpointLine)
if (newDebugExpressions.isEmpty()) return null if (newDebugExpressions.isEmpty()) return null
@@ -124,6 +124,7 @@ fun addDebugExpressionIntoTmpFileForExtractFunction(originalFile: KtFile, codeFr
val tmpFile = originalFile.copy() as KtFile val tmpFile = originalFile.copy() as KtFile
tmpFile.suppressDiagnosticsInDebugMode = true tmpFile.suppressDiagnosticsInDebugMode = true
tmpFile.analysisContext = originalFile.analysisContext
val contextElement = getExpressionToAddDebugExpressionBefore(tmpFile, codeFragment.getOriginalContext(), line) ?: return emptyList() val contextElement = getExpressionToAddDebugExpressionBefore(tmpFile, codeFragment.getOriginalContext(), line) ?: return emptyList()
+12
View File
@@ -0,0 +1,12 @@
LineBreakpoint created at jcBlock.kt:6
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! jcBlock.JcBlockKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
jcBlock.kt:6
JavaClass.java:16
JavaClass.java:18
JavaClass.java:19
Compile bytecode for bodyVal
Compile bytecode for thenVal
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
+10
View File
@@ -0,0 +1,10 @@
LineBreakpoint created at jcImports.kt:6
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! jcImports.JcImportsKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
jcImports.kt:6
JavaClass.java:27
JavaClass.java:28
Compile bytecode for list.filter { it == 1 }.size
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
+10
View File
@@ -0,0 +1,10 @@
LineBreakpoint created at jcLocalVariable.kt:6
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! jcLocalVariable.JcLocalVariableKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
jcLocalVariable.kt:6
JavaClass.java:11
JavaClass.java:12
Compile bytecode for i
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
+11
View File
@@ -0,0 +1,11 @@
LineBreakpoint created at jcMarkedObject.kt:6
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! jcMarkedObject.JcMarkedObjectKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
jcMarkedObject.kt:6
JavaClass.java:39
JavaClass.java:40
Compile bytecode for i
Compile bytecode for i_DebugLabel
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
+9
View File
@@ -0,0 +1,9 @@
LineBreakpoint created at jcSimple.kt:6
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! jcSimple.JcSimpleKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
jcSimple.kt:6
JavaClass.java:7
Compile bytecode for 1 + 1
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,19 @@
package jcBlock
fun main(args: Array<String>) {
val javaClass = forTests.javaContext.JavaClass()
//Breakpoint!
javaClass.block()
}
// STEP_INTO: 1
// STEP_OVER: 2
// EXPRESSION: bodyVal
// RESULT: 1: I
// EXPRESSION: thenVal
// RESULT: 1: I
// EXPRESSION: elseVal
// RESULT: Unresolved reference: elseVal
@@ -0,0 +1,13 @@
package jcImports
fun main(args: Array<String>) {
val javaClass = forTests.javaContext.JavaClass()
//Breakpoint!
javaClass.imports()
}
// STEP_INTO: 1
// STEP_OVER: 1
// EXPRESSION: list.filter { it == 1 }.size
// RESULT: 1: I
@@ -0,0 +1,13 @@
package jcLocalVariable
fun main(args: Array<String>) {
val javaClass = forTests.javaContext.JavaClass()
//Breakpoint!
javaClass.localVariable()
}
// STEP_INTO: 1
// STEP_OVER: 1
// EXPRESSION: i
// RESULT: 1: I
@@ -0,0 +1,18 @@
package jcMarkedObject
fun main(args: Array<String>) {
val javaClass = forTests.javaContext.JavaClass()
//Breakpoint!
javaClass.markObject()
}
// STEP_INTO: 1
// STEP_OVER: 1
// EXPRESSION: i
// RESULT: instance of java.lang.Integer(id=ID): Ljava/lang/Integer;
// EXPRESSION: i_DebugLabel
// RESULT: instance of java.lang.Integer(id=ID): Ljava/lang/Integer;
// DEBUG_LABEL: i = i
@@ -0,0 +1,12 @@
package jcSimple
fun main(args: Array<String>) {
val javaClass = forTests.javaContext.JavaClass()
//Breakpoint!
javaClass.simple()
}
// STEP_INTO: 1
// EXPRESSION: 1 + 1
// RESULT: 2: I
@@ -0,0 +1,42 @@
package forTests.javaContext;
import java.util.ArrayList;
public class JavaClass {
public void simple() {
int breakpoint = 1;
}
public void localVariable() {
int i = 1;
int breakpoint = 1;
}
public void block() {
int bodyVal = 1;
if (true) {
int thenVal = 1;
int breakpoint = 1;
}
else {
int elseVal = 1;
}
}
public void imports() {
ArrayList<Integer> list = createList();
int breakpoint = 1;
}
private ArrayList<Integer> createList() {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
return list;
}
public void markObject() {
Integer i = 1;
int breakpoint = 1;
}
}
@@ -137,7 +137,7 @@ public abstract class KotlinDebuggerTestCase extends DescriptorTestCase {
MockLibraryUtil.compileKotlin(sourcesDir, outDir, CUSTOM_LIBRARY_JAR.getPath()); MockLibraryUtil.compileKotlin(sourcesDir, outDir, CUSTOM_LIBRARY_JAR.getPath());
List<String> options = Arrays.asList("-d", outputDirPath, "-classpath", ForTestCompileRuntime.runtimeJarForTests().getPath()); List<String> options = Arrays.asList("-d", outputDirPath, "-classpath", ForTestCompileRuntime.runtimeJarForTests().getPath(), "-g");
try { try {
KotlinTestUtils.compileJavaFiles(findJavaFiles(new File(sourcesDir)), options); KotlinTestUtils.compileJavaFiles(findJavaFiles(new File(sourcesDir)), options);
} }
@@ -697,6 +697,45 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
} }
} }
@TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/javaContext")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class JavaContext extends AbstractKotlinEvaluateExpressionTest {
public void testAllFilesPresentInJavaContext() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/javaContext"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("jcBlock.kt")
public void testJcBlock() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/javaContext/jcBlock.kt");
doSingleBreakpointTest(fileName);
}
@TestMetadata("jcImports.kt")
public void testJcImports() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/javaContext/jcImports.kt");
doSingleBreakpointTest(fileName);
}
@TestMetadata("jcLocalVariable.kt")
public void testJcLocalVariable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/javaContext/jcLocalVariable.kt");
doSingleBreakpointTest(fileName);
}
@TestMetadata("jcMarkedObject.kt")
public void testJcMarkedObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/javaContext/jcMarkedObject.kt");
doSingleBreakpointTest(fileName);
}
@TestMetadata("jcSimple.kt")
public void testJcSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/javaContext/jcSimple.kt");
doSingleBreakpointTest(fileName);
}
}
@TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/labels") @TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/labels")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)