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