DelegateMethodAdapter.java revision 6777f54fa44341dd4b23456c97d97c6e4ffe915f
1/* 2 * Copyright (C) 2010 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 30import java.util.ArrayList; 31 32/** 33 * This method adapter generates delegate methods. 34 * <p/> 35 * Given a method {@code SomeClass.MethodName()}, this generates 1 or 2 methods: 36 * <ul> 37 * <li> A copy of the original method named {@code SomeClass.MethodName_Original()}. 38 * The content is the original method as-is from the reader. 39 * This step is omitted if the method is native, since it has no Java implementation. 40 * <li> A brand new implementation of {@code SomeClass.MethodName()} which calls to a 41 * non-existing method named {@code SomeClass_Delegate.MethodName()}. 42 * The implementation of this 'delegate' method is done in layoutlib_brigde. 43 * </ul> 44 * A method visitor is generally constructed to generate a single method; however 45 * here we might want to generate one or two depending on the context. To achieve 46 * that, the visitor here generates the 'original' method and acts as a no-op if 47 * no such method exists (e.g. when the original is a native method). 48 * The delegate method is generated after the {@code visitEnd} of the original method 49 * or by having the class adapter <em>directly</em> call {@link #generateDelegateCode()} 50 * for native methods. 51 * <p/> 52 * When generating the 'delegate', the implementation generates a call to a class 53 * class named <code><className>_Delegate</code> with static methods matching 54 * the methods to be overridden here. The methods have the same return type. 55 * The argument type list is the same except the "this" reference is passed first 56 * for non-static methods. 57 * <p/> 58 * A new annotation is added to these 'delegate' methods so that we can easily find them 59 * for automated testing. 60 * <p/> 61 * This class isn't intended to be generic or reusable. 62 * It is called by {@link DelegateClassAdapter}, which takes care of properly initializing 63 * the two method writers for the original and the delegate class, as needed, with their 64 * expected names. 65 * <p/> 66 * The class adapter also takes care of calling {@link #generateDelegateCode()} directly for 67 * a native and use the visitor pattern for non-natives. 68 * Note that native methods have, by definition, no code so there's nothing a visitor 69 * can visit. 70 * <p/> 71 * Instances of this class are not re-usable. 72 * The class adapter creates a new instance for each method. 73 */ 74class DelegateMethodAdapter extends MethodVisitor { 75 76 /** Suffix added to delegate classes. */ 77 public static final String DELEGATE_SUFFIX = "_Delegate"; 78 79 /** The parent method writer to copy of the original method. 80 * Null when dealing with a native original method. */ 81 private MethodVisitor mOrgWriter; 82 /** The parent method writer to generate the delegating method. Never null. */ 83 private MethodVisitor mDelWriter; 84 /** The original method descriptor (return type + argument types.) */ 85 private String mDesc; 86 /** True if the original method is static. */ 87 private final boolean mIsStatic; 88 /** True if the method is contained in a static inner class */ 89 private final boolean mIsStaticInnerClass; 90 /** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */ 91 private final String mClassName; 92 /** The method name. */ 93 private final String mMethodName; 94 /** Logger object. */ 95 private final Log mLog; 96 97 /** Array used to capture the first line number information from the original method 98 * and duplicate it in the delegate. */ 99 private Object[] mDelegateLineNumber; 100 101 /** 102 * Creates a new {@link DelegateMethodAdapter} that will transform this method 103 * into a delegate call. 104 * <p/> 105 * See {@link DelegateMethodAdapter} for more details. 106 * 107 * @param log The logger object. Must not be null. 108 * @param mvOriginal The parent method writer to copy of the original method. 109 * Must be {@code null} when dealing with a native original method. 110 * @param mvDelegate The parent method writer to generate the delegating method. 111 * Must never be null. 112 * @param className The internal class name of the class to visit, 113 * e.g. <code>com/android/SomeClass$InnerClass</code>. 114 * @param methodName The simple name of the method. 115 * @param desc A method descriptor (c.f. {@link Type#getReturnType(String)} + 116 * {@link Type#getArgumentTypes(String)}) 117 * @param isStatic True if the method is declared static. 118 */ 119 public DelegateMethodAdapter(Log log, 120 MethodVisitor mvOriginal, 121 MethodVisitor mvDelegate, 122 String className, 123 String methodName, 124 String desc, 125 boolean isStatic, 126 boolean isStaticClass) { 127 super(Opcodes.ASM4); 128 mLog = log; 129 mOrgWriter = mvOriginal; 130 mDelWriter = mvDelegate; 131 mClassName = className; 132 mMethodName = methodName; 133 mDesc = desc; 134 mIsStatic = isStatic; 135 mIsStaticInnerClass = isStaticClass; 136 } 137 138 /** 139 * Generates the new code for the method. 140 * <p/> 141 * For native methods, this must be invoked directly by {@link DelegateClassAdapter} 142 * (since they have no code to visit). 143 * <p/> 144 * Otherwise for non-native methods the {@link DelegateClassAdapter} simply needs to 145 * return this instance of {@link DelegateMethodAdapter} and let the normal visitor pattern 146 * invoke it as part of the {@link ClassReader#accept(ClassVisitor, int)} workflow and then 147 * this method will be invoked from {@link MethodVisitor#visitEnd()}. 148 */ 149 public void generateDelegateCode() { 150 /* 151 * The goal is to generate a call to a static delegate method. 152 * If this method is non-static, the first parameter will be 'this'. 153 * All the parameters must be passed and then the eventual return type returned. 154 * 155 * Example, let's say we have a method such as 156 * public void myMethod(int a, Object b, ArrayList<String> c) { ... } 157 * 158 * We'll want to create a body that calls a delegate method like this: 159 * TheClass_Delegate.myMethod(this, a, b, c); 160 * 161 * If the method is non-static and the class name is an inner class (e.g. has $ in its 162 * last segment), we want to push the 'this' of the outer class first: 163 * OuterClass_InnerClass_Delegate.myMethod( 164 * OuterClass.this, 165 * OuterClass$InnerClass.this, 166 * a, b, c); 167 * 168 * Only one level of inner class is supported right now, for simplicity and because 169 * we don't need more. 170 * 171 * The generated class name is the current class name with "_Delegate" appended to it. 172 * One thing to realize is that we don't care about generics -- since generic types 173 * are erased at build time, they have no influence on the method name being called. 174 */ 175 176 // Add our annotation 177 AnnotationVisitor aw = mDelWriter.visitAnnotation( 178 Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(), 179 true); // visible at runtime 180 if (aw != null) { 181 aw.visitEnd(); 182 } 183 184 mDelWriter.visitCode(); 185 186 if (mDelegateLineNumber != null) { 187 Object[] p = mDelegateLineNumber; 188 mDelWriter.visitLineNumber((Integer) p[0], (Label) p[1]); 189 } 190 191 ArrayList<Type> paramTypes = new ArrayList<Type>(); 192 String delegateClassName = mClassName + DELEGATE_SUFFIX; 193 boolean pushedArg0 = false; 194 int maxStack = 0; 195 196 // Check if the last segment of the class name has inner an class. 197 // Right now we only support one level of inner classes. 198 Type outerType = null; 199 int slash = mClassName.lastIndexOf('/'); 200 int dol = mClassName.lastIndexOf('$'); 201 if (dol != -1 && dol > slash && dol == mClassName.indexOf('$')) { 202 String outerClass = mClassName.substring(0, dol); 203 outerType = Type.getObjectType(outerClass); 204 205 // Change a delegate class name to "com/foo/Outer_Inner_Delegate" 206 delegateClassName = delegateClassName.replace('$', '_'); 207 } 208 209 // For an instance method (e.g. non-static), push the 'this' preceded 210 // by the 'this' of any outer class, if any. 211 if (!mIsStatic) { 212 213 if (outerType != null && !mIsStaticInnerClass) { 214 // The first-level inner class has a package-protected member called 'this$0' 215 // that points to the outer class. 216 217 // Push this.getField("this$0") on the call stack. 218 mDelWriter.visitVarInsn(Opcodes.ALOAD, 0); // var 0 = this 219 mDelWriter.visitFieldInsn(Opcodes.GETFIELD, 220 mClassName, // class where the field is defined 221 "this$0", // field name 222 outerType.getDescriptor()); // type of the field 223 maxStack++; 224 paramTypes.add(outerType); 225 226 } 227 228 // Push "this" for the instance method, which is always ALOAD 0 229 mDelWriter.visitVarInsn(Opcodes.ALOAD, 0); 230 maxStack++; 231 pushedArg0 = true; 232 paramTypes.add(Type.getObjectType(mClassName)); 233 } 234 235 // Push all other arguments. Start at arg 1 if we already pushed 'this' above. 236 Type[] argTypes = Type.getArgumentTypes(mDesc); 237 int maxLocals = pushedArg0 ? 1 : 0; 238 for (Type t : argTypes) { 239 int size = t.getSize(); 240 mDelWriter.visitVarInsn(t.getOpcode(Opcodes.ILOAD), maxLocals); 241 maxLocals += size; 242 maxStack += size; 243 paramTypes.add(t); 244 } 245 246 // Construct the descriptor of the delegate based on the parameters 247 // we pushed on the call stack. The return type remains unchanged. 248 String desc = Type.getMethodDescriptor( 249 Type.getReturnType(mDesc), 250 paramTypes.toArray(new Type[paramTypes.size()])); 251 252 // Invoke the static delegate 253 mDelWriter.visitMethodInsn(Opcodes.INVOKESTATIC, 254 delegateClassName, 255 mMethodName, 256 desc); 257 258 Type returnType = Type.getReturnType(mDesc); 259 mDelWriter.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); 260 261 mDelWriter.visitMaxs(maxStack, maxLocals); 262 mDelWriter.visitEnd(); 263 264 // For debugging now. Maybe we should collect these and store them in 265 // a text file for helping create the delegates. We could also compare 266 // the text file to a golden and break the build on unsupported changes 267 // or regressions. Even better we could fancy-print something that looks 268 // like the expected Java method declaration. 269 mLog.debug("Delegate: %1$s # %2$s %3$s", delegateClassName, mMethodName, desc); 270 } 271 272 /* Pass down to visitor writer. In this implementation, either do nothing. */ 273 @Override 274 public void visitCode() { 275 if (mOrgWriter != null) { 276 mOrgWriter.visitCode(); 277 } 278 } 279 280 /* 281 * visitMaxs is called just before visitEnd if there was any code to rewrite. 282 */ 283 @Override 284 public void visitMaxs(int maxStack, int maxLocals) { 285 if (mOrgWriter != null) { 286 mOrgWriter.visitMaxs(maxStack, maxLocals); 287 } 288 } 289 290 /** End of visiting. Generate the delegating code. */ 291 @Override 292 public void visitEnd() { 293 if (mOrgWriter != null) { 294 mOrgWriter.visitEnd(); 295 } 296 generateDelegateCode(); 297 } 298 299 /* Writes all annotation from the original method. */ 300 @Override 301 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 302 if (mOrgWriter != null) { 303 return mOrgWriter.visitAnnotation(desc, visible); 304 } else { 305 return null; 306 } 307 } 308 309 /* Writes all annotation default values from the original method. */ 310 @Override 311 public AnnotationVisitor visitAnnotationDefault() { 312 if (mOrgWriter != null) { 313 return mOrgWriter.visitAnnotationDefault(); 314 } else { 315 return null; 316 } 317 } 318 319 @Override 320 public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, 321 boolean visible) { 322 if (mOrgWriter != null) { 323 return mOrgWriter.visitParameterAnnotation(parameter, desc, visible); 324 } else { 325 return null; 326 } 327 } 328 329 /* Writes all attributes from the original method. */ 330 @Override 331 public void visitAttribute(Attribute attr) { 332 if (mOrgWriter != null) { 333 mOrgWriter.visitAttribute(attr); 334 } 335 } 336 337 /* 338 * Only writes the first line number present in the original code so that source 339 * viewers can direct to the correct method, even if the content doesn't match. 340 */ 341 @Override 342 public void visitLineNumber(int line, Label start) { 343 // Capture the first line values for the new delegate method 344 if (mDelegateLineNumber == null) { 345 mDelegateLineNumber = new Object[] { line, start }; 346 } 347 if (mOrgWriter != null) { 348 mOrgWriter.visitLineNumber(line, start); 349 } 350 } 351 352 @Override 353 public void visitInsn(int opcode) { 354 if (mOrgWriter != null) { 355 mOrgWriter.visitInsn(opcode); 356 } 357 } 358 359 @Override 360 public void visitLabel(Label label) { 361 if (mOrgWriter != null) { 362 mOrgWriter.visitLabel(label); 363 } 364 } 365 366 @Override 367 public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 368 if (mOrgWriter != null) { 369 mOrgWriter.visitTryCatchBlock(start, end, handler, type); 370 } 371 } 372 373 @Override 374 public void visitMethodInsn(int opcode, String owner, String name, String desc) { 375 if (mOrgWriter != null) { 376 mOrgWriter.visitMethodInsn(opcode, owner, name, desc); 377 } 378 } 379 380 @Override 381 public void visitFieldInsn(int opcode, String owner, String name, String desc) { 382 if (mOrgWriter != null) { 383 mOrgWriter.visitFieldInsn(opcode, owner, name, desc); 384 } 385 } 386 387 @Override 388 public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { 389 if (mOrgWriter != null) { 390 mOrgWriter.visitFrame(type, nLocal, local, nStack, stack); 391 } 392 } 393 394 @Override 395 public void visitIincInsn(int var, int increment) { 396 if (mOrgWriter != null) { 397 mOrgWriter.visitIincInsn(var, increment); 398 } 399 } 400 401 @Override 402 public void visitIntInsn(int opcode, int operand) { 403 if (mOrgWriter != null) { 404 mOrgWriter.visitIntInsn(opcode, operand); 405 } 406 } 407 408 @Override 409 public void visitJumpInsn(int opcode, Label label) { 410 if (mOrgWriter != null) { 411 mOrgWriter.visitJumpInsn(opcode, label); 412 } 413 } 414 415 @Override 416 public void visitLdcInsn(Object cst) { 417 if (mOrgWriter != null) { 418 mOrgWriter.visitLdcInsn(cst); 419 } 420 } 421 422 @Override 423 public void visitLocalVariable(String name, String desc, String signature, 424 Label start, Label end, int index) { 425 if (mOrgWriter != null) { 426 mOrgWriter.visitLocalVariable(name, desc, signature, start, end, index); 427 } 428 } 429 430 @Override 431 public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { 432 if (mOrgWriter != null) { 433 mOrgWriter.visitLookupSwitchInsn(dflt, keys, labels); 434 } 435 } 436 437 @Override 438 public void visitMultiANewArrayInsn(String desc, int dims) { 439 if (mOrgWriter != null) { 440 mOrgWriter.visitMultiANewArrayInsn(desc, dims); 441 } 442 } 443 444 @Override 445 public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) { 446 if (mOrgWriter != null) { 447 mOrgWriter.visitTableSwitchInsn(min, max, dflt, labels); 448 } 449 } 450 451 @Override 452 public void visitTypeInsn(int opcode, String type) { 453 if (mOrgWriter != null) { 454 mOrgWriter.visitTypeInsn(opcode, type); 455 } 456 } 457 458 @Override 459 public void visitVarInsn(int opcode, int var) { 460 if (mOrgWriter != null) { 461 mOrgWriter.visitVarInsn(opcode, var); 462 } 463 } 464 465} 466