From abe8bc6fda143101a6e5d63fbedd43503cbf61c9 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 5 Dec 2013 22:28:13 -0700 Subject: [PATCH] fix exception wrapping for Method.invoke and static initializers Method.invoke should initialize its class before invoking the method, throwing an ExceptionInInitializerError if it fails, without wrapping said error in an InvocationTargetException. Also, we must initialize ExceptionInInitializerError.exception when throwing instances from the VM, since OpenJDK's ExceptionInInitializerError.getCause uses the exception field, not the cause field. --- .../lang/ExceptionInInitializerError.java | 4 +- classpath/java/lang/reflect/Field.java | 6 +++ classpath/java/lang/reflect/Method.java | 2 + src/avian/classpath-common.h | 5 +- src/machine.cpp | 9 +++- test/Reflection.java | 50 +++++++++++++++++++ 6 files changed, 70 insertions(+), 6 deletions(-) diff --git a/classpath/java/lang/ExceptionInInitializerError.java b/classpath/java/lang/ExceptionInInitializerError.java index d5014e9a35..1b39d248d8 100644 --- a/classpath/java/lang/ExceptionInInitializerError.java +++ b/classpath/java/lang/ExceptionInInitializerError.java @@ -11,11 +11,11 @@ package java.lang; public class ExceptionInInitializerError extends Error { - private final Throwable cause2; + private final Throwable exception; public ExceptionInInitializerError(String message) { super(message); - cause2 = null; + exception = null; } public ExceptionInInitializerError() { diff --git a/classpath/java/lang/reflect/Field.java b/classpath/java/lang/reflect/Field.java index debabdb0f3..5bb0bc6081 100644 --- a/classpath/java/lang/reflect/Field.java +++ b/classpath/java/lang/reflect/Field.java @@ -84,6 +84,8 @@ public class Field extends AccessibleObject { throw new IllegalArgumentException(); } + Classes.initialize(vmField.class_); + switch (vmField.code) { case ByteField: return Byte.valueOf @@ -171,6 +173,8 @@ public class Field extends AccessibleObject { throw new IllegalArgumentException(); } + Classes.initialize(vmField.class_); + switch (vmField.code) { case ByteField: setPrimitive(target, vmField.code, vmField.offset, (Byte) value); @@ -235,6 +239,8 @@ public class Field extends AccessibleObject { throw new IllegalArgumentException(); } + Classes.initialize(vmField.class_); + switch (vmField.code) { case ByteField: case BooleanField: diff --git a/classpath/java/lang/reflect/Method.java b/classpath/java/lang/reflect/Method.java index 55c7744c13..339db3dbb0 100644 --- a/classpath/java/lang/reflect/Method.java +++ b/classpath/java/lang/reflect/Method.java @@ -81,6 +81,8 @@ public class Method extends AccessibleObject implements Member { } if (arguments.length == vmMethod.parameterCount) { + Classes.initialize(vmMethod.class_); + return invoke(vmMethod, instance, arguments); } else { throw new ArrayIndexOutOfBoundsException(); diff --git a/src/avian/classpath-common.h b/src/avian/classpath-common.h index 4265c48998..b2c5fb7d45 100644 --- a/src/avian/classpath-common.h +++ b/src/avian/classpath-common.h @@ -567,13 +567,14 @@ invoke(Thread* t, object method, object instance, object args) } } + initClass(t, methodClass(t, method)); + unsigned returnCode = methodReturnCode(t, method); THREAD_RESOURCE0(t, { if (t->exception) { - object exception = t->exception; t->exception = makeThrowable - (t, Machine::InvocationTargetExceptionType, 0, 0, exception); + (t, Machine::InvocationTargetExceptionType, 0, 0, t->exception); set(t, t->exception, InvocationTargetExceptionTarget, throwableCause(t, t->exception)); diff --git a/src/machine.cpp b/src/machine.cpp index cb3186c216..a338e9ac6b 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -4534,8 +4534,13 @@ postInitClass(Thread* t, object c) object exception = t->exception; t->exception = 0; - throwNew(t, Machine::ExceptionInInitializerErrorType, - static_cast(0), 0, exception); + exception = makeThrowable + (t, Machine::ExceptionInInitializerErrorType, 0, 0, exception); + + set(t, exception, ExceptionInInitializerErrorException, + throwableCause(t, exception)); + + throw_(t, exception); } else { classVmFlags(t, c) &= ~(NeedInitFlag | InitFlag); } diff --git a/test/Reflection.java b/test/Reflection.java index 764293f0af..cd75f68200 100644 --- a/test/Reflection.java +++ b/test/Reflection.java @@ -2,6 +2,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.InvocationTargetException; public class Reflection { public static boolean booleanMethod() { @@ -92,6 +93,10 @@ public class Reflection { expect(args[0] == String.class); } + public static void throwOOME() { + throw new OutOfMemoryError(); + } + public static void main(String[] args) throws Exception { innerClasses(); annotations(); @@ -130,5 +135,50 @@ public class Reflection { expect("[Ljava.lang.Class;".equals(array[0].getClass().getName())); expect(Class[].class == array[0].getClass()); expect(array.getClass().getComponentType() == array[0].getClass()); + + try { + Foo.class.getMethod("foo").invoke(null); + expect(false); + } catch (ExceptionInInitializerError e) { + expect(e.getCause() instanceof MyException); + } + + try { + Foo.class.getConstructor().newInstance(); + expect(false); + } catch (NoClassDefFoundError e) { + // cool + } + + try { + Foo.class.getField("foo").get(null); + expect(false); + } catch (NoClassDefFoundError e) { + // cool + } + + { Method m = Reflection.class.getMethod("throwOOME"); + try { + m.invoke(null); + } catch(Throwable t) { + expect(t.getClass() == InvocationTargetException.class); + } + } } } + +class Foo { + static { + if (true) throw new MyException(); + } + + public Foo() { } + + public static int foo; + + public static void foo() { + // ignore + } +} + +class MyException extends RuntimeException { }