<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.apache.bcel.verifier.statics;

import org.apache.bcel.Const;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.ClassFormatException;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.CodeException;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.ConstantDouble;
import org.apache.bcel.classfile.ConstantFieldref;
import org.apache.bcel.classfile.ConstantFloat;
import org.apache.bcel.classfile.ConstantInteger;
import org.apache.bcel.classfile.ConstantInterfaceMethodref;
import org.apache.bcel.classfile.ConstantLong;
import org.apache.bcel.classfile.ConstantMethodref;
import org.apache.bcel.classfile.ConstantNameAndType;
import org.apache.bcel.classfile.ConstantString;
import org.apache.bcel.classfile.ConstantUtf8;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.LineNumber;
import org.apache.bcel.classfile.LineNumberTable;
import org.apache.bcel.classfile.LocalVariableTable;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ALOAD;
import org.apache.bcel.generic.ANEWARRAY;
import org.apache.bcel.generic.ASTORE;
import org.apache.bcel.generic.ATHROW;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.BREAKPOINT;
import org.apache.bcel.generic.CHECKCAST;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.DLOAD;
import org.apache.bcel.generic.DSTORE;
import org.apache.bcel.generic.FLOAD;
import org.apache.bcel.generic.FSTORE;
import org.apache.bcel.generic.FieldInstruction;
import org.apache.bcel.generic.GETSTATIC;
import org.apache.bcel.generic.GotoInstruction;
import org.apache.bcel.generic.IINC;
import org.apache.bcel.generic.ILOAD;
import org.apache.bcel.generic.IMPDEP1;
import org.apache.bcel.generic.IMPDEP2;
import org.apache.bcel.generic.INSTANCEOF;
import org.apache.bcel.generic.INVOKEDYNAMIC;
import org.apache.bcel.generic.INVOKEINTERFACE;
import org.apache.bcel.generic.INVOKESPECIAL;
import org.apache.bcel.generic.INVOKESTATIC;
import org.apache.bcel.generic.INVOKEVIRTUAL;
import org.apache.bcel.generic.ISTORE;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.JsrInstruction;
import org.apache.bcel.generic.LDC;
import org.apache.bcel.generic.LDC2_W;
import org.apache.bcel.generic.LLOAD;
import org.apache.bcel.generic.LOOKUPSWITCH;
import org.apache.bcel.generic.LSTORE;
import org.apache.bcel.generic.LoadClass;
import org.apache.bcel.generic.MULTIANEWARRAY;
import org.apache.bcel.generic.NEW;
import org.apache.bcel.generic.NEWARRAY;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.PUTSTATIC;
import org.apache.bcel.generic.RET;
import org.apache.bcel.generic.ReferenceType;
import org.apache.bcel.generic.ReturnInstruction;
import org.apache.bcel.generic.TABLESWITCH;
import org.apache.bcel.generic.Type;
import org.apache.bcel.verifier.PassVerifier;
import org.apache.bcel.verifier.VerificationResult;
import org.apache.bcel.verifier.Verifier;
import org.apache.bcel.verifier.VerifierFactory;
import org.apache.bcel.verifier.exc.AssertionViolatedException;
import org.apache.bcel.verifier.exc.ClassConstraintException;
import org.apache.bcel.verifier.exc.InvalidMethodException;
import org.apache.bcel.verifier.exc.StaticCodeConstraintException;
import org.apache.bcel.verifier.exc.StaticCodeInstructionConstraintException;
import org.apache.bcel.verifier.exc.StaticCodeInstructionOperandConstraintException;

/**
 * This PassVerifier verifies a class file according to pass 3, static part as described in The Java Virtual Machine
 * Specification, 2nd edition. More detailed information is to be found at the do_verify() method's documentation.
 *
 * @see #do_verify()
 */
public final class Pass3aVerifier extends PassVerifier {

    /**
     * This visitor class does the actual checking for the instruction operand's constraints.
     */
    private class InstOperandConstraintVisitor extends org.apache.bcel.generic.EmptyVisitor {
        /** The ConstantPoolGen instance this Visitor operates on. */
        private final ConstantPoolGen constantPoolGen;

        /** The only Constructor. */
        InstOperandConstraintVisitor(final ConstantPoolGen constantPoolGen) {
            this.constantPoolGen = constantPoolGen;
        }

        /**
         * A utility method to always raise an exeption.
         */
        private void constraintViolated(final Instruction i, final String message) {
            throw new StaticCodeInstructionOperandConstraintException("Instruction " + tostring(i) + " constraint violated: " + message);
        }

        /**
         * Looks for the method referenced by the given invoke instruction in the given class.
         *
         * @param jc the class that defines the referenced method
         * @param invoke the instruction that references the method
         * @return the referenced method or null if not found.
         */
        private Method getMethod(final JavaClass jc, final InvokeInstruction invoke) {
            final Method[] ms = jc.getMethods();
            for (final Method element : ms) {
                if (element.getName().equals(invoke.getMethodName(constantPoolGen))
                    && Type.getReturnType(element.getSignature()).equals(invoke.getReturnType(constantPoolGen))
                    && objArrayEquals(Type.getArgumentTypes(element.getSignature()), invoke.getArgumentTypes(constantPoolGen))) {
                    return element;
                }
            }

            return null;
        }

