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