1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21package proguard.evaluation;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.CodeAttribute;
25import proguard.classfile.constant.*;
26import proguard.classfile.constant.visitor.ConstantVisitor;
27import proguard.classfile.instruction.*;
28import proguard.classfile.util.*;
29import proguard.classfile.visitor.MemberVisitor;
30import proguard.evaluation.value.*;
31
32/**
33 * This InvocationUnit sets up the variables for entering a method,
34 * and it updates the stack for the invocation of a class member,
35 * using simple values.
36 *
37 * @author Eric Lafortune
38 */
39public class BasicInvocationUnit
40extends      SimplifiedVisitor
41implements   InvocationUnit,
42             ConstantVisitor,
43             MemberVisitor
44{
45    protected final ValueFactory valueFactory;
46
47    // Fields acting as parameters between the visitor methods.
48    private boolean isStatic;
49    private boolean isLoad;
50    private Stack   stack;
51    private Clazz   returnTypeClass;
52
53
54    /**
55     * Creates a new BasicInvocationUnit with the given value factory.
56     */
57    public BasicInvocationUnit(ValueFactory valueFactory)
58    {
59        this.valueFactory = valueFactory;
60    }
61
62
63    // Implementations for InvocationUnit.
64
65    public void enterMethod(Clazz clazz, Method method, Variables variables)
66    {
67        String descriptor = method.getDescriptor(clazz);
68
69        // Initialize the parameters.
70        boolean isStatic =
71            (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0;
72
73        // Count the number of parameters, taking into account their categories.
74        int parameterSize = ClassUtil.internalMethodParameterSize(descriptor, isStatic);
75
76        // Reuse the existing parameters object, ensuring the right size.
77        variables.reset(parameterSize);
78
79        // Go over the parameters again.
80        InternalTypeEnumeration internalTypeEnumeration =
81            new InternalTypeEnumeration(descriptor);
82
83        int parameterIndex = 0;
84        int variableIndex  = 0;
85
86        // Put the 'this' reference in variable 0.
87        if (!isStatic)
88        {
89            // Get the reference value.
90            Value value = getMethodParameterValue(clazz,
91                                                  method,
92                                                  parameterIndex++,
93                                                  ClassUtil.internalTypeFromClassName(clazz.getName()),
94                                                  clazz);
95
96            // Store the value in variable 0.
97            variables.store(variableIndex++, value);
98        }
99
100        Clazz[] referencedClasses = ((ProgramMethod)method).referencedClasses;
101        int referencedClassIndex = 0;
102
103        // Set up the variables corresponding to the parameter types and values.
104        while (internalTypeEnumeration.hasMoreTypes())
105        {
106            String type = internalTypeEnumeration.nextType();
107
108            Clazz referencedClass = referencedClasses != null &&
109                                    ClassUtil.isInternalClassType(type) ?
110                referencedClasses[referencedClassIndex++] :
111                null;
112
113            // Get the parameter value.
114            Value value = getMethodParameterValue(clazz,
115                                                  method,
116                                                  parameterIndex++,
117                                                  type,
118                                                  referencedClass);
119
120            // Store the value in the corresponding variable.
121            variables.store(variableIndex++, value);
122
123            // Increment the variable index again for Category 2 values.
124            if (value.isCategory2())
125            {
126                variableIndex++;
127            }
128        }
129    }
130
131
132    public void exitMethod(Clazz clazz, Method method, Value returnValue)
133    {
134        setMethodReturnValue(clazz, method, returnValue);
135    }
136
137
138    public void invokeMember(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction, Stack stack)
139    {
140        int constantIndex = constantInstruction.constantIndex;
141
142        switch (constantInstruction.opcode)
143        {
144            case InstructionConstants.OP_GETSTATIC:
145                isStatic = true;
146                isLoad   = true;
147                break;
148
149            case InstructionConstants.OP_PUTSTATIC:
150                isStatic = true;
151                isLoad   = false;
152                break;
153
154            case InstructionConstants.OP_GETFIELD:
155                isStatic = false;
156                isLoad   = true;
157                break;
158
159            case InstructionConstants.OP_PUTFIELD:
160                isStatic = false;
161                isLoad   = false;
162                break;
163
164            case InstructionConstants.OP_INVOKESTATIC:
165                isStatic = true;
166                break;
167
168            case InstructionConstants.OP_INVOKEVIRTUAL:
169            case InstructionConstants.OP_INVOKESPECIAL:
170            case InstructionConstants.OP_INVOKEINTERFACE:
171                isStatic = false;
172                break;
173        }
174
175        // Pop the parameters and push the return value.
176        this.stack = stack;
177        clazz.constantPoolEntryAccept(constantIndex, this);
178        this.stack = null;
179    }
180
181
182    // Implementations for ConstantVisitor.
183
184    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
185    {
186        // Pop the field value, if applicable.
187        if (!isLoad)
188        {
189            setFieldValue(clazz, fieldrefConstant, stack.pop());
190        }
191
192        // Pop the reference value, if applicable.
193        if (!isStatic)
194        {
195            setFieldClassValue(clazz, fieldrefConstant, stack.apop());
196        }
197
198        // Push the field value, if applicable.
199        if (isLoad)
200        {
201            String type = fieldrefConstant.getType(clazz);
202
203            stack.push(getFieldValue(clazz, fieldrefConstant, type));
204        }
205    }
206
207
208    public void visitAnyMethodrefConstant(Clazz clazz, RefConstant methodrefConstant)
209    {
210        String type = methodrefConstant.getType(clazz);
211
212        // Count the number of parameters.
213        int parameterCount = ClassUtil.internalMethodParameterCount(type);
214        if (!isStatic)
215        {
216            parameterCount++;
217        }
218
219        // Pop the parameters and the class reference, in reverse order.
220        for (int parameterIndex = parameterCount-1; parameterIndex >= 0; parameterIndex--)
221        {
222            setMethodParameterValue(clazz, methodrefConstant, parameterIndex, stack.pop());
223        }
224
225        // Push the return value, if applicable.
226        String returnType = ClassUtil.internalMethodReturnType(type);
227        if (returnType.charAt(0) != ClassConstants.INTERNAL_TYPE_VOID)
228        {
229            stack.push(getMethodReturnValue(clazz, methodrefConstant, returnType));
230        }
231    }
232
233
234    /**
235     * Sets the class through which the specified field is accessed.
236     */
237    protected void setFieldClassValue(Clazz          clazz,
238                                      RefConstant    refConstant,
239                                      ReferenceValue value)
240    {
241        // We don't care about the new value.
242    }
243
244
245    /**
246     * Returns the class though which the specified field is accessed.
247     */
248    protected Value getFieldClassValue(Clazz       clazz,
249                                       RefConstant refConstant,
250                                       String      type)
251    {
252        // Try to figure out the class of the return type.
253        returnTypeClass = null;
254        refConstant.referencedMemberAccept(this);
255
256        return valueFactory.createValue(type,
257                                        returnTypeClass,
258                                        true);
259    }
260
261
262    /**
263     * Sets the value of the specified field.
264     */
265    protected void setFieldValue(Clazz       clazz,
266                                 RefConstant refConstant,
267                                 Value       value)
268    {
269        // We don't care about the new field value.
270    }
271
272
273    /**
274     * Returns the value of the specified field.
275     */
276    protected Value getFieldValue(Clazz       clazz,
277                                  RefConstant refConstant,
278                                  String      type)
279    {
280        // Try to figure out the class of the return type.
281        returnTypeClass = null;
282        refConstant.referencedMemberAccept(this);
283
284        return valueFactory.createValue(type,
285                                        returnTypeClass,
286                                        true);
287    }
288
289
290    /**
291     * Sets the value of the specified method parameter.
292     */
293    protected void setMethodParameterValue(Clazz       clazz,
294                                           RefConstant refConstant,
295                                           int         parameterIndex,
296                                           Value       value)
297    {
298        // We don't care about the parameter value.
299    }
300
301
302    /**
303     * Returns the value of the specified method parameter.
304     */
305    protected Value getMethodParameterValue(Clazz  clazz,
306                                            Method method,
307                                            int    parameterIndex,
308                                            String type,
309                                            Clazz  referencedClass)
310    {
311        return valueFactory.createValue(type, referencedClass, true);
312    }
313
314
315    /**
316     * Sets the return value of the specified method.
317     */
318    protected void setMethodReturnValue(Clazz  clazz,
319                                        Method method,
320                                        Value  value)
321    {
322        // We don't care about the return value.
323    }
324
325
326    /**
327     * Returns the return value of the specified method.
328     */
329    protected Value getMethodReturnValue(Clazz       clazz,
330                                         RefConstant refConstant,
331                                         String      type)
332    {
333        // Try to figure out the class of the return type.
334        returnTypeClass = null;
335        refConstant.referencedMemberAccept(this);
336
337        return valueFactory.createValue(type,
338                                        returnTypeClass,
339                                        true);
340    }
341
342
343    // Implementations for MemberVisitor.
344
345    public void visitProgramField(ProgramClass programClass, ProgramField programField)
346    {
347        returnTypeClass = programField.referencedClass;
348    }
349
350
351    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
352    {
353        Clazz[] referencedClasses = programMethod.referencedClasses;
354        if (referencedClasses != null)
355        {
356            returnTypeClass = referencedClasses[referencedClasses.length - 1];
357        }
358    }
359
360
361    public void visitLibraryField(LibraryClass programClass, LibraryField programField)
362    {
363        returnTypeClass = programField.referencedClass;
364    }
365
366
367    public void visitLibraryMethod(LibraryClass programClass, LibraryMethod programMethod)
368    {
369        Clazz[] referencedClasses = programMethod.referencedClasses;
370        if (referencedClasses != null)
371        {
372            returnTypeClass = referencedClasses[referencedClasses.length - 1];
373        }
374    }
375
376
377//    public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember)
378//    {
379//    }
380}
381