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