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 }