c6339cb8a2
8013527: calling MethodHandles.lookup on itself leads to errors Co-authored-by: Peter Levart <plevart@openjdk.org> Co-authored-by: Claes Redestad <redestad@openjdk.org> Co-authored-by: Mandy Chung <mchung@openjdk.org> Reviewed-by: mcimadamore, plevart, egahlin, redestad, cjplummer, alanb
436 lines
20 KiB
Java
436 lines
20 KiB
Java
/*
|
|
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
/*
|
|
* @test
|
|
* @summary Tests for exceptions
|
|
* @bug 8198801 8212167 8210527
|
|
* @modules jdk.compiler/com.sun.tools.javac.api
|
|
* jdk.compiler/com.sun.tools.javac.main
|
|
* jdk.jdeps/com.sun.tools.javap
|
|
* @library /tools/lib
|
|
* @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
|
|
* @build KullaTesting TestingInputStream Compiler
|
|
* @run testng ExceptionsTest
|
|
*/
|
|
|
|
import java.io.IOException;
|
|
import java.io.PrintWriter;
|
|
import java.io.StringWriter;
|
|
import jdk.jshell.EvalException;
|
|
import jdk.jshell.JShellException;
|
|
import jdk.jshell.Snippet;
|
|
import jdk.jshell.SnippetEvent;
|
|
import jdk.jshell.UnresolvedReferenceException;
|
|
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
|
|
import org.testng.annotations.Test;
|
|
|
|
import static org.testng.Assert.*;
|
|
|
|
@Test
|
|
public class ExceptionsTest extends KullaTesting {
|
|
|
|
private final Compiler compiler = new Compiler();
|
|
private final Path outDir = Paths.get("test_class_path");
|
|
|
|
public void throwUncheckedException() {
|
|
String message = "error_message";
|
|
SnippetEvent cr = assertEvalException("throw new RuntimeException(\"" + message + "\");");
|
|
assertExceptionMatch(cr,
|
|
new ExceptionInfo(RuntimeException.class, message,
|
|
newStackTraceElement("", "", cr.snippet(), 1)));
|
|
}
|
|
|
|
public void throwCheckedException() {
|
|
String message = "error_message";
|
|
SnippetEvent cr = assertEvalException("throw new Exception(\"" + message + "\");");
|
|
assertExceptionMatch(cr,
|
|
new ExceptionInfo(Exception.class, message,
|
|
newStackTraceElement("", "", cr.snippet(), 1)));
|
|
}
|
|
|
|
public void throwFromStaticMethodOfClass() {
|
|
String message = "error_message";
|
|
Snippet s1 = methodKey(assertEval("void f() { throw new RuntimeException(\"" + message + "\"); }"));
|
|
Snippet s2 = classKey(assertEval("class A { static void g() { f(); } }"));
|
|
SnippetEvent cr3 = assertEvalException("A.g();");
|
|
assertExceptionMatch(cr3,
|
|
new ExceptionInfo(RuntimeException.class, message,
|
|
newStackTraceElement("", "f", s1, 1),
|
|
newStackTraceElement("A", "g", s2, 1),
|
|
newStackTraceElement("", "", cr3.snippet(), 1)));
|
|
}
|
|
|
|
public void throwFromStaticMethodOfInterface() {
|
|
String message = "error_message";
|
|
Snippet s1 = methodKey(assertEval("void f() { throw new RuntimeException(\"" + message + "\"); }"));
|
|
Snippet s2 = classKey(assertEval("interface A { static void g() { f(); } }"));
|
|
SnippetEvent cr3 = assertEvalException("A.g();");
|
|
assertExceptionMatch(cr3,
|
|
new ExceptionInfo(RuntimeException.class, message,
|
|
newStackTraceElement("", "f", s1, 1),
|
|
newStackTraceElement("A", "g", s2, 1),
|
|
newStackTraceElement("", "", cr3.snippet(), 1)));
|
|
}
|
|
|
|
public void throwChained() {
|
|
String message1 = "error_message1";
|
|
String message2 = "error_message2";
|
|
Snippet s1 = methodKey(assertEval("void p() throws Exception { ((String) null).toString(); }"));
|
|
Snippet s2 = methodKey(assertEval("void n() throws Exception { try { p(); } catch (Exception ex) { throw new java.io.IOException(\"" + message2 + "\", ex); }}"));
|
|
Snippet s3 = methodKey(assertEval("void m() {\n"
|
|
+ "try { n(); }\n"
|
|
+ "catch (Exception ex) {\n"
|
|
+ " throw new RuntimeException(\"" + message1 + "\", ex);\n"
|
|
+ "}}"));
|
|
SnippetEvent cr4 = assertEvalException("m();");
|
|
assertExceptionMatch(cr4,
|
|
new ExceptionInfo(RuntimeException.class, message1,
|
|
new ExceptionInfo(IOException.class, message2,
|
|
new ExceptionInfo(NullPointerException.class, null,
|
|
newStackTraceElement("", "p", s1, 1),
|
|
newStackTraceElement("", "n", s2, 1),
|
|
newStackTraceElement("", "m", s3, 2),
|
|
newStackTraceElement("", "", cr4.snippet(), 1)),
|
|
newStackTraceElement("", "n", s2, 1),
|
|
newStackTraceElement("", "m", s3, 2),
|
|
newStackTraceElement("", "", cr4.snippet(), 1)),
|
|
newStackTraceElement("", "m", s3, 4),
|
|
newStackTraceElement("", "", cr4.snippet(), 1)));
|
|
}
|
|
|
|
public void throwChainedUnresolved() {
|
|
String message1 = "error_message1";
|
|
String message2 = "error_message2";
|
|
Snippet s1 = methodKey(assertEval("void p() throws Exception { ((String) null).toString(); }"));
|
|
Snippet s2 = methodKey(assertEval("void n() throws Exception { try { p(); } catch (Exception ex) { throw new java.io.IOException(\"" + message2 + "\", ex); }}"));
|
|
Snippet s3 = methodKey(assertEval("void m() {\n"
|
|
+ "try { n(); }\n"
|
|
+ "catch (Exception ex) {\n"
|
|
+ " throw new RuntimeException(\"" + message1 + "\", ex);\n"
|
|
+ "}}"));
|
|
getState().drop(s1);
|
|
SnippetEvent cr4 = assertEvalException("m();");
|
|
assertExceptionMatch(cr4,
|
|
new ExceptionInfo(RuntimeException.class, message1,
|
|
new UnresolvedExceptionInfo(s2,
|
|
newStackTraceElement("", "n", s2, 1),
|
|
newStackTraceElement("", "m", s3, 2),
|
|
newStackTraceElement("", "", cr4.snippet(), 1)),
|
|
newStackTraceElement("", "m", s3, 4),
|
|
newStackTraceElement("", "", cr4.snippet(), 1)));
|
|
}
|
|
|
|
public void throwFromConstructor() {
|
|
String message = "error_message";
|
|
Snippet s1 = methodKey(assertEval("void f() { throw new RuntimeException(\"" + message + "\"); }"));
|
|
Snippet s2 = classKey(assertEval("class A { A() { f(); } }"));
|
|
SnippetEvent cr3 = assertEvalException("new A();");
|
|
assertExceptionMatch(cr3,
|
|
new ExceptionInfo(RuntimeException.class, message,
|
|
newStackTraceElement("", "f", s1, 1),
|
|
newStackTraceElement("A", "<init>", s2, 1),
|
|
newStackTraceElement("", "", cr3.snippet(), 1)));
|
|
}
|
|
|
|
public void throwFromDefaultMethodOfInterface() {
|
|
String message = "error_message";
|
|
Snippet s1 = methodKey(assertEval("void f() { throw new RuntimeException(\"" + message + "\"); }"));
|
|
Snippet s2 = classKey(assertEval("interface A { default void g() { f(); } }"));
|
|
SnippetEvent cr3 = assertEvalException("new A() { }.g();");
|
|
assertExceptionMatch(cr3,
|
|
new ExceptionInfo(RuntimeException.class, message,
|
|
newStackTraceElement("", "f", s1, 1),
|
|
newStackTraceElement("A", "g", s2, 1),
|
|
newStackTraceElement("", "", cr3.snippet(), 1)));
|
|
}
|
|
|
|
public void throwFromLambda() {
|
|
String message = "lambda";
|
|
Snippet s1 = varKey(assertEval(
|
|
"Runnable run = () -> {\n" +
|
|
" throw new RuntimeException(\"" + message + "\");\n" +
|
|
"};"
|
|
));
|
|
SnippetEvent cr2 = assertEvalException("run.run();");
|
|
assertExceptionMatch(cr2,
|
|
new ExceptionInfo(RuntimeException.class, message,
|
|
newStackTraceElement("", "lambda$", s1, 2),
|
|
newStackTraceElement("", "", cr2.snippet(), 1)));
|
|
}
|
|
|
|
public void throwFromAnonymousClass() {
|
|
String message = "anonymous";
|
|
Snippet s1 = varKey(assertEval(
|
|
"Runnable run = new Runnable() {\n" +
|
|
" public void run() {\n"+
|
|
" throw new RuntimeException(\"" + message + "\");\n" +
|
|
" }\n" +
|
|
"};"
|
|
));
|
|
SnippetEvent cr2 = assertEvalException("run.run();");
|
|
assertExceptionMatch(cr2,
|
|
new ExceptionInfo(RuntimeException.class, message,
|
|
newStackTraceElement("1", "run", s1, 3),
|
|
newStackTraceElement("", "", cr2.snippet(), 1)));
|
|
}
|
|
|
|
public void throwFromLocalClass() {
|
|
String message = "local";
|
|
Snippet s1 = methodKey(assertEval(
|
|
"void f() {\n" +
|
|
" class A {\n" +
|
|
" void f() {\n"+
|
|
" throw new RuntimeException(\"" + message + "\");\n" +
|
|
" }\n" +
|
|
" }\n" +
|
|
" new A().f();\n" +
|
|
"}"
|
|
));
|
|
SnippetEvent cr2 = assertEvalException("f();");
|
|
assertExceptionMatch(cr2,
|
|
new ExceptionInfo(RuntimeException.class, message,
|
|
newStackTraceElement("1A", "f", s1, 4),
|
|
newStackTraceElement("", "f", s1, 7),
|
|
newStackTraceElement("", "", cr2.snippet(), 1)));
|
|
}
|
|
|
|
// test 8210527
|
|
public void throwFromWithoutSource() {
|
|
String message = "show this";
|
|
SnippetEvent se = assertEvalException("java.lang.reflect.Proxy.newProxyInstance(" +
|
|
"Thread.currentThread().getContextClassLoader(), new Class[] {}," +
|
|
"(p, m, a) -> { throw new IllegalStateException(\"" + message + "\"); }).hashCode()");
|
|
assertExceptionMatch(se,
|
|
new ExceptionInfo(IllegalStateException.class, message,
|
|
newStackTraceElement("", "lambda$do_it$$0", se.snippet(), 1),
|
|
new StackTraceElement("jdk.proxy1.$Proxy0", "hashCode", null, -1),
|
|
newStackTraceElement("", "", se.snippet(), 1)));
|
|
}
|
|
|
|
// test 8210527
|
|
public void throwFromNoSource() {
|
|
Path path = outDir.resolve("fail");
|
|
compiler.compile(path,
|
|
"package fail;\n" +
|
|
"public class Fail {\n" +
|
|
" static { int x = 1 / 0; }\n" +
|
|
"}\n");
|
|
addToClasspath(compiler.getPath(path));
|
|
SnippetEvent se = assertEvalException("Class.forName(\"fail.Fail\")");
|
|
assertExceptionMatch(se,
|
|
new ExceptionInfo(ExceptionInInitializerError.class, null,
|
|
new StackTraceElement("java.lang.Class", "forName0", "Class.java", -2),
|
|
new StackTraceElement("java.lang.Class", "forName", "Class.java", -2),
|
|
new StackTraceElement("java.lang.Class", "forName", "Class.java", -2),
|
|
newStackTraceElement("", "", se.snippet(), 1)));
|
|
}
|
|
|
|
// test 8212167
|
|
public void throwLineFormat1() {
|
|
SnippetEvent se = assertEvalException(
|
|
"if (true) { \n" +
|
|
" int x = 10; \n" +
|
|
" int y = 10 / 0;}"
|
|
);
|
|
assertExceptionMatch(se,
|
|
new ExceptionInfo(ArithmeticException.class, "/ by zero",
|
|
newStackTraceElement("", "", se.snippet(), 3)));
|
|
}
|
|
|
|
public void throwLineFormat3() {
|
|
Snippet sp = methodKey(assertEval(
|
|
"int p() \n" +
|
|
" { return 4/0; }"));
|
|
Snippet sm = methodKey(assertEval(
|
|
"int m(int x)\n" +
|
|
" \n" +
|
|
" {\n" +
|
|
" return p() + x; \n" +
|
|
" }"));
|
|
Snippet sn = methodKey(assertEval(
|
|
"int n(int x) {\n" +
|
|
" try {\n" +
|
|
" return m(x);\n" +
|
|
" }\n" +
|
|
" catch (Throwable ex) {\n" +
|
|
" throw new IllegalArgumentException( \"GOT:\", ex);\n" +
|
|
" }\n" +
|
|
" }"));
|
|
SnippetEvent se = assertEvalException("n(33);");
|
|
assertExceptionMatch(se,
|
|
new ExceptionInfo(IllegalArgumentException.class, null,
|
|
new ExceptionInfo(ArithmeticException.class, "/ by zero",
|
|
newStackTraceElement("", "p", sp, 2),
|
|
newStackTraceElement("", "m", sm, 4),
|
|
newStackTraceElement("", "n", sn, 3),
|
|
newStackTraceElement("", "", se.snippet(), 1)),
|
|
newStackTraceElement("", "n", sn, 6),
|
|
newStackTraceElement("", "", se.snippet(), 1)));
|
|
}
|
|
|
|
@Test(enabled = false) // TODO 8129427
|
|
public void outOfMemory() {
|
|
assertEval("import java.util.*;");
|
|
assertEval("List<byte[]> list = new ArrayList<>();");
|
|
assertExecuteException("while (true) { list.add(new byte[10000]); }", OutOfMemoryError.class);
|
|
}
|
|
|
|
public void stackOverflow() {
|
|
assertEval("void f() { f(); }");
|
|
assertExecuteException("f();", StackOverflowError.class);
|
|
}
|
|
|
|
private StackTraceElement newStackTraceElement(String className, String methodName, Snippet key, int lineNumber) {
|
|
return new StackTraceElement(className, methodName, "#" + key.id(), lineNumber);
|
|
}
|
|
|
|
private static class AnyExceptionInfo {
|
|
|
|
public final StackTraceElement[] stackTraceElements;
|
|
|
|
public AnyExceptionInfo(StackTraceElement... stackTraceElements) {
|
|
this.stackTraceElements = stackTraceElements.length == 0 ? null : stackTraceElements;
|
|
}
|
|
}
|
|
|
|
private static class UnresolvedExceptionInfo extends AnyExceptionInfo {
|
|
|
|
public final Snippet sn;
|
|
|
|
public UnresolvedExceptionInfo(Snippet sn, StackTraceElement... stackTraceElements) {
|
|
super(stackTraceElements);
|
|
this.sn = sn;
|
|
}
|
|
}
|
|
|
|
private static class ExceptionInfo extends AnyExceptionInfo {
|
|
|
|
public final Class<? extends Throwable> exception;
|
|
public final String message;
|
|
public final AnyExceptionInfo cause;
|
|
|
|
public ExceptionInfo(Class<? extends Throwable> exception, String message,
|
|
StackTraceElement... stackTraceElements) {
|
|
this(exception, message, null, stackTraceElements);
|
|
}
|
|
|
|
public ExceptionInfo(Class<? extends Throwable> exception, String message,
|
|
AnyExceptionInfo cause, StackTraceElement... stackTraceElements) {
|
|
super(stackTraceElements);
|
|
this.exception = exception;
|
|
this.message = message;
|
|
this.cause = cause;
|
|
}
|
|
}
|
|
|
|
private void assertExecuteException(String input, Class<? extends Throwable> exception) {
|
|
assertExceptionMatch(assertEvalException(input), new ExceptionInfo(exception, null));
|
|
}
|
|
|
|
private void assertExceptionMatch(SnippetEvent cr, ExceptionInfo exceptionInfo) {
|
|
assertExceptionMatch(cr.exception(), cr.snippet().source(), exceptionInfo);
|
|
}
|
|
|
|
private void assertExceptionMatch(Throwable exception, String source, ExceptionInfo exceptionInfo) {
|
|
assertNotNull(exception, "Expected exception was not thrown: " + exceptionInfo.exception);
|
|
if (exception instanceof EvalException) {
|
|
EvalException ex = (EvalException) exception;
|
|
String actualException = ex.getExceptionClassName();
|
|
String expectedException = exceptionInfo.exception.getCanonicalName();
|
|
assertEquals(actualException, expectedException,
|
|
String.format("Given \"%s\" expected exception: %s, got: %s%nStack trace:%n%s",
|
|
source, expectedException, actualException, getStackTrace(ex)));
|
|
if (exceptionInfo.message != null) {
|
|
assertEquals(ex.getMessage(), exceptionInfo.message,
|
|
String.format("Given \"%s\" expected message: %s, got: %s",
|
|
source, exceptionInfo.message, ex.getMessage()));
|
|
}
|
|
assertStackMatch(ex, source, exceptionInfo);
|
|
if (exceptionInfo.cause != null) {
|
|
assertAnyExceptionMatch(exception.getCause(), exceptionInfo.cause);
|
|
}
|
|
} else {
|
|
fail("Unexpected exception: " + exception + " or exceptionInfo: " + exceptionInfo);
|
|
}
|
|
}
|
|
|
|
private void assertStackMatch(JShellException exception, String source, AnyExceptionInfo exceptionInfo) {
|
|
if (exceptionInfo.stackTraceElements != null) {
|
|
assertStackTrace(exception.getStackTrace(), exceptionInfo.stackTraceElements,
|
|
String.format("Given \"%s\"%nStack trace:%n%s%n",
|
|
source, getStackTrace(exception)));
|
|
}
|
|
}
|
|
|
|
private void assertAnyExceptionMatch(Throwable exception, AnyExceptionInfo exceptionInfo) {
|
|
if (exceptionInfo instanceof ExceptionInfo) {
|
|
assertExceptionMatch(exception, "", (ExceptionInfo) exceptionInfo);
|
|
} else {
|
|
assertTrue(exceptionInfo instanceof UnresolvedExceptionInfo, "Bad exceptionInfo: " + exceptionInfo);
|
|
assertTrue(exception instanceof UnresolvedReferenceException,
|
|
"Expected UnresolvedReferenceException: " + exception);
|
|
UnresolvedExceptionInfo uei = (UnresolvedExceptionInfo) exceptionInfo;
|
|
UnresolvedReferenceException ure = (UnresolvedReferenceException) exception;
|
|
assertEquals(ure.getSnippet(), uei.sn);
|
|
assertStackMatch(ure, "", exceptionInfo);
|
|
}
|
|
}
|
|
|
|
private void assertStackTrace(StackTraceElement[] actual, StackTraceElement[] expected, String message) {
|
|
if (actual != expected) {
|
|
if (actual == null || expected == null) {
|
|
fail(message);
|
|
} else {
|
|
assertEquals(actual.length, expected.length, message + " : arrays do not have the same size");
|
|
for (int i = 0; i < actual.length; ++i) {
|
|
StackTraceElement actualElement = actual[i];
|
|
StackTraceElement expectedElement = expected[i];
|
|
assertEquals(actualElement.getClassName(), expectedElement.getClassName(), message + " : class names [" + i + "]");
|
|
String expectedMethodName = expectedElement.getMethodName();
|
|
if (expectedMethodName.startsWith("lambda$")) {
|
|
assertTrue(actualElement.getMethodName().startsWith("lambda$"), message + " : method names");
|
|
} else {
|
|
assertEquals(actualElement.getMethodName(), expectedElement.getMethodName(), message + " : method names [" + i + "]");
|
|
}
|
|
assertEquals(actualElement.getFileName(), expectedElement.getFileName(), message + " : file names [" + i + "]");
|
|
if (expectedElement.getLineNumber() >= 0) {
|
|
assertEquals(actualElement.getLineNumber(), expectedElement.getLineNumber(), message + " : line numbers [" + i + "]"
|
|
+ " -- actual: " + actualElement.getLineNumber() + ", expected: " + expectedElement.getLineNumber() +
|
|
" -- in: " + actualElement.getClassName());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private String getStackTrace(Throwable ex) {
|
|
StringWriter st = new StringWriter();
|
|
ex.printStackTrace(new PrintWriter(st));
|
|
return st.toString();
|
|
}
|
|
}
|