Pass2Verifier xref

View Javadoc

    1   /*
    2    * Copyright  2000-2004 The Apache Software Foundation
    3    *
    4    *  Licensed under the Apache License, Version 2.0 (the "License");
    5    *  you may not use this file except in compliance with the License.
    6    *  You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   *  Unless required by applicable law or agreed to in writing, software
    11   *  distributed under the License is distributed on an "AS IS" BASIS,
    12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   *  See the License for the specific language governing permissions and
    14   *  limitations under the License.
    15   *
    16   */
    17  package org.apache.bcel.verifier.statics;
    18
    19
    20  import java.util.HashMap;
    21  import java.util.HashSet;
    22  import java.util.Locale;
    23  import java.util.Map;
    24  import java.util.Set;
    25  import org.apache.bcel.Constants;
    26  import org.apache.bcel.Repository;
    27  import org.apache.bcel.classfile.Attribute;
    28  import org.apache.bcel.classfile.ClassFormatException;
    29  import org.apache.bcel.classfile.Code;
    30  import org.apache.bcel.classfile.CodeException;
    31  import org.apache.bcel.classfile.Constant;
    32  import org.apache.bcel.classfile.ConstantClass;
    33  import org.apache.bcel.classfile.ConstantDouble;
    34  import org.apache.bcel.classfile.ConstantFieldref;
    35  import org.apache.bcel.classfile.ConstantFloat;
    36  import org.apache.bcel.classfile.ConstantInteger;
    37  import org.apache.bcel.classfile.ConstantInterfaceMethodref;
    38  import org.apache.bcel.classfile.ConstantLong;
    39  import org.apache.bcel.classfile.ConstantMethodref;
    40  import org.apache.bcel.classfile.ConstantNameAndType;
    41  import org.apache.bcel.classfile.ConstantPool;
    42  import org.apache.bcel.classfile.ConstantString;
    43  import org.apache.bcel.classfile.ConstantUtf8;
    44  import org.apache.bcel.classfile.ConstantValue;
    45  import org.apache.bcel.classfile.Deprecated;
    46  import org.apache.bcel.classfile.DescendingVisitor;
    47  import org.apache.bcel.classfile.EmptyVisitor;
    48  import org.apache.bcel.classfile.ExceptionTable;
    49  import org.apache.bcel.classfile.Field;
    50  import org.apache.bcel.classfile.InnerClass;
    51  import org.apache.bcel.classfile.InnerClasses;
    52  import org.apache.bcel.classfile.JavaClass;
    53  import org.apache.bcel.classfile.LineNumber;
    54  import org.apache.bcel.classfile.LineNumberTable;
    55  import org.apache.bcel.classfile.LocalVariable;
    56  import org.apache.bcel.classfile.LocalVariableTable;
    57  import org.apache.bcel.classfile.Method;
    58  import org.apache.bcel.classfile.Node;
    59  import org.apache.bcel.classfile.SourceFile;
    60  import org.apache.bcel.classfile.Synthetic;
    61  import org.apache.bcel.classfile.Unknown;
    62  import org.apache.bcel.classfile.Visitor;
    63  import org.apache.bcel.generic.ArrayType;
    64  import org.apache.bcel.generic.ObjectType;
    65  import org.apache.bcel.generic.Type;
    66  import org.apache.bcel.verifier.PassVerifier;
    67  import org.apache.bcel.verifier.VerificationResult;
    68  import org.apache.bcel.verifier.Verifier;
    69  import org.apache.bcel.verifier.VerifierFactory;
    70  import org.apache.bcel.verifier.exc.AssertionViolatedException;
    71  import org.apache.bcel.verifier.exc.ClassConstraintException;
    72  import org.apache.bcel.verifier.exc.LocalVariableInfoInconsistentException;
    73
    74  /***
    75   * This PassVerifier verifies a class file according to
    76   * pass 2 as described in The Java Virtual Machine
    77   * Specification, 2nd edition.
    78   * More detailed information is to be found at the do_verify()
    79   * method's documentation.
    80   *
    81   * @version $Id: Pass2Verifier.java 386056 2006-03-15 11:31:56Z tcurdt $
    82   * @author Enver Haase
    83   * @see #do_verify()
    84   */
    85  public final class Pass2Verifier extends PassVerifier implements Constants{
    86
    87  	/***
    88  	 * The LocalVariableInfo instances used by Pass3bVerifier.
    89  	 * localVariablesInfos[i] denotes the information for the
    90  	 * local variables of method number i in the
    91  	 * JavaClass this verifier operates on.
    92  	 */
    93  	private LocalVariablesInfo[] localVariablesInfos;
    94
    95  	/*** The Verifier that created this. */
    96  	private Verifier myOwner;
    97
    98  	/***
    99  	 * Should only be instantiated by a Verifier.
    100 	 *
    101 	 * @see Verifier
    102 	 */
    103 	public Pass2Verifier(Verifier owner){
    104 		myOwner = owner;
    105 	}
    106
    107 	/***
    108 	 * Returns a LocalVariablesInfo object containing information
    109 	 * about the usage of the local variables in the Code attribute
    110 	 * of the said method or <B>null</B> if the class file this
    111 	 * Pass2Verifier operates on could not be pass-2-verified correctly.
    112 	 * The method number method_nr is the method you get using
    113 	 * <B>Repository.lookupClass(myOwner.getClassname()).getMethods()[method_nr];</B>.
    114 	 * You should not add own information. Leave that to JustIce.
    115 	 */
    116 	public LocalVariablesInfo getLocalVariablesInfo(int method_nr){
    117 		if (this.verify() != VerificationResult.VR_OK) {
    118             return null; // It's cached, don't worry.
    119         }
    120 		if (method_nr < 0 || method_nr >= localVariablesInfos.length){
    121 			throw new AssertionViolatedException("Method number out of range.");
    122 		}
    123 		return localVariablesInfos[method_nr];
    124 	}
    125
    126 	/***
    127 	 * Pass 2 is the pass where static properties of the
    128 	 * class file are checked without looking into "Code"
    129 	 * arrays of methods.
    130 	 * This verification pass is usually invoked when
    131 	 * a class is resolved; and it may be possible that
    132 	 * this verification pass has to load in other classes
    133 	 * such as superclasses or implemented interfaces.
    134 	 * Therefore, Pass 1 is run on them.<BR>
    135 	 * Note that most referenced classes are <B>not</B> loaded
    136 	 * in for verification or for an existance check by this
    137 	 * pass; only the syntactical correctness of their names
    138 	 * and descriptors (a.k.a. signatures) is checked.<BR>
    139 	 * Very few checks that conceptually belong here
    140 	 * are delayed until pass 3a in JustIce. JustIce does
    141 	 * not only check for syntactical correctness but also
    142 	 * for semantical sanity - therefore it needs access to
    143 	 * the "Code" array of methods in a few cases. Please
    144 	 * see the pass 3a documentation, too.
    145 	 *
    146 	 * @see org.apache.bcel.verifier.statics.Pass3aVerifier
    147 	 */
    148 	public VerificationResult do_verify(){
    149 	    try {
    150 		VerificationResult vr1 = myOwner.doPass1();
    151 		if (vr1.equals(VerificationResult.VR_OK)){
    152
    153 			// For every method, we could have information about the local variables out of LocalVariableTable attributes of
    154 			// the Code attributes.
    155 			localVariablesInfos = new LocalVariablesInfo[Repository.lookupClass(myOwner.getClassName()).getMethods().length];
    156
    157 			VerificationResult vr = VerificationResult.VR_OK; // default.
    158 			try{
    159 				constant_pool_entries_satisfy_static_constraints();
    160 				field_and_method_refs_are_valid();
    161 				every_class_has_an_accessible_superclass();
    162 				final_methods_are_not_overridden();
    163 			}
    164 			catch (ClassConstraintException cce){
    165 				vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage());
    166 			}
    167 			return vr;
    168 		} else {
    169             return VerificationResult.VR_NOTYET;
    170         }
    171
    172 	    } catch (ClassNotFoundException e) {
    173 		// FIXME: this might not be the best way to handle missing classes.
    174 		throw new AssertionViolatedException("Missing class: " + e.toString());
    175 	    }
    176 	}
    177
    178 	/***
    179 	 * Ensures that every class has a super class and that
    180 	 * <B>final</B> classes are not subclassed.
    181 	 * This means, the class this Pass2Verifier operates
    182 	 * on has proper super classes (transitively) up to
    183 	 * java.lang.Object.
    184 	 * The reason for really loading (and Pass1-verifying)
    185 	 * all of those classes here is that we need them in
    186 	 * Pass2 anyway to verify no final methods are overridden
    187 	 * (that could be declared anywhere in the ancestor hierarchy).
    188 	 *
    189 	 * @throws ClassConstraintException otherwise.
    190 	 */
    191 	private void every_class_has_an_accessible_superclass(){
    192 	    try {
    193 		Set hs = new HashSet(); // save class names to detect circular inheritance
    194 		JavaClass jc = Repository.lookupClass(myOwner.getClassName());
    195 		int supidx = -1;
    196
    197 		while (supidx != 0){
    198 			supidx = jc.getSuperclassNameIndex();
    199
    200 			if (supidx == 0){
    201 				if (jc != Repository.lookupClass(Type.OBJECT.getClassName())){
    202 					throw new ClassConstraintException("Superclass of '"+jc.getClassName()+"' missing but not "+Type.OBJECT.getClassName()+" itself!");
    203 				}
    204 			}
    205 			else{
    206 				String supername = jc.getSuperclassName();
    207 				if (! hs.add(supername)){	// If supername already is in the list
    208 					throw new ClassConstraintException("Circular superclass hierarchy detected.");
    209 				}
    210 				Verifier v = VerifierFactory.getVerifier(supername);
    211 				VerificationResult vr = v.doPass1();
    212
    213 				if (vr != VerificationResult.VR_OK){
    214 					throw new ClassConstraintException("Could not load in ancestor class '"+supername+"'.");
    215 				}
    216 				jc = Repository.lookupClass(supername);
    217
    218 				if (jc.isFinal()){
    219 					throw new ClassConstraintException("Ancestor class '"+supername+"' has the FINAL access modifier and must therefore not be subclassed.");
    220 				}
    221 			}
    222 		}
    223
    224 	    } catch (ClassNotFoundException e) {
    225 		// FIXME: this might not be the best way to handle missing classes.
    226 		throw new AssertionViolatedException("Missing class: " + e.toString());
    227 	    }
    228 	}
    229
    230 	/***
    231 	 * Ensures that <B>final</B> methods are not overridden.
    232 	 * <B>Precondition to run this method:
    233 	 * constant_pool_entries_satisfy_static_constraints() and
    234 	 * every_class_has_an_accessible_superclass() have to be invoked before
    235 	 * (in that order).</B>
    236 	 *
    237 	 * @throws ClassConstraintException otherwise.
    238 	 * @see #constant_pool_entries_satisfy_static_constraints()
    239 	 * @see #every_class_has_an_accessible_superclass()
    240 	 */
    241 	private void final_methods_are_not_overridden(){
    242 	    try {
    243 		Map hashmap = new HashMap();
    244 		JavaClass jc = Repository.lookupClass(myOwner.getClassName());
    245
    246 		int supidx = -1;
    247 		while (supidx != 0){
    248 			supidx = jc.getSuperclassNameIndex();
    249
    250 			Method[] methods = jc.getMethods();
    251 			for (int i=0; i<methods.length; i++){
    252 				String name_and_sig = (methods[i].getName()+methods[i].getSignature());
    253
    254 				if (hashmap.containsKey(name_and_sig)){
    255 					if ( methods[i].isFinal() ){
    256 					  if (!(methods[i].isPrivate())) {
    257 						  throw new ClassConstraintException("Method '"+name_and_sig+"' in class '"+hashmap.get(name_and_sig)+"' overrides the final (not-overridable) definition in class '"+jc.getClassName()+"'.");
    258 					  }
    259 					  else{
    260 						  addMessage("Method '"+name_and_sig+"' in class '"+hashmap.get(name_and_sig)+"' overrides the final (not-overridable) definition in class '"+jc.getClassName()+"'. This is okay, as the original definition was private; however this constraint leverage was introduced by JLS 8.4.6 (not vmspec2) and the behaviour of the Sun verifiers.");
    261 					  }
    262 					}
    263 					else{
    264 						if (!methods[i].isStatic()){ // static methods don't inherit
    265 							hashmap.put(name_and_sig, jc.getClassName());
    266 						}
    267 					}
    268 				}
    269 				else{
    270 					if (!methods[i].isStatic()){ // static methods don't inherit
    271 						hashmap.put(name_and_sig, jc.getClassName());
    272 					}
    273 				}
    274 			}
    275
    276 			jc = Repository.lookupClass(jc.getSuperclassName());	// Well, for OBJECT this returns OBJECT so it works (could return anything but must not throw an Exception).
    277 		}
    278
    279 	    } catch (ClassNotFoundException e) {
    280 		// FIXME: this might not be the best way to handle missing classes.
    281 		throw new AssertionViolatedException("Missing class: " + e.toString());
    282 	    }
    283
    284 	}
    285
    286 	/***
    287 	 * Ensures that the constant pool entries satisfy the static constraints
    288 	 * as described in The Java Virtual Machine Specification, 2nd Edition.
    289 	 *
    290 	 * @throws ClassConstraintException otherwise.
    291 	 */
    292 	private void constant_pool_entries_satisfy_static_constraints(){
    293 	    try {
    294 		// Most of the consistency is handled internally by BCEL; here
    295 		// we only have to verify if the indices of the constants point
    296 		// to constants of the appropriate type and such.
    297 		JavaClass jc = Repository.lookupClass(myOwner.getClassName());
    298 		new CPESSC_Visitor(jc); // constructor implicitely traverses jc
    299
    300 	    } catch (ClassNotFoundException e) {
    301 		// FIXME: this might not be the best way to handle missing classes.
    302 		throw new AssertionViolatedException("Missing class: " + e.toString());
    303 	    }
    304 	}
    305
    306 	/***
    307 	 * A Visitor class that ensures the constant pool satisfies the static
    308 	 * constraints.
    309    * The visitXXX() methods throw ClassConstraintException instances otherwise.
    310    *
    311    * @see #constant_pool_entries_satisfy_static_constraints()
    312 	 */
    313 	private class CPESSC_Visitor extends org.apache.bcel.classfile.EmptyVisitor implements Visitor{
    314 		private Class CONST_Class;
    315 		/*
    316         private Class CONST_Fieldref;
    317 		private Class CONST_Methodref;
    318 		private Class CONST_InterfaceMethodref;
    319         */
    320 		private Class CONST_String;
    321 		private Class CONST_Integer;
    322 		private Class CONST_Float;
    323 		private Class CONST_Long;
    324 		private Class CONST_Double;
    325 		private Class CONST_NameAndType;
    326 		private Class CONST_Utf8;
    327
    328 		private final JavaClass jc;
    329 		private final ConstantPool cp; // ==jc.getConstantPool() -- only here to save typing work and computing power.
    330 		private final int cplen; // == cp.getLength() -- to save computing power.
    331 		private DescendingVisitor carrier;
    332
    333 		private Set field_names = new HashSet();
    334 		private Set field_names_and_desc = new HashSet();
    335 		private Set method_names_and_desc = new HashSet();
    336
    337 		private CPESSC_Visitor(JavaClass _jc){
    338 			jc = _jc;
    339 			cp = _jc.getConstantPool();
    340 			cplen = cp.getLength();
    341
    342 			CONST_Class = org.apache.bcel.classfile.ConstantClass.class;
    343 			/*
    344             CONST_Fieldref = org.apache.bcel.classfile.ConstantFieldref.class;
    345 			CONST_Methodref = org.apache.bcel.classfile.ConstantMethodref.class;
    346 			CONST_InterfaceMethodref = org.apache.bcel.classfile.ConstantInterfaceMethodref.class;
    347             */
    348 			CONST_String = org.apache.bcel.classfile.ConstantString.class;
    349 			CONST_Integer = org.apache.bcel.classfile.ConstantInteger.class;
    350 			CONST_Float = org.apache.bcel.classfile.ConstantFloat.class;
    351 			CONST_Long = org.apache.bcel.classfile.ConstantLong.class;
    352 			CONST_Double = org.apache.bcel.classfile.ConstantDouble.class;
    353 			CONST_NameAndType = org.apache.bcel.classfile.ConstantNameAndType.class;
    354 			CONST_Utf8 = org.apache.bcel.classfile.ConstantUtf8.class;
    355
    356 			carrier = new DescendingVisitor(_jc, this);
    357 			carrier.visit();
    358 		}
    359
    360 		private void checkIndex(Node referrer, int index, Class shouldbe){
    361 			if ((index < 0) || (index >= cplen)){
    362 				throw new ClassConstraintException("Invalid index '"+index+"' used by '"+tostring(referrer)+"'.");
    363 			}
    364 			Constant c = cp.getConstant(index);
    365 			if (! shouldbe.isInstance(c)){
    366 				/* String isnot = shouldbe.toString().substring(shouldbe.toString().lastIndexOf(".")+1); //Cut all before last "." */
    367 				throw new ClassCastException("Illegal constant '"+tostring(c)+"' at index '"+index+"'. '"+tostring(referrer)+"' expects a '"+shouldbe+"'.");
    368 			}
    369 		}
    370 		///////////////////////////////////////
    371 		// ClassFile structure (vmspec2 4.1) //
    372 		///////////////////////////////////////
    373 		public void visitJavaClass(JavaClass obj){
    374 			Attribute[] atts = obj.getAttributes();
    375 			boolean foundSourceFile = false;
    376 			boolean foundInnerClasses = false;
    377
    378 			// Is there an InnerClass referenced?
    379 			// This is a costly check; existing verifiers don't do it!
    380 			boolean hasInnerClass = new InnerClassDetector(jc).innerClassReferenced();
    381
    382 			for (int i=0; i<atts.length; i++){
    383 				if ((! (atts[i] instanceof SourceFile)) &&
    384 				    (! (atts[i] instanceof Deprecated))     &&
    385 				    (! (atts[i] instanceof InnerClasses)) &&
    386 				    (! (atts[i] instanceof Synthetic))){
    387 					addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of the ClassFile structure '"+tostring(obj)+"' is unknown and will therefore be ignored.");
    388 				}
    389
    390 				if (atts[i] instanceof SourceFile){
    391 					if (foundSourceFile == false) {
    392                         foundSourceFile = true;
    393                     } else {
    394                         throw new ClassConstraintException("A ClassFile structure (like '"+tostring(obj)+"') may have no more than one SourceFile attribute."); //vmspec2 4.7.7
    395                     }
    396 				}
    397
    398 				if (atts[i] instanceof InnerClasses){
    399 					if (foundInnerClasses == false) {
    400                         foundInnerClasses = true;
    401                     } else{
    402 						if (hasInnerClass){
    403 							throw new ClassConstraintException("A Classfile structure (like '"+tostring(obj)+"') must have exactly one InnerClasses attribute if at least one Inner Class is referenced (which is the case). More than one InnerClasses attribute was found.");
    404 						}
    405 					}
    406 					if (!hasInnerClass){
    407 						addMessage("No referenced Inner Class found, but InnerClasses attribute '"+tostring(atts[i])+"' found. Strongly suggest removal of that attribute.");
    408 					}
    409 				}
    410
    411 			}
    412 			if (hasInnerClass && !foundInnerClasses){
    413 				//throw new ClassConstraintException("A Classfile structure (like '"+tostring(obj)+"') must have exactly one InnerClasses attribute if at least one Inner Class is referenced (which is the case). No InnerClasses attribute was found.");
    414 				//vmspec2, page 125 says it would be a constraint: but existing verifiers
    415 				//don't check it and javac doesn't satisfy it when it comes to anonymous
    416 				//inner classes
    417 				addMessage("A Classfile structure (like '"+tostring(obj)+"') must have exactly one InnerClasses attribute if at least one Inner Class is referenced (which is the case). No InnerClasses attribute was found.");
    418 			}
    419 		}
    420 		/////////////////////////////
    421 		// CONSTANTS (vmspec2 4.4) //
    422 		/////////////////////////////
    423 		public void visitConstantClass(ConstantClass obj){
    424 			if (obj.getTag() != Constants.CONSTANT_Class){
    425 				throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    426 			}
    427 			checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    428
    429 		}
    430 		public void visitConstantFieldref(ConstantFieldref obj){
    431 			if (obj.getTag() != Constants.CONSTANT_Fieldref){
    432 				throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    433 			}
    434 			checkIndex(obj, obj.getClassIndex(), CONST_Class);
    435 			checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
    436 		}
    437 		public void visitConstantMethodref(ConstantMethodref obj){
    438 			if (obj.getTag() != Constants.CONSTANT_Methodref){
    439 				throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    440 			}
    441 			checkIndex(obj, obj.getClassIndex(), CONST_Class);
    442 			checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
    443 		}
    444 		public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj){
    445 			if (obj.getTag() != Constants.CONSTANT_InterfaceMethodref){
    446 				throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    447 			}
    448 			checkIndex(obj, obj.getClassIndex(), CONST_Class);
    449 			checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
    450 		}
    451 		public void visitConstantString(ConstantString obj){
    452 			if (obj.getTag() != Constants.CONSTANT_String){
    453 				throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    454 			}
    455 			checkIndex(obj, obj.getStringIndex(), CONST_Utf8);
    456 		}
    457 		public void visitConstantInteger(ConstantInteger obj){
    458 			if (obj.getTag() != Constants.CONSTANT_Integer){
    459 				throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    460 			}
    461 			// no indices to check
    462 		}
    463 		public void visitConstantFloat(ConstantFloat obj){
    464 			if (obj.getTag() != Constants.CONSTANT_Float){
    465 				throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    466 			}
    467 			//no indices to check
    468 		}
    469 		public void visitConstantLong(ConstantLong obj){
    470 			if (obj.getTag() != Constants.CONSTANT_Long){
    471 				throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    472 			}
    473 			//no indices to check
    474 		}
    475 		public void visitConstantDouble(ConstantDouble obj){
    476 			if (obj.getTag() != Constants.CONSTANT_Double){
    477 				throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    478 			}
    479 			//no indices to check
    480 		}
    481 		public void visitConstantNameAndType(ConstantNameAndType obj){
    482 			if (obj.getTag() != Constants.CONSTANT_NameAndType){
    483 				throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    484 			}
    485 			checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    486 			//checkIndex(obj, obj.getDescriptorIndex(), CONST_Utf8); //inconsistently named in BCEL, see below.
    487 			checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
    488 		}
    489 		public void visitConstantUtf8(ConstantUtf8 obj){
    490 			if (obj.getTag() != Constants.CONSTANT_Utf8){
    491 				throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    492 			}
    493 			//no indices to check
    494 		}
    495 		//////////////////////////
    496 		// FIELDS (vmspec2 4.5) //
    497 		//////////////////////////
    498 		public void visitField(Field obj){
    499
    500 			if (jc.isClass()){
    501 				int maxone=0;
    502 				if (obj.isPrivate()) {
    503                     maxone++;
    504                 }
    505 				if (obj.isProtected()) {
    506                     maxone++;
    507                 }
    508 				if (obj.isPublic()) {
    509                     maxone++;
    510                 }
    511 				if (maxone > 1){
    512 					throw new ClassConstraintException("Field '"+tostring(obj)+"' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set.");
    513 				}
    514
    515 				if (obj.isFinal() && obj.isVolatile()){
    516 					throw new ClassConstraintException("Field '"+tostring(obj)+"' must only have at most one of its ACC_FINAL, ACC_VOLATILE modifiers set.");
    517 				}
    518 			}
    519 			else{ // isInterface!
    520 				if (!obj.isPublic()){
    521 					throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_PUBLIC modifier set but hasn't!");
    522 				}
    523 				if (!obj.isStatic()){
    524 					throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_STATIC modifier set but hasn't!");
    525 				}
    526 				if (!obj.isFinal()){
    527 					throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_FINAL modifier set but hasn't!");
    528 				}
    529 			}
    530
    531 			if ((obj.getAccessFlags() & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_VOLATILE|ACC_TRANSIENT)) > 0){
    532 				addMessage("Field '"+tostring(obj)+"' has access flag(s) other than ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_VOLATILE, ACC_TRANSIENT set (ignored).");
    533 			}
    534
    535 			checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    536
    537 			String name = obj.getName();
    538 			if (! validFieldName(name)){
    539 				throw new ClassConstraintException("Field '"+tostring(obj)+"' has illegal name '"+obj.getName()+"'.");
    540 			}
    541
    542 			// A descriptor is often named signature in BCEL
    543 			checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
    544
    545 			String sig  = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
    546
    547 			try{
    548 				Type.getType(sig);  /* Don't need the return value */
    549 			}
    550 			catch (ClassFormatException cfe){
    551                 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
    552 			}
    553
    554 			String nameanddesc = (name+sig);
    555 			if (field_names_and_desc.contains(nameanddesc)){
    556 				throw new ClassConstraintException("No two fields (like '"+tostring(obj)+"') are allowed have same names and descriptors!");
    557 			}
    558 			if (field_names.contains(name)){
    559 				addMessage("More than one field of name '"+name+"' detected (but with different type descriptors). This is very unusual.");
    560 			}
    561 			field_names_and_desc.add(nameanddesc);
    562 			field_names.add(name);
    563
    564 			Attribute[] atts = obj.getAttributes();
    565 			for (int i=0; i<atts.length; i++){
    566 				if ((! (atts[i] instanceof ConstantValue)) &&
    567 				    (! (atts[i] instanceof Synthetic))     &&
    568 				    (! (atts[i] instanceof Deprecated))){
    569 					addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Field '"+tostring(obj)+"' is unknown and will therefore be ignored.");
    570 				}
    571 				if  (! (atts[i] instanceof ConstantValue)){
    572 					addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Field '"+tostring(obj)+"' is not a ConstantValue and is therefore only of use for debuggers and such.");
    573 				}
    574 			}
    575 		}
    576 		///////////////////////////
    577 		// METHODS (vmspec2 4.6) //
    578 		///////////////////////////
    579 		public void visitMethod(Method obj){
    580
    581 			checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    582
    583 			String name = obj.getName();
    584 			if (! validMethodName(name, true)){
    585 				throw new ClassConstraintException("Method '"+tostring(obj)+"' has illegal name '"+name+"'.");
    586 			}
    587
    588 			// A descriptor is often named signature in BCEL
    589 			checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
    590
    591 			String sig  = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes(); // Method's signature(=descriptor)
    592
    593 			Type t;
    594 			Type[] ts; // needed below the try block.
    595 			try{
    596 				t  = Type.getReturnType(sig);
    597 				ts = Type.getArgumentTypes(sig);
    598 			}
    599 			catch (ClassFormatException cfe){
    600                 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by Method '"+tostring(obj)+"'.");
    601 			}
    602
    603 			// Check if referenced objects exist.
    604 			Type act = t;
    605 			if (act instanceof ArrayType) {
    606                 act = ((ArrayType) act).getBasicType();
    607             }
    608 			if (act instanceof ObjectType){
    609 				Verifier v = VerifierFactory.getVerifier( ((ObjectType) act).getClassName() );
    610 				VerificationResult vr = v.doPass1();
    611 				if (vr != VerificationResult.VR_OK) {
    612 					throw new ClassConstraintException("Method '"+tostring(obj)+"' has a return type that does not pass verification pass 1: '"+vr+"'.");
    613 				}
    614 			}
    615
    616 			for (int i=0; i<ts.length; i++){
    617 				act = ts[i];
    618 				if (act instanceof ArrayType) {
    619                     act = ((ArrayType) act).getBasicType();
    620                 }
    621 				if (act instanceof ObjectType){
    622 					Verifier v = VerifierFactory.getVerifier( ((ObjectType) act).getClassName() );
    623 					VerificationResult vr = v.doPass1();
    624 					if (vr != VerificationResult.VR_OK) {
    625 						throw new ClassConstraintException("Method '"+tostring(obj)+"' has an argument type that does not pass verification pass 1: '"+vr+"'.");
    626 					}
    627 				}
    628 			}
    629
    630 			// Nearly forgot this! Funny return values are allowed, but a non-empty arguments list makes a different method out of it!
    631 			if (name.equals(STATIC_INITIALIZER_NAME) && (ts.length != 0)){
    632 				throw new ClassConstraintException("Method '"+tostring(obj)+"' has illegal name '"+name+"'. It's name resembles the class or interface initialization method which it isn't because of its arguments (==descriptor).");
    633 			}
    634
    635 			if (jc.isClass()){
    636 				int maxone=0;
    637 				if (obj.isPrivate()) {
    638                     maxone++;
    639                 }
    640 				if (obj.isProtected()) {
    641                     maxone++;
    642                 }
    643 				if (obj.isPublic()) {
    644                     maxone++;
    645                 }
    646 				if (maxone > 1){
    647 					throw new ClassConstraintException("Method '"+tostring(obj)+"' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set.");
    648 				}
    649
    650 				if (obj.isAbstract()){
    651 					if (obj.isFinal()) {
    652                         throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_FINAL modifier set.");
    653                     }
    654 					if (obj.isNative()) {
    655                         throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_NATIVE modifier set.");
    656                     }
    657 					if (obj.isPrivate()) {
    658                         throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_PRIVATE modifier set.");
    659                     }
    660 					if (obj.isStatic()) {
    661                         throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_STATIC modifier set.");
    662                     }
    663 					if (obj.isStrictfp()) {
    664                         throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_STRICT modifier set.");
    665                     }
    666 					if (obj.isSynchronized()) {
    667                         throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_SYNCHRONIZED modifier set.");
    668                     }
    669 				}
    670 			}
    671 			else{ // isInterface!
    672 				if (!name.equals(STATIC_INITIALIZER_NAME)){//vmspec2, p.116, 2nd paragraph
    673 					if (!obj.isPublic()){
    674 						throw new ClassConstraintException("Interface method '"+tostring(obj)+"' must have the ACC_PUBLIC modifier set but hasn't!");
    675 					}
    676 					if (!obj.isAbstract()){
    677 						throw new ClassConstraintException("Interface method '"+tostring(obj)+"' must have the ACC_STATIC modifier set but hasn't!");
    678 					}
    679 					if (	obj.isPrivate() ||
    680 								obj.isProtected() ||
    681 								obj.isStatic() ||
    682 								obj.isFinal() ||
    683 								obj.isSynchronized() ||
    684 								obj.isNative() ||
    685 								obj.isStrictfp() ){
    686 						throw new ClassConstraintException("Interface method '"+tostring(obj)+"' must not have any of the ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT, ACC_STRICT modifiers set.");
    687 					}
    688 				}
    689 			}
    690
    691 			// A specific instance initialization method... (vmspec2,Page 116).
    692 			if (name.equals(CONSTRUCTOR_NAME)){
    693 				//..may have at most one of ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC set: is checked above.
    694 				//..may also have ACC_STRICT set, but none of the other flags in table 4.5 (vmspec2, page 115)
    695 				if (	obj.isStatic() ||
    696 							obj.isFinal() ||
    697 							obj.isSynchronized() ||
    698 							obj.isNative() ||
    699 							obj.isAbstract() ){
    700 					throw new ClassConstraintException("Instance initialization method '"+tostring(obj)+"' must not have any of the ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT modifiers set.");
    701 				}
    702 			}
    703
    704 			// Class and interface initialization methods...
    705 			if (name.equals(STATIC_INITIALIZER_NAME)){
    706 				if ((obj.getAccessFlags() & (~ACC_STRICT)) > 0){
    707 					addMessage("Class or interface initialization method '"+tostring(obj)+"' has superfluous access modifier(s) set: everything but ACC_STRICT is ignored.");
    708 				}
    709 				if (obj.isAbstract()){
    710 					throw new ClassConstraintException("Class or interface initialization method '"+tostring(obj)+"' must not be abstract. This contradicts the Java Language Specification, Second Edition (which omits this constraint) but is common practice of existing verifiers.");
    711 				}
    712 			}
    713
    714 			if ((obj.getAccessFlags() & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) > 0){
    715 				addMessage("Method '"+tostring(obj)+"' has access flag(s) other than ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT, ACC_STRICT set (ignored).");
    716 			}
    717
    718 			String nameanddesc = (name+sig);
    719 			if (method_names_and_desc.contains(nameanddesc)){
    720 				throw new ClassConstraintException("No two methods (like '"+tostring(obj)+"') are allowed have same names and desciptors!");
    721 			}
    722 			method_names_and_desc.add(nameanddesc);
    723
    724 			Attribute[] atts = obj.getAttributes();
    725 			int num_code_atts = 0;
    726 			for (int i=0; i<atts.length; i++){
    727 				if ((! (atts[i] instanceof Code)) &&
    728 				    (! (atts[i] instanceof ExceptionTable))     &&
    729 				    (! (atts[i] instanceof Synthetic)) &&
    730 				    (! (atts[i] instanceof Deprecated))){
    731 					addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Method '"+tostring(obj)+"' is unknown and will therefore be ignored.");
    732 				}
    733 				if ((! (atts[i] instanceof Code)) &&
    734 						(! (atts[i] instanceof ExceptionTable))){
    735 					addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Method '"+tostring(obj)+"' is neither Code nor Exceptions and is therefore only of use for debuggers and such.");
    736 				}
    737 				if ((atts[i] instanceof Code) && (obj.isNative() || obj.isAbstract())){
    738 					throw new ClassConstraintException("Native or abstract methods like '"+tostring(obj)+"' must not have a Code attribute like '"+tostring(atts[i])+"'."); //vmspec2 page120, 4.7.3
    739 				}
    740 				if (atts[i] instanceof Code) {
    741                     num_code_atts++;
    742                 }
    743 			}
    744 			if ( !obj.isNative() && !obj.isAbstract() && num_code_atts != 1){
    745 				throw new ClassConstraintException("Non-native, non-abstract methods like '"+tostring(obj)+"' must have exactly one Code attribute (found: "+num_code_atts+").");
    746 			}
    747 		}
    748 		///////////////////////////////////////////////////////
    749 		// ClassFile-structure-ATTRIBUTES (vmspec2 4.1, 4.7) //
    750 		///////////////////////////////////////////////////////
    751 		public void visitSourceFile(SourceFile obj){//vmspec2 4.7.7
    752
    753 			// zero or one SourceFile attr per ClassFile: see visitJavaClass()
    754
    755 			checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    756
    757 			String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    758 			if (! name.equals("SourceFile")){
    759 				throw new ClassConstraintException("The SourceFile attribute '"+tostring(obj)+"' is not correctly named 'SourceFile' but '"+name+"'.");
    760 			}
    761
    762 			checkIndex(obj, obj.getSourceFileIndex(), CONST_Utf8);
    763
    764 			String sourcefilename = ((ConstantUtf8) cp.getConstant(obj.getSourceFileIndex())).getBytes(); //==obj.getSourceFileName() ?
    765 			String sourcefilenamelc = sourcefilename.toLowerCase(Locale.ENGLISH);
    766
    767 			if (	(sourcefilename.indexOf('/') != -1) ||
    768 						(sourcefilename.indexOf('//') != -1) ||
    769 						(sourcefilename.indexOf(':') != -1) ||
    770 						(sourcefilenamelc.lastIndexOf(".java") == -1)	){
    771 				addMessage("SourceFile attribute '"+tostring(obj)+"' has a funny name: remember not to confuse certain parsers working on javap's output. Also, this name ('"+sourcefilename+"') is considered an unqualified (simple) file name only.");
    772 			}
    773 		}
    774 		public void visitDeprecated(Deprecated obj){//vmspec2 4.7.10
    775 			checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    776
    777 			String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    778 			if (! name.equals("Deprecated")){
    779 				throw new ClassConstraintException("The Deprecated attribute '"+tostring(obj)+"' is not correctly named 'Deprecated' but '"+name+"'.");
    780 			}
    781 		}
    782 		public void visitSynthetic(Synthetic obj){//vmspec2 4.7.6
    783 			checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    784 			String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    785 			if (! name.equals("Synthetic")){
    786 				throw new ClassConstraintException("The Synthetic attribute '"+tostring(obj)+"' is not correctly named 'Synthetic' but '"+name+"'.");
    787 			}
    788 		}
    789 		public void visitInnerClasses(InnerClasses obj){//vmspec2 4.7.5
    790
    791 			// exactly one InnerClasses attr per ClassFile if some inner class is refernced: see visitJavaClass()
    792
    793 			checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    794
    795 			String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    796 			if (! name.equals("InnerClasses")){
    797 				throw new ClassConstraintException("The InnerClasses attribute '"+tostring(obj)+"' is not correctly named 'InnerClasses' but '"+name+"'.");
    798 			}
    799
    800 			InnerClass[] ics = obj.getInnerClasses();
    801
    802 			for (int i=0; i<ics.length; i++){
    803 				checkIndex(obj, ics[i].getInnerClassIndex(), CONST_Class);
    804 				int outer_idx = ics[i].getOuterClassIndex();
    805 				if (outer_idx != 0){
    806 					checkIndex(obj, outer_idx, CONST_Class);
    807 				}
    808 				int innername_idx = ics[i].getInnerNameIndex();
    809 				if (innername_idx != 0){
    810 					checkIndex(obj, innername_idx, CONST_Utf8);
    811 				}
    812 				int acc = ics[i].getInnerAccessFlags();
    813 				acc = acc & (~ (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT));
    814 				if (acc != 0){
    815 					addMessage("Unknown access flag for inner class '"+tostring(ics[i])+"' set (InnerClasses attribute '"+tostring(obj)+"').");
    816 				}
    817 			}
    818 			// Semantical consistency is not yet checked by Sun, see vmspec2 4.7.5.
    819 			// [marked TODO in JustIce]
    820 		}
    821 		////////////////////////////////////////////////////////
    822 		// field_info-structure-ATTRIBUTES (vmspec2 4.5, 4.7) //
    823 		////////////////////////////////////////////////////////
    824 		public void visitConstantValue(ConstantValue obj){//vmspec2 4.7.2
    825 			// Despite its name, this really is an Attribute,
    826 			// not a constant!
    827 			checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    828
    829 			String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    830 			if (! name.equals("ConstantValue")){
    831 				throw new ClassConstraintException("The ConstantValue attribute '"+tostring(obj)+"' is not correctly named 'ConstantValue' but '"+name+"'.");
    832 			}
    833
    834 			Object pred = carrier.predecessor();
    835 			if (pred instanceof Field){ //ConstantValue attributes are quite senseless if the predecessor is not a field.
    836 				Field f = (Field) pred;
    837 				// Field constraints have been checked before -- so we are safe using their type information.
    838 				Type field_type = Type.getType(((ConstantUtf8) (cp.getConstant(f.getSignatureIndex()))).getBytes());
    839
    840 				int index = obj.getConstantValueIndex();
    841 				if ((index < 0) || (index >= cplen)){
    842 					throw new ClassConstraintException("Invalid index '"+index+"' used by '"+tostring(obj)+"'.");
    843 				}
    844 				Constant c = cp.getConstant(index);
    845
    846 				if (CONST_Long.isInstance(c) && field_type.equals(Type.LONG)){
    847 					return;
    848 				}
    849 				if (CONST_Float.isInstance(c) && field_type.equals(Type.FLOAT)){
    850 					return;
    851 				}
    852 				if (CONST_Double.isInstance(c) && field_type.equals(Type.DOUBLE)){
    853 					return;
    854 				}
    855 				if (CONST_Integer.isInstance(c) && (field_type.equals(Type.INT) || field_type.equals(Type.SHORT) || field_type.equals(Type.CHAR) || field_type.equals(Type.BYTE) || field_type.equals(Type.BOOLEAN))){
    856 					return;
    857 				}
    858 				if (CONST_String.isInstance(c) && field_type.equals(Type.STRING)){
    859 					return;
    860 				}
    861
    862 				throw new ClassConstraintException("Illegal type of ConstantValue '"+obj+"' embedding Constant '"+c+"'. It is referenced by field '"+tostring(f)+"' expecting a different type: '"+field_type+"'.");
    863 			}
    864 		}
    865 		// SYNTHETIC: see above
    866 		// DEPRECATED: see above
    867 		/////////////////////////////////////////////////////////
    868 		// method_info-structure-ATTRIBUTES (vmspec2 4.6, 4.7) //
    869 		/////////////////////////////////////////////////////////
    870 		public void visitCode(Code obj){//vmspec2 4.7.3
    871 		    try {
    872 			// No code attribute allowed for native or abstract methods: see visitMethod(Method).
    873 			// Code array constraints are checked in Pass3 (3a and 3b).
    874
    875 			checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    876
    877 			String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    878 			if (! name.equals("Code")){
    879 				throw new ClassConstraintException("The Code attribute '"+tostring(obj)+"' is not correctly named 'Code' but '"+name+"'.");
    880 			}
    881
    882 			Method m = null; // satisfy compiler
    883 			if (!(carrier.predecessor() instanceof Method)){
    884 				addMessage("Code attribute '"+tostring(obj)+"' is not declared in a method_info structure but in '"+carrier.predecessor()+"'. Ignored.");
    885 				return;
    886 			}
    887 			else{
    888 				m = (Method) carrier.predecessor();	// we can assume this method was visited before;
    889 																						// i.e. the data consistency was verified.
    890 			}
    891
    892 			if (obj.getCode().length == 0){
    893 				throw new ClassConstraintException("Code array of Code attribute '"+tostring(obj)+"' (method '"+m+"') must not be empty.");
    894 			}
    895
    896 			//In JustIce, the check for correct offsets into the code array is delayed to Pass 3a.
    897 			CodeException[] exc_table = obj.getExceptionTable();
    898 			for (int i=0; i<exc_table.length; i++){
    899 				int exc_index = exc_table[i].getCatchType();
    900 				if (exc_index != 0){ // if 0, it catches all Throwables
    901 					checkIndex(obj, exc_index, CONST_Class);
    902 					ConstantClass cc = (ConstantClass) (cp.getConstant(exc_index));
    903 					checkIndex(cc, cc.getNameIndex(), CONST_Utf8); // cannot be sure this ConstantClass has already been visited (checked)!
    904 					String cname = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes().replace('/','.');
    905
    906 					Verifier v = VerifierFactory.getVerifier(cname);
    907 					VerificationResult vr = v.doPass1();
    908
    909 					if (vr != VerificationResult.VR_OK){
    910 						throw new ClassConstraintException("Code attribute '"+tostring(obj)+"' (method '"+m+"') has an exception_table entry '"+tostring(exc_table[i])+"' that references '"+cname+"' as an Exception but it does not pass verification pass 1: "+vr);
    911 					}
    912 					else{
    913 						// We cannot safely trust any other "instanceof" mechanism. We need to transitively verify
    914 						// the ancestor hierarchy.
    915 						JavaClass e = Repository.lookupClass(cname);
    916 						JavaClass t = Repository.lookupClass(Type.THROWABLE.getClassName());
    917 						JavaClass o = Repository.lookupClass(Type.OBJECT.getClassName());
    918 						while (e != o){
    919 							if (e == t) {
    920                                 break; // It's a subclass of Throwable, OKAY, leave.
    921                             }
    922
    923 							v = VerifierFactory.getVerifier(e.getSuperclassName());
    924 							vr = v.doPass1();
    925 							if (vr != VerificationResult.VR_OK){
    926 								throw new ClassConstraintException("Code attribute '"+tostring(obj)+"' (method '"+m+"') has an exception_table entry '"+tostring(exc_table[i])+"' that references '"+cname+"' as an Exception but '"+e.getSuperclassName()+"' in the ancestor hierachy does not pass verification pass 1: "+vr);
    927 							}
    928 							else{
    929 								e = Repository.lookupClass(e.getSuperclassName());
    930 							}
    931 						}
    932 						if (e != t) {
    933                             throw new ClassConstraintException("Code attribute '"+tostring(obj)+"' (method '"+m+"') has an exception_table entry '"+tostring(exc_table[i])+"' that references '"+cname+"' as an Exception but it is not a subclass of '"+t.getClassName()+"'.");
    934                         }
    935 					}
    936 				}
    937 			}
    938
    939 			// Create object for local variables information
    940 			// This is highly unelegant due to usage of the Visitor pattern.
    941 			// TODO: rework it.
    942 			int method_number = -1;
    943 			Method[] ms = Repository.lookupClass(myOwner.getClassName()).getMethods();
    944 			for (int mn=0; mn<ms.length; mn++){
    945 				if (m == ms[mn]){
    946 					method_number = mn;
    947 					break;
    948 				}
    949 			}
    950 			if (method_number < 0){ // Mmmmh. Can we be sure BCEL does not sometimes instantiate new objects?
    951 				throw new AssertionViolatedException("Could not find a known BCEL Method object in the corresponding BCEL JavaClass object.");
    952 			}
    953 			localVariablesInfos[method_number] = new LocalVariablesInfo(obj.getMaxLocals());
    954
    955 			int num_of_lvt_attribs = 0;
    956 			// Now iterate through the attributes the Code attribute has.
    957 			Attribute[] atts = obj.getAttributes();
    958 			for (int a=0; a<atts.length; a++){
    959 				if ((! (atts[a] instanceof LineNumberTable)) &&
    960 				    (! (atts[a] instanceof LocalVariableTable))){
    961 					addMessage("Attribute '"+tostring(atts[a])+"' as an attribute of Code attribute '"+tostring(obj)+"' (method '"+m+"') is unknown and will therefore be ignored.");
    962 				}
    963 				else{// LineNumberTable or LocalVariableTable
    964 					addMessage("Attribute '"+tostring(atts[a])+"' as an attribute of Code attribute '"+tostring(obj)+"' (method '"+m+"') will effectively be ignored and is only useful for debuggers and such.");
    965 				}
    966
    967 				//LocalVariableTable check (partially delayed to Pass3a).
    968 				//Here because its easier to collect the information of the
    969 				//(possibly more than one) LocalVariableTables belonging to
    970 				//one certain Code attribute.
    971 				if (atts[a] instanceof LocalVariableTable){ // checks conforming to vmspec2 4.7.9
    972
    973 					LocalVariableTable lvt = (LocalVariableTable) atts[a];
    974
    975 					checkIndex(lvt, lvt.getNameIndex(), CONST_Utf8);
    976
    977 					String lvtname = ((ConstantUtf8) cp.getConstant(lvt.getNameIndex())).getBytes();
    978 					if (! lvtname.equals("LocalVariableTable")){
    979 						throw new ClassConstraintException("The LocalVariableTable attribute '"+tostring(lvt)+"' is not correctly named 'LocalVariableTable' but '"+lvtname+"'.");
    980 					}
    981
    982 					Code code = obj;
    983
    984 					//In JustIce, the check for correct offsets into the code array is delayed to Pass 3a.
    985 					LocalVariable[] localvariables = lvt.getLocalVariableTable();
    986
    987 					for (int i=0; i<localvariables.length; i++){
    988 						checkIndex(lvt, localvariables[i].getNameIndex(), CONST_Utf8);
    989 						String localname = ((ConstantUtf8) cp.getConstant(localvariables[i].getNameIndex())).getBytes();
    990 						if (!validJavaIdentifier(localname)){
    991 							throw new ClassConstraintException("LocalVariableTable '"+tostring(lvt)+"' references a local variable by the name '"+localname+"' which is not a legal Java simple name.");
    992 						}
    993
    994 						checkIndex(lvt, localvariables[i].getSignatureIndex(), CONST_Utf8);
    995 						String localsig  = ((ConstantUtf8) (cp.getConstant(localvariables[i].getSignatureIndex()))).getBytes(); // Local signature(=descriptor)
    996 						Type t;
    997 						try{
    998 							t = Type.getType(localsig);
    999 						}
    1000 						catch (ClassFormatException cfe){
    1001 							throw new ClassConstraintException("Illegal descriptor (==signature) '"+localsig+"' used by LocalVariable '"+tostring(localvariables[i])+"' referenced by '"+tostring(lvt)+"'.");
    1002 						}
    1003 						int localindex = localvariables[i].getIndex();
    1004 						if ( ( (t==Type.LONG || t==Type.DOUBLE)? localindex+1:localindex) >= code.getMaxLocals()){
    1005 							throw new ClassConstraintException("LocalVariableTable attribute '"+tostring(lvt)+"' references a LocalVariable '"+tostring(localvariables[i])+"' with an index that exceeds the surrounding Code attribute's max_locals value of '"+code.getMaxLocals()+"'.");
    1006 						}
    1007
    1008 						try{
    1009 							localVariablesInfos[method_number].add(localindex, localname, localvariables[i].getStartPC(), localvariables[i].getLength(), t);
    1010 						}
    1011 						catch(LocalVariableInfoInconsistentException lviie){
    1012 							throw new ClassConstraintException("Conflicting information in LocalVariableTable '"+tostring(lvt)+"' found in Code attribute '"+tostring(obj)+"' (method '"+tostring(m)+"'). "+lviie.getMessage());
    1013 						}
    1014 					}// for all local variables localvariables[i] in the LocalVariableTable attribute atts[a] END
    1015
    1016 					num_of_lvt_attribs++;
    1017 					if (num_of_lvt_attribs > obj.getMaxLocals()){
    1018 						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 no more than one LocalVariableTable attribute per local variable in the Code attribute.').");
    1019 					}
    1020 				}// if atts[a] instanceof LocalVariableTable END
    1021 			}// for all attributes atts[a] END
    1022
    1023 		    } catch (ClassNotFoundException e) {
    1024 			// FIXME: this might not be the best way to handle missing classes.
    1025 			throw new AssertionViolatedException("Missing class: " + e.toString());
    1026 		    }
    1027
    1028 		}// visitCode(Code) END
    1029
    1030 		public void visitExceptionTable(ExceptionTable obj){//vmspec2 4.7.4
    1031 		    try {
    1032 			// incorrectly named, it's the Exceptions attribute (vmspec2 4.7.4)
    1033 			checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    1034
    1035 			String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    1036 			if (! name.equals("Exceptions")){
    1037 				throw new ClassConstraintException("The Exceptions attribute '"+tostring(obj)+"' is not correctly named 'Exceptions' but '"+name+"'.");
    1038 			}
    1039
    1040 			int[] exc_indices = obj.getExceptionIndexTable();
    1041
    1042 			for (int i=0; i<exc_indices.length; i++){
    1043 				checkIndex(obj, exc_indices[i], CONST_Class);
    1044
    1045 				ConstantClass cc = (ConstantClass) (cp.getConstant(exc_indices[i]));
    1046 				checkIndex(cc, cc.getNameIndex(), CONST_Utf8); // cannot be sure this ConstantClass has already been visited (checked)!
    1047 				String cname = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes().replace('/','.'); //convert internal notation on-the-fly to external notation
    1048
    1049 				Verifier v = VerifierFactory.getVerifier(cname);
    1050 				VerificationResult vr = v.doPass1();
    1051
    1052 				if (vr != VerificationResult.VR_OK){
    1053 					throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+"' as an Exception but it does not pass verification pass 1: "+vr);
    1054 				}
    1055 				else{
    1056 					// We cannot safely trust any other "instanceof" mechanism. We need to transitively verify
    1057 					// the ancestor hierarchy.
    1058 					JavaClass e = Repository.lookupClass(cname);
    1059 					JavaClass t = Repository.lookupClass(Type.THROWABLE.getClassName());
    1060 					JavaClass o = Repository.lookupClass(Type.OBJECT.getClassName());
    1061 					while (e != o){
    1062 						if (e == t) {
    1063                             break; // It's a subclass of Throwable, OKAY, leave.
    1064                         }
    1065
    1066 						v = VerifierFactory.getVerifier(e.getSuperclassName());
    1067 						vr = v.doPass1();
    1068 						if (vr != VerificationResult.VR_OK){
    1069 							throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+"' as an Exception but '"+e.getSuperclassName()+"' in the ancestor hierachy does not pass verification pass 1: "+vr);
    1070 						}
    1071 						else{
    1072 							e = Repository.lookupClass(e.getSuperclassName());
    1073 						}
    1074 					}
    1075 					if (e != t) {
    1076                         throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+"' as an Exception but it is not a subclass of '"+t.getClassName()+"'.");
    1077                     }
    1078 				}
    1079 			}
    1080
    1081 		    } catch (ClassNotFoundException e) {
    1082 			// FIXME: this might not be the best way to handle missing classes.
    1083 			throw new AssertionViolatedException("Missing class: " + e.toString());
    1084 		    }
    1085 		}
    1086 		// SYNTHETIC: see above
    1087 		// DEPRECATED: see above
    1088 		//////////////////////////////////////////////////////////////
    1089 		// code_attribute-structure-ATTRIBUTES (vmspec2 4.7.3, 4.7) //
    1090 		//////////////////////////////////////////////////////////////
    1091 		public void visitLineNumberTable(LineNumberTable obj){//vmspec2 4.7.8
    1092 			checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    1093
    1094 			String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    1095 			if (! name.equals("LineNumberTable")){
    1096 				throw new ClassConstraintException("The LineNumberTable attribute '"+tostring(obj)+"' is not correctly named 'LineNumberTable' but '"+name+"'.");
    1097 			}
    1098
    1099 			//In JustIce,this check is delayed to Pass 3a.
    1100 			//LineNumber[] linenumbers = obj.getLineNumberTable();
    1101 			// ...validity check...
    1102
    1103 		}
    1104 		public void visitLocalVariableTable(LocalVariableTable obj){//vmspec2 4.7.9
    1105 			//In JustIce,this check is partially delayed to Pass 3a.
    1106 			//The other part can be found in the visitCode(Code) method.
    1107 		}
    1108 		////////////////////////////////////////////////////
    1109 		// MISC-structure-ATTRIBUTES (vmspec2 4.7.1, 4.7) //
    1110 		////////////////////////////////////////////////////
    1111 		public void visitUnknown(Unknown obj){//vmspec2 4.7.1
    1112 			// Represents an unknown attribute.
    1113 			checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    1114
    1115 			// Maybe only misnamed? Give a (warning) message.
    1116 			addMessage("Unknown attribute '"+tostring(obj)+"'. This attribute is not known in any context!");
    1117 		}
    1118 		//////////
    1119 		// BCEL //
    1120 		//////////
    1121 		public void visitLocalVariable(LocalVariable obj){
    1122 			// This does not represent an Attribute but is only
    1123 			// related to internal BCEL data representation.
    1124
    1125 			// see visitLocalVariableTable(LocalVariableTable)
    1126 		}
    1127 		public void visitCodeException(CodeException obj){
    1128 			// Code constraints are checked in Pass3 (3a and 3b).
    1129 			// This does not represent an Attribute but is only
    1130 			// related to internal BCEL data representation.
    1131
    1132 			// see visitCode(Code)
    1133 		}
    1134 		public void visitConstantPool(ConstantPool obj){
    1135 			// No need to. We're piggybacked by the DescendingVisitor.
    1136 			// This does not represent an Attribute but is only
    1137 			// related to internal BCEL data representation.
    1138 		}
    1139 		public void visitInnerClass(InnerClass obj){
    1140 			// This does not represent an Attribute but is only
    1141 			// related to internal BCEL data representation.
    1142 		}
    1143 		public void visitLineNumber(LineNumber obj){
    1144 			// This does not represent an Attribute but is only
    1145 			// related to internal BCEL data representation.
    1146
    1147 			// see visitLineNumberTable(LineNumberTable)
    1148 		}
    1149 	}
    1150
    1151 	/***
    1152 	 * Ensures that the ConstantCP-subclassed entries of the constant
    1153 	 * pool are valid. According to "Yellin: Low Level Security in Java",
    1154 	 * this method does not verify the existence of referenced entities
    1155 	 * (such as classes) but only the formal correctness (such as well-formed
    1156 	 * signatures).
    1157    * The visitXXX() methods throw ClassConstraintException instances otherwise.
    1158 	 * <B>Precondition: index-style cross referencing in the constant
    1159 	 * pool must be valid. Simply invoke constant_pool_entries_satisfy_static_constraints()
    1160 	 * before.</B>
    1161 	 *
    1162 	 * @throws ClassConstraintException otherwise.
    1163 	 * @see #constant_pool_entries_satisfy_static_constraints()
    1164 	 */
    1165 	private void field_and_method_refs_are_valid(){
    1166 	    try {
    1167 		JavaClass jc = Repository.lookupClass(myOwner.getClassName());
    1168 		DescendingVisitor v = new DescendingVisitor(jc, new FAMRAV_Visitor(jc));
    1169 		v.visit();
    1170
    1171 	    } catch (ClassNotFoundException e) {
    1172 		// FIXME: this might not be the best way to handle missing classes.
    1173 		throw new AssertionViolatedException("Missing class: " + e.toString());
    1174 	    }
    1175 	}
    1176
    1177 	/***
    1178 	 * A Visitor class that ensures the ConstantCP-subclassed entries
    1179 	 * of the constant pool are valid.
    1180    * <B>Precondition: index-style cross referencing in the constant
    1181    * pool must be valid.</B>
    1182 	 *
    1183    * @see #constant_pool_entries_satisfy_static_constraints()
    1184 	 * @see org.apache.bcel.classfile.ConstantCP
    1185 	 */
    1186 	private class FAMRAV_Visitor extends EmptyVisitor implements Visitor{
    1187 		private final ConstantPool cp; // ==jc.getConstantPool() -- only here to save typing work.
    1188 		private FAMRAV_Visitor(JavaClass _jc){
    1189 			cp = _jc.getConstantPool();
    1190 		}
    1191
    1192 		public void visitConstantFieldref(ConstantFieldref obj){
    1193 			if (obj.getTag() != Constants.CONSTANT_Fieldref){
    1194 				throw new ClassConstraintException("ConstantFieldref '"+tostring(obj)+"' has wrong tag!");
    1195 			}
    1196 			int name_and_type_index = obj.getNameAndTypeIndex();
    1197 			ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
    1198 			String name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes(); // Field or Method name
    1199 			if (!validFieldName(name)){
    1200 				throw new ClassConstraintException("Invalid field name '"+name+"' referenced by '"+tostring(obj)+"'.");
    1201 			}
    1202
    1203 			int class_index = obj.getClassIndex();
    1204 			ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
    1205 			String className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes(); // Class Name in internal form
    1206 			if (! validClassName(className)){
    1207 				throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
    1208 			}
    1209
    1210 			String sig  = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
    1211
    1212 			try{
    1213 				Type.getType(sig); /* Don't need the return value */
    1214 			}
    1215 			catch (ClassFormatException cfe){
    1216 				throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
    1217 			}
    1218 		}
    1219
    1220 		public void visitConstantMethodref(ConstantMethodref obj){
    1221 			if (obj.getTag() != Constants.CONSTANT_Methodref){
    1222 				throw new ClassConstraintException("ConstantMethodref '"+tostring(obj)+"' has wrong tag!");
    1223 			}
    1224 			int name_and_type_index = obj.getNameAndTypeIndex();
    1225 			ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
    1226 			String name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes(); // Field or Method name
    1227 			if (!validClassMethodName(name)){
    1228 				throw new ClassConstraintException("Invalid (non-interface) method name '"+name+"' referenced by '"+tostring(obj)+"'.");
    1229 			}
    1230
    1231 			int class_index = obj.getClassIndex();
    1232 			ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
    1233 			String className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes(); // Class Name in internal form
    1234 			if (! validClassName(className)){
    1235 				throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
    1236 			}
    1237
    1238 			String sig  = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
    1239
    1240 			try{
    1241 				Type   t  = Type.getReturnType(sig);
    1242 				if ( name.equals(CONSTRUCTOR_NAME) && (t != Type.VOID) ){
    1243 					throw new ClassConstraintException("Instance initialization method must have VOID return type.");
    1244 				}
    1245 			}
    1246 			catch (ClassFormatException cfe){
    1247 				throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
    1248 			}
    1249 		}
    1250
    1251 		public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj){
    1252 			if (obj.getTag() != Constants.CONSTANT_InterfaceMethodref){
    1253 				throw new ClassConstraintException("ConstantInterfaceMethodref '"+tostring(obj)+"' has wrong tag!");
    1254 			}
    1255 			int name_and_type_index = obj.getNameAndTypeIndex();
    1256 			ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
    1257 			String name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes(); // Field or Method name
    1258 			if (!validInterfaceMethodName(name)){
    1259 				throw new ClassConstraintException("Invalid (interface) method name '"+name+"' referenced by '"+tostring(obj)+"'.");
    1260 			}
    1261
    1262 			int class_index = obj.getClassIndex();
    1263 			ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
    1264 			String className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes(); // Class Name in internal form
    1265 			if (! validClassName(className)){
    1266 				throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
    1267 			}
    1268
    1269 			String sig  = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
    1270
    1271 			try{
    1272 				Type   t  = Type.getReturnType(sig);
    1273 				if ( name.equals(STATIC_INITIALIZER_NAME) && (t != Type.VOID) ){
    1274 					addMessage("Class or interface initialization method '"+STATIC_INITIALIZER_NAME+"' usually has VOID return type instead of '"+t+"'. Note this is really not a requirement of The Java Virtual Machine Specification, Second Edition.");
    1275 				}
    1276 			}
    1277 			catch (ClassFormatException cfe){
    1278 				throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
    1279 			}
    1280
    1281 		}
    1282
    1283 	}
    1284
    1285 	/***
    1286 	 * This method returns true if and only if the supplied String
    1287 	 * represents a valid Java class name.
    1288 	 */
    1289 	private static final boolean validClassName(String name){
    1290         /*
    1291          * TODO: implement.
    1292 		 * Are there any restrictions?
    1293          */
    1294 		return true;
    1295 	}
    1296 	/***
    1297 	 * This method returns true if and only if the supplied String
    1298 	 * represents a valid method name.
    1299 	 * This is basically the same as a valid identifier name in the
    1300 	 * Java programming language, but the special name for
    1301 	 * the instance initialization method is allowed and the special name
    1302 	 * for the class/interface initialization method may be allowed.
    1303 	 */
    1304 	private static boolean validMethodName(String name, boolean allowStaticInit){
    1305 		if (validJavaLangMethodName(name)) {
    1306             return true;
    1307         }
    1308
    1309 		if (allowStaticInit){
    1310 			return (name.equals(CONSTRUCTOR_NAME) || name.equals(STATIC_INITIALIZER_NAME));
    1311 		}
    1312 		else{
    1313 			return name.equals(CONSTRUCTOR_NAME);
    1314 		}
    1315 	}
    1316
    1317 	/***
    1318 	 * This method returns true if and only if the supplied String
    1319 	 * represents a valid method name that may be referenced by
    1320 	 * ConstantMethodref objects.
    1321 	 */
    1322 	private static boolean validClassMethodName(String name){
    1323 		return validMethodName(name, false);
    1324 	}
    1325
    1326 	/***
    1327 	 * This method returns true if and only if the supplied String
    1328 	 * represents a valid Java programming language method name stored as a simple
    1329 	 * (non-qualified) name.
    1330 	 * Conforming to: The Java Virtual Machine Specification, Second Edition, ß2.7, ß2.7.1, ß2.2.
    1331 	 */
    1332 	private static boolean validJavaLangMethodName(String name){
    1333 		if (!Character.isJavaIdentifierStart(name.charAt(0))) {
    1334             return false;
    1335         }
    1336
    1337 		for (int i=1; i<name.length(); i++){
    1338 			if (!Character.isJavaIdentifierPart(name.charAt(i))) {
    1339                 return false;
    1340             }
    1341 		}
    1342 		return true;
    1343 	}
    1344
    1345 	/***
    1346 	 * This method returns true if and only if the supplied String
    1347 	 * represents a valid Java interface method name that may be
    1348 	 * referenced by ConstantInterfaceMethodref objects.
    1349 	 */
    1350 	private static boolean validInterfaceMethodName(String name){
    1351 		// I guess we should assume special names forbidden here.
    1352 		if (name.startsWith("<")) {
    1353             return false;
    1354         }
    1355 		return validJavaLangMethodName(name);
    1356 	}
    1357
    1358 	/***
    1359 	 * This method returns true if and only if the supplied String
    1360 	 * represents a valid Java identifier (so-called simple name).
    1361 	 */
    1362 	private static boolean validJavaIdentifier(String name){
    1363     if  (name.length() == 0) {
    1364         return false; // must not be empty, reported by <francis.andre@easynet.fr>, thanks!
    1365     }
    1366
    1367 		// vmspec2 2.7, vmspec2 2.2
    1368 		if (!Character.isJavaIdentifierStart(name.charAt(0))) {
    1369             return false;
    1370         }
    1371
    1372 		for (int i=1; i<name.length(); i++){
    1373 			if (!Character.isJavaIdentifierPart(name.charAt(i))) {
    1374                 return false;
    1375             }
    1376 		}
    1377 		return true;
    1378 	}
    1379
    1380 	/***
    1381 	 * This method returns true if and only if the supplied String
    1382 	 * represents a valid Java field name.
    1383 	 */
    1384 	private static boolean validFieldName(String name){
    1385 		// vmspec2 2.7, vmspec2 2.2
    1386 		return validJavaIdentifier(name);
    1387 	}
    1388
    1389 	/***
    1390 	 * This class serves for finding out if a given JavaClass' ConstantPool
    1391 	 * references an Inner Class.
    1392 	 * The Java Virtual Machine Specification, Second Edition is not very precise
    1393 	 * about when an "InnerClasses" attribute has to appear. However, it states that
    1394 	 * there has to be exactly one InnerClasses attribute in the ClassFile structure
    1395 	 * if the constant pool of a class or interface refers to any class or interface
    1396 	 * "that is not a member of a package". Sun does not mean "member of the default
    1397 	 * package". In "Inner Classes Specification" they point out how a "bytecode name"
    1398 	 * is derived so one has to deduce what a class name of a class "that is not a
    1399 	 * member of a package" looks like: there is at least one character in the byte-
    1400 	 * code name that cannot be part of a legal Java Language Class name (and not equal
    1401 	 * to '/'). This assumption is wrong as the delimiter is '$' for which
    1402 	 * Character.isJavaIdentifierPart() == true.
    1403 	 * Hence, you really run into trouble if you have a toplevel class called
    1404 	 * "A$XXX" and another toplevel class called "A" with in inner class called "XXX".
    1405 	 * JustIce cannot repair this; please note that existing verifiers at this
    1406 	 * time even fail to detect missing InnerClasses attributes in pass 2.
    1407 	 */
    1408 	private static class InnerClassDetector extends EmptyVisitor{
    1409 		private boolean hasInnerClass = false;
    1410 		private JavaClass jc;
    1411 		private ConstantPool cp;
    1412
    1413 		/*** Constructs an InnerClassDetector working on the JavaClass _jc. */
    1414 		public InnerClassDetector(JavaClass _jc){
    1415 			jc = _jc;
    1416 			cp = jc.getConstantPool();
    1417 			(new DescendingVisitor(jc, this)).visit();
    1418 		}
    1419 		/***
    1420 		 * Returns if the JavaClass this InnerClassDetector is working on
    1421 		 * has an Inner Class reference in its constant pool.
    1422 		 */
    1423 		public boolean innerClassReferenced(){
    1424 			return hasInnerClass;
    1425 		}
    1426 		/*** This method casually visits ConstantClass references. */
    1427 		public void visitConstantClass(ConstantClass obj){
    1428 			Constant c = cp.getConstant(obj.getNameIndex());
    1429 			if (c instanceof ConstantUtf8){ //Ignore the case where it's not a ConstantUtf8 here, we'll find out later.
    1430 				String classname = ((ConstantUtf8) c).getBytes();
    1431 				if (classname.startsWith(jc.getClassName().replace('.','/')+"$")){
    1432 					hasInnerClass = true;
    1433 				}
    1434 			}
    1435 		}
    1436 	}
    1437
    1438 	/***
    1439 	 * This method is here to save typing work and improve code readability.
    1440 	 */
    1441 	private static String tostring(Node n){
    1442 		return new StringRepresentation(n).toString();
    1443 	}
    1444 }