• 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.classfile.util;
22 
23 import proguard.classfile.*;
24 import proguard.classfile.attribute.*;
25 import proguard.classfile.attribute.visitor.*;
26 import proguard.classfile.constant.*;
27 import proguard.classfile.constant.visitor.ConstantVisitor;
28 import proguard.classfile.instruction.*;
29 import proguard.classfile.instruction.visitor.InstructionVisitor;
30 import proguard.util.StringMatcher;
31 
32 /**
33  * This InstructionVisitor initializes any constant <code>Class.forName</code> or
34  * <code>.class</code> references of all classes it visits. More specifically,
35  * it fills out the references of string constant pool entries that refer to a
36  * class in the program class pool or in the library class pool.
37  * <p>
38  * It optionally prints notes if on usage of
39  * <code>(SomeClass)Class.forName(variable).newInstance()</code>.
40  * <p>
41  * The class hierarchy must be initialized before using this visitor.
42  *
43  * @see ClassSuperHierarchyInitializer
44  *
45  * @author Eric Lafortune
46  */
47 public class DynamicClassReferenceInitializer
48 extends      SimplifiedVisitor
49 implements   InstructionVisitor,
50              ConstantVisitor,
51              AttributeVisitor
52 {
53     public static final int X = InstructionSequenceMatcher.X;
54     public static final int Y = InstructionSequenceMatcher.Y;
55     public static final int Z = InstructionSequenceMatcher.Z;
56 
57     public static final int A = InstructionSequenceMatcher.A;
58     public static final int B = InstructionSequenceMatcher.B;
59     public static final int C = InstructionSequenceMatcher.C;
60     public static final int D = InstructionSequenceMatcher.D;
61 
62 
63     private final Constant[] CLASS_FOR_NAME_CONSTANTS = new Constant[]
64     {
65         // 0
66         new MethodrefConstant(1, 2, null, null),
67         new ClassConstant(3, null),
68         new NameAndTypeConstant(4, 5),
69         new Utf8Constant(ClassConstants.NAME_JAVA_LANG_CLASS),
70         new Utf8Constant(ClassConstants.METHOD_NAME_CLASS_FOR_NAME),
71         new Utf8Constant(ClassConstants.METHOD_TYPE_CLASS_FOR_NAME),
72 
73         // 6
74         new MethodrefConstant(1, 7, null, null),
75         new NameAndTypeConstant(8, 9),
76         new Utf8Constant(ClassConstants.METHOD_NAME_NEW_INSTANCE),
77         new Utf8Constant(ClassConstants.METHOD_TYPE_NEW_INSTANCE),
78 
79         // 10
80         new MethodrefConstant(1, 11, null, null),
81         new NameAndTypeConstant(12, 13),
82         new Utf8Constant(ClassConstants.METHOD_NAME_CLASS_GET_COMPONENT_TYPE),
83         new Utf8Constant(ClassConstants.METHOD_TYPE_CLASS_GET_COMPONENT_TYPE),
84     };
85 
86     // Class.forName("SomeClass").
87     private final Instruction[] CONSTANT_CLASS_FOR_NAME_INSTRUCTIONS = new Instruction[]
88     {
89         new ConstantInstruction(InstructionConstants.OP_LDC, X),
90         new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
91     };
92 
93     // (SomeClass)Class.forName(someName).newInstance().
94     private final Instruction[] CLASS_FOR_NAME_CAST_INSTRUCTIONS = new Instruction[]
95     {
96         new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
97         new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 6),
98         new ConstantInstruction(InstructionConstants.OP_CHECKCAST, X),
99     };
100 
101 
102 //    private Constant[] DOT_CLASS_JAVAC_CONSTANTS = new Constant[]
103 //    {
104 //        new MethodrefConstant(A, 1, null, null),
105 //        new NameAndTypeConstant(2, 3),
106 //        new Utf8Constant(ClassConstants.METHOD_NAME_DOT_CLASS_JAVAC),
107 //        new Utf8Constant(ClassConstants.METHOD_TYPE_DOT_CLASS_JAVAC),
108 //    };
109 
110     private final Constant[] DOT_CLASS_JAVAC_CONSTANTS = new Constant[]
111     {
112         new MethodrefConstant(A, 1, null, null),
113         new NameAndTypeConstant(B, 2),
114         new Utf8Constant(ClassConstants.METHOD_TYPE_DOT_CLASS_JAVAC),
115     };
116 
117     // SomeClass.class = class$("SomeClass") (javac).
118     private final Instruction[] DOT_CLASS_JAVAC_INSTRUCTIONS = new Instruction[]
119     {
120         new ConstantInstruction(InstructionConstants.OP_LDC, X),
121         new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
122     };
123 
124 
125 //    private Constant[] DOT_CLASS_JIKES_CONSTANTS = new Constant[]
126 //    {
127 //        new MethodrefConstant(A, 1, null, null),
128 //        new NameAndTypeConstant(2, 3),
129 //        new Utf8Constant(ClassConstants.METHOD_NAME_DOT_CLASS_JIKES),
130 //        new Utf8Constant(ClassConstants.METHOD_TYPE_DOT_CLASS_JIKES),
131 //    };
132 
133     private final Constant[] DOT_CLASS_JIKES_CONSTANTS = new Constant[]
134     {
135         new MethodrefConstant(A, 1, null, null),
136         new NameAndTypeConstant(B, 2),
137         new Utf8Constant(ClassConstants.METHOD_TYPE_DOT_CLASS_JIKES),
138     };
139 
140     // SomeClass.class = class("SomeClass", false) (jikes).
141     private final Instruction[] DOT_CLASS_JIKES_INSTRUCTIONS = new Instruction[]
142     {
143         new ConstantInstruction(InstructionConstants.OP_LDC, X),
144         new SimpleInstruction(InstructionConstants.OP_ICONST_0),
145         new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
146     };
147 
148     // return Class.forName(v0).
149     private final Instruction[] DOT_CLASS_JAVAC_IMPLEMENTATION_INSTRUCTIONS = new Instruction[]
150     {
151         new VariableInstruction(InstructionConstants.OP_ALOAD_0),
152         new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
153         new SimpleInstruction(InstructionConstants.OP_ARETURN),
154     };
155 
156     // return Class.forName(v0), if (!v1) .getComponentType().
157     private final Instruction[] DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS = new Instruction[]
158     {
159         new VariableInstruction(InstructionConstants.OP_ALOAD_0),
160         new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
161         new VariableInstruction(InstructionConstants.OP_ALOAD_1),
162         new BranchInstruction(InstructionConstants.OP_IFNE, +6),
163         new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 10),
164         new SimpleInstruction(InstructionConstants.OP_ARETURN),
165     };
166 
167     // return Class.forName(v0).getComponentType().
168     private final Instruction[] DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS2 = new Instruction[]
169     {
170         new VariableInstruction(InstructionConstants.OP_ALOAD_0),
171         new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
172         new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 10),
173         new SimpleInstruction(InstructionConstants.OP_ARETURN),
174     };
175 
176 
177     private final ClassPool      programClassPool;
178     private final ClassPool      libraryClassPool;
179     private final WarningPrinter missingNotePrinter;
180     private final WarningPrinter dependencyWarningPrinter;
181     private final WarningPrinter notePrinter;
182     private final StringMatcher  noteExceptionMatcher;
183 
184 
185     private final InstructionSequenceMatcher constantClassForNameMatcher =
186         new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
187                                        CONSTANT_CLASS_FOR_NAME_INSTRUCTIONS);
188 
189     private final InstructionSequenceMatcher classForNameCastMatcher =
190         new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
191                                        CLASS_FOR_NAME_CAST_INSTRUCTIONS);
192 
193     private final InstructionSequenceMatcher dotClassJavacMatcher =
194         new InstructionSequenceMatcher(DOT_CLASS_JAVAC_CONSTANTS,
195                                        DOT_CLASS_JAVAC_INSTRUCTIONS);
196 
197     private final InstructionSequenceMatcher dotClassJikesMatcher =
198         new InstructionSequenceMatcher(DOT_CLASS_JIKES_CONSTANTS,
199                                        DOT_CLASS_JIKES_INSTRUCTIONS);
200 
201     private final InstructionSequenceMatcher dotClassJavacImplementationMatcher =
202         new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
203                                        DOT_CLASS_JAVAC_IMPLEMENTATION_INSTRUCTIONS);
204 
205     private final InstructionSequenceMatcher dotClassJikesImplementationMatcher =
206         new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
207                                        DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS);
208 
209     private final InstructionSequenceMatcher dotClassJikesImplementationMatcher2 =
210         new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
211                                        DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS2);
212 
213 
214     // A field acting as a return variable for the visitors.
215     private boolean isClassForNameInvocation;
216 
217 
218     /**
219      * Creates a new DynamicClassReferenceInitializer that optionally prints
220      * warnings and notes, with optional class specifications for which never
221      * to print notes.
222      */
DynamicClassReferenceInitializer(ClassPool programClassPool, ClassPool libraryClassPool, WarningPrinter missingNotePrinter, WarningPrinter dependencyWarningPrinter, WarningPrinter notePrinter, StringMatcher noteExceptionMatcher)223     public DynamicClassReferenceInitializer(ClassPool      programClassPool,
224                                             ClassPool      libraryClassPool,
225                                             WarningPrinter missingNotePrinter,
226                                             WarningPrinter dependencyWarningPrinter,
227                                             WarningPrinter notePrinter,
228                                             StringMatcher  noteExceptionMatcher)
229     {
230         this.programClassPool         = programClassPool;
231         this.libraryClassPool         = libraryClassPool;
232         this.missingNotePrinter       = missingNotePrinter;
233         this.dependencyWarningPrinter = dependencyWarningPrinter;
234         this.notePrinter              = notePrinter;
235         this.noteExceptionMatcher     = noteExceptionMatcher;
236     }
237 
238 
239     // Implementations for InstructionVisitor.
240 
visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)241     public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
242     {
243         // Try to match the (SomeClass)Class.forName(someName).newInstance()
244         // construct. Apply this matcher first, so the next matcher can still
245         // reset it after the first instruction.
246         instruction.accept(clazz, method, codeAttribute, offset,
247                            classForNameCastMatcher);
248 
249         // Did we find a match?
250         if (classForNameCastMatcher.isMatching())
251         {
252             // Print out a note about the construct.
253             clazz.constantPoolEntryAccept(classForNameCastMatcher.matchedConstantIndex(X), this);
254         }
255 
256         // Try to match the Class.forName("SomeClass") construct.
257         instruction.accept(clazz, method, codeAttribute, offset,
258                            constantClassForNameMatcher);
259 
260         // Did we find a match?
261         if (constantClassForNameMatcher.isMatching())
262         {
263             // Fill out the matched string constant.
264             clazz.constantPoolEntryAccept(constantClassForNameMatcher.matchedConstantIndex(X), this);
265 
266             // Don't look for the dynamic construct.
267             classForNameCastMatcher.reset();
268         }
269 
270         // Try to match the javac .class construct.
271         instruction.accept(clazz, method, codeAttribute, offset,
272                            dotClassJavacMatcher);
273 
274         // Did we find a match?
275         if (dotClassJavacMatcher.isMatching() &&
276             isDotClassMethodref(clazz, dotClassJavacMatcher.matchedConstantIndex(0)))
277         {
278             // Fill out the matched string constant.
279             clazz.constantPoolEntryAccept(dotClassJavacMatcher.matchedConstantIndex(X), this);
280         }
281 
282         // Try to match the jikes .class construct.
283         instruction.accept(clazz, method, codeAttribute, offset,
284                            dotClassJikesMatcher);
285 
286         // Did we find a match?
287         if (dotClassJikesMatcher.isMatching() &&
288             isDotClassMethodref(clazz, dotClassJikesMatcher.matchedConstantIndex(0)))
289         {
290             // Fill out the matched string constant.
291             clazz.constantPoolEntryAccept(dotClassJikesMatcher.matchedConstantIndex(X), this);
292         }
293     }
294 
295 
296     // Implementations for ConstantVisitor.
297 
298     /**
299      * Fills out the link to the referenced class.
300      */
visitStringConstant(Clazz clazz, StringConstant stringConstant)301     public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
302     {
303         // Save a reference to the corresponding class.
304         String externalClassName = stringConstant.getString(clazz);
305         String internalClassName = ClassUtil.internalClassName(
306                                    ClassUtil.externalBaseType(externalClassName));
307 
308         stringConstant.referencedClass = findClass(clazz.getName(), internalClassName);
309     }
310 
311 
312     /**
313      * Prints out a note about the class cast to this class, if applicable.
314      */
visitClassConstant(Clazz clazz, ClassConstant classConstant)315     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
316     {
317         // Print out a note about the class cast.
318         if (noteExceptionMatcher == null ||
319             !noteExceptionMatcher.matches(classConstant.getName(clazz)))
320         {
321             notePrinter.print(clazz.getName(),
322                               classConstant.getName(clazz),
323                               "Note: " +
324                               ClassUtil.externalClassName(clazz.getName()) +
325                               " calls '(" +
326                               ClassUtil.externalClassName(classConstant.getName(clazz)) +
327                               ")Class.forName(variable).newInstance()'");
328         }
329     }
330 
331 
332     /**
333      * Checks whether the referenced method is a .class method.
334      */
visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)335     public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
336     {
337         String methodType = methodrefConstant.getType(clazz);
338 
339         // Do the method's class and type match?
340         if (methodType.equals(ClassConstants.METHOD_TYPE_DOT_CLASS_JAVAC) ||
341             methodType.equals(ClassConstants.METHOD_TYPE_DOT_CLASS_JIKES))
342         {
343             String methodName = methodrefConstant.getName(clazz);
344 
345             // Does the method's name match one of the special names?
346             isClassForNameInvocation =
347                 methodName.equals(ClassConstants.METHOD_NAME_DOT_CLASS_JAVAC) ||
348                 methodName.equals(ClassConstants.METHOD_NAME_DOT_CLASS_JIKES);
349 
350             if (isClassForNameInvocation)
351             {
352                 return;
353             }
354 
355             String className = methodrefConstant.getClassName(clazz);
356 
357             // Note that we look for the class by name, since the referenced
358             // class has not been initialized yet.
359             Clazz referencedClass = programClassPool.getClass(className);
360             if (referencedClass != null)
361             {
362                 // Check if the code of the referenced method is .class code.
363                 // Note that we look for the method by name and type, since the
364                 // referenced method has not been initialized yet.
365                 referencedClass.methodAccept(methodName,
366                                              methodType,
367                                              new AllAttributeVisitor(this));
368             }
369         }
370     }
371 
372 
373     // Implementations for AttributeVisitor.
374 
visitAnyAttribute(Clazz clazz, Attribute attribute)375     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
376 
377 
visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)378     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
379     {
380         // Check whether this is class$(String), as generated by javac, or
381         // class(String, boolean), as generated by jikes, or an optimized
382         // version.
383         isClassForNameInvocation =
384             isDotClassMethodCode(clazz, method, codeAttribute,
385                                  dotClassJavacImplementationMatcher, 5)  ||
386             isDotClassMethodCode(clazz, method, codeAttribute,
387                                  dotClassJikesImplementationMatcher, 12) ||
388             isDotClassMethodCode(clazz, method, codeAttribute,
389                                  dotClassJikesImplementationMatcher2, 8);
390     }
391 
392 
393     // Small utility methods.
394 
395     /**
396      * Returns whether the given method reference corresponds to a .class
397      * method, as generated by javac or by jikes.
398      */
isDotClassMethodref(Clazz clazz, int methodrefConstantIndex)399     private boolean isDotClassMethodref(Clazz clazz, int methodrefConstantIndex)
400     {
401         isClassForNameInvocation = false;
402 
403         // Check if the code of the referenced method is .class code.
404         clazz.constantPoolEntryAccept(methodrefConstantIndex, this);
405 
406         return isClassForNameInvocation;
407     }
408 
409 
410     /**
411      * Returns whether the first whether the first instructions of the
412      * given code attribute match with the given instruction matcher.
413      */
isDotClassMethodCode(Clazz clazz, Method method, CodeAttribute codeAttribute, InstructionSequenceMatcher codeMatcher, int codeLength)414     private boolean isDotClassMethodCode(Clazz                      clazz,
415                                          Method                     method,
416                                          CodeAttribute              codeAttribute,
417                                          InstructionSequenceMatcher codeMatcher,
418                                          int                        codeLength)
419     {
420         // Check the minimum code length.
421         if (codeAttribute.u4codeLength < codeLength)
422         {
423             return false;
424         }
425 
426         // Check the actual instructions.
427         codeMatcher.reset();
428         codeAttribute.instructionsAccept(clazz, method, 0, codeLength, codeMatcher);
429         return codeMatcher.isMatching();
430     }
431 
432 
433     /**
434      * Returns the class with the given name, either for the program class pool
435      * or from the library class pool, or <code>null</code> if it can't be found.
436      */
findClass(String referencingClassName, String name)437     private Clazz findClass(String referencingClassName, String name)
438     {
439         // Is it an array type?
440         if (ClassUtil.isInternalArrayType(name))
441         {
442             // Ignore any primitive array types.
443             if (!ClassUtil.isInternalClassType(name))
444             {
445                 return null;
446             }
447 
448             // Strip the array part.
449             name = ClassUtil.internalClassNameFromClassType(name);
450         }
451 
452         // First look for the class in the program class pool.
453         Clazz clazz = programClassPool.getClass(name);
454 
455         // Otherwise look for the class in the library class pool.
456         if (clazz == null)
457         {
458             clazz = libraryClassPool.getClass(name);
459 
460             if (clazz == null &&
461                 missingNotePrinter != null)
462             {
463                 // We didn't find the superclass or interface. Print a note.
464                 missingNotePrinter.print(referencingClassName,
465                                          name,
466                                          "Note: " +
467                                          ClassUtil.externalClassName(referencingClassName) +
468                                          ": can't find dynamically referenced class " +
469                                          ClassUtil.externalClassName(name));
470             }
471         }
472         else if (dependencyWarningPrinter != null)
473         {
474             // The superclass or interface was found in the program class pool.
475             // Print a warning.
476             dependencyWarningPrinter.print(referencingClassName,
477                                            name,
478                                            "Warning: library class " +
479                                            ClassUtil.externalClassName(referencingClassName) +
480                                            " depends dynamically on program class " +
481                                            ClassUtil.externalClassName(name));
482         }
483 
484         return clazz;
485     }
486 }
487