• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  *             of Java bytecode.
4  *
5  * Copyright (c) 2002-2014 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  */
21 package proguard.evaluation;
22 
23 import proguard.classfile.*;
24 import proguard.classfile.attribute.CodeAttribute;
25 import proguard.classfile.constant.*;
26 import proguard.classfile.constant.visitor.ConstantVisitor;
27 import proguard.classfile.instruction.*;
28 import proguard.classfile.util.*;
29 import proguard.classfile.visitor.MemberVisitor;
30 import 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  */
39 public class BasicInvocationUnit
40 extends      SimplifiedVisitor
41 implements   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      */
BasicInvocationUnit(ValueFactory valueFactory)57     public BasicInvocationUnit(ValueFactory valueFactory)
58     {
59         this.valueFactory = valueFactory;
60     }
61 
62 
63     // Implementations for InvocationUnit.
64 
enterMethod(Clazz clazz, Method method, Variables variables)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.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 
exitMethod(Clazz clazz, Method method, Value returnValue)132     public void exitMethod(Clazz clazz, Method method, Value returnValue)
133     {
134         setMethodReturnValue(clazz, method, returnValue);
135     }
136 
137 
invokeMember(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction, Stack stack)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 
visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)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 
visitAnyMethodrefConstant(Clazz clazz, RefConstant methodrefConstant)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.TYPE_VOID)
229         {
230             stack.push(getMethodReturnValue(clazz, methodrefConstant, returnType));
231         }
232     }
233 
visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)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.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      */
setFieldClassValue(Clazz clazz, RefConstant refConstant, ReferenceValue value)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      */
getFieldClassValue(Clazz clazz, RefConstant refConstant, String type)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      */
setFieldValue(Clazz clazz, RefConstant refConstant, Value value)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      */
getFieldValue(Clazz clazz, RefConstant refConstant, String type)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      */
setMethodParameterValue(Clazz clazz, RefConstant refConstant, int parameterIndex, Value value)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      */
getMethodParameterValue(Clazz clazz, Method method, int parameterIndex, String type, Clazz referencedClass)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      */
setMethodReturnValue(Clazz clazz, Method method, Value value)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      */
getMethodReturnValue(Clazz clazz, RefConstant refConstant, String type)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      */
getMethodReturnValue(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant, String type)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 
visitProgramField(ProgramClass programClass, ProgramField programField)390     public void visitProgramField(ProgramClass programClass, ProgramField programField)
391     {
392         returnTypeClass = programField.referencedClass;
393     }
394 
395 
visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)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 
visitLibraryField(LibraryClass programClass, LibraryField programField)406     public void visitLibraryField(LibraryClass programClass, LibraryField programField)
407     {
408         returnTypeClass = programField.referencedClass;
409     }
410 
411 
visitLibraryMethod(LibraryClass programClass, LibraryMethod programMethod)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