1/* 2 * Copyright (C) 2013 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.ClassVisitor; 21import org.objectweb.asm.FieldVisitor; 22import org.objectweb.asm.Label; 23import org.objectweb.asm.MethodVisitor; 24import org.objectweb.asm.Opcodes; 25import org.objectweb.asm.Type; 26import org.objectweb.asm.signature.SignatureReader; 27import org.objectweb.asm.signature.SignatureVisitor; 28import org.objectweb.asm.signature.SignatureWriter; 29 30/** 31 * Provides the common code for RenameClassAdapter and RefactorClassAdapter. It 32 * goes through the complete class and finds references to other classes. It 33 * then calls {@link #renameInternalType(String)} to convert the className to 34 * the new value, if need be. 35 */ 36public abstract class AbstractClassAdapter extends ClassVisitor { 37 38 /** 39 * Returns the new FQCN for the class, if the reference to this class needs 40 * to be updated. Else, it returns the same string. 41 * @param name Old FQCN 42 * @return New FQCN if it needs to be renamed, else the old FQCN 43 */ 44 abstract String renameInternalType(String name); 45 46 public AbstractClassAdapter(ClassVisitor cv) { 47 super(Opcodes.ASM4, cv); 48 } 49 50 /** 51 * Renames a type descriptor, e.g. "Lcom.package.MyClass;" 52 * If the type doesn't need to be renamed, returns the input string as-is. 53 */ 54 String renameTypeDesc(String desc) { 55 if (desc == null) { 56 return null; 57 } 58 59 return renameType(Type.getType(desc)); 60 } 61 62 /** 63 * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an 64 * object element, e.g. "[Lcom.package.MyClass;" 65 * If the type doesn't need to be renamed, returns the internal name of the input type. 66 */ 67 String renameType(Type type) { 68 if (type == null) { 69 return null; 70 } 71 72 if (type.getSort() == Type.OBJECT) { 73 String in = type.getInternalName(); 74 return "L" + renameInternalType(in) + ";"; 75 } else if (type.getSort() == Type.ARRAY) { 76 StringBuilder sb = new StringBuilder(); 77 for (int n = type.getDimensions(); n > 0; n--) { 78 sb.append('['); 79 } 80 sb.append(renameType(type.getElementType())); 81 return sb.toString(); 82 } 83 return type.getDescriptor(); 84 } 85 86 /** 87 * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an 88 * object element, e.g. "[Lcom.package.MyClass;". 89 * This is like renameType() except that it returns a Type object. 90 * If the type doesn't need to be renamed, returns the input type object. 91 */ 92 Type renameTypeAsType(Type type) { 93 if (type == null) { 94 return null; 95 } 96 97 if (type.getSort() == Type.OBJECT) { 98 String in = type.getInternalName(); 99 String newIn = renameInternalType(in); 100 if (!newIn.equals(in)) { 101 return Type.getType("L" + newIn + ";"); 102 } 103 } else if (type.getSort() == Type.ARRAY) { 104 StringBuilder sb = new StringBuilder(); 105 for (int n = type.getDimensions(); n > 0; n--) { 106 sb.append('['); 107 } 108 sb.append(renameType(type.getElementType())); 109 return Type.getType(sb.toString()); 110 } 111 return type; 112 } 113 114 /** 115 * Renames a method descriptor, i.e. applies renameType to all arguments and to the 116 * return value. 117 */ 118 String renameMethodDesc(String desc) { 119 if (desc == null) { 120 return null; 121 } 122 123 Type[] args = Type.getArgumentTypes(desc); 124 125 StringBuilder sb = new StringBuilder("("); 126 for (Type arg : args) { 127 String name = renameType(arg); 128 sb.append(name); 129 } 130 sb.append(')'); 131 132 Type ret = Type.getReturnType(desc); 133 String name = renameType(ret); 134 sb.append(name); 135 136 return sb.toString(); 137 } 138 139 140 /** 141 * Renames the ClassSignature handled by ClassVisitor.visit 142 * or the MethodTypeSignature handled by ClassVisitor.visitMethod. 143 */ 144 String renameTypeSignature(String sig) { 145 if (sig == null) { 146 return null; 147 } 148 SignatureReader reader = new SignatureReader(sig); 149 SignatureWriter writer = new SignatureWriter(); 150 reader.accept(new RenameSignatureAdapter(writer)); 151 sig = writer.toString(); 152 return sig; 153 } 154 155 156 /** 157 * Renames the FieldTypeSignature handled by ClassVisitor.visitField 158 * or MethodVisitor.visitLocalVariable. 159 */ 160 String renameFieldSignature(String sig) { 161 return renameTypeSignature(sig); 162 } 163 164 165 //---------------------------------- 166 // Methods from the ClassAdapter 167 168 @Override 169 public void visit(int version, int access, String name, String signature, 170 String superName, String[] interfaces) { 171 name = renameInternalType(name); 172 superName = renameInternalType(superName); 173 signature = renameTypeSignature(signature); 174 if (interfaces != null) { 175 for (int i = 0; i < interfaces.length; ++i) { 176 interfaces[i] = renameInternalType(interfaces[i]); 177 } 178 } 179 180 /* Java 7 verifies the StackMapTable of a class if its version number is greater than 50.0. 181 * However, the check is disabled if the class version number is 50.0 or less. Generation 182 * of the StackMapTable requires a rewrite using the tree API of ASM. As a workaround, 183 * we rewrite the version number of the class to be 50.0 184 * 185 * http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6693236 186 */ 187 if (version > 50) { 188 version = 50; 189 } 190 191 super.visit(version, access, name, signature, superName, interfaces); 192 } 193 194 @Override 195 public void visitInnerClass(String name, String outerName, String innerName, int access) { 196 name = renameInternalType(name); 197 outerName = renameInternalType(outerName); 198 super.visitInnerClass(name, outerName, innerName, access); 199 } 200 201 @Override 202 public void visitOuterClass(String owner, String name, String desc) { 203 super.visitOuterClass(renameInternalType(owner), name, renameTypeDesc(desc)); 204 } 205 206 @Override 207 public MethodVisitor visitMethod(int access, String name, String desc, 208 String signature, String[] exceptions) { 209 desc = renameMethodDesc(desc); 210 signature = renameTypeSignature(signature); 211 MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions); 212 return new RenameMethodAdapter(mw); 213 } 214 215 @Override 216 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 217 desc = renameTypeDesc(desc); 218 return super.visitAnnotation(desc, visible); 219 } 220 221 @Override 222 public FieldVisitor visitField(int access, String name, String desc, 223 String signature, Object value) { 224 desc = renameTypeDesc(desc); 225 return super.visitField(access, name, desc, signature, value); 226 } 227 228 229 //---------------------------------- 230 231 /** 232 * A method visitor that renames all references from an old class name to a new class name. 233 */ 234 public class RenameMethodAdapter extends MethodVisitor { 235 236 /** 237 * Creates a method visitor that renames all references from a given old name to a given new 238 * name. The method visitor will also rename all inner classes. 239 * The names must be full qualified internal ASM names (e.g. com/blah/MyClass$InnerClass). 240 */ 241 public RenameMethodAdapter(MethodVisitor mv) { 242 super(Opcodes.ASM4, mv); 243 } 244 245 @Override 246 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 247 desc = renameTypeDesc(desc); 248 249 return super.visitAnnotation(desc, visible); 250 } 251 252 @Override 253 public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { 254 desc = renameTypeDesc(desc); 255 256 return super.visitParameterAnnotation(parameter, desc, visible); 257 } 258 259 @Override 260 public void visitTypeInsn(int opcode, String type) { 261 // The type sometimes turns out to be a type descriptor. We try to detect it and fix. 262 if (type.indexOf(';') > 0) { 263 type = renameTypeDesc(type); 264 } else { 265 type = renameInternalType(type); 266 } 267 super.visitTypeInsn(opcode, type); 268 } 269 270 @Override 271 public void visitFieldInsn(int opcode, String owner, String name, String desc) { 272 owner = renameInternalType(owner); 273 desc = renameTypeDesc(desc); 274 275 super.visitFieldInsn(opcode, owner, name, desc); 276 } 277 278 @Override 279 public void visitMethodInsn(int opcode, String owner, String name, String desc) { 280 // The owner sometimes turns out to be a type descriptor. We try to detect it and fix. 281 if (owner.indexOf(';') > 0) { 282 owner = renameTypeDesc(owner); 283 } else { 284 owner = renameInternalType(owner); 285 } 286 desc = renameMethodDesc(desc); 287 288 super.visitMethodInsn(opcode, owner, name, desc); 289 } 290 291 @Override 292 public void visitLdcInsn(Object cst) { 293 // If cst is a Type, this means the code is trying to pull the .class constant 294 // for this class, so it needs to be renamed too. 295 if (cst instanceof Type) { 296 cst = renameTypeAsType((Type) cst); 297 } 298 super.visitLdcInsn(cst); 299 } 300 301 @Override 302 public void visitMultiANewArrayInsn(String desc, int dims) { 303 desc = renameTypeDesc(desc); 304 305 super.visitMultiANewArrayInsn(desc, dims); 306 } 307 308 @Override 309 public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 310 type = renameInternalType(type); 311 312 super.visitTryCatchBlock(start, end, handler, type); 313 } 314 315 @Override 316 public void visitLocalVariable(String name, String desc, String signature, 317 Label start, Label end, int index) { 318 desc = renameTypeDesc(desc); 319 signature = renameFieldSignature(signature); 320 321 super.visitLocalVariable(name, desc, signature, start, end, index); 322 } 323 324 } 325 326 //---------------------------------- 327 328 public class RenameSignatureAdapter extends SignatureVisitor { 329 330 private final SignatureVisitor mSv; 331 332 public RenameSignatureAdapter(SignatureVisitor sv) { 333 super(Opcodes.ASM4); 334 mSv = sv; 335 } 336 337 @Override 338 public void visitClassType(String name) { 339 name = renameInternalType(name); 340 mSv.visitClassType(name); 341 } 342 343 @Override 344 public void visitInnerClassType(String name) { 345 name = renameInternalType(name); 346 mSv.visitInnerClassType(name); 347 } 348 349 @Override 350 public SignatureVisitor visitArrayType() { 351 SignatureVisitor sv = mSv.visitArrayType(); 352 return new RenameSignatureAdapter(sv); 353 } 354 355 @Override 356 public void visitBaseType(char descriptor) { 357 mSv.visitBaseType(descriptor); 358 } 359 360 @Override 361 public SignatureVisitor visitClassBound() { 362 SignatureVisitor sv = mSv.visitClassBound(); 363 return new RenameSignatureAdapter(sv); 364 } 365 366 @Override 367 public void visitEnd() { 368 mSv.visitEnd(); 369 } 370 371 @Override 372 public SignatureVisitor visitExceptionType() { 373 SignatureVisitor sv = mSv.visitExceptionType(); 374 return new RenameSignatureAdapter(sv); 375 } 376 377 @Override 378 public void visitFormalTypeParameter(String name) { 379 mSv.visitFormalTypeParameter(name); 380 } 381 382 @Override 383 public SignatureVisitor visitInterface() { 384 SignatureVisitor sv = mSv.visitInterface(); 385 return new RenameSignatureAdapter(sv); 386 } 387 388 @Override 389 public SignatureVisitor visitInterfaceBound() { 390 SignatureVisitor sv = mSv.visitInterfaceBound(); 391 return new RenameSignatureAdapter(sv); 392 } 393 394 @Override 395 public SignatureVisitor visitParameterType() { 396 SignatureVisitor sv = mSv.visitParameterType(); 397 return new RenameSignatureAdapter(sv); 398 } 399 400 @Override 401 public SignatureVisitor visitReturnType() { 402 SignatureVisitor sv = mSv.visitReturnType(); 403 return new RenameSignatureAdapter(sv); 404 } 405 406 @Override 407 public SignatureVisitor visitSuperclass() { 408 SignatureVisitor sv = mSv.visitSuperclass(); 409 return new RenameSignatureAdapter(sv); 410 } 411 412 @Override 413 public void visitTypeArgument() { 414 mSv.visitTypeArgument(); 415 } 416 417 @Override 418 public SignatureVisitor visitTypeArgument(char wildcard) { 419 SignatureVisitor sv = mSv.visitTypeArgument(wildcard); 420 return new RenameSignatureAdapter(sv); 421 } 422 423 @Override 424 public void visitTypeVariable(String name) { 425 mSv.visitTypeVariable(name); 426 } 427 428 } 429} 430