KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > bcel > verifier > statics > Pass2Verifier


1 /*
2  * Copyright 2000-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */

17 package org.apache.bcel.verifier.statics;
18
19
20 import java.util.HashMap JavaDoc;
21 import java.util.HashSet JavaDoc;
22 import java.util.Locale JavaDoc;
23 import java.util.Map JavaDoc;
24 import java.util.Set JavaDoc;
25 import org.apache.bcel.Constants;
26 import org.apache.bcel.Repository;
27 import org.apache.bcel.classfile.Attribute;
28 import org.apache.bcel.classfile.ClassFormatException;
29 import org.apache.bcel.classfile.Code;
30 import org.apache.bcel.classfile.CodeException;
31 import org.apache.bcel.classfile.Constant;
32 import org.apache.bcel.classfile.ConstantClass;
33 import org.apache.bcel.classfile.ConstantDouble;
34 import org.apache.bcel.classfile.ConstantFieldref;
35 import org.apache.bcel.classfile.ConstantFloat;
36 import org.apache.bcel.classfile.ConstantInteger;
37 import org.apache.bcel.classfile.ConstantInterfaceMethodref;
38 import org.apache.bcel.classfile.ConstantLong;
39 import org.apache.bcel.classfile.ConstantMethodref;
40 import org.apache.bcel.classfile.ConstantNameAndType;
41 import org.apache.bcel.classfile.ConstantPool;
42 import org.apache.bcel.classfile.ConstantString;
43 import org.apache.bcel.classfile.ConstantUtf8;
44 import org.apache.bcel.classfile.ConstantValue;
45 import org.apache.bcel.classfile.Deprecated;
46 import org.apache.bcel.classfile.DescendingVisitor;
47 import org.apache.bcel.classfile.EmptyVisitor;
48 import org.apache.bcel.classfile.ExceptionTable;
49 import org.apache.bcel.classfile.Field;
50 import org.apache.bcel.classfile.InnerClass;
51 import org.apache.bcel.classfile.InnerClasses;
52 import org.apache.bcel.classfile.JavaClass;
53 import org.apache.bcel.classfile.LineNumber;
54 import org.apache.bcel.classfile.LineNumberTable;
55 import org.apache.bcel.classfile.LocalVariable;
56 import org.apache.bcel.classfile.LocalVariableTable;
57 import org.apache.bcel.classfile.Method;
58 import org.apache.bcel.classfile.Node;
59 import org.apache.bcel.classfile.SourceFile;
60 import org.apache.bcel.classfile.Synthetic;
61 import org.apache.bcel.classfile.Unknown;
62 import org.apache.bcel.classfile.Visitor;
63 import org.apache.bcel.generic.ArrayType;
64 import org.apache.bcel.generic.ObjectType;
65 import org.apache.bcel.generic.Type;
66 import org.apache.bcel.verifier.PassVerifier;
67 import org.apache.bcel.verifier.VerificationResult;
68 import org.apache.bcel.verifier.Verifier;
69 import org.apache.bcel.verifier.VerifierFactory;
70 import org.apache.bcel.verifier.exc.AssertionViolatedException;
71 import org.apache.bcel.verifier.exc.ClassConstraintException;
72 import org.apache.bcel.verifier.exc.LocalVariableInfoInconsistentException;
73
74 /**
75  * This PassVerifier verifies a class file according to
76  * pass 2 as described in The Java Virtual Machine
77  * Specification, 2nd edition.
78  * More detailed information is to be found at the do_verify()
79  * method's documentation.
80  *
81  * @version $Id: Pass2Verifier.java 386056 2006-03-15 11:31:56Z tcurdt $
82  * @author Enver Haase
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 Verifier myOwner;
97
98     /**
99      * Should only be instantiated by a Verifier.
100      *
101      * @see Verifier
102      */

103     public Pass2Verifier(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(int method_nr){
117         if (this.verify() != VerificationResult.VR_OK) {
118             return null; // It's cached, don't worry.
119
}
120         if (method_nr < 0 || method_nr >= localVariablesInfos.length){
121             throw new AssertionViolatedException("Method number out of range.");
122         }
123         return localVariablesInfos[method_nr];
124     }
125     
126     /**
127      * Pass 2 is the pass where static properties of the
128      * class file are checked without looking into "Code"
129      * arrays of methods.
130      * This verification pass is usually invoked when
131      * a class is resolved; and it may be possible that
132      * this verification pass has to load in other classes
133      * such as superclasses or implemented interfaces.
134      * Therefore, Pass 1 is run on them.<BR>
135      * Note that most referenced classes are <B>not</B> loaded
136      * in for verification or for an existance check by this
137      * pass; only the syntactical correctness of their names
138      * and descriptors (a.k.a. signatures) is checked.<BR>
139      * Very few checks that conceptually belong here
140      * are delayed until pass 3a in JustIce. JustIce does
141      * not only check for syntactical correctness but also
142      * for semantical sanity - therefore it needs access to
143      * the "Code" array of methods in a few cases. Please
144      * see the pass 3a documentation, too.
145      *
146      * @see org.apache.bcel.verifier.statics.Pass3aVerifier
147      */

148     public VerificationResult do_verify(){
149         try {
150         VerificationResult vr1 = myOwner.doPass1();
151         if (vr1.equals(VerificationResult.VR_OK)){
152             
153             // For every method, we could have information about the local variables out of LocalVariableTable attributes of
154
// the Code attributes.
155
localVariablesInfos = new LocalVariablesInfo[Repository.lookupClass(myOwner.getClassName()).getMethods().length];
156
157             VerificationResult vr = VerificationResult.VR_OK; // default.
158
try{
159                 constant_pool_entries_satisfy_static_constraints();
160                 field_and_method_refs_are_valid();
161                 every_class_has_an_accessible_superclass();
162                 final_methods_are_not_overridden();
163             }
164             catch (ClassConstraintException cce){
165                 vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage());
166             }
167             return vr;
168         } else {
169             return VerificationResult.VR_NOTYET;
170         }
171
172         } catch (ClassNotFoundException JavaDoc e) {
173         // FIXME: this might not be the best way to handle missing classes.
174
throw new AssertionViolatedException("Missing class: " + e.toString());
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         Set JavaDoc hs = new HashSet JavaDoc(); // 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()+"' missing but not "+Type.OBJECT.getClassName()+" itself!");
203                 }
204             }
205             else{
206                 String JavaDoc supername = jc.getSuperclassName();
207                 if (! hs.add(supername)){ // If supername already is in the list
208
throw new ClassConstraintException("Circular superclass hierarchy detected.");
209                 }
210                 Verifier v = VerifierFactory.getVerifier(supername);
211                 VerificationResult vr = v.doPass1();
212
213                 if (vr != VerificationResult.VR_OK){
214                     throw new ClassConstraintException("Could not load in ancestor class '"+supername+"'.");
215                 }
216                 jc = Repository.lookupClass(supername);
217
218                 if (jc.isFinal()){
219                     throw new ClassConstraintException("Ancestor class '"+supername+"' has the FINAL access modifier and must therefore not be subclassed.");
220                 }
221             }
222         }
223
224         } catch (ClassNotFoundException JavaDoc e) {
225         // FIXME: this might not be the best way to handle missing classes.
226
throw new AssertionViolatedException("Missing class: " + e.toString());
227         }
228     }
229
230     /**
231      * Ensures that <B>final</B> methods are not overridden.
232      * <B>Precondition to run this method:
233      * constant_pool_entries_satisfy_static_constraints() and
234      * every_class_has_an_accessible_superclass() have to be invoked before
235      * (in that order).</B>
236      *
237      * @throws ClassConstraintException otherwise.
238      * @see #constant_pool_entries_satisfy_static_constraints()
239      * @see #every_class_has_an_accessible_superclass()
240      */

