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 public static void main(final String[] args) { 060 System.out.println(BANNER); 061 for (int index = 0; index < args.length; index++) { 062 try { 063 if (args[index].endsWith(JavaClass.EXTENSION)) { 064 final int dotclasspos = args[index].lastIndexOf(JavaClass.EXTENSION); 065 if (dotclasspos != -1) { 066 args[index] = args[index].substring(0, dotclasspos); 067 } 068 } 069 args[index] = Utility.pathToPackage(args[index]); 070 System.out.println("Now verifying: " + args[index] + "\n"); 071 verifyType(args[index]); 072 org.apache.bcel.Repository.clearCache(); 073 System.gc(); 074 } catch (final ClassNotFoundException e) { 075 e.printStackTrace(); 076 } 077 } 078 } 079 080 static void verifyType(final String fullyQualifiedClassName) throws ClassNotFoundException { 081 final Verifier verifier = VerifierFactory.getVerifier(fullyQualifiedClassName); 082 VerificationResult verificationResult; 083 verificationResult = verifier.doPass1(); 084 System.out.println("Pass 1:\n" + verificationResult); 085 verificationResult = verifier.doPass2(); 086 System.out.println("Pass 2:\n" + verificationResult); 087 if (verificationResult == VerificationResult.VR_OK) { 088 final JavaClass jc = org.apache.bcel.Repository.lookupClass(fullyQualifiedClassName); 089 for (int i = 0; i < jc.getMethods().length; i++) { 090 verificationResult = verifier.doPass3a(i); 091 System.out.println("Pass 3a, method number " + i + " ['" + jc.getMethods()[i] + "']:\n" + verificationResult); 092 verificationResult = verifier.doPass3b(i); 093 System.out.println("Pass 3b, method number " + i + " ['" + jc.getMethods()[i] + "']:\n" + verificationResult); 094 } 095 } 096 System.out.println("Warnings:"); 097 final String[] warnings = verifier.getMessages(); 098 if (warnings.length == 0) { 099 System.out.println("<none>"); 100 } 101 for (final String warning : warnings) { 102 System.out.println(warning); 103 } 104 System.out.println("\n"); 105 // avoid swapping. 106 verifier.flush(); 107 } 108 109 /** 110 * The name of the class this verifier operates on. 111 */ 112 private final String className; 113 114 /** A Pass1Verifier for this Verifier instance. */ 115 private Pass1Verifier p1v; 116 117 /** A Pass2Verifier for this Verifier instance. */ 118 private Pass2Verifier p2v; 119 120 /** The Pass3aVerifiers for this Verifier instance. Key: Interned string specifying the method number. */ 121 private final Map<String, Pass3aVerifier> p3avs = new HashMap<>(); 122 123 /** The Pass3bVerifiers for this Verifier instance. Key: Interned string specifying the method number. */ 124 private final Map<String, Pass3bVerifier> p3bvs = new HashMap<>(); 125 126 /** 127 * Instantiation is done by the VerifierFactory. 128 * 129 * @see VerifierFactory 130 */ 131 Verifier(final String fullyQualifiedClassName) { 132 className = fullyQualifiedClassName; 133 } 134 135 /** Returns the VerificationResult for the given pass. */ 136 public VerificationResult doPass1() { 137 if (p1v == null) { 138 p1v = new Pass1Verifier(this); 139 } 140 return p1v.verify(); 141 } 142 143 /** Returns the VerificationResult for the given pass. */ 144 public VerificationResult doPass2() { 145 if (p2v == null) { 146 p2v = new Pass2Verifier(this); 147 } 148 return p2v.verify(); 149 } 150 151 /** 152 * Returns the VerificationResult for the given pass. 153 * 154 * @param methodNo The method to verify 155 * @return the VerificationResult 156 */ 157 public VerificationResult doPass3a(final int methodNo) { 158 return p3avs.computeIfAbsent(Integer.toString(methodNo), k -> new Pass3aVerifier(this, methodNo)).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 doPass3b(final int methodNo) { 168 return p3bvs.computeIfAbsent(Integer.toString(methodNo), k -> new Pass3bVerifier(this, methodNo)).verify(); 169 } 170 171 /** 172 * Forget everything known about the class file; that means, really start a new verification of a possibly different 173 * class file from BCEL's repository. 174 */ 175 public void flush() { 176 p1v = null; 177 p2v = null; 178 p3avs.clear(); 179 p3bvs.clear(); 180 } 181 182 /** 183 * Returns the name of the class this verifier operates on. This is particularly interesting when this verifier was 184 * created recursively by another Verifier and you got a reference to this Verifier by the getVerifiers() method of the 185 * VerifierFactory. 186 * 187 * @see VerifierFactory 188 */ 189 public final String getClassName() { 190 return className; 191 } 192 193 /** 194 * This returns all the (warning) messages collected during verification. A prefix shows from which verifying pass a 195 * message originates. 196 * 197 * @throws ClassNotFoundException if this class can't be found. 198 */ 199 public String[] getMessages() throws ClassNotFoundException { 200 final List<String> messages = new ArrayList<>(); 201 if (p1v != null) { 202 p1v.getMessagesList().forEach(element -> messages.add("Pass 1: " + element)); 203 } 204 if (p2v != null) { 205 p2v.getMessagesList().forEach(element -> messages.add("Pass 2: " + element)); 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}