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