lin
2025-07-31 065ea569db06206874bbfa18eb25ff6121aec09b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
 * 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);
}