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