001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License. 
016     *
017     */ 
018    package org.apache.bcel.verifier.statics;
019    
020    
021    import org.apache.bcel.Constants;
022    import org.apache.bcel.Repository;
023    import org.apache.bcel.classfile.Attribute;
024    import org.apache.bcel.classfile.Code;
025    import org.apache.bcel.classfile.CodeException;
026    import org.apache.bcel.classfile.Constant;
027    import org.apache.bcel.classfile.ConstantClass;
028    import org.apache.bcel.classfile.ConstantDouble;
029    import org.apache.bcel.classfile.ConstantFieldref;
030    import org.apache.bcel.classfile.ConstantFloat;
031    import org.apache.bcel.classfile.ConstantInteger;
032    import org.apache.bcel.classfile.ConstantInterfaceMethodref;
033    import org.apache.bcel.classfile.ConstantLong;
034    import org.apache.bcel.classfile.ConstantMethodref;
035    import org.apache.bcel.classfile.ConstantNameAndType;
036    import org.apache.bcel.classfile.ConstantString;
037    import org.apache.bcel.classfile.ConstantUtf8;
038    import org.apache.bcel.classfile.Field;
039    import org.apache.bcel.classfile.JavaClass;
040    import org.apache.bcel.classfile.LineNumber;
041    import org.apache.bcel.classfile.LineNumberTable;
042    import org.apache.bcel.classfile.LocalVariable;
043    import org.apache.bcel.classfile.LocalVariableTable;
044    import org.apache.bcel.classfile.Method;
045    import org.apache.bcel.generic.ALOAD;
046    import org.apache.bcel.generic.ANEWARRAY;
047    import org.apache.bcel.generic.ASTORE;
048    import org.apache.bcel.generic.ATHROW;
049    import org.apache.bcel.generic.ArrayType;
050    import org.apache.bcel.generic.BREAKPOINT;
051    import org.apache.bcel.generic.CHECKCAST;
052    import org.apache.bcel.generic.ConstantPoolGen;
053    import org.apache.bcel.generic.DLOAD;
054    import org.apache.bcel.generic.DSTORE;
055    import org.apache.bcel.generic.FLOAD;
056    import org.apache.bcel.generic.FSTORE;
057    import org.apache.bcel.generic.FieldInstruction;
058    import org.apache.bcel.generic.GETSTATIC;
059    import org.apache.bcel.generic.GotoInstruction;
060    import org.apache.bcel.generic.IINC;
061    import org.apache.bcel.generic.ILOAD;
062    import org.apache.bcel.generic.IMPDEP1;
063    import org.apache.bcel.generic.IMPDEP2;
064    import org.apache.bcel.generic.INSTANCEOF;
065    import org.apache.bcel.generic.INVOKEINTERFACE;
066    import org.apache.bcel.generic.INVOKESPECIAL;
067    import org.apache.bcel.generic.INVOKESTATIC;
068    import org.apache.bcel.generic.INVOKEVIRTUAL;
069    import org.apache.bcel.generic.ISTORE;
070    import org.apache.bcel.generic.Instruction;
071    import org.apache.bcel.generic.InstructionHandle;
072    import org.apache.bcel.generic.InstructionList;
073    import org.apache.bcel.generic.InvokeInstruction;
074    import org.apache.bcel.generic.JsrInstruction;
075    import org.apache.bcel.generic.LDC;
076    import org.apache.bcel.generic.LDC2_W;
077    import org.apache.bcel.generic.LLOAD;
078    import org.apache.bcel.generic.LOOKUPSWITCH;
079    import org.apache.bcel.generic.LSTORE;
080    import org.apache.bcel.generic.LoadClass;
081    import org.apache.bcel.generic.MULTIANEWARRAY;
082    import org.apache.bcel.generic.NEW;
083    import org.apache.bcel.generic.NEWARRAY;
084    import org.apache.bcel.generic.ObjectType;
085    import org.apache.bcel.generic.PUTSTATIC;
086    import org.apache.bcel.generic.RET;
087    import org.apache.bcel.generic.ReturnInstruction;
088    import org.apache.bcel.generic.TABLESWITCH;
089    import org.apache.bcel.generic.Type;
090    import org.apache.bcel.verifier.PassVerifier;
091    import org.apache.bcel.verifier.VerificationResult;
092    import org.apache.bcel.verifier.Verifier;
093    import org.apache.bcel.verifier.VerifierFactory;
094    import org.apache.bcel.verifier.exc.AssertionViolatedException;
095    import org.apache.bcel.verifier.exc.ClassConstraintException;
096    import org.apache.bcel.verifier.exc.InvalidMethodException;
097    import org.apache.bcel.verifier.exc.StaticCodeConstraintException;
098    import org.apache.bcel.verifier.exc.StaticCodeInstructionConstraintException;
099    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.java 1152072 2011-07-29 01:54:05Z dbrosius $
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.toString(), 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 (int a=0; a<atts.length; a++){
265                            if (atts[a] instanceof LocalVariableTable){
266                                    LocalVariableTable lvt = (LocalVariableTable) atts[a];
267                                    if (lvt != null){
268                                            LocalVariable[] localVariables = lvt.getLocalVariableTable();
269                                            for (int i=0; i<localVariables.length; i++){
270                                                    int startpc = localVariables[i].getStartPC();
271                                                    int length  = localVariables[i].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 (int i=0; i<exceptionTable.length; i++){
292                            int startpc = exceptionTable[i].getStartPC();
293                            int endpc = exceptionTable[i].getEndPC();
294                            int handlerpc = exceptionTable[i].getHandlerPC();
295                            if (startpc >= endpc){
296                                    throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+exceptionTable[i]+"' 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 '"+exceptionTable[i]+"' 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 '"+exceptionTable[i]+"' 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 '"+exceptionTable[i]+"' 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.toString(), 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 j=0; j<ints.length; j++){
427                            if (ints[j]==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.toString(), 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 (int i=0; i<fields.length; i++){
561                                    if (fields[i].getName().equals(field_name)){
562                                      Type f_type = Type.getType(fields[i].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 = fields[i];
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.toString(), 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 (int i=0; i<ts.length; i++){
678                                    t = ts[i];
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 (int i=0; i<fields.length; i++){
1002                                    if (fields[i].getName().equals(field_name)){
1003                                            f = fields[i];
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.toString(), 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 (int i=0; i<fields.length; i++){
1042                                    if (fields[i].getName().equals(field_name)){
1043                                            f = fields[i];
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.toString(), 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 (int i=0; i<ms.length; i++){
1083                                    if ( (ms[i].getName().equals(o.getMethodName(cpg))) &&
1084                                         (Type.getReturnType(ms[i].getSignature()).equals(o.getReturnType(cpg))) &&
1085                                         (objarrayequals(Type.getArgumentTypes(ms[i].getSignature()), o.getArgumentTypes(cpg))) ){
1086                                            m = ms[i];
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.toString(), 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 (int i=0; i<ms.length; i++){
1115                                    if ( (ms[i].getName().equals(o.getMethodName(cpg))) &&
1116                                         (Type.getReturnType(ms[i].getSignature()).equals(o.getReturnType(cpg))) &&
1117                                         (objarrayequals(Type.getArgumentTypes(ms[i].getSignature()), o.getArgumentTypes(cpg))) ){
1118                                            m = ms[i];
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 (int i=0; i<meths.length; i++){
1143                                                                    if      ( (meths[i].getName().equals(o.getMethodName(cpg))) &&
1144                                                                    (Type.getReturnType(meths[i].getSignature()).equals(o.getReturnType(cpg))) &&
1145                                                                    (objarrayequals(Type.getArgumentTypes(meths[i].getSignature()), o.getArgumentTypes(cpg))) ){
1146                                                                            meth = meths[i];
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.toString(), 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 (int i=0; i<ms.length; i++){
1181                                    if ( (ms[i].getName().equals(o.getMethodName(cpg))) &&
1182                                         (Type.getReturnType(ms[i].getSignature()).equals(o.getReturnType(cpg))) &&
1183                                         (objarrayequals(Type.getArgumentTypes(ms[i].getSignature()), o.getArgumentTypes(cpg))) ){
1184                                            m = ms[i];
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.toString(), 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 (int i=0; i<ms.length; i++){
1214                                    if ( (ms[i].getName().equals(o.getMethodName(cpg))) &&
1215                                         (Type.getReturnType(ms[i].getSignature()).equals(o.getReturnType(cpg))) &&
1216                                         (objarrayequals(Type.getArgumentTypes(ms[i].getSignature()), o.getArgumentTypes(cpg))) ){
1217                                            m = ms[i];
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.toString(), 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    }