        /**
         * Looks for the method referenced by the given invoke instruction in the given class or its super classes and super
         * interfaces.
         *
         * @param jc the class that defines the referenced method
         * @param invoke the instruction that references the method
         * @return the referenced method or null if not found.
         */
        private Method getMethodRecursive(final JavaClass jc, final InvokeInstruction invoke) throws ClassNotFoundException {
            Method m;
            // look in the given class
            m = getMethod(jc, invoke);
            if (m != null) {
                // method found in given class
                return m;
            }
            // method not found, look in super classes
            for (final JavaClass superclass : jc.getSuperClasses()) {
                m = getMethod(superclass, invoke);
                if (m != null) {
                    // method found in super class
                    return m;
                }
            }
            // method not found, look in super interfaces
            for (final JavaClass superclass : jc.getInterfaces()) {
                m = getMethod(superclass, invoke);
                if (m != null) {
                    // method found in super interface
                    return m;
                }
            }
            // method not found in the hierarchy
            return null;
        }

        private ObjectType getObjectType(final FieldInstruction o) {
            final ReferenceType rt = o.getReferenceType(constantPoolGen);
            if (rt instanceof ObjectType) {
                return (ObjectType) rt;
            }
            constraintViolated(o, "expecting ObjectType but got " + rt);
            return null;
        }

        // The target of each jump and branch instruction [...] must be the opcode [...]
        // BCEL _DOES_ handle this.

        // tableswitch: BCEL will do it, supposedly.

        // lookupswitch: BCEL will do it, supposedly.

        /**
         * A utility method to raise an exception if the index is not a valid constant pool index.
         */
        private void indexValid(final Instruction i, final int idx) {
            if (idx < 0 || idx >= constantPoolGen.getSize()) {
                constraintViolated(i, "Illegal constant pool index '" + idx + "'.");
            }
        }

        /**
         * Utility method to return the max_locals value of the method verified by the surrounding Pass3aVerifier instance.
         */
        private int maxLocals() {
            try {
                return Repository.lookupClass(myOwner.getClassName()).getMethods()[methodNo].getCode().getMaxLocals();
            } catch (final ClassNotFoundException e) {
                // FIXME: maybe not the best way to handle this
                throw new AssertionViolatedException("Missing class: " + e, e);
            }
        }

