content: /*
   * 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 static art.Redefinition.doCommonClassRedefinition;
  
  import java.lang.reflect.Method;
  import java.util.ArrayList;
  import java.util.Base64;
  import java.util.LinkedList;
  
  public class Main {
  
    // TODO We should make this run on the RI.
    /**
     * This test cannot be run on the RI.
     */
    private static final byte[] CLASS_BYTES = new byte[0];
  
    // TODO It might be a good idea to replace this hard-coded Object definition with a
    // retransformation based test.
    /**
     * Base64 encoding of the following smali file.
     *
     *  .class public Ljava/lang/Object;
     *  .source "Object.java"
     *  # instance fields
     *  .field private transient shadow$_klass_:Ljava/lang/Class;
     *      .annotation system Ldalvik/annotation/Signature;
     *          value = {
     *              "Ljava/lang/Class",
     *              "<*>;"
     *          }
     *      .end annotation
     *  .end field
     *
     *  .field private transient shadow$_monitor_:I
     *  # direct methods
     *  .method public constructor <init>()V
     *      .registers 1
     *      .prologue
     *      invoke-static {p0}, Lart/test/TestWatcher;->NotifyConstructed(Ljava/lang/Object;)V
     *      return-void
     *  .end method
     *
     *  .method static identityHashCode(Ljava/lang/Object;)I
     *      .registers 7
     *      .prologue
     *      iget v0, p0, Ljava/lang/Object;->shadow$_monitor_:I
     *      const/high16 v3, -0x40000000    # -2.0f
     *      const/high16 v2, -0x80000000
     *      const v1, 0xfffffff
     *      const/high16 v4, -0x40000000    # -2.0f
     *      and-int/2addr v4, v0
     *      const/high16 v5, -0x80000000
     *      if-ne v4, v5, :cond_15
     *      const v4, 0xfffffff
     *      and-int/2addr v4, v0
     *      return v4
     *      :cond_15
     *      invoke-static {p0}, Ljava/lang/Object;->identityHashCodeNative(Ljava/lang/Object;)I
     *      move-result v4
     *      return v4
     *  .end method
     *
     *  .method private static native identityHashCodeNative(Ljava/lang/Object;)I
     *      .annotation build Ldalvik/annotation/optimization/FastNative;
     *      .end annotation
     *  .end method
     *
     *  .method private native internalClone()Ljava/lang/Object;
     *      .annotation build Ldalvik/annotation/optimization/FastNative;
     *      .end annotation
     *  .end method
     *
     *
     *  # virtual methods
     *  .method protected clone()Ljava/lang/Object;
     *      .registers 4
     *      .annotation system Ldalvik/annotation/Throws;
     *          value = {
     *              Ljava/lang/CloneNotSupportedException;
     *          }
     *      .end annotation
     *
     *      .prologue
     *      instance-of v0, p0, Ljava/lang/Cloneable;
     *      if-nez v0, :cond_2d
     *      new-instance v0, Ljava/lang/CloneNotSupportedException;
     *      new-instance v1, Ljava/lang/StringBuilder;
     *      invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V
     *      const-string/jumbo v2, "Class "
     *      invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
     *      move-result-object v1
     *      invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class;
     *      move-result-object v2
     *      invoke-virtual {v2}, Ljava/lang/Class;->getName()Ljava/lang/String;
     *      move-result-object v2
     *      invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
     *      move-result-object v1
     *      const-string/jumbo v2, " doesn\'t implement Cloneable"
     *      invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
     *      move-result-object v1
     *      invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
     *      move-result-object v1
     *      invoke-direct {v0, v1}, Ljava/lang/CloneNotSupportedException;-><init>(Ljava/lang/String;)V
     *      throw v0
     *      :cond_2d
     *      invoke-direct {p0}, Ljava/lang/Object;->internalClone()Ljava/lang/Object;
     *      move-result-object v0
     *      return-object v0
     *  .end method
     *
     *  .method public equals(Ljava/lang/Object;)Z
     *      .registers 3
     *      .prologue
     *      if-ne p0, p1, :cond_4
     *      const/4 v0, 0x1
     *      :goto_3
     *      return v0
     *      :cond_4
     *      const/4 v0, 0x0
     *      goto :goto_3
     *  .end method
     *
     *  .method protected finalize()V
     *      .registers 1
     *      .annotation system Ldalvik/annotation/Throws;
     *          value = {
     *              Ljava/lang/Throwable;
     *          }
     *      .end annotation
     *      .prologue
     *      return-void
     *  .end method
     *
     *  .method public final getClass()Ljava/lang/Class;
     *      .registers 2
     *      .annotation system Ldalvik/annotation/Signature;
     *          value = {
     *              "()",
     *              "Ljava/lang/Class",
     *              "<*>;"
     *          }
     *      .end annotation
     *      .prologue
     *      iget-object v0, p0, Ljava/lang/Object;->shadow$_klass_:Ljava/lang/Class;
     *      return-object v0
     *  .end method
     *
     *  .method public hashCode()I
     *      .registers 2
     *      .prologue
     *      invoke-static {p0}, Ljava/lang/Object;->identityHashCode(Ljava/lang/Object;)I
     *      move-result v0
     *      return v0
     *  .end method
     *
     *  .method public final native notify()V
     *      .annotation build Ldalvik/annotation/optimization/FastNative;
     *      .end annotation
     *  .end method
     *
     *  .method public final native notifyAll()V
     *      .annotation build Ldalvik/annotation/optimization/FastNative;
     *      .end annotation
     *  .end method
     *
     *  .method public toString()Ljava/lang/String;
     *      .registers 3
     *      .prologue
     *      new-instance v0, Ljava/lang/StringBuilder;
     *      invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V
     *      invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class;
     *      move-result-object v1
     *      invoke-virtual {v1}, Ljava/lang/Class;->getName()Ljava/lang/String;
     *      move-result-object v1
     *      invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
     *      move-result-object v0
     *      const-string/jumbo v1, "@"
     *      invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
     *      move-result-object v0
     *      invoke-virtual {p0}, Ljava/lang/Object;->hashCode()I
     *      move-result v1
     *      invoke-static {v1}, Ljava/lang/Integer;->toHexString(I)Ljava/lang/String;
     *      move-result-object v1
     *      invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
     *      move-result-object v0
     *      invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
     *      move-result-object v0
     *      return-object v0
     *  .end method
     *
     *  .method public final native wait()V
     *      .annotation system Ldalvik/annotation/Throws;
     *          value = {
     *              Ljava/lang/InterruptedException;
     *          }
     *      .end annotation
     *
     *      .annotation build Ldalvik/annotation/optimization/FastNative;
     *      .end annotation
     *  .end method
     *
     *  .method public final wait(J)V
     *      .registers 4
     *      .annotation system Ldalvik/annotation/Throws;
     *          value = {
     *              Ljava/lang/InterruptedException;
     *          }
     *      .end annotation
     *      .prologue
     *      const/4 v0, 0x0
     *      invoke-virtual {p0, p1, p2, v0}, Ljava/lang/Object;->wait(JI)V
     *      return-void
     *  .end method
     *
     *  .method public final native wait(JI)V
     *      .annotation system Ldalvik/annotation/Throws;
     *          value = {
     *              Ljava/lang/InterruptedException;
     *          }
     *      .end annotation
     *
     *      .annotation build Ldalvik/annotation/optimization/FastNative;
     *      .end annotation
     *  .end method
     */
    private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
        "ZGV4CjAzNQDUlMR9j03MYuOKekKs2p7zJzu2IfDb7RlMCgAAcAAAAHhWNBIAAAAAAAAAAIgJAAA6" +
        "AAAAcAAAABEAAABYAQAADQAAAJwBAAACAAAAOAIAABYAAABIAgAAAQAAAPgCAAA0BwAAGAMAABgD" +
        "AAA2AwAAOgMAAEADAABIAwAASwMAAFMDAABWAwAAWgMAAF0DAABgAwAAZAMAAGgDAACAAwAAnwMA" +
        "ALsDAADoAwAA+gMAAA0EAAA1BAAATAQAAGEEAACDBAAAlwQAAKsEAADGBAAA3QQAAPAEAAD9BAAA" +
        "AAUAAAQFAAAJBQAADQUAABAFAAAUBQAAHAUAACMFAAArBQAANQUAAD8FAABIBQAAUgUAAGQFAAB8" +
        "BQAAiwUAAJUFAACnBQAAugUAAM0FAADVBQAA3QUAAOgFAADtBQAA/QUAAA8GAAAcBgAAJgYAAC0G" +
        "AAAGAAAACAAAAAwAAAANAAAADgAAAA8AAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAA" +
        "ABkAAAAcAAAAIAAAAAYAAAAAAAAAAAAAAAcAAAAAAAAAPAYAAAkAAAAGAAAAAAAAAAkAAAALAAAA" +
        "AAAAAAkAAAAMAAAAAAAAAAoAAAAMAAAARAYAAAsAAAANAAAAVAYAABwAAAAPAAAAAAAAAB0AAAAP" +
        "AAAATAYAAB4AAAAPAAAANAYAAB8AAAAPAAAAPAYAAB8AAAAPAAAAVAYAACEAAAAQAAAAPAYAAAsA" +
        "BgA0AAAACwAAADUAAAACAAoAGgAAAAYABAAnAAAABwALAAMAAAAJAAUANgAAAAsABwADAAAACwAD" +
        "ACMAAAALAAwAJAAAAAsABwAlAAAACwACACYAAAALAAAAKAAAAAsAAQApAAAACwABACoAAAALAAMA" +
        "KwAAAAsABwAxAAAACwAHADIAAAALAAQANwAAAAsABwA5AAAACwAIADkAAAALAAkAOQAAAA0ABwAD" +
        "AAAADQAGACIAAAANAAQANwAAAAsAAAABAAAA/////wAAAAAbAAAA0AYAAD4JAAAAAAAAHCBkb2Vz" +
        "bid0IGltcGxlbWVudCBDbG9uZWFibGUAAigpAAQ8Kj47AAY8aW5pdD4AAUAABkNsYXNzIAABSQAC" +
        "SUwAAUoAAUwAAkxJAAJMTAAWTGFydC90ZXN0L1Rlc3RXYXRjaGVyOwAdTGRhbHZpay9hbm5vdGF0" +
        "aW9uL1NpZ25hdHVyZTsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ACtMZGFsdmlrL2Fubm90" +
        "YXRpb24vb3B0aW1pemF0aW9uL0Zhc3ROYXRpdmU7ABBMamF2YS9sYW5nL0NsYXNzABFMamF2YS9s" +
        "YW5nL0NsYXNzOwAmTGphdmEvbGFuZy9DbG9uZU5vdFN1cHBvcnRlZEV4Y2VwdGlvbjsAFUxqYXZh" +
        "L2xhbmcvQ2xvbmVhYmxlOwATTGphdmEvbGFuZy9JbnRlZ2VyOwAgTGphdmEvbGFuZy9JbnRlcnJ1" +
        "cHRlZEV4Y2VwdGlvbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABlM" +
        "amF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7ABVMamF2YS9sYW5nL1Rocm93YWJsZTsAEU5vdGlmeUNv" +
        "bnN0cnVjdGVkAAtPYmplY3QuamF2YQABVgACVkoAA1ZKSQACVkwAAVoAAlpMAAZhcHBlbmQABWNs" +
        "b25lAAZlcXVhbHMACGZpbmFsaXplAAhnZXRDbGFzcwAHZ2V0TmFtZQAIaGFzaENvZGUAEGlkZW50" +
        "aXR5SGFzaENvZGUAFmlkZW50aXR5SGFzaENvZGVOYXRpdmUADWludGVybmFsQ2xvbmUACGxvY2tX" +
        "b3JkABBsb2NrV29yZEhhc2hNYXNrABFsb2NrV29yZFN0YXRlSGFzaAARbG9ja1dvcmRTdGF0ZU1h" +
        "c2sABm1pbGxpcwAGbm90aWZ5AAlub3RpZnlBbGwAA29iagAOc2hhZG93JF9rbGFzc18AEHNoYWRv" +
        "dyRfbW9uaXRvcl8AC3RvSGV4U3RyaW5nAAh0b1N0cmluZwAFdmFsdWUABHdhaXQAAAIAAAABAAAA" +
        "AQAAAAsAAAABAAAAAAAAAAEAAAABAAAAAQAAAAwAAgQBOBwBGAcCBAE4HAEYCgIDATgcAhcQFwIC" +
        "BAE4HAEYDgAFAAIDATgcAxcBFxAXAgAAAAAAAAAAAAEAAABaBgAAAgAAAGIGAAB8BgAAAQAAAGIG" +
        "AAABAAAAagYAAAEAAAB0BgAAAQAAAHwGAAABAAAAfwYAAAAAAAABAAAACgAAAAAAAAAAAAAAsAYA" +
        "AAUAAACUBgAABwAAALgGAAAIAAAAyAYAAAsAAADABgAADAAAAMAGAAANAAAAwAYAAA4AAADABgAA" +
        "EAAAAJwGAAARAAAAqAYAABIAAACcBgAAKAAHDgBwATQHDi0DAC0BLQMDMAEtAwIvATwDAS4BeFsA" +
        "7AEABw5LARoPOsYArAEBNAcOAMUEAAcOAEEABw4AaAAHDgCRAgAHDgCmAwExBw5LAAAAAQABAAEA" +
        "AAA4BwAABAAAAHEQAAAAAA4ABwABAAEAAAA9BwAAGgAAAFJgAQAVAwDAFQIAgBQB////DxUEAMC1" +
        "BBUFAIAzVAcAFAT///8PtQQPBHEQCwAGAAoEDwQEAAEAAgAAAFkHAAAyAAAAIDAIADkAKwAiAAcA" +
        "IgENAHAQEwABABsCBQAAAG4gFAAhAAwBbhAIAAMADAJuEAEAAgAMAm4gFAAhAAwBGwIAAAAAbiAU" +
        "ACEADAFuEBUAAQAMAXAgAgAQACcAcBAMAAMADAARAAMAAgAAAAAAZQcAAAYAAAAzIQQAEhAPABIA" +
        "KP4BAAEAAAAAAGwHAAABAAAADgAAAAIAAQAAAAAAcgcAAAMAAABUEAAAEQAAAAIAAQABAAAAdwcA" +
        "AAUAAABxEAoAAQAKAA8AAAADAAEAAgAAAHwHAAApAAAAIgANAHAQEwAAAG4QCAACAAwBbhABAAEA" +
        "DAFuIBQAEAAMABsBBAAAAG4gFAAQAAwAbhAJAAIACgFxEAMAAQAMAW4gFAAQAAwAbhAVAAAADAAR" +
        "AAAABAADAAQAAACCBwAABQAAABIAbkASACEDDgAAAgQLAIIBAYIBBIGABIwPBgikDwGKAgABggIA" +
        "BQToDwEB3BABBPgQARGMEQEBpBEEkQIAAZECAAEBwBEBkQIAARGkEgGRAgAAABAAAAAAAAAAAQAA" +
        "AAAAAAABAAAAOgAAAHAAAAACAAAAEQAAAFgBAAADAAAADQAAAJwBAAAEAAAAAgAAADgCAAAFAAAA" +
        "FgAAAEgCAAAGAAAAAQAAAPgCAAACIAAAOgAAABgDAAABEAAABQAAADQGAAAEIAAABgAAAFoGAAAD" +
        "EAAACQAAAIwGAAAGIAAAAQAAANAGAAADIAAACQAAADgHAAABIAAACQAAAIwHAAAAIAAAAQAAAD4J" +
        "AAAAEAAAAQAAAIgJAAA=");
  
    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");
      doCommonClassRedefinition(Object.class, CLASS_BYTES, DEX_BYTES);
  
      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!");
    }
  
    private static native void addToBootClassLoader(String s);
  }
  