1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.bcel.verifier.statics;
18
19
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.Locale;
23 import java.util.Map;
24 import java.util.Set;
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;
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
154
155 localVariablesInfos = new LocalVariablesInfo[Repository.lookupClass(myOwner.getClassName()).getMethods().length];
156
157 VerificationResult vr = VerificationResult.VR_OK;
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 e) {
173
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 hs = new HashSet();
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 supername = jc.getSuperclassName();
207 if (! hs.add(supername)){
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 e) {
225
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 hashmap = new HashMap();
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 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()){
265 hashmap.put(name_and_sig, jc.getClassName());
266 }
267 }
268 }
269 else{
270 if (!methods[i].isStatic()){
271 hashmap.put(name_and_sig, jc.getClassName());
272 }
273 }
274 }
275
276 jc = Repository.lookupClass(jc.getSuperclassName());
277 }
278
279 } catch (ClassNotFoundException e) {
280
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
295
296
297 JavaClass jc = Repository.lookupClass(myOwner.getClassName());
298 new CPESSC_Visitor(jc);
299
300 } catch (ClassNotFoundException e) {
301
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 CONST_Class;
315
316
317
318
319
320 private Class CONST_String;
321 private Class CONST_Integer;
322 private Class CONST_Float;
323 private Class CONST_Long;
324 private Class CONST_Double;
325 private Class CONST_NameAndType;
326 private Class CONST_Utf8;
327
328 private final JavaClass jc;
329 private final ConstantPool cp;
330 private final int cplen;
331 private DescendingVisitor carrier;
332
333 private Set field_names = new HashSet();
334 private Set field_names_and_desc = new HashSet();
335 private Set method_names_and_desc = new HashSet();
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
345
346
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 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
367 throw new ClassCastException("Illegal constant '"+tostring(c)+"' at index '"+index+"'. '"+tostring(referrer)+"' expects a '"+shouldbe+"'.");
368 }
369 }
370
371
372
373 public void visitJavaClass(JavaClass obj){
374 Attribute[] atts = obj.getAttributes();
375 boolean foundSourceFile = false;
376 boolean foundInnerClasses = false;
377
378
379
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)) &&
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.");
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
414
415
416
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
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
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
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
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
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
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
494 }
495
496
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{
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 name = obj.getName();
538 if (! validFieldName(name)){
539 throw new ClassConstraintException("Field '"+tostring(obj)+"' has illegal name '"+obj.getName()+"'.");
540 }
541
542
543 checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
544
545 String sig = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes();
546
547 try{
548 Type.getType(sig);
549 }
550 catch (ClassFormatException cfe){
551 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
552 }
553
554 String 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))){
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
578
579 public void visitMethod(Method obj){
580
581 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
582
583 String name = obj.getName();
584 if (! validMethodName(name, true)){
585 throw new ClassConstraintException("Method '"+tostring(obj)+"' has illegal name '"+name+"'.");
586 }
587
588
589 checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
590
591 String sig = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes();
592
593 Type t;
594 Type[] ts;
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
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
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{
672 if (!name.equals(STATIC_INITIALIZER_NAME)){
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
692 if (name.equals(CONSTRUCTOR_NAME)){
693
694
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
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 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))){
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])+"'.");
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
750
751 public void visitSourceFile(SourceFile obj){
752
753
754
755 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
756
757 String 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 sourcefilename = ((ConstantUtf8) cp.getConstant(obj.getSourceFileIndex())).getBytes();
765 String 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 obj){
775 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
776
777 String 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){
783 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
784 String 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){
790
791
792
793 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
794
795 String 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
819
820 }
821
822
823
824 public void visitConstantValue(ConstantValue obj){
825
826
827 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
828
829 String 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 pred = carrier.predecessor();
835 if (pred instanceof Field){
836 Field f = (Field) pred;
837
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
866
867
868
869
870 public void visitCode(Code obj){
871 try {
872
873
874
875 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
876
877 String 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;
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();
889
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
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){
901 checkIndex(obj, exc_index, CONST_Class);
902 ConstantClass cc = (ConstantClass) (cp.getConstant(exc_index));
903 checkIndex(cc, cc.getNameIndex(), CONST_Utf8);
904 String 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
914
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;
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
940
941
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){
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
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{
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
968
969
970
971 if (atts[a] instanceof LocalVariableTable){
972
973 LocalVariableTable lvt = (LocalVariableTable) atts[a];
974
975 checkIndex(lvt, lvt.getNameIndex(), CONST_Utf8);
976
977 String 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
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 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 localsig = ((ConstantUtf8) (cp.getConstant(localvariables[i].getSignatureIndex()))).getBytes();
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 }
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 }
1021 }
1022
1023 } catch (ClassNotFoundException e) {
1024
1025 throw new AssertionViolatedException("Missing class: " + e.toString());
1026 }
1027
1028 }
1029
1030 public void visitExceptionTable(ExceptionTable obj){
1031 try {
1032
1033 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
1034
1035 String 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);
1047 String cname = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes().replace('/','.');
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
1057
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;
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 e) {
1082
1083 throw new AssertionViolatedException("Missing class: " + e.toString());
1084 }
1085 }
1086
1087
1088
1089
1090
1091 public void visitLineNumberTable(LineNumberTable obj){
1092 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
1093
1094 String 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
1100
1101
1102
1103 }
1104 public void visitLocalVariableTable(LocalVariableTable obj){
1105
1106
1107 }
1108
1109
1110
1111 public void visitUnknown(Unknown obj){
1112
1113 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
1114
1115
1116 addMessage("Unknown attribute '"+tostring(obj)+"'. This attribute is not known in any context!");
1117 }
1118
1119
1120
1121 public void visitLocalVariable(LocalVariable obj){
1122
1123
1124
1125
1126 }
1127 public void visitCodeException(CodeException obj){
1128
1129
1130
1131
1132
1133 }
1134 public void visitConstantPool(ConstantPool obj){
1135
1136
1137
1138 }
1139 public void visitInnerClass(InnerClass obj){
1140
1141
1142 }
1143 public void visitLineNumber(LineNumber obj){
1144
1145
1146
1147
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 e) {
1172
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;
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 name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes();
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 className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes();
1206 if (! validClassName(className)){
1207 throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
1208 }
1209
1210 String sig = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes();
1211
1212 try{
1213 Type.getType(sig);
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 name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes();
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 className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes();
1234 if (! validClassName(className)){
1235 throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
1236 }
1237
1238 String sig = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes();
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 name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes();
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 className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes();
1265 if (! validClassName(className)){
1266 throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
1267 }
1268
1269 String sig = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes();
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 name){
1290
1291
1292
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 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 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 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 name){
1351
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 name){
1363 if (name.length() == 0) {
1364 return false;
1365 }
1366
1367
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 name){
1385
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){
1430 String 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 tostring(Node n){
1442 return new StringRepresentation(n).toString();
1443 }
1444 }