KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > proguard > classfile > util > DynamicClassReferenceInitializer


1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  * of Java bytecode.
4  *
5  * Copyright (c) 2002-2007 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.ClassNameListMatcher;
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 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.INTERNAL_CLASS_NAME_JAVA_LANG_CLASS),
70         new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_FOR_NAME),
71         new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_FOR_NAME),
72
73         // 6
74
new MethodrefConstant(1, 7, null, null),
75         new NameAndTypeConstant(8, 9),
76         new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_NEW_INSTANCE),
77         new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_NEW_INSTANCE),
78
79         // 10
80
new MethodrefConstant(1, 11, null, null),
81         new NameAndTypeConstant(12, 13),
82         new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_COMPONENT_TYPE),
83         new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_COMPONENT_TYPE),
84     };
85
86     // Class.forName("SomeClass").
87
private 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 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.INTERNAL_METHOD_NAME_DOT_CLASS_JAVAC),
107
// new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC),
108
// };
109

110     private Constant[] DOT_CLASS_JAVAC_CONSTANTS = new Constant[]
111     {
112         new MethodrefConstant(A, 1, null, null),
113         new NameAndTypeConstant(B, 2),
114         new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC),
115     };
116
117     // SomeClass.class = class$("SomeClass") (javac).
118
private 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.INTERNAL_METHOD_NAME_DOT_CLASS_JIKES),
130
// new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES),
131
// };
132

133     private Constant[] DOT_CLASS_JIKES_CONSTANTS = new Constant[]
134     {
135         new MethodrefConstant(A, 1, null, null),
136         new NameAndTypeConstant(B, 2),
137         new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES),
138     };
139
140     // SomeClass.class = class("SomeClass", false) (jikes).
141
private 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 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 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 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 ClassPool programClassPool;
178     private ClassPool libraryClassPool;
179     private WarningPrinter notePrinter;
180     private ClassNameListMatcher noteExceptionMatcher;
181
182
183     private InstructionSequenceMatcher constantClassForNameMatcher =
184         new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
185                                        CONSTANT_CLASS_FOR_NAME_INSTRUCTIONS);
186
187     private InstructionSequenceMatcher classForNameCastMatcher =
188         new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
189                                        CLASS_FOR_NAME_CAST_INSTRUCTIONS);
190
191     private InstructionSequenceMatcher dotClassJavacMatcher =
192         new InstructionSequenceMatcher(DOT_CLASS_JAVAC_CONSTANTS,
193                                        DOT_CLASS_JAVAC_INSTRUCTIONS);
194
195     private InstructionSequenceMatcher dotClassJikesMatcher =
196         new InstructionSequenceMatcher(DOT_CLASS_JIKES_CONSTANTS,
197                                        DOT_CLASS_JIKES_INSTRUCTIONS);
198
199     private InstructionSequenceMatcher dotClassJavacImplementationMatcher =
200         new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
201                                        DOT_CLASS_JAVAC_IMPLEMENTATION_INSTRUCTIONS);
202
203     private InstructionSequenceMatcher dotClassJikesImplementationMatcher =
204         new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
205                                        DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS);
206
207     private InstructionSequenceMatcher dotClassJikesImplementationMatcher2 =
208         new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
209                                        DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS2);
210
211
212     // A field acting as a return variable for the visitors.
213
private boolean isClassForNameInvocation;
214
215
216     /**
217      * Creates a new DynamicClassReferenceInitializer that optionally prints
218      * notes, with optional class specifications for which never to print notes.
219      */

220     public DynamicClassReferenceInitializer(ClassPool programClassPool,
221                                             ClassPool libraryClassPool,
222                                             WarningPrinter notePrinter,
223                                             ClassNameListMatcher noteExceptionMatcher)
224     {
225         this.programClassPool = programClassPool;
226         this.libraryClassPool = libraryClassPool;
227         this.notePrinter = notePrinter;
228         this.noteExceptionMatcher = noteExceptionMatcher;
229     }
230
231
232     // Implementations for InstructionVisitor.
233

234     public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
235     {
236         // Try to match the Class.forName("SomeClass") construct.
237
instruction.accept(clazz, method, codeAttribute, offset,
238                            constantClassForNameMatcher);
239
240         // Did we find a match?
241
if (constantClassForNameMatcher.isMatching())
242         {
243             // Fill out the matched string constant.
244
clazz.constantPoolEntryAccept(constantClassForNameMatcher.matchedConstantIndex(X), this);
245
246             // Don't look for the dynamic construct.
247
classForNameCastMatcher.reset();
248         }
249
250         // Try to match the (SomeClass)Class.forName(someName).newInstance()
251
// construct.
252
instruction.accept(clazz, method, codeAttribute, offset,
253                            classForNameCastMatcher);
254
255         // Did we find a match?
256
if (classForNameCastMatcher.isMatching())
257         {
258             // Print out a note about the construct.
259
clazz.constantPoolEntryAccept(classForNameCastMatcher.matchedConstantIndex(X), this);
260         }
261
262         // Try to match the javac .class construct.
263
instruction.accept(clazz, method, codeAttribute, offset,
264                            dotClassJavacMatcher);
265
266         // Did we find a match?
267
if (dotClassJavacMatcher.isMatching() &&
268             isDotClassMethodref(clazz, dotClassJavacMatcher.matchedConstantIndex(0)))
269         {
270             // Fill out the matched string constant.
271
clazz.constantPoolEntryAccept(dotClassJavacMatcher.matchedConstantIndex(X), this);
272         }
273
274         // Try to match the jikes .class construct.
275
instruction.accept(clazz, method, codeAttribute, offset,
276                            dotClassJikesMatcher);
277
278         // Did we find a match?
279
if (dotClassJikesMatcher.isMatching() &&
280             isDotClassMethodref(clazz, dotClassJikesMatcher.matchedConstantIndex(0)))
281         {
282             // Fill out the matched string constant.
283
clazz.constantPoolEntryAccept(dotClassJikesMatcher.matchedConstantIndex(X), this);
284         }
285     }
286
287
288     // Implementations for ConstantVisitor.
289

