StubMethodAdapter.java revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
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 mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, 78 "com/android/tools/layoutlib/create/OverrideMethod", 79 "invoke", 80 "(Ljava/lang/String;ZLjava/lang/Object;)V"); 81 } 82 83 private void generateReturn() { 84 /* Generates one of, depending on the return type: 85 * return; 86 * return 0; 87 * return 0L; 88 * return 0.0f; 89 * return 0.0; 90 * return null; 91 */ 92 switch(mReturnType != null ? mReturnType.getSort() : Type.VOID) { 93 case Type.VOID: 94 mParentVisitor.visitInsn(Opcodes.RETURN); 95 break; 96 case Type.BOOLEAN: 97 case Type.CHAR: 98 case Type.BYTE: 99 case Type.SHORT: 100 case Type.INT: 101 mParentVisitor.visitInsn(Opcodes.ICONST_0); 102 mParentVisitor.visitInsn(Opcodes.IRETURN); 103 break; 104 case Type.LONG: 105 mParentVisitor.visitInsn(Opcodes.LCONST_0); 106 mParentVisitor.visitInsn(Opcodes.LRETURN); 107 break; 108 case Type.FLOAT: 109 mParentVisitor.visitInsn(Opcodes.FCONST_0); 110 mParentVisitor.visitInsn(Opcodes.FRETURN); 111 break; 112 case Type.DOUBLE: 113 mParentVisitor.visitInsn(Opcodes.DCONST_0); 114 mParentVisitor.visitInsn(Opcodes.DRETURN); 115 break; 116 case Type.ARRAY: 117 case Type.OBJECT: 118 mParentVisitor.visitInsn(Opcodes.ACONST_NULL); 119 mParentVisitor.visitInsn(Opcodes.ARETURN); 120 break; 121 } 122 } 123 124 /* Pass down to visitor writer. In this implementation, either do nothing. */ 125 public void visitCode() { 126 mParentVisitor.visitCode(); 127 } 128 129 /* 130 * visitMaxs is called just before visitEnd if there was any code to rewrite. 131 * For non-constructor, generate the messaging code and the return statement 132 * if it hasn't been done before. 133 */ 134 public void visitMaxs(int maxStack, int maxLocals) { 135 if (!mIsInitMethod && !mMessageGenerated) { 136 generateInvoke(); 137 generateReturn(); 138 mMessageGenerated = true; 139 } 140 mParentVisitor.visitMaxs(maxStack, maxLocals); 141 } 142 143 /** 144 * End of visiting. 145 * For non-constructor, generate the messaging code and the return statement 146 * if it hasn't been done before. 147 */ 148 public void visitEnd() { 149 if (!mIsInitMethod && !mMessageGenerated) { 150 generateInvoke(); 151 generateReturn(); 152 mMessageGenerated = true; 153 mParentVisitor.visitMaxs(1, 1); 154 } 155 mParentVisitor.visitEnd(); 156 } 157 158 /* Writes all annotation from the original method. */ 159 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 160 return mParentVisitor.visitAnnotation(desc, visible); 161 } 162 163 /* Writes all annotation default values from the original method. */ 164 public AnnotationVisitor visitAnnotationDefault() { 165 return mParentVisitor.visitAnnotationDefault(); 166 } 167 168 public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, 169 boolean visible) { 170 return mParentVisitor.visitParameterAnnotation(parameter, desc, visible); 171 } 172 173 /* Writes all attributes from the original method. */ 174 public void visitAttribute(Attribute attr) { 175 mParentVisitor.visitAttribute(attr); 176 } 177 178 /* 179 * Only writes the first line number present in the original code so that source 180 * viewers can direct to the correct method, even if the content doesn't match. 181 */ 182 public void visitLineNumber(int line, Label start) { 183 if (mIsInitMethod || mOutputFirstLineNumber) { 184 mParentVisitor.visitLineNumber(line, start); 185 mOutputFirstLineNumber = false; 186 } 187 } 188 189 /** 190 * For non-constructor, rewrite existing "return" instructions to write the message. 191 */ 192 public void visitInsn(int opcode) { 193 if (mIsInitMethod) { 194 switch (opcode) { 195 case Opcodes.RETURN: 196 case Opcodes.ARETURN: 197 case Opcodes.DRETURN: 198 case Opcodes.FRETURN: 199 case Opcodes.IRETURN: 200 case Opcodes.LRETURN: 201 generateInvoke(); 202 mMessageGenerated = true; 203 } 204 mParentVisitor.visitInsn(opcode); 205 } 206 } 207 208 public void visitLabel(Label label) { 209 if (mIsInitMethod) { 210 mParentVisitor.visitLabel(label); 211 } 212 } 213 214 public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 215 if (mIsInitMethod) { 216 mParentVisitor.visitTryCatchBlock(start, end, handler, type); 217 } 218 } 219 220 public void visitMethodInsn(int opcode, String owner, String name, String desc) { 221 if (mIsInitMethod) { 222 mParentVisitor.visitMethodInsn(opcode, owner, name, desc); 223 } 224 } 225 226 public void visitFieldInsn(int opcode, String owner, String name, String desc) { 227 if (mIsInitMethod) { 228 mParentVisitor.visitFieldInsn(opcode, owner, name, desc); 229 } 230 } 231 232 public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { 233 if (mIsInitMethod) { 234 mParentVisitor.visitFrame(type, nLocal, local, nStack, stack); 235 } 236 } 237 238 public void visitIincInsn(int var, int increment) { 239 if (mIsInitMethod) { 240 mParentVisitor.visitIincInsn(var, increment); 241 } 242 } 243 244 public void visitIntInsn(int opcode, int operand) { 245 if (mIsInitMethod) { 246 mParentVisitor.visitIntInsn(opcode, operand); 247 } 248 } 249 250 public void visitJumpInsn(int opcode, Label label) { 251 if (mIsInitMethod) { 252 mParentVisitor.visitJumpInsn(opcode, label); 253 } 254 } 255 256 public void visitLdcInsn(Object cst) { 257 if (mIsInitMethod) { 258 mParentVisitor.visitLdcInsn(cst); 259 } 260 } 261 262 public void visitLocalVariable(String name, String desc, String signature, 263 Label start, Label end, int index) { 264 if (mIsInitMethod) { 265 mParentVisitor.visitLocalVariable(name, desc, signature, start, end, index); 266 } 267 } 268 269 public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { 270 if (mIsInitMethod) { 271 mParentVisitor.visitLookupSwitchInsn(dflt, keys, labels); 272 } 273 } 274 275 public void visitMultiANewArrayInsn(String desc, int dims) { 276 if (mIsInitMethod) { 277 mParentVisitor.visitMultiANewArrayInsn(desc, dims); 278 } 279 } 280 281 public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) { 282 if (mIsInitMethod) { 283 mParentVisitor.visitTableSwitchInsn(min, max, dflt, labels); 284 } 285 } 286 287 public void visitTypeInsn(int opcode, String type) { 288 if (mIsInitMethod) { 289 mParentVisitor.visitTypeInsn(opcode, type); 290 } 291 } 292 293 public void visitVarInsn(int opcode, int var) { 294 if (mIsInitMethod) { 295 mParentVisitor.visitVarInsn(opcode, var); 296 } 297 } 298 299} 300