241     private void final_methods_are_not_overridden(){
242         try {
243         Map JavaDoc hashmap = new HashMap JavaDoc();
244         JavaClass jc = Repository.lookupClass(myOwner.getClassName());
245         
246         int supidx = -1;
247         while (supidx != 0){
248             supidx = jc.getSuperclassNameIndex();
249
250             Method[] methods = jc.getMethods();
251             for (int i=0; i<methods.length; i++){
252                 String JavaDoc name_and_sig = (methods[i].getName()+methods[i].getSignature());
253
254                 if (hashmap.containsKey(name_and_sig)){
255                     if ( methods[i].isFinal() ){
256                       if (!(methods[i].isPrivate())) {
257                           throw new ClassConstraintException("Method '"+name_and_sig+"' in class '"+hashmap.get(name_and_sig)+"' overrides the final (not-overridable) definition in class '"+jc.getClassName()+"'.");
258                       }
259                       else{
260                           addMessage("Method '"+name_and_sig+"' in class '"+hashmap.get(name_and_sig)+"' overrides the final (not-overridable) definition in class '"+jc.getClassName()+"'. This is okay, as the original definition was private; however this constraint leverage was introduced by JLS 8.4.6 (not vmspec2) and the behaviour of the Sun verifiers.");
261                       }
262                     }
263                     else{
264                         if (!methods[i].isStatic()){ // static methods don't inherit
265
hashmap.put(name_and_sig, jc.getClassName());
266                         }
267                     }
268                 }
269                 else{
270                     if (!methods[i].isStatic()){ // static methods don't inherit
271
hashmap.put(name_and_sig, jc.getClassName());
272                     }
273                 }
274             }
275         
276             jc = Repository.lookupClass(jc.getSuperclassName()); // Well, for OBJECT this returns OBJECT so it works (could return anything but must not throw an Exception).
277
}
278
279         } catch (ClassNotFoundException JavaDoc e) {
280         // FIXME: this might not be the best way to handle missing classes.
281
throw new AssertionViolatedException("Missing class: " + e.toString());
282         }
283
284     }
285
286     /**
287      * Ensures that the constant pool entries satisfy the static constraints
288      * as described in The Java Virtual Machine Specification, 2nd Edition.
289      *
290      * @throws ClassConstraintException otherwise.
291      */

292     private void constant_pool_entries_satisfy_static_constraints(){
293         try {
294         // Most of the consistency is handled internally by BCEL; here
295
// we only have to verify if the indices of the constants point
296
// to constants of the appropriate type and such.
297
JavaClass jc = Repository.lookupClass(myOwner.getClassName());
298         new CPESSC_Visitor(jc); // constructor implicitely traverses jc
299

300         } catch (ClassNotFoundException JavaDoc e) {
301         // FIXME: this might not be the best way to handle missing classes.
302
throw new AssertionViolatedException("Missing class: " + e.toString());
303         }
304     }
305
306     /**
307      * A Visitor class that ensures the constant pool satisfies the static
308      * constraints.
309    * The visitXXX() methods throw ClassConstraintException instances otherwise.
310    *
311    * @see #constant_pool_entries_satisfy_static_constraints()
312      */

