StubMethodAdapter.java revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
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        mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
78                "com/android/tools/layoutlib/create/OverrideMethod",
79                "invoke",
80                "(Ljava/lang/String;ZLjava/lang/Object;)V");
81    }
82
83    private void generateReturn() {
84        /* Generates one of, depending on the return type:
85         *   return;
86         *   return 0;
87         *   return 0L;
88         *   return 0.0f;
89         *   return 0.0;
90         *   return null;
91         */
92        switch(mReturnType != null ? mReturnType.getSort() : Type.VOID) {
93        case Type.VOID:
94            mParentVisitor.visitInsn(Opcodes.RETURN);
95            break;
96        case Type.BOOLEAN:
97        case Type.CHAR:
98        case Type.BYTE:
99        case Type.SHORT:
100        case Type.INT:
101            mParentVisitor.visitInsn(Opcodes.ICONST_0);
102            mParentVisitor.visitInsn(Opcodes.IRETURN);
103            break;
104        case Type.LONG:
105            mParentVisitor.visitInsn(Opcodes.LCONST_0);
106            mParentVisitor.visitInsn(Opcodes.LRETURN);
107            break;
108        case Type.FLOAT:
109            mParentVisitor.visitInsn(Opcodes.FCONST_0);
110            mParentVisitor.visitInsn(Opcodes.FRETURN);
111            break;
112        case Type.DOUBLE:
113            mParentVisitor.visitInsn(Opcodes.DCONST_0);
114            mParentVisitor.visitInsn(Opcodes.DRETURN);
115            break;
116        case Type.ARRAY:
117        case Type.OBJECT:
118            mParentVisitor.visitInsn(Opcodes.ACONST_NULL);
119            mParentVisitor.visitInsn(Opcodes.ARETURN);
120            break;
121        }
122    }
123
124    /* Pass down to visitor writer. In this implementation, either do nothing. */
125    public void visitCode() {
126        mParentVisitor.visitCode();
127    }
128
129    /*
130     * visitMaxs is called just before visitEnd if there was any code to rewrite.
131     * For non-constructor, generate the messaging code and the return statement
132     * if it hasn't been done before.
133     */
134    public void visitMaxs(int maxStack, int maxLocals) {
135        if (!mIsInitMethod && !mMessageGenerated) {
136            generateInvoke();
137            generateReturn();
138            mMessageGenerated = true;
139        }
140        mParentVisitor.visitMaxs(maxStack, maxLocals);
141    }
142
143    /**
144     * End of visiting.
145     * For non-constructor, generate the messaging code and the return statement
146     * if it hasn't been done before.
147     */
148    public void visitEnd() {
149        if (!mIsInitMethod && !mMessageGenerated) {
150            generateInvoke();
151            generateReturn();
152            mMessageGenerated = true;
153            mParentVisitor.visitMaxs(1, 1);
154        }
155        mParentVisitor.visitEnd();
156    }
157
158    /* Writes all annotation from the original method. */
159    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
160        return mParentVisitor.visitAnnotation(desc, visible);
161    }
162
163    /* Writes all annotation default values from the original method. */
164    public AnnotationVisitor visitAnnotationDefault() {
165        return mParentVisitor.visitAnnotationDefault();
166    }
167
168    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
169            boolean visible) {
170        return mParentVisitor.visitParameterAnnotation(parameter, desc, visible);
171    }
172
173    /* Writes all attributes from the original method. */
174    public void visitAttribute(Attribute attr) {
175        mParentVisitor.visitAttribute(attr);
176    }
177
178    /*
179     * Only writes the first line number present in the original code so that source
180     * viewers can direct to the correct method, even if the content doesn't match.
181     */
182    public void visitLineNumber(int line, Label start) {
183        if (mIsInitMethod || mOutputFirstLineNumber) {
184            mParentVisitor.visitLineNumber(line, start);
185            mOutputFirstLineNumber = false;
186        }
187    }
188
189    /**
190     * For non-constructor, rewrite existing "return" instructions to write the message.
191     */
192    public void visitInsn(int opcode) {
193        if (mIsInitMethod) {
194            switch (opcode) {
195            case Opcodes.RETURN:
196            case Opcodes.ARETURN:
197            case Opcodes.DRETURN:
198            case Opcodes.FRETURN:
199            case Opcodes.IRETURN:
200            case Opcodes.LRETURN:
201                generateInvoke();
202                mMessageGenerated = true;
203            }
204            mParentVisitor.visitInsn(opcode);
205        }
206    }
207
208    public void visitLabel(Label label) {
209        if (mIsInitMethod) {
210            mParentVisitor.visitLabel(label);
211        }
212    }
213
214    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
215        if (mIsInitMethod) {
216            mParentVisitor.visitTryCatchBlock(start, end, handler, type);
217        }
218    }
219
220    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
221        if (mIsInitMethod) {
222            mParentVisitor.visitMethodInsn(opcode, owner, name, desc);
223        }
224    }
225
226    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
227        if (mIsInitMethod) {
228            mParentVisitor.visitFieldInsn(opcode, owner, name, desc);
229        }
230    }
231
232    public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
233        if (mIsInitMethod) {
234            mParentVisitor.visitFrame(type, nLocal, local, nStack, stack);
235        }
236    }
237
238    public void visitIincInsn(int var, int increment) {
239        if (mIsInitMethod) {
240            mParentVisitor.visitIincInsn(var, increment);
241        }
242    }
243
244    public void visitIntInsn(int opcode, int operand) {
245        if (mIsInitMethod) {
246            mParentVisitor.visitIntInsn(opcode, operand);
247        }
248    }
249
250    public void visitJumpInsn(int opcode, Label label) {
251        if (mIsInitMethod) {
252            mParentVisitor.visitJumpInsn(opcode, label);
253        }
254    }
255
256    public void visitLdcInsn(Object cst) {
257        if (mIsInitMethod) {
258            mParentVisitor.visitLdcInsn(cst);
259        }
260    }
261
262    public void visitLocalVariable(String name, String desc, String signature,
263            Label start, Label end, int index) {
264        if (mIsInitMethod) {
265            mParentVisitor.visitLocalVariable(name, desc, signature, start, end, index);
266        }
267    }
268
269    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
270        if (mIsInitMethod) {
271            mParentVisitor.visitLookupSwitchInsn(dflt, keys, labels);
272        }
273    }
274
275    public void visitMultiANewArrayInsn(String desc, int dims) {
276        if (mIsInitMethod) {
277            mParentVisitor.visitMultiANewArrayInsn(desc, dims);
278        }
279    }
280
281    public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
282        if (mIsInitMethod) {
283            mParentVisitor.visitTableSwitchInsn(min, max, dflt, labels);
284        }
285    }
286
287    public void visitTypeInsn(int opcode, String type) {
288        if (mIsInitMethod) {
289            mParentVisitor.visitTypeInsn(opcode, type);
290        }
291    }
292
293    public void visitVarInsn(int opcode, int var) {
294        if (mIsInitMethod) {
295            mParentVisitor.visitVarInsn(opcode, var);
296        }
297    }
298
299}
300