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}