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.FieldVisitor; 21import org.objectweb.asm.MethodVisitor; 22import org.objectweb.asm.Opcodes; 23import org.objectweb.asm.Type; 24 25import java.util.Set; 26 27/** 28 * Class adapter that can stub some or all of the methods of the class. 29 */ 30class TransformClassAdapter extends ClassVisitor { 31 32 /** True if all methods should be stubbed, false if only native ones must be stubbed. */ 33 private final boolean mStubAll; 34 /** True if the class is an interface. */ 35 private boolean mIsInterface; 36 private final String mClassName; 37 private final Log mLog; 38 private final Set<String> mStubMethods; 39 private Set<String> mDeleteReturns; 40 41 /** 42 * Creates a new class adapter that will stub some or all methods. 43 * @param logger 44 * @param stubMethods list of method signatures to always stub out 45 * @param deleteReturns list of types that trigger the deletion of methods returning them. 46 * @param className The name of the class being modified 47 * @param cv The parent class writer visitor 48 * @param stubNativesOnly True if only native methods should be stubbed. False if all 49 * methods should be stubbed. 50 * @param hasNative True if the method has natives, in which case its access should be 51 * changed. 52 */ 53 public TransformClassAdapter(Log logger, Set<String> stubMethods, 54 Set<String> deleteReturns, String className, ClassVisitor cv, 55 boolean stubNativesOnly, boolean hasNative) { 56 super(Opcodes.ASM4, cv); 57 mLog = logger; 58 mStubMethods = stubMethods; 59 mClassName = className; 60 mStubAll = !stubNativesOnly; 61 mIsInterface = false; 62 mDeleteReturns = deleteReturns; 63 } 64 65 /* Visits the class header. */ 66 @Override 67 public void visit(int version, int access, String name, 68 String signature, String superName, String[] interfaces) { 69 70 // This class might be being renamed. 71 name = mClassName; 72 73 // remove protected or private and set as public 74 if (Main.sOptions.generatePublicAccess) { 75 access = access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED); 76 access |= Opcodes.ACC_PUBLIC; 77 } 78 // remove final 79 access = access & ~Opcodes.ACC_FINAL; 80 // note: leave abstract classes as such 81 // don't try to implement stub for interfaces 82 83 mIsInterface = ((access & Opcodes.ACC_INTERFACE) != 0); 84 super.visit(version, access, name, signature, superName, interfaces); 85 } 86 87 /* Visits the header of an inner class. */ 88 @Override 89 public void visitInnerClass(String name, String outerName, String innerName, int access) { 90 // remove protected or private and set as public 91 if (Main.sOptions.generatePublicAccess) { 92 access = access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED); 93 access |= Opcodes.ACC_PUBLIC; 94 } 95 // remove final 96 access = access & ~Opcodes.ACC_FINAL; 97 // note: leave abstract classes as such 98 // don't try to implement stub for interfaces 99 100 super.visitInnerClass(name, outerName, innerName, access); 101 } 102 103 /* Visits a method. */ 104 @Override 105 public MethodVisitor visitMethod(int access, String name, String desc, 106 String signature, String[] exceptions) { 107 108 if (mDeleteReturns != null) { 109 Type t = Type.getReturnType(desc); 110 if (t.getSort() == Type.OBJECT) { 111 String returnType = t.getInternalName(); 112 if (returnType != null) { 113 if (mDeleteReturns.contains(returnType)) { 114 return null; 115 } 116 } 117 } 118 } 119 120 String methodSignature = mClassName.replace('/', '.') + "#" + name; 121 122 // change access to public 123 if (Main.sOptions.generatePublicAccess) { 124 access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE); 125 access |= Opcodes.ACC_PUBLIC; 126 } 127 128 // remove final 129 access = access & ~Opcodes.ACC_FINAL; 130 131 // stub this method if they are all to be stubbed or if it is a native method 132 // and don't try to stub interfaces nor abstract non-native methods. 133 if (!mIsInterface && 134 ((access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) != Opcodes.ACC_ABSTRACT) && 135 (mStubAll || 136 (access & Opcodes.ACC_NATIVE) != 0) || 137 mStubMethods.contains(methodSignature)) { 138 139 boolean isStatic = (access & Opcodes.ACC_STATIC) != 0; 140 boolean isNative = (access & Opcodes.ACC_NATIVE) != 0; 141 142 // remove abstract, final and native 143 access = access & ~(Opcodes.ACC_ABSTRACT | Opcodes.ACC_FINAL | Opcodes.ACC_NATIVE); 144 145 String invokeSignature = methodSignature + desc; 146 mLog.debug(" Stub: %s (%s)", invokeSignature, isNative ? "native" : ""); 147 148 MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions); 149 return new StubMethodAdapter(mw, name, returnType(desc), invokeSignature, 150 isStatic, isNative); 151 152 } else { 153 mLog.debug(" Keep: %s %s", name, desc); 154 return super.visitMethod(access, name, desc, signature, exceptions); 155 } 156 } 157 158 /* Visits a field. Makes it public. */ 159 @Override 160 public FieldVisitor visitField(int access, String name, String desc, String signature, 161 Object value) { 162 // change access to public 163 if (Main.sOptions.generatePublicAccess) { 164 access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE); 165 access |= Opcodes.ACC_PUBLIC; 166 } 167 return super.visitField(access, name, desc, signature, value); 168 } 169 170 /** 171 * Extracts the return {@link Type} of this descriptor. 172 */ 173 Type returnType(String desc) { 174 if (desc != null) { 175 try { 176 return Type.getReturnType(desc); 177 } catch (ArrayIndexOutOfBoundsException e) { 178 // ignore, not a valid type. 179 } 180 } 181 return null; 182 } 183} 184