/*
* Copyright (C) 2017 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.
*/
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Base64;
import java.util.LinkedList;
public class Main {
/**
* NB This test cannot be run on the RI.
* TODO We should make this run on the RI.
*/
private static final String LISTENER_LOCATION =
System.getenv("DEX_LOCATION") + "/980-redefine-object-ex.jar";
private static Method doEnableReporting;
private static Method doDisableReporting;
private static void DisableReporting() {
if (doDisableReporting == null) {
return;
}
try {
doDisableReporting.invoke(null);
} catch (Exception e) {
throw new Error("Unable to disable reporting!");
}
}
private static void EnableReporting() {
if (doEnableReporting == null) {
return;
}
try {
doEnableReporting.invoke(null);
} catch (Exception e) {
throw new Error("Unable to enable reporting!");
}
}
public static void main(String[] args) {
doTest();
}
private static void ensureTestWatcherInitialized() {
try {
// Make sure the TestWatcher class can be found from the Object <init> function.
addToBootClassLoader(LISTENER_LOCATION);
// Load TestWatcher from the bootclassloader and make sure it is initialized.
Class<?> testwatcher_class = Class.forName("art.test.TestWatcher", true, null);
doEnableReporting = testwatcher_class.getDeclaredMethod("EnableReporting");
doDisableReporting = testwatcher_class.getDeclaredMethod("DisableReporting");
} catch (Exception e) {
throw new Error("Exception while making testwatcher", e);
}
}
// NB This function will cause 2 objects of type "Ljava/nio/HeapCharBuffer;" and
// "Ljava/nio/HeapCharBuffer;" to be allocated each time it is called.
private static void safePrintln(Object o) {
DisableReporting();
System.out.println("\t" + o);
EnableReporting();
}
private static void throwFrom(int depth) throws Exception {
if (depth <= 0) {
throw new Exception("Throwing the exception");
} else {
throwFrom(depth - 1);
}
}
public static void doTest() {
safePrintln("Initializing and loading the TestWatcher class that will (eventually) be " +
"notified of object allocations");
// Make sure the TestWatcher class is initialized before we do anything else.
ensureTestWatcherInitialized();
safePrintln("Allocating an j.l.Object before redefining Object class");
// Make sure these aren't shown.
Object o = new Object();
safePrintln("Allocating a Transform before redefining Object class");
Transform t = new Transform();
// Redefine the Object Class.
safePrintln("Redefining the Object class to add a hook into the <init> method");
addMemoryTrackingCall(Object.class, Thread.currentThread());
safePrintln("Allocating an j.l.Object after redefining Object class");
Object o2 = new Object();
safePrintln("Allocating a Transform after redefining Object class");
Transform t2 = new Transform();
// This shouldn't cause the Object constructor to be run.
safePrintln("Allocating an int[] after redefining Object class");
int[] abc = new int[12];
// Try adding stuff to an array list.
safePrintln("Allocating an array list");
ArrayList<Object> al = new ArrayList<>();
safePrintln("Adding a bunch of stuff to the array list");
al.add(new Object());
al.add(new Object());
al.add(o2);
al.add(o);
al.add(t);
al.add(t2);
al.add(new Transform());
// Try adding stuff to a LinkedList
safePrintln("Allocating a linked list");
LinkedList<Object> ll = new LinkedList<>();
safePrintln("Adding a bunch of stuff to the linked list");
ll.add(new Object());
ll.add(new Object());
ll.add(o2);
ll.add(o);
ll.add(t);
ll.add(t2);
ll.add(new Transform());
// Try making an exception.
safePrintln("Throwing from down 4 stack frames");
try {
throwFrom(4);
} catch (Exception e) {
safePrintln("Exception caught.");
}
safePrintln("Finishing test!");
}
// This is from 929-search/search.cc
private static native void addToBootClassLoader(String s);
// This is from 980-redefine-object/redef_object.cc
// It will add a call to Lart/test/TestWatcher;->NotifyConstructed()V in the Object <init>()V
// function.
private static native void addMemoryTrackingCall(Class c, Thread thr);
}