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