KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > asm > util > CheckClassAdapter


1 /***
2  * ASM: a very small and fast Java bytecode manipulation framework
3  * Copyright (c) 2000-2005 INRIA, France Telecom
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the copyright holders nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28  * THE POSSIBILITY OF SUCH DAMAGE.
29  */

30 package com.tc.asm.util;
31
32 import java.io.FileInputStream JavaDoc;
33 import java.io.PrintWriter JavaDoc;
34 import java.util.List JavaDoc;
35
36 import com.tc.asm.AnnotationVisitor;
37 import com.tc.asm.FieldVisitor;
38 import com.tc.asm.ClassAdapter;
39 import com.tc.asm.ClassReader;
40 import com.tc.asm.ClassVisitor;
41 import com.tc.asm.MethodVisitor;
42 import com.tc.asm.Opcodes;
43 import com.tc.asm.Attribute;
44 import com.tc.asm.Type;
45 import com.tc.asm.tree.AbstractInsnNode;
46 import com.tc.asm.tree.MethodNode;
47 import com.tc.asm.tree.ClassNode;
48 import com.tc.asm.tree.TryCatchBlockNode;
49 import com.tc.asm.tree.analysis.Analyzer;
50 import com.tc.asm.tree.analysis.SimpleVerifier;
51 import com.tc.asm.tree.analysis.Frame;
52
53 /**
54  * A {@link ClassAdapter} that checks that its methods are properly used. More
55  * precisely this class adapter checks each method call individually, based
56  * <i>only</i> on its arguments, but does <i>not</i> check the <i>sequence</i>
57  * of method calls. For example, the invalid sequence
58  * <tt>visitField(ACC_PUBLIC, "i", "I", null)</tt> <tt>visitField(ACC_PUBLIC,
59  * "i", "D", null)</tt>
60  * will <i>not</i> be detected by this class adapter.
61  *
62  * @author Eric Bruneton
63  */

