    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 *  Unless required by applicable law or agreed to in writing, software
    012 *  distributed under the License is distributed on an "AS IS" BASIS,
    013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 *  See the License for the specific language governing permissions and
    015 *  limitations under the License.
    016 *
    017 */
    018package org.apache.bcel.verifier.statics;
    019
    020
    021import java.util.HashMap;
    022import java.util.HashSet;
    023import java.util.Locale;
    024import java.util.Map;
    025import java.util.Set;
    026
    027import org.apache.bcel.Const;
    028import org.apache.bcel.Constants;
    029import org.apache.bcel.Repository;
    030import org.apache.bcel.classfile.Attribute;
    031import org.apache.bcel.classfile.ClassFormatException;
    032import org.apache.bcel.classfile.Code;
    033import org.apache.bcel.classfile.CodeException;
    034import org.apache.bcel.classfile.Constant;
    035import org.apache.bcel.classfile.ConstantClass;
    036import org.apache.bcel.classfile.ConstantDouble;
    037import org.apache.bcel.classfile.ConstantFieldref;
    038import org.apache.bcel.classfile.ConstantFloat;
    039import org.apache.bcel.classfile.ConstantInteger;
    040import org.apache.bcel.classfile.ConstantInterfaceMethodref;
    041import org.apache.bcel.classfile.ConstantLong;
    042import org.apache.bcel.classfile.ConstantMethodref;
    043import org.apache.bcel.classfile.ConstantNameAndType;
    044import org.apache.bcel.classfile.ConstantPool;
    045import org.apache.bcel.classfile.ConstantString;
    046import org.apache.bcel.classfile.ConstantUtf8;
    047import org.apache.bcel.classfile.ConstantValue;
    048import org.apache.bcel.classfile.Deprecated;
    049import org.apache.bcel.classfile.DescendingVisitor;
    050import org.apache.bcel.classfile.EmptyVisitor;
    051import org.apache.bcel.classfile.ExceptionTable;
    052import org.apache.bcel.classfile.Field;
    053import org.apache.bcel.classfile.InnerClass;
    054import org.apache.bcel.classfile.InnerClasses;
    055import org.apache.bcel.classfile.JavaClass;
    056import org.apache.bcel.classfile.LineNumber;
    057import org.apache.bcel.classfile.LineNumberTable;
    058import org.apache.bcel.classfile.LocalVariable;
    059import org.apache.bcel.classfile.LocalVariableTable;
    060import org.apache.bcel.classfile.Method;
    061import org.apache.bcel.classfile.Node;
    062import org.apache.bcel.classfile.SourceFile;
    063import org.apache.bcel.classfile.Synthetic;
    064import org.apache.bcel.classfile.Unknown;
    065import org.apache.bcel.generic.ArrayType;
    066import org.apache.bcel.generic.ObjectType;
    067import org.apache.bcel.generic.Type;
    068import org.apache.bcel.verifier.PassVerifier;
    069import org.apache.bcel.verifier.VerificationResult;
    070import org.apache.bcel.verifier.Verifier;
    071import org.apache.bcel.verifier.VerifierFactory;
    072import org.apache.bcel.verifier.exc.AssertionViolatedException;
    073import org.apache.bcel.verifier.exc.ClassConstraintException;
    074import org.apache.bcel.verifier.exc.LocalVariableInfoInconsistentException;
    075
    076/**
    077 * This PassVerifier verifies a class file according to
    078 * pass 2 as described in The Java Virtual Machine
    079 * Specification, 2nd edition.
    080 * More detailed information is to be found at the do_verify()
    081 * method's documentation.
    082 *
    083 * @version $Id: Pass2Verifier.java 1806200 2017-08-25 16:33:06Z ggregory $
    084 * @see #do_verify()
    085 */
    086public final class Pass2Verifier extends PassVerifier implements Constants {
    087
    088    /**
    089     * The LocalVariableInfo instances used by Pass3bVerifier.
    090     * localVariablesInfos[i] denotes the information for the
    091     * local variables of method number i in the
    092     * JavaClass this verifier operates on.
    093     */
    094    private LocalVariablesInfo[] localVariablesInfos;
    095
    096    /** The Verifier that created this. */
    097    private final Verifier myOwner;
    098
    099    /**
    100     * Should only be instantiated by a Verifier.
    101     *
    102     * @see Verifier
    103     */
    104    public Pass2Verifier(final Verifier owner) {
    105        myOwner = owner;
    106    }
    107
    108    /**
    109     * Returns a LocalVariablesInfo object containing information
    110     * about the usage of the local variables in the Code attribute
    111     * of the said method or <B>null</B> if the class file this
    112     * Pass2Verifier operates on could not be pass-2-verified correctly.
    113     * The method number method_nr is the method you get using
    114     * <B>Repository.lookupClass(myOwner.getClassname()).getMethods()[method_nr];</B>.
    115     * You should not add own information. Leave that to JustIce.
    116     */
    117    public LocalVariablesInfo getLocalVariablesInfo(final int method_nr) {
    118        if (this.verify() != VerificationResult.VR_OK) {
    119            return null; // It's cached, don't worry.
    120        }
    121        if (method_nr < 0 || method_nr >= localVariablesInfos.length) {
    122            throw new AssertionViolatedException("Method number out of range.");
    123        }
    124        return localVariablesInfos[method_nr];
    125    }
    126
    127    /**
    128     * Pass 2 is the pass where static properties of the
    129     * class file are checked without looking into "Code"
    130     * arrays of methods.
    131     * This verification pass is usually invoked when
    132     * a class is resolved; and it may be possible that
    133     * this verification pass has to load in other classes
    134     * such as superclasses or implemented interfaces.
    135     * Therefore, Pass 1 is run on them.<BR>
    136     * Note that most referenced classes are <B>not</B> loaded
    137     * in for verification or for an existance check by this
    138     * pass; only the syntactical correctness of their names
    139     * and descriptors (a.k.a. signatures) is checked.<BR>
    140     * Very few checks that conceptually belong here
    141     * are delayed until pass 3a in JustIce. JustIce does
    142     * not only check for syntactical correctness but also
    143     * for semantical sanity - therefore it needs access to
    144     * the "Code" array of methods in a few cases. Please
    145     * see the pass 3a documentation, too.
    146     *
    147     * @see Pass3aVerifier
    148     */
    149    @Override
    150    public VerificationResult do_verify() {
    151        try {
    152        final VerificationResult vr1 = myOwner.doPass1();
    153        if (vr1.equals(VerificationResult.VR_OK)) {
    154
    155            // For every method, we could have information about the local variables out of LocalVariableTable attributes of
    156            // the Code attributes.
    157            localVariablesInfos = new LocalVariablesInfo[Repository.lookupClass(myOwner.getClassName()).getMethods().length];
    158
    159            VerificationResult vr = VerificationResult.VR_OK; // default.
    160            try{
    161                constant_pool_entries_satisfy_static_constraints();
    162                field_and_method_refs_are_valid();
    163                every_class_has_an_accessible_superclass();
    164                final_methods_are_not_overridden();
    165            }
    166            catch (final ClassConstraintException cce) {
    167                vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage());
    168            }
    169            return vr;
    170        }
    171        return VerificationResult.VR_NOTYET;
    172
    173        } catch (final ClassNotFoundException e) {
    174        // FIXME: this might not be the best way to handle missing classes.
    175        throw new AssertionViolatedException("Missing class: " + e, e);
    176        }
    177    }
    178
    179    /**
    180     * Ensures that every class has a super class and that
    181     * <B>final</B> classes are not subclassed.
    182     * This means, the class this Pass2Verifier operates
    183     * on has proper super classes (transitively) up to
    184     * java.lang.Object.
    185     * The reason for really loading (and Pass1-verifying)
    186     * all of those classes here is that we need them in
    187     * Pass2 anyway to verify no final methods are overridden
    188     * (that could be declared anywhere in the ancestor hierarchy).
    189     *
    190     * @throws ClassConstraintException otherwise.
    191     */
    192    private void every_class_has_an_accessible_superclass() {
    193        try {
    194        final Set<String> hs = new HashSet<>(); // save class names to detect circular inheritance
    195        JavaClass jc = Repository.lookupClass(myOwner.getClassName());
    196        int supidx = -1;
    197
    198        while (supidx != 0) {
    199            supidx = jc.getSuperclassNameIndex();
    200
    201            if (supidx == 0) {
    202                if (jc != Repository.lookupClass(Type.OBJECT.getClassName())) {
    203                    throw new ClassConstraintException("Superclass of '"+jc.getClassName()+
    204                            "' missing but not "+Type.OBJECT.getClassName()+" itself!");
    205                }
    206            }
    207            else{
    208                final String supername = jc.getSuperclassName();
    209                if (! hs.add(supername)) {    // If supername already is in the list
    210                    throw new ClassConstraintException("Circular superclass hierarchy detected.");
    211                }
    212                final Verifier v = VerifierFactory.getVerifier(supername);
    213                final VerificationResult vr = v.doPass1();
    214
    215                if (vr != VerificationResult.VR_OK) {
    216                    throw new ClassConstraintException("Could not load in ancestor class '"+supername+"'.");
    217                }
    218                jc = Repository.lookupClass(supername);
    219
    220                if (jc.isFinal()) {
    221                    throw new ClassConstraintException("Ancestor class '"+supername+
    222                            "' has the FINAL access modifier and must therefore not be subclassed.");
    223                }
    224            }
    225        }
    226
    227        } catch (final ClassNotFoundException e) {
    228        // FIXME: this might not be the best way to handle missing classes.
    229        throw new AssertionViolatedException("Missing class: " + e, e);
    230        }
    231    }
    232
    233    /**
    234     * Ensures that <B>final</B> methods are not overridden.
    235     * <B>Precondition to run this method:
    236     * constant_pool_entries_satisfy_static_constraints() and
    237     * every_class_has_an_accessible_superclass() have to be invoked before
    238     * (in that order).</B>
    239     *
    240     * @throws ClassConstraintException otherwise.
    241     * @see #constant_pool_entries_satisfy_static_constraints()
    242     * @see #every_class_has_an_accessible_superclass()
    243     */
    244    private void final_methods_are_not_overridden() {
    245        try {
    246        final Map<String, String> hashmap = new HashMap<>();
    247        JavaClass jc = Repository.lookupClass(myOwner.getClassName());
    248
    249        int supidx = -1;
    250        while (supidx != 0) {
    251            supidx = jc.getSuperclassNameIndex();
    252
    253            final Method[] methods = jc.getMethods();
    254            for (final Method method : methods) {
    255                final String nameAndSig = method.getName() + method.getSignature();
    256
    257                if (hashmap.containsKey(nameAndSig)) {
    258                    if (method.isFinal()) {
    259                        if (!(method.isPrivate())) {
    260                            throw new ClassConstraintException("Method '" + nameAndSig + "' in class '" + hashmap.get(nameAndSig) +
    261                                "' overrides the final (not-overridable) definition in class '" + jc.getClassName() + "'.");
    262                        }
    263                        addMessage("Method '" + nameAndSig + "' in class '" + hashmap.get(nameAndSig) +
    264                            "' overrides the final (not-overridable) definition in class '" + jc.getClassName() +
    265                            "'. This is okay, as the original definition was private; however this constraint leverage"+
    266                            " was introduced by JLS 8.4.6 (not vmspec2) and the behaviour of the Sun verifiers.");
    267                    } else {
    268                        if (!method.isStatic()) { // static methods don't inherit
    269                            hashmap.put(nameAndSig, jc.getClassName());
    270                        }
    271                    }
    272                } else {
    273                    if (!method.isStatic()) { // static methods don't inherit
    274                        hashmap.put(nameAndSig, jc.getClassName());
    275                    }
    276                }
    277            }
    278
    279            jc = Repository.lookupClass(jc.getSuperclassName());
    280            // Well, for OBJECT this returns OBJECT so it works (could return anything but must not throw an Exception).
    281        }
    282
    283        } catch (final ClassNotFoundException e) {
    284        // FIXME: this might not be the best way to handle missing classes.
    285        throw new AssertionViolatedException("Missing class: " + e, e);
    286        }
    287
    288    }
    289
    290    /**
    291     * Ensures that the constant pool entries satisfy the static constraints
    292     * as described in The Java Virtual Machine Specification, 2nd Edition.
    293     *
    294     * @throws ClassConstraintException otherwise.
    295     */
    296    private void constant_pool_entries_satisfy_static_constraints() {
    297        try {
    298        // Most of the consistency is handled internally by BCEL; here
    299        // we only have to verify if the indices of the constants point
    300        // to constants of the appropriate type and such.
    301        final JavaClass jc = Repository.lookupClass(myOwner.getClassName());
    302        new CPESSC_Visitor(jc); // constructor implicitly traverses jc
    303
    304        } catch (final ClassNotFoundException e) {
    305        // FIXME: this might not be the best way to handle missing classes.
    306        throw new AssertionViolatedException("Missing class: " + e, e);
    307        }
    308    }
    309
    310    /**
    311     * A Visitor class that ensures the constant pool satisfies the static
    312     * constraints.
    313     * The visitXXX() methods throw ClassConstraintException instances otherwise.
    314     *
    315     * @see #constant_pool_entries_satisfy_static_constraints()
    316     */
    317    private final class CPESSC_Visitor extends org.apache.bcel.classfile.EmptyVisitor{
    318        private final Class<?> CONST_Class;
    319        /*
    320        private Class<?> CONST_Fieldref;
    321        private Class<?> CONST_Methodref;
    322        private Class<?> CONST_InterfaceMethodref;
    323        */
    324        private final Class<?> CONST_String;
    325        private final Class<?> CONST_Integer;
    326        private final Class<?> CONST_Float;
    327        private final Class<?> CONST_Long;
    328        private final Class<?> CONST_Double;
    329        private final Class<?> CONST_NameAndType;
    330        private final Class<?> CONST_Utf8;
    331
    332        private final JavaClass jc;
    333        private final ConstantPool cp; // ==jc.getConstantPool() -- only here to save typing work and computing power.
    334        private final int cplen; // == cp.getLength() -- to save computing power.
    335        private final DescendingVisitor carrier;
    336
    337        private final Set<String> field_names = new HashSet<>();
    338        private final Set<String> field_names_and_desc = new HashSet<>();
    339        private final Set<String> method_names_and_desc = new HashSet<>();
    340
    341        private CPESSC_Visitor(final JavaClass _jc) {
    342            jc = _jc;
    343            cp = _jc.getConstantPool();
    344            cplen = cp.getLength();
    345
    346            CONST_Class = ConstantClass.class;
    347            /*
    348            CONST_Fieldref = ConstantFieldref.class;
    349            CONST_Methodref = ConstantMethodref.class;
    350            CONST_InterfaceMethodref = ConstantInterfaceMethodref.class;
    351            */
    352            CONST_String = ConstantString.class;
    353            CONST_Integer = ConstantInteger.class;
    354            CONST_Float = ConstantFloat.class;
    355            CONST_Long = ConstantLong.class;
    356            CONST_Double = ConstantDouble.class;
    357            CONST_NameAndType = ConstantNameAndType.class;
    358            CONST_Utf8 = ConstantUtf8.class;
    359
    360            carrier = new DescendingVisitor(_jc, this);
    361            carrier.visit();
    362        }
    363
    364        private void checkIndex(final Node referrer, final int index, final Class<?> shouldbe) {
    365            if ((index < 0) || (index >= cplen)) {
    366                throw new ClassConstraintException("Invalid index '"+index+"' used by '"+tostring(referrer)+"'.");
    367            }
    368            final Constant c = cp.getConstant(index);
    369            if (! shouldbe.isInstance(c)) {
    370                /* String isnot = shouldbe.toString().substring(shouldbe.toString().lastIndexOf(".")+1); //Cut all before last "." */
    371                throw new ClassCastException("Illegal constant '"+tostring(c)+"' at index '"+
    372                    index+"'. '"+tostring(referrer)+"' expects a '"+shouldbe+"'.");
    373            }
    374        }
    375        ///////////////////////////////////////
    376        // ClassFile structure (vmspec2 4.1) //
    377        ///////////////////////////////////////
    378        @Override
    379        public void visitJavaClass(final JavaClass obj) {
    380            final Attribute[] atts = obj.getAttributes();
    381            boolean foundSourceFile = false;
    382            boolean foundInnerClasses = false;
    383
    384            // Is there an InnerClass referenced?
    385            // This is a costly check; existing verifiers don't do it!
    386            final boolean hasInnerClass = new InnerClassDetector(jc).innerClassReferenced();
    387
    388            for (final Attribute att : atts) {
    389                if ((!(att instanceof SourceFile)) &&
    390                        (!(att instanceof Deprecated)) &&
    391                        (!(att instanceof InnerClasses)) &&
    392                        (!(att instanceof Synthetic))) {
    393                    addMessage("Attribute '" + tostring(att) + "' as an attribute of the ClassFile structure '" +
    394                        tostring(obj) + "' is unknown and will therefore be ignored.");
    395                }
    396
    397                if (att instanceof SourceFile) {
    398                    if (!foundSourceFile) {
    399                        foundSourceFile = true;
    400                    } else {
    401                        throw new ClassConstraintException("A ClassFile structure (like '" +
    402                            tostring(obj) + "') may have no more than one SourceFile attribute."); //vmspec2 4.7.7
    403                    }
    404                }
    405
    406                if (att instanceof InnerClasses) {
    407                    if (!foundInnerClasses) {
    408                        foundInnerClasses = true;
    409                    } else {
    410                        if (hasInnerClass) {
    411                            throw new ClassConstraintException("A Classfile structure (like '" + tostring(obj) +
    412                                "') must have exactly one InnerClasses attribute"+
    413                                " if at least one Inner Class is referenced (which is the case)."+
    414                                " More than one InnerClasses attribute was found.");
    415                        }
    416                    }
    417                    if (!hasInnerClass) {
    418                        addMessage("No referenced Inner Class found, but InnerClasses attribute '" + tostring(att) +
    419                            "' found. Strongly suggest removal of that attribute.");
    420                    }
    421                }
    422
    423            }
    424            if (hasInnerClass && !foundInnerClasses) {
    425                //throw new ClassConstraintException("A Classfile structure (like '"+tostring(obj)+
    426                // "') must have exactly one InnerClasses attribute if at least one Inner Class is referenced (which is the case)."+
    427                // " No InnerClasses attribute was found.");
    428                //vmspec2, page 125 says it would be a constraint: but existing verifiers
    429                //don't check it and javac doesn't satisfy it when it comes to anonymous
    430                //inner classes
    431                addMessage("A Classfile structure (like '"+tostring(obj)+
    432                    "') must have exactly one InnerClasses attribute if at least one Inner Class is referenced (which is the case)."+
    433                        " No InnerClasses attribute was found.");
    434            }
    435        }
    436        /////////////////////////////
    437        // CONSTANTS (vmspec2 4.4) //
    438        /////////////////////////////
    439        @Override
    440        public void visitConstantClass(final ConstantClass obj) {
    441            if (obj.getTag() != Const.CONSTANT_Class) {
    442                throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    443            }
    444            checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    445
    446        }
    447        @Override
    448        public void visitConstantFieldref(final ConstantFieldref obj) {
    449            if (obj.getTag() != Const.CONSTANT_Fieldref) {
    450                throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    451            }
    452            checkIndex(obj, obj.getClassIndex(), CONST_Class);
    453            checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
    454        }
    455        @Override
    456        public void visitConstantMethodref(final ConstantMethodref obj) {
    457            if (obj.getTag() != Const.CONSTANT_Methodref) {
    458                throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    459            }
    460            checkIndex(obj, obj.getClassIndex(), CONST_Class);
    461            checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
    462        }
    463        @Override
    464        public void visitConstantInterfaceMethodref(final ConstantInterfaceMethodref obj) {
    465            if (obj.getTag() != Const.CONSTANT_InterfaceMethodref) {
    466                throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    467            }
    468            checkIndex(obj, obj.getClassIndex(), CONST_Class);
    469            checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
    470        }
    471        @Override
    472        public void visitConstantString(final ConstantString obj) {
    473            if (obj.getTag() != Const.CONSTANT_String) {
    474                throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    475            }
    476            checkIndex(obj, obj.getStringIndex(), CONST_Utf8);
    477        }
    478        @Override
    479        public void visitConstantInteger(final ConstantInteger obj) {
    480            if (obj.getTag() != Const.CONSTANT_Integer) {
    481                throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    482            }
    483            // no indices to check
    484        }
    485        @Override
    486        public void visitConstantFloat(final ConstantFloat obj) {
    487            if (obj.getTag() != Const.CONSTANT_Float) {
    488                throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    489            }
    490            //no indices to check
    491        }
    492        @Override
    493        public void visitConstantLong(final ConstantLong obj) {
    494            if (obj.getTag() != Const.CONSTANT_Long) {
    495                throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    496            }
    497            //no indices to check
    498        }
    499        @Override
    500        public void visitConstantDouble(final ConstantDouble obj) {
    501            if (obj.getTag() != Const.CONSTANT_Double) {
    502                throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    503            }
    504            //no indices to check
    505        }
    506        @Override
    507        public void visitConstantNameAndType(final ConstantNameAndType obj) {
    508            if (obj.getTag() != Const.CONSTANT_NameAndType) {
    509                throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    510            }
    511            checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    512            //checkIndex(obj, obj.getDescriptorIndex(), CONST_Utf8); //inconsistently named in BCEL, see below.
    513            checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
    514        }
    515        @Override
    516        public void visitConstantUtf8(final ConstantUtf8 obj) {
    517            if (obj.getTag() != Const.CONSTANT_Utf8) {
    518                throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
    519            }
    520            //no indices to check
    521        }
    522        //////////////////////////
    523        // FIELDS (vmspec2 4.5) //
    524        //////////////////////////
    525        @Override
    526        public void visitField(final Field obj) {
    527
    528            if (jc.isClass()) {
    529                int maxone=0;
    530                if (obj.isPrivate()) {
    531                    maxone++;
    532                }
    533                if (obj.isProtected()) {
    534                    maxone++;
    535                }
    536                if (obj.isPublic()) {
    537                    maxone++;
    538                }
    539                if (maxone > 1) {
    540                    throw new ClassConstraintException("Field '"+tostring(obj)+
    541                        "' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set.");
    542                }
    543
    544                if (obj.isFinal() && obj.isVolatile()) {
    545                    throw new ClassConstraintException("Field '"+tostring(obj)+
    546                        "' must only have at most one of its ACC_FINAL, ACC_VOLATILE modifiers set.");
    547                }
    548            }
    549            else{ // isInterface!
    550                if (!obj.isPublic()) {
    551                    throw new ClassConstraintException("Interface field '"+tostring(obj)+
    552                        "' must have the ACC_PUBLIC modifier set but hasn't!");
    553                }
    554                if (!obj.isStatic()) {
    555                    throw new ClassConstraintException("Interface field '"+tostring(obj)+
    556                        "' must have the ACC_STATIC modifier set but hasn't!");
    557                }
    558                if (!obj.isFinal()) {
    559                    throw new ClassConstraintException("Interface field '"+tostring(obj)+
    560                        "' must have the ACC_FINAL modifier set but hasn't!");
    561                }
    562            }
    563
    564            if ((obj.getAccessFlags() & ~(Const.ACC_PUBLIC|Const.ACC_PRIVATE|Const.ACC_PROTECTED|Const.ACC_STATIC|
    565                                          Const.ACC_FINAL|Const.ACC_VOLATILE|Const.ACC_TRANSIENT)) > 0) {
    566                addMessage("Field '"+tostring(obj)+
    567                    "' has access flag(s) other than ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED,"+
    568                        " ACC_STATIC, ACC_FINAL, ACC_VOLATILE, ACC_TRANSIENT set (ignored).");
    569            }
    570
    571            checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    572
    573            final String name = obj.getName();
    574            if (! validFieldName(name)) {
    575                throw new ClassConstraintException("Field '"+tostring(obj)+"' has illegal name '"+obj.getName()+"'.");
    576            }
    577
    578            // A descriptor is often named signature in BCEL
    579            checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
    580
    581            final String sig  = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes(); // Field or Method sig.(=descriptor)
    582
    583            try{
    584                Type.getType(sig);  /* Don't need the return value */
    585            }
    586            catch (final ClassFormatException cfe) {
    587                throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.", cfe);
    588            }
    589
    590            final String nameanddesc = name+sig;
    591            if (field_names_and_desc.contains(nameanddesc)) {
    592                throw new ClassConstraintException("No two fields (like '"+tostring(obj)+
    593                    "') are allowed have same names and descriptors!");
    594            }
    595            if (field_names.contains(name)) {
    596                addMessage("More than one field of name '"+name+
    597                    "' detected (but with different type descriptors). This is very unusual.");
    598            }
    599            field_names_and_desc.add(nameanddesc);
    600            field_names.add(name);
    601
    602            final Attribute[] atts = obj.getAttributes();
    603            for (final Attribute att : atts) {
    604                if ((!(att instanceof ConstantValue)) &&
    605                        (!(att instanceof Synthetic)) &&
    606                        (!(att instanceof Deprecated))) {
    607                    addMessage("Attribute '" + tostring(att) + "' as an attribute of Field '" +
    608                        tostring(obj) + "' is unknown and will therefore be ignored.");
    609                }
    610                if (!(att instanceof ConstantValue)) {
    611                    addMessage("Attribute '" + tostring(att) + "' as an attribute of Field '" + tostring(obj) +
    612                        "' is not a ConstantValue and is therefore only of use for debuggers and such.");
    613                }
    614            }
    615        }
    616        ///////////////////////////
    617        // METHODS (vmspec2 4.6) //
    618        ///////////////////////////
    619        @Override
    620        public void visitMethod(final Method obj) {
    621
    622            checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    623
    624            final String name = obj.getName();
    625            if (! validMethodName(name, true)) {
    626                throw new ClassConstraintException("Method '"+tostring(obj)+"' has illegal name '"+name+"'.");
    627            }
    628
    629            // A descriptor is often named signature in BCEL
    630            checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
    631
    632            final String sig  = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes(); // Method's signature(=descriptor)
    633
    634            Type t;
    635            Type[] ts; // needed below the try block.
    636            try{
    637                t  = Type.getReturnType(sig);
    638                ts = Type.getArgumentTypes(sig);
    639            }
    640            catch (final ClassFormatException cfe) {
    641                throw new ClassConstraintException(
    642                    "Illegal descriptor (==signature) '"+sig+"' used by Method '"+tostring(obj)+"'.", cfe);
    643            }
    644
    645            // Check if referenced objects exist.
    646            Type act = t;
    647            if (act instanceof ArrayType) {
    648                act = ((ArrayType) act).getBasicType();
    649            }
    650            if (act instanceof ObjectType) {
    651                final Verifier v = VerifierFactory.getVerifier( ((ObjectType) act).getClassName() );
    652                final VerificationResult vr = v.doPass1();
    653                if (vr != VerificationResult.VR_OK) {
    654                    throw new ClassConstraintException(
    655                        "Method '"+tostring(obj)+"' has a return type that does not pass verification pass 1: '"+vr+"'.");
    656                }
    657            }
    658
    659            for (final Type element : ts) {
    660                act = element;
    661                if (act instanceof ArrayType) {
    662                    act = ((ArrayType) act).getBasicType();
    663                }
    664                if (act instanceof ObjectType) {
    665                    final Verifier v = VerifierFactory.getVerifier( ((ObjectType) act).getClassName() );
    666                    final VerificationResult vr = v.doPass1();
    667                    if (vr != VerificationResult.VR_OK) {
    668                        throw new ClassConstraintException(
    669                            "Method '"+tostring(obj)+"' has an argument type that does not pass verification pass 1: '"+vr+"'.");
    670                    }
    671                }
    672            }
    673
    674            // Nearly forgot this! Funny return values are allowed, but a non-empty arguments list makes a different method out of it!
    675            if (name.equals(Const.STATIC_INITIALIZER_NAME) && (ts.length != 0)) {
    676                throw new ClassConstraintException(
    677                    "Method '"+tostring(obj)+"' has illegal name '"+name+"'."+
    678                    " Its name resembles the class or interface initialization method"+
    679                    " which it isn't because of its arguments (==descriptor).");
    680            }
    681
    682            if (jc.isClass()) {
    683                int maxone=0;
    684                if (obj.isPrivate()) {
    685                    maxone++;
    686                }
    687                if (obj.isProtected()) {
    688                    maxone++;
    689                }
    690                if (obj.isPublic()) {
    691                    maxone++;
    692                }
    693                if (maxone > 1) {
    694                    throw new ClassConstraintException("Method '"+tostring(obj)+
    695                        "' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set.");
    696                }
    697
    698                if (obj.isAbstract()) {
    699                    if (obj.isFinal()) {
    700                        throw new ClassConstraintException(
    701                            "Abstract method '"+tostring(obj)+"' must not have the ACC_FINAL modifier set.");
    702                    }
    703                    if (obj.isNative()) {
    704                        throw new ClassConstraintException(
    705                            "Abstract method '"+tostring(obj)+"' must not have the ACC_NATIVE modifier set.");
    706                    }
    707                    if (obj.isPrivate()) {
    708                        throw new ClassConstraintException(
    709                            "Abstract method '"+tostring(obj)+"' must not have the ACC_PRIVATE modifier set.");
    710                    }
    711                    if (obj.isStatic()) {
    712                        throw new ClassConstraintException(
    713                            "Abstract method '"+tostring(obj)+"' must not have the ACC_STATIC modifier set.");
    714                    }
    715                    if (obj.isStrictfp()) {
    716                        throw new ClassConstraintException(
    717                            "Abstract method '"+tostring(obj)+"' must not have the ACC_STRICT modifier set.");
    718                    }
    719                    if (obj.isSynchronized()) {
    720                        throw new ClassConstraintException(
    721                            "Abstract method '"+tostring(obj)+"' must not have the ACC_SYNCHRONIZED modifier set.");
    722                    }
    723                }
    724
    725                // A specific instance initialization method... (vmspec2,Page 116).
    726                if (name.equals(Const.CONSTRUCTOR_NAME)) {
    727                    //..may have at most one of ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC set: is checked above.
    728                    //..may also have ACC_STRICT set, but none of the other flags in table 4.5 (vmspec2, page 115)
    729                    if (obj.isStatic() ||
    730                            obj.isFinal() ||
    731                            obj.isSynchronized() ||
    732                            obj.isNative() ||
    733                            obj.isAbstract()) {
    734                        throw new ClassConstraintException("Instance initialization method '" + tostring(obj) + "' must not have" +
    735                            " any of the ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT modifiers set.");
    736                    }
    737                }
    738            }
    739            else{ // isInterface!
    740                if (!name.equals(Const.STATIC_INITIALIZER_NAME)) {//vmspec2, p.116, 2nd paragraph
    741                    if (jc.getMajor() >= Const.MAJOR_1_8) {
    742                        if (!(obj.isPublic() ^ obj.isPrivate())) {
    743                            throw new ClassConstraintException("Interface method '" + tostring(obj) + "' must have" +
    744                                " exactly one of its ACC_PUBLIC and ACC_PRIVATE modifiers set.");
    745                        }
    746                        if (obj.isProtected()
    747                                || obj.isFinal()
    748                                || obj.isSynchronized()
    749                                || obj.isNative()) {
    750                            throw new ClassConstraintException("Interface method '"+tostring(obj)+ "' must not have" +
    751                                " any of the ACC_PROTECTED, ACC_FINAL, ACC_SYNCHRONIZED, or ACC_NATIVE modifiers set.");
    752                        }
    753
    754                    } else {
    755                        if (!obj.isPublic()) {
    756                            throw new ClassConstraintException(
    757                                "Interface method '"+tostring(obj)+"' must have the ACC_PUBLIC modifier set but hasn't!");
    758                        }
    759                        if (!obj.isAbstract()) {
    760                            throw new ClassConstraintException(
    761                                "Interface method '"+tostring(obj)+"' must have the ACC_ABSTRACT modifier set but hasn't!");
    762                        }
    763                        if (obj.isPrivate()
    764                                || obj.isProtected()
    765                                || obj.isStatic()
    766                                || obj.isFinal()
    767                                || obj.isSynchronized()
    768                                || obj.isNative()
    769                                || obj.isStrictfp() ) {
    770                            throw new ClassConstraintException("Interface method '"+tostring(obj)+ "' must not have" +
    771                                " any of the ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED,"+
    772                                " ACC_NATIVE, ACC_ABSTRACT, ACC_STRICT modifiers set.");
    773                        }
    774                    }
    775                }
    776            }
    777
    778            if ((obj.getAccessFlags() &
    779                    ~(Const.ACC_PUBLIC|Const.ACC_PRIVATE|Const.ACC_PROTECTED|Const.ACC_STATIC|Const.ACC_FINAL|
    780                      Const.ACC_SYNCHRONIZED|Const.ACC_NATIVE|Const.ACC_ABSTRACT|Const.ACC_STRICT)) > 0) {
    781                addMessage("Method '"+tostring(obj)+"' has access flag(s) other than"+
    782                    " ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL,"+
    783                        " ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT, ACC_STRICT set (ignored).");
    784            }
    785
    786            final String nameanddesc = name+sig;
    787            if (method_names_and_desc.contains(nameanddesc)) {
    788                throw new ClassConstraintException(
    789                    "No two methods (like '"+tostring(obj)+"') are allowed have same names and desciptors!");
    790            }
    791            method_names_and_desc.add(nameanddesc);
    792
    793            final Attribute[] atts = obj.getAttributes();
    794            int num_code_atts = 0;
    795            for (final Attribute att : atts) {
    796                if ((!(att instanceof Code)) &&
    797                        (!(att instanceof ExceptionTable)) &&
    798                        (!(att instanceof Synthetic)) &&
    799                        (!(att instanceof Deprecated))) {
    800                    addMessage("Attribute '" + tostring(att) + "' as an attribute of Method '" + tostring(obj) +
    801                        "' is unknown and will therefore be ignored.");
    802                }
    803                if ((!(att instanceof Code)) &&
    804                        (!(att instanceof ExceptionTable))) {
    805                    addMessage("Attribute '" + tostring(att) + "' as an attribute of Method '" + tostring(obj) +
    806                        "' is neither Code nor Exceptions and is therefore only of use for debuggers and such.");
    807                }
    808                if ((att instanceof Code) && (obj.isNative() || obj.isAbstract())) {
    809                    throw new ClassConstraintException("Native or abstract methods like '" + tostring(obj) +
    810                        "' must not have a Code attribute like '" + tostring(att) + "'."); //vmspec2 page120, 4.7.3
    811                }
    812                if (att instanceof Code) {
    813                    num_code_atts++;
    814                }
    815            }
    816            if ( !obj.isNative() && !obj.isAbstract() && num_code_atts != 1) {
    817                throw new ClassConstraintException("Non-native, non-abstract methods like '"+tostring(obj)+
    818                    "' must have exactly one Code attribute (found: "+num_code_atts+").");
    819            }
    820        }
    821        ///////////////////////////////////////////////////////
    822        // ClassFile-structure-ATTRIBUTES (vmspec2 4.1, 4.7) //
    823        ///////////////////////////////////////////////////////
    824        @Override
    825        public void visitSourceFile(final SourceFile obj) {//vmspec2 4.7.7
    826
    827            // zero or one SourceFile attr per ClassFile: see visitJavaClass()
    828
    829            checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    830
    831            final String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    832            if (! name.equals("SourceFile")) {
    833                throw new ClassConstraintException(
    834                    "The SourceFile attribute '"+tostring(obj)+"' is not correctly named 'SourceFile' but '"+name+"'.");
    835            }
    836
    837            checkIndex(obj, obj.getSourceFileIndex(), CONST_Utf8);
    838
    839            final String sourcefilename = ((ConstantUtf8) cp.getConstant(obj.getSourceFileIndex())).getBytes(); //==obj.getSourceFileName() ?
    840            final String sourcefilenamelc = sourcefilename.toLowerCase(Locale.ENGLISH);
    841
    842            if (    (sourcefilename.indexOf('/') != -1) ||
    843                        (sourcefilename.indexOf('\\') != -1) ||
    844                        (sourcefilename.indexOf(':') != -1) ||
    845                        (sourcefilenamelc.lastIndexOf(".java") == -1)    ) {
    846                addMessage("SourceFile attribute '"+tostring(obj)+
    847                    "' has a funny name: remember not to confuse certain parsers working on javap's output. Also, this name ('"+
    848                    sourcefilename+"') is considered an unqualified (simple) file name only.");
    849            }
    850        }
    851        @Override
    852        public void visitDeprecated(final Deprecated obj) {//vmspec2 4.7.10
    853            checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    854
    855            final String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    856            if (! name.equals("Deprecated")) {
    857                throw new ClassConstraintException("The Deprecated attribute '"+tostring(obj)+
    858                    "' is not correctly named 'Deprecated' but '"+name+"'.");
    859            }
    860        }
    861        @Override
    862        public void visitSynthetic(final Synthetic obj) {//vmspec2 4.7.6
    863            checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    864            final String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    865            if (! name.equals("Synthetic")) {
    866                throw new ClassConstraintException(
    867                    "The Synthetic attribute '"+tostring(obj)+"' is not correctly named 'Synthetic' but '"+name+"'.");
    868            }
    869        }
    870        @Override
    871        public void visitInnerClasses(final InnerClasses obj) {//vmspec2 4.7.5
    872
    873            // exactly one InnerClasses attr per ClassFile if some inner class is refernced: see visitJavaClass()
    874
    875            checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    876
    877            final String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    878            if (! name.equals("InnerClasses")) {
    879                throw new ClassConstraintException(
    880                    "The InnerClasses attribute '"+tostring(obj)+"' is not correctly named 'InnerClasses' but '"+name+"'.");
    881            }
    882
    883            final InnerClass[] ics = obj.getInnerClasses();
    884
    885            for (final InnerClass ic : ics) {
    886                checkIndex(obj, ic.getInnerClassIndex(), CONST_Class);
    887                final int outer_idx = ic.getOuterClassIndex();
    888                if (outer_idx != 0) {
    889                    checkIndex(obj, outer_idx, CONST_Class);
    890                }
    891                final int innername_idx = ic.getInnerNameIndex();
    892                if (innername_idx != 0) {
    893                    checkIndex(obj, innername_idx, CONST_Utf8);
    894                }
    895                int acc = ic.getInnerAccessFlags();
    896                acc = acc & (~ (Const.ACC_PUBLIC | Const.ACC_PRIVATE | Const.ACC_PROTECTED |
    897                                Const.ACC_STATIC | Const.ACC_FINAL | Const.ACC_INTERFACE | Const.ACC_ABSTRACT));
    898                if (acc != 0) {
    899                    addMessage(
    900                        "Unknown access flag for inner class '"+tostring(ic)+"' set (InnerClasses attribute '"+tostring(obj)+"').");
    901                }
    902            }
    903            // Semantical consistency is not yet checked by Sun, see vmspec2 4.7.5.
    904            // [marked TODO in JustIce]
    905        }
    906        ////////////////////////////////////////////////////////
    907        // field_info-structure-ATTRIBUTES (vmspec2 4.5, 4.7) //
    908        ////////////////////////////////////////////////////////
    909        @Override
    910        public void visitConstantValue(final ConstantValue obj) {//vmspec2 4.7.2
    911            // Despite its name, this really is an Attribute,
    912            // not a constant!
    913            checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    914
    915            final String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    916            if (! name.equals("ConstantValue")) {
    917                throw new ClassConstraintException(
    918                    "The ConstantValue attribute '"+tostring(obj)+"' is not correctly named 'ConstantValue' but '"+name+"'.");
    919            }
    920
    921            final Object pred = carrier.predecessor();
    922            if (pred instanceof Field) { //ConstantValue attributes are quite senseless if the predecessor is not a field.
    923                final Field f = (Field) pred;
    924                // Field constraints have been checked before -- so we are safe using their type information.
    925                final Type field_type = Type.getType(((ConstantUtf8) (cp.getConstant(f.getSignatureIndex()))).getBytes());
    926
    927                final int index = obj.getConstantValueIndex();
    928                if ((index < 0) || (index >= cplen)) {
    929                    throw new ClassConstraintException("Invalid index '"+index+"' used by '"+tostring(obj)+"'.");
    930                }
    931                final Constant c = cp.getConstant(index);
    932
    933                if (CONST_Long.isInstance(c) && field_type.equals(Type.LONG)) {
    934                    return;
    935                }
    936                if (CONST_Float.isInstance(c) && field_type.equals(Type.FLOAT)) {
    937                    return;
    938                }
    939                if (CONST_Double.isInstance(c) && field_type.equals(Type.DOUBLE)) {
    940                    return;
    941                }
    942                if (CONST_Integer.isInstance(c) && (field_type.equals(Type.INT) || field_type.equals(Type.SHORT) ||
    943                   field_type.equals(Type.CHAR) || field_type.equals(Type.BYTE) || field_type.equals(Type.BOOLEAN))) {
    944                    return;
    945                }
    946                if (CONST_String.isInstance(c) && field_type.equals(Type.STRING)) {
    947                    return;
    948                }
    949
    950                throw new ClassConstraintException("Illegal type of ConstantValue '"+obj+"' embedding Constant '"+c+
    951                    "'. It is referenced by field '"+tostring(f)+"' expecting a different type: '"+field_type+"'.");
    952            }
    953        }
    954        // SYNTHETIC: see above
    955        // DEPRECATED: see above
    956        /////////////////////////////////////////////////////////
    957        // method_info-structure-ATTRIBUTES (vmspec2 4.6, 4.7) //
    958        /////////////////////////////////////////////////////////
    959        @Override
    960        public void visitCode(final Code obj) {//vmspec2 4.7.3
    961            try {
    962            // No code attribute allowed for native or abstract methods: see visitMethod(Method).
    963            // Code array constraints are checked in Pass3 (3a and 3b).
    964
    965            checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    966
    967            final String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    968            if (! name.equals("Code")) {
    969                throw new ClassConstraintException(
    970                    "The Code attribute '"+tostring(obj)+"' is not correctly named 'Code' but '"+name+"'.");
    971            }
    972
    973            Method m = null; // satisfy compiler
    974            if (!(carrier.predecessor() instanceof Method)) {
    975                addMessage("Code attribute '"+tostring(obj)+"' is not declared in a method_info structure but in '"+
    976                            carrier.predecessor()+"'. Ignored.");
    977                return;
    978            }
    979            m = (Method) carrier.predecessor();    // we can assume this method was visited before;
    980                                                                                    // i.e. the data consistency was verified.
    981
    982            if (obj.getCode().length == 0) {
    983                throw new ClassConstraintException(
    984                    "Code array of Code attribute '"+tostring(obj)+"' (method '"+m+"') must not be empty.");
    985            }
    986
    987            //In JustIce, the check for correct offsets into the code array is delayed to Pass 3a.
    988            final CodeException[] exc_table = obj.getExceptionTable();
    989            for (final CodeException element : exc_table) {
    990                final int exc_index = element.getCatchType();
    991                if (exc_index != 0) { // if 0, it catches all Throwables
    992                    checkIndex(obj, exc_index, CONST_Class);
    993                    final ConstantClass cc = (ConstantClass) (cp.getConstant(exc_index));
    994                    // cannot be sure this ConstantClass has already been visited (checked)!
    995                    checkIndex(cc, cc.getNameIndex(), CONST_Utf8);
    996                    final String cname = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes().replace('/','.');
    997
    998                    Verifier v = VerifierFactory.getVerifier(cname);
    999                    VerificationResult vr = v.doPass1();
    1000
    1001                    if (vr != VerificationResult.VR_OK) {
    1002                        throw new ClassConstraintException("Code attribute '"+tostring(obj)+"' (method '"+m+
    1003                           "') has an exception_table entry '"+tostring(element)+"' that references '"+cname+
    1004                           "' as an Exception but it does not pass verification pass 1: "+vr);
    1005                    }
    1006                    // We cannot safely trust any other "instanceof" mechanism. We need to transitively verify
    1007                    // the ancestor hierarchy.
    1008                    JavaClass e = Repository.lookupClass(cname);
    1009                    final JavaClass t = Repository.lookupClass(Type.THROWABLE.getClassName());
    1010                    final JavaClass o = Repository.lookupClass(Type.OBJECT.getClassName());
    1011                    while (e != o) {
    1012                        if (e == t) {
    1013                            break; // It's a subclass of Throwable, OKAY, leave.
    1014                        }
    1015
    1016                        v = VerifierFactory.getVerifier(e.getSuperclassName());
    1017                        vr = v.doPass1();
    1018                        if (vr != VerificationResult.VR_OK) {
    1019                            throw new ClassConstraintException("Code attribute '"+tostring(obj)+"' (method '"+m+
    1020                                "') has an exception_table entry '"+tostring(element)+"' that references '"+cname+
    1021                                "' as an Exception but '"+e.getSuperclassName()+
    1022                                "' in the ancestor hierachy does not pass verification pass 1: "+vr);
    1023                        }
    1024                        e = Repository.lookupClass(e.getSuperclassName());
    1025                    }
    1026                    if (e != t) {
    1027                        throw new ClassConstraintException("Code attribute '"+tostring(obj)+"' (method '"+m+
    1028                            "') has an exception_table entry '"+tostring(element)+"' that references '"+cname+
    1029                            "' as an Exception but it is not a subclass of '"+t.getClassName()+"'.");
    1030                    }
    1031                }
    1032            }
    1033
    1034            // Create object for local variables information
    1035            // This is highly unelegant due to usage of the Visitor pattern.
    1036            // TODO: rework it.
    1037            int method_number = -1;
    1038            final Method[] ms = Repository.lookupClass(myOwner.getClassName()).getMethods();
    1039            for (int mn=0; mn<ms.length; mn++) {
    1040                if (m == ms[mn]) {
    1041                    method_number = mn;
    1042                    break;
    1043                }
    1044            }
    1045            if (method_number < 0) { // Mmmmh. Can we be sure BCEL does not sometimes instantiate new objects?
    1046                throw new AssertionViolatedException(
    1047                    "Could not find a known BCEL Method object in the corresponding BCEL JavaClass object.");
    1048            }
    1049            localVariablesInfos[method_number] = new LocalVariablesInfo(obj.getMaxLocals());
    1050
    1051            int num_of_lvt_attribs = 0;
    1052            // Now iterate through the attributes the Code attribute has.
    1053            final Attribute[] atts = obj.getAttributes();
    1054            for (int a=0; a<atts.length; a++) {
    1055                if ((! (atts[a] instanceof LineNumberTable)) &&
    1056                    (! (atts[a] instanceof LocalVariableTable))) {
    1057                    addMessage("Attribute '"+tostring(atts[a])+"' as an attribute of Code attribute '"+tostring(obj)+
    1058                        "' (method '"+m+"') is unknown and will therefore be ignored.");
    1059                }
    1060                else{// LineNumberTable or LocalVariableTable
    1061                    addMessage("Attribute '"+tostring(atts[a])+"' as an attribute of Code attribute '"+tostring(obj)+
    1062                        "' (method '"+m+"') will effectively be ignored and is only useful for debuggers and such.");
    1063                }
    1064
    1065                //LocalVariableTable check (partially delayed to Pass3a).
    1066                //Here because its easier to collect the information of the
    1067                //(possibly more than one) LocalVariableTables belonging to
    1068                //one certain Code attribute.
    1069                if (atts[a] instanceof LocalVariableTable) { // checks conforming to vmspec2 4.7.9
    1070
    1071                    final LocalVariableTable lvt = (LocalVariableTable) atts[a];
    1072
    1073                    checkIndex(lvt, lvt.getNameIndex(), CONST_Utf8);
    1074
    1075                    final String lvtname = ((ConstantUtf8) cp.getConstant(lvt.getNameIndex())).getBytes();
    1076                    if (! lvtname.equals("LocalVariableTable")) {
    1077                        throw new ClassConstraintException("The LocalVariableTable attribute '"+tostring(lvt)+
    1078                                "' is not correctly named 'LocalVariableTable' but '"+lvtname+"'.");
    1079                    }
    1080
    1081                    final Code code = obj;
    1082
    1083                    //In JustIce, the check for correct offsets into the code array is delayed to Pass 3a.
    1084                    final LocalVariable[] localvariables = lvt.getLocalVariableTable();
    1085
    1086                    for (final LocalVariable localvariable : localvariables) {
    1087                        checkIndex(lvt, localvariable.getNameIndex(), CONST_Utf8);
    1088                        final String localname = ((ConstantUtf8) cp.getConstant(localvariable.getNameIndex())).getBytes();
    1089                        if (!validJavaIdentifier(localname)) {
    1090                            throw new ClassConstraintException("LocalVariableTable '"+tostring(lvt)+
    1091                                "' references a local variable by the name '"+localname+"' which is not a legal Java simple name.");
    1092                        }
    1093
    1094                        checkIndex(lvt, localvariable.getSignatureIndex(), CONST_Utf8);
    1095                        final String localsig  =
    1096                            ((ConstantUtf8) (cp.getConstant(localvariable.getSignatureIndex()))).getBytes(); // Local sig.(=descriptor)
    1097                        Type t;
    1098                        try{
    1099                            t = Type.getType(localsig);
    1100                        }
    1101                        catch (final ClassFormatException cfe) {
    1102                            throw new ClassConstraintException("Illegal descriptor (==signature) '"+localsig+
    1103                                "' used by LocalVariable '"+tostring(localvariable)+"' referenced by '"+tostring(lvt)+"'.", cfe);
    1104                        }
    1105                        final int localindex = localvariable.getIndex();
    1106                        if ( ( (t==Type.LONG || t==Type.DOUBLE)? localindex+1:localindex) >= code.getMaxLocals()) {
    1107                            throw new ClassConstraintException("LocalVariableTable attribute '"+tostring(lvt)+
    1108                                "' references a LocalVariable '"+tostring(localvariable)+
    1109                                "' with an index that exceeds the surrounding Code attribute's max_locals value of '"+
    1110                                code.getMaxLocals()+"'.");
    1111                        }
    1112
    1113                        try{
    1114                            localVariablesInfos[method_number].add(localindex, localname, localvariable.getStartPC(),
    1115                                                                   localvariable.getLength(), t);
    1116                        }
    1117                        catch(final LocalVariableInfoInconsistentException lviie) {
    1118                            throw new ClassConstraintException("Conflicting information in LocalVariableTable '"+tostring(lvt)+
    1119                                "' found in Code attribute '"+tostring(obj)+
    1120                                "' (method '"+tostring(m)+"'). "+lviie.getMessage(), lviie);
    1121                        }
    1122                    }// for all local variables localvariables[i] in the LocalVariableTable attribute atts[a] END
    1123
    1124                    num_of_lvt_attribs++;
    1125                    if (!m.isStatic() && num_of_lvt_attribs > obj.getMaxLocals()) {
    1126                        throw new ClassConstraintException("Number of LocalVariableTable attributes of Code attribute '"+
    1127                            tostring(obj)+"' (method '"+tostring(m)+"') exceeds number of local variable slots '"+obj.getMaxLocals()+
    1128                            "' ('There may be at most one LocalVariableTable attribute per local variable in the Code attribute.').");
    1129                    }
    1130                }// if atts[a] instanceof LocalVariableTable END
    1131            }// for all attributes atts[a] END
    1132
    1133            } catch (final ClassNotFoundException e) {
    1134            // FIXME: this might not be the best way to handle missing classes.
    1135            throw new AssertionViolatedException("Missing class: " + e, e);
    1136            }
    1137
    1138        }// visitCode(Code) END
    1139
    1140        @Override
    1141        public void visitExceptionTable(final ExceptionTable obj) {//vmspec2 4.7.4
    1142            try {
    1143            // incorrectly named, it's the Exceptions attribute (vmspec2 4.7.4)
    1144            checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    1145
    1146            final String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    1147            if (! name.equals("Exceptions")) {
    1148                throw new ClassConstraintException(
    1149                    "The Exceptions attribute '"+tostring(obj)+"' is not correctly named 'Exceptions' but '"+name+"'.");
    1150            }
    1151
    1152            final int[] exc_indices = obj.getExceptionIndexTable();
    1153
    1154            for (final int exc_indice : exc_indices) {
    1155                checkIndex(obj, exc_indice, CONST_Class);
    1156
    1157                final ConstantClass cc = (ConstantClass) (cp.getConstant(exc_indice));
    1158                checkIndex(cc, cc.getNameIndex(), CONST_Utf8); // can't be sure this ConstantClass has already been visited (checked)!
    1159                //convert internal notation on-the-fly to external notation:
    1160                final String cname = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes().replace('/','.');
    1161
    1162                Verifier v = VerifierFactory.getVerifier(cname);
    1163                VerificationResult vr = v.doPass1();
    1164
    1165                if (vr != VerificationResult.VR_OK) {
    1166                    throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+
    1167                            "' as an Exception but it does not pass verification pass 1: "+vr);
    1168                }
    1169                // We cannot safely trust any other "instanceof" mechanism. We need to transitively verify
    1170                // the ancestor hierarchy.
    1171                JavaClass e = Repository.lookupClass(cname);
    1172                final JavaClass t = Repository.lookupClass(Type.THROWABLE.getClassName());
    1173                final JavaClass o = Repository.lookupClass(Type.OBJECT.getClassName());
    1174                while (e != o) {
    1175                    if (e == t) {
    1176                        break; // It's a subclass of Throwable, OKAY, leave.
    1177                    }
    1178
    1179                    v = VerifierFactory.getVerifier(e.getSuperclassName());
    1180                    vr = v.doPass1();
    1181                    if (vr != VerificationResult.VR_OK) {
    1182                        throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+
    1183                                "' as an Exception but '"+e.getSuperclassName()+
    1184                                "' in the ancestor hierachy does not pass verification pass 1: "+vr);
    1185                    }
    1186                    e = Repository.lookupClass(e.getSuperclassName());
    1187                }
    1188                if (e != t) {
    1189                    throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+
    1190                            "' as an Exception but it is not a subclass of '"+t.getClassName()+"'.");
    1191                }
    1192            }
    1193
    1194            } catch (final ClassNotFoundException e) {
    1195            // FIXME: this might not be the best way to handle missing classes.
    1196            throw new AssertionViolatedException("Missing class: " + e, e);
    1197            }
    1198        }
    1199        // SYNTHETIC: see above
    1200        // DEPRECATED: see above
    1201        //////////////////////////////////////////////////////////////
    1202        // code_attribute-structure-ATTRIBUTES (vmspec2 4.7.3, 4.7) //
    1203        //////////////////////////////////////////////////////////////
    1204        @Override
    1205        public void visitLineNumberTable(final LineNumberTable obj) {//vmspec2 4.7.8
    1206            checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    1207
    1208            final String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
    1209            if (! name.equals("LineNumberTable")) {
    1210                throw new ClassConstraintException("The LineNumberTable attribute '"+tostring(obj)+
    1211                        "' is not correctly named 'LineNumberTable' but '"+name+"'.");
    1212            }
    1213
    1214            //In JustIce,this check is delayed to Pass 3a.
    1215            //LineNumber[] linenumbers = obj.getLineNumberTable();
    1216            // ...validity check...
    1217
    1218        }
    1219        @Override
    1220        public void visitLocalVariableTable(final LocalVariableTable obj) {//vmspec2 4.7.9
    1221            //In JustIce,this check is partially delayed to Pass 3a.
    1222            //The other part can be found in the visitCode(Code) method.
    1223        }
    1224        ////////////////////////////////////////////////////
    1225        // MISC-structure-ATTRIBUTES (vmspec2 4.7.1, 4.7) //
    1226        ////////////////////////////////////////////////////
    1227        @Override
    1228        public void visitUnknown(final Unknown obj) {//vmspec2 4.7.1
    1229            // Represents an unknown attribute.
    1230            checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
    1231
    1232            // Maybe only misnamed? Give a (warning) message.
    1233            addMessage("Unknown attribute '"+tostring(obj)+"'. This attribute is not known in any context!");
    1234        }
    1235        //////////
    1236        // BCEL //
    1237        //////////
    1238        @Override
    1239        public void visitLocalVariable(final LocalVariable obj) {
    1240            // This does not represent an Attribute but is only
    1241            // related to internal BCEL data representation.
    1242
    1243            // see visitLocalVariableTable(LocalVariableTable)
    1244        }
    1245        @Override
    1246        public void visitCodeException(final CodeException obj) {
    1247            // Code constraints are checked in Pass3 (3a and 3b).
    1248            // This does not represent an Attribute but is only
    1249            // related to internal BCEL data representation.
    1250
    1251            // see visitCode(Code)
    1252        }
    1253        @Override
    1254        public void visitConstantPool(final ConstantPool obj) {
    1255            // No need to. We're piggybacked by the DescendingVisitor.
    1256            // This does not represent an Attribute but is only
    1257            // related to internal BCEL data representation.
    1258        }
    1259        @Override
    1260        public void visitInnerClass(final InnerClass obj) {
    1261            // This does not represent an Attribute but is only
    1262            // related to internal BCEL data representation.
    1263        }
    1264        @Override
    1265        public void visitLineNumber(final LineNumber obj) {
    1266            // This does not represent an Attribute but is only
    1267            // related to internal BCEL data representation.
    1268
    1269            // see visitLineNumberTable(LineNumberTable)
    1270        }
    1271    }
    1272
    1273    /**
    1274     * Ensures that the ConstantCP-subclassed entries of the constant
    1275     * pool are valid. According to "Yellin: Low Level Security in Java",
    1276     * this method does not verify the existence of referenced entities
    1277     * (such as classes) but only the formal correctness (such as well-formed
    1278     * signatures).
    1279     * The visitXXX() methods throw ClassConstraintException instances otherwise.
    1280     * <B>Precondition: index-style cross referencing in the constant
    1281     * pool must be valid. Simply invoke constant_pool_entries_satisfy_static_constraints()
    1282     * before.</B>
    1283     *
    1284     * @throws ClassConstraintException otherwise.
    1285     * @see #constant_pool_entries_satisfy_static_constraints()
    1286     */
    1287    private void field_and_method_refs_are_valid() {
    1288        try {
    1289        final JavaClass jc = Repository.lookupClass(myOwner.getClassName());
    1290        final DescendingVisitor v = new DescendingVisitor(jc, new FAMRAV_Visitor(jc));
    1291        v.visit();
    1292
    1293        } catch (final ClassNotFoundException e) {
    1294        // FIXME: this might not be the best way to handle missing classes.
    1295        throw new AssertionViolatedException("Missing class: " + e, e);
    1296        }
    1297    }
    1298
    1299    /**
    1300     * A Visitor class that ensures the ConstantCP-subclassed entries
    1301     * of the constant pool are valid.
    1302     * <B>Precondition: index-style cross referencing in the constant
    1303     * pool must be valid.</B>
    1304     *
    1305     * @see #constant_pool_entries_satisfy_static_constraints()
    1306     * @see org.apache.bcel.classfile.ConstantCP
    1307     */
    1308    private final class FAMRAV_Visitor extends EmptyVisitor{
    1309        private final ConstantPool cp; // ==jc.getConstantPool() -- only here to save typing work.
    1310        private FAMRAV_Visitor(final JavaClass _jc) {
    1311            cp = _jc.getConstantPool();
    1312        }
    1313
    1314        @Override
    1315        public void visitConstantFieldref(final ConstantFieldref obj) {
    1316            if (obj.getTag() != Const.CONSTANT_Fieldref) {
    1317                throw new ClassConstraintException("ConstantFieldref '"+tostring(obj)+"' has wrong tag!");
    1318            }
    1319            final int name_and_type_index = obj.getNameAndTypeIndex();
    1320            final ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
    1321            final String name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes(); // Field or Method name
    1322            if (!validFieldName(name)) {
    1323                throw new ClassConstraintException("Invalid field name '"+name+"' referenced by '"+tostring(obj)+"'.");
    1324            }
    1325
    1326            final int class_index = obj.getClassIndex();
    1327            final ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
    1328            final String className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes(); // Class Name in internal form
    1329            if (! validClassName(className)) {
    1330                throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
    1331            }
    1332
    1333            final String sig  = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes(); // Field or Method sig.(=descriptor)
    1334
    1335            try{
    1336                Type.getType(sig); /* Don't need the return value */
    1337            }
    1338            catch (final ClassFormatException cfe) {
    1339                throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.", cfe);
    1340            }
    1341        }
    1342
    1343        @Override
    1344        public void visitConstantMethodref(final ConstantMethodref obj) {
    1345            if (obj.getTag() != Const.CONSTANT_Methodref) {
    1346                throw new ClassConstraintException("ConstantMethodref '"+tostring(obj)+"' has wrong tag!");
    1347            }
    1348            final int name_and_type_index = obj.getNameAndTypeIndex();
    1349            final ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
    1350            final String name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes(); // Field or Method name
    1351            if (!validClassMethodName(name)) {
    1352                throw new ClassConstraintException(
    1353                    "Invalid (non-interface) method name '"+name+"' referenced by '"+tostring(obj)+"'.");
    1354            }
    1355
    1356            final int class_index = obj.getClassIndex();
    1357            final ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
    1358            final String className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes(); // Class Name in internal form
    1359            if (! validClassName(className)) {
    1360                throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
    1361            }
    1362
    1363            final String sig  = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes(); // Field or Method sig.(=descriptor)
    1364
    1365            try{
    1366                final Type   t  = Type.getReturnType(sig);
    1367                if ( name.equals(Const.CONSTRUCTOR_NAME) && (t != Type.VOID) ) {
    1368                    throw new ClassConstraintException("Instance initialization method must have VOID return type.");
    1369                }
    1370            }
    1371            catch (final ClassFormatException cfe) {
    1372                throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.", cfe);
    1373            }
    1374        }
    1375
    1376        @Override
    1377        public void visitConstantInterfaceMethodref(final ConstantInterfaceMethodref obj) {
    1378            if (obj.getTag() != Const.CONSTANT_InterfaceMethodref) {
    1379                throw new ClassConstraintException("ConstantInterfaceMethodref '"+tostring(obj)+"' has wrong tag!");
    1380            }
    1381            final int name_and_type_index = obj.getNameAndTypeIndex();
    1382            final ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
    1383            final String name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes(); // Field or Method name
    1384            if (!validInterfaceMethodName(name)) {
    1385                throw new ClassConstraintException("Invalid (interface) method name '"+name+"' referenced by '"+tostring(obj)+"'.");
    1386            }
    1387
    1388            final int class_index = obj.getClassIndex();
    1389            final ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
    1390            final String className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes(); // Class Name in internal form
    1391            if (! validClassName(className)) {
    1392                throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
    1393            }
    1394
    1395            final String sig  = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes(); // Field or Method sig.(=descriptor)
    1396
    1397            try{
    1398                final Type   t  = Type.getReturnType(sig);
    1399                if ( name.equals(Const.STATIC_INITIALIZER_NAME) && (t != Type.VOID) ) {
    1400                    addMessage("Class or interface initialization method '"+Const.STATIC_INITIALIZER_NAME+
    1401                        "' usually has VOID return type instead of '"+t+
    1402                        "'. Note this is really not a requirement of The Java Virtual Machine Specification, Second Edition.");
    1403                }
    1404            }
    1405            catch (final ClassFormatException cfe) {
    1406                throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.", cfe);
    1407            }
    1408
    1409        }
    1410
    1411    }
    1412
    1413    /**
    1414     * This method returns true if and only if the supplied String
    1415     * represents a valid Java class name.
    1416     */
    1417    private static boolean validClassName(final String name) {
    1418        /*
    1419         * TODO: implement.
    1420         * Are there any restrictions?
    1421         */
    1422        return true;
    1423    }
    1424    /**
    1425     * This method returns true if and only if the supplied String
    1426     * represents a valid method name.
    1427     * This is basically the same as a valid identifier name in the
    1428     * Java programming language, but the special name for
    1429     * the instance initialization method is allowed and the special name
    1430     * for the class/interface initialization method may be allowed.
    1431     */
    1432    private static boolean validMethodName(final String name, final boolean allowStaticInit) {
    1433        if (validJavaLangMethodName(name)) {
    1434            return true;
    1435        }
    1436
    1437        if (allowStaticInit) {
    1438            return name.equals(Const.CONSTRUCTOR_NAME) || name.equals(Const.STATIC_INITIALIZER_NAME);
    1439        }
    1440        return name.equals(Const.CONSTRUCTOR_NAME);
    1441    }
    1442
    1443    /**
    1444     * This method returns true if and only if the supplied String
    1445     * represents a valid method name that may be referenced by
    1446     * ConstantMethodref objects.
    1447     */
    1448    private static boolean validClassMethodName(final String name) {
    1449        return validMethodName(name, false);
    1450    }
    1451
    1452    /**
    1453     * This method returns true if and only if the supplied String
    1454     * represents a valid Java programming language method name stored as a simple
    1455     * (non-qualified) name.
    1456     * Conforming to: The Java Virtual Machine Specification, Second Edition, �2.7, �2.7.1, �2.2.
    1457     */
    1458    private static boolean validJavaLangMethodName(final String name) {
    1459        if (!Character.isJavaIdentifierStart(name.charAt(0))) {
    1460            return false;
    1461        }
    1462
    1463        for (int i=1; i<name.length(); i++) {
    1464            if (!Character.isJavaIdentifierPart(name.charAt(i))) {
    1465                return false;
    1466            }
    1467        }
    1468        return true;
    1469    }
    1470
    1471    /**
    1472     * This method returns true if and only if the supplied String
    1473     * represents a valid Java interface method name that may be
    1474     * referenced by ConstantInterfaceMethodref objects.
    1475     */
    1476    private static boolean validInterfaceMethodName(final String name) {
    1477        // I guess we should assume special names forbidden here.
    1478        if (name.startsWith("<")) {
    1479            return false;
    1480        }
    1481        return validJavaLangMethodName(name);
    1482    }
    1483
    1484    /**
    1485     * This method returns true if and only if the supplied String
    1486     * represents a valid Java identifier (so-called simple name).
    1487     */
    1488    private static boolean validJavaIdentifier(final String name) {
    1489    if  (name.length() == 0) {
    1490        return false; // must not be empty, reported by <francis.andre@easynet.fr>, thanks!
    1491    }
    1492
    1493        // vmspec2 2.7, vmspec2 2.2
    1494        if (!Character.isJavaIdentifierStart(name.charAt(0))) {
    1495            return false;
    1496        }
    1497
    1498        for (int i=1; i<name.length(); i++) {
    1499            if (!Character.isJavaIdentifierPart(name.charAt(i))) {
    1500                return false;
    1501            }
    1502        }
    1503        return true;
    1504    }
    1505
    1506    /**
    1507     * This method returns true if and only if the supplied String
    1508     * represents a valid Java field name.
    1509     */
    1510    private static boolean validFieldName(final String name) {
    1511        // vmspec2 2.7, vmspec2 2.2
    1512        return validJavaIdentifier(name);
    1513    }
    1514
    1515    /**
    1516     * This class serves for finding out if a given JavaClass' ConstantPool
    1517     * references an Inner Class.
    1518     * The Java Virtual Machine Specification, Second Edition is not very precise
    1519     * about when an "InnerClasses" attribute has to appear. However, it states that
    1520     * there has to be exactly one InnerClasses attribute in the ClassFile structure
    1521     * if the constant pool of a class or interface refers to any class or interface
    1522     * "that is not a member of a package". Sun does not mean "member of the default
    1523     * package". In "Inner Classes Specification" they point out how a "bytecode name"
    1524     * is derived so one has to deduce what a class name of a class "that is not a
    1525     * member of a package" looks like: there is at least one character in the byte-
    1526     * code name that cannot be part of a legal Java Language Class name (and not equal
    1527     * to '/'). This assumption is wrong as the delimiter is '$' for which
    1528     * Character.isJavaIdentifierPart() == true.
    1529     * Hence, you really run into trouble if you have a toplevel class called
    1530     * "A$XXX" and another toplevel class called "A" with in inner class called "XXX".
    1531     * JustIce cannot repair this; please note that existing verifiers at this
    1532     * time even fail to detect missing InnerClasses attributes in pass 2.
    1533     */
    1534    private static class InnerClassDetector extends EmptyVisitor{
    1535        private boolean hasInnerClass = false;
    1536        private final JavaClass jc;
    1537        private final ConstantPool cp;
    1538
    1539        /** Constructs an InnerClassDetector working on the JavaClass _jc. */
    1540        public InnerClassDetector(final JavaClass _jc) {
    1541            jc = _jc;
    1542            cp = jc.getConstantPool();
    1543            (new DescendingVisitor(jc, this)).visit();
    1544        }
    1545        /**
    1546         * Returns if the JavaClass this InnerClassDetector is working on
    1547         * has an Inner Class reference in its constant pool.
    1548         */
    1549        public boolean innerClassReferenced() {
    1550            return hasInnerClass;
    1551        }
    1552        /** This method casually visits ConstantClass references. */
    1553        @Override
    1554        public void visitConstantClass(final ConstantClass obj) {
    1555            final Constant c = cp.getConstant(obj.getNameIndex());
    1556            if (c instanceof ConstantUtf8) { //Ignore the case where it's not a ConstantUtf8 here, we'll find out later.
    1557                final String classname = ((ConstantUtf8) c).getBytes();
    1558                if (classname.startsWith(jc.getClassName().replace('.','/')+"$")) {
    1559                    hasInnerClass = true;
    1560                }
    1561            }
    1562        }
    1563    }
    1564
    1565    /**
    1566     * This method is here to save typing work and improve code readability.
    1567     */
    1568    private static String tostring(final Node n) {
    1569        return new StringRepresentation(n).toString();
    1570    }
    1571}