| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.tools.layoutlib.create; |
| |
| import org.objectweb.asm.AnnotationVisitor; |
| import org.objectweb.asm.ClassVisitor; |
| import org.objectweb.asm.FieldVisitor; |
| import org.objectweb.asm.Label; |
| import org.objectweb.asm.MethodVisitor; |
| import org.objectweb.asm.Type; |
| import org.objectweb.asm.signature.SignatureReader; |
| import org.objectweb.asm.signature.SignatureVisitor; |
| import org.objectweb.asm.signature.SignatureWriter; |
| |
| /** |
| * Provides the common code for RenameClassAdapter and RefactorClassAdapter. It |
| * goes through the complete class and finds references to other classes. It |
| * then calls {@link #renameInternalType(String)} to convert the className to |
| * the new value, if need be. |
| */ |
| public abstract class AbstractClassAdapter extends ClassVisitor { |
| |
| /** |
| * Returns the new FQCN for the class, if the reference to this class needs |
| * to be updated. Else, it returns the same string. |
| * @param name Old FQCN |
| * @return New FQCN if it needs to be renamed, else the old FQCN |
| */ |
| abstract String renameInternalType(String name); |
| |
| public AbstractClassAdapter(ClassVisitor cv) { |
| super(Main.ASM_VERSION, cv); |
| } |
| |
| /** |
| * Renames a type descriptor, e.g. "Lcom.package.MyClass;" |
| * If the type doesn't need to be renamed, returns the input string as-is. |
| */ |
| String renameTypeDesc(String desc) { |
| if (desc == null) { |
| return null; |
| } |
| |
| return renameType(Type.getType(desc)); |
| } |
| |
| /** |
| * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an |
| * object element, e.g. "[Lcom.package.MyClass;" |
| * If the type doesn't need to be renamed, returns the internal name of the input type. |
| */ |
| String renameType(Type type) { |
| if (type == null) { |
| return null; |
| } |
| |
| if (type.getSort() == Type.OBJECT) { |
| String in = type.getInternalName(); |
| return "L" + renameInternalType(in) + ";"; |
| } else if (type.getSort() == Type.ARRAY) { |
| StringBuilder sb = new StringBuilder(); |
| for (int n = type.getDimensions(); n > 0; n--) { |
| sb.append('['); |
| } |
| sb.append(renameType(type.getElementType())); |
| return sb.toString(); |
| } |
| return type.getDescriptor(); |
| } |
| |
| /** |
| * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an |
| * object element, e.g. "[Lcom.package.MyClass;". |
| * This is like renameType() except that it returns a Type object. |
| * If the type doesn't need to be renamed, returns the input type object. |
| */ |
| Type renameTypeAsType(Type type) { |
| if (type == null) { |
| return null; |
| } |
| |
| if (type.getSort() == Type.OBJECT) { |
| String in = type.getInternalName(); |
| String newIn = renameInternalType(in); |
| if (!newIn.equals(in)) { |
| return Type.getType("L" + newIn + ";"); |
| } |
| } else if (type.getSort() == Type.ARRAY) { |
| StringBuilder sb = new StringBuilder(); |
| for (int n = type.getDimensions(); n > 0; n--) { |
| sb.append('['); |
| } |
| sb.append(renameType(type.getElementType())); |
| return Type.getType(sb.toString()); |
| } |
| return type; |
| } |
| |
| /** |
| * Renames a method descriptor, i.e. applies renameType to all arguments and to the |
| * return value. |
| */ |
| String renameMethodDesc(String desc) { |
| if (desc == null) { |
| return null; |
| } |
| |
| Type[] args = Type.getArgumentTypes(desc); |
| |
| StringBuilder sb = new StringBuilder("("); |
| for (Type arg : args) { |
| String name = renameType(arg); |
| sb.append(name); |
| } |
| sb.append(')'); |
| |
| Type ret = Type.getReturnType(desc); |
| String name = renameType(ret); |
| sb.append(name); |
| |
| return sb.toString(); |
| } |
| |
| |
| /** |
| * Renames the ClassSignature handled by ClassVisitor.visit |
| * or the MethodTypeSignature handled by ClassVisitor.visitMethod. |
| */ |
| String renameTypeSignature(String sig) { |
| if (sig == null) { |
| return null; |
| } |
| SignatureReader reader = new SignatureReader(sig); |
| SignatureWriter writer = new SignatureWriter(); |
| reader.accept(new RenameSignatureAdapter(writer)); |
| sig = writer.toString(); |
| return sig; |
| } |
| |
| |
| /** |
| * Renames the FieldTypeSignature handled by ClassVisitor.visitField |
| * or MethodVisitor.visitLocalVariable. |
| */ |
| String renameFieldSignature(String sig) { |
| return renameTypeSignature(sig); |
| } |
| |
| |
| //---------------------------------- |
| // Methods from the ClassAdapter |
| |
| @Override |
| public void visit(int version, int access, String name, String signature, |
| String superName, String[] interfaces) { |
| name = renameInternalType(name); |
| superName = renameInternalType(superName); |
| signature = renameTypeSignature(signature); |
| if (interfaces != null) { |
| for (int i = 0; i < interfaces.length; ++i) { |
| interfaces[i] = renameInternalType(interfaces[i]); |
| } |
| } |
| |
| super.visit(version, access, name, signature, superName, interfaces); |
| } |
| |
| @Override |
| public void visitInnerClass(String name, String outerName, String innerName, int access) { |
| name = renameInternalType(name); |
| outerName = renameInternalType(outerName); |
| super.visitInnerClass(name, outerName, innerName, access); |
| } |
| |
| @Override |
| public void visitOuterClass(String owner, String name, String desc) { |
| super.visitOuterClass(renameInternalType(owner), name, renameTypeDesc(desc)); |
| } |
| |
| @Override |
| public MethodVisitor visitMethod(int access, String name, String desc, |
| String signature, String[] exceptions) { |
| desc = renameMethodDesc(desc); |
| signature = renameTypeSignature(signature); |
| MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions); |
| return new RenameMethodAdapter(mw); |
| } |
| |
| @Override |
| public AnnotationVisitor visitAnnotation(String desc, boolean visible) { |
| desc = renameTypeDesc(desc); |
| return super.visitAnnotation(desc, visible); |
| } |
| |
| @Override |
| public FieldVisitor visitField(int access, String name, String desc, |
| String signature, Object value) { |
| desc = renameTypeDesc(desc); |
| return super.visitField(access, name, desc, signature, value); |
| } |
| |
| |
| //---------------------------------- |
| |
| /** |
| * A method visitor that renames all references from an old class name to a new class name. |
| */ |
| public class RenameMethodAdapter extends MethodVisitor { |
| |
| /** |
| * Creates a method visitor that renames all references from a given old name to a given new |
| * name. The method visitor will also rename all inner classes. |
| * The names must be full qualified internal ASM names (e.g. com/blah/MyClass$InnerClass). |
| */ |
| public RenameMethodAdapter(MethodVisitor mv) { |
| super(Main.ASM_VERSION, mv); |
| } |
| |
| @Override |
| public AnnotationVisitor visitAnnotation(String desc, boolean visible) { |
| desc = renameTypeDesc(desc); |
| |
| return super.visitAnnotation(desc, visible); |
| } |
| |
| @Override |
| public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { |
| desc = renameTypeDesc(desc); |
| |
| return super.visitParameterAnnotation(parameter, desc, visible); |
| } |
| |
| @Override |
| public void visitTypeInsn(int opcode, String type) { |
| // The type sometimes turns out to be a type descriptor. We try to detect it and fix. |
| if (type.indexOf(';') > 0) { |
| type = renameTypeDesc(type); |
| } else { |
| type = renameInternalType(type); |
| } |
| super.visitTypeInsn(opcode, type); |
| } |
| |
| @Override |
| public void visitFieldInsn(int opcode, String owner, String name, String desc) { |
| owner = renameInternalType(owner); |
| desc = renameTypeDesc(desc); |
| |
| super.visitFieldInsn(opcode, owner, name, desc); |
| } |
| |
| @Override |
| public void visitMethodInsn(int opcode, String owner, String name, String desc, |
| boolean itf) { |
| // The owner sometimes turns out to be a type descriptor. We try to detect it and fix. |
| if (owner.indexOf(';') > 0) { |
| owner = renameTypeDesc(owner); |
| } else { |
| owner = renameInternalType(owner); |
| } |
| desc = renameMethodDesc(desc); |
| |
| super.visitMethodInsn(opcode, owner, name, desc, itf); |
| } |
| |
| @Override |
| public void visitLdcInsn(Object cst) { |
| // If cst is a Type, this means the code is trying to pull the .class constant |
| // for this class, so it needs to be renamed too. |
| if (cst instanceof Type) { |
| cst = renameTypeAsType((Type) cst); |
| } |
| super.visitLdcInsn(cst); |
| } |
| |
| @Override |
| public void visitMultiANewArrayInsn(String desc, int dims) { |
| desc = renameTypeDesc(desc); |
| |
| super.visitMultiANewArrayInsn(desc, dims); |
| } |
| |
| @Override |
| public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { |
| type = renameInternalType(type); |
| |
| super.visitTryCatchBlock(start, end, handler, type); |
| } |
| |
| @Override |
| public void visitLocalVariable(String name, String desc, String signature, |
| Label start, Label end, int index) { |
| desc = renameTypeDesc(desc); |
| signature = renameFieldSignature(signature); |
| |
| super.visitLocalVariable(name, desc, signature, start, end, index); |
| } |
| |
| } |
| |
| //---------------------------------- |
| |
| public class RenameSignatureAdapter extends SignatureVisitor { |
| |
| private final SignatureVisitor mSv; |
| |
| public RenameSignatureAdapter(SignatureVisitor sv) { |
| super(Main.ASM_VERSION); |
| mSv = sv; |
| } |
| |
| @Override |
| public void visitClassType(String name) { |
| name = renameInternalType(name); |
| mSv.visitClassType(name); |
| } |
| |
| @Override |
| public void visitInnerClassType(String name) { |
| name = renameInternalType(name); |
| mSv.visitInnerClassType(name); |
| } |
| |
| @Override |
| public SignatureVisitor visitArrayType() { |
| SignatureVisitor sv = mSv.visitArrayType(); |
| return new RenameSignatureAdapter(sv); |
| } |
| |
| @Override |
| public void visitBaseType(char descriptor) { |
| mSv.visitBaseType(descriptor); |
| } |
| |
| @Override |
| public SignatureVisitor visitClassBound() { |
| SignatureVisitor sv = mSv.visitClassBound(); |
| return new RenameSignatureAdapter(sv); |
| } |
| |
| @Override |
| public void visitEnd() { |
| mSv.visitEnd(); |
| } |
| |
| @Override |
| public SignatureVisitor visitExceptionType() { |
| SignatureVisitor sv = mSv.visitExceptionType(); |
| return new RenameSignatureAdapter(sv); |
| } |
| |
| @Override |
| public void visitFormalTypeParameter(String name) { |
| mSv.visitFormalTypeParameter(name); |
| } |
| |
| @Override |
| public SignatureVisitor visitInterface() { |
| SignatureVisitor sv = mSv.visitInterface(); |
| return new RenameSignatureAdapter(sv); |
| } |
| |
| @Override |
| public SignatureVisitor visitInterfaceBound() { |
| SignatureVisitor sv = mSv.visitInterfaceBound(); |
| return new RenameSignatureAdapter(sv); |
| } |
| |
| @Override |
| public SignatureVisitor visitParameterType() { |
| SignatureVisitor sv = mSv.visitParameterType(); |
| return new RenameSignatureAdapter(sv); |
| } |
| |
| @Override |
| public SignatureVisitor visitReturnType() { |
| SignatureVisitor sv = mSv.visitReturnType(); |
| return new RenameSignatureAdapter(sv); |
| } |
| |
| @Override |
| public SignatureVisitor visitSuperclass() { |
| SignatureVisitor sv = mSv.visitSuperclass(); |
| return new RenameSignatureAdapter(sv); |
| } |
| |
| @Override |
| public void visitTypeArgument() { |
| mSv.visitTypeArgument(); |
| } |
| |
| @Override |
| public SignatureVisitor visitTypeArgument(char wildcard) { |
| SignatureVisitor sv = mSv.visitTypeArgument(wildcard); |
| return new RenameSignatureAdapter(sv); |
| } |
| |
| @Override |
| public void visitTypeVariable(String name) { |
| mSv.visitTypeVariable(name); |
| } |
| |
| } |
| } |