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