Evaluate expression: evaluate block of code (CODE_BLOCK mode)
This commit is contained in:
@@ -29,17 +29,19 @@ import com.intellij.psi.JavaCodeFragmentFactory
|
||||
import org.jetbrains.jet.plugin.debugger.KotlinEditorTextProvider
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
import org.jetbrains.jet.lang.psi.JetBlockExpression
|
||||
import org.jetbrains.jet.lang.psi.JetBlockCodeFragment
|
||||
|
||||
class KotlinCodeFragmentFactory: CodeFragmentFactory() {
|
||||
override fun createCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment {
|
||||
if (item.getKind() == CodeFragmentKind.EXPRESSION) {
|
||||
val codeFragment = JetExpressionCodeFragment(project, "fragment.kt", item.getText(), getContextElement(context))
|
||||
if (item.getImports().isNotEmpty()) {
|
||||
codeFragment.addImportsFromString(item.getImports())
|
||||
}
|
||||
return codeFragment
|
||||
val codeFragment = if (item.getKind() == CodeFragmentKind.EXPRESSION) {
|
||||
JetExpressionCodeFragment(project, "fragment.kt", item.getText(), getContextElement(context))
|
||||
}
|
||||
return JavaCodeFragmentFactory.getInstance(project)!!.createCodeBlockCodeFragment(item.getText(), context, true)
|
||||
else {
|
||||
JetBlockCodeFragment(project, "fragment.kt", item.getText(), getContextElement(context))
|
||||
}
|
||||
codeFragment.addImportsFromString(item.getImports())
|
||||
return codeFragment
|
||||
}
|
||||
|
||||
override fun createPresentationCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment {
|
||||
|
||||
@@ -48,8 +48,6 @@ import org.jetbrains.eval4j.jdi.asValue
|
||||
import org.jetbrains.jet.plugin.refactoring.createTempCopy
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionData
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.performAnalysis
|
||||
import org.jetbrains.jet.plugin.util.MaybeError
|
||||
import org.jetbrains.jet.plugin.util.MaybeValue
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.validate
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.generateFunction
|
||||
import org.jetbrains.jet.lang.psi.JetNamedFunction
|
||||
@@ -61,13 +59,14 @@ import org.jetbrains.jet.lang.psi.JetExpressionCodeFragment
|
||||
import org.jetbrains.jet.plugin.caches.resolve.getAnalysisResults
|
||||
import org.jetbrains.jet.lang.psi.JetCodeFragment
|
||||
import org.jetbrains.jet.lang.psi.JetImportList
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
import org.jetbrains.jet.lang.psi.codeFragmentUtil.setSkipVisibilityCheck
|
||||
import org.jetbrains.jet.lang.psi.JetBlockCodeFragment
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.Status
|
||||
|
||||
object KotlinEvaluationBuilder: EvaluatorBuilder {
|
||||
override fun build(codeFragment: PsiElement, position: SourcePosition?): ExpressionEvaluator {
|
||||
if (codeFragment !is JetExpressionCodeFragment || position == null) {
|
||||
if (codeFragment !is JetCodeFragment || position == null) {
|
||||
return EvaluatorBuilderImpl.getInstance()!!.build(codeFragment, position)
|
||||
}
|
||||
|
||||
@@ -78,11 +77,11 @@ object KotlinEvaluationBuilder: EvaluatorBuilder {
|
||||
codeFragment.addImportsFromString("import $packageName.*")
|
||||
}
|
||||
}
|
||||
return ExpressionEvaluatorImpl(KotlinEvaluator(codeFragment as JetExpressionCodeFragment, position))
|
||||
return ExpressionEvaluatorImpl(KotlinEvaluator(codeFragment as JetCodeFragment, position))
|
||||
}
|
||||
}
|
||||
|
||||
class KotlinEvaluator(val codeFragment: JetExpressionCodeFragment,
|
||||
class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
||||
val sourcePosition: SourcePosition
|
||||
) : Evaluator {
|
||||
override fun evaluate(context: EvaluationContextImpl): Any? {
|
||||
@@ -214,7 +213,7 @@ package packageForDebugger
|
||||
|
||||
private val packageInternalName = PackageClassUtils.getPackageClassFqName(FqName("packageForDebugger")).asString().replace(".", "/")
|
||||
|
||||
private fun createFileForDebugger(codeFragment: JetExpressionCodeFragment,
|
||||
private fun createFileForDebugger(codeFragment: JetCodeFragment,
|
||||
extractedFunction: JetNamedFunction
|
||||
): JetFile {
|
||||
var fileText = template.replace("!IMPORT_LIST!",
|
||||
@@ -247,11 +246,19 @@ fun addImportsToFile(newImportList: JetImportList?, tmpFile: JetFile) {
|
||||
}
|
||||
}
|
||||
|
||||
fun addDebugExpressionBeforeContextElement(debugExpression: JetExpression, contextElement: PsiElement): JetExpression? {
|
||||
fun addDebugExpressionBeforeContextElement(codeFragment: JetCodeFragment, contextElement: PsiElement): JetExpression? {
|
||||
val parent = contextElement.getParent()
|
||||
if (parent == null) return null
|
||||
|
||||
parent.addBefore(JetPsiFactory.createNewLine(contextElement.getProject()), contextElement)
|
||||
|
||||
val debugExpression = when(codeFragment) {
|
||||
is JetExpressionCodeFragment -> codeFragment.getExpression()
|
||||
is JetBlockCodeFragment -> codeFragment.getBlock()
|
||||
else -> null
|
||||
}
|
||||
if (debugExpression == null) return null
|
||||
|
||||
val newDebugExpression = parent.addBefore(debugExpression, contextElement)
|
||||
if (newDebugExpression == null) return null
|
||||
|
||||
@@ -261,14 +268,12 @@ fun addDebugExpressionBeforeContextElement(debugExpression: JetExpression, conte
|
||||
}
|
||||
|
||||
private fun getFunctionForExtractedFragment(
|
||||
codeFragment: JetExpressionCodeFragment,
|
||||
codeFragment: JetCodeFragment,
|
||||
breakpointFile: PsiFile,
|
||||
breakpointLine: Int
|
||||
): JetNamedFunction? {
|
||||
return ApplicationManager.getApplication()?.runReadAction(object: Computable<JetNamedFunction> {
|
||||
override fun compute(): JetNamedFunction? {
|
||||
val project = codeFragment.getProject()
|
||||
|
||||
val originalFile = breakpointFile as JetFile
|
||||
|
||||
val lineStart = CodeInsightUtils.getStartLineOffset(originalFile, breakpointLine)
|
||||
@@ -284,8 +289,7 @@ private fun getFunctionForExtractedFragment(
|
||||
|
||||
addImportsToFile(codeFragment.importsAsImportList(), tmpFile)
|
||||
|
||||
val debugExpression = JetPsiFactory.createExpression(project, codeFragment.getText())
|
||||
val newDebugExpression = addDebugExpressionBeforeContextElement(debugExpression, contextElement)
|
||||
val newDebugExpression = addDebugExpressionBeforeContextElement(codeFragment, contextElement)
|
||||
if (newDebugExpression == null) return null
|
||||
|
||||
val nextSibling = tmpFile.getDeclarations().firstOrNull()
|
||||
@@ -293,7 +297,7 @@ private fun getFunctionForExtractedFragment(
|
||||
|
||||
val analysisResult = ExtractionData(tmpFile, Collections.singletonList(newDebugExpression), nextSibling).performAnalysis()
|
||||
if (analysisResult.status != Status.SUCCESS) {
|
||||
throw EvaluateExceptionUtil.createEvaluateException(analysisResult.messages.makeString("\n"))
|
||||
throw EvaluateExceptionUtil.createEvaluateException(analysisResult.messages.makeString(", "))
|
||||
}
|
||||
|
||||
val validationResult = analysisResult.descriptor!!.validate()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
LineBreakpoint created at collections.kt:5
|
||||
LineBreakpoint created at collections.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 !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! collections.CollectionsPackage
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
collections.kt:4
|
||||
collections.kt:5
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package collections
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val ar = intArray(1, 2, 100, 200)
|
||||
//Breakpoint!
|
||||
args.size
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
ar.map { if (it > 50) "big" else "small" }
|
||||
.filter { it == "small" }
|
||||
.size
|
||||
|
||||
// RESULT: 2: I
|
||||
@@ -0,0 +1,4 @@
|
||||
val a = 1
|
||||
a + args.size
|
||||
|
||||
// RESULT: 1: I
|
||||
+26
-8
@@ -33,14 +33,18 @@ import org.jetbrains.eval4j.Value
|
||||
import org.jetbrains.eval4j.ObjectValue
|
||||
import com.sun.jdi.ObjectReference
|
||||
import org.jetbrains.jet.lang.psi.JetCodeFragment
|
||||
import java.util.Collections
|
||||
|
||||
public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestCase() {
|
||||
fun doTest(path: String) {
|
||||
val fileContent = FileUtil.loadFile(File(path))
|
||||
val file = File(path)
|
||||
val fileContent = FileUtil.loadFile(file, true)
|
||||
val expressions = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileContent, "// EXPRESSION: ")
|
||||
val expectedResults = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileContent, "// RESULT: ")
|
||||
val expectedExpressionResults = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileContent, "// RESULT: ")
|
||||
assert(expressions.size == expectedExpressionResults.size, "Sizes of test directives are different")
|
||||
|
||||
assert(expressions.size == expectedResults.size, "Sizes of test directives are different")
|
||||
val blocks = findFilesWithBlocks(file).map { FileUtil.loadFile(it, true) }
|
||||
val expectedBlockResults = blocks.map { InTextDirectivesUtils.findStringWithPrefixes(it, "// RESULT: ") ?: throw AssertionError("Couldn't find expected result for block: $it") }
|
||||
|
||||
createDebugProcess(path)
|
||||
|
||||
@@ -48,13 +52,22 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
|
||||
val exceptions = linkedMapOf<String, Throwable>()
|
||||
for ((i, expression) in expressions.withIndices()) {
|
||||
try {
|
||||
evaluate(expression, expectedResults[i])
|
||||
evaluate(expression, CodeFragmentKind.EXPRESSION, expectedExpressionResults[i])
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
exceptions.put(expression, e)
|
||||
}
|
||||
}
|
||||
|
||||
for ((i, block) in blocks.withIndices()) {
|
||||
try {
|
||||
evaluate(block, CodeFragmentKind.CODE_BLOCK, expectedBlockResults[i])
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
exceptions.put(block, e)
|
||||
}
|
||||
}
|
||||
|
||||
if (!exceptions.empty) {
|
||||
for (exc in exceptions.values()) {
|
||||
exc.printStackTrace()
|
||||
@@ -65,6 +78,11 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun findFilesWithBlocks(mainFile: File): List<File> {
|
||||
val mainFileName = mainFile.getName()
|
||||
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)
|
||||
@@ -78,7 +96,7 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
|
||||
}
|
||||
}
|
||||
|
||||
private fun SuspendContextImpl.evaluate(expression: String, expectedResult: String) {
|
||||
private fun SuspendContextImpl.evaluate(text: String, codeFragmentKind: CodeFragmentKind, expectedResult: String) {
|
||||
try {
|
||||
val sourcePosition = ContextUtil.getSourcePosition(this)
|
||||
val contextElement = ContextUtil.getContextElement(sourcePosition)!!
|
||||
@@ -87,8 +105,8 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
|
||||
|
||||
val evaluator = DebuggerInvocationUtil.commitAndRunReadAction(getProject()) {
|
||||
EvaluatorBuilderImpl.build(TextWithImportsImpl(
|
||||
CodeFragmentKind.EXPRESSION,
|
||||
expression,
|
||||
codeFragmentKind,
|
||||
text,
|
||||
JetCodeFragment.getImportsForElement(contextElement),
|
||||
JetFileType.INSTANCE),
|
||||
contextElement,
|
||||
@@ -98,7 +116,7 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
|
||||
|
||||
val value = evaluator.evaluate(createEvaluationContext(this))
|
||||
val actualResult = value.asValue().asString()
|
||||
Assert.assertTrue("Evaluate expression returns wrong result for $expression:\nexpected = $expectedResult\nactual = $actualResult\n", expectedResult == actualResult)
|
||||
Assert.assertTrue("Evaluate expression returns wrong result for $text:\nexpected = $expectedResult\nactual = $actualResult\n", expectedResult == actualResult)
|
||||
}
|
||||
finally {
|
||||
resume(this)
|
||||
|
||||
Reference in New Issue
Block a user