in src/main/java/org/apache/bcel/verifier/statics/Pass2Verifier.java [153:324]
public void visitCode(final Code obj) {// vmspec2 4.7.3
try {
// No code attribute allowed for native or abstract methods: see visitMethod(Method).
// Code array constraints are checked in Pass3 (3a and 3b).
checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
final String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
if (!name.equals("Code")) {
throw new ClassConstraintException("The Code attribute '" + tostring(obj) + "' is not correctly named 'Code' but '" + name + "'.");
}
if (!(carrier.predecessor() instanceof Method)) {
addMessage(
"Code attribute '" + tostring(obj) + "' is not declared in a method_info structure but in '" + carrier.predecessor() + "'. Ignored.");
return;
}
final Method m = (Method) carrier.predecessor(); // we can assume this method was visited before;
// i.e. the data consistency was verified.
if (obj.getCode().length == 0) {
throw new ClassConstraintException("Code array of Code attribute '" + tostring(obj) + "' (method '" + m + "') must not be empty.");
}
// In JustIce, the check for correct offsets into the code array is delayed to Pass 3a.
final CodeException[] excTable = obj.getExceptionTable();
for (final CodeException element : excTable) {
final int excIndex = element.getCatchType();
if (excIndex != 0) { // if 0, it catches all Throwables
checkIndex(obj, excIndex, CONST_Class);
final ConstantClass cc = (ConstantClass) cp.getConstant(excIndex);
// cannot be sure this ConstantClass has already been visited (checked)!
checkIndex(cc, cc.getNameIndex(), CONST_Utf8);
final String cname = Utility.pathToPackage(((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes());
Verifier v = VerifierFactory.getVerifier(cname);
VerificationResult vr = v.doPass1();
if (vr != VerificationResult.VR_OK) {
throw new ClassConstraintException("Code attribute '" + tostring(obj) + "' (method '" + m + "') has an exception_table entry '"
+ tostring(element) + "' that references '" + cname + "' as an Exception but it does not pass verification pass 1: " + vr);
}
// We cannot safely trust any other "instanceof" mechanism. We need to transitively verify
// the ancestor hierarchy.
JavaClass e = Repository.lookupClass(cname);
final JavaClass t = Repository.lookupClass(Type.THROWABLE.getClassName());
final JavaClass o = Repository.lookupClass(Type.OBJECT.getClassName());
while (e != o) {
if (e == t) {
break; // It's a subclass of Throwable, OKAY, leave.
}
v = VerifierFactory.getVerifier(e.getSuperclassName());
vr = v.doPass1();
if (vr != VerificationResult.VR_OK) {
throw new ClassConstraintException("Code attribute '" + tostring(obj) + "' (method '" + m + "') has an exception_table entry '"
+ tostring(element) + "' that references '" + cname + "' as an Exception but '" + e.getSuperclassName()
+ "' in the ancestor hierachy does not pass verification pass 1: " + vr);
}
e = Repository.lookupClass(e.getSuperclassName());
}
if (e != t) {
throw new ClassConstraintException(
"Code attribute '" + tostring(obj) + "' (method '" + m + "') has an exception_table entry '" + tostring(element)
+ "' that references '" + cname + "' as an Exception but it is not a subclass of '" + t.getClassName() + "'.");
}
}
}
// Create object for local variables information
// This is highly unelegant due to usage of the Visitor pattern.
// TODO: rework it.
int methodNumber = -1;
final Method[] ms = Repository.lookupClass(verifier.getClassName()).getMethods();
for (int mn = 0; mn < ms.length; mn++) {
if (m == ms[mn]) {
methodNumber = mn;
break;
}
}
// If the .class file is malformed the loop above may not find a method.
// Try matching names instead of pointers.
if (methodNumber < 0) {
for (int mn = 0; mn < ms.length; mn++) {
if (m.getName().equals(ms[mn].getName())) {
methodNumber = mn;
break;
}
}
}
if (methodNumber < 0) { // Mmmmh. Can we be sure BCEL does not sometimes instantiate new objects?
throw new AssertionViolatedException("Could not find a known BCEL Method object in the corresponding BCEL JavaClass object.");
}
localVariablesInfos[methodNumber] = new LocalVariablesInfo(obj.getMaxLocals());
int numOfLvtAttribs = 0;
// Now iterate through the attributes the Code attribute has.
final Attribute[] atts = obj.getAttributes();
for (final Attribute att : atts) {
if (!(att instanceof LineNumberTable) && !(att instanceof LocalVariableTable)) {
addMessage("Attribute '" + tostring(att) + "' as an attribute of Code attribute '" + tostring(obj) + "' (method '" + m
+ "') is unknown and will therefore be ignored.");
} else {// LineNumberTable or LocalVariableTable
addMessage("Attribute '" + tostring(att) + "' as an attribute of Code attribute '" + tostring(obj) + "' (method '" + m
+ "') will effectively be ignored and is only useful for debuggers and such.");
}
// LocalVariableTable check (partially delayed to Pass3a).
// Here because its easier to collect the information of the
// (possibly more than one) LocalVariableTables belonging to
// one certain Code attribute.
if (att instanceof LocalVariableTable) { // checks conforming to vmspec2 4.7.9
final LocalVariableTable lvt = (LocalVariableTable) att;
checkIndex(lvt, lvt.getNameIndex(), CONST_Utf8);
final String lvtname = ((ConstantUtf8) cp.getConstant(lvt.getNameIndex())).getBytes();
if (!lvtname.equals("LocalVariableTable")) {
throw new ClassConstraintException(
"The LocalVariableTable attribute '" + tostring(lvt) + "' is not correctly named 'LocalVariableTable' but '" + lvtname + "'.");
}
// In JustIce, the check for correct offsets into the code array is delayed to Pass 3a.
for (final LocalVariable localvariable : lvt.getLocalVariableTable()) {
checkIndex(lvt, localvariable.getNameIndex(), CONST_Utf8);
final String localname = ((ConstantUtf8) cp.getConstant(localvariable.getNameIndex())).getBytes();
if (!validJavaIdentifier(localname)) {
throw new ClassConstraintException("LocalVariableTable '" + tostring(lvt) + "' references a local variable by the name '"
+ localname + "' which is not a legal Java simple name.");
}
checkIndex(lvt, localvariable.getSignatureIndex(), CONST_Utf8);
final String localsig = ((ConstantUtf8) cp.getConstant(localvariable.getSignatureIndex())).getBytes(); // Local sig.(=descriptor)
Type t;
try {
t = Type.getType(localsig);
} catch (final ClassFormatException cfe) {
throw new ClassConstraintException("Illegal descriptor (==signature) '" + localsig + "' used by LocalVariable '"
+ tostring(localvariable) + "' referenced by '" + tostring(lvt) + "'.", cfe);
}
final int localindex = localvariable.getIndex();
if ((t == Type.LONG || t == Type.DOUBLE ? localindex + 1 : localindex) >= obj.getMaxLocals()) {
throw new ClassConstraintException(
"LocalVariableTable attribute '" + tostring(lvt) + "' references a LocalVariable '" + tostring(localvariable)
+ "' with an index that exceeds the surrounding Code attribute's max_locals value of '" + obj.getMaxLocals() + "'.");
}
try {
localVariablesInfos[methodNumber].add(localindex, localname, localvariable.getStartPC(), localvariable.getLength(), t);
} catch (final LocalVariableInfoInconsistentException lviie) {
throw new ClassConstraintException("Conflicting information in LocalVariableTable '" + tostring(lvt)
+ "' found in Code attribute '" + tostring(obj) + "' (method '" + tostring(m) + "'). " + lviie.getMessage(), lviie);
}
} // for all local variables localvariables[i] in the LocalVariableTable attribute atts[a] END
numOfLvtAttribs++;
if (!m.isStatic() && numOfLvtAttribs > obj.getMaxLocals()) {
throw new ClassConstraintException("Number of LocalVariableTable attributes of Code attribute '" + tostring(obj) + "' (method '"
+ tostring(m) + "') exceeds number of local variable slots '" + obj.getMaxLocals()
+ "' ('There may be at most one LocalVariableTable attribute per local variable in the Code attribute.').");
}
} // if atts[a] instanceof LocalVariableTable END
} // for all attributes atts[a] END
} catch (final ClassNotFoundException e) {
// FIXME: this might not be the best way to handle missing classes.
throw new AssertionViolatedException("Missing class: " + e, e);
}
}// visitCode(Code) END