diff --git a/compiler/cli/src/org/jetbrains/jet/cli/jvm/repl/ReplInterpreter.java b/compiler/cli/src/org/jetbrains/jet/cli/jvm/repl/ReplInterpreter.java index f3588e791dd..8fe6c8df6db 100644 --- a/compiler/cli/src/org/jetbrains/jet/cli/jvm/repl/ReplInterpreter.java +++ b/compiler/cli/src/org/jetbrains/jet/cli/jvm/repl/ReplInterpreter.java @@ -276,7 +276,7 @@ public class ReplInterpreter { scriptInstance = scriptInstanceConstructor.newInstance(constructorArgs); } catch (Throwable e) { - return LineResult.error(Throwables.getStackTraceAsString(e.getCause())); + return LineResult.error(renderStackTrace(e.getCause())); } Field rvField = scriptClass.getDeclaredField("rv"); rvField.setAccessible(true); @@ -295,6 +295,27 @@ public class ReplInterpreter { } } + @NotNull + private static String renderStackTrace(@NotNull Throwable cause) { + StackTraceElement[] oldTrace = cause.getStackTrace(); + List newTrace = new ArrayList(); + boolean skip = true; + for (int i = oldTrace.length - 1; i >= 0; i--) { + StackTraceElement element = oldTrace[i]; + // All our code happens in the script constructor, and no reflection/native code happens in constructors. + // So we ignore everything in the stack trace until the first constructor + if (element.getMethodName().equals("")) { + skip = false; + } + if (!skip) { + newTrace.add(element); + } + } + Collections.reverse(newTrace); + cause.setStackTrace(newTrace.toArray(new StackTraceElement[newTrace.size()])); + return Throwables.getStackTraceAsString(cause); + } + @Nullable private ScriptDescriptor doAnalyze(@NotNull JetFile psiFile, @NotNull MessageCollector messageCollector) { WritableScope scope = new WritableScopeImpl( diff --git a/compiler/testData/repl/evaluationErrors.repl b/compiler/testData/repl/evaluationErrors.repl index eead2a71370..1cfc280cba8 100644 --- a/compiler/testData/repl/evaluationErrors.repl +++ b/compiler/testData/repl/evaluationErrors.repl @@ -1,5 +1,11 @@ >>> throw Exception("hi there") -substring: Exception: hi there +java.lang.Exception: hi there + at Line1.(Unknown Source) >>> fun foo() = 2 >>> foo() 2 +>>> fun bar() = throw AssertionError() +>>> bar() +java.lang.AssertionError + at Line4.bar(Unknown Source) + at Line5.(Unknown Source) diff --git a/compiler/tests/org/jetbrains/jet/repl/AbstractReplInterpreterTest.kt b/compiler/tests/org/jetbrains/jet/repl/AbstractReplInterpreterTest.kt index 7867b97a14a..d55f7c219f6 100644 --- a/compiler/tests/org/jetbrains/jet/repl/AbstractReplInterpreterTest.kt +++ b/compiler/tests/org/jetbrains/jet/repl/AbstractReplInterpreterTest.kt @@ -30,7 +30,6 @@ import org.junit.Assert private val START_PATTERN = Pattern.compile(">>>( *)(.*)$") private val INCOMPLETE_PATTERN = Pattern.compile("\\.\\.\\.( *)(.*)$") -private val SUBSTRING_PATTERN = Pattern.compile("substring: (.*)") private val INCOMPLETE_LINE_MESSAGE = "incomplete line" @@ -39,12 +38,7 @@ public abstract class AbstractReplInterpreterTest : UsefulTestCase() { System.setProperty("java.awt.headless", "true") } - private enum class MatchType { - EQUALS - SUBSTRING - } - - private data class OneLine(val code: String, val expected: String, val matchType: MatchType) + private data class OneLine(val code: String, val expected: String) private fun loadLines(file: File): List { val lines = ArrayDeque(file.readLines()) @@ -63,16 +57,9 @@ public abstract class AbstractReplInterpreterTest : UsefulTestCase() { if (lines.isNotEmpty()) { val nextLine = lines.peek()!! - val substringMatcher = SUBSTRING_PATTERN.matcher(nextLine) - if (substringMatcher.matches()) { - result.add(OneLine(code, substringMatcher.group(1)!!, MatchType.SUBSTRING)) - lines.poll() - continue - } - val incompleteMatcher = INCOMPLETE_PATTERN.matcher(nextLine) if (incompleteMatcher.matches()) { - result.add(OneLine(code, INCOMPLETE_LINE_MESSAGE, MatchType.EQUALS)) + result.add(OneLine(code, INCOMPLETE_LINE_MESSAGE)) continue } } @@ -82,7 +69,7 @@ public abstract class AbstractReplInterpreterTest : UsefulTestCase() { value.appendln(lines.poll()!!) } - result.add(OneLine(code, value.toString(), MatchType.EQUALS)) + result.add(OneLine(code, value.toString())) } return result @@ -92,26 +79,19 @@ public abstract class AbstractReplInterpreterTest : UsefulTestCase() { val configuration = JetTestUtils.compilerConfigurationForTests(ConfigurationKind.ALL, TestJdkKind.FULL_JDK) val repl = ReplInterpreter(getTestRootDisposable()!!, configuration) - for ((code, expected, matchType) in loadLines(File(path))) { - val expectedString = StringUtil.convertLineSeparators(expected).replaceFirst("\n$", "") - + for ((code, expected) in loadLines(File(path))) { val lineResult = repl.eval(code) val actual = when (lineResult.getType()) { ReplInterpreter.LineResultType.SUCCESS -> lineResult.getValue()?.toString() ?: "" ReplInterpreter.LineResultType.ERROR -> lineResult.getErrorText() ReplInterpreter.LineResultType.INCOMPLETE -> INCOMPLETE_LINE_MESSAGE } - val actualString = StringUtil.convertLineSeparators(actual).replaceFirst("\n$", "") - when (matchType) { - MatchType.EQUALS -> { - Assert.assertEquals("After evaluation of: $code", expectedString, actualString) - } - MatchType.SUBSTRING -> { - Assert.assertTrue("Evaluated result must contain substring: $expectedString, actual: $actualString, line: $code", - expectedString in actualString) - } - } + Assert.assertEquals( + "After evaluation of: $code", + StringUtil.convertLineSeparators(expected).replaceFirst("\n$", ""), + StringUtil.convertLineSeparators(actual).replaceFirst("\n$", "") + ) } } }