/* * 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 ()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;->()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;->(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;->()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 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 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 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 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); }