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