1282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/* 2282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Copyright (C) 2010 The Android Open Source Project 3282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 4282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License"); 5282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * you may not use this file except in compliance with the License. 6282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * You may obtain a copy of the License at 7282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 8282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * http://www.apache.org/licenses/LICENSE-2.0 9282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 10282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Unless required by applicable law or agreed to in writing, software 11282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS, 12282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * See the License for the specific language governing permissions and 14282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * limitations under the License. 15282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 16282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 17282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipackage com.android.tools.layoutlib.create; 18282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 19282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 20282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport static org.junit.Assert.assertEquals; 21282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport static org.junit.Assert.assertFalse; 22282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport static org.junit.Assert.assertNotNull; 23282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport static org.junit.Assert.assertSame; 24282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport static org.junit.Assert.assertTrue; 25282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport static org.junit.Assert.fail; 26282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 27282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.tools.layoutlib.create.dataclass.ClassWithNative; 28282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.tools.layoutlib.create.dataclass.OuterClass; 29282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.tools.layoutlib.create.dataclass.OuterClass.InnerClass; 306777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perezimport com.android.tools.layoutlib.create.dataclass.OuterClass.StaticInnerClass; 31282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 32282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.junit.Before; 33282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.junit.Test; 34282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.objectweb.asm.ClassReader; 35282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.objectweb.asm.ClassVisitor; 36282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.objectweb.asm.ClassWriter; 37282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 38282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.IOException; 39282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.PrintWriter; 40282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.StringWriter; 41282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.lang.annotation.Annotation; 42282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.lang.reflect.Constructor; 43282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.lang.reflect.InvocationTargetException; 44282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.lang.reflect.Method; 45282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.lang.reflect.Modifier; 46282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.HashMap; 47282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.HashSet; 48282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.Map; 49282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.Map.Entry; 50282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.Set; 51282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 52282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipublic class DelegateClassAdapterTest { 53282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 54282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private MockLog mLog; 55282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 56a9de835c17a8d8d36ef4ccab7cd06254f3a081faDeepanshu Gupta private static final String NATIVE_CLASS_NAME = ClassWithNative.class.getName(); 57a9de835c17a8d8d36ef4ccab7cd06254f3a081faDeepanshu Gupta private static final String OUTER_CLASS_NAME = OuterClass.class.getName(); 58a9de835c17a8d8d36ef4ccab7cd06254f3a081faDeepanshu Gupta private static final String INNER_CLASS_NAME = InnerClass.class.getName(); 59a9de835c17a8d8d36ef4ccab7cd06254f3a081faDeepanshu Gupta private static final String STATIC_INNER_CLASS_NAME = StaticInnerClass.class.getName(); 60282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 61282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski @Before 62282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void setUp() throws Exception { 63282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLog = new MockLog(); 64282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLog.setVerbose(true); // capture debug error too 65282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 66282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 67282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 68282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Tests that a class not being modified still works. 69282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 70282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski @Test 71282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void testNoOp() throws Throwable { 72282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // create an instance of the class that will be modified 73282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // (load the class in a distinct class loader so that we can trash its definition later) 74282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassLoader cl1 = new ClassLoader(this.getClass().getClassLoader()) { }; 75a9de835c17a8d8d36ef4ccab7cd06254f3a081faDeepanshu Gupta @SuppressWarnings("unchecked") 76282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Class<ClassWithNative> clazz1 = (Class<ClassWithNative>) cl1.loadClass(NATIVE_CLASS_NAME); 77282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassWithNative instance1 = clazz1.newInstance(); 78282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertEquals(42, instance1.add(20, 22)); 79282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski try { 80282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski instance1.callNativeInstance(10, 3.1415, new Object[0] ); 81282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski fail("Test should have failed to invoke callTheNativeMethod [1]"); 82282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } catch (UnsatisfiedLinkError e) { 83282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // This is expected to fail since the native method is not implemented. 84282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 85282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 86282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Now process it but tell the delegate to not modify any method 87282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassWriter cw = new ClassWriter(0 /*flags*/); 88282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 8923e47f5621271db9b84f53f15a3e3d81d8b8b48dDeepanshu Gupta HashSet<String> delegateMethods = new HashSet<>(); 90282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski String internalClassName = NATIVE_CLASS_NAME.replace('.', '/'); 91282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski DelegateClassAdapter cv = new DelegateClassAdapter( 92282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLog, cw, internalClassName, delegateMethods); 93282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 94282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassReader cr = new ClassReader(NATIVE_CLASS_NAME); 95282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cr.accept(cv, 0 /* flags */); 96282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 97282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Load the generated class in a different class loader and try it again 98282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 99282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassLoader2 cl2 = null; 100282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski try { 101282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cl2 = new ClassLoader2() { 102282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski @Override 103282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void testModifiedInstance() throws Exception { 104282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Class<?> clazz2 = loadClass(NATIVE_CLASS_NAME); 105282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Object i2 = clazz2.newInstance(); 106282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertNotNull(i2); 107282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertEquals(42, callAdd(i2, 20, 22)); 108282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 109282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski try { 110282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski callCallNativeInstance(i2, 10, 3.1415, new Object[0]); 111282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski fail("Test should have failed to invoke callTheNativeMethod [2]"); 112282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } catch (InvocationTargetException e) { 113282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // This is expected to fail since the native method has NOT been 114282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // overridden here. 115282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertEquals(UnsatisfiedLinkError.class, e.getCause().getClass()); 116282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 117282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 118282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Check that the native method does NOT have the new annotation 119282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Method[] m = clazz2.getDeclaredMethods(); 120d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta Method nativeInstanceMethod = null; 121d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta for (Method method : m) { 122d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta if ("native_instance".equals(method.getName())) { 123d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta nativeInstanceMethod = method; 124d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta break; 125d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta } 126d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta } 127d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta assertNotNull(nativeInstanceMethod); 128d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta assertTrue(Modifier.isNative(nativeInstanceMethod.getModifiers())); 129d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta Annotation[] a = nativeInstanceMethod.getAnnotations(); 130282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertEquals(0, a.length); 131282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 132282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski }; 133282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cl2.add(NATIVE_CLASS_NAME, cw); 134282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cl2.testModifiedInstance(); 135282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } catch (Throwable t) { 136282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski throw dumpGeneratedClass(t, cl2); 137282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 138282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 139282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 140282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 141c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta * {@link DelegateMethodAdapter} does not support overriding constructors yet, 142282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * so this should fail with an {@link UnsupportedOperationException}. 143282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 144282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Although not tested here, the message of the exception should contain the 145282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * constructor signature. 146282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 147282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski @Test(expected=UnsupportedOperationException.class) 148282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void testConstructorsNotSupported() throws IOException { 149282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassWriter cw = new ClassWriter(0 /*flags*/); 150282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 151282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski String internalClassName = NATIVE_CLASS_NAME.replace('.', '/'); 152282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 15323e47f5621271db9b84f53f15a3e3d81d8b8b48dDeepanshu Gupta HashSet<String> delegateMethods = new HashSet<>(); 154282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski delegateMethods.add("<init>"); 155282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski DelegateClassAdapter cv = new DelegateClassAdapter( 156282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLog, cw, internalClassName, delegateMethods); 157282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 158282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassReader cr = new ClassReader(NATIVE_CLASS_NAME); 159282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cr.accept(cv, 0 /* flags */); 160282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 161282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 162282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski @Test 163282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void testDelegateNative() throws Throwable { 164282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassWriter cw = new ClassWriter(0 /*flags*/); 165282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski String internalClassName = NATIVE_CLASS_NAME.replace('.', '/'); 166282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 16723e47f5621271db9b84f53f15a3e3d81d8b8b48dDeepanshu Gupta HashSet<String> delegateMethods = new HashSet<>(); 168282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski delegateMethods.add(DelegateClassAdapter.ALL_NATIVES); 169282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski DelegateClassAdapter cv = new DelegateClassAdapter( 170282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLog, cw, internalClassName, delegateMethods); 171282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 172282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassReader cr = new ClassReader(NATIVE_CLASS_NAME); 173282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cr.accept(cv, 0 /* flags */); 174282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 175282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Load the generated class in a different class loader and try it 176282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassLoader2 cl2 = null; 177282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski try { 178282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cl2 = new ClassLoader2() { 179282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski @Override 180282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void testModifiedInstance() throws Exception { 181282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Class<?> clazz2 = loadClass(NATIVE_CLASS_NAME); 182282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Object i2 = clazz2.newInstance(); 183282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertNotNull(i2); 184282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 185282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Use reflection to access inner methods 186282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertEquals(42, callAdd(i2, 20, 22)); 187282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 188282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Object[] objResult = new Object[] { null }; 189282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski int result = callCallNativeInstance(i2, 10, 3.1415, objResult); 190282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertEquals((int)(10 + 3.1415), result); 191282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertSame(i2, objResult[0]); 192282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 193282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Check that the native method now has the new annotation and is not native 194282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Method[] m = clazz2.getDeclaredMethods(); 195d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta Method nativeInstanceMethod = null; 196d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta for (Method method : m) { 197d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta if ("native_instance".equals(method.getName())) { 198d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta nativeInstanceMethod = method; 199d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta break; 200d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta } 201d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta } 202d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta assertNotNull(nativeInstanceMethod); 203d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta assertFalse(Modifier.isNative(nativeInstanceMethod.getModifiers())); 204d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta Annotation[] a = nativeInstanceMethod.getAnnotations(); 205282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertEquals("LayoutlibDelegate", a[0].annotationType().getSimpleName()); 206282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 207282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski }; 208282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cl2.add(NATIVE_CLASS_NAME, cw); 209282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cl2.testModifiedInstance(); 210282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } catch (Throwable t) { 211282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski throw dumpGeneratedClass(t, cl2); 212282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 213282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 214282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 215282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski @Test 216282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void testDelegateInner() throws Throwable { 217282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // We'll delegate the "get" method of both the inner and outer class. 21823e47f5621271db9b84f53f15a3e3d81d8b8b48dDeepanshu Gupta HashSet<String> delegateMethods = new HashSet<>(); 219282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski delegateMethods.add("get"); 220282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski delegateMethods.add("privateMethod"); 221282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 222282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Generate the delegate for the outer class. 223282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassWriter cwOuter = new ClassWriter(0 /*flags*/); 224282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski String outerClassName = OUTER_CLASS_NAME.replace('.', '/'); 225282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski DelegateClassAdapter cvOuter = new DelegateClassAdapter( 226282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLog, cwOuter, outerClassName, delegateMethods); 227282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassReader cr = new ClassReader(OUTER_CLASS_NAME); 228282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cr.accept(cvOuter, 0 /* flags */); 229282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 230282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Generate the delegate for the inner class. 231282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassWriter cwInner = new ClassWriter(0 /*flags*/); 232282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski String innerClassName = INNER_CLASS_NAME.replace('.', '/'); 233282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski DelegateClassAdapter cvInner = new DelegateClassAdapter( 234282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLog, cwInner, innerClassName, delegateMethods); 235282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cr = new ClassReader(INNER_CLASS_NAME); 236282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cr.accept(cvInner, 0 /* flags */); 237282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 238282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Load the generated classes in a different class loader and try them 239282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassLoader2 cl2 = null; 240282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski try { 241282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cl2 = new ClassLoader2() { 242282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski @Override 243282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void testModifiedInstance() throws Exception { 244282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 245282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Check the outer class 246282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Class<?> outerClazz2 = loadClass(OUTER_CLASS_NAME); 247282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Object o2 = outerClazz2.newInstance(); 248282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertNotNull(o2); 249282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 250282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // The original Outer.get returns 1+10+20, 251282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // but the delegate makes it return 4+10+20 252282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertEquals(4+10+20, callGet(o2, 10, 20)); 253282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertEquals(1+10+20, callGet_Original(o2, 10, 20)); 254282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 255d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta // The original Outer has a private method, 256d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta // so by default we can't access it. 257282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski boolean gotIllegalAccessException = false; 258282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski try { 259282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski callMethod(o2, "privateMethod", false /*makePublic*/); 260282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } catch(IllegalAccessException e) { 261282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski gotIllegalAccessException = true; 262282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 263282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertTrue(gotIllegalAccessException); 264d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta 265d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta // The private method from original Outer has been 266d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta // delegated. The delegate generated should have the 267d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta // same access. 268d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta gotIllegalAccessException = false; 269d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta try { 270d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta assertEquals("outerPrivateMethod", 271d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta callMethod(o2, "privateMethod_Original", false /*makePublic*/)); 272d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta } catch (IllegalAccessException e) { 273d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta gotIllegalAccessException = true; 274d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta } 275d2a411840c58eaee05499bab0190e3f3656e2e84Deepanshu Gupta assertTrue(gotIllegalAccessException); 276282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 277282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Check the inner class. Since it's not a static inner class, we need 278282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // to use the hidden constructor that takes the outer class as first parameter. 279282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Class<?> innerClazz2 = loadClass(INNER_CLASS_NAME); 280c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta Constructor<?> innerCons = innerClazz2.getConstructor(outerClazz2); 281c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta Object i2 = innerCons.newInstance(o2); 282282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertNotNull(i2); 283282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 284282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // The original Inner.get returns 3+10+20, 285282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // but the delegate makes it return 6+10+20 286282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertEquals(6+10+20, callGet(i2, 10, 20)); 287282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assertEquals(3+10+20, callGet_Original(i2, 10, 20)); 288282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 289282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski }; 290282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cl2.add(OUTER_CLASS_NAME, cwOuter.toByteArray()); 291282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cl2.add(INNER_CLASS_NAME, cwInner.toByteArray()); 292282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cl2.testModifiedInstance(); 293282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } catch (Throwable t) { 294282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski throw dumpGeneratedClass(t, cl2); 295282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 2966777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez } 2976777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez 2986777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez @Test 2996777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez public void testDelegateStaticInner() throws Throwable { 3006777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez // We'll delegate the "get" method of both the inner and outer class. 30123e47f5621271db9b84f53f15a3e3d81d8b8b48dDeepanshu Gupta HashSet<String> delegateMethods = new HashSet<>(); 3026777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez delegateMethods.add("get"); 3036777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez 3046777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez // Generate the delegate for the outer class. 3056777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez ClassWriter cwOuter = new ClassWriter(0 /*flags*/); 3066777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez String outerClassName = OUTER_CLASS_NAME.replace('.', '/'); 3076777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez DelegateClassAdapter cvOuter = new DelegateClassAdapter( 3086777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez mLog, cwOuter, outerClassName, delegateMethods); 3096777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez ClassReader cr = new ClassReader(OUTER_CLASS_NAME); 3106777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez cr.accept(cvOuter, 0 /* flags */); 3116777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez 3126777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez // Generate the delegate for the static inner class. 3136777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez ClassWriter cwInner = new ClassWriter(0 /*flags*/); 3146777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez String innerClassName = STATIC_INNER_CLASS_NAME.replace('.', '/'); 3156777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez DelegateClassAdapter cvInner = new DelegateClassAdapter( 3166777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez mLog, cwInner, innerClassName, delegateMethods); 3176777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez cr = new ClassReader(STATIC_INNER_CLASS_NAME); 3186777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez cr.accept(cvInner, 0 /* flags */); 3196777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez 3206777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez // Load the generated classes in a different class loader and try them 3216777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez ClassLoader2 cl2 = null; 3226777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez try { 3236777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez cl2 = new ClassLoader2() { 3246777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez @Override 3256777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez public void testModifiedInstance() throws Exception { 3266777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez 3276777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez // Check the outer class 3286777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez Class<?> outerClazz2 = loadClass(OUTER_CLASS_NAME); 3296777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez Object o2 = outerClazz2.newInstance(); 3306777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez assertNotNull(o2); 3316777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez 3326777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez // Check the inner class. Since it's not a static inner class, we need 3336777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez // to use the hidden constructor that takes the outer class as first parameter. 3346777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez Class<?> innerClazz2 = loadClass(STATIC_INNER_CLASS_NAME); 3356777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez Constructor<?> innerCons = innerClazz2.getConstructor(); 3366777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez Object i2 = innerCons.newInstance(); 3376777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez assertNotNull(i2); 3386777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez 3396777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez // The original StaticInner.get returns 100+10+20, 3406777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez // but the delegate makes it return 6+10+20 3416777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez assertEquals(6+10+20, callGet(i2, 10, 20)); 3426777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez assertEquals(100+10+20, callGet_Original(i2, 10, 20)); 3436777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez } 3446777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez }; 3456777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez cl2.add(OUTER_CLASS_NAME, cwOuter.toByteArray()); 3466777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez cl2.add(STATIC_INNER_CLASS_NAME, cwInner.toByteArray()); 3476777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez cl2.testModifiedInstance(); 3486777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez } catch (Throwable t) { 3496777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez throw dumpGeneratedClass(t, cl2); 3506777f54fa44341dd4b23456c97d97c6e4ffe915fDiego Perez } 351282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 352282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 353282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski //------- 354282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 355282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 356282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * A class loader than can define and instantiate our modified classes. 357282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p/> 358282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * The trick here is that this class loader will test our <em>modified</em> version 359282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * of the classes, the one with the delegate calls. 360282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p/> 361282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Trying to do so in the original class loader generates all sort of link issues because 362282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * there are 2 different definitions of the same class name. This class loader will 363282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * define and load the class when requested by name and provide helpers to access the 364282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * instance methods via reflection. 365282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 366282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private abstract class ClassLoader2 extends ClassLoader { 367282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 36823e47f5621271db9b84f53f15a3e3d81d8b8b48dDeepanshu Gupta private final Map<String, byte[]> mClassDefs = new HashMap<>(); 369282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 370282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public ClassLoader2() { 371282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski super(null); 372282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 373282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 374282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public ClassLoader2 add(String className, byte[] definition) { 375282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mClassDefs.put(className, definition); 376282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return this; 377282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 378282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 379282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public ClassLoader2 add(String className, ClassWriter rewrittenClass) { 380282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mClassDefs.put(className, rewrittenClass.toByteArray()); 381282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return this; 382282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 383282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 384282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private Set<Entry<String, byte[]>> getByteCode() { 385282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return mClassDefs.entrySet(); 386282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 387282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 388282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski @SuppressWarnings("unused") 389282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski @Override 390282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski protected Class<?> findClass(String name) throws ClassNotFoundException { 391282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski try { 392282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return super.findClass(name); 393282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } catch (ClassNotFoundException e) { 394282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 395282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski byte[] def = mClassDefs.get(name); 396282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (def != null) { 397282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Load the modified ClassWithNative from its bytes representation. 398282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return defineClass(name, def, 0, def.length); 399282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 400282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 401282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski try { 402282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Load everything else from the original definition into the new class loader. 403282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassReader cr = new ClassReader(name); 404282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassWriter cw = new ClassWriter(0); 405282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cr.accept(cw, 0); 406282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski byte[] bytes = cw.toByteArray(); 407282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return defineClass(name, bytes, 0, bytes.length); 408282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 409282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } catch (IOException ioe) { 410282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski throw new RuntimeException(ioe); 411282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 412282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 413282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 414282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 415282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 416282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Accesses {@link OuterClass#get} or {@link InnerClass#get}via reflection. 417282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 418282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public int callGet(Object instance, int a, long b) throws Exception { 419282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Method m = instance.getClass().getMethod("get", 420c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta int.class, long.class); 421282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 422c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta Object result = m.invoke(instance, a, b); 423c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta return (Integer) result; 424282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 425282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 426282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 427282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Accesses the "_Original" methods for {@link OuterClass#get} 428282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * or {@link InnerClass#get}via reflection. 429282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 430282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public int callGet_Original(Object instance, int a, long b) throws Exception { 431282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Method m = instance.getClass().getMethod("get_Original", 432c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta int.class, long.class); 433282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 434c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta Object result = m.invoke(instance, a, b); 435c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta return (Integer) result; 436282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 437282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 438282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 439282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Accesses the any declared method that takes no parameter via reflection. 440282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 441282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski @SuppressWarnings("unchecked") 442282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public <T> T callMethod(Object instance, String methodName, boolean makePublic) throws Exception { 443282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Method m = instance.getClass().getDeclaredMethod(methodName, (Class<?>[])null); 444282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 445282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski boolean wasAccessible = m.isAccessible(); 446282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (makePublic && !wasAccessible) { 447282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski m.setAccessible(true); 448282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 449282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 450282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Object result = m.invoke(instance, (Object[])null); 451282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 452282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (makePublic && !wasAccessible) { 453282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski m.setAccessible(false); 454282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 455282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 456282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return (T) result; 457282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 458282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 459282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 460282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Accesses {@link ClassWithNative#add(int, int)} via reflection. 461282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 462282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public int callAdd(Object instance, int a, int b) throws Exception { 463282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Method m = instance.getClass().getMethod("add", 464c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta int.class, int.class); 465282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 466c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta Object result = m.invoke(instance, a, b); 467c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta return (Integer) result; 468282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 469282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 470282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 471282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Accesses {@link ClassWithNative#callNativeInstance(int, double, Object[])} 472282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * via reflection. 473282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 474282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public int callCallNativeInstance(Object instance, int a, double d, Object[] o) 475282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski throws Exception { 476282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Method m = instance.getClass().getMethod("callNativeInstance", 477c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta int.class, double.class, Object[].class); 478282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 479c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta Object result = m.invoke(instance, a, d, o); 480c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta return (Integer) result; 481282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 482282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 483282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public abstract void testModifiedInstance() throws Exception; 484282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 485282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 486282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 487282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * For debugging, it's useful to dump the content of the generated classes 488282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * along with the exception that was generated. 489282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 490282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * However to make it work you need to pull in the org.objectweb.asm.util.TraceClassVisitor 491282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * class and associated utilities which are found in the ASM source jar. Since we don't 492282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * want that dependency in the source code, we only put it manually for development and 493282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * access the TraceClassVisitor via reflection if present. 494282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 495282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param t The exception thrown by {@link ClassLoader2#testModifiedInstance()} 496282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param cl2 The {@link ClassLoader2} instance with the generated bytecode. 497282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @return Either original {@code t} or a new wrapper {@link Throwable} 498282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 499282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private Throwable dumpGeneratedClass(Throwable t, ClassLoader2 cl2) { 500282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski try { 501282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // For debugging, dump the bytecode of the class in case of unexpected error 502282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // if we can find the TraceClassVisitor class. 503282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Class<?> tcvClass = Class.forName("org.objectweb.asm.util.TraceClassVisitor"); 504282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 505282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski StringBuilder sb = new StringBuilder(); 506282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski sb.append('\n').append(t.getClass().getCanonicalName()); 507282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (t.getMessage() != null) { 508282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski sb.append(": ").append(t.getMessage()); 509282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 510282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 511282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski for (Entry<String, byte[]> entry : cl2.getByteCode()) { 512282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski String className = entry.getKey(); 513282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski byte[] bytes = entry.getValue(); 514282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 515282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski StringWriter sw = new StringWriter(); 516282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski PrintWriter pw = new PrintWriter(sw); 517282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // next 2 lines do: TraceClassVisitor tcv = new TraceClassVisitor(pw); 518c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta Constructor<?> cons = tcvClass.getConstructor(pw.getClass()); 519c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta Object tcv = cons.newInstance(pw); 520282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski ClassReader cr2 = new ClassReader(bytes); 521282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cr2.accept((ClassVisitor) tcv, 0 /* flags */); 522282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 523282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski sb.append("\nBytecode dump: <").append(className).append(">:\n") 524282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski .append(sw.toString()); 525282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 526282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 527282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Re-throw exception with new message 528c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta return new RuntimeException(sb.toString(), t); 529282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } catch (Throwable ignore) { 530282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // In case of problem, just throw the original exception as-is. 531282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return t; 532282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 533282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 534282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 535282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski} 536