org.apache.bcel.verifier.statics.Pass1Verifier Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of commons-bcel Show documentation
Show all versions of commons-bcel Show documentation
Apache Commons Bytecode Engineering Library, with type annotations
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.bcel.verifier.statics;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.ClassFormatException;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.verifier.PassVerifier;
import org.apache.bcel.verifier.VerificationResult;
import org.apache.bcel.verifier.Verifier;
import org.apache.bcel.verifier.exc.LoadingException;
import org.apache.bcel.verifier.exc.Utility;
/**
* This PassVerifier verifies a class file according to pass 1 as
* described in The Java Virtual Machine Specification, 2nd edition.
* More detailed information is to be found at the do_verify() method's
* documentation.
*
* @version $Id$
* @see #do_verify()
*/
public final class Pass1Verifier extends PassVerifier{
/**
* DON'T USE THIS EVEN PRIVATELY! USE getJavaClass() INSTEAD.
* @see #getJavaClass()
*/
private JavaClass jc;
/**
* The Verifier that created this.
*/
private final Verifier myOwner;
/**
* Used to load in and return the myOwner-matching JavaClass object when needed.
* Avoids loading in a class file when it's not really needed!
*/
private JavaClass getJavaClass() {
if (jc == null) {
try {
jc = Repository.lookupClass(myOwner.getClassName());
} catch (final ClassNotFoundException e) {
// FIXME: currently, Pass1Verifier treats jc == null as a special
// case, so we don't need to do anything here. A better solution
// would be to simply throw the ClassNotFoundException
// out of this method.
}
}
return jc;
}
/**
* Should only be instantiated by a Verifier.
*
* @see Verifier
*/
public Pass1Verifier(final Verifier owner) {
myOwner = owner;
}
/**
* Pass-one verification basically means loading in a class file.
* The Java Virtual Machine Specification is not too precise about
* what makes the difference between passes one and two.
* The answer is that only pass one is performed on a class file as
* long as its resolution is not requested; whereas pass two and
* pass three are performed during the resolution process.
* Only four constraints to be checked are explicitly stated by
* The Java Virtual Machine Specification, 2nd edition:
*
* - The first four bytes must contain the right magic number (0xCAFEBABE).
*
- All recognized attributes must be of the proper length.
*
- The class file must not be truncated or have extra bytes at the end.
*
- The constant pool must not contain any superficially unrecognizable information.
*
* A more in-depth documentation of what pass one should do was written by
* Philip W. L. Fong:
*
* - the file should not be truncated.
*
- the file should not have extra bytes at the end.
*
- all variable-length structures should be well-formatted:
*
* - there should only be constant_pool_count-1 many entries in the constant pool.
*
- all constant pool entries should have size the same as indicated by their type tag.
*
- there are exactly interfaces_count many entries in the interfaces array of the class file.
*
- there are exactly fields_count many entries in the fields array of the class file.
*
- there are exactly methods_count many entries in the methods array of the class file.
*
- there are exactly attributes_count many entries in the attributes array of the class file,
* fields, methods, and code attribute.
*
- there should be exactly attribute_length many bytes in each attribute.
* Inconsistency between attribute_length and the actually size of the attribute content should be uncovered.
* For example, in an Exceptions attribute, the actual number of exceptions as required by the number_of_exceptions field
* might yeild an attribute size that doesn't match the attribute_length. Such an anomaly should be detected.
*
- all attributes should have proper length. In particular, under certain context (e.g. while parsing method_info),
* recognizable attributes (e.g. "Code" attribute) should have correct format (e.g. attribute_length is 2).
*
* - Also, certain constant values are checked for validity:
*
* - The magic number should be 0xCAFEBABE.
*
- The major and minor version numbers are valid.
*
- All the constant pool type tags are recognizable.
*
- All undocumented access flags are masked off before use. Strictly speaking, this is not really a check.
*
- The field this_class should point to a string that represents a legal non-array class name,
* and this name should be the same as the class file being loaded.
*
- the field super_class should point to a string that represents a legal non-array class name.
*
- Because some of the above checks require cross referencing the constant pool entries,
* guards are set up to make sure that the referenced entries are of the right type and the indices
* are within the legal range (0 < index < constant_pool_count).
*
* - Extra checks done in pass 1:
*
* - the constant values of static fields should have the same type as the fields.
*
- the number of words in a parameter list does not exceed 255 and locals_max.
*
- the name and signature of fields and methods are verified to be of legal format.
*
*
* (From the Paper
* The Mysterious Pass One, first draft, September 2, 1997.)
*
* However, most of this is done by parsing a class file or generating a class file into BCEL's internal data structure.
* Therefore, all that is really done here is look up the class file from BCEL's repository.
* This is also motivated by the fact that some omitted things
* (like the check for extra bytes at the end of the class file) are handy when actually using BCEL to repair a class file
* (otherwise you would not be able to load it into BCEL).
*
* @see org.apache.bcel.Repository
* @see org.apache.bcel.Const#JVM_CLASSFILE_MAGIC
*/
@Override
public VerificationResult do_verify() {
JavaClass jc;
try{
jc = getJavaClass(); //loads in the class file if not already done.
if (jc != null) {
/* If we find more constraints to check, we should do this in an own method. */
if (! myOwner.getClassName().equals(jc.getClassName())) {
// This should maybe caught by BCEL: In case of renamed .class files we get wrong
// JavaClass objects here.
throw new LoadingException("Wrong name: the internal name of the .class file '"+jc.getClassName()+
"' does not match the file's name '"+myOwner.getClassName()+"'.");
}
}
}
catch(final LoadingException e) {
return new VerificationResult(VerificationResult.VERIFIED_REJECTED, e.getMessage());
}
catch(final ClassFormatException e) {
return new VerificationResult(VerificationResult.VERIFIED_REJECTED, e.getMessage());
}
catch(final RuntimeException e) {
// BCEL does not catch every possible RuntimeException; e.g. if
// a constant pool index is referenced that does not exist.
return new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Parsing via BCEL did not succeed. "+
e.getClass().getName()+" occured:\n"+Utility.getStackTrace(e));
}
if (jc != null) {
return VerificationResult.VR_OK;
}
//TODO: Maybe change Repository's behaviour to throw a LoadingException instead of just returning "null"
// if a class file cannot be found or in another way be looked up.
return new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Repository.lookup() failed. FILE NOT FOUND?");
}
/**
* Currently this returns an empty array of String.
* One could parse the error messages of BCEL
* (written to java.lang.System.err) when loading
* a class file such as detecting unknown attributes
* or trailing garbage at the end of a class file.
* However, Markus Dahm does not like the idea so this
* method is currently useless and therefore marked as
* TODO.
*/
@Override
public String[] getMessages() {
// This method is only here to override the javadoc-comment.
return super.getMessages();
}
}