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 }