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  * @version $Id$
84  * @see #do_verify()
85  */
86 public final class Pass2Verifier extends PassVerifier implements Constants {
87 
88     /**
89      * The LocalVariableInfo instances used by Pass3bVerifier.
90      * localVariablesInfos[i] denotes the information for the
91      * local variables of method number i in the
92      * JavaClass this verifier operates on.
93      */
94     private LocalVariablesInfo[] localVariablesInfos;
95 
96     /** The Verifier that created this. */
97     private final Verifier myOwner;
98 
99     /**
100      * Should only be instantiated by a Verifier.
101      *
102      * @see Verifier
103      */
Pass2Verifier(final Verifier owner)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      */
getLocalVariablesInfo(final int method_nr)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
do_verify()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      */
every_class_has_an_accessible_superclass()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      */
final_methods_are_not_overridden()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      */
constant_pool_entries_satisfy_static_constraints()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 
CPESSC_Visitor(final JavaClass _jc)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 
checkIndex(final Node referrer, final int index, final Class<?> shouldbe)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
visitJavaClass(final JavaClass obj)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
visitConstantClass(final ConstantClass obj)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
visitConstantFieldref(final ConstantFieldref obj)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
visitConstantMethodref(final ConstantMethodref obj)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
visitConstantInterfaceMethodref(final ConstantInterfaceMethodref obj)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
visitConstantString(final ConstantString obj)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
visitConstantInteger(final ConstantInteger obj)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
visitConstantFloat(final ConstantFloat obj)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
visitConstantLong(final ConstantLong obj)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
visitConstantDouble(final ConstantDouble obj)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
visitConstantNameAndType(final ConstantNameAndType obj)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
visitConstantUtf8(final ConstantUtf8 obj)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
visitField(final Field obj)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
visitMethod(final Method obj)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
visitSourceFile(final SourceFile obj)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
visitDeprecated(final Deprecated obj)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
visitSynthetic(final Synthetic obj)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
visitInnerClasses(final InnerClasses obj)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
visitConstantValue(final ConstantValue obj)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
visitCode(final Code obj)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
visitExceptionTable(final ExceptionTable obj)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
visitLineNumberTable(final LineNumberTable obj)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
visitLocalVariableTable(final LocalVariableTable obj)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
visitUnknown(final Unknown obj)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
visitLocalVariable(final LocalVariable obj)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
visitCodeException(final CodeException obj)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
visitConstantPool(final ConstantPool obj)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
visitInnerClass(final InnerClass obj)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
visitLineNumber(final LineNumber obj)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      */
field_and_method_refs_are_valid()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.
FAMRAV_Visitor(final JavaClass _jc)1310         private FAMRAV_Visitor(final JavaClass _jc) {
1311             cp = _jc.getConstantPool();
1312         }
1313 
1314         @Override
visitConstantFieldref(final ConstantFieldref obj)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
visitConstantMethodref(final ConstantMethodref obj)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
visitConstantInterfaceMethodref(final ConstantInterfaceMethodref obj)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      */
validClassName(final String name)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      */
validMethodName(final String name, final boolean allowStaticInit)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      */
validClassMethodName(final String name)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      */
validJavaLangMethodName(final String name)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      */
validInterfaceMethodName(final String name)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      */
validJavaIdentifier(final String name)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      */
validFieldName(final String name)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. */
InnerClassDetector(final 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          */
innerClassReferenced()1549         public boolean innerClassReferenced() {
1550             return hasInnerClass;
1551         }
1552         /** This method casually visits ConstantClass references. */
1553         @Override
visitConstantClass(final ConstantClass obj)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      */
tostring(final Node n)1568     private static String tostring(final Node n) {
1569         return new StringRepresentation(n).toString();
1570     }
1571 }
1572