313     private class CPESSC_Visitor extends org.apache.bcel.classfile.EmptyVisitor implements Visitor{
314         private Class JavaDoc CONST_Class;
315         /*
316         private Class CONST_Fieldref;
317         private Class CONST_Methodref;
318         private Class CONST_InterfaceMethodref;
319         */

320         private Class JavaDoc CONST_String;
321         private Class JavaDoc CONST_Integer;
322         private Class JavaDoc CONST_Float;
323         private Class JavaDoc CONST_Long;
324         private Class JavaDoc CONST_Double;
325         private Class JavaDoc CONST_NameAndType;
326         private Class JavaDoc CONST_Utf8;
327
328         private final JavaClass jc;
329         private final ConstantPool cp; // ==jc.getConstantPool() -- only here to save typing work and computing power.
330
private final int cplen; // == cp.getLength() -- to save computing power.
331
private DescendingVisitor carrier;
332
333         private Set JavaDoc field_names = new HashSet JavaDoc();
334         private Set JavaDoc field_names_and_desc = new HashSet JavaDoc();
335         private Set JavaDoc method_names_and_desc = new HashSet JavaDoc();
336
337         private CPESSC_Visitor(JavaClass _jc){
338             jc = _jc;
339             cp = _jc.getConstantPool();
340             cplen = cp.getLength();
341
342             CONST_Class = org.apache.bcel.classfile.ConstantClass.class;
343             /*
344             CONST_Fieldref = org.apache.bcel.classfile.ConstantFieldref.class;
345             CONST_Methodref = org.apache.bcel.classfile.ConstantMethodref.class;
346             CONST_InterfaceMethodref = org.apache.bcel.classfile.ConstantInterfaceMethodref.class;
347             */

348             CONST_String = org.apache.bcel.classfile.ConstantString.class;
349             CONST_Integer = org.apache.bcel.classfile.ConstantInteger.class;
350             CONST_Float = org.apache.bcel.classfile.ConstantFloat.class;
351             CONST_Long = org.apache.bcel.classfile.ConstantLong.class;
352             CONST_Double = org.apache.bcel.classfile.ConstantDouble.class;
353             CONST_NameAndType = org.apache.bcel.classfile.ConstantNameAndType.class;
354             CONST_Utf8 = org.apache.bcel.classfile.ConstantUtf8.class;
355
356             carrier = new DescendingVisitor(_jc, this);
357             carrier.visit();
358         }
359
360         private void checkIndex(Node referrer, int index, Class JavaDoc shouldbe){
361             if ((index < 0) || (index >= cplen)){
362                 throw new ClassConstraintException("Invalid index '"+index+"' used by '"+tostring(referrer)+"'.");
363             }
364             Constant c = cp.getConstant(index);
365             if (! shouldbe.isInstance(c)){
366                 /* String isnot = shouldbe.toString().substring(shouldbe.toString().lastIndexOf(".")+1); //Cut all before last "." */
367                 throw new ClassCastException JavaDoc("Illegal constant '"+tostring(c)+"' at index '"+index+"'. '"+tostring(referrer)+"' expects a '"+shouldbe+"'.");
368             }
369         }
370         ///////////////////////////////////////
371
// ClassFile structure (vmspec2 4.1) //
372
///////////////////////////////////////
373
public void visitJavaClass(JavaClass obj){
374             Attribute[] atts = obj.getAttributes();
375             boolean foundSourceFile = false;
376             boolean foundInnerClasses = false;
377
378             // Is there an InnerClass referenced?
379
// This is a costly check; existing verifiers don't do it!
380
boolean hasInnerClass = new InnerClassDetector(jc).innerClassReferenced();
381
382             for (int i=0; i<atts.length; i++){
383                 if ((! (atts[i] instanceof SourceFile)) &&
384                     (! (atts[i] instanceof Deprecated JavaDoc)) &&
385                     (! (atts[i] instanceof InnerClasses)) &&
386                     (! (atts[i] instanceof Synthetic))){
387                     addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of the ClassFile structure '"+tostring(obj)+"' is unknown and will therefore be ignored.");
388                 }
389
390                 if (atts[i] instanceof SourceFile){
391                     if (foundSourceFile == false) {
392                         foundSourceFile = true;
393                     } else {
394                         throw new ClassConstraintException("A ClassFile structure (like '"+tostring(obj)+"') may have no more than one SourceFile attribute."); //vmspec2 4.7.7
395
}
396                 }
397
398                 if (atts[i] instanceof InnerClasses){
399                     if (foundInnerClasses == false) {
400                         foundInnerClasses = true;
401                     } else{
402                         if (hasInnerClass){
403                             throw new ClassConstraintException("A Classfile structure (like '"+tostring(obj)+"') must have exactly one InnerClasses attribute if at least one Inner Class is referenced (which is the case). More than one InnerClasses attribute was found.");
404                         }
405                     }
406                     if (!hasInnerClass){
407                         addMessage("No referenced Inner Class found, but InnerClasses attribute '"+tostring(atts[i])+"' found. Strongly suggest removal of that attribute.");
408                     }
409                 }
410
411             }
412             if (hasInnerClass && !foundInnerClasses){
413                 //throw new ClassConstraintException("A Classfile structure (like '"+tostring(obj)+"') must have exactly one InnerClasses attribute if at least one Inner Class is referenced (which is the case). No InnerClasses attribute was found.");
414
//vmspec2, page 125 says it would be a constraint: but existing verifiers
415
//don't check it and javac doesn't satisfy it when it comes to anonymous
416
//inner classes
417
addMessage("A Classfile structure (like '"+tostring(obj)+"') must have exactly one InnerClasses attribute if at least one Inner Class is referenced (which is the case). No InnerClasses attribute was found.");
418             }
419         }
420         /////////////////////////////
421
// CONSTANTS (vmspec2 4.4) //
422
/////////////////////////////
423
public void visitConstantClass(ConstantClass obj){
424             if (obj.getTag() != Constants.CONSTANT_Class){
425                 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
426             }
427             checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
428
429         }
430         public void visitConstantFieldref(ConstantFieldref obj){
431             if (obj.getTag() != Constants.CONSTANT_Fieldref){
432                 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
433             }
434             checkIndex(obj, obj.getClassIndex(), CONST_Class);
435             checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
436         }
437         public void visitConstantMethodref(ConstantMethodref obj){
438             if (obj.getTag() != Constants.CONSTANT_Methodref){
439                 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
440             }
441             checkIndex(obj, obj.getClassIndex(), CONST_Class);
442             checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
443         }
444         public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj){
445             if (obj.getTag() != Constants.CONSTANT_InterfaceMethodref){
446                 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
447             }
448             checkIndex(obj, obj.getClassIndex(), CONST_Class);
449             checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
450         }
451         public void visitConstantString(ConstantString obj){
452             if (obj.getTag() != Constants.CONSTANT_String){
453                 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
454             }
455             checkIndex(obj, obj.getStringIndex(), CONST_Utf8);
456         }
457         public void visitConstantInteger(ConstantInteger obj){
458             if (obj.getTag() != Constants.CONSTANT_Integer){
459                 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
460             }
461             // no indices to check
462
}
463         public void visitConstantFloat(ConstantFloat obj){
464             if (obj.getTag() != Constants.CONSTANT_Float){
465                 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
466             }
467             //no indices to check
468
}
469         public void visitConstantLong(ConstantLong obj){
470             if (obj.getTag() != Constants.CONSTANT_Long){
471                 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
472             }
473             //no indices to check
474
}
475         public void visitConstantDouble(ConstantDouble obj){
476             if (obj.getTag() != Constants.CONSTANT_Double){
477                 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
478             }
479             //no indices to check
480
}
481         public void visitConstantNameAndType(ConstantNameAndType obj){
482             if (obj.getTag() != Constants.CONSTANT_NameAndType){
483                 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
484             }
485             checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
486             //checkIndex(obj, obj.getDescriptorIndex(), CONST_Utf8); //inconsistently named in BCEL, see below.
487
checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
488         }
489         public void visitConstantUtf8(ConstantUtf8 obj){
490             if (obj.getTag() != Constants.CONSTANT_Utf8){
491                 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
492             }
493             //no indices to check
494
}
495         //////////////////////////
496
// FIELDS (vmspec2 4.5) //
497
//////////////////////////
498
public void visitField(Field obj){
499
500             if (jc.isClass()){
501                 int maxone=0;
502                 if (obj.isPrivate()) {
503                     maxone++;
504                 }
505                 if (obj.isProtected()) {
506                     maxone++;
507                 }
508                 if (obj.isPublic()) {
509                     maxone++;
510                 }
511                 if (maxone > 1){
512                     throw new ClassConstraintException("Field '"+tostring(obj)+"' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set.");
513                 }
514
515                 if (obj.isFinal() && obj.isVolatile()){
516                     throw new ClassConstraintException("Field '"+tostring(obj)+"' must only have at most one of its ACC_FINAL, ACC_VOLATILE modifiers set.");
517                 }
518             }
519             else{ // isInterface!
520
if (!obj.isPublic()){
521                     throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_PUBLIC modifier set but hasn't!");
522                 }
523                 if (!obj.isStatic()){
524                     throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_STATIC modifier set but hasn't!");
525                 }
526                 if (!obj.isFinal()){
527                     throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_FINAL modifier set but hasn't!");
528                 }
529             }
530
531             if ((obj.getAccessFlags() & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_VOLATILE|ACC_TRANSIENT)) > 0){
532                 addMessage("Field '"+tostring(obj)+"' has access flag(s) other than ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_VOLATILE, ACC_TRANSIENT set (ignored).");
533             }
534
535             checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
536
537             String JavaDoc name = obj.getName();
538             if (! validFieldName(name)){
539                 throw new ClassConstraintException("Field '"+tostring(obj)+"' has illegal name '"+obj.getName()+"'.");
540             }
541
542             // A descriptor is often named signature in BCEL
543
checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
544
545             String JavaDoc sig = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
546

