| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.codehaus.groovy.ant; |
| |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.taskdefs.MatchingTask; |
| import org.objectweb.asm.ClassReader; |
| import org.objectweb.asm.ClassWriter; |
| import org.objectweb.asm.tree.AbstractInsnNode; |
| import org.objectweb.asm.tree.ClassNode; |
| import org.objectweb.asm.tree.MethodNode; |
| import org.objectweb.asm.tree.analysis.Analyzer; |
| import org.objectweb.asm.tree.analysis.SimpleVerifier; |
| import org.objectweb.asm.util.CheckClassAdapter; |
| import org.objectweb.asm.util.TraceMethodVisitor; |
| |
| import java.io.BufferedInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.List; |
| |
| /** |
| * Verify Class files. This task can take the following |
| * arguments: |
| * <ul> |
| * <li>dir |
| * </ul> |
| * When this task executes, it will recursively scan the dir and |
| * look for class files to verify. |
| */ |
| public class VerifyClass extends MatchingTask { |
| private String topDir = null; |
| private boolean verbose = false; |
| |
| public VerifyClass() { |
| } |
| |
| @Override |
| public void execute() throws BuildException { |
| if (topDir == null) throw new BuildException("no dir attribute is set"); |
| File top = new File(topDir); |
| if (!top.exists()) throw new BuildException("the directory " + top + " does not exist"); |
| log("top dir is " + top); |
| int fails = execute(top); |
| if (fails == 0) { |
| log("no bytecode problems found"); |
| } else { |
| log("found " + fails + " failing classes"); |
| } |
| } |
| |
| public void setDir(String dir) throws BuildException { |
| topDir = dir; |
| } |
| |
| public void setVerbose(boolean v) { |
| verbose = v; |
| } |
| |
| private int execute(File dir) { |
| int fails = 0; |
| File[] files = dir.listFiles(); |
| if (files != null) { |
| for (File f : files) { |
| if (f.isDirectory()) { |
| fails += execute(f); |
| } else if (f.getName().endsWith(".class")) { |
| try { |
| boolean ok = readClass(f.getCanonicalPath()); |
| if (!ok) fails++; |
| } catch (IOException ioe) { |
| log(ioe.getMessage()); |
| throw new BuildException(ioe); |
| } |
| } |
| } |
| } |
| return fails; |
| } |
| |
| private boolean readClass(String clazz) throws IOException { |
| ClassNode ca; |
| try (final InputStream inputStream = |
| new BufferedInputStream( |
| new FileInputStream(clazz))) { |
| ClassReader cr = new ClassReader(inputStream); |
| ca = new ClassNode() { |
| @Override |
| public void visitEnd() { |
| //accept(cv); |
| } |
| }; |
| cr.accept(new CheckClassAdapter(ca), ClassWriter.COMPUTE_MAXS); |
| } |
| |
| boolean failed = false; |
| List<MethodNode> methods = ca.methods; |
| for (MethodNode method : methods) { |
| if (method.instructions.size() > 0) { |
| Analyzer<?> a = new Analyzer<>(new SimpleVerifier()); |
| try { |
| a.analyze(ca.name, method); |
| continue; |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| |
| if (!failed) { |
| failed = true; |
| log("verifying of class " + clazz + " failed"); |
| } |
| if (verbose) log(method.name + method.desc); |
| |
| TraceMethodVisitor mv = new TraceMethodVisitor(null); |
| for (int j = 0; j < method.instructions.size(); ++j) { |
| AbstractInsnNode insn = method.instructions.get(j); |
| if (insn != null) { |
| insn.accept(mv); |
| } else { |
| mv.visitLabel(null); |
| } |
| } |
| mv.visitMaxs(0, 0); |
| } |
| } |
| return !failed; |
| } |
| |
| } |