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