547             try{
548                 Type.getType(sig); /* Don't need the return value */
549             }
550             catch (ClassFormatException cfe){
551                 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
552             }
553
554             String JavaDoc nameanddesc = (name+sig);
555             if (field_names_and_desc.contains(nameanddesc)){
556                 throw new ClassConstraintException("No two fields (like '"+tostring(obj)+"') are allowed have same names and descriptors!");
557             }
558             if (field_names.contains(name)){
559                 addMessage("More than one field of name '"+name+"' detected (but with different type descriptors). This is very unusual.");
560             }
561             field_names_and_desc.add(nameanddesc);
562             field_names.add(name);
563
564             Attribute[] atts = obj.getAttributes();
565             for (int i=0; i<atts.length; i++){
566                 if ((! (atts[i] instanceof ConstantValue)) &&
567                     (! (atts[i] instanceof Synthetic)) &&
568                     (! (atts[i] instanceof Deprecated JavaDoc))){
569                     addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Field '"+tostring(obj)+"' is unknown and will therefore be ignored.");
570                 }
571                 if (! (atts[i] instanceof ConstantValue)){
572                     addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Field '"+tostring(obj)+"' is not a ConstantValue and is therefore only of use for debuggers and such.");
573                 }
574             }
575         }
576         ///////////////////////////
577
// METHODS (vmspec2 4.6) //
578
///////////////////////////
579
public void visitMethod(Method obj){
580
581             checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
582
583             String JavaDoc name = obj.getName();
584             if (! validMethodName(name, true)){
585                 throw new ClassConstraintException("Method '"+tostring(obj)+"' has illegal name '"+name+"'.");
586             }
587
588             // A descriptor is often named signature in BCEL
589
checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
590
591             String JavaDoc sig = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes(); // Method's signature(=descriptor)
592

593             Type t;
594             Type[] ts; // needed below the try block.
595
try{
596                 t = Type.getReturnType(sig);
597                 ts = Type.getArgumentTypes(sig);
598             }
599             catch (ClassFormatException cfe){
600                 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by Method '"+tostring(obj)+"'.");
601             }
602
603             // Check if referenced objects exist.
604
Type act = t;
605             if (act instanceof ArrayType) {
606                 act = ((ArrayType) act).getBasicType();
607             }
608             if (act instanceof ObjectType){
609                 Verifier v = VerifierFactory.getVerifier( ((ObjectType) act).getClassName() );
610                 VerificationResult vr = v.doPass1();
611                 if (vr != VerificationResult.VR_OK) {
612                     throw new ClassConstraintException("Method '"+tostring(obj)+"' has a return type that does not pass verification pass 1: '"+vr+"'.");
613                 }
614             }
615
616             for (int i=0; i<ts.length; i++){
617                 act = ts[i];
618                 if (act instanceof ArrayType) {
619                     act = ((ArrayType) act).getBasicType();
620                 }
621                 if (act instanceof ObjectType){
622                     Verifier v = VerifierFactory.getVerifier( ((ObjectType) act).getClassName() );
623                     VerificationResult vr = v.doPass1();
624                     if (vr != VerificationResult.VR_OK) {
625                         throw new ClassConstraintException("Method '"+tostring(obj)+"' has an argument type that does not pass verification pass 1: '"+vr+"'.");
626                     }
627                 }
628             }
629
630             // Nearly forgot this! Funny return values are allowed, but a non-empty arguments list makes a different method out of it!
631
if (name.equals(STATIC_INITIALIZER_NAME) && (ts.length != 0)){
632                 throw new ClassConstraintException("Method '"+tostring(obj)+"' has illegal name '"+name+"'. It's name resembles the class or interface initialization method which it isn't because of its arguments (==descriptor).");
633             }
634
635             if (jc.isClass()){
636                 int maxone=0;
637                 if (obj.isPrivate()) {
638                     maxone++;
639                 }
640                 if (obj.isProtected()) {
641                     maxone++;
642                 }
643                 if (obj.isPublic()) {
644                     maxone++;
645                 }
646                 if (maxone > 1){
647                     throw new ClassConstraintException("Method '"+tostring(obj)+"' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set.");
648                 }
649
650                 if (obj.isAbstract()){
651                     if (obj.isFinal()) {
652                         throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_FINAL modifier set.");
653                     }
654                     if (obj.isNative()) {
655                         throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_NATIVE modifier set.");
656                     }
657                     if (obj.isPrivate()) {
658                         throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_PRIVATE modifier set.");
659                     }
660                     if (obj.isStatic()) {
661                         throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_STATIC modifier set.");
662                     }
663                     if (obj.isStrictfp()) {
664                         throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_STRICT modifier set.");
665                     }
666                     if (obj.isSynchronized()) {
667                         throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_SYNCHRONIZED modifier set.");
668                     }
669                 }
670             }
671             else{ // isInterface!
672
if (!name.equals(STATIC_INITIALIZER_NAME)){//vmspec2, p.116, 2nd paragraph
673
if (!obj.isPublic()){
674                         throw new ClassConstraintException("Interface method '"+tostring(obj)+"' must have the ACC_PUBLIC modifier set but hasn't!");
675                     }
676                     if (!obj.isAbstract()){
677                         throw new ClassConstraintException("Interface method '"+tostring(obj)+"' must have the ACC_STATIC modifier set but hasn't!");
678                     }
679                     if ( obj.isPrivate() ||
680                                 obj.isProtected() ||
681                                 obj.isStatic() ||
682                                 obj.isFinal() ||
683                                 obj.isSynchronized() ||
684                                 obj.isNative() ||
685                                 obj.isStrictfp() ){
686                         throw new ClassConstraintException("Interface method '"+tostring(obj)+"' must not have any of the ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT, ACC_STRICT modifiers set.");
687                     }
688                 }
689             }
690
691             // A specific instance initialization method... (vmspec2,Page 116).
692
if (name.equals(CONSTRUCTOR_NAME)){
693                 //..may have at most one of ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC set: is checked above.
694
//..may also have ACC_STRICT set, but none of the other flags in table 4.5 (vmspec2, page 115)
695
if ( obj.isStatic() ||
696                             obj.isFinal() ||
697                             obj.isSynchronized() ||
698                             obj.isNative() ||
699                             obj.isAbstract() ){
700                     throw new ClassConstraintException("Instance initialization method '"+tostring(obj)+"' must not have any of the ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT modifiers set.");
701                 }
702             }
703
704             // Class and interface initialization methods...
705
if (name.equals(STATIC_INITIALIZER_NAME)){
706                 if ((obj.getAccessFlags() & (~ACC_STRICT)) > 0){
707                     addMessage("Class or interface initialization method '"+tostring(obj)+"' has superfluous access modifier(s) set: everything but ACC_STRICT is ignored.");
708                 }
709                 if (obj.isAbstract()){
710                     throw new ClassConstraintException("Class or interface initialization method '"+tostring(obj)+"' must not be abstract. This contradicts the Java Language Specification, Second Edition (which omits this constraint) but is common practice of existing verifiers.");
711                 }
712             }
713
714             if ((obj.getAccessFlags() & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) > 0){
715                 addMessage("Method '"+tostring(obj)+"' has access flag(s) other than ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT, ACC_STRICT set (ignored).");
716             }
717
718             String JavaDoc nameanddesc = (name+sig);
719             if (method_names_and_desc.contains(nameanddesc)){
720                 throw new ClassConstraintException("No two methods (like '"+tostring(obj)+"') are allowed have same names and desciptors!");
721             }
722             method_names_and_desc.add(nameanddesc);
723
724             Attribute[] atts = obj.getAttributes();
725             int num_code_atts = 0;
726             for (int i=0; i<atts.length; i++){
727                 if ((! (atts[i] instanceof Code)) &&
728                     (! (atts[i] instanceof ExceptionTable)) &&
729                     (! (atts[i] instanceof Synthetic)) &&
730                     (! (atts[i] instanceof Deprecated JavaDoc))){
731                     addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Method '"+tostring(obj)+"' is unknown and will therefore be ignored.");
732                 }
733                 if ((! (atts[i] instanceof Code)) &&
734                         (! (atts[i] instanceof ExceptionTable))){
735                     addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Method '"+tostring(obj)+"' is neither Code nor Exceptions and is therefore only of use for debuggers and such.");
736                 }
737                 if ((atts[i] instanceof Code) && (obj.isNative() || obj.isAbstract())){
738                     throw new ClassConstraintException("Native or abstract methods like '"+tostring(obj)+"' must not have a Code attribute like '"+tostring(atts[i])+"'."); //vmspec2 page120, 4.7.3
739
}
740                 if (atts[i] instanceof Code) {
741                     num_code_atts++;
742                 }
743             }
744             if ( !obj.isNative() && !obj.isAbstract() && num_code_atts != 1){
745                 throw new ClassConstraintException("Non-native, non-abstract methods like '"+tostring(obj)+"' must have exactly one Code attribute (found: "+num_code_atts+").");
746             }
747         }
748         ///////////////////////////////////////////////////////
749
// ClassFile-structure-ATTRIBUTES (vmspec2 4.1, 4.7) //
750
///////////////////////////////////////////////////////
751
public void visitSourceFile(SourceFile obj){//vmspec2 4.7.7
752

753             // zero or one SourceFile attr per ClassFile: see visitJavaClass()
754

755             checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
756
757             String JavaDoc name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
758             if (! name.equals("SourceFile")){
759                 throw new ClassConstraintException("The SourceFile attribute '"+tostring(obj)+"' is not correctly named 'SourceFile' but '"+name+"'.");
760             }
761
762             checkIndex(obj, obj.getSourceFileIndex(), CONST_Utf8);
763
764             String JavaDoc sourcefilename = ((ConstantUtf8) cp.getConstant(obj.getSourceFileIndex())).getBytes(); //==obj.getSourceFileName() ?
765
String JavaDoc sourcefilenamelc = sourcefilename.toLowerCase(Locale.ENGLISH);
766
767             if ( (sourcefilename.indexOf('/') != -1) ||
768                         (sourcefilename.indexOf('\\') != -1) ||
769                         (sourcefilename.indexOf(':') != -1) ||
770                         (sourcefilenamelc.lastIndexOf(".java") == -1) ){
771                 addMessage("SourceFile attribute '"+tostring(obj)+"' has a funny name: remember not to confuse certain parsers working on javap's output. Also, this name ('"+sourcefilename+"') is considered an unqualified (simple) file name only.");
772             }
773         }
774         public void visitDeprecated(Deprecated JavaDoc obj){//vmspec2 4.7.10
775
checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
776
777             String JavaDoc name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
778             if (! name.equals("Deprecated")){
779                 throw new ClassConstraintException("The Deprecated attribute '"+tostring(obj)+"' is not correctly named 'Deprecated' but '"+name+"'.");
780             }
781         }
782         public void visitSynthetic(Synthetic obj){//vmspec2 4.7.6
783
checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
784             String JavaDoc name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
785             if (! name.equals("Synthetic")){
786                 throw new ClassConstraintException("The Synthetic attribute '"+tostring(obj)+"' is not correctly named 'Synthetic' but '"+name+"'.");
787             }
788         }
789         public void visitInnerClasses(InnerClasses obj){//vmspec2 4.7.5
790

791             // exactly one InnerClasses attr per ClassFile if some inner class is refernced: see visitJavaClass()
792

793             checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
794
795             String JavaDoc name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
796             if (! name.equals("InnerClasses")){
797                 throw new ClassConstraintException("The InnerClasses attribute '"+tostring(obj)+"' is not correctly named 'InnerClasses' but '"+name+"'.");
798             }
799
800             InnerClass[] ics = obj.getInnerClasses();
801
802             for (int i=0; i<ics.length; i++){
803                 checkIndex(obj, ics[i].getInnerClassIndex(), CONST_Class);
804                 int outer_idx = ics[i].getOuterClassIndex();
805                 if (outer_idx != 0){
806                     checkIndex(obj, outer_idx, CONST_Class);
807                 }
808                 int innername_idx = ics[i].getInnerNameIndex();
809                 if (innername_idx != 0){
810                     checkIndex(obj, innername_idx, CONST_Utf8);
811                 }
812                 int acc = ics[i].getInnerAccessFlags();
813                 acc = acc & (~ (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT));
814                 if (acc != 0){
815                     addMessage("Unknown access flag for inner class '"+tostring(ics[i])+"' set (InnerClasses attribute '"+tostring(obj)+"').");
816                 }
817             }
818             // Semantical consistency is not yet checked by Sun, see vmspec2 4.7.5.
819
// [marked TODO in JustIce]
820
}
821         ////////////////////////////////////////////////////////
822
// field_info-structure-ATTRIBUTES (vmspec2 4.5, 4.7) //
823
////////////////////////////////////////////////////////
824
public void visitConstantValue(ConstantValue obj){//vmspec2 4.7.2
825
// Despite its name, this really is an Attribute,
826
// not a constant!
827
checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
828
829             String JavaDoc name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
830             if (! name.equals("ConstantValue")){
831                 throw new ClassConstraintException("The ConstantValue attribute '"+tostring(obj)+"' is not correctly named 'ConstantValue' but '"+name+"'.");
832             }
833
834             Object JavaDoc pred = carrier.predecessor();
835             if (pred instanceof Field){ //ConstantValue attributes are quite senseless if the predecessor is not a field.
836
Field f = (Field) pred;
837                 // Field constraints have been checked before -- so we are safe using their type information.
838
Type field_type = Type.getType(((ConstantUtf8) (cp.getConstant(f.getSignatureIndex()))).getBytes());
839
840                 int index = obj.getConstantValueIndex();
841                 if ((index < 0) || (index >= cplen)){
842                     throw new ClassConstraintException("Invalid index '"+index+"' used by '"+tostring(obj)+"'.");
843                 }
844                 Constant c = cp.getConstant(index);
845
846                 if (CONST_Long.isInstance(c) && field_type.equals(Type.LONG)){
847                     return;
848                 }
849                 if (CONST_Float.isInstance(c) && field_type.equals(Type.FLOAT)){
850                     return;
851                 }
852                 if (CONST_Double.isInstance(c) && field_type.equals(Type.DOUBLE)){
853                     return;
854                 }
855                 if (CONST_Integer.isInstance(c) && (field_type.equals(Type.INT) || field_type.equals(Type.SHORT) || field_type.equals(Type.CHAR) || field_type.equals(Type.BYTE) || field_type.equals(Type.BOOLEAN))){
856                     return;
857                 }
858                 if (CONST_String.isInstance(c) && field_type.equals(Type.STRING)){
859                     return;
860                 }
861
862                 throw new ClassConstraintException("Illegal type of ConstantValue '"+obj+"' embedding Constant '"+c+"'. It is referenced by field '"+tostring(f)+"' expecting a different type: '"+field_type+"'.");
863             }
864         }
865         // SYNTHETIC: see above
866
// DEPRECATED: see above
867
/////////////////////////////////////////////////////////
868
// method_info-structure-ATTRIBUTES (vmspec2 4.6, 4.7) //
869
/////////////////////////////////////////////////////////
870
public void visitCode(Code obj){//vmspec2 4.7.3
871
try {
872             // No code attribute allowed for native or abstract methods: see visitMethod(Method).
873
// Code array constraints are checked in Pass3 (3a and 3b).
874

875             checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
876
877             String JavaDoc name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
878             if (! name.equals("Code")){
879                 throw new ClassConstraintException("The Code attribute '"+tostring(obj)+"' is not correctly named 'Code' but '"+name+"'.");
880             }
881
882             Method m = null; // satisfy compiler
883
if (!(carrier.predecessor() instanceof Method)){
884                 addMessage("Code attribute '"+tostring(obj)+"' is not declared in a method_info structure but in '"+carrier.predecessor()+"'. Ignored.");
885                 return;
886             }
887             else{
888                 m = (Method) carrier.predecessor(); // we can assume this method was visited before;
889
// i.e. the data consistency was verified.
890
}
891
892             if (obj.getCode().length == 0){
893                 throw new ClassConstraintException("Code array of Code attribute '"+tostring(obj)+"' (method '"+m+"') must not be empty.");
894             }
895
896             //In JustIce, the check for correct offsets into the code array is delayed to Pass 3a.
897
CodeException[] exc_table = obj.getExceptionTable();
898             for (int i=0; i<exc_table.length; i++){
899                 int exc_index = exc_table[i].getCatchType();
900                 if (exc_index != 0){ // if 0, it catches all Throwables
901
checkIndex(obj, exc_index, CONST_Class);
902                     ConstantClass cc = (ConstantClass) (cp.getConstant(exc_index));
903                     checkIndex(cc, cc.getNameIndex(), CONST_Utf8); // cannot be sure this ConstantClass has already been visited (checked)!
904
String JavaDoc cname = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes().replace('/','.');
905
906                     Verifier v = VerifierFactory.getVerifier(cname);
907                     VerificationResult vr = v.doPass1();
908
909                     if (vr != VerificationResult.VR_OK){
910                         throw new ClassConstraintException("Code attribute '"+tostring(obj)+"' (method '"+m+"') has an exception_table entry '"+tostring(exc_table[i])+"' that references '"+cname+"' as an Exception but it does not pass verification pass 1: "+vr);
911                     }
912                     else{
913                         // We cannot safely trust any other "instanceof" mechanism. We need to transitively verify
914
// the ancestor hierarchy.
915
JavaClass e = Repository.lookupClass(cname);
916                         JavaClass t = Repository.lookupClass(Type.THROWABLE.getClassName());
917                         JavaClass o = Repository.lookupClass(Type.OBJECT.getClassName());
918                         while (e != o){
919                             if (e == t) {
920                                 break; // It's a subclass of Throwable, OKAY, leave.
921
}
922
923                             v = VerifierFactory.getVerifier(e.getSuperclassName());
924                             vr = v.doPass1();
925                             if (vr != VerificationResult.VR_OK){
926                                 throw new ClassConstraintException("Code attribute '"+tostring(obj)+"' (method '"+m+"') has an exception_table entry '"+tostring(exc_table[i])+"' that references '"+cname+"' as an Exception but '"+e.getSuperclassName()+"' in the ancestor hierachy does not pass verification pass 1: "+vr);
927                             }
928                             else{
929                                 e = Repository.lookupClass(e.getSuperclassName());
930                             }
931                         }
932                         if (e != t) {
933                             throw new ClassConstraintException("Code attribute '"+tostring(obj)+"' (method '"+m+"') has an exception_table entry '"+tostring(exc_table[i])+"' that references '"+cname+"' as an Exception but it is not a subclass of '"+t.getClassName()+"'.");
934                         }
935                     }
936                 }
937             }
938
939             // Create object for local variables information
940
// This is highly unelegant due to usage of the Visitor pattern.
941
// TODO: rework it.
942
int method_number = -1;
943             Method[] ms = Repository.lookupClass(myOwner.getClassName()).getMethods();
944             for (int mn=0; mn<ms.length; mn++){
945                 if (m == ms[mn]){
946                     method_number = mn;
947                     break;
948                 }
949             }
950             if (method_number < 0){ // Mmmmh. Can we be sure BCEL does not sometimes instantiate new objects?
951
throw new AssertionViolatedException("Could not find a known BCEL Method object in the corresponding BCEL JavaClass object.");
952             }
953             localVariablesInfos[method_number] = new LocalVariablesInfo(obj.getMaxLocals());
954
955             int num_of_lvt_attribs = 0;
956             // Now iterate through the attributes the Code attribute has.
957
Attribute[] atts = obj.getAttributes();
958             for (int a=0; a<atts.length; a++){
959                 if ((! (atts[a] instanceof LineNumberTable)) &&
960                     (! (atts[a] instanceof LocalVariableTable))){
961                     addMessage("Attribute '"+tostring(atts[a])+"' as an attribute of Code attribute '"+tostring(obj)+"' (method '"+m+"') is unknown and will therefore be ignored.");
962                 }
963                 else{// LineNumberTable or LocalVariableTable
964
addMessage("Attribute '"+tostring(atts[a])+"' as an attribute of Code attribute '"+tostring(obj)+"' (method '"+m+"') will effectively be ignored and is only useful for debuggers and such.");
965                 }
966
967                 //LocalVariableTable check (partially delayed to Pass3a).
968
//Here because its easier to collect the information of the
969
//(possibly more than one) LocalVariableTables belonging to
970
//one certain Code attribute.
971
if (atts[a] instanceof LocalVariableTable){ // checks conforming to vmspec2 4.7.9
972

973                     LocalVariableTable lvt = (LocalVariableTable) atts[a];
974
975                     checkIndex(lvt, lvt.getNameIndex(), CONST_Utf8);
976
977                     String JavaDoc lvtname = ((ConstantUtf8) cp.getConstant(lvt.getNameIndex())).getBytes();
978                     if (! lvtname.equals("LocalVariableTable")){
979                         throw new ClassConstraintException("The LocalVariableTable attribute '"+tostring(lvt)+"' is not correctly named 'LocalVariableTable' but '"+lvtname+"'.");
980                     }
981
982                     Code code = obj;
983
984                     //In JustIce, the check for correct offsets into the code array is delayed to Pass 3a.
985
LocalVariable[] localvariables = lvt.getLocalVariableTable();
986
987                     for (int i=0; i<localvariables.length; i++){
988                         checkIndex(lvt, localvariables[i].getNameIndex(), CONST_Utf8);
989                         String JavaDoc localname = ((ConstantUtf8) cp.getConstant(localvariables[i].getNameIndex())).getBytes();
990                         if (!validJavaIdentifier(localname)){
991                             throw new ClassConstraintException("LocalVariableTable '"+tostring(lvt)+"' references a local variable by the name '"+localname+"' which is not a legal Java simple name.");
992                         }
993
994                         checkIndex(lvt, localvariables[i].getSignatureIndex(), CONST_Utf8);
995                         String JavaDoc localsig = ((ConstantUtf8) (cp.getConstant(localvariables[i].getSignatureIndex()))).getBytes(); // Local signature(=descriptor)
996
Type t;
997                         try{
998                             t = Type.getType(localsig);
999                         }
1000                        catch (ClassFormatException cfe){
1001                            throw new ClassConstraintException("Illegal descriptor (==signature) '"+localsig+"' used by LocalVariable '"+tostring(localvariables[i])+"' referenced by '"+tostring(lvt)+"'.");
1002                        }
1003                        int localindex = localvariables[i].getIndex();
1004                        if ( ( (t==Type.LONG || t==Type.DOUBLE)? localindex+1:localindex) >= code.getMaxLocals()){
1005                            throw new ClassConstraintException("LocalVariableTable attribute '"+tostring(lvt)+"' references a LocalVariable '"+tostring(localvariables[i])+"' with an index that exceeds the surrounding Code attribute's max_locals value of '"+code.getMaxLocals()+"'.");
1006                        }
1007
1008                        try{
1009                            localVariablesInfos[method_number].add(localindex, localname, localvariables[i].getStartPC(), localvariables[i].getLength(), t);
1010                        }
1011                        catch(LocalVariableInfoInconsistentException lviie){
1012                            throw new ClassConstraintException("Conflicting information in LocalVariableTable '"+tostring(lvt)+"' found in Code attribute '"+tostring(obj)+"' (method '"+tostring(m)+"'). "+lviie.getMessage());
1013                        }
1014                    }// for all local variables localvariables[i] in the LocalVariableTable attribute atts[a] END
1015

1016                    num_of_lvt_attribs++;
1017                    if (num_of_lvt_attribs > obj.getMaxLocals()){
1018                        throw new ClassConstraintException("Number of LocalVariableTable attributes of Code attribute '"+tostring(obj)+"' (method '"+tostring(m)+"') exceeds number of local variable slots '"+obj.getMaxLocals()+"' ('There may be no more than one LocalVariableTable attribute per local variable in the Code attribute.').");
1019                    }
1020                }// if atts[a] instanceof LocalVariableTable END
1021
}// for all attributes atts[a] END
1022

1023            } catch (ClassNotFoundException JavaDoc e) {
1024            // FIXME: this might not be the best way to handle missing classes.
1025
throw new AssertionViolatedException("Missing class: " + e.toString());
1026            }
1027
1028        }// visitCode(Code) END
1029

