/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package org.visage.tools.bytecodeverifier;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipInputStream;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.tree.analysis.*;
import org.objectweb.asm.util.CheckClassAdapter;
/**
* This class verifies bytecode in .class files, .jar files and
* in directories (recursively) using Objectweb's ASM library.
*
* This is intendend to be used to verify class files generated by
* visagec and the mangler tool.
*
* @author A. Sundararajan
*/
public class Verifier {
// do not create me!
private Verifier() {
}
private static boolean verify(String name, ClassReader reader, final PrintWriter err, boolean verbose) {
if (verbose) {
err.println("Verifying " + name);
}
ClassNode classNode = new ClassNode();
reader.accept(new CheckClassAdapter(classNode), ClassReader.SKIP_DEBUG);
Type syperType = classNode.superName == null
? null : Type.getObjectType(classNode.superName);
List<Type> interfaces = new ArrayList<Type>();
for (Object iface : classNode.interfaces) {
interfaces.add(Type.getObjectType(iface.toString()));
}
List<MethodNode> methods = classNode.methods;
for (int i = 0; i < methods.size(); i++) {
MethodNode method = methods.get(i);
SimpleVerifier verifier = new SimpleVerifier(
Type.getObjectType(classNode.name),
syperType, interfaces, false) {
@Override
protected boolean isAssignableFrom(Type t, Type u) {
// FIXME: Assignment check in the superclass implementation uses
// Class.forName to check currently loaded classes. We don't want
// to use loaded Class objects in the test. We leave the reference
// assignment compatibility checks for now.
return true;
}
};
Analyzer analyzer = new Analyzer(verifier);
try {
analyzer.analyze(classNode.name, method);
} catch (AnalyzerException exp) {
err.println("Verification failed for " + name);
exp.printStackTrace(err);
err.flush();
return false;
}
}
err.flush();
return true;
}
private static boolean verifyClass(String name, InputStream is, PrintWriter err, boolean verbose) throws IOException {
ClassReader cr = new ClassReader(is);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new CheckClassAdapter(cw);
cr.accept(cv, 0);
return verify(name, new ClassReader(cw.toByteArray()), err, verbose);
}
private static boolean verifyZip(String name, ZipInputStream zis, PrintWriter err, boolean verbose) throws IOException {
if (verbose) {
err.println("Verifying " + name);
}
boolean result = false;
ZipEntry ze = zis.getNextEntry();
while (ze != null) {
if (!ze.isDirectory()) {
String zname = ze.getName();
if (zname.endsWith(".class")) {
result |= verifyClass(ze.getName(), zis, err, verbose);
}
}
zis.closeEntry();
ze = zis.getNextEntry();
}
return result;
}
public static boolean verifyFile(File f, PrintWriter err, boolean verbose) throws IOException {
if (!f.exists()) {
err.println(f.getAbsolutePath() + " does not exist!");
}
boolean result = false;
if (f.isDirectory()) {
File[] contents = f.listFiles();
for (File c : contents) {
result |= verifyFile(c, err, verbose);
}
} else {
String path = f.getAbsolutePath();
InputStream is = new BufferedInputStream(new FileInputStream(f));
if (path.endsWith(".jar") || path.endsWith(".zip")) {
ZipInputStream zis = null;
try {
zis = new ZipInputStream(is);
result |= verifyZip(path, zis, err, verbose);
} finally {
if (zis != null) {
zis.close();
}
if (is != null) {
is.close();
}
}
} else if (path.endsWith(".class")) {
// verify single .class
try {
result |= verifyClass(path, is, err, verbose);
} finally {
if (is != null) {
is.close();
}
}
}
}
return result;
}
public static boolean verifyPath(String path, PrintWriter err, boolean verbose) throws IOException {
if (verbose) {
err.println("Verifying " + path);
}
boolean result = false;
String[] files = path.split(File.pathSeparator);
for (String file : files) {
result |= verifyFile(new File(file), err, verbose);
}
if (verbose && result) {
err.println("All files in " + path + " verified OK!");
}
err.flush();
return result;
}
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("Usage: java " + Verifier.class + " <path>");
System.exit(1);
}
try {
verifyPath(args[0], new PrintWriter(System.out), true);
} catch (IOException exp) {
exp.printStackTrace();
}
}
}