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