1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.bcel.verifier.statics;
19
20
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Locale;
24 import java.util.Map;
25 import java.util.Set;
26 import org.apache.bcel.Constants;
27 import org.apache.bcel.Repository;
28 import org.apache.bcel.classfile.Attribute;
29 import org.apache.bcel.classfile.ClassFormatException;
30 import org.apache.bcel.classfile.Code;
31 import org.apache.bcel.classfile.CodeException;
32 import org.apache.bcel.classfile.Constant;
33 import org.apache.bcel.classfile.ConstantClass;
34 import org.apache.bcel.classfile.ConstantDouble;
35 import org.apache.bcel.classfile.ConstantFieldref;
36 import org.apache.bcel.classfile.ConstantFloat;
37 import org.apache.bcel.classfile.ConstantInteger;
38 import org.apache.bcel.classfile.ConstantInterfaceMethodref;
39 import org.apache.bcel.classfile.ConstantLong;
40 import org.apache.bcel.classfile.ConstantMethodref;
41 import org.apache.bcel.classfile.ConstantNameAndType;
42 import org.apache.bcel.classfile.ConstantPool;
43 import org.apache.bcel.classfile.ConstantString;
44 import org.apache.bcel.classfile.ConstantUtf8;
45 import org.apache.bcel.classfile.ConstantValue;
46 import org.apache.bcel.classfile.Deprecated;
47 import org.apache.bcel.classfile.DescendingVisitor;
48 import org.apache.bcel.classfile.EmptyVisitor;
49 import org.apache.bcel.classfile.ExceptionTable;
50 import org.apache.bcel.classfile.Field;
51 import org.apache.bcel.classfile.InnerClass;
52 import org.apache.bcel.classfile.InnerClasses;
53 import org.apache.bcel.classfile.JavaClass;
54 import org.apache.bcel.classfile.LineNumber;
55 import org.apache.bcel.classfile.LineNumberTable;
56 import org.apache.bcel.classfile.LocalVariable;
57 import org.apache.bcel.classfile.LocalVariableTable;
58 import org.apache.bcel.classfile.Method;
59 import org.apache.bcel.classfile.Node;
60 import org.apache.bcel.classfile.SourceFile;
61 import org.apache.bcel.classfile.Synthetic;
62 import org.apache.bcel.classfile.Unknown;
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
76
77
78
79
80
81
82
83
84
85 public final class Pass2Verifier extends PassVerifier implements Constants{
86
87
88
89
90
91
92
93 private LocalVariablesInfo[] localVariablesInfos;
94
95
96 private Verifier myOwner;
97
98
99
100
101
102
103 public Pass2Verifier(Verifier owner){
104 myOwner = owner;
105 }
106
107
108
109
110
111
112
113
114
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 @Override
149 public VerificationResult do_verify(){
150 try {
151 VerificationResult vr1 = myOwner.doPass1();
152 if (vr1.equals(VerificationResult.VR_OK)){
153
154
155
156 localVariablesInfos = new LocalVariablesInfo[Repository.lookupClass(myOwner.getClassName()).getMethods().length];
157
158 VerificationResult vr = VerificationResult.VR_OK;
159 try{
160 constant_pool_entries_satisfy_static_constraints();
161 field_and_method_refs_are_valid();
162 every_class_has_an_accessible_superclass();
163 final_methods_are_not_overridden();
164 }
165 catch (ClassConstraintException cce){
166 vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage());
167 }
168 return vr;
169 } else {
170 return VerificationResult.VR_NOTYET;
171 }
172
173 } catch (ClassNotFoundException e) {
174
175 throw new AssertionViolatedException("Missing class: " + e.toString(), e);
176 }
177 }
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192 private void every_class_has_an_accessible_superclass(){
193 try {
194 Set<String> hs = new HashSet<String>();
195 JavaClass jc = Repository.lookupClass(myOwner.getClassName());
196 int supidx = -1;
197
198 while (supidx != 0){
199 supidx = jc.getSuperclassNameIndex();
200
201 if (supidx == 0){
202 if (jc != Repository.lookupClass(Type.OBJECT.getClassName())){
203 throw new ClassConstraintException("Superclass of '"+jc.getClassName()+"' missing but not "+Type.OBJECT.getClassName()+" itself!");
204 }
205 }
206 else{
207 String supername = jc.getSuperclassName();
208 if (! hs.add(supername)){
209 throw new ClassConstraintException("Circular superclass hierarchy detected.");
210 }
211 Verifier v = VerifierFactory.getVerifier(supername);
212 VerificationResult vr = v.doPass1();
213
214 if (vr != VerificationResult.VR_OK){
215 throw new ClassConstraintException("Could not load in ancestor class '"+supername+"'.");
216 }
217 jc = Repository.lookupClass(supername);
218
219 if (jc.isFinal()){
220 throw new ClassConstraintException("Ancestor class '"+supername+"' has the FINAL access modifier and must therefore not be subclassed.");
221 }
222 }
223 }
224
225 } catch (ClassNotFoundException e) {
226
227 throw new AssertionViolatedException("Missing class: " + e.toString(), e);
228 }
229 }
230
231
232
233
234
235
236
237
238
239
240
241
242 private void final_methods_are_not_overridden(){
243 try {
244 Map<String, String> hashmap = new HashMap<String, String>();
245 JavaClass jc = Repository.lookupClass(myOwner.getClassName());
246
247 int supidx = -1;
248 while (supidx != 0){
249 supidx = jc.getSuperclassNameIndex();
250
251 Method[] methods = jc.getMethods();
252 for (int i=0; i<methods.length; i++){
253 String name_and_sig = (methods[i].getName()+methods[i].getSignature());
254
255 if (hashmap.containsKey(name_and_sig)){
256 if ( methods[i].isFinal() ){
257 if (!(methods[i].isPrivate())) {
258 throw new ClassConstraintException("Method '"+name_and_sig+"' in class '"+hashmap.get(name_and_sig)+"' overrides the final (not-overridable) definition in class '"+jc.getClassName()+"'.");
259 }
260 else{
261 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.");
262 }
263 }
264 else{
265 if (!methods[i].isStatic()){
266 hashmap.put(name_and_sig, jc.getClassName());
267 }
268 }
269 }
270 else{
271 if (!methods[i].isStatic()){
272 hashmap.put(name_and_sig, jc.getClassName());
273 }
274 }
275 }
276
277 jc = Repository.lookupClass(jc.getSuperclassName());
278 }
279
280 } catch (ClassNotFoundException e) {
281
282 throw new AssertionViolatedException("Missing class: " + e.toString(), e);
283 }
284
285 }
286
287
288
289
290
291
292
293 private void constant_pool_entries_satisfy_static_constraints(){
294 try {
295
296
297
298 JavaClass jc = Repository.lookupClass(myOwner.getClassName());
299 new CPESSC_Visitor(jc);
300
301 } catch (ClassNotFoundException e) {
302
303 throw new AssertionViolatedException("Missing class: " + e.toString(), e);
304 }
305 }
306
307
308
309
310
311
312
313
314 private class CPESSC_Visitor extends org.apache.bcel.classfile.EmptyVisitor{
315 private Class<?> CONST_Class;
316
317
318
319
320
321 private Class<?> CONST_String;
322 private Class<?> CONST_Integer;
323 private Class<?> CONST_Float;
324 private Class<?> CONST_Long;
325 private Class<?> CONST_Double;
326 private Class<?> CONST_NameAndType;
327 private Class<?> CONST_Utf8;
328
329 private final JavaClass jc;
330 private final ConstantPool cp;
331 private final int cplen;
332 private DescendingVisitor carrier;
333
334 private Set<String> field_names = new HashSet<String>();
335 private Set<String> field_names_and_desc = new HashSet<String>();
336 private Set<String> method_names_and_desc = new HashSet<String>();
337
338 private CPESSC_Visitor(JavaClass _jc){
339 jc = _jc;
340 cp = _jc.getConstantPool();
341 cplen = cp.getLength();
342
343 CONST_Class = org.apache.bcel.classfile.ConstantClass.class;
344
345
346
347
348
349 CONST_String = org.apache.bcel.classfile.ConstantString.class;
350 CONST_Integer = org.apache.bcel.classfile.ConstantInteger.class;
351 CONST_Float = org.apache.bcel.classfile.ConstantFloat.class;
352 CONST_Long = org.apache.bcel.classfile.ConstantLong.class;
353 CONST_Double = org.apache.bcel.classfile.ConstantDouble.class;
354 CONST_NameAndType = org.apache.bcel.classfile.ConstantNameAndType.class;
355 CONST_Utf8 = org.apache.bcel.classfile.ConstantUtf8.class;
356
357 carrier = new DescendingVisitor(_jc, this);
358 carrier.visit();
359 }
360
361 private void checkIndex(Node referrer, int index, Class<?> shouldbe){
362 if ((index < 0) || (index >= cplen)){
363 throw new ClassConstraintException("Invalid index '"+index+"' used by '"+tostring(referrer)+"'.");
364 }
365 Constant c = cp.getConstant(index);
366 if (! shouldbe.isInstance(c)){
367
368 throw new ClassCastException("Illegal constant '"+tostring(c)+"' at index '"+index+"'. '"+tostring(referrer)+"' expects a '"+shouldbe+"'.");
369 }
370 }
371
372
373
374 @Override
375 public void visitJavaClass(JavaClass obj){
376 Attribute[] atts = obj.getAttributes();
377 boolean foundSourceFile = false;
378 boolean foundInnerClasses = false;
379
380
381
382 boolean hasInnerClass = new InnerClassDetector(jc).innerClassReferenced();
383
384 for (int i=0; i<atts.length; i++){
385 if ((! (atts[i] instanceof SourceFile)) &&
386 (! (atts[i] instanceof Deprecated)) &&
387 (! (atts[i] instanceof InnerClasses)) &&
388 (! (atts[i] instanceof Synthetic))){
389 addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of the ClassFile structure '"+tostring(obj)+"' is unknown and will therefore be ignored.");
390 }
391
392 if (atts[i] instanceof SourceFile){
393 if (foundSourceFile == false) {
394 foundSourceFile = true;
395 } else {
396 throw new ClassConstraintException("A ClassFile structure (like '"+tostring(obj)+"') may have no more than one SourceFile attribute.");
397 }
398 }
399
400 if (atts[i] instanceof InnerClasses){
401 if (foundInnerClasses == false) {
402 foundInnerClasses = true;
403 } else{
404 if (hasInnerClass){
405 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.");
406 }
407 }
408 if (!hasInnerClass){
409 addMessage("No referenced Inner Class found, but InnerClasses attribute '"+tostring(atts[i])+"' found. Strongly suggest removal of that attribute.");
410 }
411 }
412
413 }
414 if (hasInnerClass && !foundInnerClasses){
415
416
417
418
419 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.");
420 }
421 }
422
423
424
425 @Override
426 public void visitConstantClass(ConstantClass obj){
427 if (obj.getTag() != Constants.CONSTANT_Class){
428 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
429 }
430 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
431
432 }
433 @Override
434 public void visitConstantFieldref(ConstantFieldref obj){
435 if (obj.getTag() != Constants.CONSTANT_Fieldref){
436 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
437 }
438 checkIndex(obj, obj.getClassIndex(), CONST_Class);
439 checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
440 }
441 @Override
442 public void visitConstantMethodref(ConstantMethodref obj){
443 if (obj.getTag() != Constants.CONSTANT_Methodref){
444 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
445 }
446 checkIndex(obj, obj.getClassIndex(), CONST_Class);
447 checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
448 }
449 @Override
450 public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj){
451 if (obj.getTag() != Constants.CONSTANT_InterfaceMethodref){
452 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
453 }
454 checkIndex(obj, obj.getClassIndex(), CONST_Class);
455 checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
456 }
457 @Override
458 public void visitConstantString(ConstantString obj){
459 if (obj.getTag() != Constants.CONSTANT_String){
460 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
461 }
462 checkIndex(obj, obj.getStringIndex(), CONST_Utf8);
463 }
464 @Override
465 public void visitConstantInteger(ConstantInteger obj){
466 if (obj.getTag() != Constants.CONSTANT_Integer){
467 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
468 }
469
470 }
471 @Override
472 public void visitConstantFloat(ConstantFloat obj){
473 if (obj.getTag() != Constants.CONSTANT_Float){
474 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
475 }
476
477 }
478 @Override
479 public void visitConstantLong(ConstantLong obj){
480 if (obj.getTag() != Constants.CONSTANT_Long){
481 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
482 }
483
484 }
485 @Override
486 public void visitConstantDouble(ConstantDouble obj){
487 if (obj.getTag() != Constants.CONSTANT_Double){
488 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
489 }
490
491 }
492 @Override
493 public void visitConstantNameAndType(ConstantNameAndType obj){
494 if (obj.getTag() != Constants.CONSTANT_NameAndType){
495 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
496 }
497 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
498
499 checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
500 }
501 @Override
502 public void visitConstantUtf8(ConstantUtf8 obj){
503 if (obj.getTag() != Constants.CONSTANT_Utf8){
504 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
505 }
506
507 }
508
509
510
511 @Override
512 public void visitField(Field obj){
513
514 if (jc.isClass()){
515 int maxone=0;
516 if (obj.isPrivate()) {
517 maxone++;
518 }
519 if (obj.isProtected()) {
520 maxone++;
521 }
522 if (obj.isPublic()) {
523 maxone++;
524 }
525 if (maxone > 1){
526 throw new ClassConstraintException("Field '"+tostring(obj)+"' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set.");
527 }
528
529 if (obj.isFinal() && obj.isVolatile()){
530 throw new ClassConstraintException("Field '"+tostring(obj)+"' must only have at most one of its ACC_FINAL, ACC_VOLATILE modifiers set.");
531 }
532 }
533 else{
534 if (!obj.isPublic()){
535 throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_PUBLIC modifier set but hasn't!");
536 }
537 if (!obj.isStatic()){
538 throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_STATIC modifier set but hasn't!");
539 }
540 if (!obj.isFinal()){
541 throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_FINAL modifier set but hasn't!");
542 }
543 }
544
545 if ((obj.getAccessFlags() & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_VOLATILE|ACC_TRANSIENT)) > 0){
546 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).");
547 }
548
549 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
550
551 String name = obj.getName();
552 if (! validFieldName(name)){
553 throw new ClassConstraintException("Field '"+tostring(obj)+"' has illegal name '"+obj.getName()+"'.");
554 }
555
556
557 checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
558
559 String sig = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes();
560
561 try{
562 Type.getType(sig);
563 }
564 catch (ClassFormatException cfe){
565 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.", cfe);
566 }
567
568 String nameanddesc = (name+sig);
569 if (field_names_and_desc.contains(nameanddesc)){
570 throw new ClassConstraintException("No two fields (like '"+tostring(obj)+"') are allowed have same names and descriptors!");
571 }
572 if (field_names.contains(name)){
573 addMessage("More than one field of name '"+name+"' detected (but with different type descriptors). This is very unusual.");
574 }
575 field_names_and_desc.add(nameanddesc);
576 field_names.add(name);
577
578 Attribute[] atts = obj.getAttributes();
579 for (int i=0; i<atts.length; i++){
580 if ((! (atts[i] instanceof ConstantValue)) &&
581 (! (atts[i] instanceof Synthetic)) &&
582 (! (atts[i] instanceof Deprecated))){
583 addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Field '"+tostring(obj)+"' is unknown and will therefore be ignored.");
584 }
585 if (! (atts[i] instanceof ConstantValue)){
586 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.");
587 }
588 }
589 }
590
591
592
593 @Override
594 public void visitMethod(Method obj){
595
596 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
597
598 String name = obj.getName();
599 if (! validMethodName(name, true)){
600 throw new ClassConstraintException("Method '"+tostring(obj)+"' has illegal name '"+name+"'.");
601 }
602
603
604 checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
605
606 String sig = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes();
607
608 Type t;
609 Type[] ts;
610 try{
611 t = Type.getReturnType(sig);
612 ts = Type.getArgumentTypes(sig);
613 }
614 catch (ClassFormatException cfe){
615 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by Method '"+tostring(obj)+"'.", cfe);
616 }
617
618
619 Type act = t;
620 if (act instanceof ArrayType) {
621 act = ((ArrayType) act).getBasicType();
622 }
623 if (act instanceof ObjectType){
624 Verifier v = VerifierFactory.getVerifier( ((ObjectType) act).getClassName() );
625 VerificationResult vr = v.doPass1();
626 if (vr != VerificationResult.VR_OK) {
627 throw new ClassConstraintException("Method '"+tostring(obj)+"' has a return type that does not pass verification pass 1: '"+vr+"'.");
628 }
629 }
630
631 for (int i=0; i<ts.length; i++){
632 act = ts[i];
633 if (act instanceof ArrayType) {
634 act = ((ArrayType) act).getBasicType();
635 }
636 if (act instanceof ObjectType){
637 Verifier v = VerifierFactory.getVerifier( ((ObjectType) act).getClassName() );
638 VerificationResult vr = v.doPass1();
639 if (vr != VerificationResult.VR_OK) {
640 throw new ClassConstraintException("Method '"+tostring(obj)+"' has an argument type that does not pass verification pass 1: '"+vr+"'.");
641 }
642 }
643 }
644
645
646 if (name.equals(STATIC_INITIALIZER_NAME) && (ts.length != 0)){
647 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).");
648 }
649
650 if (jc.isClass()){
651 int maxone=0;
652 if (obj.isPrivate()) {
653 maxone++;
654 }
655 if (obj.isProtected()) {
656 maxone++;
657 }
658 if (obj.isPublic()) {
659 maxone++;
660 }
661 if (maxone > 1){
662 throw new ClassConstraintException("Method '"+tostring(obj)+"' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set.");
663 }
664
665 if (obj.isAbstract()){
666 if (obj.isFinal()) {
667 throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_FINAL modifier set.");
668 }
669 if (obj.isNative()) {
670 throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_NATIVE modifier set.");
671 }
672 if (obj.isPrivate()) {
673 throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_PRIVATE modifier set.");
674 }
675 if (obj.isStatic()) {
676 throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_STATIC modifier set.");
677 }
678 if (obj.isStrictfp()) {
679 throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_STRICT modifier set.");
680 }
681 if (obj.isSynchronized()) {
682 throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_SYNCHRONIZED modifier set.");
683 }
684 }
685 }
686 else{
687 if (!name.equals(STATIC_INITIALIZER_NAME)){
688 if (!obj.isPublic()){
689 throw new ClassConstraintException("Interface method '"+tostring(obj)+"' must have the ACC_PUBLIC modifier set but hasn't!");
690 }
691 if (!obj.isAbstract()){
692 throw new ClassConstraintException("Interface method '"+tostring(obj)+"' must have the ACC_STATIC modifier set but hasn't!");
693 }
694 if ( obj.isPrivate() ||
695 obj.isProtected() ||
696 obj.isStatic() ||
697 obj.isFinal() ||
698 obj.isSynchronized() ||
699 obj.isNative() ||
700 obj.isStrictfp() ){
701 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.");
702 }
703 }
704 }
705
706
707 if (name.equals(CONSTRUCTOR_NAME)){
708
709
710 if ( obj.isStatic() ||
711 obj.isFinal() ||
712 obj.isSynchronized() ||
713 obj.isNative() ||
714 obj.isAbstract() ){
715 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.");
716 }
717 }
718
719
720 if (name.equals(STATIC_INITIALIZER_NAME)){
721 if ((obj.getAccessFlags() & (~ACC_STRICT)) > 0){
722 addMessage("Class or interface initialization method '"+tostring(obj)+"' has superfluous access modifier(s) set: everything but ACC_STRICT is ignored.");
723 }
724 if (obj.isAbstract()){
725 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.");
726 }
727 }
728
729 if ((obj.getAccessFlags() & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) > 0){
730 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).");
731 }
732
733 String nameanddesc = (name+sig);
734 if (method_names_and_desc.contains(nameanddesc)){
735 throw new ClassConstraintException("No two methods (like '"+tostring(obj)+"') are allowed have same names and desciptors!");
736 }
737 method_names_and_desc.add(nameanddesc);
738
739 Attribute[] atts = obj.getAttributes();
740 int num_code_atts = 0;
741 for (int i=0; i<atts.length; i++){
742 if ((! (atts[i] instanceof Code)) &&
743 (! (atts[i] instanceof ExceptionTable)) &&
744 (! (atts[i] instanceof Synthetic)) &&
745 (! (atts[i] instanceof Deprecated))){
746 addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Method '"+tostring(obj)+"' is unknown and will therefore be ignored.");
747 }
748 if ((! (atts[i] instanceof Code)) &&
749 (! (atts[i] instanceof ExceptionTable))){
750 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.");
751 }
752 if ((atts[i] instanceof Code) && (obj.isNative() || obj.isAbstract())){
753 throw new ClassConstraintException("Native or abstract methods like '"+tostring(obj)+"' must not have a Code attribute like '"+tostring(atts[i])+"'.");
754 }
755 if (atts[i] instanceof Code) {
756 num_code_atts++;
757 }
758 }
759 if ( !obj.isNative() && !obj.isAbstract() && num_code_atts != 1){
760 throw new ClassConstraintException("Non-native, non-abstract methods like '"+tostring(obj)+"' must have exactly one Code attribute (found: "+num_code_atts+").");
761 }
762 }
763
764
765
766 @Override
767 public void visitSourceFile(SourceFile obj){
768
769
770
771 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
772
773 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
774 if (! name.equals("SourceFile")){
775 throw new ClassConstraintException("The SourceFile attribute '"+tostring(obj)+"' is not correctly named 'SourceFile' but '"+name+"'.");
776 }
777
778 checkIndex(obj, obj.getSourceFileIndex(), CONST_Utf8);
779
780 String sourcefilename = ((ConstantUtf8) cp.getConstant(obj.getSourceFileIndex())).getBytes();
781 String sourcefilenamelc = sourcefilename.toLowerCase(Locale.ENGLISH);
782
783 if ( (sourcefilename.indexOf('/') != -1) ||
784 (sourcefilename.indexOf('\\') != -1) ||
785 (sourcefilename.indexOf(':') != -1) ||
786 (sourcefilenamelc.lastIndexOf(".java") == -1) ){
787 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.");
788 }
789 }
790 @Override
791 public void visitDeprecated(Deprecated obj){
792 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
793
794 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
795 if (! name.equals("Deprecated")){
796 throw new ClassConstraintException("The Deprecated attribute '"+tostring(obj)+"' is not correctly named 'Deprecated' but '"+name+"'.");
797 }
798 }
799 @Override
800 public void visitSynthetic(Synthetic obj){
801 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
802 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
803 if (! name.equals("Synthetic")){
804 throw new ClassConstraintException("The Synthetic attribute '"+tostring(obj)+"' is not correctly named 'Synthetic' but '"+name+"'.");
805 }
806 }
807 @Override
808 public void visitInnerClasses(InnerClasses obj){
809
810
811
812 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
813
814 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
815 if (! name.equals("InnerClasses")){
816 throw new ClassConstraintException("The InnerClasses attribute '"+tostring(obj)+"' is not correctly named 'InnerClasses' but '"+name+"'.");
817 }
818
819 InnerClass[] ics = obj.getInnerClasses();
820
821 for (int i=0; i<ics.length; i++){
822 checkIndex(obj, ics[i].getInnerClassIndex(), CONST_Class);
823 int outer_idx = ics[i].getOuterClassIndex();
824 if (outer_idx != 0){
825 checkIndex(obj, outer_idx, CONST_Class);
826 }
827 int innername_idx = ics[i].getInnerNameIndex();
828 if (innername_idx != 0){
829 checkIndex(obj, innername_idx, CONST_Utf8);
830 }
831 int acc = ics[i].getInnerAccessFlags();
832 acc = acc & (~ (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT));
833 if (acc != 0){
834 addMessage("Unknown access flag for inner class '"+tostring(ics[i])+"' set (InnerClasses attribute '"+tostring(obj)+"').");
835 }
836 }
837
838
839 }
840
841
842
843 @Override
844 public void visitConstantValue(ConstantValue obj){
845
846
847 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
848
849 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
850 if (! name.equals("ConstantValue")){
851 throw new ClassConstraintException("The ConstantValue attribute '"+tostring(obj)+"' is not correctly named 'ConstantValue' but '"+name+"'.");
852 }
853
854 Object pred = carrier.predecessor();
855 if (pred instanceof Field){
856 Field f = (Field) pred;
857
858 Type field_type = Type.getType(((ConstantUtf8) (cp.getConstant(f.getSignatureIndex()))).getBytes());
859
860 int index = obj.getConstantValueIndex();
861 if ((index < 0) || (index >= cplen)){
862 throw new ClassConstraintException("Invalid index '"+index+"' used by '"+tostring(obj)+"'.");
863 }
864 Constant c = cp.getConstant(index);
865
866 if (CONST_Long.isInstance(c) && field_type.equals(Type.LONG)){
867 return;
868 }
869 if (CONST_Float.isInstance(c) && field_type.equals(Type.FLOAT)){
870 return;
871 }
872 if (CONST_Double.isInstance(c) && field_type.equals(Type.DOUBLE)){
873 return;
874 }
875 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))){
876 return;
877 }
878 if (CONST_String.isInstance(c) && field_type.equals(Type.STRING)){
879 return;
880 }
881
882 throw new ClassConstraintException("Illegal type of ConstantValue '"+obj+"' embedding Constant '"+c+"'. It is referenced by field '"+tostring(f)+"' expecting a different type: '"+field_type+"'.");
883 }
884 }
885
886
887
888
889
890 @Override
891 public void visitCode(Code obj){
892 try {
893
894
895
896 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
897
898 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
899 if (! name.equals("Code")){
900 throw new ClassConstraintException("The Code attribute '"+tostring(obj)+"' is not correctly named 'Code' but '"+name+"'.");
901 }
902
903 Method m = null;
904 if (!(carrier.predecessor() instanceof Method)){
905 addMessage("Code attribute '"+tostring(obj)+"' is not declared in a method_info structure but in '"+carrier.predecessor()+"'. Ignored.");
906 return;
907 }
908 else{
909 m = (Method) carrier.predecessor();
910
911 }
912
913 if (obj.getCode().length == 0){
914 throw new ClassConstraintException("Code array of Code attribute '"+tostring(obj)+"' (method '"+m+"') must not be empty.");
915 }
916
917
918 CodeException[] exc_table = obj.getExceptionTable();
919 for (int i=0; i<exc_table.length; i++){
920 int exc_index = exc_table[i].getCatchType();
921 if (exc_index != 0){
922 checkIndex(obj, exc_index, CONST_Class);
923 ConstantClass cc = (ConstantClass) (cp.getConstant(exc_index));
924 checkIndex(cc, cc.getNameIndex(), CONST_Utf8);
925 String cname = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes().replace('/','.');
926
927 Verifier v = VerifierFactory.getVerifier(cname);
928 VerificationResult vr = v.doPass1();
929
930 if (vr != VerificationResult.VR_OK){
931 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);
932 }
933 else{
934
935
936 JavaClass e = Repository.lookupClass(cname);
937 JavaClass t = Repository.lookupClass(Type.THROWABLE.getClassName());
938 JavaClass o = Repository.lookupClass(Type.OBJECT.getClassName());
939 while (e != o){
940 if (e == t) {
941 break;
942 }
943
944 v = VerifierFactory.getVerifier(e.getSuperclassName());
945 vr = v.doPass1();
946 if (vr != VerificationResult.VR_OK){
947 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);
948 }
949 else{
950 e = Repository.lookupClass(e.getSuperclassName());
951 }
952 }
953 if (e != t) {
954 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()+"'.");
955 }
956 }
957 }
958 }
959
960
961
962
963 int method_number = -1;
964 Method[] ms = Repository.lookupClass(myOwner.getClassName()).getMethods();
965 for (int mn=0; mn<ms.length; mn++){
966 if (m == ms[mn]){
967 method_number = mn;
968 break;
969 }
970 }
971 if (method_number < 0){
972 throw new AssertionViolatedException("Could not find a known BCEL Method object in the corresponding BCEL JavaClass object.");
973 }
974 localVariablesInfos[method_number] = new LocalVariablesInfo(obj.getMaxLocals());
975
976 int num_of_lvt_attribs = 0;
977
978 Attribute[] atts = obj.getAttributes();
979 for (int a=0; a<atts.length; a++){
980 if ((! (atts[a] instanceof LineNumberTable)) &&
981 (! (atts[a] instanceof LocalVariableTable))){
982 addMessage("Attribute '"+tostring(atts[a])+"' as an attribute of Code attribute '"+tostring(obj)+"' (method '"+m+"') is unknown and will therefore be ignored.");
983 }
984 else{
985 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.");
986 }
987
988
989
990
991
992 if (atts[a] instanceof LocalVariableTable){
993
994 LocalVariableTable lvt = (LocalVariableTable) atts[a];
995
996 checkIndex(lvt, lvt.getNameIndex(), CONST_Utf8);
997
998 String lvtname = ((ConstantUtf8) cp.getConstant(lvt.getNameIndex())).getBytes();
999 if (! lvtname.equals("LocalVariableTable")){
1000 throw new ClassConstraintException("The LocalVariableTable attribute '"+tostring(lvt)+"' is not correctly named 'LocalVariableTable' but '"+lvtname+"'.");
1001 }
1002
1003 Code code = obj;
1004
1005
1006 LocalVariable[] localvariables = lvt.getLocalVariableTable();
1007
1008 for (int i=0; i<localvariables.length; i++){
1009 checkIndex(lvt, localvariables[i].getNameIndex(), CONST_Utf8);
1010 String localname = ((ConstantUtf8) cp.getConstant(localvariables[i].getNameIndex())).getBytes();
1011 if (!validJavaIdentifier(localname)){
1012 throw new ClassConstraintException("LocalVariableTable '"+tostring(lvt)+"' references a local variable by the name '"+localname+"' which is not a legal Java simple name.");
1013 }
1014
1015 checkIndex(lvt, localvariables[i].getSignatureIndex(), CONST_Utf8);
1016 String localsig = ((ConstantUtf8) (cp.getConstant(localvariables[i].getSignatureIndex()))).getBytes();
1017 Type t;
1018 try{
1019 t = Type.getType(localsig);
1020 }
1021 catch (ClassFormatException cfe){
1022 throw new ClassConstraintException("Illegal descriptor (==signature) '"+localsig+"' used by LocalVariable '"+tostring(localvariables[i])+"' referenced by '"+tostring(lvt)+"'.", cfe);
1023 }
1024 int localindex = localvariables[i].getIndex();
1025 if ( ( (t==Type.LONG || t==Type.DOUBLE)? localindex+1:localindex) >= code.getMaxLocals()){
1026 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()+"'.");
1027 }
1028
1029 try{
1030 localVariablesInfos[method_number].add(localindex, localname, localvariables[i].getStartPC(), localvariables[i].getLength(), t);
1031 }
1032 catch(LocalVariableInfoInconsistentException lviie){
1033 throw new ClassConstraintException("Conflicting information in LocalVariableTable '"+tostring(lvt)+"' found in Code attribute '"+tostring(obj)+"' (method '"+tostring(m)+"'). "+lviie.getMessage(), lviie);
1034 }
1035 }
1036
1037 num_of_lvt_attribs++;
1038 if (!m.isStatic() && num_of_lvt_attribs > obj.getMaxLocals()){
1039 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.').");
1040 }
1041 }
1042 }
1043
1044 } catch (ClassNotFoundException e) {
1045
1046 throw new AssertionViolatedException("Missing class: " + e.toString(), e);
1047 }
1048
1049 }
1050
1051 @Override
1052 public void visitExceptionTable(ExceptionTable obj){
1053 try {
1054
1055 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
1056
1057 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
1058 if (! name.equals("Exceptions")){
1059 throw new ClassConstraintException("The Exceptions attribute '"+tostring(obj)+"' is not correctly named 'Exceptions' but '"+name+"'.");
1060 }
1061
1062 int[] exc_indices = obj.getExceptionIndexTable();
1063
1064 for (int i=0; i<exc_indices.length; i++){
1065 checkIndex(obj, exc_indices[i], CONST_Class);
1066
1067 ConstantClass cc = (ConstantClass) (cp.getConstant(exc_indices[i]));
1068 checkIndex(cc, cc.getNameIndex(), CONST_Utf8);
1069 String cname = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes().replace('/','.');
1070
1071 Verifier v = VerifierFactory.getVerifier(cname);
1072 VerificationResult vr = v.doPass1();
1073
1074 if (vr != VerificationResult.VR_OK){
1075 throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+"' as an Exception but it does not pass verification pass 1: "+vr);
1076 }
1077 else{
1078
1079
1080 JavaClass e = Repository.lookupClass(cname);
1081 JavaClass t = Repository.lookupClass(Type.THROWABLE.getClassName());
1082 JavaClass o = Repository.lookupClass(Type.OBJECT.getClassName());
1083 while (e != o){
1084 if (e == t) {
1085 break;
1086 }
1087
1088 v = VerifierFactory.getVerifier(e.getSuperclassName());
1089 vr = v.doPass1();
1090 if (vr != VerificationResult.VR_OK){
1091 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);
1092 }
1093 else{
1094 e = Repository.lookupClass(e.getSuperclassName());
1095 }
1096 }
1097 if (e != t) {
1098 throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+"' as an Exception but it is not a subclass of '"+t.getClassName()+"'.");
1099 }
1100 }
1101 }
1102
1103 } catch (ClassNotFoundException e) {
1104
1105 throw new AssertionViolatedException("Missing class: " + e.toString(), e);
1106 }
1107 }
1108
1109
1110
1111
1112
1113 @Override
1114 public void visitLineNumberTable(LineNumberTable obj){
1115 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
1116
1117 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
1118 if (! name.equals("LineNumberTable")){
1119 throw new ClassConstraintException("The LineNumberTable attribute '"+tostring(obj)+"' is not correctly named 'LineNumberTable' but '"+name+"'.");
1120 }
1121
1122
1123
1124
1125
1126 }
1127 @Override
1128 public void visitLocalVariableTable(LocalVariableTable obj){
1129
1130
1131 }
1132
1133
1134
1135 @Override
1136 public void visitUnknown(Unknown obj){
1137
1138 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
1139
1140
1141 addMessage("Unknown attribute '"+tostring(obj)+"'. This attribute is not known in any context!");
1142 }
1143
1144
1145
1146 @Override
1147 public void visitLocalVariable(LocalVariable obj){
1148
1149
1150
1151
1152 }
1153 @Override
1154 public void visitCodeException(CodeException obj){
1155
1156
1157
1158
1159
1160 }
1161 @Override
1162 public void visitConstantPool(ConstantPool obj){
1163
1164
1165
1166 }
1167 @Override
1168 public void visitInnerClass(InnerClass obj){
1169
1170
1171 }
1172 @Override
1173 public void visitLineNumber(LineNumber obj){
1174
1175
1176
1177
1178 }
1179 }
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195 private void field_and_method_refs_are_valid(){
1196 try {
1197 JavaClass jc = Repository.lookupClass(myOwner.getClassName());
1198 DescendingVisitor v = new DescendingVisitor(jc, new FAMRAV_Visitor(jc));
1199 v.visit();
1200
1201 } catch (ClassNotFoundException e) {
1202
1203 throw new AssertionViolatedException("Missing class: " + e.toString(), e);
1204 }
1205 }
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216 private class FAMRAV_Visitor extends EmptyVisitor{
1217 private final ConstantPool cp;
1218 private FAMRAV_Visitor(JavaClass _jc){
1219 cp = _jc.getConstantPool();
1220 }
1221
1222 @Override
1223 public void visitConstantFieldref(ConstantFieldref obj){
1224 if (obj.getTag() != Constants.CONSTANT_Fieldref){
1225 throw new ClassConstraintException("ConstantFieldref '"+tostring(obj)+"' has wrong tag!");
1226 }
1227 int name_and_type_index = obj.getNameAndTypeIndex();
1228 ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
1229 String name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes();
1230 if (!validFieldName(name)){
1231 throw new ClassConstraintException("Invalid field name '"+name+"' referenced by '"+tostring(obj)+"'.");
1232 }
1233
1234 int class_index = obj.getClassIndex();
1235 ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
1236 String className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes();
1237 if (! validClassName(className)){
1238 throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
1239 }
1240
1241 String sig = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes();
1242
1243 try{
1244 Type.getType(sig);
1245 }
1246 catch (ClassFormatException cfe){
1247 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.", cfe);
1248 }
1249 }
1250
1251 @Override
1252 public void visitConstantMethodref(ConstantMethodref obj){
1253 if (obj.getTag() != Constants.CONSTANT_Methodref){
1254 throw new ClassConstraintException("ConstantMethodref '"+tostring(obj)+"' has wrong tag!");
1255 }
1256 int name_and_type_index = obj.getNameAndTypeIndex();
1257 ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
1258 String name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes();
1259 if (!validClassMethodName(name)){
1260 throw new ClassConstraintException("Invalid (non-interface) method name '"+name+"' referenced by '"+tostring(obj)+"'.");
1261 }
1262
1263 int class_index = obj.getClassIndex();
1264 ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
1265 String className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes();
1266 if (! validClassName(className)){
1267 throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
1268 }
1269
1270 String sig = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes();
1271
1272 try{
1273 Type t = Type.getReturnType(sig);
1274 if ( name.equals(CONSTRUCTOR_NAME) && (t != Type.VOID) ){
1275 throw new ClassConstraintException("Instance initialization method must have VOID return type.");
1276 }
1277 }
1278 catch (ClassFormatException cfe){
1279 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.", cfe);
1280 }
1281 }
1282
1283 @Override
1284 public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj){
1285 if (obj.getTag() != Constants.CONSTANT_InterfaceMethodref){
1286 throw new ClassConstraintException("ConstantInterfaceMethodref '"+tostring(obj)+"' has wrong tag!");
1287 }
1288 int name_and_type_index = obj.getNameAndTypeIndex();
1289 ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
1290 String name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes();
1291 if (!validInterfaceMethodName(name)){
1292 throw new ClassConstraintException("Invalid (interface) method name '"+name+"' referenced by '"+tostring(obj)+"'.");
1293 }
1294
1295 int class_index = obj.getClassIndex();
1296 ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
1297 String className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes();
1298 if (! validClassName(className)){
1299 throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
1300 }
1301
1302 String sig = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes();
1303
1304 try{
1305 Type t = Type.getReturnType(sig);
1306 if ( name.equals(STATIC_INITIALIZER_NAME) && (t != Type.VOID) ){
1307 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.");
1308 }
1309 }
1310 catch (ClassFormatException cfe){
1311 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.", cfe);
1312 }
1313
1314 }
1315
1316 }
1317
1318
1319
1320
1321
1322 private static final boolean validClassName(String name){
1323
1324
1325
1326
1327 return true;
1328 }
1329
1330
1331
1332
1333
1334
1335
1336
1337 private static boolean validMethodName(String name, boolean allowStaticInit){
1338 if (validJavaLangMethodName(name)) {
1339 return true;
1340 }
1341
1342 if (allowStaticInit){
1343 return (name.equals(CONSTRUCTOR_NAME) || name.equals(STATIC_INITIALIZER_NAME));
1344 }
1345 else{
1346 return name.equals(CONSTRUCTOR_NAME);
1347 }
1348 }
1349
1350
1351
1352
1353
1354
1355 private static boolean validClassMethodName(String name){
1356 return validMethodName(name, false);
1357 }
1358
1359
1360
1361
1362
1363
1364
1365 private static boolean validJavaLangMethodName(String name){
1366 if (!Character.isJavaIdentifierStart(name.charAt(0))) {
1367 return false;
1368 }
1369
1370 for (int i=1; i<name.length(); i++){
1371 if (!Character.isJavaIdentifierPart(name.charAt(i))) {
1372 return false;
1373 }
1374 }
1375 return true;
1376 }
1377
1378
1379
1380
1381
1382
1383 private static boolean validInterfaceMethodName(String name){
1384
1385 if (name.startsWith("<")) {
1386 return false;
1387 }
1388 return validJavaLangMethodName(name);
1389 }
1390
1391
1392
1393
1394
1395 private static boolean validJavaIdentifier(String name){
1396 if (name.length() == 0) {
1397 return false;
1398 }
1399
1400
1401 if (!Character.isJavaIdentifierStart(name.charAt(0))) {
1402 return false;
1403 }
1404
1405 for (int i=1; i<name.length(); i++){
1406 if (!Character.isJavaIdentifierPart(name.charAt(i))) {
1407 return false;
1408 }
1409 }
1410 return true;
1411 }
1412
1413
1414
1415
1416
1417 private static boolean validFieldName(String name){
1418
1419 return validJavaIdentifier(name);
1420 }
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441 private static class InnerClassDetector extends EmptyVisitor{
1442 private boolean hasInnerClass = false;
1443 private JavaClass jc;
1444 private ConstantPool cp;
1445
1446
1447 public InnerClassDetector(JavaClass _jc){
1448 jc = _jc;
1449 cp = jc.getConstantPool();
1450 (new DescendingVisitor(jc, this)).visit();
1451 }
1452
1453
1454
1455
1456 public boolean innerClassReferenced(){
1457 return hasInnerClass;
1458 }
1459
1460 @Override
1461 public void visitConstantClass(ConstantClass obj){
1462 Constant c = cp.getConstant(obj.getNameIndex());
1463 if (c instanceof ConstantUtf8){
1464 String classname = ((ConstantUtf8) c).getBytes();
1465 if (classname.startsWith(jc.getClassName().replace('.','/')+"$")){
1466 hasInnerClass = true;
1467 }
1468 }
1469 }
1470 }
1471
1472
1473
1474
1475 private static String tostring(Node n){
1476 return new StringRepresentation(n).toString();
1477 }
1478 }