1030        public void visitExceptionTable(ExceptionTable obj){//vmspec2 4.7.4
1031
try {
1032            // incorrectly named, it's the Exceptions attribute (vmspec2 4.7.4)
1033
checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
1034
1035            String JavaDoc name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
1036            if (! name.equals("Exceptions")){
1037                throw new ClassConstraintException("The Exceptions attribute '"+tostring(obj)+"' is not correctly named 'Exceptions' but '"+name+"'.");
1038            }
1039
1040            int[] exc_indices = obj.getExceptionIndexTable();
1041
1042            for (int i=0; i<exc_indices.length; i++){
1043                checkIndex(obj, exc_indices[i], CONST_Class);
1044
1045                ConstantClass cc = (ConstantClass) (cp.getConstant(exc_indices[i]));
1046                checkIndex(cc, cc.getNameIndex(), CONST_Utf8); // cannot be sure this ConstantClass has already been visited (checked)!
1047
String JavaDoc cname = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes().replace('/','.'); //convert internal notation on-the-fly to external notation
1048

1049                Verifier v = VerifierFactory.getVerifier(cname);
1050                VerificationResult vr = v.doPass1();
1051
1052                if (vr != VerificationResult.VR_OK){
1053                    throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+"' as an Exception but it does not pass verification pass 1: "+vr);
1054                }
1055                else{
1056                    // We cannot safely trust any other "instanceof" mechanism. We need to transitively verify
1057
// the ancestor hierarchy.
1058
JavaClass e = Repository.lookupClass(cname);
1059                    JavaClass t = Repository.lookupClass(Type.THROWABLE.getClassName());
1060                    JavaClass o = Repository.lookupClass(Type.OBJECT.getClassName());
1061                    while (e != o){
1062                        if (e == t) {
1063                            break; // It's a subclass of Throwable, OKAY, leave.
1064
}
1065
1066                        v = VerifierFactory.getVerifier(e.getSuperclassName());
1067                        vr = v.doPass1();
1068                        if (vr != VerificationResult.VR_OK){
1069                            throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+"' as an Exception but '"+e.getSuperclassName()+"' in the ancestor hierachy does not pass verification pass 1: "+vr);
1070                        }
1071                        else{
1072                            e = Repository.lookupClass(e.getSuperclassName());
1073                        }
1074                    }
1075                    if (e != t) {
1076                        throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+"' as an Exception but it is not a subclass of '"+t.getClassName()+"'.");
1077                    }
1078                }
1079            }
1080
1081            } catch (ClassNotFoundException JavaDoc e) {
1082            // FIXME: this might not be the best way to handle missing classes.
1083
throw new AssertionViolatedException("Missing class: " + e.toString());
1084            }
1085        }
1086        // SYNTHETIC: see above
1087
// DEPRECATED: see above
1088
//////////////////////////////////////////////////////////////
1089
// code_attribute-structure-ATTRIBUTES (vmspec2 4.7.3, 4.7) //
1090
//////////////////////////////////////////////////////////////
1091
public void visitLineNumberTable(LineNumberTable obj){//vmspec2 4.7.8
1092
checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
1093
1094            String JavaDoc name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
1095            if (! name.equals("LineNumberTable")){
1096                throw new ClassConstraintException("The LineNumberTable attribute '"+tostring(obj)+"' is not correctly named 'LineNumberTable' but '"+name+"'.");
1097            }
1098
1099            //In JustIce,this check is delayed to Pass 3a.
1100
//LineNumber[] linenumbers = obj.getLineNumberTable();
1101
// ...validity check...
1102

1103        }
1104        public void visitLocalVariableTable(LocalVariableTable obj){//vmspec2 4.7.9
1105
//In JustIce,this check is partially delayed to Pass 3a.
1106
//The other part can be found in the visitCode(Code) method.
1107
}
1108        ////////////////////////////////////////////////////
1109
// MISC-structure-ATTRIBUTES (vmspec2 4.7.1, 4.7) //
1110
////////////////////////////////////////////////////
1111
public void visitUnknown(Unknown obj){//vmspec2 4.7.1
1112
// Represents an unknown attribute.
1113
checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
1114
1115            // Maybe only misnamed? Give a (warning) message.
1116
addMessage("Unknown attribute '"+tostring(obj)+"'. This attribute is not known in any context!");
1117        }
1118        //////////
1119
// BCEL //
1120
//////////
1121
public void visitLocalVariable(LocalVariable obj){
1122            // This does not represent an Attribute but is only
1123
// related to internal BCEL data representation.
1124

1125            // see visitLocalVariableTable(LocalVariableTable)
1126
}
1127        public void visitCodeException(CodeException obj){
1128            // Code constraints are checked in Pass3 (3a and 3b).
1129
// This does not represent an Attribute but is only
1130
// related to internal BCEL data representation.
1131

1132            // see visitCode(Code)
1133
}
1134        public void visitConstantPool(ConstantPool obj){
1135            // No need to. We're piggybacked by the DescendingVisitor.
1136
// This does not represent an Attribute but is only
1137
// related to internal BCEL data representation.
1138
}
1139        public void visitInnerClass(InnerClass obj){
1140            // This does not represent an Attribute but is only
1141
// related to internal BCEL data representation.
1142
}
1143        public void visitLineNumber(LineNumber obj){
1144            // This does not represent an Attribute but is only
1145
// related to internal BCEL data representation.
1146

1147            // see visitLineNumberTable(LineNumberTable)
1148
}
1149    }
1150
1151    /**
1152     * Ensures that the ConstantCP-subclassed entries of the constant
1153     * pool are valid. According to "Yellin: Low Level Security in Java",
1154     * this method does not verify the existence of referenced entities
1155     * (such as classes) but only the formal correctness (such as well-formed
1156     * signatures).
1157   * The visitXXX() methods throw ClassConstraintException instances otherwise.
1158     * <B>Precondition: index-style cross referencing in the constant
1159     * pool must be valid. Simply invoke constant_pool_entries_satisfy_static_constraints()
1160     * before.</B>
1161     *
1162     * @throws ClassConstraintException otherwise.
1163     * @see #constant_pool_entries_satisfy_static_constraints()
1164     */

