1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import java.lang.reflect.Method;
18 import java.util.ArrayList;
19 import java.util.Base64;
20 import java.util.LinkedList;
21 
22 public class Main {
23   /**
24    * NB This test cannot be run on the RI.
25    * TODO We should make this run on the RI.
26    */
27 
28   private static final String LISTENER_LOCATION =
29       System.getenv("DEX_LOCATION") + "/980-redefine-object-ex.jar";
30 
31   private static Method doEnableReporting;
32   private static Method doDisableReporting;
33 
DisableReporting()34   private static void DisableReporting() {
35     if (doDisableReporting == null) {
36       return;
37     }
38     try {
39       doDisableReporting.invoke(null);
40     } catch (Exception e) {
41       throw new Error("Unable to disable reporting!");
42     }
43   }
44 
EnableReporting()45   private static void EnableReporting() {
46     if (doEnableReporting == null) {
47       return;
48     }
49     try {
50       doEnableReporting.invoke(null);
51     } catch (Exception e) {
52       throw new Error("Unable to enable reporting!");
53     }
54   }
55 
main(String[] args)56   public static void main(String[] args) {
57     doTest();
58   }
59 
ensureTestWatcherInitialized()60   private static void ensureTestWatcherInitialized() {
61     try {
62       // Make sure the TestWatcher class can be found from the Object <init> function.
63       addToBootClassLoader(LISTENER_LOCATION);
64       // Load TestWatcher from the bootclassloader and make sure it is initialized.
65       Class<?> testwatcher_class = Class.forName("art.test.TestWatcher", true, null);
66       doEnableReporting = testwatcher_class.getDeclaredMethod("EnableReporting");
67       doDisableReporting = testwatcher_class.getDeclaredMethod("DisableReporting");
68     } catch (Exception e) {
69       throw new Error("Exception while making testwatcher", e);
70     }
71   }
72 
73   // NB This function will cause 2 objects of type "Ljava/nio/HeapCharBuffer;" and
74   // "Ljava/nio/HeapCharBuffer;" to be allocated each time it is called.
safePrintln(Object o)75   private static void safePrintln(Object o) {
76     DisableReporting();
77     System.out.println("\t" + o);
78     EnableReporting();
79   }
80 
throwFrom(int depth)81   private static void throwFrom(int depth) throws Exception {
82     if (depth <= 0) {
83       throw new Exception("Throwing the exception");
84     } else {
85       throwFrom(depth - 1);
86     }
87   }
88 
doTest()89   public static void doTest() {
90     safePrintln("Initializing and loading the TestWatcher class that will (eventually) be " +
91                 "notified of object allocations");
92     // Make sure the TestWatcher class is initialized before we do anything else.
93     ensureTestWatcherInitialized();
94     safePrintln("Allocating an j.l.Object before redefining Object class");
95     // Make sure these aren't shown.
96     Object o = new Object();
97     safePrintln("Allocating a Transform before redefining Object class");
98     Transform t = new Transform();
99 
100     // Redefine the Object Class.
101     safePrintln("Redefining the Object class to add a hook into the <init> method");
102     addMemoryTrackingCall(Object.class, Thread.currentThread());
103 
104     safePrintln("Allocating an j.l.Object after redefining Object class");
105     Object o2 = new Object();
106     safePrintln("Allocating a Transform after redefining Object class");
107     Transform t2 = new Transform();
108 
109     // This shouldn't cause the Object constructor to be run.
110     safePrintln("Allocating an int[] after redefining Object class");
111     int[] abc = new int[12];
112 
113     // Try adding stuff to an array list.
114     safePrintln("Allocating an array list");
115     ArrayList<Object> al = new ArrayList<>();
116     safePrintln("Adding a bunch of stuff to the array list");
117     al.add(new Object());
118     al.add(new Object());
119     al.add(o2);
120     al.add(o);
121     al.add(t);
122     al.add(t2);
123     al.add(new Transform());
124 
125     // Try adding stuff to a LinkedList
126     safePrintln("Allocating a linked list");
127     LinkedList<Object> ll = new LinkedList<>();
128     safePrintln("Adding a bunch of stuff to the linked list");
129     ll.add(new Object());
130     ll.add(new Object());
131     ll.add(o2);
132     ll.add(o);
133     ll.add(t);
134     ll.add(t2);
135     ll.add(new Transform());
136 
137     // Try making an exception.
138     safePrintln("Throwing from down 4 stack frames");
139     try {
140       throwFrom(4);
141     } catch (Exception e) {
142       safePrintln("Exception caught.");
143     }
144 
145     safePrintln("Finishing test!");
146   }
147 
148   // This is from 929-search/search.cc
addToBootClassLoader(String s)149   private static native void addToBootClassLoader(String s);
150   // This is from 980-redefine-object/redef_object.cc
151   // It will add a call to Lart/test/TestWatcher;->NotifyConstructed()V in the Object <init>()V
152   // function.
addMemoryTrackingCall(Class c, Thread thr)153   private static native void addMemoryTrackingCall(Class c, Thread thr);
154 }
155