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