/* * 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(); } } }