1165    private void field_and_method_refs_are_valid(){
1166        try {
1167        JavaClass jc = Repository.lookupClass(myOwner.getClassName());
1168        DescendingVisitor v = new DescendingVisitor(jc, new FAMRAV_Visitor(jc));
1169        v.visit();
1170
1171        } catch (ClassNotFoundException JavaDoc e) {
1172        // FIXME: this might not be the best way to handle missing classes.
1173
throw new AssertionViolatedException("Missing class: " + e.toString());
1174        }
1175    }
1176
1177    /**
1178     * A Visitor class that ensures the ConstantCP-subclassed entries
1179     * of the constant pool are valid.
1180   * <B>Precondition: index-style cross referencing in the constant
1181   * pool must be valid.</B>
1182     *
1183   * @see #constant_pool_entries_satisfy_static_constraints()
1184     * @see org.apache.bcel.classfile.ConstantCP
1185     */

1186    private class FAMRAV_Visitor extends EmptyVisitor implements Visitor{
1187        private final ConstantPool cp; // ==jc.getConstantPool() -- only here to save typing work.
1188
private FAMRAV_Visitor(JavaClass _jc){
1189            cp = _jc.getConstantPool();
1190        }
1191        
1192        public void visitConstantFieldref(ConstantFieldref obj){
1193            if (obj.getTag() != Constants.CONSTANT_Fieldref){
1194                throw new ClassConstraintException("ConstantFieldref '"+tostring(obj)+"' has wrong tag!");
1195            }
1196            int name_and_type_index = obj.getNameAndTypeIndex();
1197            ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
1198            String JavaDoc name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes(); // Field or Method name
1199
if (!validFieldName(name)){
1200                throw new ClassConstraintException("Invalid field name '"+name+"' referenced by '"+tostring(obj)+"'.");
1201            }
1202            
1203            int class_index = obj.getClassIndex();
1204            ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
1205            String JavaDoc className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes(); // Class Name in internal form
1206
if (! validClassName(className)){
1207                throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
1208            }
1209
1210            String JavaDoc sig = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
1211

1212            try{
1213                Type.getType(sig); /* Don't need the return value */
1214            }
1215            catch (ClassFormatException cfe){
1216                throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
1217            }
1218        }
1219
1220        public void visitConstantMethodref(ConstantMethodref obj){
1221            if (obj.getTag() != Constants.CONSTANT_Methodref){
1222                throw new ClassConstraintException("ConstantMethodref '"+tostring(obj)+"' has wrong tag!");
1223            }
1224            int name_and_type_index = obj.getNameAndTypeIndex();
1225            ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
1226            String JavaDoc name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes(); // Field or Method name
1227
if (!validClassMethodName(name)){
1228                throw new ClassConstraintException("Invalid (non-interface) method name '"+name+"' referenced by '"+tostring(obj)+"'.");
1229            }
1230
1231            int class_index = obj.getClassIndex();
1232            ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
1233            String JavaDoc className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes(); // Class Name in internal form
1234
if (! validClassName(className)){
1235                throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
1236            }
1237
1238            String JavaDoc sig = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
1239

1240            try{
1241                Type t = Type.getReturnType(sig);
1242                if ( name.equals(CONSTRUCTOR_NAME) && (t != Type.VOID) ){
1243                    throw new ClassConstraintException("Instance initialization method must have VOID return type.");
1244                }
1245            }
1246            catch (ClassFormatException cfe){
1247                throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
1248            }
1249        }
1250
1251        public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj){
1252            if (obj.getTag() != Constants.CONSTANT_InterfaceMethodref){
1253                throw new ClassConstraintException("ConstantInterfaceMethodref '"+tostring(obj)+"' has wrong tag!");
1254            }
1255            int name_and_type_index = obj.getNameAndTypeIndex();
1256            ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
1257            String JavaDoc name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes(); // Field or Method name
1258
if (!validInterfaceMethodName(name)){
1259                throw new ClassConstraintException("Invalid (interface) method name '"+name+"' referenced by '"+tostring(obj)+"'.");
1260            }
1261
1262            int class_index = obj.getClassIndex();
1263            ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
1264            String JavaDoc className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes(); // Class Name in internal form
1265
if (! validClassName(className)){
1266                throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
1267            }
1268
1269            String JavaDoc sig = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
1270

1271            try{
1272                Type t = Type.getReturnType(sig);
1273                if ( name.equals(STATIC_INITIALIZER_NAME) && (t != Type.VOID) ){
1274                    addMessage("Class or interface initialization method '"+STATIC_INITIALIZER_NAME+"' usually has VOID return type instead of '"+t+"'. Note this is really not a requirement of The Java Virtual Machine Specification, Second Edition.");
1275                }
1276            }
1277            catch (ClassFormatException cfe){
1278                throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
1279            }
1280
1281        }
1282        
1283    }
1284
1285    /**
1286     * This method returns true if and only if the supplied String
1287     * represents a valid Java class name.
1288     */

