View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   *
17   */
18  package org.apache.bcel.verifier.statics;
19  
20  
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.Locale;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import org.apache.bcel.Const;
28  import org.apache.bcel.Constants;
29  import org.apache.bcel.Repository;
30  import org.apache.bcel.classfile.Attribute;
31  import org.apache.bcel.classfile.ClassFormatException;
32  import org.apache.bcel.classfile.Code;
33  import org.apache.bcel.classfile.CodeException;
34  import org.apache.bcel.classfile.Constant;
35  import org.apache.bcel.classfile.ConstantClass;
36  import org.apache.bcel.classfile.ConstantDouble;
37  import org.apache.bcel.classfile.ConstantFieldref;
38  import org.apache.bcel.classfile.ConstantFloat;
39  import org.apache.bcel.classfile.ConstantInteger;
40  import org.apache.bcel.classfile.ConstantInterfaceMethodref;
41  import org.apache.bcel.classfile.ConstantLong;
42  import org.apache.bcel.classfile.ConstantMethodref;
43  import org.apache.bcel.classfile.ConstantNameAndType;
44  import org.apache.bcel.classfile.ConstantPool;
45  import org.apache.bcel.classfile.ConstantString;
46  import org.apache.bcel.classfile.ConstantUtf8;
47  import org.apache.bcel.classfile.ConstantValue;
48  import org.apache.bcel.classfile.Deprecated;
49  import org.apache.bcel.classfile.DescendingVisitor;
50  import org.apache.bcel.classfile.EmptyVisitor;
51  import org.apache.bcel.classfile.ExceptionTable;
52  import org.apache.bcel.classfile.Field;
53  import org.apache.bcel.classfile.InnerClass;
54  import org.apache.bcel.classfile.InnerClasses;
55  import org.apache.bcel.classfile.JavaClass;
56  import org.apache.bcel.classfile.LineNumber;
57  import org.apache.bcel.classfile.LineNumberTable;
58  import org.apache.bcel.classfile.LocalVariable;
59  import org.apache.bcel.classfile.LocalVariableTable;
60  import org.apache.bcel.classfile.Method;
61  import org.apache.bcel.classfile.Node;
62  import org.apache.bcel.classfile.SourceFile;
63  import org.apache.bcel.classfile.Synthetic;
64  import org.apache.bcel.classfile.Unknown;
65  import org.apache.bcel.generic.ArrayType;
66  import org.apache.bcel.generic.ObjectType;
67  import org.apache.bcel.generic.Type;
68  import org.apache.bcel.verifier.PassVerifier;
69  import org.apache.bcel.verifier.VerificationResult;
70  import org.apache.bcel.verifier.Verifier;
71  import org.apache.bcel.verifier.VerifierFactory;
72  import org.apache.bcel.verifier.exc.AssertionViolatedException;
73  import org.apache.bcel.verifier.exc.ClassConstraintException;
74  import org.apache.bcel.verifier.exc.LocalVariableInfoInconsistentException;
75  
76  /**
77   * This PassVerifier verifies a class file according to
78   * pass 2 as described in The Java Virtual Machine
79   * Specification, 2nd edition.
80   * More detailed information is to be found at the do_verify()
81   * method's documentation.
82   *
83   * @see #do_verify()
84   */
85  public final class Pass2Verifier extends PassVerifier implements Constants {
86  
87      /**
88       * The LocalVariableInfo instances used by Pass3bVerifier.
89       * localVariablesInfos[i] denotes the information for the
90       * local variables of method number i in the
91       * JavaClass this verifier operates on.
92       */
93      private LocalVariablesInfo[] localVariablesInfos;
94  
95      /** The Verifier that created this. */
96      private final Verifier myOwner;
97  
98      /**
99       * 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 methodNr) {
117         if (this.verify() != VerificationResult.VR_OK) {
118             return null; // It's cached, don't worry.
119         }
120         if (methodNr < 0 || methodNr >= localVariablesInfos.length) {
121             throw new AssertionViolatedException("Method number out of range.");
122         }
123         return localVariablesInfos[methodNr];
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 Fieldhref="../../../../../org/apache/bcel/classfile/Field.html#Field">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../../../../org/apache/bcel/classfile/ConstantClass.html#ConstantClass">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../../org/apache/bcel/classfile/LocalVariableTable.html#LocalVariableTable">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../../../../org/apache/bcel/classfile/ConstantClass.html#ConstantClass">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 DescendingVisitorndingVisitor.html#DescendingVisitor">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/../org/apache/bcel/classfile/ConstantNameAndType.html#ConstantNameAndType">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../../../../org/apache/bcel/classfile/ConstantClass.html#ConstantClass">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/../org/apache/bcel/classfile/ConstantNameAndType.html#ConstantNameAndType">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../../../../org/apache/bcel/classfile/ConstantClass.html#ConstantClass">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/../org/apache/bcel/classfile/ConstantNameAndType.html#ConstantNameAndType">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../../../../org/apache/bcel/classfile/ConstantClass.html#ConstantClass">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 javaClass) {
1540             this.jc = javaClass;
1541             this.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 }