StubMethodAdapterTest.java revision 625460fb3256459b60fa737be40e47a0b4eaf54e
1625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta/* 2625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * Copyright (C) 2016 The Android Open Source Project 3625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * 4625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License"); 5625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * you may not use this file except in compliance with the License. 6625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * You may obtain a copy of the License at 7625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * 8625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * http://www.apache.org/licenses/LICENSE-2.0 9625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * 10625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * Unless required by applicable law or agreed to in writing, software 11625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS, 12625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * See the License for the specific language governing permissions and 14625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * limitations under the License. 15625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta */ 16625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 17625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Guptapackage com.android.tools.layoutlib.create; 18625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 19625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Guptaimport com.android.tools.layoutlib.create.dataclass.StubClass; 20625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 21625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Guptaimport org.junit.Assert; 22625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Guptaimport org.junit.Test; 23625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Guptaimport org.objectweb.asm.ClassReader; 24625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Guptaimport org.objectweb.asm.ClassVisitor; 25625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Guptaimport org.objectweb.asm.ClassWriter; 26625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Guptaimport org.objectweb.asm.MethodVisitor; 27625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Guptaimport org.objectweb.asm.Opcodes; 28625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Guptaimport org.objectweb.asm.Type; 29625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 30625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Guptaimport java.lang.reflect.Method; 31625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Guptaimport java.util.function.BiPredicate; 32625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Guptaimport java.util.function.Consumer; 33625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 34625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Guptaimport static org.junit.Assert.*; 35625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 36625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Guptapublic class StubMethodAdapterTest { 37625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 38625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta private static final String STUB_CLASS_NAME = StubClass.class.getName(); 39625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 40625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta /** 41625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * Load a dummy class, stub one of its method and ensure that the modified class works as 42625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * intended. 43625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta */ 44625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta @Test 45625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta public void testBoolean() throws Exception { 46625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta final String methodName = "returnTrue"; 47625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta // First don't change the method and assert that it returns true 48625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta testBoolean((name, type) -> false, Assert::assertTrue, methodName); 49625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta // Change the method now and assert that it returns false. 50625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta testBoolean((name, type) -> methodName.equals(name) && 51625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta Type.BOOLEAN_TYPE.equals(type.getReturnType()), Assert::assertFalse, methodName); 52625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta } 53625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 54625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta /** 55625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta * @param methodPredicate tests if the method should be replaced 56625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta */ 57625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta private void testBoolean(BiPredicate<String, Type> methodPredicate, Consumer<Boolean> assertion, 58625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta String methodName) throws Exception { 59625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); 60625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta // Always rename the class to avoid conflict with the original class. 61625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta String newClassName = STUB_CLASS_NAME + '_'; 62625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta new ClassReader(STUB_CLASS_NAME).accept( 63625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta new ClassAdapter(newClassName, writer, methodPredicate), 0); 64625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta MyClassLoader myClassLoader = new MyClassLoader(newClassName, writer.toByteArray()); 65625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta Class<?> aClass = myClassLoader.loadClass(newClassName); 66625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta assertTrue("StubClass not loaded by the classloader. Likely a bug in the test.", 67625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta myClassLoader.findClassCalled); 68625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta Method method = aClass.getMethod(methodName); 69625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta Object o = aClass.newInstance(); 70625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta assertion.accept((Boolean) method.invoke(o)); 71625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta } 72625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 73625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta private static class ClassAdapter extends ClassVisitor { 74625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 75625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta private final String mClassName; 76625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta private final BiPredicate<String, Type> mMethodPredicate; 77625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 78625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta private ClassAdapter(String className, ClassVisitor cv, 79625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta BiPredicate<String, Type> methodPredicate) { 80625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta super(Main.ASM_VERSION, cv); 81625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta mClassName = className.replace('.', '/'); 82625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta mMethodPredicate = methodPredicate; 83625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta } 84625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 85625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta @Override 86625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta public void visit(int version, int access, String name, String signature, String superName, 87625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta String[] interfaces) { 88625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta super.visit(version, access, mClassName, signature, superName, 89625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta interfaces); 90625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta } 91625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 92625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta @Override 93625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta public MethodVisitor visitMethod(int access, String name, String desc, String signature, 94625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta String[] exceptions) { 95625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta // Copied partly from 96625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta // com.android.tools.layoutlib.create.DelegateClassAdapter.visitMethod() 97625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta // but not generating the _Original method. 98625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta boolean isStatic = (access & Opcodes.ACC_STATIC) != 0; 99625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta boolean isNative = (access & Opcodes.ACC_NATIVE) != 0; 100625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta MethodVisitor originalMethod = 101625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta super.visitMethod(access, name, desc, signature, exceptions); 102625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta Type descriptor = Type.getMethodType(desc); 103625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta if (mMethodPredicate.test(name, descriptor)) { 104625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta String methodSignature = mClassName + "#" + name; 105625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta String invokeSignature = methodSignature + desc; 106625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta return new StubMethodAdapter(originalMethod, name, descriptor.getReturnType(), 107625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta invokeSignature, isStatic, isNative); 108625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta } 109625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta return originalMethod; 110625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta } 111625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta } 112625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 113625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta private static class MyClassLoader extends ClassLoader { 114625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta private final String mName; 115625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta private final byte[] mBytes; 116625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta private boolean findClassCalled; 117625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 118625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta private MyClassLoader(String name, byte[] bytes) { 119625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta mName = name; 120625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta mBytes = bytes; 121625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta } 122625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta 123625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta @Override 124625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta protected Class<?> findClass(String name) throws ClassNotFoundException { 125625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta if (name.equals(mName)) { 126625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta findClassCalled = true; 127625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta return defineClass(name, mBytes, 0, mBytes.length); 128625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta } 129625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta return super.findClass(name); 130625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta } 131625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta } 132625460fb3256459b60fa737be40e47a0b4eaf54eDeepanshu Gupta} 133