blob: f18d5ef3d6abf9594fca27452fd308e3ac2a20a1 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package art;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.HashSet;
public class Test989 {
static boolean PRINT_STACK_TRACE = false;
static Set<Method> testMethods = new HashSet<>();
static MethodTracer currentTracer = new MethodTracer() {
public void methodEntry(Object o) { return; }
public void methodExited(Object o, boolean e, Object r) { return; }
};
private static boolean DISABLE_TRACING = false;
static {
try {
testMethods.add(Test989.class.getDeclaredMethod("doNothing"));
testMethods.add(Test989.class.getDeclaredMethod("doNothingNative"));
testMethods.add(Test989.class.getDeclaredMethod("throwA"));
testMethods.add(Test989.class.getDeclaredMethod("throwANative"));
testMethods.add(Test989.class.getDeclaredMethod("returnFloat"));
testMethods.add(Test989.class.getDeclaredMethod("returnFloatNative"));
testMethods.add(Test989.class.getDeclaredMethod("returnDouble"));
testMethods.add(Test989.class.getDeclaredMethod("returnDoubleNative"));
testMethods.add(Test989.class.getDeclaredMethod("returnValue"));
testMethods.add(Test989.class.getDeclaredMethod("returnValueNative"));
testMethods.add(Test989.class.getDeclaredMethod("acceptValue", Object.class));
testMethods.add(Test989.class.getDeclaredMethod("acceptValueNative", Object.class));
testMethods.add(Test989.class.getDeclaredMethod("tryCatchExit"));
} catch (Exception e) {
throw new Error("Bad static!", e);
}
}
// Disables tracing only on RI. Used to work around an annoying piece of behavior where in the
// RI throwing an exception in an exit hook causes the exit hook to be re-executed. This leads
// to an infinite loop on the RI.
private static void disableTraceForRI() {
if (!System.getProperty("java.vm.name").equals("Dalvik")) {
Trace.disableTracing(Thread.currentThread());
}
}
private static String getInfo(Object m, boolean exception, Object result) {
String out = m.toString() + " returned ";
if (exception) {
out += "<exception>";
} else {
out += result;
}
return out;
}
public static interface MethodTracer {
public void methodEntry(Object m);
public void methodExited(Object m, boolean exception, Object result);
public default Class<?> entryException() { return null; }
public default Class<?> exitException() { return null; }
}
public static class NormalTracer implements MethodTracer {
public void methodEntry(Object m) {
if (testMethods.contains(m)) {
System.out.println("Normal: Entering " + m);
}
}
public void methodExited(Object m, boolean exception, Object result) {
if (testMethods.contains(m)) {
System.out.println("Normal: Leaving " + getInfo(m, exception, result));
}
}
}
public static class ThrowEnterTracer implements MethodTracer {
public void methodEntry(Object m) {
if (testMethods.contains(m)) {
System.out.println("ThrowEnter: Entering " + m);
throw new ErrorB("Throwing error while entering " + m);
}
}
public void methodExited(Object m, boolean exception, Object result) {
if (testMethods.contains(m)) {
System.out.println("ThrowEnter: Leaving " + getInfo(m, exception, result));
}
}
public Class<?> entryException() { return ErrorB.class; }
}
public static class ThrowExitTracer implements MethodTracer {
public void methodEntry(Object m) {
if (testMethods.contains(m)) {
System.out.println("ThrowExit: Entering " + m);
}
}
public void methodExited(Object m, boolean exception, Object result) {
if (testMethods.contains(m)) {
// The RI goes into an infinite loop if we throw exceptions in an ExitHook. See
// disableTraceForRI for explanation.
disableTraceForRI();
System.out.println("ThrowExit: Leaving " + getInfo(m, exception, result));
throw new ErrorB("Throwing error while exit " + getInfo(m, exception, result));
}
}
public Class<?> exitException() { return ErrorB.class; }
}
public static class ThrowBothTracer implements MethodTracer {
public void methodEntry(Object m) {
if (testMethods.contains(m)) {
System.out.println("ThrowBoth: Entering " + m);
throw new ErrorB("Throwing error while entering " + m);
}
}
public void methodExited(Object m, boolean exception, Object result) {
if (testMethods.contains(m)) {
// The RI goes into an infinite loop if we throw exceptions in an ExitHook. See
// disableTraceForRI for explanation.
disableTraceForRI();
System.out.println("ThrowBoth: Leaving " + getInfo(m, exception, result));
throw new ErrorC("Throwing error while exit " + getInfo(m, exception, result));
}
}
public Class<?> entryException() { return ErrorB.class; }
public Class<?> exitException() { return ErrorC.class; }
}
public static class ForceGCTracer implements MethodTracer {
public void methodEntry(Object m) {
if (System.getProperty("java.vm.name").equals("Dalvik") &&
testMethods.contains(m)) {
Runtime.getRuntime().gc();
}
}
public void methodExited(Object m, boolean exception, Object result) {
if (System.getProperty("java.vm.name").equals("Dalvik") &&
testMethods.contains(m)) {
Runtime.getRuntime().gc();
}
}
}
private static void maybeDisableTracing() throws Exception {
if (DISABLE_TRACING) {
Trace.disableTracing(Thread.currentThread());
}
}
public static void baseNotifyMethodEntry(Object o) {
currentTracer.methodEntry(o);
}
public static void baseNotifyMethodExit(Object o, boolean exception, Object res) {
currentTracer.methodExited(o, exception, res);
}
private static void setupTracing() throws Exception {
Trace.enableMethodTracing(
Test989.class,
Test989.class.getDeclaredMethod("baseNotifyMethodEntry", Object.class),
Test989.class.getDeclaredMethod(
"baseNotifyMethodExit", Object.class, Boolean.TYPE, Object.class),
Thread.currentThread());
}
private static void setEntry(MethodTracer type) throws Exception {
if (DISABLE_TRACING || !System.getProperty("java.vm.name").equals("Dalvik")) {
Trace.disableTracing(Thread.currentThread());
setupTracing();
}
currentTracer = type;
}
private static String testDescription(MethodTracer type, Runnable test) {
return "test[" + type.getClass() + ", " + test.getClass() + "]";
}
private static Class<?> getExpectedError(MethodTracer t, MyRunnable r) {
if (t.exitException() != null) {
return t.exitException();
} else if (t.entryException() != null) {
return t.entryException();
} else {
return r.expectedThrow();
}
}
private static void doTest(MethodTracer type, MyRunnable test) throws Exception {
Class<?> expected = getExpectedError(type, test);
setEntry(type);
try {
test.run();
// Disabling method tracing just makes this test somewhat faster.
maybeDisableTracing();
if (expected == null) {
System.out.println(
"Received no exception as expected for " + testDescription(type, test) + ".");
return;
}
} catch (Error t) {
// Disabling method tracing just makes this test somewhat faster.
maybeDisableTracing();
if (expected == null) {
throw new Error("Unexpected error occured: " + t + " for " + testDescription(type, test), t);
} else if (!expected.isInstance(t)) {
throw new Error("Expected error of type " + expected + " not " + t +
" for " + testDescription(type, test), t);
} else {
System.out.println(
"Received expected error for " + testDescription(type, test) + " - " + t);
if (PRINT_STACK_TRACE) {
t.printStackTrace();
}
return;
}
}
System.out.println("Expected an error of type " + expected + " but got no exception for "
+ testDescription(type, test));
// throw new Error("Expected an error of type " + expected + " but got no exception for "
// + testDescription(type, test));
}
public static interface MyRunnable extends Runnable {
public default Class<?> expectedThrow() {
return null;
}
}
public static void run() throws Exception {
MyRunnable[] testCases = new MyRunnable[] {
new doNothingClass(),
new doNothingNativeClass(),
new throwAClass(),
new throwANativeClass(),
new returnValueClass(),
new returnValueNativeClass(),
new acceptValueClass(),
new acceptValueNativeClass(),
new tryCatchExitClass(),
new returnFloatClass(),
new returnFloatNativeClass(),
new returnDoubleClass(),
new returnDoubleNativeClass(),
};
MethodTracer[] tracers = new MethodTracer[] {
new NormalTracer(),
new ThrowEnterTracer(),
new ThrowExitTracer(),
new ThrowBothTracer(),
new ForceGCTracer(),
};
setupTracing();
for (MethodTracer t : tracers) {
for (MyRunnable r : testCases) {
doTest(t, r);
}
}
maybeDisableTracing();
System.out.println("Finished!");
Trace.disableTracing(Thread.currentThread());
}
private static final class throwAClass implements MyRunnable {
public void run() {
throwA();
}
@Override
public Class<?> expectedThrow() {
return ErrorA.class;
}
}
private static final class throwANativeClass implements MyRunnable {
public void run() {
throwANative();
}
@Override
public Class<?> expectedThrow() {
return ErrorA.class;
}
}
private static final class tryCatchExitClass implements MyRunnable {
public void run() {
tryCatchExit();
}
}
private static final class doNothingClass implements MyRunnable {
public void run() {
doNothing();
}
}
private static final class doNothingNativeClass implements MyRunnable {
public void run() {
doNothingNative();
}
}
private static final class acceptValueClass implements MyRunnable {
public void run() {
acceptValue(mkTestObject());
}
}
private static final class acceptValueNativeClass implements MyRunnable {
public void run() {
acceptValueNative(mkTestObject());
}
}
private static final class returnValueClass implements MyRunnable {
public void run() {
Object o = returnValue();
System.out.println("returnValue returned: " + o);
}
}
private static final class returnValueNativeClass implements MyRunnable {
public void run() {
Object o = returnValueNative();
System.out.println("returnValueNative returned: " + o);
}
}
private static final class returnFloatClass implements MyRunnable {
public void run() {
float d = returnFloat();
System.out.println("returnFloat returned: " + d);
}
}
private static final class returnFloatNativeClass implements MyRunnable {
public void run() {
float d = returnFloatNative();
System.out.println("returnFloatNative returned: " + d);
}
}
private static final class returnDoubleClass implements MyRunnable {
public void run() {
double d = returnDouble();
System.out.println("returnDouble returned: " + d);
}
}
private static final class returnDoubleNativeClass implements MyRunnable {
public void run() {
double d = returnDoubleNative();
System.out.println("returnDoubleNative returned: " + d);
}
}
private static class ErrorA extends Error {
private static final long serialVersionUID = 0;
public ErrorA(String s) { super(s); }
}
private static class ErrorB extends Error {
private static final long serialVersionUID = 1;
public ErrorB(String s) { super(s); }
}
private static class ErrorC extends Error {
private static final long serialVersionUID = 2;
public ErrorC(String s) { super(s); }
}
// Does nothing.
public static void doNothing() { }
public static void tryCatchExit() {
try {
Object o = mkTestObject();
return;
} catch (ErrorB b) {
System.out.println("ERROR: Caught " + b);
b.printStackTrace();
} catch (ErrorC c) {
System.out.println("ERROR: Caught " + c);
c.printStackTrace();
}
}
public static float returnFloat() {
return doGetFloat();
}
public static double returnDouble() {
return doGetDouble();
}
// Throws an ErrorA.
public static void throwA() {
doThrowA();
}
public static void doThrowA() {
throw new ErrorA("Throwing Error A");
}
static final class TestObject {
private int idx;
public TestObject(int v) {
this.idx = v;
}
@Override
public String toString() {
return "TestObject(" + idx + ")";
}
}
static int counter = 0;
public static Object mkTestObject() {
return new TestObject(counter++);
}
public static void printObject(Object o) {
System.out.println("Recieved " + o);
}
// Returns a newly allocated value.
public static Object returnValue() {
return mkTestObject();
}
public static void acceptValue(Object o) {
printObject(o);
}
public static float doGetFloat() {
return 1.618f;
}
public static double doGetDouble() {
return 3.14159628;
}
// Calls mkTestObject from native code and returns it.
public static native Object returnValueNative();
// Calls printObject from native code.
public static native void acceptValueNative(Object t);
public static native void doNothingNative();
public static native void throwANative();
public static native float returnFloatNative();
public static native double returnDoubleNative();
}