001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 * 017 */ 018package org.apache.bcel.verifier; 019 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024 025import org.apache.bcel.classfile.JavaClass; 026import org.apache.bcel.verifier.statics.Pass1Verifier; 027import org.apache.bcel.verifier.statics.Pass2Verifier; 028import org.apache.bcel.verifier.statics.Pass3aVerifier; 029import org.apache.bcel.verifier.structurals.Pass3bVerifier; 030 031/** 032 * A Verifier instance is there to verify a class file according to The Java Virtual 033 * Machine Specification, 2nd Edition. 034 * 035 * Pass-3b-verification includes pass-3a-verification; 036 * pass-3a-verification includes pass-2-verification; 037 * pass-2-verification includes pass-1-verification. 038 * 039 * A Verifier creates PassVerifier instances to perform the actual verification. 040 * Verifier instances are usually generated by the VerifierFactory. 041 * 042 * @version $Id: Verifier.java 1749603 2016-06-21 20:50:19Z ggregory $ 043 * @see VerifierFactory 044 * @see PassVerifier 045 */ 046public class Verifier { 047 048 /** 049 * The name of the class this verifier operates on. 050 */ 051 private final String classname; 052 /** A Pass1Verifier for this Verifier instance. */ 053 private Pass1Verifier p1v; 054 /** A Pass2Verifier for this Verifier instance. */ 055 private Pass2Verifier p2v; 056 /** The Pass3aVerifiers for this Verifier instance. Key: Interned string specifying the method number. */ 057 private final Map<String, Pass3aVerifier> p3avs = new HashMap<>(); 058 /** The Pass3bVerifiers for this Verifier instance. Key: Interned string specifying the method number. */ 059 private final Map<String, Pass3bVerifier> p3bvs = new HashMap<>(); 060 061 062 /** Returns the VerificationResult for the given pass. */ 063 public VerificationResult doPass1() { 064 if (p1v == null) { 065 p1v = new Pass1Verifier(this); 066 } 067 return p1v.verify(); 068 } 069 070 071 /** Returns the VerificationResult for the given pass. */ 072 public VerificationResult doPass2() { 073 if (p2v == null) { 074 p2v = new Pass2Verifier(this); 075 } 076 return p2v.verify(); 077 } 078 079 080 /** Returns the VerificationResult for the given pass. */ 081 public VerificationResult doPass3a( final int method_no ) { 082 final String key = Integer.toString(method_no); 083 Pass3aVerifier p3av; 084 p3av = p3avs.get(key); 085 if (p3avs.get(key) == null) { 086 p3av = new Pass3aVerifier(this, method_no); 087 p3avs.put(key, p3av); 088 } 089 return p3av.verify(); 090 } 091 092 093 /** Returns the VerificationResult for the given pass. */ 094 public VerificationResult doPass3b( final int method_no ) { 095 final String key = Integer.toString(method_no); 096 Pass3bVerifier p3bv; 097 p3bv = p3bvs.get(key); 098 if (p3bvs.get(key) == null) { 099 p3bv = new Pass3bVerifier(this, method_no); 100 p3bvs.put(key, p3bv); 101 } 102 return p3bv.verify(); 103 } 104 105 106 /** 107 * Instantiation is done by the VerifierFactory. 108 * 109 * @see VerifierFactory 110 */ 111 Verifier(final String fully_qualified_classname) { 112 classname = fully_qualified_classname; 113 flush(); 114 } 115 116 117 /** 118 * Returns the name of the class this verifier operates on. 119 * This is particularly interesting when this verifier was created 120 * recursively by another Verifier and you got a reference to this 121 * Verifier by the getVerifiers() method of the VerifierFactory. 122 * @see VerifierFactory 123 */ 124 public final String getClassName() { 125 return classname; 126 } 127 128 129 /** 130 * Forget everything known about the class file; that means, really 131 * start a new verification of a possibly different class file from 132 * BCEL's repository. 133 * 134 */ 135 public void flush() { 136 p1v = null; 137 p2v = null; 138 p3avs.clear(); 139 p3bvs.clear(); 140 } 141 142 143 /** 144 * This returns all the (warning) messages collected during verification. 145 * A prefix shows from which verifying pass a message originates. 146 */ 147 public String[] getMessages() throws ClassNotFoundException { 148 final List<String> messages = new ArrayList<>(); 149 if (p1v != null) { 150 final String[] p1m = p1v.getMessages(); 151 for (final String element : p1m) { 152 messages.add("Pass 1: " + element); 153 } 154 } 155 if (p2v != null) { 156 final String[] p2m = p2v.getMessages(); 157 for (final String element : p2m) { 158 messages.add("Pass 2: " + element); 159 } 160 } 161 for (final Pass3aVerifier pv : p3avs.values()) { 162 final String[] p3am = pv.getMessages(); 163 final int meth = pv.getMethodNo(); 164 for (final String element : p3am) { 165 messages.add("Pass 3a, method " + meth + " ('" 166 + org.apache.bcel.Repository.lookupClass(classname).getMethods()[meth] 167 + "'): " + element); 168 } 169 } 170 for (final Pass3bVerifier pv : p3bvs.values()) { 171 final String[] p3bm = pv.getMessages(); 172 final int meth = pv.getMethodNo(); 173 for (final String element : p3bm) { 174 messages.add("Pass 3b, method " + meth + " ('" 175 + org.apache.bcel.Repository.lookupClass(classname).getMethods()[meth] 176 + "'): " + element); 177 } 178 } 179 180 return messages.toArray(new String[messages.size()]); 181 } 182 183 184 /** 185 * Verifies class files. 186 * This is a simple demonstration of how the API of BCEL's 187 * class file verifier "JustIce" may be used. 188 * You should supply command-line arguments which are 189 * fully qualified namea of the classes to verify. These class files 190 * must be somewhere in your CLASSPATH (refer to Sun's 191 * documentation for questions about this) or you must have put the classes 192 * into the BCEL Repository yourself (via 'addClass(JavaClass)'). 193 */ 194 public static void main( final String[] args ) { 195 System.out 196 .println("JustIce by Enver Haase, (C) 2001-2002.\n<http://bcel.sourceforge.net>\n<http://commons.apache.org/bcel>\n"); 197 for (int k = 0; k < args.length; k++) { 198 try { 199 if (args[k].endsWith(".class")) { 200 final int dotclasspos = args[k].lastIndexOf(".class"); 201 if (dotclasspos != -1) { 202 args[k] = args[k].substring(0, dotclasspos); 203 } 204 } 205 args[k] = args[k].replace('/', '.'); 206 System.out.println("Now verifying: " + args[k] + "\n"); 207 final Verifier v = VerifierFactory.getVerifier(args[k]); 208 VerificationResult vr; 209 vr = v.doPass1(); 210 System.out.println("Pass 1:\n" + vr); 211 vr = v.doPass2(); 212 System.out.println("Pass 2:\n" + vr); 213 if (vr == VerificationResult.VR_OK) { 214 final JavaClass jc = org.apache.bcel.Repository.lookupClass(args[k]); 215 for (int i = 0; i < jc.getMethods().length; i++) { 216 vr = v.doPass3a(i); 217 System.out.println("Pass 3a, method number " + i + " ['" 218 + jc.getMethods()[i] + "']:\n" + vr); 219 vr = v.doPass3b(i); 220 System.out.println("Pass 3b, method number " + i + " ['" 221 + jc.getMethods()[i] + "']:\n" + vr); 222 } 223 } 224 System.out.println("Warnings:"); 225 final String[] warnings = v.getMessages(); 226 if (warnings.length == 0) { 227 System.out.println("<none>"); 228 } 229 for (final String warning : warnings) { 230 System.out.println(warning); 231 } 232 System.out.println("\n"); 233 // avoid swapping. 234 v.flush(); 235 org.apache.bcel.Repository.clearCache(); 236 System.gc(); 237 } catch (final ClassNotFoundException e) { 238 e.printStackTrace(); 239 } 240 } 241 } 242}