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