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