1289    private static final boolean validClassName(String JavaDoc name){
1290        /*
1291         * TODO: implement.
1292         * Are there any restrictions?
1293         */

1294        return true;
1295    }
1296    /**
1297     * This method returns true if and only if the supplied String
1298     * represents a valid method name.
1299     * This is basically the same as a valid identifier name in the
1300     * Java programming language, but the special name for
1301     * the instance initialization method is allowed and the special name
1302     * for the class/interface initialization method may be allowed.
1303     */

1304    private static boolean validMethodName(String JavaDoc name, boolean allowStaticInit){
1305        if (validJavaLangMethodName(name)) {
1306            return true;
1307        }
1308        
1309        if (allowStaticInit){
1310            return (name.equals(CONSTRUCTOR_NAME) || name.equals(STATIC_INITIALIZER_NAME));
1311        }
1312        else{
1313            return name.equals(CONSTRUCTOR_NAME);
1314        }
1315    }
1316
1317    /**
1318     * This method returns true if and only if the supplied String
1319     * represents a valid method name that may be referenced by
1320     * ConstantMethodref objects.
1321     */

1322    private static boolean validClassMethodName(String JavaDoc name){
1323        return validMethodName(name, false);
1324    }
1325
1326    /**
1327     * This method returns true if and only if the supplied String
1328     * represents a valid Java programming language method name stored as a simple
1329     * (non-qualified) name.
1330     * Conforming to: The Java Virtual Machine Specification, Second Edition, §2.7, §2.7.1, §2.2.
1331     */