        /**
         * A utility method like equals(Object) for arrays. The equality of the elements is based on their equals(Object) method
         * instead of their object identity.
         */
        private boolean objArrayEquals(final Object[] o, final Object[] p) {
            if (o.length != p.length) {
                return false;
            }

            for (int i = 0; i < o.length; i++) {
                if (!o[i].equals(p[i])) {
                    return false;
                }
            }

            return true;
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitALOAD(final ALOAD o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative.");
            } else {
                final int maxminus1 = maxLocals() - 1;
                if (idx > maxminus1) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitANEWARRAY(final ANEWARRAY o) {
            indexValid(o, o.getIndex());
            final Constant c = constantPoolGen.getConstant(o.getIndex());
            if (!(c instanceof ConstantClass)) {
                constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
            }
            final Type t = o.getType(constantPoolGen);
            if (t instanceof ArrayType) {
                final int dimensions = ((ArrayType) t).getDimensions();
                if (dimensions > Const.MAX_ARRAY_DIMENSIONS) {
                    constraintViolated(o,
                        "Not allowed to create an array with more than " + Const.MAX_ARRAY_DIMENSIONS + " dimensions;" + " actual: " + dimensions);
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitASTORE(final ASTORE o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative.");
            } else {
                final int maxminus1 = maxLocals() - 1;
                if (idx > maxminus1) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitCHECKCAST(final CHECKCAST o) {
            indexValid(o, o.getIndex());
            final Constant c = constantPoolGen.getConstant(o.getIndex());
            if (!(c instanceof ConstantClass)) {
                constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitDLOAD(final DLOAD o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative."
                    + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
            } else {
                final int maxminus2 = maxLocals() - 2;
                if (idx > maxminus2) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitDSTORE(final DSTORE o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative."
                    + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
            } else {
                final int maxminus2 = maxLocals() - 2;
                if (idx > maxminus2) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        // getfield, putfield, getstatic, putstatic
        @Override
        public void visitFieldInstruction(final FieldInstruction o) {
            try {
                indexValid(o, o.getIndex());
                final Constant c = constantPoolGen.getConstant(o.getIndex());
                if (!(c instanceof ConstantFieldref)) {
                    constraintViolated(o, "Indexing a constant that's not a CONSTANT_Fieldref but a '" + tostring(c) + "'.");
                }

                final String fieldName = o.getFieldName(constantPoolGen);

                final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName());
                Field[] fields = jc.getFields();
                Field f = null;
                for (final Field field : fields) {
                    if (field.getName().equals(fieldName)) {
                        final Type fType = Type.getType(field.getSignature());
                        final Type oType = o.getType(constantPoolGen);
                        /*
                         * TODO: Check if assignment compatibility is sufficient. What does Sun do?
                         */
                        if (fType.equals(oType)) {
                            f = field;
                            break;
                        }
                    }
                }
                if (f == null) {
                    final JavaClass[] superclasses = jc.getSuperClasses();
                    outer: for (final JavaClass superclass : superclasses) {
                        fields = superclass.getFields();
                        for (final Field field : fields) {
                            if (field.getName().equals(fieldName)) {
                                final Type fType = Type.getType(field.getSignature());
                                final Type oType = o.getType(constantPoolGen);
                                if (fType.equals(oType)) {
                                    f = field;
                                    if ((f.getAccessFlags() & (Const.ACC_PUBLIC | Const.ACC_PROTECTED)) == 0) {
                                        f = null;
                                    }
                                    break outer;
                                }
                            }
                        }
                    }
                    if (f == null) {
                        constraintViolated(o, "Referenced field '" + fieldName + "' does not exist in class '" + jc.getClassName() + "'.");
                    }
                } else {
                    /*
                     * TODO: Check if assignment compatibility is sufficient. What does Sun do?
                     */
                    Type.getType(f.getSignature());
                    o.getType(constantPoolGen);
//                Type f_type = Type.getType(f.getSignature());
//                Type o_type = o.getType(cpg);

                    // Argh. Sun's implementation allows us to have multiple fields of
                    // the same name but with a different signature.
                    // if (! f_type.equals(o_type)) {
                    // constraintViolated(o,
                    // "Referenced field '"+field_name+"' has type '"+f_type+"' instead of '"+o_type+"' as expected.");
                    // }

                    /* TODO: Check for access modifiers here. */
                }
            } catch (final ClassNotFoundException e) {
                // FIXME: maybe not the best way to handle this
                throw new AssertionViolatedException("Missing class: " + e, e);
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitFLOAD(final FLOAD o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative.");
            } else {
                final int maxminus1 = maxLocals() - 1;
                if (idx > maxminus1) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitFSTORE(final FSTORE o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative.");
            } else {
                final int maxminus1 = maxLocals() - 1;
                if (idx > maxminus1) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitGETSTATIC(final GETSTATIC o) {
            try {
                final String fieldName = o.getFieldName(constantPoolGen);
                final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName());
                final Field[] fields = jc.getFields();
                Field f = null;
                for (final Field field : fields) {
                    if (field.getName().equals(fieldName)) {
                        f = field;
                        break;
                    }
                }
                if (f == null) {
                    throw new AssertionViolatedException("Field '" + fieldName + "' not found in " + jc.getClassName());
                }

                if (!f.isStatic()) {
                    constraintViolated(o, "Referenced field '" + f + "' is not static which it should be.");
                }
            } catch (final ClassNotFoundException e) {
                // FIXME: maybe not the best way to handle this
                throw new AssertionViolatedException("Missing class: " + e, e);
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitIINC(final IINC o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative.");
            } else {
                final int maxminus1 = maxLocals() - 1;
                if (idx > maxminus1) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitILOAD(final ILOAD o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative.");
            } else {
                final int maxminus1 = maxLocals() - 1;
                if (idx > maxminus1) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitINSTANCEOF(final INSTANCEOF o) {
            indexValid(o, o.getIndex());
            final Constant c = constantPoolGen.getConstant(o.getIndex());
            if (!(c instanceof ConstantClass)) {
                constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitINVOKEDYNAMIC(final INVOKEDYNAMIC o) {
            throw new UnsupportedOperationException("INVOKEDYNAMIC instruction is not supported at this time");
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitInvokeInstruction(final InvokeInstruction o) {
            indexValid(o, o.getIndex());
            if (o instanceof INVOKEVIRTUAL || o instanceof INVOKESPECIAL || o instanceof INVOKESTATIC) {
                final Constant c = constantPoolGen.getConstant(o.getIndex());
                if (!(c instanceof ConstantMethodref)) {
                    constraintViolated(o, "Indexing a constant that's not a CONSTANT_Methodref but a '" + tostring(c) + "'.");
                } else {
                    // Constants are okay due to pass2.
                    final ConstantNameAndType cnat = (ConstantNameAndType) constantPoolGen.getConstant(((ConstantMethodref) c).getNameAndTypeIndex());
                    final ConstantUtf8 cutf8 = (ConstantUtf8) constantPoolGen.getConstant(cnat.getNameIndex());
                    if (cutf8.getBytes().equals(Const.CONSTRUCTOR_NAME) && !(o instanceof INVOKESPECIAL)) {
                        constraintViolated(o, "Only INVOKESPECIAL is allowed to invoke instance initialization methods.");
                    }
                    if (!cutf8.getBytes().equals(Const.CONSTRUCTOR_NAME) && cutf8.getBytes().startsWith("<")) {
                        constraintViolated(o, "No method with a name beginning with '<' other than the instance initialization methods"
                            + " may be called by the method invocation instructions.");
                    }
                }
            } else {
                final Constant c = constantPoolGen.getConstant(o.getIndex());
                if (!(c instanceof ConstantInterfaceMethodref)) {
                    constraintViolated(o, "Indexing a constant that's not a CONSTANT_InterfaceMethodref but a '" + tostring(c) + "'.");
                }
                // TODO: From time to time check if BCEL allows to detect if the
                // 'count' operand is consistent with the information in the
                // CONSTANT_InterfaceMethodref and if the last operand is zero.
                // By now, BCEL hides those two operands because they're superfluous.

                // Invoked method must not be <init> or <clinit>
                final ConstantNameAndType cnat = (ConstantNameAndType) constantPoolGen.getConstant(((ConstantInterfaceMethodref) c).getNameAndTypeIndex());
                final String name = ((ConstantUtf8) constantPoolGen.getConstant(cnat.getNameIndex())).getBytes();
                if (name.equals(Const.CONSTRUCTOR_NAME)) {
                    constraintViolated(o, "Method to invoke must not be '" + Const.CONSTRUCTOR_NAME + "'.");
                }
                if (name.equals(Const.STATIC_INITIALIZER_NAME)) {
                    constraintViolated(o, "Method to invoke must not be '" + Const.STATIC_INITIALIZER_NAME + "'.");
                }
            }

            // The LoadClassType is the method-declaring class, so we have to check the other types.

            Type t = o.getReturnType(constantPoolGen);
            if (t instanceof ArrayType) {
                t = ((ArrayType) t).getBasicType();
            }
            if (t instanceof ObjectType) {
                final Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName());
                final VerificationResult vr = v.doPass2();
                if (vr.getStatus() != VerificationResult.VERIFIED_OK) {
                    constraintViolated(o, "Return type class/interface could not be verified successfully: '" + vr.getMessage() + "'.");
                }
            }

            final Type[] ts = o.getArgumentTypes(constantPoolGen);
            for (final Type element : ts) {
                t = element;
                if (t instanceof ArrayType) {
                    t = ((ArrayType) t).getBasicType();
                }
                if (t instanceof ObjectType) {
                    final Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName());
                    final VerificationResult vr = v.doPass2();
                    if (vr.getStatus() != VerificationResult.VERIFIED_OK) {
                        constraintViolated(o, "Argument type class/interface could not be verified successfully: '" + vr.getMessage() + "'.");
                    }
                }
            }

        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitINVOKEINTERFACE(final INVOKEINTERFACE o) {
            try {
                // INVOKEINTERFACE is a LoadClass; the Class where the referenced method is declared in,
                // is therefore resolved/verified.
                // INVOKEINTERFACE is an InvokeInstruction, the argument and return types are resolved/verified,
                // too. So are the allowed method names.
                final String classname = o.getClassName(constantPoolGen);
                final JavaClass jc = Repository.lookupClass(classname);
                final Method m = getMethodRecursive(jc, o);
                if (m == null) {
                    constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
                        + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
                }
                if (jc.isClass()) {
                    constraintViolated(o, "Referenced class '" + jc.getClassName() + "' is a class, but not an interface as expected.");
                }
            } catch (final ClassNotFoundException e) {
                // FIXME: maybe not the best way to handle this
                throw new AssertionViolatedException("Missing class: " + e, e);
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitINVOKESPECIAL(final INVOKESPECIAL o) {
            try {
                // INVOKESPECIAL is a LoadClass; the Class where the referenced method is declared in,
                // is therefore resolved/verified.
                // INVOKESPECIAL is an InvokeInstruction, the argument and return types are resolved/verified,
                // too. So are the allowed method names.
                final String classname = o.getClassName(constantPoolGen);
                final JavaClass jc = Repository.lookupClass(classname);
                final Method m = getMethodRecursive(jc, o);
                if (m == null) {
                    constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
                        + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
                }

                JavaClass current = Repository.lookupClass(myOwner.getClassName());
                if (current.isSuper() && Repository.instanceOf(current, jc) && !current.equals(jc)
                    && !o.getMethodName(constantPoolGen).equals(Const.CONSTRUCTOR_NAME)) {
                    // Special lookup procedure for ACC_SUPER classes.

                    int supidx = -1;

                    Method meth = null;
                    while (supidx != 0) {
                        supidx = current.getSuperclassNameIndex();
                        current = Repository.lookupClass(current.getSuperclassName());

                        final Method[] meths = current.getMethods();
                        for (final Method meth2 : meths) {
                            if (meth2.getName().equals(o.getMethodName(constantPoolGen))
                                && Type.getReturnType(meth2.getSignature()).equals(o.getReturnType(constantPoolGen))
                                && objArrayEquals(Type.getArgumentTypes(meth2.getSignature()), o.getArgumentTypes(constantPoolGen))) {
                                meth = meth2;
                                break;
                            }
                        }
                        if (meth != null) {
                            break;
                        }
                    }
                    if (meth == null) {
                        constraintViolated(o, "ACC_SUPER special lookup procedure not successful: method '" + o.getMethodName(constantPoolGen)
                            + "' with proper signature not declared in superclass hierarchy.");
                    }
                }
<commit_msg>Reuse Arrays.equals()<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.apache.bcel.verifier.statics;

import java.util.Arrays;

import org.apache.bcel.Const;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.ClassFormatException;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.CodeException;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.ConstantDouble;
import org.apache.bcel.classfile.ConstantFieldref;
import org.apache.bcel.classfile.ConstantFloat;
import org.apache.bcel.classfile.ConstantInteger;
import org.apache.bcel.classfile.ConstantInterfaceMethodref;
import org.apache.bcel.classfile.ConstantLong;
import org.apache.bcel.classfile.ConstantMethodref;
import org.apache.bcel.classfile.ConstantNameAndType;
import org.apache.bcel.classfile.ConstantString;
import org.apache.bcel.classfile.ConstantUtf8;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.LineNumber;
import org.apache.bcel.classfile.LineNumberTable;
import org.apache.bcel.classfile.LocalVariableTable;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ALOAD;
import org.apache.bcel.generic.ANEWARRAY;
import org.apache.bcel.generic.ASTORE;
import org.apache.bcel.generic.ATHROW;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.BREAKPOINT;
import org.apache.bcel.generic.CHECKCAST;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.DLOAD;
import org.apache.bcel.generic.DSTORE;
import org.apache.bcel.generic.FLOAD;
import org.apache.bcel.generic.FSTORE;
import org.apache.bcel.generic.FieldInstruction;
import org.apache.bcel.generic.GETSTATIC;
import org.apache.bcel.generic.GotoInstruction;
import org.apache.bcel.generic.IINC;
import org.apache.bcel.generic.ILOAD;
import org.apache.bcel.generic.IMPDEP1;
import org.apache.bcel.generic.IMPDEP2;
import org.apache.bcel.generic.INSTANCEOF;
import org.apache.bcel.generic.INVOKEDYNAMIC;
import org.apache.bcel.generic.INVOKEINTERFACE;
import org.apache.bcel.generic.INVOKESPECIAL;
import org.apache.bcel.generic.INVOKESTATIC;
import org.apache.bcel.generic.INVOKEVIRTUAL;
import org.apache.bcel.generic.ISTORE;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.JsrInstruction;
import org.apache.bcel.generic.LDC;
import org.apache.bcel.generic.LDC2_W;
import org.apache.bcel.generic.LLOAD;
import org.apache.bcel.generic.LOOKUPSWITCH;
import org.apache.bcel.generic.LSTORE;
import org.apache.bcel.generic.LoadClass;
import org.apache.bcel.generic.MULTIANEWARRAY;
import org.apache.bcel.generic.NEW;
import org.apache.bcel.generic.NEWARRAY;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.PUTSTATIC;
import org.apache.bcel.generic.RET;
import org.apache.bcel.generic.ReferenceType;
import org.apache.bcel.generic.ReturnInstruction;
import org.apache.bcel.generic.TABLESWITCH;
import org.apache.bcel.generic.Type;
import org.apache.bcel.verifier.PassVerifier;
import org.apache.bcel.verifier.VerificationResult;
import org.apache.bcel.verifier.Verifier;
import org.apache.bcel.verifier.VerifierFactory;
import org.apache.bcel.verifier.exc.AssertionViolatedException;
import org.apache.bcel.verifier.exc.ClassConstraintException;
import org.apache.bcel.verifier.exc.InvalidMethodException;
import org.apache.bcel.verifier.exc.StaticCodeConstraintException;
import org.apache.bcel.verifier.exc.StaticCodeInstructionConstraintException;
import org.apache.bcel.verifier.exc.StaticCodeInstructionOperandConstraintException;

/**
 * This PassVerifier verifies a class file according to pass 3, static part as described in The Java Virtual Machine
 * Specification, 2nd edition. More detailed information is to be found at the do_verify() method's documentation.
 *
 * @see #do_verify()
 */
public final class Pass3aVerifier extends PassVerifier {

    /**
     * This visitor class does the actual checking for the instruction operand's constraints.
     */
    private class InstOperandConstraintVisitor extends org.apache.bcel.generic.EmptyVisitor {
        /** The ConstantPoolGen instance this Visitor operates on. */
        private final ConstantPoolGen constantPoolGen;

        /** The only Constructor. */
        InstOperandConstraintVisitor(final ConstantPoolGen constantPoolGen) {
            this.constantPoolGen = constantPoolGen;
        }

        /**
         * A utility method to always raise an exception.
         */
        private void constraintViolated(final Instruction i, final String message) {
            throw new StaticCodeInstructionOperandConstraintException("Instruction " + tostring(i) + " constraint violated: " + message);
        }

        /**
         * Looks for the method referenced by the given invoke instruction in the given class.
         *
         * @param jc the class that defines the referenced method
         * @param invoke the instruction that references the method
         * @return the referenced method or null if not found.
         */
        private Method getMethod(final JavaClass jc, final InvokeInstruction invoke) {
            final Method[] ms = jc.getMethods();
            for (final Method element : ms) {
                if (element.getName().equals(invoke.getMethodName(constantPoolGen))
                    && Type.getReturnType(element.getSignature()).equals(invoke.getReturnType(constantPoolGen))
                    && Arrays.equals(Type.getArgumentTypes(element.getSignature()), invoke.getArgumentTypes(constantPoolGen))) {
                    return element;
                }
            }

            return null;
        }

        /**
         * Looks for the method referenced by the given invoke instruction in the given class or its super classes and super
         * interfaces.
         *
         * @param jc the class that defines the referenced method
         * @param invoke the instruction that references the method
         * @return the referenced method or null if not found.
         */
        private Method getMethodRecursive(final JavaClass jc, final InvokeInstruction invoke) throws ClassNotFoundException {
            Method m;
            // look in the given class
            m = getMethod(jc, invoke);
            if (m != null) {
                // method found in given class
                return m;
            }
            // method not found, look in super classes
            for (final JavaClass superclass : jc.getSuperClasses()) {
                m = getMethod(superclass, invoke);
                if (m != null) {
                    // method found in super class
                    return m;
                }
            }
            // method not found, look in super interfaces
            for (final JavaClass superclass : jc.getInterfaces()) {
                m = getMethod(superclass, invoke);
                if (m != null) {
                    // method found in super interface
                    return m;
                }
            }
            // method not found in the hierarchy
            return null;
        }

        private ObjectType getObjectType(final FieldInstruction o) {
            final ReferenceType rt = o.getReferenceType(constantPoolGen);
            if (rt instanceof ObjectType) {
                return (ObjectType) rt;
            }
            constraintViolated(o, "expecting ObjectType but got " + rt);
            return null;
        }

        // The target of each jump and branch instruction [...] must be the opcode [...]
        // BCEL _DOES_ handle this.

        // tableswitch: BCEL will do it, supposedly.

        // lookupswitch: BCEL will do it, supposedly.

        /**
         * A utility method to raise an exception if the index is not a valid constant pool index.
         */
        private void indexValid(final Instruction i, final int idx) {
            if (idx < 0 || idx >= constantPoolGen.getSize()) {
                constraintViolated(i, "Illegal constant pool index '" + idx + "'.");
            }
        }

        /**
         * Utility method to return the max_locals value of the method verified by the surrounding Pass3aVerifier instance.
         */
        private int maxLocals() {
            try {
                return Repository.lookupClass(myOwner.getClassName()).getMethods()[methodNo].getCode().getMaxLocals();
            } catch (final ClassNotFoundException e) {
                // FIXME: maybe not the best way to handle this
                throw new AssertionViolatedException("Missing class: " + e, e);
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitALOAD(final ALOAD o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative.");
            } else {
                final int maxminus1 = maxLocals() - 1;
                if (idx > maxminus1) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitANEWARRAY(final ANEWARRAY o) {
            indexValid(o, o.getIndex());
            final Constant c = constantPoolGen.getConstant(o.getIndex());
            if (!(c instanceof ConstantClass)) {
                constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
            }
            final Type t = o.getType(constantPoolGen);
            if (t instanceof ArrayType) {
                final int dimensions = ((ArrayType) t).getDimensions();
                if (dimensions > Const.MAX_ARRAY_DIMENSIONS) {
                    constraintViolated(o,
                        "Not allowed to create an array with more than " + Const.MAX_ARRAY_DIMENSIONS + " dimensions;" + " actual: " + dimensions);
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitASTORE(final ASTORE o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative.");
            } else {
                final int maxminus1 = maxLocals() - 1;
                if (idx > maxminus1) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitCHECKCAST(final CHECKCAST o) {
            indexValid(o, o.getIndex());
            final Constant c = constantPoolGen.getConstant(o.getIndex());
            if (!(c instanceof ConstantClass)) {
                constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitDLOAD(final DLOAD o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative."
                    + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
            } else {
                final int maxminus2 = maxLocals() - 2;
                if (idx > maxminus2) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitDSTORE(final DSTORE o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative."
                    + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
            } else {
                final int maxminus2 = maxLocals() - 2;
                if (idx > maxminus2) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        // getfield, putfield, getstatic, putstatic
        @Override
        public void visitFieldInstruction(final FieldInstruction o) {
            try {
                indexValid(o, o.getIndex());
                final Constant c = constantPoolGen.getConstant(o.getIndex());
                if (!(c instanceof ConstantFieldref)) {
                    constraintViolated(o, "Indexing a constant that's not a CONSTANT_Fieldref but a '" + tostring(c) + "'.");
                }

                final String fieldName = o.getFieldName(constantPoolGen);

                final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName());
                Field[] fields = jc.getFields();
                Field f = null;
                for (final Field field : fields) {
                    if (field.getName().equals(fieldName)) {
                        final Type fType = Type.getType(field.getSignature());
                        final Type oType = o.getType(constantPoolGen);
                        /*
                         * TODO: Check if assignment compatibility is sufficient. What does Sun do?
                         */
                        if (fType.equals(oType)) {
                            f = field;
                            break;
                        }
                    }
                }
                if (f == null) {
                    final JavaClass[] superclasses = jc.getSuperClasses();
                    outer: for (final JavaClass superclass : superclasses) {
                        fields = superclass.getFields();
                        for (final Field field : fields) {
                            if (field.getName().equals(fieldName)) {
                                final Type fType = Type.getType(field.getSignature());
                                final Type oType = o.getType(constantPoolGen);
                                if (fType.equals(oType)) {
                                    f = field;
                                    if ((f.getAccessFlags() & (Const.ACC_PUBLIC | Const.ACC_PROTECTED)) == 0) {
                                        f = null;
                                    }
                                    break outer;
                                }
                            }
                        }
                    }
                    if (f == null) {
                        constraintViolated(o, "Referenced field '" + fieldName + "' does not exist in class '" + jc.getClassName() + "'.");
                    }
                } else {
                    /*
                     * TODO: Check if assignment compatibility is sufficient. What does Sun do?
                     */
                    Type.getType(f.getSignature());
                    o.getType(constantPoolGen);
//                Type f_type = Type.getType(f.getSignature());
//                Type o_type = o.getType(cpg);

                    // Argh. Sun's implementation allows us to have multiple fields of
                    // the same name but with a different signature.
                    // if (! f_type.equals(o_type)) {
                    // constraintViolated(o,
                    // "Referenced field '"+field_name+"' has type '"+f_type+"' instead of '"+o_type+"' as expected.");
                    // }

                    /* TODO: Check for access modifiers here. */
                }
            } catch (final ClassNotFoundException e) {
                // FIXME: maybe not the best way to handle this
                throw new AssertionViolatedException("Missing class: " + e, e);
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitFLOAD(final FLOAD o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative.");
            } else {
                final int maxminus1 = maxLocals() - 1;
                if (idx > maxminus1) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitFSTORE(final FSTORE o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative.");
            } else {
                final int maxminus1 = maxLocals() - 1;
                if (idx > maxminus1) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitGETSTATIC(final GETSTATIC o) {
            try {
                final String fieldName = o.getFieldName(constantPoolGen);
                final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName());
                final Field[] fields = jc.getFields();
                Field f = null;
                for (final Field field : fields) {
                    if (field.getName().equals(fieldName)) {
                        f = field;
                        break;
                    }
                }
                if (f == null) {
                    throw new AssertionViolatedException("Field '" + fieldName + "' not found in " + jc.getClassName());
                }

                if (!f.isStatic()) {
                    constraintViolated(o, "Referenced field '" + f + "' is not static which it should be.");
                }
            } catch (final ClassNotFoundException e) {
                // FIXME: maybe not the best way to handle this
                throw new AssertionViolatedException("Missing class: " + e, e);
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitIINC(final IINC o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative.");
            } else {
                final int maxminus1 = maxLocals() - 1;
                if (idx > maxminus1) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitILOAD(final ILOAD o) {
            final int idx = o.getIndex();
            if (idx < 0) {
                constraintViolated(o, "Index '" + idx + "' must be non-negative.");
            } else {
                final int maxminus1 = maxLocals() - 1;
                if (idx > maxminus1) {
                    constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
                }
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitINSTANCEOF(final INSTANCEOF o) {
            indexValid(o, o.getIndex());
            final Constant c = constantPoolGen.getConstant(o.getIndex());
            if (!(c instanceof ConstantClass)) {
                constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitINVOKEDYNAMIC(final INVOKEDYNAMIC o) {
            throw new UnsupportedOperationException("INVOKEDYNAMIC instruction is not supported at this time");
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitInvokeInstruction(final InvokeInstruction o) {
            indexValid(o, o.getIndex());
            if (o instanceof INVOKEVIRTUAL || o instanceof INVOKESPECIAL || o instanceof INVOKESTATIC) {
                final Constant c = constantPoolGen.getConstant(o.getIndex());
                if (!(c instanceof ConstantMethodref)) {
                    constraintViolated(o, "Indexing a constant that's not a CONSTANT_Methodref but a '" + tostring(c) + "'.");
                } else {
                    // Constants are okay due to pass2.
                    final ConstantNameAndType cnat = (ConstantNameAndType) constantPoolGen.getConstant(((ConstantMethodref) c).getNameAndTypeIndex());
                    final ConstantUtf8 cutf8 = (ConstantUtf8) constantPoolGen.getConstant(cnat.getNameIndex());
                    if (cutf8.getBytes().equals(Const.CONSTRUCTOR_NAME) && !(o instanceof INVOKESPECIAL)) {
                        constraintViolated(o, "Only INVOKESPECIAL is allowed to invoke instance initialization methods.");
                    }
                    if (!cutf8.getBytes().equals(Const.CONSTRUCTOR_NAME) && cutf8.getBytes().startsWith("<")) {
                        constraintViolated(o, "No method with a name beginning with '<' other than the instance initialization methods"
                            + " may be called by the method invocation instructions.");
                    }
                }
            } else {
                final Constant c = constantPoolGen.getConstant(o.getIndex());
                if (!(c instanceof ConstantInterfaceMethodref)) {
                    constraintViolated(o, "Indexing a constant that's not a CONSTANT_InterfaceMethodref but a '" + tostring(c) + "'.");
                }
                // TODO: From time to time check if BCEL allows to detect if the
                // 'count' operand is consistent with the information in the
                // CONSTANT_InterfaceMethodref and if the last operand is zero.
                // By now, BCEL hides those two operands because they're superfluous.

                // Invoked method must not be <init> or <clinit>
                final ConstantNameAndType cnat = (ConstantNameAndType) constantPoolGen.getConstant(((ConstantInterfaceMethodref) c).getNameAndTypeIndex());
                final String name = ((ConstantUtf8) constantPoolGen.getConstant(cnat.getNameIndex())).getBytes();
                if (name.equals(Const.CONSTRUCTOR_NAME)) {
                    constraintViolated(o, "Method to invoke must not be '" + Const.CONSTRUCTOR_NAME + "'.");
                }
                if (name.equals(Const.STATIC_INITIALIZER_NAME)) {
                    constraintViolated(o, "Method to invoke must not be '" + Const.STATIC_INITIALIZER_NAME + "'.");
                }
            }

            // The LoadClassType is the method-declaring class, so we have to check the other types.

            Type t = o.getReturnType(constantPoolGen);
            if (t instanceof ArrayType) {
                t = ((ArrayType) t).getBasicType();
            }
            if (t instanceof ObjectType) {
                final Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName());
                final VerificationResult vr = v.doPass2();
                if (vr.getStatus() != VerificationResult.VERIFIED_OK) {
                    constraintViolated(o, "Return type class/interface could not be verified successfully: '" + vr.getMessage() + "'.");
                }
            }

            final Type[] ts = o.getArgumentTypes(constantPoolGen);
            for (final Type element : ts) {
                t = element;
                if (t instanceof ArrayType) {
                    t = ((ArrayType) t).getBasicType();
                }
                if (t instanceof ObjectType) {
                    final Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName());
                    final VerificationResult vr = v.doPass2();
                    if (vr.getStatus() != VerificationResult.VERIFIED_OK) {
                        constraintViolated(o, "Argument type class/interface could not be verified successfully: '" + vr.getMessage() + "'.");
                    }
                }
            }

        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitINVOKEINTERFACE(final INVOKEINTERFACE o) {
            try {
                // INVOKEINTERFACE is a LoadClass; the Class where the referenced method is declared in,
                // is therefore resolved/verified.
                // INVOKEINTERFACE is an InvokeInstruction, the argument and return types are resolved/verified,
                // too. So are the allowed method names.
                final String classname = o.getClassName(constantPoolGen);
                final JavaClass jc = Repository.lookupClass(classname);
                final Method m = getMethodRecursive(jc, o);
                if (m == null) {
                    constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
                        + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
                }
                if (jc.isClass()) {
                    constraintViolated(o, "Referenced class '" + jc.getClassName() + "' is a class, but not an interface as expected.");
                }
            } catch (final ClassNotFoundException e) {
                // FIXME: maybe not the best way to handle this
                throw new AssertionViolatedException("Missing class: " + e, e);
            }
        }

        /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
        @Override
        public void visitINVOKESPECIAL(final INVOKESPECIAL o) {
            try {
                // INVOKESPECIAL is a LoadClass; the Class where the referenced method is declared in,
                // is therefore resolved/verified.
                // INVOKESPECIAL is an InvokeInstruction, the argument and return types are resolved/verified,
                // too. So are the allowed method names.
                final String classname = o.getClassName(constantPoolGen);
                final JavaClass jc = Repository.lookupClass(classname);
                final Method m = getMethodRecursive(jc, o);
                if (m == null) {
                    constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
                        + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
                }

                JavaClass current = Repository.lookupClass(myOwner.getClassName());
                if (current.isSuper() && Repository.instanceOf(current, jc) && !current.equals(jc)
                    && !o.getMethodName(constantPoolGen).equals(Const.CONSTRUCTOR_NAME)) {
                    // Special lookup procedure for ACC_SUPER classes.

                    int supidx = -1;

                    Method meth = null;
                    while (supidx != 0) {
                        supidx = current.getSuperclassNameIndex();
                        current = Repository.lookupClass(current.getSuperclassName());

                        final Method[] meths = current.getMethods();
                        for (final Method meth2 : meths) {
                            if (meth2.getName().equals(o.getMethodName(constantPoolGen))
                                && Type.getReturnType(meth2.getSignature()).equals(o.getReturnType(constantPoolGen))
                                && Arrays.equals(Type.getArgumentTypes(meth2.getSignature()), o.getArgumentTypes(constantPoolGen))) {
                                meth = meth2;
                                break;
                            }
                        }
                        if (meth != null) {
                            break;
                        }
                    }
                    if (meth == null) {
                        constraintViolated(o, "ACC_SUPER special lookup procedure not successful: method '" + o.getMethodName(constantPoolGen)
                            + "' with proper signature not declared in superclass hierarchy.");
                    }
                }

{'fraction_non_alphanumeric': 0.09448895996866688, 'fraction_numerical': 0.0025947745483623544, 'mean_word_length': 1.80115194733955, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5831846', 'n_tokens_mistral': 15905, 'n_tokens_neox': 15249, 'n_words': 4418}