    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 methodNo;
    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 methodNo) {
    138        myOwner = owner;
    139        this.methodNo = methodNo;
    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 (methodNo >= methods.length) {
    168                throw new InvalidMethodException("METHOD DOES NOT EXIST!");
    169            }
    170            final Method method = methods[methodNo];
    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 methodNo;
    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()[methodNo].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("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()[methodNo].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 UnsupportedOperationException("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}