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 * @see VerifierFactory 043 * @see PassVerifier 044 */ 045public class Verifier { 046 047 /** 048 * The name of the class this verifier operates on. 049 */ 050 private final String classname; 051 /** A Pass1Verifier for this Verifier instance. */ 052 private Pass1Verifier p1v; 053 /** A Pass2Verifier for this Verifier instance. */ 054 private Pass2Verifier p2v; 055 /** The Pass3aVerifiers for this Verifier instance. Key: Interned string specifying the method number. */ 056 private final Map<String, Pass3aVerifier> p3avs = new HashMap<>(); 057 /** The Pass3bVerifiers for this Verifier instance. Key: Interned string specifying the method number. */ 058 private final Map<String, Pass3bVerifier> p3bvs = new HashMap<>(); 059 060 061 /** Returns the VerificationResult for the given pass. */ 062 public VerificationResult doPass1() { 063 if (p1v == null) { 064 p1v = new Pass1Verifier(this); 065 } 066 return p1v.verify(); 067 } 068 069 070 /** Returns the VerificationResult for the given pass. */ 071 public VerificationResult doPass2() { 072 if (p2v == null) { 073 p2v = new Pass2Verifier(this); 074 } 075 return p2v.verify(); 076 } 077 078 079 /** Returns the VerificationResult for the given pass. */ 080 public VerificationResult doPass3a( final int method_no ) { 081 final String key = Integer.toString(method_no); 082 Pass3aVerifier p3av; 083 p3av = p3avs.get(key); 084 if (p3avs.get(key) == null) { 085 p3av = new Pass3aVerifier(this, method_no); 086 p3avs.put(key, p3av); 087 } 088 return p3av.verify(); 089 } 090 091 092 /** Returns the VerificationResult for the given pass. */ 093 public VerificationResult doPass3b( final int method_no ) { 094 final String key = Integer.toString(method_no); 095 Pass3bVerifier p3bv; 096 p3bv = p3bvs.get(key); 097 if (p3bvs.get(key) == null) { 098 p3bv = new Pass3bVerifier(this, method_no); 099 p3bvs.put(key, p3bv); 100 } 101 return p3bv.verify(); 102 } 103 104 105 /** 106 * Instantiation is done by the VerifierFactory. 107 * 108 * @see VerifierFactory 109 */ 110 Verifier(final String fully_qualified_classname) { 111 classname = fully_qualified_classname; 112 flush(); 113 } 114 115 116 /** 117 * Returns the name of the class this verifier operates on. 118 * This is particularly interesting when this verifier was created 119 * recursively by another Verifier and you got a reference to this 120 * Verifier by the getVerifiers() method of the VerifierFactory. 121 * @see VerifierFactory 122 */ 123 public final String getClassName() { 124 return classname; 125 } 126 127 128 /** 129 * Forget everything known about the class file; that means, really 130 * start a new verification of a possibly different class file from 131 * BCEL's repository. 132 * 133 */ 134 public void flush() { 135 p1v = null; 136 p2v = null; 137 p3avs.clear(); 138 p3bvs.clear(); 139 } 140 141 142 /** 143 * This returns all the (warning) messages collected during verification. 144 * A prefix shows from which verifying pass a message originates. 145 */ 146 public String[] getMessages() throws ClassNotFoundException { 147 final List<String> messages = new ArrayList<>(); 148 if (p1v != null) { 149 final String[] p1m = p1v.getMessages(); 150 for (final String element : p1m) { 151 messages.add("Pass 1: " + element); 152 } 153 } 154 if (p2v != null) { 155 final String[] p2m = p2v.getMessages(); 156 for (final String element : p2m) { 157 messages.add("Pass 2: " + element); 158 } 159 } 160 for (final Pass3aVerifier pv : p3avs.values()) { 161 final String[] p3am = pv.getMessages(); 162 final int meth = pv.getMethodNo(); 163 for (final String element : p3am) { 164 messages.add("Pass 3a, method " + meth + " ('" 165 + org.apache.bcel.Repository.lookupClass(classname).getMethods()[meth] 166 + "'): " + element); 167 } 168 } 169 for (final Pass3bVerifier pv : p3bvs.values()) { 170 final String[] p3bm = pv.getMessages(); 171 final int meth = pv.getMethodNo(); 172 for (final String element : p3bm) { 173 messages.add("Pass 3b, method " + meth + " ('" 174 + org.apache.bcel.Repository.lookupClass(classname).getMethods()[meth] 175 + "'): " + element); 176 } 177 } 178 179 return messages.toArray(new String[messages.size()]); 180 } 181 182 183 /** 184 * Verifies class files. 185 * This is a simple demonstration of how the API of BCEL's 186 * class file verifier "JustIce" may be used. 187 * You should supply command-line arguments which are 188 * fully qualified namea of the classes to verify. These class files 189 * must be somewhere in your CLASSPATH (refer to Sun's 190 * documentation for questions about this) or you must have put the classes 191 * into the BCEL Repository yourself (via 'addClass(JavaClass)'). 192 */ 193 public static void main( final String[] args ) { 194 System.out 195 .println("JustIce by Enver Haase, (C) 2001-2002.\n<http://bcel.sourceforge.net>\n<https://commons.apache.org/bcel>\n"); 196 for (int index = 0; index < args.length; index++) { 197 try { 198 if (args[index].endsWith(".class")) { 199 final int dotclasspos = args[index].lastIndexOf(".class"); 200 if (dotclasspos != -1) { 201 args[index] = args[index].substring(0, dotclasspos); 202 } 203 } 204 args[index] = args[index].replace('/', '.'); 205 System.out.println("Now verifying: " + args[index] + "\n"); 206 verifyType(args[index]); 207 org.apache.bcel.Repository.clearCache(); 208 System.gc(); 209 } catch (final ClassNotFoundException e) { 210 e.printStackTrace(); 211 } 212 } 213 } 214 215 216 static void verifyType(final String fullyQualifiedClassName) throws ClassNotFoundException { 217 final Verifier verifier = VerifierFactory.getVerifier(fullyQualifiedClassName); 218 VerificationResult verificationResult; 219 verificationResult = verifier.doPass1(); 220 System.out.println("Pass 1:\n" + verificationResult); 221 verificationResult = verifier.doPass2(); 222 System.out.println("Pass 2:\n" + verificationResult); 223 if (verificationResult == VerificationResult.VR_OK) { 224 final JavaClass jc = org.apache.bcel.Repository.lookupClass(fullyQualifiedClassName); 225 for (int i = 0; i < jc.getMethods().length; i++) { 226 verificationResult = verifier.doPass3a(i); 227 System.out.println("Pass 3a, method number " + i + " ['" 228 + jc.getMethods()[i] + "']:\n" + verificationResult); 229 verificationResult = verifier.doPass3b(i); 230 System.out.println("Pass 3b, method number " + i + " ['" 231 + jc.getMethods()[i] + "']:\n" + verificationResult); 232 } 233 } 234 System.out.println("Warnings:"); 235 final String[] warnings = verifier.getMessages(); 236 if (warnings.length == 0) { 237 System.out.println("<none>"); 238 } 239 for (final String warning : warnings) { 240 System.out.println(warning); 241 } 242 System.out.println("\n"); 243 // avoid swapping. 244 verifier.flush(); 245 } 246}