DelegateMethodAdapter.java revision 9f63ff263b0a97f0fa63e97136c18f6abccbfc68
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 com.android.tools.layoutlib.annotations.LayoutlibDelegate;
20
21import org.objectweb.asm.AnnotationVisitor;
22import org.objectweb.asm.Attribute;
23import org.objectweb.asm.ClassReader;
24import org.objectweb.asm.ClassVisitor;
25import org.objectweb.asm.Label;
26import org.objectweb.asm.MethodVisitor;
27import org.objectweb.asm.Opcodes;
28import org.objectweb.asm.Type;
29
30/**
31 * This method adapter rewrites a method by discarding the original code and generating
32 * a call to a delegate. Original annotations are passed along unchanged.
33 * <p/>
34 * Calls are delegated to a class named <code>&lt;className&gt;_Delegate</code> with
35 * static methods matching the methods to be overridden here. The methods have the
36 * same return type. The argument type list is the same except the "this" reference is
37 * passed first for non-static methods.
38 * <p/>
39 * A new annotation is added.
40 * <p/>
41 * Note that native methods have, by definition, no code so there's nothing a visitor
42 * can visit. That means the caller must call {@link #generateCode()} directly for
43 * a native and use the visitor pattern for non-natives.
44 * <p/>
45 * Instances of this class are not re-usable. You need a new instance for each method.
46 */
47class DelegateMethodAdapter implements MethodVisitor {
48
49    /**
50     * Suffix added to delegate classes.
51     */
52    public static final String DELEGATE_SUFFIX = "_Delegate";
53
54    private static String CONSTRUCTOR = "<init>";
55    private static String CLASS_INIT = "<clinit>";
56
57    /** The parent method writer */
58    private MethodVisitor mParentVisitor;
59    /** Flag to output the first line number. */
60    private boolean mOutputFirstLineNumber = true;
61    /** The original method descriptor (return type + argument types.) */
62    private String mDesc;
63    /** True if the original method is static. */
64    private final boolean mIsStatic;
65    /** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */
66    private final String mClassName;
67    /** The method name. */
68    private final String mMethodName;
69    /** Logger object. */
70    private final Log mLog;
71    /** True if {@link #visitCode()} has been invoked. */
72    private boolean mVisitCodeCalled;
73
74    /**
75     * Creates a new {@link DelegateMethodAdapter} that will transform this method
76     * into a delegate call.
77     * <p/>
78     * See {@link DelegateMethodAdapter} for more details.
79     *
80     * @param log The logger object. Must not be null.
81     * @param mv the method visitor to which this adapter must delegate calls.
82     * @param className The internal class name of the class to visit,
83     *          e.g. <code>com/android/SomeClass$InnerClass</code>.
84     * @param methodName The simple name of the method.
85     * @param desc A method descriptor (c.f. {@link Type#getReturnType(String)} +
86     *          {@link Type#getArgumentTypes(String)})
87     * @param isStatic True if the method is declared static.
88     */
89    public DelegateMethodAdapter(Log log,
90            MethodVisitor mv,
91            String className,
92            String methodName,
93            String desc,
94            boolean isStatic) {
95        mLog = log;
96        mParentVisitor = mv;
97        mClassName = className;
98        mMethodName = methodName;
99        mDesc = desc;
100        mIsStatic = isStatic;
101
102        if (CONSTRUCTOR.equals(methodName) || CLASS_INIT.equals(methodName)) {
103            // We're going to simplify by not supporting constructors.
104            // The only trick with a constructor is to find the proper super constructor
105            // and call it (and deciding if we should mirror the original method call to
106            // a custom constructor or call a default one.)
107            throw new UnsupportedOperationException(
108                    String.format("Delegate doesn't support overriding constructor %1$s:%2$s(%3$s)",
109                            className, methodName, desc));
110        }
111    }
112
113    /**
114     * Generates the new code for the method.
115     * <p/>
116     * For native methods, this must be invoked directly by {@link DelegateClassAdapter}
117     * (since they have no code to visit).
118     * <p/>
119     * Otherwise for non-native methods the {@link DelegateClassAdapter} simply needs to
120     * return this instance of {@link DelegateMethodAdapter} and let the normal visitor pattern
121     * invoke it as part of the {@link ClassReader#accept(ClassVisitor, int)} workflow and then
122     * this method will be invoked from {@link MethodVisitor#visitEnd()}.
123     */
124    public void generateCode() {
125        /*
126         * The goal is to generate a call to a static delegate method.
127         * If this method is not-static, the first parameter will be this.
128         * All the parameters must be passed and then the eventual return type returned.
129         *
130         * Example, let's say we have a method such as
131         *   public void method_1(int a, Object b, ArrayList<String> c) { ... }
132         *
133         * We'll want to create a body that calls a delegate method like this:
134         *   TheClass_Delegate.method_1(this, a, b, c);
135         *
136         * The generated class name is the current class name with "_Delegate" appended to it.
137         * One thing to realize is that we don't care about generics -- since generic types
138         * are erased at runtime, they have no influence on the method being called.
139         */
140
141        // Add our annotation
142        AnnotationVisitor aw = mParentVisitor.visitAnnotation(
143                Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(),
144                true); // visible at runtime
145        aw.visitEnd();
146
147        if (!mVisitCodeCalled) {
148            // If this is a direct call to generateCode() as done by DelegateClassAdapter
149            // for natives, visitCode() hasn't been called yet.
150            mParentVisitor.visitCode();
151            mVisitCodeCalled = true;
152        }
153
154        int numVars = 0;
155
156        // Push "this" for an instance method, which is always ALOAD 0
157        if (!mIsStatic) {
158            mParentVisitor.visitVarInsn(Opcodes.ALOAD, numVars++);
159        }
160
161        // Push all other arguments
162        Type[] argTypes = Type.getArgumentTypes(mDesc);
163        for (Type t : argTypes) {
164            int size = t.getSize();
165            mParentVisitor.visitVarInsn(t.getOpcode(Opcodes.ILOAD), numVars);
166            numVars += size;
167        }
168
169        // Construct the descriptor of the delegate. For a static method, it's the same
170        // however for an instance method we need to pass the 'this' reference first
171        String desc = mDesc;
172        if (!mIsStatic) {
173            Type[] argTypes2 = new Type[argTypes.length + 1];
174
175            argTypes2[0] = Type.getObjectType(mClassName);
176            System.arraycopy(argTypes, 0, argTypes2, 1, argTypes.length);
177
178            desc = Type.getMethodDescriptor(Type.getReturnType(mDesc), argTypes2);
179        }
180
181        String delegateClassName = mClassName + DELEGATE_SUFFIX;
182
183        // Invoke the static delegate
184        mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
185                delegateClassName,
186                mMethodName,
187                desc);
188
189        Type returnType = Type.getReturnType(mDesc);
190        mParentVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
191
192        mParentVisitor.visitMaxs(numVars, numVars);
193        mParentVisitor.visitEnd();
194
195        // For debugging now. Maybe we should collect these and store them in
196        // a text file for helping create the delegates. We could also compare
197        // the text file to a golden and break the build on unsupported changes
198        // or regressions. Even better we could fancy-print something that looks
199        // like the expected Java method declaration.
200        mLog.debug("Delegate: %1$s # %2$s %3$s", delegateClassName, mMethodName, desc);
201    }
202
203    /* Pass down to visitor writer. In this implementation, either do nothing. */
204    public void visitCode() {
205        mVisitCodeCalled = true;
206        mParentVisitor.visitCode();
207    }
208
209    /*
210     * visitMaxs is called just before visitEnd if there was any code to rewrite.
211     * Skip the original.
212     */
213    public void visitMaxs(int maxStack, int maxLocals) {
214    }
215
216    /**
217     * End of visiting. Generate the messaging code.
218     */
219    public void visitEnd() {
220        generateCode();
221    }
222
223    /* Writes all annotation from the original method. */
224    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
225        return mParentVisitor.visitAnnotation(desc, visible);
226    }
227
228    /* Writes all annotation default values from the original method. */
229    public AnnotationVisitor visitAnnotationDefault() {
230        return mParentVisitor.visitAnnotationDefault();
231    }
232
233    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
234            boolean visible) {
235        return mParentVisitor.visitParameterAnnotation(parameter, desc, visible);
236    }
237
238    /* Writes all attributes from the original method. */
239    public void visitAttribute(Attribute attr) {
240        mParentVisitor.visitAttribute(attr);
241    }
242
243    /*
244     * Only writes the first line number present in the original code so that source
245     * viewers can direct to the correct method, even if the content doesn't match.
246     */
247    public void visitLineNumber(int line, Label start) {
248        if (mOutputFirstLineNumber) {
249            mParentVisitor.visitLineNumber(line, start);
250            mOutputFirstLineNumber = false;
251        }
252    }
253
254    public void visitInsn(int opcode) {
255        // Skip original code.
256    }
257
258    public void visitLabel(Label label) {
259        // Skip original code.
260    }
261
262    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
263        // Skip original code.
264    }
265
266    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
267        // Skip original code.
268    }
269
270    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
271        // Skip original code.
272    }
273
274    public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
275        // Skip original code.
276    }
277
278    public void visitIincInsn(int var, int increment) {
279        // Skip original code.
280    }
281
282    public void visitIntInsn(int opcode, int operand) {
283        // Skip original code.
284    }
285
286    public void visitJumpInsn(int opcode, Label label) {
287        // Skip original code.
288    }
289
290    public void visitLdcInsn(Object cst) {
291        // Skip original code.
292    }
293
294    public void visitLocalVariable(String name, String desc, String signature,
295            Label start, Label end, int index) {
296        // Skip original code.
297    }
298
299    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
300        // Skip original code.
301    }
302
303    public void visitMultiANewArrayInsn(String desc, int dims) {
304        // Skip original code.
305    }
306
307    public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
308        // Skip original code.
309    }
310
311    public void visitTypeInsn(int opcode, String type) {
312        // Skip original code.
313    }
314
315    public void visitVarInsn(int opcode, int var) {
316        // Skip original code.
317    }
318
319}
320