290     /**
291      * Fills out the link to the referenced class.
292      */

293     public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
294     {
295         // Save a reference to the corresponding class.
296
String JavaDoc externalClassName = stringConstant.getString(clazz);
297         String JavaDoc internalClassName = ClassUtil.internalClassName(externalClassName);
298
299         stringConstant.referencedClass = findClass(internalClassName);
300     }
301
302
303     /**
304      * Prints out a note about the class cast to this class, if applicable.
305      */

306     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
307     {
308         // Print out a note about the class cast.
309
if (notePrinter != null &&
310             (noteExceptionMatcher == null ||
311              !noteExceptionMatcher.matches(classConstant.getName(clazz))))
312         {
313             notePrinter.print("Note: " +
314                               ClassUtil.externalClassName(clazz.getName()) +
315                               " calls '(" +
316                               ClassUtil.externalClassName(classConstant.getName(clazz)) +
317                               ")Class.forName(variable).newInstance()'");
318         }
319     }
320
321
322     /**
323      * Checks whether the referenced method is a .class method.
324      */

325     public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
326     {
327         String JavaDoc methodType = methodrefConstant.getType(clazz);
328
329         // Do the method's class and type match?
330
if (methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC) ||
331             methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES))
332         {
333             String JavaDoc methodName = methodrefConstant.getName(clazz);
334
335             // Does the method's name match one of the special names?
336
isClassForNameInvocation =
337                 methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JAVAC) ||
338                 methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JIKES);
339
340             if (isClassForNameInvocation)
341             {
342                 return;
343             }
344
345             String JavaDoc className = methodrefConstant.getClassName(clazz);
346
347             // Note that we look for the class by name, since the referenced
348
// class has not been initialized yet.
349
Clazz referencedClass = programClassPool.getClass(className);
350             if (referencedClass != null)
351             {
352                 // Check if the code of the referenced method is .class code.
353
// Note that we look for the method by name and type, since the
354
// referenced method has not been initialized yet.
355
referencedClass.methodAccept(methodName,
356                                              methodType,
357                                              new AllAttributeVisitor(this));
358             }
359         }
360     }
361
362
363     // Implementations for AttributeVisitor.
364

365     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
366
367
368     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
369     {
370         // Check whether this is class$(String), as generated by javac, or
371
// class(String, boolean), as generated by jikes, or an optimized
372
// version.
373
isClassForNameInvocation =
374             isDotClassMethodCode(clazz, method, codeAttribute,
375                                  dotClassJavacImplementationMatcher, 5) ||
376             isDotClassMethodCode(clazz, method, codeAttribute,
377                                  dotClassJikesImplementationMatcher, 12) ||
378             isDotClassMethodCode(clazz, method, codeAttribute,
379                                  dotClassJikesImplementationMatcher2, 8);
380     }
381
382
383     // Small utility methods.
384

385     /**
386      * Returns whether the given method reference corresponds to a .class
387      * method, as generated by javac or by jikes.
388      */

389     private boolean isDotClassMethodref(Clazz clazz, int methodrefConstantIndex)
390     {
391         isClassForNameInvocation = false;
392
393         // Check if the code of the referenced method is .class code.
394
clazz.constantPoolEntryAccept(methodrefConstantIndex, this);
395
396         return isClassForNameInvocation;
397     }
398
399
400     /**
401      * Returns whether the first whether the first instructions of the
402      * given code attribute match with the given instruction matcher.
403      */

404     private boolean isDotClassMethodCode(Clazz clazz,
405                                          Method method,
406                                          CodeAttribute codeAttribute,
407                                          InstructionSequenceMatcher codeMatcher,
408                                          int codeLength)
409     {
410         // Check the minimum code length.
411
if (codeAttribute.u4codeLength < codeLength)
412         {
413             return false;
414         }
415
416         // Check the actual instructions.
417
codeMatcher.reset();
418         codeAttribute.instructionsAccept(clazz, method, 0, codeLength, codeMatcher);
419         return codeMatcher.isMatching();
420     }
421
422
423     /**
424      * Returns the class with the given name, either for the program class pool
425      * or from the library class pool, or <code>null</code> if it can't be found.
426      */

427     private Clazz findClass(String JavaDoc name)
428     {
429         // First look for the class in the program class pool.
430
Clazz clazz = programClassPool.getClass(name);
431
432         // Otherwise look for the class in the library class pool.
433
if (clazz == null)
434         {
435             clazz = libraryClassPool.getClass(name);
436         }
437
438         return clazz;
439     }
440 }
441
Popular Tags