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