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 */ 017package org.apache.bcel.verifier; 018 019import java.util.ArrayList; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.bcel.classfile.JavaClass; 025import org.apache.bcel.classfile.Utility; 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; 030import org.apache.commons.lang3.ArrayUtils; 031 032/** 033 * A Verifier instance is there to verify a class file according to The Java Virtual Machine Specification, 2nd Edition. 034 * 035 * Pass-3b-verification includes pass-3a-verification; pass-3a-verification includes pass-2-verification; 036 * pass-2-verification includes pass-1-verification. 037 * 038 * A Verifier creates PassVerifier instances to perform the actual verification. Verifier instances are usually 039 * generated by the VerifierFactory. 040 * 041 * @see VerifierFactory 042 * @see PassVerifier 043 */ 044public class Verifier { 045 046 static final String NAME = "Apache Commons BCEL"; 047 static final String BANNER = NAME + "\nhttps://commons.apache.org/bcel\n"; 048 049 static final Verifier[] EMPTY_ARRAY = {}; 050 051 /** 052 * Verifies class files. This is a simple demonstration of how the API of BCEL's class file verifier "JustIce" may be 053 * used. You should supply command-line arguments which are fully qualified namea of the classes to verify. These class 054 * files must be somewhere in your CLASSPATH (refer to Sun's documentation for questions about this) or you must have 055 * put the classes into the BCEL Repository yourself (via 'addClass(JavaClass)'). 056 */ 057 public static void main(final String[] args) { 058 System.out.println(BANNER); 059 for (int index = 0; index < args.length; index++) { 060 try { 061 if (args[index].endsWith(JavaClass.EXTENSION)) { 062 final int dotclasspos = args[index].lastIndexOf(JavaClass.EXTENSION); 063 if (dotclasspos != -1) { 064 args[index] = args[index].substring(0, dotclasspos); 065 } 066 } 067 args[index] = Utility.pathToPackage(args[index]); 068 System.out.println("Now verifying: " + args[index] + "\n"); 069 verifyType(args[index]); 070 org.apache.bcel.Repository.clearCache(); 071 System.gc(); 072 } catch (final ClassNotFoundException e) { 073 e.printStackTrace(); 074 } 075 } 076 } 077 078 static void verifyType(final String fullyQualifiedClassName) throws ClassNotFoundException { 079 final Verifier verifier = VerifierFactory.getVerifier(fullyQualifiedClassName); 080 VerificationResult verificationResult; 081 verificationResult = verifier.doPass1(); 082 System.out.println("Pass 1:\n" + verificationResult); 083 verificationResult = verifier.doPass2(); 084 System.out.println("Pass 2:\n" + verificationResult); 085 if (verificationResult == VerificationResult.VR_OK) { 086 final JavaClass jc = org.apache.bcel.Repository.lookupClass(fullyQualifiedClassName); 087 for (int i = 0; i < jc.getMethods().length; i++) { 088 verificationResult = verifier.doPass3a(i); 089 System.out.println("Pass 3a, method number " + i + " ['" + jc.getMethods()[i] + "']:\n" + verificationResult); 090 verificationResult = verifier.doPass3b(i); 091 System.out.println("Pass 3b, method number " + i + " ['" + jc.getMethods()[i] + "']:\n" + verificationResult); 092 } 093 } 094 System.out.println("Warnings:"); 095 final String[] warnings = verifier.getMessages(); 096 if (warnings.length == 0) { 097 System.out.println("<none>"); 098 } 099 for (final String warning : warnings) { 100 System.out.println(warning); 101 } 102 System.out.println("\n"); 103 // avoid swapping. 104 verifier.flush(); 105 } 106 107 /** 108 * The name of the class this verifier operates on. 109 */ 110 private final String className; 111 112 /** A Pass1Verifier for this Verifier instance. */ 113 private Pass1Verifier p1v; 114 115 /** A Pass2Verifier for this Verifier instance. */ 116 private Pass2Verifier p2v; 117 118 /** The Pass3aVerifiers for this Verifier instance. Key: Interned string specifying the method number. */ 119 private final Map<String, Pass3aVerifier> p3avs = new HashMap<>(); 120 121 /** The Pass3bVerifiers for this Verifier instance. Key: Interned string specifying the method number. */ 122 private final Map<String, Pass3bVerifier> p3bvs = new HashMap<>(); 123 124 /** 125 * Instantiation is done by the VerifierFactory. 126 * 127 * @see VerifierFactory 128 */ 129 Verifier(final String fullyQualifiedClassName) { 130 className = fullyQualifiedClassName; 131 } 132 133 /** Returns the VerificationResult for the given pass. */ 134 public VerificationResult doPass1() { 135 if (p1v == null) { 136 p1v = new Pass1Verifier(this); 137 } 138 return p1v.verify(); 139 } 140 141 /** Returns the VerificationResult for the given pass. */ 142 public VerificationResult doPass2() { 143 if (p2v == null) { 144 p2v = new Pass2Verifier(this); 145 } 146 return p2v.verify(); 147 } 148 149 /** 150 * Returns the VerificationResult for the given pass. 151 * 152 * @param methodNo The method to verify 153 * @return the VerificationResult 154 */ 155 public VerificationResult doPass3a(final int methodNo) { 156 return p3avs.computeIfAbsent(Integer.toString(methodNo), k -> new Pass3aVerifier(this, methodNo)).verify(); 157 } 158 159 /** 160 * Returns the VerificationResult for the given pass. 161 * 162 * @param methodNo The method to verify 163 * @return the VerificationResult 164 */ 165 public VerificationResult doPass3b(final int methodNo) { 166 return p3bvs.computeIfAbsent(Integer.toString(methodNo), k -> new Pass3bVerifier(this, methodNo)).verify(); 167 } 168 169 /** 170 * Forget everything known about the class file; that means, really start a new verification of a possibly different 171 * class file from BCEL's repository. 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 * @throws ClassNotFoundException if this class can't be found. 196 */ 197 public String[] getMessages() throws ClassNotFoundException { 198 final List<String> messages = new ArrayList<>(); 199 if (p1v != null) { 200 p1v.getMessagesList().forEach(element -> messages.add("Pass 1: " + element)); 201 } 202 if (p2v != null) { 203 p2v.getMessagesList().forEach(element -> messages.add("Pass 2: " + element)); 204 } 205 for (final Pass3aVerifier pv : p3avs.values()) { 206 final int meth = pv.getMethodNo(); 207 for (final String element : pv.getMessages()) { 208 messages.add("Pass 3a, method " + meth + " ('" + org.apache.bcel.Repository.lookupClass(className).getMethods()[meth] + "'): " + element); 209 } 210 } 211 for (final Pass3bVerifier pv : p3bvs.values()) { 212 final int meth = pv.getMethodNo(); 213 for (final String element : pv.getMessages()) { 214 messages.add("Pass 3b, method " + meth + " ('" + org.apache.bcel.Repository.lookupClass(className).getMethods()[meth] + "'): " + element); 215 } 216 } 217 218 return messages.toArray(ArrayUtils.EMPTY_STRING_ARRAY); 219 } 220}