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:
@@ -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-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-7549`](https://youtrack.jetbrains.com/issue/KT-13037) Allow to evaluate kotlin expressions in Java files
|
||||
|
||||
### JS
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.psi
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.*
|
||||
@@ -25,6 +26,7 @@ import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import com.intellij.testFramework.LightVirtualFile
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
import java.util.*
|
||||
@@ -41,6 +43,10 @@ abstract class KtCodeFragment(
|
||||
private var viewProvider = super.getViewProvider() as SingleRootFileViewProvider
|
||||
private var imports = LinkedHashSet<String>()
|
||||
|
||||
private val fakeContextForJavaFile: PsiElement? by lazy {
|
||||
this.getCopyableUserData(FAKE_CONTEXT_FOR_JAVA_FILE)?.invoke()
|
||||
}
|
||||
|
||||
init {
|
||||
getViewProvider().forceCachedPsi(this)
|
||||
init(TokenType.CODE_FRAGMENT, elementType)
|
||||
@@ -67,7 +73,15 @@ abstract class KtCodeFragment(
|
||||
|
||||
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()
|
||||
|
||||
@@ -171,6 +185,8 @@ abstract class KtCodeFragment(
|
||||
companion object {
|
||||
val IMPORT_SEPARATOR: String = ","
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
+114
-12
@@ -20,6 +20,7 @@ import com.intellij.debugger.DebuggerManagerEx
|
||||
import com.intellij.debugger.engine.evaluation.CodeFragmentFactory
|
||||
import com.intellij.debugger.engine.evaluation.CodeFragmentKind
|
||||
import com.intellij.debugger.engine.evaluation.TextWithImports
|
||||
import com.intellij.debugger.engine.events.DebuggerCommandImpl
|
||||
import com.intellij.debugger.impl.DebuggerContextImpl
|
||||
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl
|
||||
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.impl.XDebugSessionImpl
|
||||
import com.intellij.xdebugger.impl.ui.tree.ValueMarkup
|
||||
import com.sun.jdi.ArrayReference
|
||||
import com.sun.jdi.PrimitiveValue
|
||||
import com.sun.jdi.Value
|
||||
import com.sun.jdi.*
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.eval4j.jdi.asValue
|
||||
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.util.IdeDescriptorRenderers
|
||||
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.psi.*
|
||||
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
|
||||
}
|
||||
|
||||
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? {
|
||||
if (imports != null && !imports.isEmpty()) {
|
||||
return imports.split(KtCodeFragment.IMPORT_SEPARATOR)
|
||||
@@ -142,8 +219,11 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
|
||||
return import
|
||||
}
|
||||
|
||||
private fun getWrappedContextElement(project: Project, context: PsiElement?)
|
||||
= wrapContextIfNeeded(project, context, getContextElement(context))
|
||||
private fun getWrappedContextElement(project: Project, context: PsiElement?): PsiElement? {
|
||||
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 {
|
||||
val kotlinCodeFragment = createCodeFragment(item, context, project)
|
||||
@@ -190,11 +270,16 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
|
||||
}
|
||||
|
||||
override fun isContextAccepted(contextElement: PsiElement?): Boolean {
|
||||
if (contextElement is PsiCodeBlock) {
|
||||
// PsiCodeBlock -> DummyHolder -> originalElement
|
||||
return isContextAccepted(contextElement.context?.context)
|
||||
return when {
|
||||
// PsiCodeBlock -> DummyHolder -> originalElement
|
||||
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
|
||||
@@ -206,7 +291,7 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
|
||||
val DEBUG_LABEL_SUFFIX: String = "_DebugLabel"
|
||||
@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 is PsiCodeBlock) {
|
||||
@@ -218,6 +303,7 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
|
||||
}
|
||||
|
||||
val containingFile = elementAt.containingFile
|
||||
if (containingFile is PsiJavaFile) return elementAt
|
||||
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)
|
||||
@@ -300,11 +386,27 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
|
||||
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
|
||||
fun createWrappingContext(
|
||||
newFragmentText: String,
|
||||
labels: Map<String, Value>,
|
||||
originalContext: PsiElement?,
|
||||
originalContext: KtElement?,
|
||||
project: Project
|
||||
): KtElement? {
|
||||
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)
|
||||
}
|
||||
|
||||
val file = position.file
|
||||
if (file !is KtFile) {
|
||||
throw EvaluateExceptionUtil.createEvaluateException("Couldn't evaluate kotlin expression in non-kotlin context")
|
||||
}
|
||||
|
||||
if (position.line < 0) {
|
||||
throw EvaluateExceptionUtil.createEvaluateException("Couldn't evaluate kotlin expression at $position")
|
||||
}
|
||||
|
||||
val document = PsiDocumentManager.getInstance(file.project).getDocument(file)
|
||||
if (document == null || document.lineCount < position.line) {
|
||||
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.")
|
||||
val file = position.file
|
||||
if (file is KtFile) {
|
||||
val document = PsiDocumentManager.getInstance(file.project).getDocument(file)
|
||||
if (document == null || document.lineCount < position.line) {
|
||||
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) {
|
||||
|
||||
+2
-1
@@ -82,7 +82,7 @@ fun getFunctionForExtractedFragment(
|
||||
}
|
||||
|
||||
fun generateFunction(): ExtractionResult? {
|
||||
val originalFile = breakpointFile as KtFile
|
||||
val originalFile = codeFragment.getContextContainingFile() ?: return null
|
||||
|
||||
val newDebugExpressions = addDebugExpressionIntoTmpFileForExtractFunction(originalFile, codeFragment, breakpointLine)
|
||||
if (newDebugExpressions.isEmpty()) return null
|
||||
@@ -124,6 +124,7 @@ fun addDebugExpressionIntoTmpFileForExtractFunction(originalFile: KtFile, codeFr
|
||||
|
||||
val tmpFile = originalFile.copy() as KtFile
|
||||
tmpFile.suppressDiagnosticsInDebugMode = true
|
||||
tmpFile.analysisContext = originalFile.analysisContext
|
||||
|
||||
val contextElement = getExpressionToAddDebugExpressionBefore(tmpFile, codeFragment.getOriginalContext(), line) ?: return emptyList()
|
||||
|
||||
|
||||
+12
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
+19
@@ -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
|
||||
+13
@@ -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
|
||||
Vendored
+13
@@ -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
|
||||
+18
@@ -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
|
||||
+12
@@ -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());
|
||||
|
||||
List<String> options = Arrays.asList("-d", outputDirPath, "-classpath", ForTestCompileRuntime.runtimeJarForTests().getPath());
|
||||
List<String> options = Arrays.asList("-d", outputDirPath, "-classpath", ForTestCompileRuntime.runtimeJarForTests().getPath(), "-g");
|
||||
try {
|
||||
KotlinTestUtils.compileJavaFiles(findJavaFiles(new File(sourcesDir)), options);
|
||||
}
|
||||
|
||||
+39
@@ -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")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user