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