1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.tools.layoutlib.create; 18 19import org.objectweb.asm.ClassVisitor; 20import org.objectweb.asm.MethodVisitor; 21import org.objectweb.asm.Opcodes; 22import org.objectweb.asm.Type; 23 24import java.util.Set; 25 26/** 27 * Class adapter that can stub some or all of the methods of the class. 28 */ 29class TransformClassAdapter extends ClassVisitor { 30 31 /** True if all methods should be stubbed, false if only native ones must be stubbed. */ 32 private final boolean mStubAll; 33 /** True if the class is an interface. */ 34 private boolean mIsInterface; 35 private final String mClassName; 36 private final Log mLog; 37 private final Set<String> mStubMethods; 38 private Set<String> mDeleteReturns; 39 40 /** 41 * Creates a new class adapter that will stub some or all methods. 42 * @param stubMethods list of method signatures to always stub out 43 * @param deleteReturns list of types that trigger the deletion of methods returning them. 44 * @param className The name of the class being modified 45 * @param cv The parent class writer visitor 46 * @param stubNativesOnly True if only native methods should be stubbed. False if all 47 * methods should be stubbed. 48 */ 49 public TransformClassAdapter(Log logger, Set<String> stubMethods, 50 Set<String> deleteReturns, String className, ClassVisitor cv, 51 boolean stubNativesOnly) { 52 super(Opcodes.ASM4, cv); 53 mLog = logger; 54 mStubMethods = stubMethods; 55 mClassName = className; 56 mStubAll = !stubNativesOnly; 57 mIsInterface = false; 58 mDeleteReturns = deleteReturns; 59 } 60 61 /* Visits the class header. */ 62 @Override 63 public void visit(int version, int access, String name, 64 String signature, String superName, String[] interfaces) { 65 66 // This class might be being renamed. 67 name = mClassName; 68 69 // remove final 70 access = access & ~Opcodes.ACC_FINAL; 71 // note: leave abstract classes as such 72 // don't try to implement stub for interfaces 73 74 mIsInterface = ((access & Opcodes.ACC_INTERFACE) != 0); 75 super.visit(version, access, name, signature, superName, interfaces); 76 } 77 78 /* Visits the header of an inner class. */ 79 @Override 80 public void visitInnerClass(String name, String outerName, String innerName, int access) { 81 // remove final 82 access = access & ~Opcodes.ACC_FINAL; 83 // note: leave abstract classes as such 84 // don't try to implement stub for interfaces 85 86 super.visitInnerClass(name, outerName, innerName, access); 87 } 88 89 /* Visits a method. */ 90 @Override 91 public MethodVisitor visitMethod(int access, String name, String desc, 92 String signature, String[] exceptions) { 93 94 if (mDeleteReturns != null) { 95 Type t = Type.getReturnType(desc); 96 if (t.getSort() == Type.OBJECT) { 97 String returnType = t.getInternalName(); 98 if (returnType != null) { 99 if (mDeleteReturns.contains(returnType)) { 100 return null; 101 } 102 } 103 } 104 } 105 106 String methodSignature = mClassName.replace('/', '.') + "#" + name; 107 108 // remove final 109 access = access & ~Opcodes.ACC_FINAL; 110 111 // stub this method if they are all to be stubbed or if it is a native method 112 // and don't try to stub interfaces nor abstract non-native methods. 113 if (!mIsInterface && 114 ((access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) != Opcodes.ACC_ABSTRACT) && 115 (mStubAll || 116 (access & Opcodes.ACC_NATIVE) != 0) || 117 mStubMethods.contains(methodSignature)) { 118 119 boolean isStatic = (access & Opcodes.ACC_STATIC) != 0; 120 boolean isNative = (access & Opcodes.ACC_NATIVE) != 0; 121 122 // remove abstract, final and native 123 access = access & ~(Opcodes.ACC_ABSTRACT | Opcodes.ACC_FINAL | Opcodes.ACC_NATIVE); 124 125 String invokeSignature = methodSignature + desc; 126 mLog.debug(" Stub: %s (%s)", invokeSignature, isNative ? "native" : ""); 127 128 MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions); 129 return new StubMethodAdapter(mw, name, returnType(desc), invokeSignature, 130 isStatic, isNative); 131 132 } else { 133 mLog.debug(" Keep: %s %s", name, desc); 134 return super.visitMethod(access, name, desc, signature, exceptions); 135 } 136 } 137 138 /** 139 * Extracts the return {@link Type} of this descriptor. 140 */ 141 Type returnType(String desc) { 142 if (desc != null) { 143 try { 144 return Type.getReturnType(desc); 145 } catch (ArrayIndexOutOfBoundsException e) { 146 // ignore, not a valid type. 147 } 148 } 149 return null; 150 } 151} 152