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