64 public class CheckClassAdapter extends ClassAdapter {
65
66     /**
67      * <tt>true</tt> if the visit method has been called.
68      */

69     private boolean start;
70
71     /**
72      * <tt>true</tt> if the visitSource method has been called.
73      */

74     private boolean source;
75
76     /**
77      * <tt>true</tt> if the visitOuterClass method has been called.
78      */

79     private boolean outer;
80
81     /**
82      * <tt>true</tt> if the visitEnd method has been called.
83      */

84     private boolean end;
85
86     /**
87      * Checks a given class. <p> Usage: CheckClassAdapter &lt;fully qualified
88      * class name or class file name&gt;
89      *
90      * @param args the command line arguments.
91      *
92      * @throws Exception if the class cannot be found, or if an IO exception
93      * occurs.
94      */

95     public static void main(final String JavaDoc[] args) throws Exception JavaDoc {
96         if (args.length != 1) {
97             System.err.println("Verifies the given class.");
98             System.err.println("Usage: CheckClassAdapter "
99                     + "<fully qualified class name or class file name>");
100             return;
101         }
102         ClassReader cr;
103         if (args[0].endsWith(".class")) {
104             cr = new ClassReader(new FileInputStream JavaDoc(args[0]));
105         } else {
106             cr = new ClassReader(args[0]);
107         }
108
109         verify(cr, false, new PrintWriter JavaDoc(System.err));
110     }
111
112     /**
113      * Checks a given class
114      *
115      * @param cr a <code>ClassReader</code> that contains bytecode for the analysis.
116      * @param dump true if bytecode should be printed out not only when errors are found.
117      * @param pw write where results going to be printed
118      */

119     public static void verify(ClassReader cr, boolean dump, PrintWriter JavaDoc pw) {
120         ClassNode cn = new ClassNode();
121         cr.accept(new CheckClassAdapter(cn), true);
122
123         List JavaDoc methods = cn.methods;
124         for (int i = 0; i < methods.size(); ++i) {
125             MethodNode method = (MethodNode) methods.get(i);
126             if (method.instructions.size() > 0) {
127                 Analyzer a = new Analyzer(new SimpleVerifier(Type.getType("L"
128                         + cn.name + ";"),
129                         Type.getType("L" + cn.superName + ";"),
130                         (cn.access & Opcodes.ACC_INTERFACE) != 0));
131                 try {
132                     a.analyze(cn.name, method);
133                     if (!dump) {
134                         continue;
135                     }
136                 } catch (Exception JavaDoc e) {
137                     e.printStackTrace();
138                 }
139                 Frame[] frames = a.getFrames();
140
141                 TraceMethodVisitor mv = new TraceMethodVisitor();
142
143                 pw.println(method.name + method.desc);
144                 for (int j = 0; j < method.instructions.size(); ++j) {
145                     ((AbstractInsnNode) method.instructions.get(j)).accept(mv);
146                     
147                     StringBuffer JavaDoc s = new StringBuffer JavaDoc();
148                     Frame f = frames[j];
149                     if (f == null) {
150                         s.append('?');
151                     } else {
152                         for (int k = 0; k < f.getLocals(); ++k) {
153                             s.append(getShortName(f.getLocal(k).toString()))
154                                     .append(' ');
155                         }
156                         s.append(" : ");
157                         for (int k = 0; k < f.getStackSize(); ++k) {
158                             s.append(getShortName(f.getStack(k).toString()))
159                                     .append(' ');
160                         }
161                     }
162                     while (s.length() < method.maxStack + method.maxLocals + 1)
163                     {
164                         s.append(' ');
165                     }
166                     pw.print(Integer.toString(j + 100000).substring(1));
167                     pw.print(" " + s + " : " + mv.buf); // mv.text.get(j));
168
}
169                 for (int j = 0; j < method.tryCatchBlocks.size(); ++j) {
170                     ((TryCatchBlockNode) method.tryCatchBlocks.get(j)).accept(mv);
171                     pw.print(" " + mv.buf);
172                 }
173                 pw.println();
174             }
175         }
176     }
177
178     private static String JavaDoc getShortName(String JavaDoc name) {
179         int n = name.lastIndexOf('/');
180         int k = name.length();
181         if(name.charAt(k-1)==';') k--;
182         return n==-1 ? name : name.substring(n+1, k);
183     }
184
185     /**
186      * Constructs a new {@link CheckClassAdapter}.
187      *
188      * @param cv the class visitor to which this adapter must delegate calls.
189      */

190     public CheckClassAdapter(final ClassVisitor cv) {
191         super(cv);
192     }
193
194     // ------------------------------------------------------------------------
195
// Implementation of the ClassVisitor interface
196
// ------------------------------------------------------------------------
197

198     public void visit(
199         final int version,
200         final int access,
201         final String JavaDoc name,
202         final String JavaDoc signature,
203         final String JavaDoc superName,
204         final String JavaDoc[] interfaces)
205     {
206         if (start) {
207             throw new IllegalStateException JavaDoc("visit must be called only once");
208         } else {
209             start = true;
210         }
211         checkState();
212         checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL
213                 + Opcodes.ACC_SUPER + Opcodes.ACC_INTERFACE
214                 + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC
215                 + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM
216                 + Opcodes.ACC_DEPRECATED);
217         CheckMethodAdapter.checkInternalName(name, "class name");
218         if ("java/lang/Object".equals(name)) {
219             if (superName != null) {
220                 throw new IllegalArgumentException JavaDoc("The super class name of the Object class must be 'null'");
221             }
222         } else {
223             CheckMethodAdapter.checkInternalName(superName, "super class name");
224         }
225         if (signature != null) {
226             // TODO
227
}
228         if ((access & Opcodes.ACC_INTERFACE) != 0) {
229             if (!"java/lang/Object".equals(superName)) {
230                 throw new IllegalArgumentException JavaDoc("The super class name of interfaces must be 'java/lang/Object'");
231             }
232         }
233         if (interfaces != null) {
234             for (int i = 0; i < interfaces.length; ++i) {
235                 CheckMethodAdapter.checkInternalName(interfaces[i],
236                         "interface name at index " + i);
237             }
238         }
239         cv.visit(version, access, name, signature, superName, interfaces);
240     }
241
242     public void visitSource(final String JavaDoc file, final String JavaDoc debug) {
243         checkState();
244         if (source) {
245             throw new IllegalStateException JavaDoc("visitSource can be called only once.");
246         }
247         source = true;
248         cv.visitSource(file, debug);
249     }
250
251     public void visitOuterClass(
252         final String JavaDoc owner,
253         final String JavaDoc name,
254         final String JavaDoc desc)
255     {
256         checkState();
257         if (outer) {
258             throw new IllegalStateException JavaDoc("visitSource can be called only once.");
259         }
260         outer = true;
261         if (owner == null) {
262             throw new IllegalArgumentException JavaDoc("Illegal outer class owner");
263         }
264         if (desc != null) {
265             CheckMethodAdapter.checkMethodDesc(desc);
266         }
267         cv.visitOuterClass(owner, name, desc);
268     }
269
270     public void visitInnerClass(
271         final String JavaDoc name,
272         final String JavaDoc outerName,
273         final String JavaDoc innerName,
274         final int access)
275     {
276         checkState();
277         CheckMethodAdapter.checkInternalName(name, "class name");
278         if (outerName != null) {
279             CheckMethodAdapter.checkInternalName(outerName, "outer class name");
280         }
281         if (innerName != null) {
282             CheckMethodAdapter.checkIdentifier(innerName, "inner class name");
283         }
284         checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
285                 + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
286                 + Opcodes.ACC_FINAL + Opcodes.ACC_INTERFACE
287                 + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC
288                 + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM);
289         cv.visitInnerClass(name, outerName, innerName, access);
290     }
291
292     public FieldVisitor visitField(
293         final int access,
294         final String JavaDoc name,
295         final String JavaDoc desc,
296         final String JavaDoc signature,
297         final Object JavaDoc value)
298     {
299         checkState();
300         checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
301                 + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
302                 + Opcodes.ACC_FINAL + Opcodes.ACC_VOLATILE
303                 + Opcodes.ACC_TRANSIENT + Opcodes.ACC_SYNTHETIC
304                 + Opcodes.ACC_ENUM + Opcodes.ACC_DEPRECATED);
305         CheckMethodAdapter.checkIdentifier(name, "field name");
306         CheckMethodAdapter.checkDesc(desc, false);
307         if (signature != null) {
308             // TODO
309
}
310         if (value != null) {
311             CheckMethodAdapter.checkConstant(value);
312         }
313         FieldVisitor av = cv.visitField(access, name, desc, signature, value);
314         return new CheckFieldAdapter(av);
315     }
316
317     public MethodVisitor visitMethod(
318         final int access,
319         final String JavaDoc name,
320         final String JavaDoc desc,
321         final String JavaDoc signature,
322         final String JavaDoc[] exceptions)
323     {
324         checkState();
325         checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
326                 + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
327                 + Opcodes.ACC_FINAL + Opcodes.ACC_SYNCHRONIZED
328                 + Opcodes.ACC_BRIDGE + Opcodes.ACC_VARARGS + Opcodes.ACC_NATIVE
329                 + Opcodes.ACC_ABSTRACT + Opcodes.ACC_STRICT
330                 + Opcodes.ACC_SYNTHETIC + Opcodes.ACC_DEPRECATED);
331         CheckMethodAdapter.checkMethodIdentifier(name, "method name");
332         CheckMethodAdapter.checkMethodDesc(desc);
333         if (signature != null) {
334             // TODO
335
}
336         if (exceptions != null) {
337             for (int i = 0; i < exceptions.length; ++i) {
338                 CheckMethodAdapter.checkInternalName(exceptions[i],
339                         "exception name at index " + i);
340             }
341         }
342         return new CheckMethodAdapter(cv.visitMethod(access,
343                 name,
344                 desc,
345                 signature,
346                 exceptions));
347     }
348
349     public AnnotationVisitor visitAnnotation(
350         final String JavaDoc desc,
351         final boolean visible)
352     {
353         checkState();
354         CheckMethodAdapter.checkDesc(desc, false);
355         return new CheckAnnotationAdapter(cv.visitAnnotation(desc, visible));
356     }
357
358     public void visitAttribute(final Attribute attr) {
359         checkState();
360         if (attr == null) {
361             throw new IllegalArgumentException JavaDoc("Invalid attribute (must not be null)");
362         }
363         cv.visitAttribute(attr);
364     }
365
366     public void visitEnd() {
367         checkState();
368         end = true;
369         cv.visitEnd();
370     }
371
372     // ------------------------------------------------------------------------
373
// Utility methods
374
// ------------------------------------------------------------------------
375

