1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2013 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            case InstructionConstants.OP_INVOKEDYNAMIC:
166                isStatic = true;
167                break;
168
169            case InstructionConstants.OP_INVOKEVIRTUAL:
170            case InstructionConstants.OP_INVOKESPECIAL:
171            case InstructionConstants.OP_INVOKEINTERFACE:
172                isStatic = false;
173                break;
174        }
175
176        // Pop the parameters and push the return value.
177        this.stack = stack;
178        clazz.constantPoolEntryAccept(constantIndex, this);
179        this.stack = null;
180    }
181
182
183    // Implementations for ConstantVisitor.
184
185    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
186    {
187        // Pop the field value, if applicable.
188        if (!isLoad)
189        {
190            setFieldValue(clazz, fieldrefConstant, stack.pop());
191        }
192
193        // Pop the reference value, if applicable.
194        if (!isStatic)
195        {
196            setFieldClassValue(clazz, fieldrefConstant, stack.apop());
197        }
198
199        // Push the field value, if applicable.
200        if (isLoad)
201        {
202            String type = fieldrefConstant.getType(clazz);
203
204            stack.push(getFieldValue(clazz, fieldrefConstant, type));
205        }
206    }
207
208
209    public void visitAnyMethodrefConstant(Clazz clazz, RefConstant methodrefConstant)
210    {
211        String type = methodrefConstant.getType(clazz);
212
213        // Count the number of parameters.
214        int parameterCount = ClassUtil.internalMethodParameterCount(type);
215        if (!isStatic)
216        {
217            parameterCount++;
218        }
219
220        // Pop the parameters and the class reference, in reverse order.
221        for (int parameterIndex = parameterCount-1; parameterIndex >= 0; parameterIndex--)
222        {
223            setMethodParameterValue(clazz, methodrefConstant, parameterIndex, stack.pop());
224        }
225
226        // Push the return value, if applicable.
227        String returnType = ClassUtil.internalMethodReturnType(type);
228        if (returnType.charAt(0) != ClassConstants.INTERNAL_TYPE_VOID)
229        {
230            stack.push(getMethodReturnValue(clazz, methodrefConstant, returnType));
231        }
232    }
233
234    public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)
235    {
236        String type = invokeDynamicConstant.getType(clazz);
237
238        // Count the number of parameters.
239        int parameterCount = ClassUtil.internalMethodParameterCount(type);
240        if (!isStatic)
241        {
242            parameterCount++;
243        }
244
245        // Pop the parameters and the class reference, in reverse order.
246        for (int parameterIndex = parameterCount-1; parameterIndex >= 0; parameterIndex--)
247        {
248            stack.pop();
249        }
250
251        // Push the return value, if applicable.
252        String returnType = ClassUtil.internalMethodReturnType(type);
253        if (returnType.charAt(0) != ClassConstants.INTERNAL_TYPE_VOID)
254        {
255            stack.push(getMethodReturnValue(clazz, invokeDynamicConstant, returnType));
256        }
257    }
258
259
260    /**
261     * Sets the class through which the specified field is accessed.
262     */
263    protected void setFieldClassValue(Clazz          clazz,
264                                      RefConstant    refConstant,
265                                      ReferenceValue value)
266    {
267        // We don't care about the new value.
268    }
269
270
271    /**
272     * Returns the class though which the specified field is accessed.
273     */
274    protected Value getFieldClassValue(Clazz       clazz,
275                                       RefConstant refConstant,
276                                       String      type)
277    {
278        // Try to figure out the class of the return type.
279        returnTypeClass = null;
280        refConstant.referencedMemberAccept(this);
281
282        return valueFactory.createValue(type,
283                                        returnTypeClass,
284                                        true);
285    }
286
287
288    /**
289     * Sets the value of the specified field.
290     */
291    protected void setFieldValue(Clazz       clazz,
292                                 RefConstant refConstant,
293                                 Value       value)
294    {
295        // We don't care about the new field value.
296    }
297
298
299    /**
300     * Returns the value of the specified field.
301     */
302    protected Value getFieldValue(Clazz       clazz,
303                                  RefConstant refConstant,
304                                  String      type)
305    {
306        // Try to figure out the class of the return type.
307        returnTypeClass = null;
308        refConstant.referencedMemberAccept(this);
309
310        return valueFactory.createValue(type,
311                                        returnTypeClass,
312                                        true);
313    }
314
315
316    /**
317     * Sets the value of the specified method parameter.
318     */
319    protected void setMethodParameterValue(Clazz       clazz,
320                                           RefConstant refConstant,
321                                           int         parameterIndex,
322                                           Value       value)
323    {
324        // We don't care about the parameter value.
325    }
326
327
328    /**
329     * Returns the value of the specified method parameter.
330     */
331    protected Value getMethodParameterValue(Clazz  clazz,
332                                            Method method,
333                                            int    parameterIndex,
334                                            String type,
335                                            Clazz  referencedClass)
336    {
337        return valueFactory.createValue(type, referencedClass, true);
338    }
339
340
341    /**
342     * Sets the return value of the specified method.
343     */
344    protected void setMethodReturnValue(Clazz  clazz,
345                                        Method method,
346                                        Value  value)
347    {
348        // We don't care about the return value.
349    }
350
351
352    /**
353     * Returns the return value of the specified method.
354     */
355    protected Value getMethodReturnValue(Clazz       clazz,
356                                         RefConstant refConstant,
357                                         String      type)
358    {
359        // Try to figure out the class of the return type.
360        returnTypeClass = null;
361        refConstant.referencedMemberAccept(this);
362
363        return valueFactory.createValue(type,
364                                        returnTypeClass,
365                                        true);
366    }
367
368
369    /**
370     * Returns the return value of the specified method.
371     */
372    protected Value getMethodReturnValue(Clazz                 clazz,
373                                         InvokeDynamicConstant invokeDynamicConstant,
374                                         String                type)
375    {
376        // Try to figure out the class of the return type.
377        Clazz[] referencedClasses = invokeDynamicConstant.referencedClasses;
378
379        Clazz returnTypeClass = referencedClasses == null ? null :
380            referencedClasses[referencedClasses.length - 1];
381
382        return valueFactory.createValue(type,
383                                        returnTypeClass,
384                                        true);
385    }
386
387
388    // Implementations for MemberVisitor.
389
390    public void visitProgramField(ProgramClass programClass, ProgramField programField)
391    {
392        returnTypeClass = programField.referencedClass;
393    }
394
395
396    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
397    {
398        Clazz[] referencedClasses = programMethod.referencedClasses;
399        if (referencedClasses != null)
400        {
401            returnTypeClass = referencedClasses[referencedClasses.length - 1];
402        }
403    }
404
405
406    public void visitLibraryField(LibraryClass programClass, LibraryField programField)
407    {
408        returnTypeClass = programField.referencedClass;
409    }
410
411
412    public void visitLibraryMethod(LibraryClass programClass, LibraryMethod programMethod)
413    {
414        Clazz[] referencedClasses = programMethod.referencedClasses;
415        if (referencedClasses != null)
416        {
417            returnTypeClass = referencedClasses[referencedClasses.length - 1];
418        }
419    }
420
421
422//    public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember)
423//    {
424//    }
425}
426