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