| 1 package com.sun.org.apache.bcel.internal.verifier.statics; 2 3 56 57 import com.sun.org.apache.bcel.internal.Constants; 58 import com.sun.org.apache.bcel.internal.Repository; 59 import com.sun.org.apache.bcel.internal.classfile.*; 60 import com.sun.org.apache.bcel.internal.classfile.Deprecated; import com.sun.org.apache.bcel.internal.classfile.DescendingVisitor; import com.sun.org.apache.bcel.internal.classfile.EmptyVisitor; import com.sun.org.apache.bcel.internal.classfile.Visitor; import com.sun.org.apache.bcel.internal.generic.*; 65 import com.sun.org.apache.bcel.internal.verifier.*; 66 import com.sun.org.apache.bcel.internal.verifier.exc.*; 67 import java.util.HashMap ; 68 import java.util.HashSet ; 69 70 81 public final class Pass2Verifier extends PassVerifier implements Constants{ 82 83 89 private LocalVariablesInfo[] localVariablesInfos; 90 91 92 private Verifier myOwner; 93 94 99 public Pass2Verifier(Verifier owner){ 100 myOwner = owner; 101 } 102 103 112 public LocalVariablesInfo getLocalVariablesInfo(int method_nr){ 113 if (this.verify() != VerificationResult.VR_OK) return null; if (method_nr < 0 || method_nr >= localVariablesInfos.length){ 115 throw new AssertionViolatedException("Method number out of range."); 116 } 117 return localVariablesInfos[method_nr]; 118 } 119 120 142 public VerificationResult do_verify(){ 143 VerificationResult vr1 = myOwner.doPass1(); 144 if (vr1.equals(VerificationResult.VR_OK)){ 145 146 localVariablesInfos = new LocalVariablesInfo[Repository.lookupClass(myOwner.getClassName()).getMethods().length]; 149 150 VerificationResult vr = VerificationResult.VR_OK; try{ 152 constant_pool_entries_satisfy_static_constraints(); 153 field_and_method_refs_are_valid(); 154 every_class_has_an_accessible_superclass(); 155 final_methods_are_not_overridden(); 156 } 157 catch (ClassConstraintException cce){ 158 vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage()); 159 } 160 return vr; 161 } 162 else 163 return VerificationResult.VR_NOTYET; 164 } 165 166 179 private void every_class_has_an_accessible_superclass(){ 180 HashSet hs = new HashSet (); JavaClass jc = Repository.lookupClass(myOwner.getClassName()); 182 int supidx = -1; 183 184 while (supidx != 0){ 185 supidx = jc.getSuperclassNameIndex(); 186 187 if (supidx == 0){ 188 if (jc != Repository.lookupClass(Type.OBJECT.getClassName())){ 189 throw new ClassConstraintException("Superclass of '"+jc.getClassName()+"' missing but not "+Type.OBJECT.getClassName()+" itself!"); 190 } 191 } 192 else{ 193 String supername = jc.getSuperclassName(); 194 if (! hs.add(supername)){ throw new ClassConstraintException("Circular superclass hierarchy detected."); 196 } 197 Verifier v = VerifierFactory.getVerifier(supername); 198 VerificationResult vr = v.doPass1(); 199 200 if (vr != VerificationResult.VR_OK){ 201 throw new ClassConstraintException("Could not load in ancestor class '"+supername+"'."); 202 } 203 jc = Repository.lookupClass(supername); 204 205 if (jc.isFinal()){ 206 throw new ClassConstraintException("Ancestor class '"+supername+"' has the FINAL access modifier and must therefore not be subclassed."); 207 } 208 } 209 } 210 } 211 212 223 private void final_methods_are_not_overridden(){ 224 HashMap hashmap = new HashMap (); 225 JavaClass jc = Repository.lookupClass(myOwner.getClassName()); 226 227 int supidx = -1; 228 while (supidx != 0){ 229 supidx = jc.getSuperclassNameIndex(); 230 231 ConstantPoolGen cpg = new ConstantPoolGen(jc.getConstantPool()); 232 Method[] methods = jc.getMethods(); 233 for (int i=0; i<methods.length; i++){ 234 String name_and_sig = (methods[i].getName()+methods[i].getSignature()); 235 236 if (hashmap.containsKey(name_and_sig)){ 237 if (methods[i].isFinal()){ 238 throw new ClassConstraintException("Method '"+name_and_sig+"' in class '"+hashmap.get(name_and_sig)+"' overrides the final (not-overridable) definition in class '"+jc.getClassName()+"'."); 239 } 240 else{ 241 if (!methods[i].isStatic()){ hashmap.put(name_and_sig, jc.getClassName()); 243 } 244 } 245 } 246 else{ 247 if (!methods[i].isStatic()){ hashmap.put(name_and_sig, jc.getClassName()); 249 } 250 } 251 } 252 253 jc = Repository.lookupClass(jc.getSuperclassName()); } 255 256 } 257 258 264 private void constant_pool_entries_satisfy_static_constraints(){ 265 JavaClass jc = Repository.lookupClass(myOwner.getClassName()); 269 new CPESSC_Visitor(jc); } 271 272 279 private class CPESSC_Visitor extends com.sun.org.apache.bcel.internal.classfile.EmptyVisitor implements Visitor{ 280 private Class CONST_Class; 281 private Class CONST_Fieldref; 282 private Class CONST_Methodref; 283 private Class CONST_InterfaceMethodref; 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; private final int cplen; 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 = com.sun.org.apache.bcel.internal.classfile.ConstantClass.class; 307 CONST_Fieldref = com.sun.org.apache.bcel.internal.classfile.ConstantFieldref.class; 308 CONST_Methodref = com.sun.org.apache.bcel.internal.classfile.ConstantMethodref.class; 309 CONST_InterfaceMethodref = com.sun.org.apache.bcel.internal.classfile.ConstantInterfaceMethodref.class; 310 CONST_String = com.sun.org.apache.bcel.internal.classfile.ConstantString.class; 311 CONST_Integer = com.sun.org.apache.bcel.internal.classfile.ConstantInteger.class; 312 CONST_Float = com.sun.org.apache.bcel.internal.classfile.ConstantFloat.class; 313 CONST_Long = com.sun.org.apache.bcel.internal.classfile.ConstantLong.class; 314 CONST_Double = com.sun.org.apache.bcel.internal.classfile.ConstantDouble.class; 315 CONST_NameAndType = com.sun.org.apache.bcel.internal.classfile.ConstantNameAndType.class; 316 CONST_Utf8 = com.sun.org.apache.bcel.internal.classfile.ConstantUtf8.class; 317 318 carrier = new DescendingVisitor(_jc, this); 319 carrier.visit(); 320 } 321 322 private void checkIndex(Node referrer, int index, Class shouldbe){ 323 if ((index < 0) || (index >= cplen)){ 324 throw new ClassConstraintException("Invalid index '"+index+"' used by '"+tostring(referrer)+"'."); 325 } 326 Constant c = cp.getConstant(index); 327 if (! shouldbe.isInstance(c)){ 328 String isnot = shouldbe.toString().substring(shouldbe.toString().lastIndexOf(".")+1); throw new ClassCastException ("Illegal constant '"+tostring(c)+"' at index '"+index+"'. '"+tostring(referrer)+"' expects a '"+shouldbe+"'."); 330 } 331 } 332 public void visitJavaClass(JavaClass obj){ 336 Attribute[] atts = obj.getAttributes(); 337 boolean foundSourceFile = false; 338 boolean foundInnerClasses = false; 339 340 boolean hasInnerClass = new InnerClassDetector(jc).innerClassReferenced(); 343 344 for (int i=0; i<atts.length; i++){ 345 if ((! (atts[i] instanceof SourceFile)) && 346 (! (atts[i] instanceof Deprecated )) && 347 (! (atts[i] instanceof InnerClasses)) && 348 (! (atts[i] instanceof Synthetic))){ 349 addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of the ClassFile structure '"+tostring(obj)+"' is unknown and will therefore be ignored."); 350 } 351 352 if (atts[i] instanceof SourceFile){ 353 if (foundSourceFile == false) foundSourceFile = true; 354 else throw new ClassConstraintException("A ClassFile structure (like '"+tostring(obj)+"') may have no more than one SourceFile attribute."); } 356 357 if (atts[i] instanceof InnerClasses){ 358 if (foundInnerClasses == false) foundInnerClasses = true; 359 else{ 360 if (hasInnerClass){ 361 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."); 362 } 363 } 364 if (!hasInnerClass){ 365 addMessage("No referenced Inner Class found, but InnerClasses attribute '"+tostring(atts[i])+"' found. Strongly suggest removal of that attribute."); 366 } 367 } 368 369 } 370 if (hasInnerClass && !foundInnerClasses){ 371 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."); 376 } 377 } 378 public void visitConstantClass(ConstantClass obj){ 382 if (obj.getTag() != Constants.CONSTANT_Class){ 383 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'."); 384 } 385 checkIndex(obj, obj.getNameIndex(), CONST_Utf8); 386 387 } 388 public void visitConstantFieldref(ConstantFieldref obj){ 389 if (obj.getTag() != Constants.CONSTANT_Fieldref){ 390 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'."); 391 } 392 checkIndex(obj, obj.getClassIndex(), CONST_Class); 393 checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType); 394 } 395 public void visitConstantMethodref(ConstantMethodref obj){ 396 if (obj.getTag() != Constants.CONSTANT_Methodref){ 397 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'."); 398 } 399 checkIndex(obj, obj.getClassIndex(), CONST_Class); 400 checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType); 401 } 402 public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj){ 403 if (obj.getTag() != Constants.CONSTANT_InterfaceMethodref){ 404 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'."); 405 } 406 checkIndex(obj, obj.getClassIndex(), CONST_Class); 407 checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType); 408 } 409 public void visitConstantString(ConstantString obj){ 410 if (obj.getTag() != Constants.CONSTANT_String){ 411 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'."); 412 } 413 checkIndex(obj, obj.getStringIndex(), CONST_Utf8); 414 } 415 public void visitConstantInteger(ConstantInteger obj){ 416 if (obj.getTag() != Constants.CONSTANT_Integer){ 417 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'."); 418 } 419 } 421 public void visitConstantFloat(ConstantFloat obj){ 422 if (obj.getTag() != Constants.CONSTANT_Float){ 423 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'."); 424 } 425 } 427 public void visitConstantLong(ConstantLong obj){ 428 if (obj.getTag() != Constants.CONSTANT_Long){ 429 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'."); 430 } 431 } 433 public void visitConstantDouble(ConstantDouble obj){ 434 if (obj.getTag() != Constants.CONSTANT_Double){ 435 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'."); 436 } 437 } 439 public void visitConstantNameAndType(ConstantNameAndType obj){ 440 if (obj.getTag() != Constants.CONSTANT_NameAndType){ 441 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'."); 442 } 443 checkIndex(obj, obj.getNameIndex(), CONST_Utf8); 444 checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8); 446 } 447 public void visitConstantUtf8(ConstantUtf8 obj){ 448 if (obj.getTag() != Constants.CONSTANT_Utf8){ 449 throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'."); 450 } 451 } 453 public void visitField(Field obj){ 457 458 if (jc.isClass()){ 459 int maxone=0; 460 if (obj.isPrivate()) maxone++; 461 if (obj.isProtected()) maxone++; 462 if (obj.isPublic()) maxone++; 463 if (maxone > 1){ 464 throw new ClassConstraintException("Field '"+tostring(obj)+"' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set."); 465 } 466 467 if (obj.isFinal() && obj.isVolatile()){ 468 throw new ClassConstraintException("Field '"+tostring(obj)+"' must only have at most one of its ACC_FINAL, ACC_VOLATILE modifiers set."); 469 } 470 } 471 else{ if (!obj.isPublic()){ 473 throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_PUBLIC modifier set but hasn't!"); 474 } 475 if (!obj.isStatic()){ 476 throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_STATIC modifier set but hasn't!"); 477 } 478 if (!obj.isFinal()){ 479 throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_FINAL modifier set but hasn't!"); 480 } 481 } 482 483 if ((obj.getAccessFlags() & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_VOLATILE|ACC_TRANSIENT)) > 0){ 484 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)."); 485 } 486 487 checkIndex(obj, obj.getNameIndex(), CONST_Utf8); 488 489 String name = obj.getName(); 490 if (! validFieldName(name)){ 491 throw new ClassConstraintException("Field '"+tostring(obj)+"' has illegal name '"+obj.getName()+"'."); 492 } 493 494 checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8); 496 497 String sig = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes(); 499 try{ 500 Type t = Type.getType(sig); 501 } 502 catch (ClassFormatError cfe){ throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'."); 504 } 505 506 String nameanddesc = (name+sig); 507 if (field_names_and_desc.contains(nameanddesc)){ 508 throw new ClassConstraintException("No two fields (like '"+tostring(obj)+"') are allowed have same names and descriptors!"); 509 } 510 if (field_names.contains(name)){ 511 addMessage("More than one field of name '"+name+"' detected (but with different type descriptors). This is very unusual."); 512 } 513 field_names_and_desc.add(nameanddesc); 514 field_names.add(name); 515 516 Attribute[] atts = obj.getAttributes(); 517 for (int i=0; i<atts.length; i++){ 518 if ((! (atts[i] instanceof ConstantValue)) && 519 (! (atts[i] instanceof Synthetic)) && 520 (! (atts[i] instanceof Deprecated ))){ 521 addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Field '"+tostring(obj)+"' is unknown and will therefore be ignored."); 522 } 523 if (! (atts[i] instanceof ConstantValue)){ 524 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."); 525 } 526 } 527 } 528 public void visitMethod(Method obj){ 532 533 checkIndex(obj, obj.getNameIndex(), CONST_Utf8); 534 535 String name = obj.getName(); 536 if (! validMethodName(name, true)){ 537 throw new ClassConstraintException("Method '"+tostring(obj)+"' has illegal name '"+name+"'."); 538 } 539 540 checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8); 542 543 String sig = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes(); 545 Type t; 546 Type[] ts; try{ 548 t = Type.getReturnType(sig); 549 ts = Type.getArgumentTypes(sig); 550 } 551 catch (ClassFormatError cfe){ 552 throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by Method '"+tostring(obj)+"'."); 554 } 555 556 Type act = t; 558 if (act instanceof ArrayType) act = ((ArrayType) act).getBasicType(); 559 if (act instanceof ObjectType){ 560 Verifier v = VerifierFactory.getVerifier( ((ObjectType) act).getClassName() ); 561 VerificationResult vr = v.doPass1(); 562 if (vr != VerificationResult.VR_OK) { 563 throw new ClassConstraintException("Method '"+tostring(obj)+"' has a return type that does not pass verification pass 1: '"+vr+"'."); 564 } 565 } 566 567 for (int i=0; i<ts.length; i++){ 568 act = ts[i]; 569 if (act instanceof ArrayType) act = ((ArrayType) act).getBasicType(); 570 if (act instanceof ObjectType){ 571 Verifier v = VerifierFactory.getVerifier( ((ObjectType) act).getClassName() ); 572 VerificationResult vr = v.doPass1(); 573 if (vr != VerificationResult.VR_OK) { 574 throw new ClassConstraintException("Method '"+tostring(obj)+"' has an argument type that does not pass verification pass 1: '"+vr+"'."); 575 } 576 } 577 } 578 579 if (name.equals(STATIC_INITIALIZER_NAME) && (ts.length != 0)){ 581 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)."); 582 } 583 584 if (jc.isClass()){ 585 int maxone=0; 586 if (obj.isPrivate()) maxone++; 587 if (obj.isProtected()) maxone++; 588 if (obj.isPublic()) maxone++; 589 if (maxone > 1){ 590 throw new ClassConstraintException("Method '"+tostring(obj)+"' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set."); 591 } 592 593 if (obj.isAbstract()){ 594 if (obj.isFinal()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_FINAL modifier set."); 595 if (obj.isNative()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_NATIVE modifier set."); 596 if (obj.isPrivate()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_PRIVATE modifier set."); 597 if (obj.isStatic()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_STATIC modifier set."); 598 if (obj.isStrictfp()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_STRICT modifier set."); 599 if (obj.isSynchronized()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_SYNCHRONIZED modifier set."); 600 } 601 } 602 else{ if (!name.equals(STATIC_INITIALIZER_NAME)){ if (!obj.isPublic()){ 605 throw new ClassConstraintException("Interface method '"+tostring(obj)+"' must have the ACC_PUBLIC modifier set but hasn't!"); 606 } 607 if (!obj.isAbstract()){ 608 throw new ClassConstraintException("Interface method '"+tostring(obj)+"' must have the ACC_STATIC modifier set but hasn't!"); 609 } 610 if ( obj.isPrivate() || 611 obj.isProtected() || 612 obj.isStatic() || 613 obj.isFinal() || 614 obj.isSynchronized() || 615 obj.isNative() || 616 obj.isStrictfp() ){ 617 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."); 618 } 619 } 620 } 621 622 if (name.equals(CONSTRUCTOR_NAME)){ 624 if ( obj.isStatic() || 627 obj.isFinal() || 628 obj.isSynchronized() || 629 obj.isNative() || 630 obj.isAbstract() ){ 631 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."); 632 } 633 } 634 635 if (name.equals(STATIC_INITIALIZER_NAME)){ 637 if ((obj.getAccessFlags() & (~ACC_STRICT)) > 0){ 638 addMessage("Class or interface initialization method '"+tostring(obj)+"' has superfluous access modifier(s) set: everything but ACC_STRICT is ignored."); 639 } 640 if (obj.isAbstract()){ 641 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."); 642 } 643 } 644 645 if ((obj.getAccessFlags() & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) > 0){ 646 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)."); 647 } 648 649 String nameanddesc = (name+sig); 650 if (method_names_and_desc.contains(nameanddesc)){ 651 throw new ClassConstraintException("No two methods (like '"+tostring(obj)+"') are allowed have same names and desciptors!"); 652 } 653 method_names_and_desc.add(nameanddesc); 654 655 Attribute[] atts = obj.getAttributes(); 656 int num_code_atts = 0; 657 for (int i=0; i<atts.length; i++){ 658 if ((! (atts[i] instanceof Code)) && 659 (! (atts[i] instanceof ExceptionTable)) && 660 (! (atts[i] instanceof Synthetic)) && 661 (! (atts[i] instanceof Deprecated ))){ 662 addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Method '"+tostring(obj)+"' is unknown and will therefore be ignored."); 663 } 664 if ((! (atts[i] instanceof Code)) && 665 (! (atts[i] instanceof ExceptionTable))){ 666 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."); 667 } 668 if ((atts[i] instanceof Code) && (obj.isNative() || obj.isAbstract())){ 669 throw new ClassConstraintException("Native or abstract methods like '"+tostring(obj)+"' must not have a Code attribute like '"+tostring(atts[i])+"'."); } 671 if (atts[i] instanceof Code) num_code_atts++; 672 } 673 if ( !obj.isNative() && !obj.isAbstract() && num_code_atts != 1){ 674 throw new ClassConstraintException("Non-native, non-abstract methods like '"+tostring(obj)+"' must have exactly one Code attribute (found: "+num_code_atts+")."); 675 } 676 } 677 public void visitSourceFile(SourceFile obj){ 682 684 checkIndex(obj, obj.getNameIndex(), CONST_Utf8); 685 686 String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes(); 687 if (! name.equals("SourceFile")){ 688 &nb
|