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