1332    private static boolean validJavaLangMethodName(String JavaDoc name){
1333        if (!Character.isJavaIdentifierStart(name.charAt(0))) {
1334            return false;
1335        }
1336        
1337        for (int i=1; i<name.length(); i++){
1338            if (!Character.isJavaIdentifierPart(name.charAt(i))) {
1339                return false;
1340            }
1341        }
1342        return true;
1343    }
1344
1345    /**
1346     * This method returns true if and only if the supplied String
1347     * represents a valid Java interface method name that may be
1348     * referenced by ConstantInterfaceMethodref objects.
1349     */

1350    private static boolean validInterfaceMethodName(String JavaDoc name){
1351        // I guess we should assume special names forbidden here.
1352
if (name.startsWith("<")) {
1353            return false;
1354        }
1355        return validJavaLangMethodName(name);
1356    }
1357
1358    /**
1359     * This method returns true if and only if the supplied String
1360     * represents a valid Java identifier (so-called simple name).
1361     */

1362    private static boolean validJavaIdentifier(String JavaDoc name){
1363    if (name.length() == 0) {
1364        return false; // must not be empty, reported by <francis.andre@easynet.fr>, thanks!
1365
}
1366
1367        // vmspec2 2.7, vmspec2 2.2
1368
if (!Character.isJavaIdentifierStart(name.charAt(0))) {
1369            return false;
1370        }
1371        
1372        for (int i=1; i<name.length(); i++){
1373            if (!Character.isJavaIdentifierPart(name.charAt(i))) {
1374                return false;
1375            }
1376        }
1377        return true;
1378    }
1379
1380    /**
1381     * This method returns true if and only if the supplied String
1382     * represents a valid Java field name.
1383     */

1384    private static boolean validFieldName(String JavaDoc name){
1385        // vmspec2 2.7, vmspec2 2.2
1386
return validJavaIdentifier(name);
1387    }
1388
1389    /**
1390     * This class serves for finding out if a given JavaClass' ConstantPool
1391     * references an Inner Class.
1392     * The Java Virtual Machine Specification, Second Edition is not very precise
1393     * about when an "InnerClasses" attribute has to appear. However, it states that
1394     * there has to be exactly one InnerClasses attribute in the ClassFile structure
1395     * if the constant pool of a class or interface refers to any class or interface
1396     * "that is not a member of a package". Sun does not mean "member of the default
1397     * package". In "Inner Classes Specification" they point out how a "bytecode name"
1398     * is derived so one has to deduce what a class name of a class "that is not a
1399     * member of a package" looks like: there is at least one character in the byte-
1400     * code name that cannot be part of a legal Java Language Class name (and not equal
1401     * to '/'). This assumption is wrong as the delimiter is '$' for which
1402     * Character.isJavaIdentifierPart() == true.
1403     * Hence, you really run into trouble if you have a toplevel class called
1404     * "A$XXX" and another toplevel class called "A" with in inner class called "XXX".
1405     * JustIce cannot repair this; please note that existing verifiers at this
1406     * time even fail to detect missing InnerClasses attributes in pass 2.
1407     */

1408    private static class InnerClassDetector extends EmptyVisitor{
1409        private boolean hasInnerClass = false;
1410        private JavaClass jc;
1411        private ConstantPool cp;
1412
1413        /** Constructs an InnerClassDetector working on the JavaClass _jc. */
1414        public InnerClassDetector(JavaClass _jc){
1415            jc = _jc;
1416            cp = jc.getConstantPool();
1417            (new DescendingVisitor(jc, this)).visit();
1418        }
1419        /**
1420         * Returns if the JavaClass this InnerClassDetector is working on
1421         * has an Inner Class reference in its constant pool.
1422         */

1423        public boolean innerClassReferenced(){
1424            return hasInnerClass;
1425        }
1426        /** This method casually visits ConstantClass references. */
1427        public void visitConstantClass(ConstantClass obj){
1428            Constant c = cp.getConstant(obj.getNameIndex());
1429            if (c instanceof ConstantUtf8){ //Ignore the case where it's not a ConstantUtf8 here, we'll find out later.
1430
String JavaDoc classname = ((ConstantUtf8) c).getBytes();
1431                if (classname.startsWith(jc.getClassName().replace('.','/')+"$")){
1432                    hasInnerClass = true;
1433                }
1434            }
1435        }
1436    }
1437    
1438    /**
1439     * This method is here to save typing work and improve code readability.
1440     */

1441    private static String JavaDoc tostring(Node n){
1442        return new StringRepresentation(n).toString();
1443    }
1444}
1445
Popular Tags