1 /* 2 * Copyright (C) 2016 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 package art; 18 19 import java.lang.reflect.Method; 20 import java.util.Base64; 21 22 public class Test984 { 23 24 static class Transform { 25 // This method must be 'static' so that when we try to invoke it through a j.l.r.Method we will 26 // simply use the jmethodID directly and not do any lookup in any receiver object. sayHi(Runnable r)27 public static void sayHi(Runnable r) { 28 System.out.println("hello"); 29 r.run(); 30 System.out.println("goodbye"); 31 } 32 } 33 // static class Transform { 34 // public static void sayHi(Runnable r) { 35 // System.out.println("Hello - Transformed"); 36 // r.run(); 37 // System.out.println("Goodbye - Transformed"); 38 // } 39 // } 40 private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( 41 "yv66vgAAADQAKAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAbBwAeAQAGPGluaXQ+AQADKClW" + 42 "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" + 43 "KVYBAApTb3VyY2VGaWxlAQAMVGVzdDk4NC5qYXZhDAAJAAoHAB8MACAAIQEAE0hlbGxvIC0gVHJh" + 44 "bnNmb3JtZWQHACIMACMAJAcAJQwAJgAKAQAVR29vZGJ5ZSAtIFRyYW5zZm9ybWVkBwAnAQAVYXJ0" + 45 "L1Rlc3Q5ODQkVHJhbnNmb3JtAQAJVHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5n" + 46 "L09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsB" + 47 "ABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEA" + 48 "EmphdmEvbGFuZy9SdW5uYWJsZQEAA3J1bgEAC2FydC9UZXN0OTg0ACAABwAIAAAAAAACAAAACQAK" + 49 "AAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAABQAJAA0ADgABAAsAAAA7AAIAAQAA" + 50 "ABeyAAISA7YABCq5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAcACAAIAA4ACQAWAAoAAgAP" + 51 "AAAAAgAQAB0AAAAKAAEABwAaABwACA=="); 52 private static final byte[] DEX_BYTES = Base64.getDecoder().decode( 53 "ZGV4CjAzNQB/mxSMAAAAAAAAAAAAAAAAAAAAAAAAAAA8BAAAcAAAAHhWNBIAAAAAAAAAAHgDAAAX" + 54 "AAAAcAAAAAoAAADMAAAAAwAAAPQAAAABAAAAGAEAAAUAAAAgAQAAAQAAAEgBAADUAgAAaAEAAGgB" + 55 "AABwAQAAhwEAAJwBAAC1AQAAxAEAAOgBAAAIAgAAHwIAADMCAABJAgAAXQIAAHECAAB/AgAAigIA" + 56 "AI0CAACRAgAAngIAAKQCAACpAgAAsgIAALcCAAC+AgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA" + 57 "CQAAAAoAAAALAAAADgAAAA4AAAAJAAAAAAAAAA8AAAAJAAAAyAIAAA8AAAAJAAAA0AIAAAgABAAS" + 58 "AAAAAAAAAAAAAAAAAAEAFQAAAAQAAgATAAAABQAAAAAAAAAGAAAAFAAAAAAAAAAAAAAABQAAAAAA" + 59 "AAAMAAAAaAMAADwDAAAAAAAABjxpbml0PgAVR29vZGJ5ZSAtIFRyYW5zZm9ybWVkABNIZWxsbyAt" + 60 "IFRyYW5zZm9ybWVkABdMYXJ0L1Rlc3Q5ODQkVHJhbnNmb3JtOwANTGFydC9UZXN0OTg0OwAiTGRh" + 61 "bHZpay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVy" + 62 "Q2xhc3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAUTGphdmEv" + 63 "bGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxU" + 64 "ZXN0OTg0LmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vzc0ZsYWdzAARuYW1lAANvdXQAB3By" + 65 "aW50bG4AA3J1bgAFc2F5SGkABXZhbHVlAAAAAAEAAAAGAAAAAQAAAAcAAAAFAAcOAAcBAAcOAQgP" + 66 "AQMPAQgPAAEAAQABAAAA2AIAAAQAAABwEAMAAAAOAAMAAQACAAAA3QIAABQAAABiAAAAGwECAAAA" + 67 "biACABAAchAEAAIAYgAAABsBAQAAAG4gAgAQAA4AAAACAACAgATsBQEJhAYAAAICARYYAQIDAhAE" + 68 "CBEXDQACAAAATAMAAFIDAABcAwAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAXAAAA" + 69 "cAAAAAIAAAAKAAAAzAAAAAMAAAADAAAA9AAAAAQAAAABAAAAGAEAAAUAAAAFAAAAIAEAAAYAAAAB" + 70 "AAAASAEAAAIgAAAXAAAAaAEAAAEQAAACAAAAyAIAAAMgAAACAAAA2AIAAAEgAAACAAAA7AIAAAAg" + 71 "AAABAAAAPAMAAAQgAAACAAAATAMAAAMQAAABAAAAXAMAAAYgAAABAAAAaAMAAAAQAAABAAAAeAMA" + 72 "AA=="); 73 run()74 public static void run() { 75 doTest(); 76 } 77 78 // The Method that holds an obsolete method pointer. We will fill it in by getting a jmethodID 79 // from a stack with an obsolete method in it. There should be no other ways to obtain an obsolete 80 // jmethodID in ART without unsafe casts. 81 public static Method obsolete_method = null; 82 doTest()83 public static void doTest() { 84 // Capture the obsolete method. 85 // 86 // NB The obsolete method must be direct so that we will not look in the receiver type to get 87 // the actual method. 88 Transform.sayHi(() -> { 89 System.out.println("transforming calling function"); 90 Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES); 91 System.out.println("Retrieving obsolete method from current stack"); 92 // This should get the obsolete sayHi method (as the only obsolete method on the current 93 // threads stack). 94 Test984.obsolete_method = getFirstObsoleteMethod984(); 95 }); 96 97 // Prove we did actually redefine something. 98 System.out.println("Invoking redefined version of method."); 99 Transform.sayHi(() -> { System.out.println("Not doing anything here"); }); 100 101 System.out.println("invoking obsolete method"); 102 try { 103 obsolete_method.invoke(null, (Runnable)() -> { 104 throw new Error("Unexpected code running from invoke of obsolete method!"); 105 }); 106 throw new Error("Running obsolete method did not throw exception"); 107 } catch (Throwable e) { 108 if (e instanceof InternalError || e.getCause() instanceof InternalError) { 109 System.out.println("Caught expected error from attempting to invoke an obsolete method."); 110 } else { 111 System.out.println("Unexpected error type for calling obsolete method! Expected either " 112 + "an InternalError or something that is caused by an InternalError."); 113 throw new Error("Unexpected error caught: ", e); 114 } 115 } 116 } 117 118 // Gets the first obsolete method on the current threads stack (NB only looks through the first 30 119 // stack frames). getFirstObsoleteMethod984()120 private static native Method getFirstObsoleteMethod984(); 121 } 122