376     /**
377      * Checks that the visit method has been called and that visitEnd has not
378      * been called.
379      */

380     private void checkState() {
381         if (!start) {
382             throw new IllegalStateException JavaDoc("Cannot visit member before visit has been called.");
383         }
384         if (end) {
385             throw new IllegalStateException JavaDoc("Cannot visit member after visitEnd has been called.");
386         }
387     }
388
389     /**
390      * Checks that the given access flags do not contain invalid flags. This
391      * method also checks that mutually incompatible flags are not set
392      * simultaneously.
393      *
394      * @param access the access flags to be checked
395      * @param possibleAccess the valid access flags.
396      */

397     static void checkAccess(final int access, final int possibleAccess) {
398         if ((access & ~possibleAccess) != 0) {
399             throw new IllegalArgumentException JavaDoc("Invalid access flags: "
400                     + access);
401         }
402         int pub = ((access & Opcodes.ACC_PUBLIC) != 0 ? 1 : 0);
403         int pri = ((access & Opcodes.ACC_PRIVATE) != 0 ? 1 : 0);
404         int pro = ((access & Opcodes.ACC_PROTECTED) != 0 ? 1 : 0);
405         if (pub + pri + pro > 1) {
406             throw new IllegalArgumentException JavaDoc("public private and protected are mutually exclusive: "
407                     + access);
408         }
409         int fin = ((access & Opcodes.ACC_FINAL) != 0 ? 1 : 0);
410         int abs = ((access & Opcodes.ACC_ABSTRACT) != 0 ? 1 : 0);
411         if (fin + abs > 1) {
412             throw new IllegalArgumentException JavaDoc("final and abstract are mutually exclusive: "
413                     + access);
414         }
415     }
416 }
417
Popular Tags