Source code

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