View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License. 
16   *
17   */ 
18  package org.apache.bcel.verifier.statics;
19  
20  
21  import org.apache.bcel.Constants;
22  import org.apache.bcel.Repository;
23  import org.apache.bcel.classfile.Attribute;
24  import org.apache.bcel.classfile.Code;
25  import org.apache.bcel.classfile.CodeException;
26  import org.apache.bcel.classfile.Constant;
27  import org.apache.bcel.classfile.ConstantClass;
28  import org.apache.bcel.classfile.ConstantDouble;
29  import org.apache.bcel.classfile.ConstantFieldref;
30  import org.apache.bcel.classfile.ConstantFloat;
31  import org.apache.bcel.classfile.ConstantInteger;
32  import org.apache.bcel.classfile.ConstantInterfaceMethodref;
33  import org.apache.bcel.classfile.ConstantLong;
34  import org.apache.bcel.classfile.ConstantMethodref;
35  import org.apache.bcel.classfile.ConstantNameAndType;
36  import org.apache.bcel.classfile.ConstantString;
37  import org.apache.bcel.classfile.ConstantUtf8;
38  import org.apache.bcel.classfile.Field;
39  import org.apache.bcel.classfile.JavaClass;
40  import org.apache.bcel.classfile.LineNumber;
41  import org.apache.bcel.classfile.LineNumberTable;
42  import org.apache.bcel.classfile.LocalVariable;
43  import org.apache.bcel.classfile.LocalVariableTable;
44  import org.apache.bcel.classfile.Method;
45  import org.apache.bcel.generic.ALOAD;
46  import org.apache.bcel.generic.ANEWARRAY;
47  import org.apache.bcel.generic.ASTORE;
48  import org.apache.bcel.generic.ATHROW;
49  import org.apache.bcel.generic.ArrayType;
50  import org.apache.bcel.generic.BREAKPOINT;
51  import org.apache.bcel.generic.CHECKCAST;
52  import org.apache.bcel.generic.ConstantPoolGen;
53  import org.apache.bcel.generic.DLOAD;
54  import org.apache.bcel.generic.DSTORE;
55  import org.apache.bcel.generic.FLOAD;
56  import org.apache.bcel.generic.FSTORE;
57  import org.apache.bcel.generic.FieldInstruction;
58  import org.apache.bcel.generic.GETSTATIC;
59  import org.apache.bcel.generic.GotoInstruction;
60  import org.apache.bcel.generic.IINC;
61  import org.apache.bcel.generic.ILOAD;
62  import org.apache.bcel.generic.IMPDEP1;
63  import org.apache.bcel.generic.IMPDEP2;
64  import org.apache.bcel.generic.INSTANCEOF;
65  import org.apache.bcel.generic.INVOKEINTERFACE;
66  import org.apache.bcel.generic.INVOKESPECIAL;
67  import org.apache.bcel.generic.INVOKESTATIC;
68  import org.apache.bcel.generic.INVOKEVIRTUAL;
69  import org.apache.bcel.generic.ISTORE;
70  import org.apache.bcel.generic.Instruction;
71  import org.apache.bcel.generic.InstructionHandle;
72  import org.apache.bcel.generic.InstructionList;
73  import org.apache.bcel.generic.InvokeInstruction;
74  import org.apache.bcel.generic.JsrInstruction;
75  import org.apache.bcel.generic.LDC;
76  import org.apache.bcel.generic.LDC2_W;
77  import org.apache.bcel.generic.LLOAD;
78  import org.apache.bcel.generic.LOOKUPSWITCH;
79  import org.apache.bcel.generic.LSTORE;
80  import org.apache.bcel.generic.LoadClass;
81  import org.apache.bcel.generic.MULTIANEWARRAY;
82  import org.apache.bcel.generic.NEW;
83  import org.apache.bcel.generic.NEWARRAY;
84  import org.apache.bcel.generic.ObjectType;
85  import org.apache.bcel.generic.PUTSTATIC;
86  import org.apache.bcel.generic.RET;
87  import org.apache.bcel.generic.ReturnInstruction;
88  import org.apache.bcel.generic.TABLESWITCH;
89  import org.apache.bcel.generic.Type;
90  import org.apache.bcel.verifier.PassVerifier;
91  import org.apache.bcel.verifier.VerificationResult;
92  import org.apache.bcel.verifier.Verifier;
93  import org.apache.bcel.verifier.VerifierFactory;
94  import org.apache.bcel.verifier.exc.AssertionViolatedException;
95  import org.apache.bcel.verifier.exc.ClassConstraintException;
96  import org.apache.bcel.verifier.exc.InvalidMethodException;
97  import org.apache.bcel.verifier.exc.StaticCodeConstraintException;
98  import org.apache.bcel.verifier.exc.StaticCodeInstructionConstraintException;
99  import org.apache.bcel.verifier.exc.StaticCodeInstructionOperandConstraintException;
100 
101 /**
102  * This PassVerifier verifies a class file according to
103  * pass 3, static part as described in The Java Virtual
104  * Machine Specification, 2nd edition.
105  * More detailed information is to be found at the do_verify()
106  * method's documentation. 
107  *
108  * @version $Id: Pass3aVerifier.html 898356 2014-02-18 05:44:40Z ggregory $
109  * @author Enver Haase
110  * @see #do_verify()
111  */
112 public final class Pass3aVerifier extends PassVerifier{
113 
114 	/** The Verifier that created this. */
115 	private Verifier myOwner;
116 
117 	/** 
118 	 * The method number to verify.
119 	 * This is the index in the array returned
120 	 * by JavaClass.getMethods().
121 	 */
122 	private int method_no;
123 
124 	/** The one and only InstructionList object used by an instance of this class. It's here for performance reasons by do_verify() and its callees. */	
125 	InstructionList instructionList;
126 	/** The one and only Code object used by an instance of this class. It's here for performance reasons by do_verify() and its callees. */	
127 	Code code;
128 
129 	/** Should only be instantiated by a Verifier. */
130 	public Pass3aVerifier(Verifier owner, int method_no){
131 		myOwner = owner;
132 		this.method_no = method_no;
133 	}
134 
135 	/**
136 	 * Pass 3a is the verification of static constraints of
137 	 * JVM code (such as legal targets of branch instructions).
138 	 * This is the part of pass 3 where you do not need data
139 	 * flow analysis.
140 	 * JustIce also delays the checks for a correct exception
141 	 * table of a Code attribute and correct line number entries
142 	 * in a LineNumberTable attribute of a Code attribute (which
143 	 * conceptually belong to pass 2) to this pass. Also, most
144 	 * of the check for valid local variable entries in a
145 	 * LocalVariableTable attribute of a Code attribute is
146 	 * delayed until this pass.
147 	 * All these checks need access to the code array of the
148 	 * Code attribute.
149 	 *
150 	 * @throws InvalidMethodException if the method to verify does not exist.
151 	 */
152 	@Override
153     public VerificationResult do_verify(){
154 	    try {
155 		if (myOwner.doPass2().equals(VerificationResult.VR_OK)){
156 			// Okay, class file was loaded correctly by Pass 1
157 			// and satisfies static constraints of Pass 2.
158 			JavaClass jc = Repository.lookupClass(myOwner.getClassName());
159 			Method[] methods = jc.getMethods();
160 			if (method_no >= methods.length){
161 				throw new InvalidMethodException("METHOD DOES NOT EXIST!");
162 			}
163 			Method method = methods[method_no];
164 			code = method.getCode();
165 			
166 			// No Code? Nothing to verify!
167 			if ( method.isAbstract() || method.isNative() ){ // IF mg HAS NO CODE (static constraint of Pass 2)
168 				return VerificationResult.VR_OK;
169 			}
170 
171 			// TODO:
172 			// We want a very sophisticated code examination here with good explanations
173 			// on where to look for an illegal instruction or such.
174 			// Only after that we should try to build an InstructionList and throw an
175 			// AssertionViolatedException if after our examination InstructionList building
176 			// still fails.
177 			// That examination should be implemented in a byte-oriented way, i.e. look for
178 			// an instruction, make sure its validity, count its length, find the next
179 			// instruction and so on.
180 			try{
181 				instructionList = new InstructionList(method.getCode().getCode());
182 			}
183 			catch(RuntimeException re){
184 				return new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Bad bytecode in the code array of the Code attribute of method '"+method+"'.");
185 			}
186 			
187 			instructionList.setPositions(true);
188 
189 			// Start verification.
190 			VerificationResult vr = VerificationResult.VR_OK; //default
191 			try{
192 				delayedPass2Checks();
193 			}
194 			catch(ClassConstraintException cce){
195 				vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage());
196 				return vr;
197 			}
198 			try{
199 				pass3StaticInstructionChecks();
200 				pass3StaticInstructionOperandsChecks();
201 			}
202 			catch(StaticCodeConstraintException scce){
203 				vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, scce.getMessage());
204 			}
205 			catch(ClassCastException cce){
206 				vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Class Cast Exception: " + cce.getMessage());
207 			}
208 			return vr;
209 		}
210 		else{ //did not pass Pass 2.
211 			return VerificationResult.VR_NOTYET;
212 		}
213 	    } catch (ClassNotFoundException e) {
214 		// FIXME: maybe not the best way to handle this
215 		throw new AssertionViolatedException("Missing class: " + e, e);
216 	    }
217 	}
218 
219 	/**
220 	 * These are the checks that could be done in pass 2 but are delayed to pass 3
221 	 * for performance reasons. Also, these checks need access to the code array
222 	 * of the Code attribute of a Method so it's okay to perform them here.
223 	 * Also see the description of the do_verify() method.
224 	 *
225 	 * @throws ClassConstraintException if the verification fails.
226 	 * @see #do_verify()
227 	 */
228 	private void delayedPass2Checks(){
229 
230 		int[] instructionPositions = instructionList.getInstructionPositions();
231 		int codeLength = code.getCode().length;
232 
233 		/////////////////////
234 		// LineNumberTable //
235 		/////////////////////
236 		LineNumberTable lnt = code.getLineNumberTable();
237 		if (lnt != null){
238 			LineNumber[] lineNumbers = lnt.getLineNumberTable();
239 			IntList offsets = new IntList();
240 			lineNumber_loop: for (int i=0; i < lineNumbers.length; i++){ // may appear in any order.
241 				for (int j=0; j < instructionPositions.length; j++){
242 					// TODO: Make this a binary search! The instructionPositions array is naturally ordered!
243 					int offset = lineNumbers[i].getStartPC();
244 					if (instructionPositions[j] == offset){
245 						if (offsets.contains(offset)){
246 							addMessage("LineNumberTable attribute '"+code.getLineNumberTable()+"' refers to the same code offset ('"+offset+"') more than once which is violating the semantics [but is sometimes produced by IBM's 'jikes' compiler].");
247 						}
248 						else{
249 							offsets.add(offset);
250 						}
251 						continue lineNumber_loop;
252 					}
253 				}
254 				throw new ClassConstraintException("Code attribute '"+code+"' has a LineNumberTable attribute '"+code.getLineNumberTable()+"' referring to a code offset ('"+lineNumbers[i].getStartPC()+"') that does not exist.");
255 			}
256 		}
257 
258 		///////////////////////////
259 		// LocalVariableTable(s) //
260 		///////////////////////////
261 		/* We cannot use code.getLocalVariableTable() because there could be more
262 		   than only one. This is a bug in BCEL. */
263 		Attribute[] atts = code.getAttributes();
264 		for (Attribute att : atts) {
265 			if (att instanceof LocalVariableTable){
266 				LocalVariableTable lvt = (LocalVariableTable) att;
267 				if (lvt != null){
268 					LocalVariable[] localVariables = lvt.getLocalVariableTable();
269 					for (LocalVariable localVariable : localVariables) {
270 						int startpc = localVariable.getStartPC();
271 						int length  = localVariable.getLength();
272 				
273 						if (!contains(instructionPositions, startpc)){
274 							throw new ClassConstraintException("Code attribute '"+code+"' has a LocalVariableTable attribute '"+code.getLocalVariableTable()+"' referring to a code offset ('"+startpc+"') that does not exist.");
275 						}
276 						if ( (!contains(instructionPositions, startpc+length)) && (startpc+length != codeLength) ){
277 							throw new ClassConstraintException("Code attribute '"+code+"' has a LocalVariableTable attribute '"+code.getLocalVariableTable()+"' referring to a code offset start_pc+length ('"+(startpc+length)+"') that does not exist.");
278 						}
279 					}
280 				}
281 			}
282 		}
283 		
284 		////////////////////
285 		// ExceptionTable //
286 		////////////////////
287 		// In BCEL's "classfile" API, the startPC/endPC-notation is
288 		// inclusive/exclusive as in the Java Virtual Machine Specification.
289 		// WARNING: This is not true for BCEL's "generic" API.
290 		CodeException[] exceptionTable = code.getExceptionTable();
291 		for (CodeException element : exceptionTable) {
292 			int startpc = element.getStartPC();
293 			int endpc = element.getEndPC();
294 			int handlerpc = element.getHandlerPC();
295 			if (startpc >= endpc){
296 				throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+element+"' that has its start_pc ('"+startpc+"') not smaller than its end_pc ('"+endpc+"').");
297 			}
298 			if (!contains(instructionPositions, startpc)){
299 				throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+element+"' that has a non-existant bytecode offset as its start_pc ('"+startpc+"').");
300 			}
301 			if ( (!contains(instructionPositions, endpc)) && (endpc != codeLength)){
302 				throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+element+"' that has a non-existant bytecode offset as its end_pc ('"+startpc+"') [that is also not equal to code_length ('"+codeLength+"')].");
303 			}
304 			if (!contains(instructionPositions, handlerpc)){
305 				throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+element+"' that has a non-existant bytecode offset as its handler_pc ('"+handlerpc+"').");
306 			}
307 		}
308 	}
309 
310 	/**
311 	 * These are the checks if constraints are satisfied which are described in the
312 	 * Java Virtual Machine Specification, Second Edition as Static Constraints on
313 	 * the instructions of Java Virtual Machine Code (chapter 4.8.1).
314 	 *
315 	 * @throws StaticCodeConstraintException if the verification fails.
316 	 */
317 	private void pass3StaticInstructionChecks(){
318 		
319 		// Code array must not be empty:
320 		// Enforced in pass 2 (also stated in the static constraints of the Code
321 		// array in vmspec2), together with pass 1 (reading code_length bytes and
322 		// interpreting them as code[]). So this must not be checked again here.
323 
324 		if (! (code.getCode().length < 65536)){// contradicts vmspec2 page 152 ("Limitations"), but is on page 134.
325 			throw new StaticCodeInstructionConstraintException("Code array in code attribute '"+code+"' too big: must be smaller than 65536 bytes.");
326 		}
327 
328 		// First opcode at offset 0: okay, that's clear. Nothing to do.
329 		
330 		// Only instances of the instructions documented in Section 6.4 may appear in
331 		// the code array.
332 		
333 		// For BCEL's sake, we cannot handle WIDE stuff, but hopefully BCEL does its job right :)
334 		
335 		// The last byte of the last instruction in the code array must be the byte at index
336 		// code_length-1 : See the do_verify() comments. We actually don't iterate through the
337 		// byte array, but use an InstructionList so we cannot check for this. But BCEL does
338 		// things right, so it's implicitly okay.
339 		
340 		// TODO: Check how BCEL handles (and will handle) instructions like IMPDEP1, IMPDEP2,
341 		//       BREAKPOINT... that BCEL knows about but which are illegal anyway.
342 		//       We currently go the safe way here.
343 		InstructionHandle ih = instructionList.getStart();
344 		while (ih != null){
345 			Instruction i = ih.getInstruction();
346 			if (i instanceof IMPDEP1){
347 				throw new StaticCodeInstructionConstraintException("IMPDEP1 must not be in the code, it is an illegal instruction for _internal_ JVM use!");
348 			}
349 			if (i instanceof IMPDEP2){
350 				throw new StaticCodeInstructionConstraintException("IMPDEP2 must not be in the code, it is an illegal instruction for _internal_ JVM use!");
351 			}
352 			if (i instanceof BREAKPOINT){
353 				throw new StaticCodeInstructionConstraintException("BREAKPOINT must not be in the code, it is an illegal instruction for _internal_ JVM use!");
354 			}
355 			ih = ih.getNext();
356 		}
357 		
358 		// The original verifier seems to do this check here, too.
359 		// An unreachable last instruction may also not fall through the
360 		// end of the code, which is stupid -- but with the original
361 		// verifier's subroutine semantics one cannot predict reachability.
362 		Instruction last = instructionList.getEnd().getInstruction();
363 		if (! ((last instanceof ReturnInstruction)	||
364 					(last instanceof RET)    							||
365 					(last instanceof GotoInstruction)			||
366 					(last instanceof ATHROW) )) {
367             throw new StaticCodeInstructionConstraintException("Execution must not fall off the bottom of the code array. This constraint is enforced statically as some existing verifiers do - so it may be a false alarm if the last instruction is not reachable.");
368         }
369 	}
370 
371 	/**
372 	 * These are the checks for the satisfaction of constraints which are described in the
373 	 * Java Virtual Machine Specification, Second Edition as Static Constraints on
374 	 * the operands of instructions of Java Virtual Machine Code (chapter 4.8.1).
375 	 * BCEL parses the code array to create an InstructionList and therefore has to check
376 	 * some of these constraints. Additional checks are also implemented here.
377 	 *
378 	 * @throws StaticCodeConstraintException if the verification fails.
379 	 */
380 	private void pass3StaticInstructionOperandsChecks(){
381 	    try {
382 		// When building up the InstructionList, BCEL has already done all those checks
383 		// mentioned in The Java Virtual Machine Specification, Second Edition, as
384 		// "static constraints on the operands of instructions in the code array".
385 		// TODO: see the do_verify() comments. Maybe we should really work on the
386 		//       byte array first to give more comprehensive messages.
387 		// TODO: Review Exception API, possibly build in some "offending instruction" thing
388 		//       when we're ready to insulate the offending instruction by doing the
389 		//       above thing.
390 
391 		// TODO: Implement as much as possible here. BCEL does _not_ check everything.
392 
393 		ConstantPoolGen cpg = new ConstantPoolGen(Repository.lookupClass(myOwner.getClassName()).getConstantPool());
394 		InstOperandConstraintVisitor v = new InstOperandConstraintVisitor(cpg);
395 	
396 		// Checks for the things BCEL does _not_ handle itself.
397 		InstructionHandle ih = instructionList.getStart();
398 		while (ih != null){
399 			Instruction i = ih.getInstruction();
400 			
401 			// An "own" constraint, due to JustIce's new definition of what "subroutine" means.
402 			if (i instanceof JsrInstruction){
403 				InstructionHandle target = ((JsrInstruction) i).getTarget();
404 				if (target == instructionList.getStart()){
405 					throw new StaticCodeInstructionOperandConstraintException("Due to JustIce's clear definition of subroutines, no JSR or JSR_W may have a top-level instruction (such as the very first instruction, which is targeted by instruction '"+ih+"' as its target.");
406 				}
407 				if (!(target.getInstruction() instanceof ASTORE)){
408 					throw new StaticCodeInstructionOperandConstraintException("Due to JustIce's clear definition of subroutines, no JSR or JSR_W may target anything else than an ASTORE instruction. Instruction '"+ih+"' targets '"+target+"'.");
409 				}
410 			}
411 			
412 			// vmspec2, page 134-137
413 			ih.accept(v);
414 			
415 			ih = ih.getNext();
416 		}
417 
418 	    } catch (ClassNotFoundException e) {
419 		// FIXME: maybe not the best way to handle this
420 		throw new AssertionViolatedException("Missing class: " + e, e);
421 	    }
422 	}
423 	
424 	/** A small utility method returning if a given int i is in the given int[] ints. */
425 	private static boolean contains(int[] ints, int i){
426 		for (int k : ints) {
427 			if (k==i) {
428                 return true;
429             }
430 		}
431 		return false;
432 	}
433 
434 	/** Returns the method number as supplied when instantiating. */
435 	public int getMethodNo(){
436 		return method_no;
437 	}
438 
439 	/**
440 	 * This visitor class does the actual checking for the instruction
441 	 * operand's constraints.
442 	 */
443 	private class InstOperandConstraintVisitor extends org.apache.bcel.generic.EmptyVisitor{
444 		/** The ConstantPoolGen instance this Visitor operates on. */
445 		private ConstantPoolGen cpg;
446 
447 		/** The only Constructor. */
448 		InstOperandConstraintVisitor(ConstantPoolGen cpg){
449 			this.cpg = cpg;
450 		}
451 
452 		/**
453 		 * Utility method to return the max_locals value of the method verified
454 		 * by the surrounding Pass3aVerifier instance.
455 		 */
456 		private int max_locals(){
457 		   try {
458 			return Repository.lookupClass(myOwner.getClassName()).getMethods()[method_no].getCode().getMaxLocals();
459 		    } catch (ClassNotFoundException e) {
460 			// FIXME: maybe not the best way to handle this
461 			throw new AssertionViolatedException("Missing class: " + e, e);
462 		    }
463 		}
464 
465 		/**
466 		 * A utility method to always raise an exeption.
467 		 */
468 		private void constraintViolated(Instruction i, String message) {
469 			throw new StaticCodeInstructionOperandConstraintException("Instruction "+i+" constraint violated: "+message);
470 		}
471 
472 		/**
473 		 * A utility method to raise an exception if the index is not
474 		 * a valid constant pool index.
475 		 */
476 		private void indexValid(Instruction i, int idx){
477 			if (idx < 0 || idx >= cpg.getSize()){
478 				constraintViolated(i, "Illegal constant pool index '"+idx+"'.");
479 			}
480 		}
481 
482 		///////////////////////////////////////////////////////////
483 		// The Java Virtual Machine Specification, pages 134-137 //
484 		///////////////////////////////////////////////////////////
485 		/**
486 		 * Assures the generic preconditions of a LoadClass instance.
487 		 * The referenced class is loaded and pass2-verified.
488 		 */
489 		@Override
490         public void visitLoadClass(LoadClass o){
491 			ObjectType t = o.getLoadClassType(cpg);
492 			if (t != null){// null means "no class is loaded"
493 				Verifier v = VerifierFactory.getVerifier(t.getClassName());
494 				VerificationResult vr = v.doPass1();
495 				if (vr.getStatus() != VerificationResult.VERIFIED_OK){
496 					constraintViolated((Instruction) o, "Class '"+o.getLoadClassType(cpg).getClassName()+"' is referenced, but cannot be loaded: '"+vr+"'.");
497 				}
498 			}
499 		}
500 		
501 		// The target of each jump and branch instruction [...] must be the opcode [...]
502 		// BCEL _DOES_ handle this.
503 
504 		// tableswitch: BCEL will do it, supposedly.
505 		
506 		// lookupswitch: BCEL will do it, supposedly.
507 		
508 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
509 		// LDC and LDC_W (LDC_W is a subclass of LDC in BCEL's model)
510 		@Override
511         public void visitLDC(LDC o){
512 			indexValid(o, o.getIndex());
513 			Constant c = cpg.getConstant(o.getIndex());
514 			if (c instanceof ConstantClass){
515 			  addMessage("Operand of LDC or LDC_W is CONSTANT_Class '"+c+"' - this is only supported in JDK 1.5 and higher.");
516 			}
517 			else{
518 			  if (! ( (c instanceof ConstantInteger)	||
519 			          (c instanceof ConstantFloat) 		||
520                 (c instanceof ConstantString) ) ){
521             constraintViolated(o, "Operand of LDC or LDC_W must be one of CONSTANT_Integer, CONSTANT_Float or CONSTANT_String, but is '"+c+"'.");
522 			  }
523 			}
524 		}
525 
526 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
527 		// LDC2_W
528 		@Override
529         public void visitLDC2_W(LDC2_W o){
530 			indexValid(o, o.getIndex());
531 			Constant c = cpg.getConstant(o.getIndex());
532 			if (! ( (c instanceof ConstantLong)	||
533 							(c instanceof ConstantDouble) ) ){
534 				constraintViolated(o, "Operand of LDC2_W must be CONSTANT_Long or CONSTANT_Double, but is '"+c+"'.");
535 			}
536 			try{
537 				indexValid(o, o.getIndex()+1);
538 			}
539 			catch(StaticCodeInstructionOperandConstraintException e){
540 				throw new AssertionViolatedException("OOPS: Does not BCEL handle that? LDC2_W operand has a problem.", e);
541 			}
542 		}
543 
544 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
545  		//getfield, putfield, getstatic, putstatic
546  		@Override
547         public void visitFieldInstruction(FieldInstruction o){
548 		   try {
549 			indexValid(o, o.getIndex());
550 			Constant c = cpg.getConstant(o.getIndex());
551 			if (! (c instanceof ConstantFieldref)){
552 				constraintViolated(o, "Indexing a constant that's not a CONSTANT_Fieldref but a '"+c+"'.");
553 			}
554 			
555 			String field_name = o.getFieldName(cpg);
556  
557 			JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName());
558 			Field[] fields = jc.getFields();
559 			Field f = null;
560 			for (Field field : fields) {
561 				if (field.getName().equals(field_name)){
562 				  Type f_type = Type.getType(field.getSignature());
563 				  Type o_type = o.getType(cpg);
564 					/* TODO: Check if assignment compatibility is sufficient.
565 				   * What does Sun do?
566 				   */
567 				  if (f_type.equals(o_type)){
568 						f = field;
569 						break;
570 					}
571 				}
572 			}
573 			if (f == null){
574 				JavaClass[] superclasses = jc.getSuperClasses();
575 				outer: 
576 				for (int j=0; j<superclasses.length; j++){
577 					fields = superclasses[j].getFields();
578 					for (int i=0; i<fields.length; i++){
579 						if (fields[i].getName().equals(field_name)){
580 							Type f_type = Type.getType(fields[i].getSignature());
581 							Type o_type = o.getType(cpg);
582 							if (f_type.equals(o_type)){
583 								f = fields[i];
584 								if ((f.getAccessFlags() & (Constants.ACC_PUBLIC | Constants.ACC_PROTECTED)) == 0) {
585                                     f = null;
586                                 }
587 								break outer;
588 							}
589 						}
590 					}
591 				}
592 				if (f == null) {
593                     constraintViolated(o, "Referenced field '"+field_name+"' does not exist in class '"+jc.getClassName()+"'.");
594                 }
595 			}
596 			else{
597 				/* TODO: Check if assignment compatibility is sufficient.
598 				   What does Sun do? */
599                 Type.getType(f.getSignature());
600                 o.getType(cpg);
601 //				Type f_type = Type.getType(f.getSignature());
602 //				Type o_type = o.getType(cpg);
603 								
604 				// Argh. Sun's implementation allows us to have multiple fields of
605 				// the same name but with a different signature.
606 				//if (! f_type.equals(o_type)){
607 				//	constraintViolated(o, "Referenced field '"+field_name+"' has type '"+f_type+"' instead of '"+o_type+"' as expected.");
608 				//}
609 				
610 				/* TODO: Check for access modifiers here. */
611 			}
612 		    } catch (ClassNotFoundException e) {
613 			// FIXME: maybe not the best way to handle this
614 			throw new AssertionViolatedException("Missing class: " + e, e);
615 		    }
616 		}	
617 
618 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
619 		@Override
620         public void visitInvokeInstruction(InvokeInstruction o){
621 			indexValid(o, o.getIndex());
622 			if (	(o instanceof INVOKEVIRTUAL)	||
623 						(o instanceof INVOKESPECIAL)	||
624 						(o instanceof INVOKESTATIC)	){
625 				Constant c = cpg.getConstant(o.getIndex());
626 				if (! (c instanceof ConstantMethodref)){
627 					constraintViolated(o, "Indexing a constant that's not a CONSTANT_Methodref but a '"+c+"'.");
628 				}
629 				else{
630 					// Constants are okay due to pass2.
631 					ConstantNameAndType cnat = (ConstantNameAndType) (cpg.getConstant(((ConstantMethodref) c).getNameAndTypeIndex()));
632 					ConstantUtf8 cutf8 = (ConstantUtf8) (cpg.getConstant(cnat.getNameIndex()));
633 					if (cutf8.getBytes().equals(Constants.CONSTRUCTOR_NAME) && (!(o instanceof INVOKESPECIAL)) ){
634 						constraintViolated(o, "Only INVOKESPECIAL is allowed to invoke instance initialization methods.");
635 					}
636 					if ( (! (cutf8.getBytes().equals(Constants.CONSTRUCTOR_NAME)) ) && (cutf8.getBytes().startsWith("<")) ){
637 						constraintViolated(o, "No method with a name beginning with '<' other than the instance initialization methods may be called by the method invocation instructions.");
638 					}
639 				}
640 			}
641 			else{ //if (o instanceof INVOKEINTERFACE){
642 				Constant c = cpg.getConstant(o.getIndex());
643 				if (! (c instanceof ConstantInterfaceMethodref)){
644 					constraintViolated(o, "Indexing a constant that's not a CONSTANT_InterfaceMethodref but a '"+c+"'.");
645 				}
646 				// TODO: From time to time check if BCEL allows to detect if the
647 				// 'count' operand is consistent with the information in the
648 				// CONSTANT_InterfaceMethodref and if the last operand is zero.
649 				// By now, BCEL hides those two operands because they're superfluous.
650 				
651 				// Invoked method must not be <init> or <clinit>
652 				ConstantNameAndType cnat = (ConstantNameAndType) (cpg.getConstant(((ConstantInterfaceMethodref)c).getNameAndTypeIndex()));
653 				String name = ((ConstantUtf8) (cpg.getConstant(cnat.getNameIndex()))).getBytes();
654 				if (name.equals(Constants.CONSTRUCTOR_NAME)){
655 					constraintViolated(o, "Method to invoke must not be '"+Constants.CONSTRUCTOR_NAME+"'.");
656 				}
657 				if (name.equals(Constants.STATIC_INITIALIZER_NAME)){
658 					constraintViolated(o, "Method to invoke must not be '"+Constants.STATIC_INITIALIZER_NAME+"'.");
659 				}
660 			}
661 		
662 			// The LoadClassType is the method-declaring class, so we have to check the other types.
663 			
664 			Type t = o.getReturnType(cpg);
665 			if (t instanceof ArrayType){
666 				t = ((ArrayType) t).getBasicType();
667 			}
668 			if (t instanceof ObjectType){
669 				Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName());
670 				VerificationResult vr = v.doPass2();
671 				if (vr.getStatus() != VerificationResult.VERIFIED_OK){
672 					constraintViolated(o, "Return type class/interface could not be verified successfully: '"+vr.getMessage()+"'.");
673 				}
674 			}
675 			
676 			Type[] ts = o.getArgumentTypes(cpg);
677 			for (Type element : ts) {
678 				t = element;
679 				if (t instanceof ArrayType){
680 					t = ((ArrayType) t).getBasicType();
681 				}
682 				if (t instanceof ObjectType){
683 					Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName());
684 					VerificationResult vr = v.doPass2();
685 					if (vr.getStatus() != VerificationResult.VERIFIED_OK){
686 						constraintViolated(o, "Argument type class/interface could not be verified successfully: '"+vr.getMessage()+"'.");
687 					}
688 				}
689 			}
690 			
691 		}
692 		
693 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
694 		@Override
695         public void visitINSTANCEOF(INSTANCEOF o){
696 			indexValid(o, o.getIndex());
697 			Constant c = cpg.getConstant(o.getIndex());
698 			if (!	(c instanceof ConstantClass)){
699 				constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '"+c+"'.");
700 			}
701 		}
702 
703 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
704 		@Override
705         public void visitCHECKCAST(CHECKCAST o){
706 			indexValid(o, o.getIndex());
707 			Constant c = cpg.getConstant(o.getIndex());
708 			if (!	(c instanceof ConstantClass)){
709 				constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '"+c+"'.");
710 			}
711 		}
712 
713 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
714 		@Override
715         public void visitNEW(NEW o){
716 			indexValid(o, o.getIndex());
717 			Constant c = cpg.getConstant(o.getIndex());
718 			if (!	(c instanceof ConstantClass)){
719 				constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '"+c+"'.");
720 			}
721 			else{
722 				ConstantUtf8 cutf8 = (ConstantUtf8) (cpg.getConstant( ((ConstantClass) c).getNameIndex() ));
723 				Type t = Type.getType("L"+cutf8.getBytes()+";");
724 				if (t instanceof ArrayType){
725 					constraintViolated(o, "NEW must not be used to create an array.");
726 				}
727 			}
728 			
729 		}
730 
731 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
732 		@Override
733         public void visitMULTIANEWARRAY(MULTIANEWARRAY o){
734 			indexValid(o, o.getIndex());
735 			Constant c = cpg.getConstant(o.getIndex());
736 			if (!	(c instanceof ConstantClass)){
737 				constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '"+c+"'.");
738 			}
739 			int dimensions2create = o.getDimensions();
740 			if (dimensions2create < 1){
741 				constraintViolated(o, "Number of dimensions to create must be greater than zero.");
742 			}
743 			Type t = o.getType(cpg);
744 			if (t instanceof ArrayType){
745 				int dimensions = ((ArrayType) t).getDimensions();
746 				if (dimensions < dimensions2create){
747 					constraintViolated(o, "Not allowed to create array with more dimensions ('+dimensions2create+') than the one referenced by the CONSTANT_Class '"+t+"'.");
748 				}
749 			}
750 			else{
751 				constraintViolated(o, "Expecting a CONSTANT_Class referencing an array type. [Constraint not found in The Java Virtual Machine Specification, Second Edition, 4.8.1]");
752 			}
753 		}
754 
755 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
756 		@Override
757         public void visitANEWARRAY(ANEWARRAY o){
758 			indexValid(o, o.getIndex());
759 			Constant c = cpg.getConstant(o.getIndex());
760 			if (!	(c instanceof ConstantClass)){
761 				constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '"+c+"'.");
762 			}
763 			Type t = o.getType(cpg);
764 			if (t instanceof ArrayType){
765 				int dimensions = ((ArrayType) t).getDimensions();
766 				if (dimensions >= 255){
767 					constraintViolated(o, "Not allowed to create an array with more than 255 dimensions.");
768 				}
769 			}
770 		}
771 
772 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
773 		@Override
774         public void visitNEWARRAY(NEWARRAY o){
775 			byte t = o.getTypecode();
776 			if (!	(	(t == Constants.T_BOOLEAN)	||
777 							(t == Constants.T_CHAR)			||
778 							(t == Constants.T_FLOAT)		||
779 							(t == Constants.T_DOUBLE)		||
780 							(t == Constants.T_BYTE)			||
781 							(t == Constants.T_SHORT)		||
782 							(t == Constants.T_INT)			||
783 							(t == Constants.T_LONG)	)	){
784 				constraintViolated(o, "Illegal type code '+t+' for 'atype' operand.");
785 			}
786 		}
787 
788 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
789 		@Override
790         public void visitILOAD(ILOAD o){
791 			int idx = o.getIndex();
792 			if (idx < 0){
793 				constraintViolated(o, "Index '"+idx+"' must be non-negative.");
794 			}
795 			else{
796 				int maxminus1 =  max_locals()-1;
797 				if (idx > maxminus1){
798 					constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
799 				}
800 			}
801 		}
802 
803 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
804 		@Override
805         public void visitFLOAD(FLOAD o){
806 			int idx = o.getIndex();
807 			if (idx < 0){
808 				constraintViolated(o, "Index '"+idx+"' must be non-negative.");
809 			}
810 			else{
811 				int maxminus1 =  max_locals()-1;
812 				if (idx > maxminus1){
813 					constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
814 				}
815 			}
816 		}
817 
818 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
819 		@Override
820         public void visitALOAD(ALOAD o){
821 			int idx = o.getIndex();
822 			if (idx < 0){
823 				constraintViolated(o, "Index '"+idx+"' must be non-negative.");
824 			}
825 			else{
826 				int maxminus1 =  max_locals()-1;
827 				if (idx > maxminus1){
828 					constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
829 				}
830 			}
831 		}
832 		
833 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
834 		@Override
835         public void visitISTORE(ISTORE o){
836 			int idx = o.getIndex();
837 			if (idx < 0){
838 				constraintViolated(o, "Index '"+idx+"' must be non-negative.");
839 			}
840 			else{
841 				int maxminus1 =  max_locals()-1;
842 				if (idx > maxminus1){
843 					constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
844 				}
845 			}
846 		}
847 		
848 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
849 		@Override
850         public void visitFSTORE(FSTORE o){
851 			int idx = o.getIndex();
852 			if (idx < 0){
853 				constraintViolated(o, "Index '"+idx+"' must be non-negative.");
854 			}
855 			else{
856 				int maxminus1 =  max_locals()-1;
857 				if (idx > maxminus1){
858 					constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
859 				}
860 			}
861 		}
862 
863 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
864 		@Override
865         public void visitASTORE(ASTORE o){
866 			int idx = o.getIndex();
867 			if (idx < 0){
868 				constraintViolated(o, "Index '"+idx+"' must be non-negative.");
869 			}
870 			else{
871 				int maxminus1 =  max_locals()-1;
872 				if (idx > maxminus1){
873 					constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
874 				}
875 			}
876 		}
877 
878 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
879 		@Override
880         public void visitIINC(IINC o){
881 			int idx = o.getIndex();
882 			if (idx < 0){
883 				constraintViolated(o, "Index '"+idx+"' must be non-negative.");
884 			}
885 			else{
886 				int maxminus1 =  max_locals()-1;
887 				if (idx > maxminus1){
888 					constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
889 				}
890 			}
891 		}
892 
893 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
894 		@Override
895         public void visitRET(RET o){
896 			int idx = o.getIndex();
897 			if (idx < 0){
898 				constraintViolated(o, "Index '"+idx+"' must be non-negative.");
899 			}
900 			else{
901 				int maxminus1 =  max_locals()-1;
902 				if (idx > maxminus1){
903 					constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
904 				}
905 			}
906 		}
907 
908 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
909 		@Override
910         public void visitLLOAD(LLOAD o){
911 			int idx = o.getIndex();
912 			if (idx < 0){
913 				constraintViolated(o, "Index '"+idx+"' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
914 			}
915 			else{
916 				int maxminus2 =  max_locals()-2;
917 				if (idx > maxminus2){
918 					constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'.");
919 				}
920 			}
921 		}
922 		
923 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
924 		@Override
925         public void visitDLOAD(DLOAD o){
926 			int idx = o.getIndex();
927 			if (idx < 0){
928 				constraintViolated(o, "Index '"+idx+"' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
929 			}
930 			else{
931 				int maxminus2 =  max_locals()-2;
932 				if (idx > maxminus2){
933 					constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'.");
934 				}
935 			}
936 		}
937 		
938 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
939 		@Override
940         public void visitLSTORE(LSTORE o){
941 			int idx = o.getIndex();
942 			if (idx < 0){
943 				constraintViolated(o, "Index '"+idx+"' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
944 			}
945 			else{
946 				int maxminus2 =  max_locals()-2;
947 				if (idx > maxminus2){
948 					constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'.");
949 				}
950 			}
951 		}
952 		
953 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
954 		@Override
955         public void visitDSTORE(DSTORE o){
956 			int idx = o.getIndex();
957 			if (idx < 0){
958 				constraintViolated(o, "Index '"+idx+"' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
959 			}
960 			else{
961 				int maxminus2 =  max_locals()-2;
962 				if (idx > maxminus2){
963 					constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'.");
964 				}
965 			}
966 		}
967 
968 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
969 		@Override
970         public void visitLOOKUPSWITCH(LOOKUPSWITCH o){
971 			int[] matchs = o.getMatchs();
972 			int max = Integer.MIN_VALUE;
973 			for (int i=0; i<matchs.length; i++){
974 				if (matchs[i] == max && i != 0){
975 					constraintViolated(o, "Match '"+matchs[i]+"' occurs more than once.");
976 				}
977 				if (matchs[i] < max){
978 					constraintViolated(o, "Lookup table must be sorted but isn't.");
979 				}
980 				else{
981 					max = matchs[i];
982 				}
983 			}
984 		}
985 
986 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
987 		@Override
988         public void visitTABLESWITCH(TABLESWITCH o){ 	
989 			// "high" must be >= "low". We cannot check this, as BCEL hides
990 			// it from us.
991 		}
992 
993 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
994 		@Override
995         public void visitPUTSTATIC(PUTSTATIC o){
996 		    try {
997 			String field_name = o.getFieldName(cpg);
998 			JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName());
999 			Field[] fields = jc.getFields();
1000 			Field f = null;
1001 			for (Field field : fields) {
1002 				if (field.getName().equals(field_name)){
1003 					f = field;
1004 					break;
1005 				}
1006 			}
1007 			if (f == null){
1008 				throw new AssertionViolatedException("Field not found?!?");
1009 			}
1010 
1011 			if (f.isFinal()){
1012 				if (!(myOwner.getClassName().equals(o.getClassType(cpg).getClassName()))){
1013 					constraintViolated(o, "Referenced field '"+f+"' is final and must therefore be declared in the current class '"+myOwner.getClassName()+"' which is not the case: it is declared in '"+o.getClassType(cpg).getClassName()+"'.");
1014 				}
1015 			}
1016 
1017 			if (! (f.isStatic())){
1018 				constraintViolated(o, "Referenced field '"+f+"' is not static which it should be.");
1019 			}
1020 
1021 			String meth_name = Repository.lookupClass(myOwner.getClassName()).getMethods()[method_no].getName();
1022 
1023 			// If it's an interface, it can be set only in <clinit>.
1024 			if ((!(jc.isClass())) && (!(meth_name.equals(Constants.STATIC_INITIALIZER_NAME)))){
1025 				constraintViolated(o, "Interface field '"+f+"' must be set in a '"+Constants.STATIC_INITIALIZER_NAME+"' method.");
1026 			}
1027 		    } catch (ClassNotFoundException e) {
1028 			// FIXME: maybe not the best way to handle this
1029 			throw new AssertionViolatedException("Missing class: " + e, e);
1030 		    }
1031 		}
1032 
1033 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
1034 		@Override
1035         public void visitGETSTATIC(GETSTATIC o){
1036 		    try {
1037 			String field_name = o.getFieldName(cpg);
1038 			JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName());
1039 			Field[] fields = jc.getFields();
1040 			Field f = null;
1041 			for (Field field : fields) {
1042 				if (field.getName().equals(field_name)){
1043 					f = field;
1044 					break;
1045 				}
1046 			}
1047 			if (f == null){
1048 				throw new AssertionViolatedException("Field not found?!?");
1049 			}
1050 
1051 			if (! (f.isStatic())){
1052 				constraintViolated(o, "Referenced field '"+f+"' is not static which it should be.");
1053 			}
1054 		    } catch (ClassNotFoundException e) {
1055 			// FIXME: maybe not the best way to handle this
1056 			throw new AssertionViolatedException("Missing class: " + e, e);
1057 		    }
1058 		}
1059 
1060 		/* Checks if the constraints of operands of the said instruction(s) are satisfied. */
1061 		//public void visitPUTFIELD(PUTFIELD o){
1062 			// for performance reasons done in Pass 3b
1063 		//}
1064 		
1065 		/* Checks if the constraints of operands of the said instruction(s) are satisfied. */
1066 		//public void visitGETFIELD(GETFIELD o){
1067 			// for performance reasons done in Pass 3b
1068 		//}
1069 
1070 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
1071 		@Override
1072         public void visitINVOKEINTERFACE(INVOKEINTERFACE o){
1073 		    try {
1074 			// INVOKEINTERFACE is a LoadClass; the Class where the referenced method is declared in,
1075 			// is therefore resolved/verified.
1076 			// INVOKEINTERFACE is an InvokeInstruction, the argument and return types are resolved/verified,
1077 			// too. So are the allowed method names.
1078 			String classname = o.getClassName(cpg);
1079 			JavaClass jc = Repository.lookupClass(classname);
1080 			Method[] ms = jc.getMethods();
1081 			Method m = null;
1082 			for (Method element : ms) {
1083 				if ( (element.getName().equals(o.getMethodName(cpg))) &&
1084 				     (Type.getReturnType(element.getSignature()).equals(o.getReturnType(cpg))) &&
1085 				     (objarrayequals(Type.getArgumentTypes(element.getSignature()), o.getArgumentTypes(cpg))) ){
1086 					m = element;
1087 					break;
1088 				}
1089 			}
1090 			if (m == null){
1091 				constraintViolated(o, "Referenced method '"+o.getMethodName(cpg)+"' with expected signature '"+o.getSignature(cpg)+"' not found in class '"+jc.getClassName()+"'. The native verifier does allow the method to be declared in some superinterface, which the Java Virtual Machine Specification, Second Edition does not.");
1092 			}
1093 			if (jc.isClass()){
1094 				constraintViolated(o, "Referenced class '"+jc.getClassName()+"' is a class, but not an interface as expected.");
1095 			}
1096 		    } catch (ClassNotFoundException e) {
1097 			// FIXME: maybe not the best way to handle this
1098 			throw new AssertionViolatedException("Missing class: " + e, e);
1099 		    }
1100 		}
1101 
1102 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
1103 		@Override
1104         public void visitINVOKESPECIAL(INVOKESPECIAL o){
1105 		    try {
1106 			// INVOKESPECIAL is a LoadClass; the Class where the referenced method is declared in,
1107 			// is therefore resolved/verified.
1108 			// INVOKESPECIAL is an InvokeInstruction, the argument and return types are resolved/verified,
1109 			// too. So are the allowed method names.
1110 			String classname = o.getClassName(cpg);
1111 			JavaClass jc = Repository.lookupClass(classname);
1112 			Method[] ms = jc.getMethods();
1113 			Method m = null;
1114 			for (Method element : ms) {
1115 				if ( (element.getName().equals(o.getMethodName(cpg))) &&
1116 				     (Type.getReturnType(element.getSignature()).equals(o.getReturnType(cpg))) &&
1117 				     (objarrayequals(Type.getArgumentTypes(element.getSignature()), o.getArgumentTypes(cpg))) ){
1118 					m = element;
1119 					break;
1120 				}
1121 			}
1122 			if (m == null){
1123 				constraintViolated(o, "Referenced method '"+o.getMethodName(cpg)+"' with expected signature '"+o.getSignature(cpg)+"' not found in class '"+jc.getClassName()+"'. The native verifier does allow the method to be declared in some superclass or implemented interface, which the Java Virtual Machine Specification, Second Edition does not.");
1124 			}
1125 			
1126 			JavaClass current = Repository.lookupClass(myOwner.getClassName());
1127 			if (current.isSuper()){
1128 			
1129 				if ((Repository.instanceOf( current, jc )) && (!current.equals(jc))){
1130 					
1131 					if (! (o.getMethodName(cpg).equals(Constants.CONSTRUCTOR_NAME) )){
1132 						// Special lookup procedure for ACC_SUPER classes.
1133 						
1134 						int supidx = -1;
1135 						
1136 						Method meth = null;
1137 						while (supidx != 0){
1138 							supidx = current.getSuperclassNameIndex();
1139 							current = Repository.lookupClass(current.getSuperclassName());
1140 							
1141 							Method[] meths = current.getMethods();
1142 							for (Method meth2 : meths) {
1143 								if	( (meth2.getName().equals(o.getMethodName(cpg))) &&
1144 				     				(Type.getReturnType(meth2.getSignature()).equals(o.getReturnType(cpg))) &&
1145 				     				(objarrayequals(Type.getArgumentTypes(meth2.getSignature()), o.getArgumentTypes(cpg))) ){
1146 									meth = meth2;
1147 									break;
1148 								}
1149 							}
1150 							if (meth != null) {
1151                                 break;
1152                             }
1153 						}
1154 						if (meth == null){
1155 							constraintViolated(o, "ACC_SUPER special lookup procedure not successful: method '"+o.getMethodName(cpg)+"' with proper signature not declared in superclass hierarchy.");
1156 						}						
1157 					}
1158 				}
1159 			}
1160 			
1161 		    } catch (ClassNotFoundException e) {
1162 			// FIXME: maybe not the best way to handle this
1163 			throw new AssertionViolatedException("Missing class: " + e, e);
1164 		    }
1165 			
1166 		}
1167 		
1168 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
1169 		@Override
1170         public void visitINVOKESTATIC(INVOKESTATIC o){
1171 		    try {
1172 			// INVOKESTATIC is a LoadClass; the Class where the referenced method is declared in,
1173 			// is therefore resolved/verified.
1174 			// INVOKESTATIC is an InvokeInstruction, the argument and return types are resolved/verified,
1175 			// too. So are the allowed method names.
1176 			String classname = o.getClassName(cpg);
1177 			JavaClass jc = Repository.lookupClass(classname);
1178 			Method[] ms = jc.getMethods();
1179 			Method m = null;
1180 			for (Method element : ms) {
1181 				if ( (element.getName().equals(o.getMethodName(cpg))) &&
1182 				     (Type.getReturnType(element.getSignature()).equals(o.getReturnType(cpg))) &&
1183 				     (objarrayequals(Type.getArgumentTypes(element.getSignature()), o.getArgumentTypes(cpg))) ){
1184 					m = element;
1185 					break;
1186 				}
1187 			}
1188 			if (m == null){
1189 				constraintViolated(o, "Referenced method '"+o.getMethodName(cpg)+"' with expected signature '"+o.getSignature(cpg) +"' not found in class '"+jc.getClassName()+"'. The native verifier possibly allows the method to be declared in some superclass or implemented interface, which the Java Virtual Machine Specification, Second Edition does not.");
1190 			} else if (! (m.isStatic())){ // implies it's not abstract, verified in pass 2.
1191 				constraintViolated(o, "Referenced method '"+o.getMethodName(cpg)+"' has ACC_STATIC unset.");
1192 			}
1193 		
1194 		    } catch (ClassNotFoundException e) {
1195 			// FIXME: maybe not the best way to handle this
1196 			throw new AssertionViolatedException("Missing class: " + e, e);
1197 		    }
1198 		}
1199 
1200 
1201 		/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
1202 		@Override
1203         public void visitINVOKEVIRTUAL(INVOKEVIRTUAL o){
1204 		    try {
1205 			// INVOKEVIRTUAL is a LoadClass; the Class where the referenced method is declared in,
1206 			// is therefore resolved/verified.
1207 			// INVOKEVIRTUAL is an InvokeInstruction, the argument and return types are resolved/verified,
1208 			// too. So are the allowed method names.
1209 			String classname = o.getClassName(cpg);
1210 			JavaClass jc = Repository.lookupClass(classname);
1211 			Method[] ms = jc.getMethods();
1212 			Method m = null;
1213 			for (Method element : ms) {
1214 				if ( (element.getName().equals(o.getMethodName(cpg))) &&
1215 				     (Type.getReturnType(element.getSignature()).equals(o.getReturnType(cpg))) &&
1216 				     (objarrayequals(Type.getArgumentTypes(element.getSignature()), o.getArgumentTypes(cpg))) ){
1217 					m = element;
1218 					break;
1219 				}
1220 			}
1221 			if (m == null){
1222 				constraintViolated(o, "Referenced method '"+o.getMethodName(cpg)+"' with expected signature '"+o.getSignature(cpg)+"' not found in class '"+jc.getClassName()+"'. The native verifier does allow the method to be declared in some superclass or implemented interface, which the Java Virtual Machine Specification, Second Edition does not.");
1223 			}
1224 			if (! (jc.isClass())){
1225 				constraintViolated(o, "Referenced class '"+jc.getClassName()+"' is an interface, but not a class as expected.");
1226 			}
1227 					
1228 		    } catch (ClassNotFoundException e) {
1229 			// FIXME: maybe not the best way to handle this
1230 			throw new AssertionViolatedException("Missing class: " + e, e);
1231 		    }
1232 		}
1233 
1234 		
1235 		// WIDE stuff is BCEL-internal and cannot be checked here.
1236 
1237 		/**
1238 		 * A utility method like equals(Object) for arrays.
1239 		 * The equality of the elements is based on their equals(Object)
1240 		 * method instead of their object identity.
1241 		 */ 
1242 		private boolean objarrayequals(Object[] o, Object[] p){
1243 			if (o.length != p.length){
1244 				return false;
1245 			}
1246 			
1247 			for (int i=0; i<o.length; i++){
1248 				if (! (o[i].equals(p[i])) ){
1249 					return false;
1250 				}
1251 			}
1252 			
1253 			return true;
1254 		}
1255 
1256 	}
1257 }