TransformClassAdapter.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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 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 access = access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED); 76 access |= Opcodes.ACC_PUBLIC; 77 // remove final 78 access = access & ~Opcodes.ACC_FINAL; 79 // note: leave abstract classes as such 80 // don't try to implement stub for interfaces 81 82 mIsInterface = ((access & Opcodes.ACC_INTERFACE) != 0); 83 super.visit(version, access, name, signature, superName, interfaces); 84 } 85 86 /* Visits the header of an inner class. */ 87 @Override 88 public void visitInnerClass(String name, String outerName, String innerName, int access) { 89 // remove protected or private and set as public 90 access = access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED); 91 access |= Opcodes.ACC_PUBLIC; 92 // remove final 93 access = access & ~Opcodes.ACC_FINAL; 94 // note: leave abstract classes as such 95 // don't try to implement stub for interfaces 96 97 super.visitInnerClass(name, outerName, innerName, access); 98 } 99 100 /* Visits a method. */ 101 @Override 102 public MethodVisitor visitMethod(int access, String name, String desc, 103 String signature, String[] exceptions) { 104 105 if (mDeleteReturns != null) { 106 Type t = Type.getReturnType(desc); 107 if (t.getSort() == Type.OBJECT) { 108 String returnType = t.getInternalName(); 109 if (returnType != null) { 110 if (mDeleteReturns.contains(returnType)) { 111 return null; 112 } 113 } 114 } 115 } 116 117 String methodSignature = mClassName.replace('/', '.') + "#" + name; 118 119 // change access to public 120 access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE); 121 access |= Opcodes.ACC_PUBLIC; 122 123 // remove final 124 access = access & ~Opcodes.ACC_FINAL; 125 126 // stub this method if they are all to be stubbed or if it is a native method 127 // and don't try to stub interfaces nor abstract non-native methods. 128 if (!mIsInterface && 129 ((access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) != Opcodes.ACC_ABSTRACT) && 130 (mStubAll || 131 (access & Opcodes.ACC_NATIVE) != 0) || 132 mStubMethods.contains(methodSignature)) { 133 134 // remove abstract, final and native 135 access = access & ~(Opcodes.ACC_ABSTRACT | Opcodes.ACC_FINAL | Opcodes.ACC_NATIVE); 136 137 boolean isStatic = (access & Opcodes.ACC_STATIC) != 0; 138 139 String invokeSignature = methodSignature + desc; 140 mLog.debug(" Stub: %s", invokeSignature); 141 142 MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions); 143 return new StubMethodAdapter(mw, name, returnType(desc), invokeSignature, isStatic); 144 145 } else { 146 mLog.debug(" Keep: %s %s", name, desc); 147 return super.visitMethod(access, name, desc, signature, exceptions); 148 } 149 } 150 151 /* Visits a field. Makes it public. */ 152 @Override 153 public FieldVisitor visitField(int access, String name, String desc, String signature, 154 Object value) { 155 // change access to public 156 access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE); 157 access |= Opcodes.ACC_PUBLIC; 158 159 return super.visitField(access, name, desc, signature, value); 160 } 161 162 /** 163 * Extracts the return {@link Type} of this descriptor. 164 */ 165 Type returnType(String desc) { 166 if (desc != null) { 167 try { 168 return Type.getReturnType(desc); 169 } catch (ArrayIndexOutOfBoundsException e) { 170 // ignore, not a valid type. 171 } 172 } 173 return null; 174 } 175} 176