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