Source code

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