Home | History | Annotate | Download | only in statics
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  *
     17  */
     18 package org.apache.bcel.verifier.statics;
     19 
     20 
     21 import org.apache.bcel.Const;
     22 import org.apache.bcel.Repository;
     23 import org.apache.bcel.classfile.Attribute;
     24 import org.apache.bcel.classfile.Code;
     25 import org.apache.bcel.classfile.CodeException;
     26 import org.apache.bcel.classfile.Constant;
     27 import org.apache.bcel.classfile.ConstantClass;
     28 import org.apache.bcel.classfile.ConstantDouble;
     29 import org.apache.bcel.classfile.ConstantFieldref;
     30 import org.apache.bcel.classfile.ConstantFloat;
     31 import org.apache.bcel.classfile.ConstantInteger;
     32 import org.apache.bcel.classfile.ConstantInterfaceMethodref;
     33 import org.apache.bcel.classfile.ConstantLong;
     34 import org.apache.bcel.classfile.ConstantMethodref;
     35 import org.apache.bcel.classfile.ConstantNameAndType;
     36 import org.apache.bcel.classfile.ConstantString;
     37 import org.apache.bcel.classfile.ConstantUtf8;
     38 import org.apache.bcel.classfile.Field;
     39 import org.apache.bcel.classfile.JavaClass;
     40 import org.apache.bcel.classfile.LineNumber;
     41 import org.apache.bcel.classfile.LineNumberTable;
     42 import org.apache.bcel.classfile.LocalVariable;
     43 import org.apache.bcel.classfile.LocalVariableTable;
     44 import org.apache.bcel.classfile.Method;
     45 import org.apache.bcel.generic.ALOAD;
     46 import org.apache.bcel.generic.ANEWARRAY;
     47 import org.apache.bcel.generic.ASTORE;
     48 import org.apache.bcel.generic.ATHROW;
     49 import org.apache.bcel.generic.ArrayType;
     50 import org.apache.bcel.generic.BREAKPOINT;
     51 import org.apache.bcel.generic.CHECKCAST;
     52 import org.apache.bcel.generic.ConstantPoolGen;
     53 import org.apache.bcel.generic.DLOAD;
     54 import org.apache.bcel.generic.DSTORE;
     55 import org.apache.bcel.generic.FLOAD;
     56 import org.apache.bcel.generic.FSTORE;
     57 import org.apache.bcel.generic.FieldInstruction;
     58 import org.apache.bcel.generic.GETSTATIC;
     59 import org.apache.bcel.generic.GotoInstruction;
     60 import org.apache.bcel.generic.IINC;
     61 import org.apache.bcel.generic.ILOAD;
     62 import org.apache.bcel.generic.IMPDEP1;
     63 import org.apache.bcel.generic.IMPDEP2;
     64 import org.apache.bcel.generic.INSTANCEOF;
     65 import org.apache.bcel.generic.INVOKEDYNAMIC;
     66 import org.apache.bcel.generic.INVOKEINTERFACE;
     67 import org.apache.bcel.generic.INVOKESPECIAL;
     68 import org.apache.bcel.generic.INVOKESTATIC;
     69 import org.apache.bcel.generic.INVOKEVIRTUAL;
     70 import org.apache.bcel.generic.ISTORE;
     71 import org.apache.bcel.generic.Instruction;
     72 import org.apache.bcel.generic.InstructionHandle;
     73 import org.apache.bcel.generic.InstructionList;
     74 import org.apache.bcel.generic.InvokeInstruction;
     75 import org.apache.bcel.generic.JsrInstruction;
     76 import org.apache.bcel.generic.LDC;
     77 import org.apache.bcel.generic.LDC2_W;
     78 import org.apache.bcel.generic.LLOAD;
     79 import org.apache.bcel.generic.LOOKUPSWITCH;
     80 import org.apache.bcel.generic.LSTORE;
     81 import org.apache.bcel.generic.LoadClass;
     82 import org.apache.bcel.generic.MULTIANEWARRAY;
     83 import org.apache.bcel.generic.NEW;
     84 import org.apache.bcel.generic.NEWARRAY;
     85 import org.apache.bcel.generic.ObjectType;
     86 import org.apache.bcel.generic.PUTSTATIC;
     87 import org.apache.bcel.generic.RET;
     88 import org.apache.bcel.generic.ReferenceType;
     89 import org.apache.bcel.generic.ReturnInstruction;
     90 import org.apache.bcel.generic.TABLESWITCH;
     91 import org.apache.bcel.generic.Type;
     92 import org.apache.bcel.verifier.PassVerifier;
     93 import org.apache.bcel.verifier.VerificationResult;
     94 import org.apache.bcel.verifier.Verifier;
     95 import org.apache.bcel.verifier.VerifierFactory;
     96 import org.apache.bcel.verifier.exc.AssertionViolatedException;
     97 import org.apache.bcel.verifier.exc.ClassConstraintException;
     98 import org.apache.bcel.verifier.exc.InvalidMethodException;
     99 import org.apache.bcel.verifier.exc.StaticCodeConstraintException;
    100 import org.apache.bcel.verifier.exc.StaticCodeInstructionConstraintException;
    101 import 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  * @version $Id$
    111  * @see #do_verify()
    112  */
    113 public final class Pass3aVerifier extends PassVerifier{
    114 
    115     /** The Verifier that created this. */
    116     private final Verifier myOwner;
    117 
    118     /**
    119      * The method number to verify.
    120      * This is the index in the array returned
    121      * by JavaClass.getMethods().
    122      */
    123     private final int method_no;
    124 
    125     /**
    126      * The one and only InstructionList object used by an instance of this class.
    127      * It's here for performance reasons by do_verify() and its callees.
    128      */
    129     private InstructionList instructionList;
    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 cpg;
    476 
    477         /** The only Constructor. */
    478         InstOperandConstraintVisitor(final ConstantPoolGen cpg) {
    479             this.cpg = cpg;
    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 >= cpg.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 o) {
    521             final ObjectType t = o.getLoadClassType(cpg);
    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) o,
    527                         "Class '"+o.getLoadClassType(cpg).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 o) {
    543             indexValid(o, o.getIndex());
    544             final Constant c = cpg.getConstant(o.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(o,
    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 = cpg.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(cpg);
    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 = cpg.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(cpg);
    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(cpg);
    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(cpg);
    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(cpg);
    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 = cpg.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) (cpg.getConstant(((ConstantMethodref) c).getNameAndTypeIndex()));
    674                     final ConstantUtf8 cutf8 = (ConstantUtf8) (cpg.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 = cpg.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) (cpg.getConstant(((ConstantInterfaceMethodref)c).getNameAndTypeIndex()));
    698                 final String name = ((ConstantUtf8) (cpg.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(cpg);
    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(cpg);
    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 = cpg.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 = cpg.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 = cpg.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) (cpg.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 = cpg.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(cpg);
    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 = cpg.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(cpg);
    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(cpg);
   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(cpg)+"'.");
   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(cpg);
   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(cpg);
   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(cpg)+"' with expected signature '"+o.getSignature(cpg)+
   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(cpg))) &&
   1201                      (Type.getReturnType(element.getSignature()).equals(invoke.getReturnType(cpg))) &&
   1202                      (objarrayequals(Type.getArgumentTypes(element.getSignature()), invoke.getArgumentTypes(cpg))) ) {
   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(cpg);
   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(cpg)+"' with expected signature '"+o.getSignature(cpg)
   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(cpg).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(cpg))) &&
   1244                                      (Type.getReturnType(meth2.getSignature()).equals(o.getReturnType(cpg))) &&
   1245                                      (objarrayequals(Type.getArgumentTypes(meth2.getSignature()), o.getArgumentTypes(cpg))) ) {
   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(cpg)+"' 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(cpg);
   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(cpg)+"' with expected signature '"+
   1282                     o.getSignature(cpg) +"' 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(cpg)+"' 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(cpg);
   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(cpg)+"' with expected signature '"+
   1307                     o.getSignature(cpg)+"' 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 }
   1343