DelegateMethodAdapter.java revision 9f63ff263b0a97f0fa63e97136c18f6abccbfc68
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 com.android.tools.layoutlib.annotations.LayoutlibDelegate; 20 21import org.objectweb.asm.AnnotationVisitor; 22import org.objectweb.asm.Attribute; 23import org.objectweb.asm.ClassReader; 24import org.objectweb.asm.ClassVisitor; 25import org.objectweb.asm.Label; 26import org.objectweb.asm.MethodVisitor; 27import org.objectweb.asm.Opcodes; 28import org.objectweb.asm.Type; 29 30/** 31 * This method adapter rewrites a method by discarding the original code and generating 32 * a call to a delegate. Original annotations are passed along unchanged. 33 * <p/> 34 * Calls are delegated to a class named <code><className>_Delegate</code> with 35 * static methods matching the methods to be overridden here. The methods have the 36 * same return type. The argument type list is the same except the "this" reference is 37 * passed first for non-static methods. 38 * <p/> 39 * A new annotation is added. 40 * <p/> 41 * Note that native methods have, by definition, no code so there's nothing a visitor 42 * can visit. That means the caller must call {@link #generateCode()} directly for 43 * a native and use the visitor pattern for non-natives. 44 * <p/> 45 * Instances of this class are not re-usable. You need a new instance for each method. 46 */ 47class DelegateMethodAdapter implements MethodVisitor { 48 49 /** 50 * Suffix added to delegate classes. 51 */ 52 public static final String DELEGATE_SUFFIX = "_Delegate"; 53 54 private static String CONSTRUCTOR = "<init>"; 55 private static String CLASS_INIT = "<clinit>"; 56 57 /** The parent method writer */ 58 private MethodVisitor mParentVisitor; 59 /** Flag to output the first line number. */ 60 private boolean mOutputFirstLineNumber = true; 61 /** The original method descriptor (return type + argument types.) */ 62 private String mDesc; 63 /** True if the original method is static. */ 64 private final boolean mIsStatic; 65 /** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */ 66 private final String mClassName; 67 /** The method name. */ 68 private final String mMethodName; 69 /** Logger object. */ 70 private final Log mLog; 71 /** True if {@link #visitCode()} has been invoked. */ 72 private boolean mVisitCodeCalled; 73 74 /** 75 * Creates a new {@link DelegateMethodAdapter} that will transform this method 76 * into a delegate call. 77 * <p/> 78 * See {@link DelegateMethodAdapter} for more details. 79 * 80 * @param log The logger object. Must not be null. 81 * @param mv the method visitor to which this adapter must delegate calls. 82 * @param className The internal class name of the class to visit, 83 * e.g. <code>com/android/SomeClass$InnerClass</code>. 84 * @param methodName The simple name of the method. 85 * @param desc A method descriptor (c.f. {@link Type#getReturnType(String)} + 86 * {@link Type#getArgumentTypes(String)}) 87 * @param isStatic True if the method is declared static. 88 */ 89 public DelegateMethodAdapter(Log log, 90 MethodVisitor mv, 91 String className, 92 String methodName, 93 String desc, 94 boolean isStatic) { 95 mLog = log; 96 mParentVisitor = mv; 97 mClassName = className; 98 mMethodName = methodName; 99 mDesc = desc; 100 mIsStatic = isStatic; 101 102 if (CONSTRUCTOR.equals(methodName) || CLASS_INIT.equals(methodName)) { 103 // We're going to simplify by not supporting constructors. 104 // The only trick with a constructor is to find the proper super constructor 105 // and call it (and deciding if we should mirror the original method call to 106 // a custom constructor or call a default one.) 107 throw new UnsupportedOperationException( 108 String.format("Delegate doesn't support overriding constructor %1$s:%2$s(%3$s)", 109 className, methodName, desc)); 110 } 111 } 112 113 /** 114 * Generates the new code for the method. 115 * <p/> 116 * For native methods, this must be invoked directly by {@link DelegateClassAdapter} 117 * (since they have no code to visit). 118 * <p/> 119 * Otherwise for non-native methods the {@link DelegateClassAdapter} simply needs to 120 * return this instance of {@link DelegateMethodAdapter} and let the normal visitor pattern 121 * invoke it as part of the {@link ClassReader#accept(ClassVisitor, int)} workflow and then 122 * this method will be invoked from {@link MethodVisitor#visitEnd()}. 123 */ 124 public void generateCode() { 125 /* 126 * The goal is to generate a call to a static delegate method. 127 * If this method is not-static, the first parameter will be this. 128 * All the parameters must be passed and then the eventual return type returned. 129 * 130 * Example, let's say we have a method such as 131 * public void method_1(int a, Object b, ArrayList<String> c) { ... } 132 * 133 * We'll want to create a body that calls a delegate method like this: 134 * TheClass_Delegate.method_1(this, a, b, c); 135 * 136 * The generated class name is the current class name with "_Delegate" appended to it. 137 * One thing to realize is that we don't care about generics -- since generic types 138 * are erased at runtime, they have no influence on the method being called. 139 */ 140 141 // Add our annotation 142 AnnotationVisitor aw = mParentVisitor.visitAnnotation( 143 Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(), 144 true); // visible at runtime 145 aw.visitEnd(); 146 147 if (!mVisitCodeCalled) { 148 // If this is a direct call to generateCode() as done by DelegateClassAdapter 149 // for natives, visitCode() hasn't been called yet. 150 mParentVisitor.visitCode(); 151 mVisitCodeCalled = true; 152 } 153 154 int numVars = 0; 155 156 // Push "this" for an instance method, which is always ALOAD 0 157 if (!mIsStatic) { 158 mParentVisitor.visitVarInsn(Opcodes.ALOAD, numVars++); 159 } 160 161 // Push all other arguments 162 Type[] argTypes = Type.getArgumentTypes(mDesc); 163 for (Type t : argTypes) { 164 int size = t.getSize(); 165 mParentVisitor.visitVarInsn(t.getOpcode(Opcodes.ILOAD), numVars); 166 numVars += size; 167 } 168 169 // Construct the descriptor of the delegate. For a static method, it's the same 170 // however for an instance method we need to pass the 'this' reference first 171 String desc = mDesc; 172 if (!mIsStatic) { 173 Type[] argTypes2 = new Type[argTypes.length + 1]; 174 175 argTypes2[0] = Type.getObjectType(mClassName); 176 System.arraycopy(argTypes, 0, argTypes2, 1, argTypes.length); 177 178 desc = Type.getMethodDescriptor(Type.getReturnType(mDesc), argTypes2); 179 } 180 181 String delegateClassName = mClassName + DELEGATE_SUFFIX; 182 183 // Invoke the static delegate 184 mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, 185 delegateClassName, 186 mMethodName, 187 desc); 188 189 Type returnType = Type.getReturnType(mDesc); 190 mParentVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); 191 192 mParentVisitor.visitMaxs(numVars, numVars); 193 mParentVisitor.visitEnd(); 194 195 // For debugging now. Maybe we should collect these and store them in 196 // a text file for helping create the delegates. We could also compare 197 // the text file to a golden and break the build on unsupported changes 198 // or regressions. Even better we could fancy-print something that looks 199 // like the expected Java method declaration. 200 mLog.debug("Delegate: %1$s # %2$s %3$s", delegateClassName, mMethodName, desc); 201 } 202 203 /* Pass down to visitor writer. In this implementation, either do nothing. */ 204 public void visitCode() { 205 mVisitCodeCalled = true; 206 mParentVisitor.visitCode(); 207 } 208 209 /* 210 * visitMaxs is called just before visitEnd if there was any code to rewrite. 211 * Skip the original. 212 */ 213 public void visitMaxs(int maxStack, int maxLocals) { 214 } 215 216 /** 217 * End of visiting. Generate the messaging code. 218 */ 219 public void visitEnd() { 220 generateCode(); 221 } 222 223 /* Writes all annotation from the original method. */ 224 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 225 return mParentVisitor.visitAnnotation(desc, visible); 226 } 227 228 /* Writes all annotation default values from the original method. */ 229 public AnnotationVisitor visitAnnotationDefault() { 230 return mParentVisitor.visitAnnotationDefault(); 231 } 232 233 public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, 234 boolean visible) { 235 return mParentVisitor.visitParameterAnnotation(parameter, desc, visible); 236 } 237 238 /* Writes all attributes from the original method. */ 239 public void visitAttribute(Attribute attr) { 240 mParentVisitor.visitAttribute(attr); 241 } 242 243 /* 244 * Only writes the first line number present in the original code so that source 245 * viewers can direct to the correct method, even if the content doesn't match. 246 */ 247 public void visitLineNumber(int line, Label start) { 248 if (mOutputFirstLineNumber) { 249 mParentVisitor.visitLineNumber(line, start); 250 mOutputFirstLineNumber = false; 251 } 252 } 253 254 public void visitInsn(int opcode) { 255 // Skip original code. 256 } 257 258 public void visitLabel(Label label) { 259 // Skip original code. 260 } 261 262 public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 263 // Skip original code. 264 } 265 266 public void visitMethodInsn(int opcode, String owner, String name, String desc) { 267 // Skip original code. 268 } 269 270 public void visitFieldInsn(int opcode, String owner, String name, String desc) { 271 // Skip original code. 272 } 273 274 public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { 275 // Skip original code. 276 } 277 278 public void visitIincInsn(int var, int increment) { 279 // Skip original code. 280 } 281 282 public void visitIntInsn(int opcode, int operand) { 283 // Skip original code. 284 } 285 286 public void visitJumpInsn(int opcode, Label label) { 287 // Skip original code. 288 } 289 290 public void visitLdcInsn(Object cst) { 291 // Skip original code. 292 } 293 294 public void visitLocalVariable(String name, String desc, String signature, 295 Label start, Label end, int index) { 296 // Skip original code. 297 } 298 299 public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { 300 // Skip original code. 301 } 302 303 public void visitMultiANewArrayInsn(String desc, int dims) { 304 // Skip original code. 305 } 306 307 public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) { 308 // Skip original code. 309 } 310 311 public void visitTypeInsn(int opcode, String type) { 312 // Skip original code. 313 } 314 315 public void visitVarInsn(int opcode, int var) { 316 // Skip original code. 317 } 318 319} 320