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.AnnotationVisitor; 20import org.objectweb.asm.Attribute; 21import org.objectweb.asm.Label; 22import org.objectweb.asm.MethodVisitor; 23import org.objectweb.asm.Opcodes; 24import org.objectweb.asm.Type; 25 26/** 27 * This method adapter rewrites a method by discarding the original code and generating 28 * a stub depending on the return type. Original annotations are passed along unchanged. 29 */ 30class StubMethodAdapter extends MethodVisitor { 31 32 private static String CONSTRUCTOR = "<init>"; 33 private static String CLASS_INIT = "<clinit>"; 34 35 /** The parent method writer */ 36 private MethodVisitor mParentVisitor; 37 /** The method return type. Can be null. */ 38 private Type mReturnType; 39 /** Message to be printed by stub methods. */ 40 private String mInvokeSignature; 41 /** Flag to output the first line number. */ 42 private boolean mOutputFirstLineNumber = true; 43 /** Flag that is true when implementing a constructor, to accept all original 44 * code calling the original super constructor. */ 45 private boolean mIsInitMethod = false; 46 47 private boolean mMessageGenerated; 48 private final boolean mIsStatic; 49 private final boolean mIsNative; 50 51 public StubMethodAdapter(MethodVisitor mv, String methodName, Type returnType, 52 String invokeSignature, boolean isStatic, boolean isNative) { 53 super(Opcodes.ASM4); 54 mParentVisitor = mv; 55 mReturnType = returnType; 56 mInvokeSignature = invokeSignature; 57 mIsStatic = isStatic; 58 mIsNative = isNative; 59 60 if (CONSTRUCTOR.equals(methodName) || CLASS_INIT.equals(methodName)) { 61 mIsInitMethod = true; 62 } 63 } 64 65 private void generateInvoke() { 66 /* Generates the code: 67 * OverrideMethod.invoke("signature", mIsNative ? true : false, null or this); 68 */ 69 mParentVisitor.visitLdcInsn(mInvokeSignature); 70 // push true or false 71 mParentVisitor.visitInsn(mIsNative ? Opcodes.ICONST_1 : Opcodes.ICONST_0); 72 // push null or this 73 if (mIsStatic) { 74 mParentVisitor.visitInsn(Opcodes.ACONST_NULL); 75 } else { 76 mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0); 77 } 78 79 int sort = mReturnType != null ? mReturnType.getSort() : Type.VOID; 80 switch(sort) { 81 case Type.VOID: 82 mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, 83 "com/android/tools/layoutlib/create/OverrideMethod", 84 "invokeV", 85 "(Ljava/lang/String;ZLjava/lang/Object;)V"); 86 mParentVisitor.visitInsn(Opcodes.RETURN); 87 break; 88 case Type.BOOLEAN: 89 case Type.CHAR: 90 case Type.BYTE: 91 case Type.SHORT: 92 case Type.INT: 93 mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, 94 "com/android/tools/layoutlib/create/OverrideMethod", 95 "invokeI", 96 "(Ljava/lang/String;ZLjava/lang/Object;)I"); 97 switch(sort) { 98 case Type.BOOLEAN: 99 Label l1 = new Label(); 100 mParentVisitor.visitJumpInsn(Opcodes.IFEQ, l1); 101 mParentVisitor.visitInsn(Opcodes.ICONST_1); 102 mParentVisitor.visitInsn(Opcodes.IRETURN); 103 mParentVisitor.visitLabel(l1); 104 mParentVisitor.visitInsn(Opcodes.ICONST_0); 105 break; 106 case Type.CHAR: 107 mParentVisitor.visitInsn(Opcodes.I2C); 108 break; 109 case Type.BYTE: 110 mParentVisitor.visitInsn(Opcodes.I2B); 111 break; 112 case Type.SHORT: 113 mParentVisitor.visitInsn(Opcodes.I2S); 114 break; 115 } 116 mParentVisitor.visitInsn(Opcodes.IRETURN); 117 break; 118 case Type.LONG: 119 mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, 120 "com/android/tools/layoutlib/create/OverrideMethod", 121 "invokeL", 122 "(Ljava/lang/String;ZLjava/lang/Object;)J"); 123 mParentVisitor.visitInsn(Opcodes.LRETURN); 124 break; 125 case Type.FLOAT: 126 mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, 127 "com/android/tools/layoutlib/create/OverrideMethod", 128 "invokeF", 129 "(Ljava/lang/String;ZLjava/lang/Object;)F"); 130 mParentVisitor.visitInsn(Opcodes.FRETURN); 131 break; 132 case Type.DOUBLE: 133 mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, 134 "com/android/tools/layoutlib/create/OverrideMethod", 135 "invokeD", 136 "(Ljava/lang/String;ZLjava/lang/Object;)D"); 137 mParentVisitor.visitInsn(Opcodes.DRETURN); 138 break; 139 case Type.ARRAY: 140 case Type.OBJECT: 141 mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, 142 "com/android/tools/layoutlib/create/OverrideMethod", 143 "invokeA", 144 "(Ljava/lang/String;ZLjava/lang/Object;)Ljava/lang/Object;"); 145 mParentVisitor.visitTypeInsn(Opcodes.CHECKCAST, mReturnType.getInternalName()); 146 mParentVisitor.visitInsn(Opcodes.ARETURN); 147 break; 148 } 149 150 } 151 152 private void generatePop() { 153 /* Pops the stack, depending on the return type. 154 */ 155 switch(mReturnType != null ? mReturnType.getSort() : Type.VOID) { 156 case Type.VOID: 157 break; 158 case Type.BOOLEAN: 159 case Type.CHAR: 160 case Type.BYTE: 161 case Type.SHORT: 162 case Type.INT: 163 case Type.FLOAT: 164 case Type.ARRAY: 165 case Type.OBJECT: 166 mParentVisitor.visitInsn(Opcodes.POP); 167 break; 168 case Type.LONG: 169 case Type.DOUBLE: 170 mParentVisitor.visitInsn(Opcodes.POP2); 171 break; 172 } 173 } 174 175 /* Pass down to visitor writer. In this implementation, either do nothing. */ 176 @Override 177 public void visitCode() { 178 mParentVisitor.visitCode(); 179 } 180 181 /* 182 * visitMaxs is called just before visitEnd if there was any code to rewrite. 183 * For non-constructor, generate the messaging code and the return statement 184 * if it hasn't been done before. 185 */ 186 @Override 187 public void visitMaxs(int maxStack, int maxLocals) { 188 if (!mIsInitMethod && !mMessageGenerated) { 189 generateInvoke(); 190 mMessageGenerated = true; 191 } 192 mParentVisitor.visitMaxs(maxStack, maxLocals); 193 } 194 195 /** 196 * End of visiting. 197 * For non-constructor, generate the messaging code and the return statement 198 * if it hasn't been done before. 199 */ 200 @Override 201 public void visitEnd() { 202 if (!mIsInitMethod && !mMessageGenerated) { 203 generateInvoke(); 204 mMessageGenerated = true; 205 mParentVisitor.visitMaxs(1, 1); 206 } 207 mParentVisitor.visitEnd(); 208 } 209 210 /* Writes all annotation from the original method. */ 211 @Override 212 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 213 return mParentVisitor.visitAnnotation(desc, visible); 214 } 215 216 /* Writes all annotation default values from the original method. */ 217 @Override 218 public AnnotationVisitor visitAnnotationDefault() { 219 return mParentVisitor.visitAnnotationDefault(); 220 } 221 222 @Override 223 public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, 224 boolean visible) { 225 return mParentVisitor.visitParameterAnnotation(parameter, desc, visible); 226 } 227 228 /* Writes all attributes from the original method. */ 229 @Override 230 public void visitAttribute(Attribute attr) { 231 mParentVisitor.visitAttribute(attr); 232 } 233 234 /* 235 * Only writes the first line number present in the original code so that source 236 * viewers can direct to the correct method, even if the content doesn't match. 237 */ 238 @Override 239 public void visitLineNumber(int line, Label start) { 240 if (mIsInitMethod || mOutputFirstLineNumber) { 241 mParentVisitor.visitLineNumber(line, start); 242 mOutputFirstLineNumber = false; 243 } 244 } 245 246 /** 247 * For non-constructor, rewrite existing "return" instructions to write the message. 248 */ 249 @Override 250 public void visitInsn(int opcode) { 251 if (mIsInitMethod) { 252 switch (opcode) { 253 case Opcodes.RETURN: 254 case Opcodes.ARETURN: 255 case Opcodes.DRETURN: 256 case Opcodes.FRETURN: 257 case Opcodes.IRETURN: 258 case Opcodes.LRETURN: 259 // Pop the last word from the stack since invoke will generate its own return. 260 generatePop(); 261 generateInvoke(); 262 mMessageGenerated = true; 263 //$FALL-THROUGH$ 264 default: 265 mParentVisitor.visitInsn(opcode); 266 } 267 } 268 } 269 270 @Override 271 public void visitLabel(Label label) { 272 if (mIsInitMethod) { 273 mParentVisitor.visitLabel(label); 274 } 275 } 276 277 @Override 278 public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 279 if (mIsInitMethod) { 280 mParentVisitor.visitTryCatchBlock(start, end, handler, type); 281 } 282 } 283 284 @Override 285 public void visitMethodInsn(int opcode, String owner, String name, String desc) { 286 if (mIsInitMethod) { 287 mParentVisitor.visitMethodInsn(opcode, owner, name, desc); 288 } 289 } 290 291 @Override 292 public void visitFieldInsn(int opcode, String owner, String name, String desc) { 293 if (mIsInitMethod) { 294 mParentVisitor.visitFieldInsn(opcode, owner, name, desc); 295 } 296 } 297 298 @Override 299 public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { 300 if (mIsInitMethod) { 301 mParentVisitor.visitFrame(type, nLocal, local, nStack, stack); 302 } 303 } 304 305 @Override 306 public void visitIincInsn(int var, int increment) { 307 if (mIsInitMethod) { 308 mParentVisitor.visitIincInsn(var, increment); 309 } 310 } 311 312 @Override 313 public void visitIntInsn(int opcode, int operand) { 314 if (mIsInitMethod) { 315 mParentVisitor.visitIntInsn(opcode, operand); 316 } 317 } 318 319 @Override 320 public void visitJumpInsn(int opcode, Label label) { 321 if (mIsInitMethod) { 322 mParentVisitor.visitJumpInsn(opcode, label); 323 } 324 } 325 326 @Override 327 public void visitLdcInsn(Object cst) { 328 if (mIsInitMethod) { 329 mParentVisitor.visitLdcInsn(cst); 330 } 331 } 332 333 @Override 334 public void visitLocalVariable(String name, String desc, String signature, 335 Label start, Label end, int index) { 336 if (mIsInitMethod) { 337 mParentVisitor.visitLocalVariable(name, desc, signature, start, end, index); 338 } 339 } 340 341 @Override 342 public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { 343 if (mIsInitMethod) { 344 mParentVisitor.visitLookupSwitchInsn(dflt, keys, labels); 345 } 346 } 347 348 @Override 349 public void visitMultiANewArrayInsn(String desc, int dims) { 350 if (mIsInitMethod) { 351 mParentVisitor.visitMultiANewArrayInsn(desc, dims); 352 } 353 } 354 355 @Override 356 public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) { 357 if (mIsInitMethod) { 358 mParentVisitor.visitTableSwitchInsn(min, max, dflt, labels); 359 } 360 } 361 362 @Override 363 public void visitTypeInsn(int opcode, String type) { 364 if (mIsInitMethod) { 365 mParentVisitor.visitTypeInsn(opcode, type); 366 } 367 } 368 369 @Override 370 public void visitVarInsn(int opcode, int var) { 371 if (mIsInitMethod) { 372 mParentVisitor.visitVarInsn(opcode, var); 373 } 374 } 375 376} 377