StubMethodAdapter.java revision 865c3bef54228a353fd449a093b0c8d155618296
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.Attribute;
21import org.objectweb.asm.Label;
22import org.objectweb.asm.MethodVisitor;
23import org.objectweb.asm.Opcodes;
24import org.objectweb.asm.Type;
25
26/**
27 * This method adapter rewrites a method by discarding the original code and generating
28 * a stub depending on the return type. Original annotations are passed along unchanged.
29 */
30class StubMethodAdapter implements MethodVisitor {
31
32    private static String CONSTRUCTOR = "<init>";
33    private static String CLASS_INIT = "<clinit>";
34
35    /** The parent method writer */
36    private MethodVisitor mParentVisitor;
37    /** The method return type. Can be null. */
38    private Type mReturnType;
39    /** Message to be printed by stub methods. */
40    private String mInvokeSignature;
41    /** Flag to output the first line number. */
42    private boolean mOutputFirstLineNumber = true;
43    /** Flag that is true when implementing a constructor, to accept all original
44     *  code calling the original super constructor. */
45    private boolean mIsInitMethod = false;
46
47    private boolean mMessageGenerated;
48    private final boolean mIsStatic;
49    private final boolean mIsNative;
50
51    public StubMethodAdapter(MethodVisitor mv, String methodName, Type returnType,
52            String invokeSignature, boolean isStatic, boolean isNative) {
53        mParentVisitor = mv;
54        mReturnType = returnType;
55        mInvokeSignature = invokeSignature;
56        mIsStatic = isStatic;
57        mIsNative = isNative;
58
59        if (CONSTRUCTOR.equals(methodName) || CLASS_INIT.equals(methodName)) {
60            mIsInitMethod = true;
61        }
62    }
63
64    private void generateInvoke() {
65        /* Generates the code:
66         *  OverrideMethod.invoke("signature", mIsNative ? true : false, null or this);
67         */
68        mParentVisitor.visitLdcInsn(mInvokeSignature);
69        // push true or false
70        mParentVisitor.visitInsn(mIsNative ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
71        // push null or this
72        if (mIsStatic) {
73            mParentVisitor.visitInsn(Opcodes.ACONST_NULL);
74        } else {
75            mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0);
76        }
77
78        int sort = mReturnType != null ? mReturnType.getSort() : Type.VOID;
79        switch(sort) {
80        case Type.VOID:
81            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
82                    "com/android/tools/layoutlib/create/OverrideMethod",
83                    "invokeV",
84                    "(Ljava/lang/String;ZLjava/lang/Object;)V");
85            mParentVisitor.visitInsn(Opcodes.RETURN);
86            break;
87        case Type.BOOLEAN:
88        case Type.CHAR:
89        case Type.BYTE:
90        case Type.SHORT:
91        case Type.INT:
92            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
93                    "com/android/tools/layoutlib/create/OverrideMethod",
94                    "invokeI",
95                    "(Ljava/lang/String;ZLjava/lang/Object;)I");
96            switch(sort) {
97            case Type.BOOLEAN:
98                Label l1 = new Label();
99                mParentVisitor.visitJumpInsn(Opcodes.IFEQ, l1);
100                mParentVisitor.visitInsn(Opcodes.ICONST_1);
101                mParentVisitor.visitInsn(Opcodes.IRETURN);
102                mParentVisitor.visitLabel(l1);
103                mParentVisitor.visitInsn(Opcodes.ICONST_0);
104                break;
105            case Type.CHAR:
106                mParentVisitor.visitInsn(Opcodes.I2C);
107                break;
108            case Type.BYTE:
109                mParentVisitor.visitInsn(Opcodes.I2B);
110                break;
111            case Type.SHORT:
112                mParentVisitor.visitInsn(Opcodes.I2S);
113                break;
114            }
115            mParentVisitor.visitInsn(Opcodes.IRETURN);
116            break;
117        case Type.LONG:
118            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
119                    "com/android/tools/layoutlib/create/OverrideMethod",
120                    "invokeL",
121                    "(Ljava/lang/String;ZLjava/lang/Object;)J");
122            mParentVisitor.visitInsn(Opcodes.LRETURN);
123            break;
124        case Type.FLOAT:
125            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
126                    "com/android/tools/layoutlib/create/OverrideMethod",
127                    "invokeF",
128                    "(Ljava/lang/String;ZLjava/lang/Object;)F");
129            mParentVisitor.visitInsn(Opcodes.FRETURN);
130            break;
131        case Type.DOUBLE:
132            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
133                    "com/android/tools/layoutlib/create/OverrideMethod",
134                    "invokeD",
135                    "(Ljava/lang/String;ZLjava/lang/Object;)D");
136            mParentVisitor.visitInsn(Opcodes.DRETURN);
137            break;
138        case Type.ARRAY:
139        case Type.OBJECT:
140            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
141                    "com/android/tools/layoutlib/create/OverrideMethod",
142                    "invokeA",
143                    "(Ljava/lang/String;ZLjava/lang/Object;)Ljava/lang/Object;");
144            mParentVisitor.visitTypeInsn(Opcodes.CHECKCAST, mReturnType.getInternalName());
145            mParentVisitor.visitInsn(Opcodes.ARETURN);
146            break;
147        }
148
149    }
150
151    private void generatePop() {
152        /* Pops the stack, depending on the return type.
153         */
154        switch(mReturnType != null ? mReturnType.getSort() : Type.VOID) {
155        case Type.VOID:
156            break;
157        case Type.BOOLEAN:
158        case Type.CHAR:
159        case Type.BYTE:
160        case Type.SHORT:
161        case Type.INT:
162        case Type.FLOAT:
163        case Type.ARRAY:
164        case Type.OBJECT:
165            mParentVisitor.visitInsn(Opcodes.POP);
166            break;
167        case Type.LONG:
168        case Type.DOUBLE:
169            mParentVisitor.visitInsn(Opcodes.POP2);
170            break;
171        }
172    }
173
174    /* Pass down to visitor writer. In this implementation, either do nothing. */
175    public void visitCode() {
176        mParentVisitor.visitCode();
177    }
178
179    /*
180     * visitMaxs is called just before visitEnd if there was any code to rewrite.
181     * For non-constructor, generate the messaging code and the return statement
182     * if it hasn't been done before.
183     */
184    public void visitMaxs(int maxStack, int maxLocals) {
185        if (!mIsInitMethod && !mMessageGenerated) {
186            generateInvoke();
187            mMessageGenerated = true;
188        }
189        mParentVisitor.visitMaxs(maxStack, maxLocals);
190    }
191
192    /**
193     * End of visiting.
194     * For non-constructor, generate the messaging code and the return statement
195     * if it hasn't been done before.
196     */
197    public void visitEnd() {
198        if (!mIsInitMethod && !mMessageGenerated) {
199            generateInvoke();
200            mMessageGenerated = true;
201            mParentVisitor.visitMaxs(1, 1);
202        }
203        mParentVisitor.visitEnd();
204    }
205
206    /* Writes all annotation from the original method. */
207    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
208        return mParentVisitor.visitAnnotation(desc, visible);
209    }
210
211    /* Writes all annotation default values from the original method. */
212    public AnnotationVisitor visitAnnotationDefault() {
213        return mParentVisitor.visitAnnotationDefault();
214    }
215
216    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
217            boolean visible) {
218        return mParentVisitor.visitParameterAnnotation(parameter, desc, visible);
219    }
220
221    /* Writes all attributes from the original method. */
222    public void visitAttribute(Attribute attr) {
223        mParentVisitor.visitAttribute(attr);
224    }
225
226    /*
227     * Only writes the first line number present in the original code so that source
228     * viewers can direct to the correct method, even if the content doesn't match.
229     */
230    public void visitLineNumber(int line, Label start) {
231        if (mIsInitMethod || mOutputFirstLineNumber) {
232            mParentVisitor.visitLineNumber(line, start);
233            mOutputFirstLineNumber = false;
234        }
235    }
236
237    /**
238     * For non-constructor, rewrite existing "return" instructions to write the message.
239     */
240    public void visitInsn(int opcode) {
241        if (mIsInitMethod) {
242            switch (opcode) {
243            case Opcodes.RETURN:
244            case Opcodes.ARETURN:
245            case Opcodes.DRETURN:
246            case Opcodes.FRETURN:
247            case Opcodes.IRETURN:
248            case Opcodes.LRETURN:
249                // Pop the last word from the stack since invoke will generate its own return.
250                generatePop();
251                generateInvoke();
252                mMessageGenerated = true;
253                //$FALL-THROUGH$
254            default:
255                mParentVisitor.visitInsn(opcode);
256            }
257        }
258    }
259
260    public void visitLabel(Label label) {
261        if (mIsInitMethod) {
262            mParentVisitor.visitLabel(label);
263        }
264    }
265
266    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
267        if (mIsInitMethod) {
268            mParentVisitor.visitTryCatchBlock(start, end, handler, type);
269        }
270    }
271
272    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
273        if (mIsInitMethod) {
274            mParentVisitor.visitMethodInsn(opcode, owner, name, desc);
275        }
276    }
277
278    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
279        if (mIsInitMethod) {
280            mParentVisitor.visitFieldInsn(opcode, owner, name, desc);
281        }
282    }
283
284    public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
285        if (mIsInitMethod) {
286            mParentVisitor.visitFrame(type, nLocal, local, nStack, stack);
287        }
288    }
289
290    public void visitIincInsn(int var, int increment) {
291        if (mIsInitMethod) {
292            mParentVisitor.visitIincInsn(var, increment);
293        }
294    }
295
296    public void visitIntInsn(int opcode, int operand) {
297        if (mIsInitMethod) {
298            mParentVisitor.visitIntInsn(opcode, operand);
299        }
300    }
301
302    public void visitJumpInsn(int opcode, Label label) {
303        if (mIsInitMethod) {
304            mParentVisitor.visitJumpInsn(opcode, label);
305        }
306    }
307
308    public void visitLdcInsn(Object cst) {
309        if (mIsInitMethod) {
310            mParentVisitor.visitLdcInsn(cst);
311        }
312    }
313
314    public void visitLocalVariable(String name, String desc, String signature,
315            Label start, Label end, int index) {
316        if (mIsInitMethod) {
317            mParentVisitor.visitLocalVariable(name, desc, signature, start, end, index);
318        }
319    }
320
321    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
322        if (mIsInitMethod) {
323            mParentVisitor.visitLookupSwitchInsn(dflt, keys, labels);
324        }
325    }
326
327    public void visitMultiANewArrayInsn(String desc, int dims) {
328        if (mIsInitMethod) {
329            mParentVisitor.visitMultiANewArrayInsn(desc, dims);
330        }
331    }
332
333    public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
334        if (mIsInitMethod) {
335            mParentVisitor.visitTableSwitchInsn(min, max, dflt, labels);
336        }
337    }
338
339    public void visitTypeInsn(int opcode, String type) {
340        if (mIsInitMethod) {
341            mParentVisitor.visitTypeInsn(opcode, type);
342        }
343    }
344
345    public void visitVarInsn(int opcode, int var) {
346        if (mIsInitMethod) {
347            mParentVisitor.visitVarInsn(opcode, var);
348        }
349    }
350
351}
352