1 package org.apache.bcel.verifier.statics;
2
3 /* ====================================================================
4 * The Apache Software License, Version 1.1
5 *
6 * Copyright (c) 2001 The Apache Software Foundation. All rights
7 * reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * 3. The end-user documentation included with the redistribution,
22 * if any, must include the following acknowledgment:
23 * "This product includes software developed by the
24 * Apache Software Foundation (http://www.apache.org/)."
25 * Alternately, this acknowledgment may appear in the software itself,
26 * if and wherever such third-party acknowledgments normally appear.
27 *
28 * 4. The names "Apache" and "Apache Software Foundation" and
29 * "Apache BCEL" must not be used to endorse or promote products
30 * derived from this software without prior written permission. For
31 * written permission, please contact apache@apache.org.
32 *
33 * 5. Products derived from this software may not be called "Apache",
34 * "Apache BCEL", nor may "Apache" appear in their name, without
35 * prior written permission of the Apache Software Foundation.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 * ====================================================================
50 *
51 * This software consists of voluntary contributions made by many
52 * individuals on behalf of the Apache Software Foundation. For more
53 * information on the Apache Software Foundation, please see
54 * <http://www.apache.org/>.
55 */
56
57 import org.apache.bcel.Constants;
58 import org.apache.bcel.Repository;
59 import org.apache.bcel.classfile.*;
60 import org.apache.bcel.classfile.DescendingVisitor; // Use _this_ one!
61 import org.apache.bcel.classfile.EmptyVisitor; // Use _this_ one!
62 import org.apache.bcel.classfile.Visitor; // Use _this_ one!
63 import org.apache.bcel.generic.*;
64 import org.apache.bcel.verifier.*;
65 import org.apache.bcel.verifier.exc.*;
66 import java.util.HashMap;
67 import java.util.HashSet;
68
69 /***
70 * This PassVerifier verifies a class file according to
71 * pass 2 as described in The Java Virtual Machine
72 * Specification, 2nd edition.
73 * More detailed information is to be found at the do_verify()
74 * method's documentation.
75 *
76 * @version $Id: Pass2Verifier.java,v 1.2 2002/06/13 09:32:50 enver Exp $
77 * @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A>
78 * @see #do_verify()
79 */
80 public final class Pass2Verifier extends PassVerifier implements Constants{
81
82 /***
83 * The LocalVariableInfo instances used by Pass3bVerifier.
84 * localVariablesInfos[i] denotes the information for the
85 * local variables of method number i in the
86 * JavaClass this verifier operates on.
87 */
88 private LocalVariablesInfo[] localVariablesInfos;
89
90 /*** The Verifier that created this. */
91 private Verifier myOwner;
92
93 /***
94 * Should only be instantiated by a Verifier.
95 *
96 * @see Verifier
97 */
98 public Pass2Verifier(Verifier owner){
99 myOwner = owner;
100 }
101
102 /***
103 * Returns a LocalVariablesInfo object containing information
104 * about the usage of the local variables in the Code attribute
105 * of the said method or <B>null</B> if the class file this
106 * Pass2Verifier operates on could not be pass-2-verified correctly.
107 * The method number method_nr is the method you get using
108 * <B>Repository.lookupClass(myOwner.getClassname()).getMethods()[method_nr];</B>.
109 * You should not add own information. Leave that to JustIce.
110 */
111 public LocalVariablesInfo getLocalVariablesInfo(int method_nr){
112 if (this.verify() != VerificationResult.VR_OK) return null; // It's cached, don't worry.
113 if (method_nr < 0 || method_nr >= localVariablesInfos.length){
114 throw new AssertionViolatedException("Method number out of range.");
115 }
116 return localVariablesInfos[method_nr];
117 }
118
119 /***
120 * Pass 2 is the pass where static properties of the
121 * class file are checked without looking into "Code"
122 * arrays of methods.
123 * This verification pass is usually invoked when
124 * a class is resolved; and it may be possible that
125 * this verification pass has to load in other classes
126 * such as superclasses or implemented interfaces.
127 * Therefore, Pass 1 is run on them.<BR>
128 * Note that most referenced classes are <B>not</B> loaded
129 * in for verification or for an existance check by this
130 * pass; only the syntactical correctness of their names
131 * and descriptors (a.k.a. signatures) is checked.<BR>
132 * Very few checks that conceptually belong here
133 * are delayed until pass 3a in JustIce. JustIce does
134 * not only check for syntactical correctness but also
135 * for semantical sanity - therefore it needs access to
136 * the "Code" array of methods in a few cases. Please
137 * see the pass 3a documentation, too.
138 *
139 * @see org.apache.bcel.verifier.statics.Pass3aVerifier
140 */
141 public VerificationResult do_verify(){
142 VerificationResult vr1 = myOwner.doPass1();
143 if (vr1.equals(VerificationResult.VR_OK)){
144
145 // For every method, we could have information about the local variables out of LocalVariableTable attributes of
146 // the Code attributes.
147 localVariablesInfos = new LocalVariablesInfo[Repository.lookupClass(myOwner.getClassName()).getMethods().length];
148
149 VerificationResult vr = VerificationResult.VR_OK; // default.
150 try{
151 constant_pool_entries_satisfy_static_constraints();
152 field_and_method_refs_are_valid();
153 every_class_has_an_accessible_superclass();
154 final_methods_are_not_overridden();
155 }
156 catch (ClassConstraintException cce){
157 vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage());
158 }
159 return vr;
160 }
161 else
162 return VerificationResult.VR_NOTYET;
163 }
164
165 /***
166 * Ensures that every class has a super class and that
167 * <B>final</B> classes are not subclassed.
168 * This means, the class this Pass2Verifier operates
169 * on has proper super classes (transitively) up to
170 * java.lang.Object.
171 * The reason for really loading (and Pass1-verifying)
172 * all of those classes here is that we need them in
173 * Pass2 anyway to verify no final methods are overridden
174 * (that could be declared anywhere in the ancestor hierarchy).
175 *
176 * @throws ClassConstraintException otherwise.
177 */
178 private void every_class_has_an_accessible_superclass(){
179 HashSet hs = new HashSet(); // save class names to detect circular inheritance
180 JavaClass jc = Repository.lookupClass(myOwner.getClassName());
181 int supidx = -1;
182
183 while (supidx != 0){
184 supidx = jc.getSuperclassNameIndex();
185
186 if (supidx == 0){
187 if (jc != Repository.lookupClass(Type.OBJECT.getClassName())){
188 throw new ClassConstraintException("Superclass of '"+jc.getClassName()+"' missing but not "+Type.OBJECT.getClassName()+" itself!");
189 }
190 }
191 else{
192 String supername = jc.getSuperclassName();
193 if (! hs.add(supername)){ // If supername already is in the list
194 throw new ClassConstraintException("Circular superclass hierarchy detected.");
195 }
196 Verifier v = VerifierFactory.getVerifier(supername);
197 VerificationResult vr = v.doPass1();
198
199 if (vr != VerificationResult.VR_OK){
200 throw new ClassConstraintException("Could not load in ancestor class '"+supername+"'.");
201 }
202 jc = Repository.lookupClass(supername);
203
204 if (jc.isFinal()){
205 throw new ClassConstraintException("Ancestor class '"+supername+"' has the FINAL access modifier and must therefore not be subclassed.");
206 }
207 }
208 }
209 }
210
211 /***
212 * Ensures that <B>final</B> methods are not overridden.
213 * <B>Precondition to run this method:
214 * constant_pool_entries_satisfy_static_constraints() and
215 * every_class_has_an_accessible_superclass() have to be invoked before
216 * (in that order).</B>
217 *
218 * @throws ClassConstraintException otherwise.
219 * @see #constant_pool_entries_satisfy_static_constraints()
220 * @see #every_class_has_an_accessible_superclass()
221 */
222 private void final_methods_are_not_overridden(){
223 HashMap hashmap = new HashMap();
224 JavaClass jc = Repository.lookupClass(myOwner.getClassName());
225
226 int supidx = -1;
227 while (supidx != 0){
228 supidx = jc.getSuperclassNameIndex();
229
230 Method[] methods = jc.getMethods();
231 for (int i=0; i<methods.length; i++){
232 String name_and_sig = (methods[i].getName()+methods[i].getSignature());
233
234 if (hashmap.containsKey(name_and_sig)){
235 if (methods[i].isFinal()){
236 throw new ClassConstraintException("Method '"+name_and_sig+"' in class '"+hashmap.get(name_and_sig)+"' overrides the final (not-overridable) definition in class '"+jc.getClassName()+"'.");
237 }
238 else{
239 if (!methods[i].isStatic()){ // static methods don't inherit
240 hashmap.put(name_and_sig, jc.getClassName());
241 }
242 }
243 }
244 else{
245 if (!methods[i].isStatic()){ // static methods don't inherit
246 hashmap.put(name_and_sig, jc.getClassName());
247 }
248 }
249 }
250
251 jc = Repository.lookupClass(jc.getSuperclassName()); // Well, for OBJECT this returns OBJECT so it works (could return anything but must not throw an Exception).
252 }
253
254 }
255
256 /***
257 * Ensures that the constant pool entries satisfy the static constraints
258 * as described in The Java Virtual Machine Specification, 2nd Edition.
259 *
260 * @throws ClassConstraintException otherwise.
261 */
262 private void constant_pool_entries_satisfy_static_constraints(){
263 // Most of the consistency is handled internally by BCEL; here
264 // we only have to verify if the indices of the constants point
265 // to constants of the appropriate type and such.
266 JavaClass jc = Repository.lookupClass(myOwner.getClassName());
267 new CPESSC_Visitor(jc); // constructor implicitely traverses jc
268 }
269
270 /***
271 * A Visitor class that ensures the constant pool satisfies the static
272 * constraints.
273 * The visitXXX() methods throw ClassConstraintException instances otherwise.
274 *
275 * @see #constant_pool_entries_satisfy_static_constraints()
276 */
277 private class CPESSC_Visitor extends org.apache.bcel.classfile.EmptyVisitor implements Visitor{
278 private Class CONST_Class;
279 /*
280 private Class CONST_Fieldref;
281 private Class CONST_Methodref;
282 private Class CONST_InterfaceMethodref;
283 */
284 private Class CONST_String;
285 private Class CONST_Integer;
286 private Class CONST_Float;
287 private Class CONST_Long;
288 private Class CONST_Double;
289 private Class CONST_NameAndType;
290 private Class CONST_Utf8;
291
292 private final JavaClass jc;
293 private final ConstantPool cp; // ==jc.getConstantPool() -- only here to save typing work and computing power.
294 private final int cplen; // == cp.getLength() -- to save computing power.
295 private DescendingVisitor carrier;
296
297 private HashSet field_names = new HashSet();
298 private HashSet field_names_and_desc = new HashSet();
299 private HashSet method_names_and_desc = new HashSet();
300
301 private CPESSC_Visitor(JavaClass _jc){
302 jc = _jc;
303 cp = _jc.getConstantPool();
304 cplen = cp.getLength();
305
306 CONST_Class = org.apache.bcel.classfile.ConstantClass.class;
307 /*
308 CONST_Fieldref = org.apache.bcel.classfile.ConstantFieldref.class;
309 CONST_Methodref = org.apache.bcel.classfile.ConstantMethodref.class;
310 CONST_InterfaceMethodref = org.apache.bcel.classfile.ConstantInterfaceMethodref.class;
311 */
312 CONST_String = org.apache.bcel.classfile.ConstantString.class;
313 CONST_Integer = org.apache.bcel.classfile.ConstantInteger.class;
314 CONST_Float = org.apache.bcel.classfile.ConstantFloat.class;
315 CONST_Long = org.apache.bcel.classfile.ConstantLong.class;
316 CONST_Double = org.apache.bcel.classfile.ConstantDouble.class;
317 CONST_NameAndType = org.apache.bcel.classfile.ConstantNameAndType.class;
318 CONST_Utf8 = org.apache.bcel.classfile.ConstantUtf8.class;
319
320 carrier = new DescendingVisitor(_jc, this);
321 carrier.visit();
322 }
323
324 private void checkIndex(Node referrer, int index, Class shouldbe){
325 if ((index < 0) || (index >= cplen)){
326 throw new ClassConstraintException("Invalid index '"+index+"' used by '"+tostring(referrer)+"'.");
327 }
328 Constant c = cp.getConstant(index);
329 if (! shouldbe.isInstance(c)){
330 /* String isnot = shouldbe.toString().substring(shouldbe.toString().lastIndexOf(".")+1); //Cut all before last "." */
331 throw new ClassCastException("Illegal constant '"+tostring(c)+"' at index '"+index+"'. '"+tostring(referrer)+"' expects a '"+shouldbe+"'.");
332 }
333 }
334 ///////////////////////////////////////
335 // ClassFile structure (vmspec2 4.1) //
336 ///////////////////////////////////////
337 public void visitJavaClass(JavaClass obj){
338 Attribute[] atts = obj.getAttributes();
339 boolean foundSourceFile = false;
340 boolean foundInnerClasses = false;
341
342 // Is there an InnerClass referenced?
343 // This is a costly check; existing verifiers don't do it!
344 boolean hasInnerClass = new InnerClassDetector(jc).innerClassReferenced();
345
346 for (int i=0; i<atts.length; i++){
347 if ((! (atts[i] instanceof SourceFile)) &&
348 (! (atts[i] instanceof Deprecated)) &&
349 (! (atts[i] instanceof InnerClasses)) &&
350 (! (atts[i] instanceof Synthetic))){
351 addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of the ClassFile structure '"+tostring(obj)+"' is unknown and will therefore be ignored.");
352 }
353
354 if (atts[i] instanceof SourceFile){
355 if (foundSourceFile == false) foundSourceFile = true;
356 else throw new ClassConstraintException("A ClassFile structure (like '"+tostring(obj)+"') may have no more than one SourceFile attribute."); //vmspec2 4.7.7
357 }
358
359 if (atts[i] instanceof InnerClasses){
360 if (foundInnerClasses == false) foundInnerClasses = true;
361 else{
362 if (hasInnerClass){
363 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.");
364 }
365 }
366 if (!hasInnerClass){
367 addMessage("No referenced Inner Class found, but InnerClasses attribute '"+tostring(atts[i])+"' found. Strongly suggest removal of that attribute.");
368 }
369 }
370
371 }
372 if (hasInnerClass && !foundInnerClasses){
373 //throw new ClassConstraintException("A Classfile structure (like '"+tostring(obj)+"') must have exactly one InnerClasses attribute if at least one Inner Class is referenced (which is the case). No InnerClasses attribute was found.");
374 //vmspec2, page 125 says it would be a constraint: but existing verifiers
375 //don't check it and javac doesn't satisfy it when it comes to anonymous
376 //inner classes
377 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.");
378 }
379 }
380 /////////////////////////////
381 // CONSTANTS (vmspec2 4.4) //
382 /////////////////////////////
383 public void visitConstantClass(ConstantClass obj){
384 if (obj.getTag() != Constants.CONSTANT_Class){
385 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
386 }
387 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
388
389 }
390 public void visitConstantFieldref(ConstantFieldref obj){
391 if (obj.getTag() != Constants.CONSTANT_Fieldref){
392 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
393 }
394 checkIndex(obj, obj.getClassIndex(), CONST_Class);
395 checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
396 }
397 public void visitConstantMethodref(ConstantMethodref obj){
398 if (obj.getTag() != Constants.CONSTANT_Methodref){
399 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
400 }
401 checkIndex(obj, obj.getClassIndex(), CONST_Class);
402 checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
403 }
404 public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj){
405 if (obj.getTag() != Constants.CONSTANT_InterfaceMethodref){
406 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
407 }
408 checkIndex(obj, obj.getClassIndex(), CONST_Class);
409 checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
410 }
411 public void visitConstantString(ConstantString obj){
412 if (obj.getTag() != Constants.CONSTANT_String){
413 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
414 }
415 checkIndex(obj, obj.getStringIndex(), CONST_Utf8);
416 }
417 public void visitConstantInteger(ConstantInteger obj){
418 if (obj.getTag() != Constants.CONSTANT_Integer){
419 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
420 }
421 // no indices to check
422 }
423 public void visitConstantFloat(ConstantFloat obj){
424 if (obj.getTag() != Constants.CONSTANT_Float){
425 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
426 }
427 //no indices to check
428 }
429 public void visitConstantLong(ConstantLong obj){
430 if (obj.getTag() != Constants.CONSTANT_Long){
431 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
432 }
433 //no indices to check
434 }
435 public void visitConstantDouble(ConstantDouble obj){
436 if (obj.getTag() != Constants.CONSTANT_Double){
437 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
438 }
439 //no indices to check
440 }
441 public void visitConstantNameAndType(ConstantNameAndType obj){
442 if (obj.getTag() != Constants.CONSTANT_NameAndType){
443 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
444 }
445 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
446 //checkIndex(obj, obj.getDescriptorIndex(), CONST_Utf8); //inconsistently named in BCEL, see below.
447 checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
448 }
449 public void visitConstantUtf8(ConstantUtf8 obj){
450 if (obj.getTag() != Constants.CONSTANT_Utf8){
451 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
452 }
453 //no indices to check
454 }
455 //////////////////////////
456 // FIELDS (vmspec2 4.5) //
457 //////////////////////////
458 public void visitField(Field obj){
459
460 if (jc.isClass()){
461 int maxone=0;
462 if (obj.isPrivate()) maxone++;
463 if (obj.isProtected()) maxone++;
464 if (obj.isPublic()) maxone++;
465 if (maxone > 1){
466 throw new ClassConstraintException("Field '"+tostring(obj)+"' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set.");
467 }
468
469 if (obj.isFinal() && obj.isVolatile()){
470 throw new ClassConstraintException("Field '"+tostring(obj)+"' must only have at most one of its ACC_FINAL, ACC_VOLATILE modifiers set.");
471 }
472 }
473 else{ // isInterface!
474 if (!obj.isPublic()){
475 throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_PUBLIC modifier set but hasn't!");
476 }
477 if (!obj.isStatic()){
478 throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_STATIC modifier set but hasn't!");
479 }
480 if (!obj.isFinal()){
481 throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_FINAL modifier set but hasn't!");
482 }
483 }
484
485 if ((obj.getAccessFlags() & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_VOLATILE|ACC_TRANSIENT)) > 0){
486 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).");
487 }
488
489 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
490
491 String name = obj.getName();
492 if (! validFieldName(name)){
493 throw new ClassConstraintException("Field '"+tostring(obj)+"' has illegal name '"+obj.getName()+"'.");
494 }
495
496 // A descriptor is often named signature in BCEL
497 checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
498
499 String sig = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
500
501 try{
502 Type.getType(sig); /* Don't need the return value */
503 }
504 catch (ClassFormatError cfe){ // sometimes BCEL is a little harsh describing exceptional situations.
505 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
506 }
507
508 String nameanddesc = (name+sig);
509 if (field_names_and_desc.contains(nameanddesc)){
510 throw new ClassConstraintException("No two fields (like '"+tostring(obj)+"') are allowed have same names and descriptors!");
511 }
512 if (field_names.contains(name)){
513 addMessage("More than one field of name '"+name+"' detected (but with different type descriptors). This is very unusual.");
514 }
515 field_names_and_desc.add(nameanddesc);
516 field_names.add(name);
517
518 Attribute[] atts = obj.getAttributes();
519 for (int i=0; i<atts.length; i++){
520 if ((! (atts[i] instanceof ConstantValue)) &&
521 (! (atts[i] instanceof Synthetic)) &&
522 (! (atts[i] instanceof Deprecated))){
523 addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Field '"+tostring(obj)+"' is unknown and will therefore be ignored.");
524 }
525 if (! (atts[i] instanceof ConstantValue)){
526 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.");
527 }
528 }
529 }
530 ///////////////////////////
531 // METHODS (vmspec2 4.6) //
532 ///////////////////////////
533 public void visitMethod(Method obj){
534
535 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
536
537 String name = obj.getName();
538 if (! validMethodName(name, true)){
539 throw new ClassConstraintException("Method '"+tostring(obj)+"' has illegal name '"+name+"'.");
540 }
541
542 // A descriptor is often named signature in BCEL
543 checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
544
545 String sig = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes(); // Method's signature(=descriptor)
546
547 Type t;
548 Type[] ts; // needed below the try block.
549 try{
550 t = Type.getReturnType(sig);
551 ts = Type.getArgumentTypes(sig);
552 }
553 catch (ClassFormatError cfe){
554 // Well, BCEL sometimes is a little harsh describing exceptional situations.
555 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by Method '"+tostring(obj)+"'.");
556 }
557
558 // Check if referenced objects exist.
559 Type act = t;
560 if (act instanceof ArrayType) act = ((ArrayType) act).getBasicType();
561 if (act instanceof ObjectType){
562 Verifier v = VerifierFactory.getVerifier( ((ObjectType) act).getClassName() );
563 VerificationResult vr = v.doPass1();
564 if (vr != VerificationResult.VR_OK) {
565 throw new ClassConstraintException("Method '"+tostring(obj)+"' has a return type that does not pass verification pass 1: '"+vr+"'.");
566 }
567 }
568
569 for (int i=0; i<ts.length; i++){
570 act = ts[i];
571 if (act instanceof ArrayType) act = ((ArrayType) act).getBasicType();
572 if (act instanceof ObjectType){
573 Verifier v = VerifierFactory.getVerifier( ((ObjectType) act).getClassName() );
574 VerificationResult vr = v.doPass1();
575 if (vr != VerificationResult.VR_OK) {
576 throw new ClassConstraintException("Method '"+tostring(obj)+"' has an argument type that does not pass verification pass 1: '"+vr+"'.");
577 }
578 }
579 }
580
581 // Nearly forgot this! Funny return values are allowed, but a non-empty arguments list makes a different method out of it!
582 if (name.equals(STATIC_INITIALIZER_NAME) && (ts.length != 0)){
583 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).");
584 }
585
586 if (jc.isClass()){
587 int maxone=0;
588 if (obj.isPrivate()) maxone++;
589 if (obj.isProtected()) maxone++;
590 if (obj.isPublic()) maxone++;
591 if (maxone > 1){
592 throw new ClassConstraintException("Method '"+tostring(obj)+"' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set.");
593 }
594
595 if (obj.isAbstract()){
596 if (obj.isFinal()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_FINAL modifier set.");
597 if (obj.isNative()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_NATIVE modifier set.");
598 if (obj.isPrivate()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_PRIVATE modifier set.");
599 if (obj.isStatic()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_STATIC modifier set.");
600 if (obj.isStrictfp()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_STRICT modifier set.");
601 if (obj.isSynchronized()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_SYNCHRONIZED modifier set.");
602 }
603 }
604 else{ // isInterface!
605 if (!name.equals(STATIC_INITIALIZER_NAME)){//vmspec2, p.116, 2nd paragraph
606 if (!obj.isPublic()){
607 throw new ClassConstraintException("Interface method '"+tostring(obj)+"' must have the ACC_PUBLIC modifier set but hasn't!");
608 }
609 if (!obj.isAbstract()){
610 throw new ClassConstraintException("Interface method '"+tostring(obj)+"' must have the ACC_STATIC modifier set but hasn't!");
611 }
612 if ( obj.isPrivate() ||
613 obj.isProtected() ||
614 obj.isStatic() ||
615 obj.isFinal() ||
616 obj.isSynchronized() ||
617 obj.isNative() ||
618 obj.isStrictfp() ){
619 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.");
620 }
621 }
622 }
623
624 // A specific instance initialization method... (vmspec2,Page 116).
625 if (name.equals(CONSTRUCTOR_NAME)){
626 //..may have at most one of ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC set: is checked above.
627 //..may also have ACC_STRICT set, but none of the other flags in table 4.5 (vmspec2, page 115)
628 if ( obj.isStatic() ||
629 obj.isFinal() ||
630 obj.isSynchronized() ||
631 obj.isNative() ||
632 obj.isAbstract() ){
633 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.");
634 }
635 }
636
637 // Class and interface initialization methods...
638 if (name.equals(STATIC_INITIALIZER_NAME)){
639 if ((obj.getAccessFlags() & (~ACC_STRICT)) > 0){
640 addMessage("Class or interface initialization method '"+tostring(obj)+"' has superfluous access modifier(s) set: everything but ACC_STRICT is ignored.");
641 }
642 if (obj.isAbstract()){
643 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.");
644 }
645 }
646
647 if ((obj.getAccessFlags() & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) > 0){
648 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).");
649 }
650
651 String nameanddesc = (name+sig);
652 if (method_names_and_desc.contains(nameanddesc)){
653 throw new ClassConstraintException("No two methods (like '"+tostring(obj)+"') are allowed have same names and desciptors!");
654 }
655 method_names_and_desc.add(nameanddesc);
656
657 Attribute[] atts = obj.getAttributes();
658 int num_code_atts = 0;
659 for (int i=0; i<atts.length; i++){
660 if ((! (atts[i] instanceof Code)) &&
661 (! (atts[i] instanceof ExceptionTable)) &&
662 (! (atts[i] instanceof Synthetic)) &&
663 (! (atts[i] instanceof Deprecated))){
664 addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Method '"+tostring(obj)+"' is unknown and will therefore be ignored.");
665 }
666 if ((! (atts[i] instanceof Code)) &&
667 (! (atts[i] instanceof ExceptionTable))){
668 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.");
669 }
670 if ((atts[i] instanceof Code) && (obj.isNative() || obj.isAbstract())){
671 throw new ClassConstraintException("Native or abstract methods like '"+tostring(obj)+"' must not have a Code attribute like '"+tostring(atts[i])+"'."); //vmspec2 page120, 4.7.3
672 }
673 if (atts[i] instanceof Code) num_code_atts++;
674 }
675 if ( !obj.isNative() && !obj.isAbstract() && num_code_atts != 1){
676 throw new ClassConstraintException("Non-native, non-abstract methods like '"+tostring(obj)+"' must have exactly one Code attribute (found: "+num_code_atts+").");
677 }
678 }
679 ///////////////////////////////////////////////////////
680 // ClassFile-structure-ATTRIBUTES (vmspec2 4.1, 4.7) //
681 ///////////////////////////////////////////////////////
682 public void visitSourceFile(SourceFile obj){//vmspec2 4.7.7
683
684 // zero or one SourceFile attr per ClassFile: see visitJavaClass()
685
686 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
687
688 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
689 if (! name.equals("SourceFile")){
690 throw new ClassConstraintException("The SourceFile attribute '"+tostring(obj)+"' is not correctly named 'SourceFile' but '"+name+"'.");
691 }
692
693 checkIndex(obj, obj.getSourceFileIndex(), CONST_Utf8);
694
695 String sourcefilename = ((ConstantUtf8) cp.getConstant(obj.getSourceFileIndex())).getBytes(); //==obj.getSourceFileName() ?
696 String sourcefilenamelc = sourcefilename.toLowerCase();
697
698 if ( (sourcefilename.indexOf('/') != -1) ||
699 (sourcefilename.indexOf('//') != -1) ||
700 (sourcefilename.indexOf(':') != -1) ||
701 (sourcefilenamelc.lastIndexOf(".java") == -1) ){
702 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.");
703 }
704 }
705 public void visitDeprecated(Deprecated obj){//vmspec2 4.7.10
706 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
707
708 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
709 if (! name.equals("Deprecated")){
710 throw new ClassConstraintException("The Deprecated attribute '"+tostring(obj)+"' is not correctly named 'Deprecated' but '"+name+"'.");
711 }
712 }
713 public void visitSynthetic(Synthetic obj){//vmspec2 4.7.6
714 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
715 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
716 if (! name.equals("Synthetic")){
717 throw new ClassConstraintException("The Synthetic attribute '"+tostring(obj)+"' is not correctly named 'Synthetic' but '"+name+"'.");
718 }
719 }
720 public void visitInnerClasses(InnerClasses obj){//vmspec2 4.7.5
721
722 // exactly one InnerClasses attr per ClassFile if some inner class is refernced: see visitJavaClass()
723
724 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
725
726 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
727 if (! name.equals("InnerClasses")){
728 throw new ClassConstraintException("The InnerClasses attribute '"+tostring(obj)+"' is not correctly named 'InnerClasses' but '"+name+"'.");
729 }
730
731 InnerClass[] ics = obj.getInnerClasses();
732
733 for (int i=0; i<ics.length; i++){
734 checkIndex(obj, ics[i].getInnerClassIndex(), CONST_Class);
735 int outer_idx = ics[i].getOuterClassIndex();
736 if (outer_idx != 0){
737 checkIndex(obj, outer_idx, CONST_Class);
738 }
739 int innername_idx = ics[i].getInnerNameIndex();
740 if (innername_idx != 0){
741 checkIndex(obj, innername_idx, CONST_Utf8);
742 }
743 int acc = ics[i].getInnerAccessFlags();
744 acc = acc & (~ (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT));
745 if (acc != 0){
746 addMessage("Unknown access flag for inner class '"+tostring(ics[i])+"' set (InnerClasses attribute '"+tostring(obj)+"').");
747 }
748 }
749 // Semantical consistency is not yet checked by Sun, see vmspec2 4.7.5.
750 // [marked TODO in JustIce]
751 }
752 ////////////////////////////////////////////////////////
753 // field_info-structure-ATTRIBUTES (vmspec2 4.5, 4.7) //
754 ////////////////////////////////////////////////////////
755 public void visitConstantValue(ConstantValue obj){//vmspec2 4.7.2
756 // Despite its name, this really is an Attribute,
757 // not a constant!
758 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
759
760 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
761 if (! name.equals("ConstantValue")){
762 throw new ClassConstraintException("The ConstantValue attribute '"+tostring(obj)+"' is not correctly named 'ConstantValue' but '"+name+"'.");
763 }
764
765 Object pred = carrier.predecessor();
766 if (pred instanceof Field){ //ConstantValue attributes are quite senseless if the predecessor is not a field.
767 Field f = (Field) pred;
768 // Field constraints have been checked before -- so we are safe using their type information.
769 Type field_type = Type.getType(((ConstantUtf8) (cp.getConstant(f.getSignatureIndex()))).getBytes());
770
771 int index = obj.getConstantValueIndex();
772 if ((index < 0) || (index >= cplen)){
773 throw new ClassConstraintException("Invalid index '"+index+"' used by '"+tostring(obj)+"'.");
774 }
775 Constant c = cp.getConstant(index);
776
777 if (CONST_Long.isInstance(c) && field_type.equals(Type.LONG)){
778 return;
779 }
780 if (CONST_Float.isInstance(c) && field_type.equals(Type.FLOAT)){
781 return;
782 }
783 if (CONST_Double.isInstance(c) && field_type.equals(Type.DOUBLE)){
784 return;
785 }
786 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))){
787 return;
788 }
789 if (CONST_String.isInstance(c) && field_type.equals(Type.STRING)){
790 return;
791 }
792
793 throw new ClassConstraintException("Illegal type of ConstantValue '"+obj+"' embedding Constant '"+c+"'. It is referenced by field '"+tostring(f)+"' expecting a different type: '"+field_type+"'.");
794 }
795 }
796 // SYNTHETIC: see above
797 // DEPRECATED: see above
798 /////////////////////////////////////////////////////////
799 // method_info-structure-ATTRIBUTES (vmspec2 4.6, 4.7) //
800 /////////////////////////////////////////////////////////
801 public void visitCode(Code obj){//vmspec2 4.7.3
802 // No code attribute allowed for native or abstract methods: see visitMethod(Method).
803 // Code array constraints are checked in Pass3 (3a and 3b).
804
805 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
806
807 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
808 if (! name.equals("Code")){
809 throw new ClassConstraintException("The Code attribute '"+tostring(obj)+"' is not correctly named 'Code' but '"+name+"'.");
810 }
811
812 Method m = null; // satisfy compiler
813 if (!(carrier.predecessor() instanceof Method)){
814 addMessage("Code attribute '"+tostring(obj)+"' is not declared in a method_info structure but in '"+carrier.predecessor()+"'. Ignored.");
815 return;
816 }
817 else{
818 m = (Method) carrier.predecessor(); // we can assume this method was visited before;
819 // i.e. the data consistency was verified.
820 }
821
822 if (obj.getCode().length == 0){
823 throw new ClassConstraintException("Code array of Code attribute '"+tostring(obj)+"' (method '"+m+"') must not be empty.");
824 }
825
826 //In JustIce, the check for correct offsets into the code array is delayed to Pass 3a.
827 CodeException[] exc_table = obj.getExceptionTable();
828 for (int i=0; i<exc_table.length; i++){
829 int exc_index = exc_table[i].getCatchType();
830 if (exc_index != 0){ // if 0, it catches all Throwables
831 checkIndex(obj, exc_index, CONST_Class);
832 ConstantClass cc = (ConstantClass) (cp.getConstant(exc_index));
833 checkIndex(cc, cc.getNameIndex(), CONST_Utf8); // cannot be sure this ConstantClass has already been visited (checked)!
834 String cname = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes().replace('/','.');
835
836 Verifier v = VerifierFactory.getVerifier(cname);
837 VerificationResult vr = v.doPass1();
838
839 if (vr != VerificationResult.VR_OK){
840 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);
841 }
842 else{
843 // We cannot safely trust any other "instanceof" mechanism. We need to transitively verify
844 // the ancestor hierarchy.
845 JavaClass e = Repository.lookupClass(cname);
846 JavaClass t = Repository.lookupClass(Type.THROWABLE.getClassName());
847 JavaClass o = Repository.lookupClass(Type.OBJECT.getClassName());
848 while (e != o){
849 if (e == t) break; // It's a subclass of Throwable, OKAY, leave.
850
851 v = VerifierFactory.getVerifier(e.getSuperclassName());
852 vr = v.doPass1();
853 if (vr != VerificationResult.VR_OK){
854 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);
855 }
856 else{
857 e = Repository.lookupClass(e.getSuperclassName());
858 }
859 }
860 if (e != t) 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()+"'.");
861 }
862 }
863 }
864
865 // Create object for local variables information
866 // This is highly unelegant due to usage of the Visitor pattern.
867 // TODO: rework it.
868 int method_number = -1;
869 Method[] ms = Repository.lookupClass(myOwner.getClassName()).getMethods();
870 for (int mn=0; mn<ms.length; mn++){
871 if (m == ms[mn]){
872 method_number = mn;
873 break;
874 }
875 }
876 if (method_number < 0){ // Mmmmh. Can we be sure BCEL does not sometimes instantiate new objects?
877 throw new AssertionViolatedException("Could not find a known BCEL Method object in the corresponding BCEL JavaClass object.");
878 }
879 localVariablesInfos[method_number] = new LocalVariablesInfo(obj.getMaxLocals());
880
881 int num_of_lvt_attribs = 0;
882 // Now iterate through the attributes the Code attribute has.
883 Attribute[] atts = obj.getAttributes();
884 for (int a=0; a<atts.length; a++){
885 if ((! (atts[a] instanceof LineNumberTable)) &&
886 (! (atts[a] instanceof LocalVariableTable))){
887 addMessage("Attribute '"+tostring(atts[a])+"' as an attribute of Code attribute '"+tostring(obj)+"' (method '"+m+"') is unknown and will therefore be ignored.");
888 }
889 else{// LineNumberTable or LocalVariableTable
890 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.");
891 }
892
893 //LocalVariableTable check (partially delayed to Pass3a).
894 //Here because its easier to collect the information of the
895 //(possibly more than one) LocalVariableTables belonging to
896 //one certain Code attribute.
897 if (atts[a] instanceof LocalVariableTable){ // checks conforming to vmspec2 4.7.9
898
899 LocalVariableTable lvt = (LocalVariableTable) atts[a];
900
901 checkIndex(lvt, lvt.getNameIndex(), CONST_Utf8);
902
903 String lvtname = ((ConstantUtf8) cp.getConstant(lvt.getNameIndex())).getBytes();
904 if (! lvtname.equals("LocalVariableTable")){
905 throw new ClassConstraintException("The LocalVariableTable attribute '"+tostring(lvt)+"' is not correctly named 'LocalVariableTable' but '"+lvtname+"'.");
906 }
907
908 Code code = obj;
909
910 //In JustIce, the check for correct offsets into the code array is delayed to Pass 3a.
911 LocalVariable[] localvariables = lvt.getLocalVariableTable();
912
913 for (int i=0; i<localvariables.length; i++){
914 checkIndex(lvt, localvariables[i].getNameIndex(), CONST_Utf8);
915 String localname = ((ConstantUtf8) cp.getConstant(localvariables[i].getNameIndex())).getBytes();
916 if (!validJavaIdentifier(localname)){
917 throw new ClassConstraintException("LocalVariableTable '"+tostring(lvt)+"' references a local variable by the name '"+localname+"' which is not a legal Java simple name.");
918 }
919
920 checkIndex(lvt, localvariables[i].getSignatureIndex(), CONST_Utf8);
921 String localsig = ((ConstantUtf8) (cp.getConstant(localvariables[i].getSignatureIndex()))).getBytes(); // Local signature(=descriptor)
922 Type t;
923 try{
924 t = Type.getType(localsig);
925 }
926 catch (ClassFormatError cfe){ // sometimes BCEL is a little harsh describing exceptional situations.
927 throw new ClassConstraintException("Illegal descriptor (==signature) '"+localsig+"' used by LocalVariable '"+tostring(localvariables[i])+"' referenced by '"+tostring(lvt)+"'.");
928 }
929 int localindex = localvariables[i].getIndex();
930 if ( ( (t==Type.LONG || t==Type.DOUBLE)? localindex+1:localindex) >= code.getMaxLocals()){
931 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()+"'.");
932 }
933
934 try{
935 localVariablesInfos[method_number].add(localindex, localname, localvariables[i].getStartPC(), localvariables[i].getLength(), t);
936 }
937 catch(LocalVariableInfoInconsistentException lviie){
938 throw new ClassConstraintException("Conflicting information in LocalVariableTable '"+tostring(lvt)+"' found in Code attribute '"+tostring(obj)+"' (method '"+tostring(m)+"'). "+lviie.getMessage());
939 }
940 }// for all local variables localvariables[i] in the LocalVariableTable attribute atts[a] END
941
942 num_of_lvt_attribs++;
943 if (num_of_lvt_attribs > obj.getMaxLocals()){
944 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.').");
945 }
946 }// if atts[a] instanceof LocalVariableTable END
947 }// for all attributes atts[a] END
948 }// visitCode(Code) END
949
950 public void visitExceptionTable(ExceptionTable obj){//vmspec2 4.7.4
951 // incorrectly named, it's the Exceptions attribute (vmspec2 4.7.4)
952 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
953
954 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
955 if (! name.equals("Exceptions")){
956 throw new ClassConstraintException("The Exceptions attribute '"+tostring(obj)+"' is not correctly named 'Exceptions' but '"+name+"'.");
957 }
958
959 int[] exc_indices = obj.getExceptionIndexTable();
960
961 for (int i=0; i<exc_indices.length; i++){
962 checkIndex(obj, exc_indices[i], CONST_Class);
963
964 ConstantClass cc = (ConstantClass) (cp.getConstant(exc_indices[i]));
965 checkIndex(cc, cc.getNameIndex(), CONST_Utf8); // cannot be sure this ConstantClass has already been visited (checked)!
966 String cname = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes().replace('/','.'); //convert internal notation on-the-fly to external notation
967
968 Verifier v = VerifierFactory.getVerifier(cname);
969 VerificationResult vr = v.doPass1();
970
971 if (vr != VerificationResult.VR_OK){
972 throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+"' as an Exception but it does not pass verification pass 1: "+vr);
973 }
974 else{
975 // We cannot safely trust any other "instanceof" mechanism. We need to transitively verify
976 // the ancestor hierarchy.
977 JavaClass e = Repository.lookupClass(cname);
978 JavaClass t = Repository.lookupClass(Type.THROWABLE.getClassName());
979 JavaClass o = Repository.lookupClass(Type.OBJECT.getClassName());
980 while (e != o){
981 if (e == t) break; // It's a subclass of Throwable, OKAY, leave.
982
983 v = VerifierFactory.getVerifier(e.getSuperclassName());
984 vr = v.doPass1();
985 if (vr != VerificationResult.VR_OK){
986 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);
987 }
988 else{
989 e = Repository.lookupClass(e.getSuperclassName());
990 }
991 }
992 if (e != t) throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+"' as an Exception but it is not a subclass of '"+t.getClassName()+"'.");
993 }
994 }
995 }
996 // SYNTHETIC: see above
997 // DEPRECATED: see above
998 //////////////////////////////////////////////////////////////
999 // code_attribute-structure-ATTRIBUTES (vmspec2 4.7.3, 4.7) //
1000 //////////////////////////////////////////////////////////////
1001 public void visitLineNumberTable(LineNumberTable obj){//vmspec2 4.7.8
1002 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
1003
1004 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
1005 if (! name.equals("LineNumberTable")){
1006 throw new ClassConstraintException("The LineNumberTable attribute '"+tostring(obj)+"' is not correctly named 'LineNumberTable' but '"+name+"'.");
1007 }
1008
1009 //In JustIce,this check is delayed to Pass 3a.
1010 //LineNumber[] linenumbers = obj.getLineNumberTable();
1011 // ...validity check...
1012
1013 }
1014 public void visitLocalVariableTable(LocalVariableTable obj){//vmspec2 4.7.9
1015 //In JustIce,this check is partially delayed to Pass 3a.
1016 //The other part can be found in the visitCode(Code) method.
1017 }
1018 ////////////////////////////////////////////////////
1019 // MISC-structure-ATTRIBUTES (vmspec2 4.7.1, 4.7) //
1020 ////////////////////////////////////////////////////
1021 public void visitUnknown(Unknown obj){//vmspec2 4.7.1
1022 // Represents an unknown attribute.
1023 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
1024
1025 // Maybe only misnamed? Give a (warning) message.
1026 addMessage("Unknown attribute '"+tostring(obj)+"'. This attribute is not known in any context!");
1027 }
1028 //////////
1029 // BCEL //
1030 //////////
1031 public void visitLocalVariable(LocalVariable obj){
1032 // This does not represent an Attribute but is only
1033 // related to internal BCEL data representation.
1034
1035 // see visitLocalVariableTable(LocalVariableTable)
1036 }
1037 public void visitCodeException(CodeException obj){
1038 // Code constraints are checked in Pass3 (3a and 3b).
1039 // This does not represent an Attribute but is only
1040 // related to internal BCEL data representation.
1041
1042 // see visitCode(Code)
1043 }
1044 public void visitConstantPool(ConstantPool obj){
1045 // No need to. We're piggybacked by the DescendingVisitor.
1046 // This does not represent an Attribute but is only
1047 // related to internal BCEL data representation.
1048 }
1049 public void visitInnerClass(InnerClass obj){
1050 // This does not represent an Attribute but is only
1051 // related to internal BCEL data representation.
1052 }
1053 public void visitLineNumber(LineNumber obj){
1054 // This does not represent an Attribute but is only
1055 // related to internal BCEL data representation.
1056
1057 // see visitLineNumberTable(LineNumberTable)
1058 }
1059 }
1060
1061 /***
1062 * Ensures that the ConstantCP-subclassed entries of the constant
1063 * pool are valid. According to "Yellin: Low Level Security in Java",
1064 * this method does not verify the existence of referenced entities
1065 * (such as classes) but only the formal correctness (such as well-formed
1066 * signatures).
1067 * The visitXXX() methods throw ClassConstraintException instances otherwise.
1068 * <B>Precondition: index-style cross referencing in the constant
1069 * pool must be valid. Simply invoke constant_pool_entries_satisfy_static_constraints()
1070 * before.</B>
1071 *
1072 * @throws ClassConstraintException otherwise.
1073 * @see #constant_pool_entries_satisfy_static_constraints()
1074 */
1075 private void field_and_method_refs_are_valid(){
1076 JavaClass jc = Repository.lookupClass(myOwner.getClassName());
1077 DescendingVisitor v = new DescendingVisitor(jc, new FAMRAV_Visitor(jc));
1078 v.visit();
1079 }
1080
1081 /***
1082 * A Visitor class that ensures the ConstantCP-subclassed entries
1083 * of the constant pool are valid.
1084 * <B>Precondition: index-style cross referencing in the constant
1085 * pool must be valid.</B>
1086 *
1087 * @see #constant_pool_entries_satisfy_static_constraints()
1088 * @see org.apache.bcel.classfile.ConstantCP
1089 */
1090 private class FAMRAV_Visitor extends EmptyVisitor implements Visitor{
1091 private final ConstantPool cp; // ==jc.getConstantPool() -- only here to save typing work.
1092 private FAMRAV_Visitor(JavaClass _jc){
1093 cp = _jc.getConstantPool();
1094 }
1095
1096 public void visitConstantFieldref(ConstantFieldref obj){
1097 if (obj.getTag() != Constants.CONSTANT_Fieldref){
1098 throw new ClassConstraintException("ConstantFieldref '"+tostring(obj)+"' has wrong tag!");
1099 }
1100 int name_and_type_index = obj.getNameAndTypeIndex();
1101 ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
1102 String name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes(); // Field or Method name
1103 if (!validFieldName(name)){
1104 throw new ClassConstraintException("Invalid field name '"+name+"' referenced by '"+tostring(obj)+"'.");
1105 }
1106
1107 int class_index = obj.getClassIndex();
1108 ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
1109 String className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes(); // Class Name in internal form
1110 if (! validClassName(className)){
1111 throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
1112 }
1113
1114 String sig = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
1115
1116 try{
1117 Type.getType(sig); /* Don't need the return value */
1118 }
1119 catch (ClassFormatError cfe){
1120 // Well, BCEL sometimes is a little harsh describing exceptional situations.
1121 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
1122 }
1123 }
1124
1125 public void visitConstantMethodref(ConstantMethodref obj){
1126 if (obj.getTag() != Constants.CONSTANT_Methodref){
1127 throw new ClassConstraintException("ConstantMethodref '"+tostring(obj)+"' has wrong tag!");
1128 }
1129 int name_and_type_index = obj.getNameAndTypeIndex();
1130 ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
1131 String name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes(); // Field or Method name
1132 if (!validClassMethodName(name)){
1133 throw new ClassConstraintException("Invalid (non-interface) method name '"+name+"' referenced by '"+tostring(obj)+"'.");
1134 }
1135
1136 int class_index = obj.getClassIndex();
1137 ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
1138 String className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes(); // Class Name in internal form
1139 if (! validClassName(className)){
1140 throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
1141 }
1142
1143 String sig = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
1144
1145 try{
1146 Type t = Type.getReturnType(sig);
1147 if ( name.equals(CONSTRUCTOR_NAME) && (t != Type.VOID) ){
1148 throw new ClassConstraintException("Instance initialization method must have VOID return type.");
1149 }
1150 }
1151 catch (ClassFormatError cfe){
1152 // Well, BCEL sometimes is a little harsh describing exceptional situations.
1153 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
1154 }
1155 }
1156
1157 public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj){
1158 if (obj.getTag() != Constants.CONSTANT_InterfaceMethodref){
1159 throw new ClassConstraintException("ConstantInterfaceMethodref '"+tostring(obj)+"' has wrong tag!");
1160 }
1161 int name_and_type_index = obj.getNameAndTypeIndex();
1162 ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
1163 String name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes(); // Field or Method name
1164 if (!validInterfaceMethodName(name)){
1165 throw new ClassConstraintException("Invalid (interface) method name '"+name+"' referenced by '"+tostring(obj)+"'.");
1166 }
1167
1168 int class_index = obj.getClassIndex();
1169 ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
1170 String className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes(); // Class Name in internal form
1171 if (! validClassName(className)){
1172 throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
1173 }
1174
1175 String sig = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
1176
1177 try{
1178 Type t = Type.getReturnType(sig);
1179 if ( name.equals(STATIC_INITIALIZER_NAME) && (t != Type.VOID) ){
1180 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.");
1181 }
1182 }
1183 catch (ClassFormatError cfe){
1184 // Well, BCEL sometimes is a little harsh describing exceptional situations.
1185 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
1186 }
1187
1188 }
1189
1190 }
1191
1192 /***
1193 * This method returns true if and only if the supplied String
1194 * represents a valid Java class name.
1195 */
1196 private static final boolean validClassName(String name){
1197 /*
1198 * TODO: implement.
1199 * Are there any restrictions?
1200 */
1201 return true;
1202 }
1203 /***
1204 * This method returns true if and only if the supplied String
1205 * represents a valid method name.
1206 * This is basically the same as a valid identifier name in the
1207 * Java programming language, but the special name for
1208 * the instance initialization method is allowed and the special name
1209 * for the class/interface initialization method may be allowed.
1210 */
1211 private static boolean validMethodName(String name, boolean allowStaticInit){
1212 if (validJavaLangMethodName(name)) return true;
1213
1214 if (allowStaticInit){
1215 return (name.equals(CONSTRUCTOR_NAME) || name.equals(STATIC_INITIALIZER_NAME));
1216 }
1217 else{
1218 return name.equals(CONSTRUCTOR_NAME);
1219 }
1220 }
1221
1222 /***
1223 * This method returns true if and only if the supplied String
1224 * represents a valid method name that may be referenced by
1225 * ConstantMethodref objects.
1226 */
1227 private static boolean validClassMethodName(String name){
1228 return validMethodName(name, false);
1229 }
1230
1231 /***
1232 * This method returns true if and only if the supplied String
1233 * represents a valid Java programming language method name stored as a simple
1234 * (non-qualified) name.
1235 * Conforming to: The Java Virtual Machine Specification, Second Edition, §2.7, §2.7.1, §2.2.
1236 */
1237 private static boolean validJavaLangMethodName(String name){
1238 if (!Character.isJavaIdentifierStart(name.charAt(0))) return false;
1239
1240 for (int i=1; i<name.length(); i++){
1241 if (!Character.isJavaIdentifierPart(name.charAt(i))) return false;
1242 }
1243 return true;
1244 }
1245
1246 /***
1247 * This method returns true if and only if the supplied String
1248 * represents a valid Java interface method name that may be
1249 * referenced by ConstantInterfaceMethodref objects.
1250 */
1251 private static boolean validInterfaceMethodName(String name){
1252 // I guess we should assume special names forbidden here.
1253 if (name.startsWith("<")) return false;
1254 return validJavaLangMethodName(name);
1255 }
1256
1257 /***
1258 * This method returns true if and only if the supplied String
1259 * represents a valid Java identifier (so-called simple name).
1260 */
1261 private static boolean validJavaIdentifier(String name){
1262 // vmspec2 2.7, vmspec2 2.2
1263 if (!Character.isJavaIdentifierStart(name.charAt(0))) return false;
1264
1265 for (int i=1; i<name.length(); i++){
1266 if (!Character.isJavaIdentifierPart(name.charAt(i))) return false;
1267 }
1268 return true;
1269 }
1270
1271 /***
1272 * This method returns true if and only if the supplied String
1273 * represents a valid Java field name.
1274 */
1275 private static boolean validFieldName(String name){
1276 // vmspec2 2.7, vmspec2 2.2
1277 return validJavaIdentifier(name);
1278 }
1279
1280 /***
1281 * This class serves for finding out if a given JavaClass' ConstantPool
1282 * references an Inner Class.
1283 * The Java Virtual Machine Specification, Second Edition is not very precise
1284 * about when an "InnerClasses" attribute has to appear. However, it states that
1285 * there has to be exactly one InnerClasses attribute in the ClassFile structure
1286 * if the constant pool of a class or interface refers to any class or interface
1287 * "that is not a member of a package". Sun does not mean "member of the default
1288 * package". In "Inner Classes Specification" they point out how a "bytecode name"
1289 * is derived so one has to deduce what a class name of a class "that is not a
1290 * member of a package" looks like: there is at least one character in the byte-
1291 * code name that cannot be part of a legal Java Language Class name (and not equal
1292 * to '/'). This assumption is wrong as the delimiter is '$' for which
1293 * Character.isJavaIdentifierPart() == true.
1294 * Hence, you really run into trouble if you have a toplevel class called
1295 * "A$XXX" and another toplevel class called "A" with in inner class called "XXX".
1296 * JustIce cannot repair this; please note that existing verifiers at this
1297 * time even fail to detect missing InnerClasses attributes in pass 2.
1298 */
1299 private class InnerClassDetector extends EmptyVisitor{
1300 private boolean hasInnerClass = false;
1301 private JavaClass jc;
1302 private ConstantPool cp;
1303 private InnerClassDetector(){} // Don't use.
1304 /*** Constructs an InnerClassDetector working on the JavaClass _jc. */
1305 public InnerClassDetector(JavaClass _jc){
1306 jc = _jc;
1307 cp = jc.getConstantPool();
1308 (new DescendingVisitor(jc, this)).visit();
1309 }
1310 /***
1311 * Returns if the JavaClass this InnerClassDetector is working on
1312 * has an Inner Class reference in its constant pool.
1313 */
1314 public boolean innerClassReferenced(){
1315 return hasInnerClass;
1316 }
1317 /*** This method casually visits ConstantClass references. */
1318 public void visitConstantClass(ConstantClass obj){
1319 Constant c = cp.getConstant(obj.getNameIndex());
1320 if (c instanceof ConstantUtf8){ //Ignore the case where it's not a ConstantUtf8 here, we'll find out later.
1321 String classname = ((ConstantUtf8) c).getBytes();
1322 if (classname.startsWith(jc.getClassName().replace('.','/')+"$")){
1323 hasInnerClass = true;
1324 }
1325 }
1326 }
1327 }
1328
1329 /***
1330 * This method is here to save typing work and improve code readability.
1331 */
1332 private static String tostring(Node n){
1333 return new StringRepresentation(n).toString();
1334 }
1335 }
This page was automatically generated by Maven