001 /*
002 $Id: VerifyClass.java 3419 2006-01-19 00:07:02Z blackdrag $
003
004 Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005
006 Redistribution and use of this software and associated documentation
007 ("Software"), with or without modification, are permitted provided
008 that the following conditions are met:
009
010 1. Redistributions of source code must retain copyright
011 statements and notices. Redistributions must also contain a
012 copy of this document.
013
014 2. Redistributions in binary form must reproduce the
015 above copyright notice, this list of conditions and the
016 following disclaimer in the documentation and/or other
017 materials provided with the distribution.
018
019 3. The name "groovy" must not be used to endorse or promote
020 products derived from this Software without prior written
021 permission of The Codehaus. For written permission,
022 please contact info@codehaus.org.
023
024 4. Products derived from this Software may not be called "groovy"
025 nor may "groovy" appear in their names without prior written
026 permission of The Codehaus. "groovy" is a registered
027 trademark of The Codehaus.
028
029 5. Due credit should be given to The Codehaus -
030 http://groovy.codehaus.org/
031
032 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
036 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043 OF THE POSSIBILITY OF SUCH DAMAGE.
044
045 */
046 package org.codehaus.groovy.ant;
047
048 import java.io.File;
049 import java.io.FileInputStream;
050 import java.io.IOException;
051 import java.util.List;
052
053 import org.apache.tools.ant.BuildException;
054 import org.apache.tools.ant.taskdefs.MatchingTask;
055 import org.objectweb.asm.ClassReader;
056 import org.objectweb.asm.Label;
057 import org.objectweb.asm.util.CheckClassAdapter;
058 import org.objectweb.asm.util.TraceMethodVisitor;
059 import org.objectweb.asm.tree.AbstractInsnNode ;
060 import org.objectweb.asm.tree.ClassNode ;
061 import org.objectweb.asm.tree.MethodNode ;
062 import org.objectweb.asm.tree.analysis.Analyzer ;
063 import org.objectweb.asm.tree.analysis.Frame ;
064 import org.objectweb.asm.tree.analysis.SimpleVerifier ;
065
066 /**
067 * Verify Class files. This task can take the following
068 * arguments:
069 * <ul>
070 * <li>dir
071 * </ul>
072 * When this task executes, it will recursively scan the dir and
073 * look for class files to verify.
074 */
075 public class VerifyClass extends MatchingTask {
076 private String topDir=null;
077 private boolean verbose = false;
078
079 public VerifyClass() {}
080
081 public void execute() throws BuildException {
082 if (topDir==null) throw new BuildException("no dir attribute is set");
083 File top = new File(topDir);
084 if (!top.exists()) throw new BuildException("the directory "+top+" does not exist");
085 log ("top dir is "+top);
086 int fails = execute(top);
087 if (fails==0) {
088 log ("no bytecode problems found");
089 } else {
090 log ("found "+fails+" failing classes");
091 }
092 }
093
094 public void setDir(String dir) throws BuildException {
095 topDir = dir;
096 }
097
098 public void setVerbose(boolean v) {
099 verbose = v;
100 }
101
102 private int execute(File dir) {
103 int fails = 0;
104 File[] files = dir.listFiles();
105 for (int i = 0; i < files.length; i++) {
106 File f =files[i];
107 if (f.isDirectory()) {
108 fails += execute(f);
109 } else if (f.getName().endsWith(".class")) {
110 try {
111 boolean ok = readClass(f.getCanonicalPath());
112 if (!ok) fails++;
113 } catch (IOException ioe) {
114 log(ioe.getMessage());
115 throw new BuildException(ioe);
116 }
117 }
118 }
119 return fails;
120 }
121
122 private boolean readClass(String clazz) throws IOException {
123 ClassReader cr = new ClassReader(new FileInputStream(clazz));
124 ClassNode ca = new ClassNode ( )
125 {
126 public void visitEnd () {
127 //accept(cv);
128 }
129 } ;
130 cr.accept(new CheckClassAdapter(ca), true);
131 boolean failed=false;
132
133 List methods = ca.methods;
134 for (int i = 0; i < methods.size(); ++i) {
135 MethodNode method = (MethodNode)methods.get(i);
136 if (method.instructions.size() > 0) {
137 Analyzer a = new Analyzer(new SimpleVerifier());
138 try {
139 a.analyze(ca.name, method);
140 continue;
141 } catch (Exception e) {
142 e.printStackTrace();
143 }
144 final Frame[] frames = a.getFrames();
145
146 if (!failed) {
147 failed=true;
148 log("verifying of class "+clazz+" failed");
149 }
150 if (verbose) log(method.name + method.desc);
151 TraceMethodVisitor cv = new TraceMethodVisitor(null) {
152 public void visitMaxs (int maxStack, int maxLocals) {
153 StringBuffer buffer = new StringBuffer();
154 for (int i = 0; i < text.size(); ++i) {
155 String s = frames[i] == null ? "null" : frames[i].toString();
156 while (s.length() < maxStack+maxLocals+1) {
157 s += " ";
158 }
159 buffer.append(Integer.toString(i + 100000).substring(1));
160 buffer.append(" ");
161 buffer.append(s);
162 buffer.append(" : ");
163 buffer.append(text.get(i));
164 }
165 if (verbose) log(buffer.toString());
166 }
167 };
168 for (int j = 0; j < method.instructions.size(); ++j) {
169 Object insn = method.instructions.get(j);
170 if (insn instanceof AbstractInsnNode) {
171 ((AbstractInsnNode)insn).accept(cv);
172 } else {
173 cv.visitLabel((Label)insn);
174 }
175 }
176 cv.visitMaxs(method.maxStack, method.maxLocals);
177 }
178 }
179 return !failed;
180 }
181
182 }