1 /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. 2 * 3 * This program and the accompanying materials are made available under 4 * the terms of the Common Public License v1.0 which accompanies this distribution, 5 * and is available at http://www.eclipse.org/legal/cpl-v10.html 6 * 7 * $Id: ClassDef.java,v 1.1.1.1.2.1 2004/07/16 23:32:30 vlad_r Exp $ 8 */ 9 package com.vladium.jcd.cls; 10 11 import java.io.DataOutputStream; 12 import java.io.IOException; 13 import java.security.MessageDigest; 14 import java.security.NoSuchAlgorithmException; 15 import java.util.Arrays; 16 17 import com.vladium.jcd.cls.attribute.AttributeElementFactory; 18 import com.vladium.jcd.cls.attribute.Attribute_info; 19 import com.vladium.jcd.cls.attribute.CodeAttribute_info; 20 import com.vladium.jcd.cls.attribute.InnerClassesAttribute_info; 21 import com.vladium.jcd.cls.constant.CONSTANT_Class_info; 22 import com.vladium.jcd.cls.constant.CONSTANT_Fieldref_info; 23 import com.vladium.jcd.cls.constant.CONSTANT_NameAndType_info; 24 import com.vladium.jcd.cls.constant.CONSTANT_String_info; 25 import com.vladium.jcd.cls.constant.CONSTANT_Utf8_info; 26 import com.vladium.jcd.compiler.IClassFormatOutput; 27 import com.vladium.jcd.lib.Types; 28 import com.vladium.jcd.lib.UDataOutputStream; 29 import com.vladium.util.ByteArrayOStream; 30 31 // ---------------------------------------------------------------------------- 32 /** 33 * This class represents the abstract syntax table (AST) that {@link com.vladium.jcd.parser.ClassDefParser} 34 * produces from bytecode. Most elements are either settable or extendible. 35 * This class also implements {@link com.vladium.jcd.compiler.IClassFormatOutput} 36 * and works with {@link com.vladium.jcd.compiler.ClassWriter} to produce 37 * bytecode without an external compiler.<P> 38 * 39 * MT-safety: this class and all interfaces used by it are not safe for 40 * access from multiple concurrent threads. 41 * 42 * @author (C) 2001, Vlad Roubtsov 43 */ 44 public 45 final class ClassDef implements Cloneable, IAccessFlags, IClassFormatOutput 46 { 47 // public: ................................................................ 48 49 ClassDef()50 public ClassDef () 51 { 52 m_version = new int [2]; 53 54 m_constants = ElementFactory.newConstantCollection (-1); 55 m_interfaces = ElementFactory.newInterfaceCollection (-1); 56 m_fields = ElementFactory.newFieldCollection (-1); 57 m_methods = ElementFactory.newMethodCollection (-1); 58 m_attributes = ElementFactory.newAttributeCollection (-1); 59 } 60 61 // Visitor: 62 accept(final IClassDefVisitor visitor, final Object ctx)63 public void accept (final IClassDefVisitor visitor, final Object ctx) 64 { 65 visitor.visit (this, ctx); 66 } 67 68 getMagic()69 public long getMagic () 70 { 71 return m_magic; 72 } 73 setMagic(final long magic)74 public void setMagic (final long magic) 75 { 76 m_magic = magic; 77 } 78 79 getVersion()80 public int [] getVersion () 81 { 82 return m_version; 83 } 84 setVersion(final int [] version)85 public void setVersion (final int [] version) 86 { 87 m_version [0] = version [0]; 88 m_version [1] = version [1]; 89 } 90 setDeclaredSUID(final long suid)91 public final void setDeclaredSUID (final long suid) 92 { 93 m_declaredSUID = suid; 94 } 95 96 getThisClassIndex()97 public int getThisClassIndex () 98 { 99 return m_this_class_index; 100 } 101 setThisClassIndex(final int this_class_index)102 public void setThisClassIndex (final int this_class_index) 103 { 104 m_this_class_index = this_class_index; 105 } 106 getThisClass()107 public CONSTANT_Class_info getThisClass () 108 { 109 return (CONSTANT_Class_info) m_constants.get (m_this_class_index); 110 } 111 getSuperClass()112 public CONSTANT_Class_info getSuperClass () 113 { 114 return (CONSTANT_Class_info) m_constants.get (m_super_class_index); 115 } 116 getName()117 public String getName () 118 { 119 return getThisClass ().getName (this); 120 } 121 122 getSuperClassIndex()123 public int getSuperClassIndex () 124 { 125 return m_super_class_index; 126 } 127 setSuperClassIndex(final int super_class_index)128 public void setSuperClassIndex (final int super_class_index) 129 { 130 m_super_class_index = super_class_index; 131 } 132 133 // IAccessFlags: 134 getAccessFlags()135 public final int getAccessFlags () 136 { 137 return m_access_flags; 138 } 139 setAccessFlags(final int flags)140 public final void setAccessFlags (final int flags) 141 { 142 m_access_flags = flags; 143 } 144 isInterface()145 public boolean isInterface () 146 { 147 return (m_access_flags & ACC_INTERFACE) != 0; 148 } 149 isSynthetic()150 public boolean isSynthetic () 151 { 152 return m_attributes.hasSynthetic (); 153 } 154 isNested(final int [] nestedAccessFlags)155 public boolean isNested (final int [] nestedAccessFlags) 156 { 157 final InnerClassesAttribute_info innerClassesAttribute = m_attributes.getInnerClassesAttribute (); 158 159 if (innerClassesAttribute == null) 160 return false; 161 else 162 return innerClassesAttribute.makesClassNested (m_this_class_index, nestedAccessFlags); 163 } 164 165 // methods for getting various nested tables: 166 getConstants()167 public IConstantCollection getConstants () 168 { 169 return m_constants; 170 } 171 getInterfaces()172 public IInterfaceCollection getInterfaces () 173 { 174 return m_interfaces; 175 } 176 getFields()177 public IFieldCollection getFields () 178 { 179 return m_fields; 180 } 181 getMethods()182 public IMethodCollection getMethods () 183 { 184 return m_methods; 185 } 186 getAttributes()187 public IAttributeCollection getAttributes () 188 { 189 return m_attributes; 190 } 191 getFields(final String name)192 public int [] getFields (final String name) 193 { 194 return m_fields.get (this, name); 195 } 196 getMethods(final String name)197 public int [] getMethods (final String name) 198 { 199 return m_methods.get (this, name); 200 } 201 202 // Cloneable: 203 204 /** 205 * Performs a deep copy. 206 */ clone()207 public Object clone () 208 { 209 try 210 { 211 final ClassDef _clone = (ClassDef) super.clone (); 212 213 // do deep copy: 214 _clone.m_version = (int []) m_version.clone (); 215 _clone.m_constants = (IConstantCollection) m_constants.clone (); 216 _clone.m_interfaces = (IInterfaceCollection) m_interfaces.clone (); 217 _clone.m_fields = (IFieldCollection) m_fields.clone (); 218 _clone.m_methods = (IMethodCollection) m_methods.clone (); 219 _clone.m_attributes = (IAttributeCollection) m_attributes.clone (); 220 221 return _clone; 222 } 223 catch (CloneNotSupportedException e) 224 { 225 throw new InternalError (e.toString ()); 226 } 227 } 228 229 230 // IClassFormatOutput: 231 writeInClassFormat(final UDataOutputStream out)232 public void writeInClassFormat (final UDataOutputStream out) throws IOException 233 { 234 if (out == null) throw new IllegalArgumentException ("null input: out"); 235 236 out.writeU4 (m_magic); 237 238 out.writeU2 (m_version [1]); 239 out.writeU2 (m_version [0]); 240 241 m_constants.writeInClassFormat (out); 242 243 out.writeU2 (m_access_flags); 244 245 out.writeU2 (m_this_class_index); 246 out.writeU2 (m_super_class_index); 247 248 m_interfaces.writeInClassFormat (out); 249 m_fields.writeInClassFormat (out); 250 m_methods.writeInClassFormat (out); 251 m_attributes.writeInClassFormat (out); 252 } 253 getDeclaredSUID()254 public final long getDeclaredSUID () 255 { 256 return m_declaredSUID; 257 } 258 259 /** 260 * This follows the spec at http://java.sun.com/j2se/1.4.1/docs/guide/serialization/spec/class.doc6.html#4100 261 * as well as undocumented hacks used by Sun's 1.4.2 J2SDK 262 */ computeSUID(final boolean skipCLINIT)263 public final long computeSUID (final boolean skipCLINIT) 264 { 265 long result = m_declaredSUID; 266 if (result != 0L) 267 return result; 268 else 269 { 270 try 271 { 272 final ByteArrayOStream bout = new ByteArrayOStream (1024); // TODO: reuse these 273 final DataOutputStream dout = new DataOutputStream (bout); 274 275 // (1) The class name written using UTF encoding: 276 277 dout.writeUTF (Types.vmNameToJavaName (getName ())); // [in Java format] 278 279 // (2) The class modifiers written as a 32-bit integer: 280 281 // ACC_STATIC is never written for nested classes/interfaces; 282 // however, ACC_SUPER must be ignored: 283 { 284 // this is tricky: for static/non-static nested classes that 285 // were declared protected in the source the usual access flags 286 // will have ACC_PUBLIC set; the only way to achieve J2SDK 287 // compatibility is to recover the source access flags 288 // from the InnerClasses attribute: 289 290 final int [] nestedAccessFlags = new int [1]; 291 292 final int modifiers = (isNested (nestedAccessFlags) 293 ? nestedAccessFlags [0] 294 : getAccessFlags ()) 295 & (ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT); 296 297 // if/when emma decides to instrument interfaces for <clinit> 298 // coverage, compensate for javac bug in which ABSTRACT bit 299 // was set for an interface only if the interface declared methods 300 // [Sun's J2SDK]: 301 302 dout.writeInt (modifiers); 303 } 304 305 // not doing another J2SDK compensation for arrays, because 306 // we never load/instrument those 307 308 // (3) The name of each interface sorted by name written using UTF encoding 309 { 310 final IInterfaceCollection interfaces = getInterfaces (); 311 final String [] ifcs = new String [interfaces.size ()]; 312 313 final int iLimit = ifcs.length; 314 for (int i = 0; i < iLimit; ++ i) 315 { 316 // [in Java format] 317 ifcs [i] = Types.vmNameToJavaName (((CONSTANT_Class_info) m_constants.get (interfaces.get (i))).getName (this)); 318 } 319 320 Arrays.sort (ifcs); 321 for (int i = 0; i < iLimit; ++ i) 322 { 323 dout.writeUTF (ifcs [i]); 324 } 325 } 326 327 // (4) For each field of the class sorted by field name (except 328 // private static and private transient fields): 329 // a. The name of the field in UTF encoding. 330 // b. The modifiers of the field written as a 32-bit integer. 331 // c. The descriptor of the field in UTF encoding 332 { 333 final IFieldCollection fields = getFields (); 334 final FieldDescriptor [] fds = new FieldDescriptor [fields.size ()]; 335 336 int fcount = 0; 337 for (int f = 0, fLimit = fds.length; f < fLimit; ++ f) 338 { 339 final Field_info field = fields.get (f); 340 final int modifiers = field.getAccessFlags (); 341 342 if (((modifiers & ACC_PRIVATE) == 0) || 343 ((modifiers & (ACC_STATIC | ACC_TRANSIENT)) == 0)) 344 fds [fcount ++] = new FieldDescriptor (field.getName (this), modifiers, field.getDescriptor (this)); 345 } 346 347 if (fcount > 0) 348 { 349 Arrays.sort (fds, 0, fcount); 350 for (int i = 0; i < fcount; ++ i) 351 { 352 final FieldDescriptor fd = fds [i]; 353 354 dout.writeUTF (fd.m_name); 355 dout.writeInt (fd.m_modifiers); 356 dout.writeUTF (fd.m_descriptor); 357 } 358 } 359 } 360 361 // (5) If a class initializer exists, write out the following: 362 // a. The name of the method, <clinit>, in UTF encoding. 363 // b. The modifier of the method, ACC_STATIC, written as a 32-bit integer. 364 // c. The descriptor of the method, ()V, in UTF encoding. 365 // (6) For each non-private constructor sorted by method name and signature: 366 // a. The name of the method, <init>, in UTF encoding. 367 // b. The modifiers of the method written as a 32-bit integer. 368 // c. The descriptor of the method in UTF encoding. 369 // (7) For each non-private method sorted by method name and signature: 370 // a. The name of the method in UTF encoding. 371 // b. The modifiers of the method written as a 32-bit integer. 372 // c. The descriptor of the method in UTF encoding. 373 374 // note: although this is not documented, J2SDK code uses '.''s as 375 // descriptor separators (this is done for methods only, not for fields) 376 { 377 final IMethodCollection methods = getMethods (); 378 379 boolean hasCLINIT = false; 380 final ConstructorDescriptor [] cds = new ConstructorDescriptor [methods.size ()]; 381 final MethodDescriptor [] mds = new MethodDescriptor [cds.length]; 382 383 int ccount = 0, mcount = 0; 384 385 for (int i = 0, iLimit = cds.length; i < iLimit; ++ i) 386 { 387 final Method_info method = methods.get (i); 388 389 final String name = method.getName (this); 390 391 if (! hasCLINIT && IClassDefConstants.CLINIT_NAME.equals (name)) 392 { 393 hasCLINIT = true; 394 continue; 395 } 396 else 397 { 398 final int modifiers = method.getAccessFlags (); 399 if ((modifiers & ACC_PRIVATE) == 0) 400 { 401 if (IClassDefConstants.INIT_NAME.equals (name)) 402 cds [ccount ++] = new ConstructorDescriptor (modifiers, method.getDescriptor (this)); 403 else 404 mds [mcount ++] = new MethodDescriptor (name, modifiers, method.getDescriptor (this)); 405 } 406 } 407 } 408 409 if (hasCLINIT && ! skipCLINIT) 410 { 411 dout.writeUTF (IClassDefConstants.CLINIT_NAME); 412 dout.writeInt (ACC_STATIC); 413 dout.writeUTF (IClassDefConstants.CLINIT_DESCRIPTOR); 414 } 415 416 if (ccount > 0) 417 { 418 Arrays.sort (cds, 0, ccount); 419 420 for (int i = 0; i < ccount; ++ i) 421 { 422 final ConstructorDescriptor cd = cds [i]; 423 424 dout.writeUTF (IClassDefConstants.INIT_NAME); 425 dout.writeInt (cd.m_modifiers); 426 dout.writeUTF (cd.m_descriptor.replace ('/', '.')); 427 } 428 } 429 430 if (mcount > 0) 431 { 432 Arrays.sort (mds, 0, mcount); 433 434 for (int i = 0; i < mcount; ++ i) 435 { 436 final MethodDescriptor md = mds [i]; 437 438 dout.writeUTF (md.m_name); 439 dout.writeInt (md.m_modifiers); 440 dout.writeUTF (md.m_descriptor.replace ('/', '.')); 441 } 442 } 443 } 444 445 dout.flush(); 446 447 448 if (DEBUG_SUID) 449 { 450 byte [] dump = bout.copyByteArray (); 451 for (int x = 0; x < dump.length; ++ x) 452 { 453 System.out.println ("DUMP[" + x + "] = " + dump [x] + "\t" + (char) dump[x]); 454 } 455 } 456 457 final MessageDigest md = MessageDigest.getInstance ("SHA"); 458 459 md.update (bout.getByteArray (), 0, bout.size ()); 460 final byte [] hash = md.digest (); 461 462 if (DEBUG_SUID) 463 { 464 for (int x = 0; x < hash.length; ++ x) 465 { 466 System.out.println ("HASH[" + x + "] = " + hash [x]); 467 } 468 } 469 470 // final int hash0 = hash [0]; 471 // final int hash1 = hash [1]; 472 // result = ((hash0 >>> 24) & 0xFF) | ((hash0 >>> 16) & 0xFF) << 8 | ((hash0 >>> 8) & 0xFF) << 16 | ((hash0 >>> 0) & 0xFF) << 24 | 473 // ((hash1 >>> 24) & 0xFF) << 32 | ((hash1 >>> 16) & 0xFF) << 40 | ((hash1 >>> 8) & 0xFF) << 48 | ((hash1 >>> 0) & 0xFF) << 56; 474 475 for (int i = Math.min (hash.length, 8) - 1; i >= 0; -- i) 476 { 477 result = (result << 8) | (hash [i] & 0xFF); 478 } 479 480 return result; 481 } 482 catch (IOException ioe) 483 { 484 throw new Error (ioe.getMessage ()); 485 } 486 catch (NoSuchAlgorithmException nsae) 487 { 488 throw new SecurityException (nsae.getMessage()); 489 } 490 } 491 } 492 493 addCONSTANT_Utf8(final String value, final boolean keepUnique)494 public int addCONSTANT_Utf8 (final String value, final boolean keepUnique) 495 { 496 if (keepUnique) 497 { 498 final int existing = m_constants.findCONSTANT_Utf8 (value); 499 if (existing > 0) 500 { 501 return existing; 502 } 503 504 // [else fall through] 505 } 506 507 return m_constants.add (new CONSTANT_Utf8_info (value)); 508 } 509 addStringConstant(final String value)510 public int addStringConstant (final String value) 511 { 512 final int value_index = addCONSTANT_Utf8 (value, true); 513 514 // TODO: const uniqueness 515 return m_constants.add (new CONSTANT_String_info (value_index)); 516 } 517 addNameType(final String name, final String typeDescriptor)518 public int addNameType (final String name, final String typeDescriptor) 519 { 520 final int name_index = addCONSTANT_Utf8 (name, true); 521 final int descriptor_index = addCONSTANT_Utf8 (typeDescriptor, true); 522 523 return m_constants.add (new CONSTANT_NameAndType_info (name_index, descriptor_index)); 524 } 525 526 addClassref(final String classJVMName)527 public int addClassref (final String classJVMName) 528 { 529 final int name_index = addCONSTANT_Utf8 (classJVMName, true); 530 // TODO: this should do uniqueness checking: 531 532 return m_constants.add (new CONSTANT_Class_info (name_index)); 533 } 534 535 536 /** 537 * Adds a new declared field to this class [with no attributes] 538 */ addField(final String name, final String descriptor, final int access_flags)539 public int addField (final String name, final String descriptor, final int access_flags) 540 { 541 // TODO: support Fields with initializer attributes? 542 // TODO: no "already exists" check done here 543 544 final int name_index = addCONSTANT_Utf8 (name, true); 545 final int descriptor_index = addCONSTANT_Utf8 (descriptor, true); 546 547 final Field_info field = new Field_info (access_flags, name_index, descriptor_index, 548 ElementFactory.newAttributeCollection (0)); 549 550 return m_fields.add (field); 551 } 552 553 /** 554 * Adds a new declared field to this class [with given attributes] 555 */ addField(final String name, final String descriptor, final int access_flags, final IAttributeCollection attributes)556 public int addField (final String name, final String descriptor, final int access_flags, 557 final IAttributeCollection attributes) 558 { 559 // TODO: support Fields with initializer attributes? 560 // TODO: no "already exists" check done here 561 562 final int name_index = addCONSTANT_Utf8 (name, true); 563 final int descriptor_index = addCONSTANT_Utf8 (descriptor, true); 564 565 final Field_info field = new Field_info (access_flags, name_index, descriptor_index, attributes); 566 567 return m_fields.add (field); 568 } 569 570 571 // TODO: rework this API 572 newEmptyMethod(final String name, final String descriptor, final int access_flags)573 public Method_info newEmptyMethod (final String name, final String descriptor, final int access_flags) 574 { 575 // TODO: flag for making synthetic etc 576 final int attribute_name_index = addCONSTANT_Utf8 (Attribute_info.ATTRIBUTE_CODE, true); 577 final int name_index = addCONSTANT_Utf8 (name, true); 578 final int descriptor_index = addCONSTANT_Utf8 (descriptor, true); 579 580 final IAttributeCollection attributes = ElementFactory.newAttributeCollection (0); 581 final CodeAttribute_info code = new CodeAttribute_info (attribute_name_index, 0, 0, 582 CodeAttribute_info.EMPTY_BYTE_ARRAY, 583 AttributeElementFactory.newExceptionHandlerTable (0), 584 ElementFactory.newAttributeCollection (0)); 585 586 attributes.add (code); 587 588 final Method_info method = new Method_info (access_flags, name_index, descriptor_index, attributes); 589 590 return method; 591 } 592 addMethod(final Method_info method)593 public int addMethod (final Method_info method) 594 { 595 return m_methods.add (method); 596 } 597 598 /** 599 * Adds a reference to a field declared by this class. 600 * 601 * @return constant pool index of the reference 602 */ addFieldref(final Field_info field)603 public int addFieldref (final Field_info field) 604 { 605 // TODO: keepUnique flag 606 607 final CONSTANT_NameAndType_info nametype = new CONSTANT_NameAndType_info (field.m_name_index, field.m_descriptor_index); 608 final int nametype_index = m_constants.add (nametype); // TODO: unique logic 609 610 return m_constants.add (new CONSTANT_Fieldref_info (m_this_class_index, nametype_index)); 611 } 612 613 /** 614 * Adds a reference to a field declared by this class. 615 * 616 * @return constant pool index of the reference 617 */ addFieldref(final int offset)618 public int addFieldref (final int offset) 619 { 620 // TODO: keepUnique flag 621 622 final Field_info field = m_fields.get (offset); 623 624 final CONSTANT_NameAndType_info nametype = new CONSTANT_NameAndType_info (field.m_name_index, field.m_descriptor_index); 625 final int nametype_index = m_constants.add (nametype); // TODO: unique logic 626 627 return m_constants.add (new CONSTANT_Fieldref_info (m_this_class_index, nametype_index)); 628 } 629 630 // protected: ............................................................. 631 632 // package: ............................................................... 633 634 // private: ............................................................... 635 636 637 private static final class FieldDescriptor implements Comparable 638 { 639 // Comparable: 640 compareTo(final Object obj)641 public final int compareTo (final Object obj) 642 { 643 return m_name.compareTo (((FieldDescriptor) obj).m_name); 644 } 645 FieldDescriptor(final String name, final int modifiers, final String descriptor)646 FieldDescriptor (final String name, final int modifiers, final String descriptor) 647 { 648 m_name = name; 649 m_modifiers = modifiers; 650 m_descriptor = descriptor; 651 } 652 653 654 final String m_name; 655 final int m_modifiers; 656 final String m_descriptor; 657 658 } // end of nested class 659 660 661 private static final class ConstructorDescriptor implements Comparable 662 { 663 // Comparable: 664 compareTo(final Object obj)665 public final int compareTo (final Object obj) 666 { 667 return m_descriptor.compareTo (((ConstructorDescriptor) obj).m_descriptor); 668 } 669 ConstructorDescriptor(final int modifiers, final String descriptor)670 ConstructorDescriptor (final int modifiers, final String descriptor) 671 { 672 m_modifiers = modifiers; 673 m_descriptor = descriptor; 674 } 675 676 677 final int m_modifiers; 678 final String m_descriptor; 679 680 } // end of nested class 681 682 683 private static final class MethodDescriptor implements Comparable 684 { 685 // Comparable: 686 compareTo(final Object obj)687 public final int compareTo (final Object obj) 688 { 689 final MethodDescriptor rhs = (MethodDescriptor) obj; 690 691 int result = m_name.compareTo (rhs.m_name); 692 if (result == 0) 693 result = m_descriptor.compareTo (rhs.m_descriptor); 694 695 return result; 696 } 697 MethodDescriptor(final String name, final int modifiers, final String descriptor)698 MethodDescriptor (final String name, final int modifiers, final String descriptor) 699 { 700 m_name = name; 701 m_modifiers = modifiers; 702 m_descriptor = descriptor; 703 } 704 705 706 final String m_name; 707 final int m_modifiers; 708 final String m_descriptor; 709 710 } // end of nested class 711 712 713 // TODO: final fields 714 715 private long m_magic; 716 private int [] /* major, minor */ m_version; 717 private int m_access_flags; 718 719 private int m_this_class_index, m_super_class_index; 720 721 private IConstantCollection m_constants; 722 private IInterfaceCollection m_interfaces; 723 private IFieldCollection m_fields; 724 private IMethodCollection m_methods; 725 private IAttributeCollection m_attributes; 726 727 private long m_declaredSUID; 728 729 private static final boolean DEBUG_SUID = false; 730 731 } // end of class 732 // ---------------------------------------------------------------------------- 733