KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > vladium > jcd > cls > ClassDef


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 JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.security.MessageDigest JavaDoc;
14 import java.security.NoSuchAlgorithmException JavaDoc;
15 import java.util.Arrays JavaDoc;
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 JavaDoc, IAccessFlags, IClassFormatOutput
46 {
47     // public: ................................................................
48

49
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

63     public void accept (final IClassDefVisitor visitor, final Object JavaDoc ctx)
64     {
65         visitor.visit (this, ctx);
66     }
67
68
69     public long getMagic ()
70     {
71         return m_magic;
72     }
73     
74     public void setMagic (final long magic)
75     {
76         m_magic = magic;
77     }
78     
79     
80     public int [] getVersion ()
81     {
82         return m_version;
83     }
84     
85     public void setVersion (final int [] version)
86     {
87         m_version [0] = version [0];
88         m_version [1] = version [1];
89     }
90     
91     public final void setDeclaredSUID (final long suid)
92     {
93         m_declaredSUID = suid;
94     }
95     
96
97     public int getThisClassIndex ()
98     {
99         return m_this_class_index;
100     }
101     
102     public void setThisClassIndex (final int this_class_index)
103     {
104         m_this_class_index = this_class_index;
105     }
106     
107     public CONSTANT_Class_info getThisClass ()
108     {
109         return (CONSTANT_Class_info) m_constants.get (m_this_class_index);
110     }
111     
112     public CONSTANT_Class_info getSuperClass ()
113     {
114         return (CONSTANT_Class_info) m_constants.get (m_super_class_index);
115     }
116     
117     public String JavaDoc getName ()
118     {
119         return getThisClass ().getName (this);
120     }
121
122
123     public int getSuperClassIndex ()
124     {
125         return m_super_class_index;
126     }
127     
128     public void setSuperClassIndex (final int super_class_index)
129     {
130         m_super_class_index = super_class_index;
131     }
132     
133     // IAccessFlags:
134

135     public final int getAccessFlags ()
136     {
137         return m_access_flags;
138     }
139
140     public final void setAccessFlags (final int flags)
141     {
142         m_access_flags = flags;
143     }
144     
145     public boolean isInterface ()
146     {
147         return (m_access_flags & ACC_INTERFACE) != 0;
148     }
149     
150     public boolean isSynthetic ()
151     {
152         return m_attributes.hasSynthetic ();
153     }
154     
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

167     public IConstantCollection getConstants ()
168     {
169         return m_constants;
170     }
171     
172     public IInterfaceCollection getInterfaces ()
173     {
174         return m_interfaces;
175     }
176     
177     public IFieldCollection getFields ()
178     {
179         return m_fields;
180     }
181     
182     public IMethodCollection getMethods ()
183     {
184         return m_methods;
185     }
186     
187     public IAttributeCollection getAttributes ()
188     {
189         return m_attributes;
190     }
191     
192     public int [] getFields (final String JavaDoc name)
193     {
194         return m_fields.get (this, name);
195     }
196     
197     public int [] getMethods (final String JavaDoc name)
198     {
199         return m_methods.get (this, name);
200     }
201     
202     // Cloneable:
203

204     /**
205      * Performs a deep copy.
206      */

207     public Object JavaDoc 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 JavaDoc e)
224         {
225             throw new InternalError JavaDoc (e.toString ());
226         }
227     }
228     
229     
230     // IClassFormatOutput:
231

232     public void writeInClassFormat (final UDataOutputStream out) throws IOException JavaDoc
233     {
234         if (out == null) throw new IllegalArgumentException JavaDoc ("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     
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      */

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 JavaDoc dout = new DataOutputStream JavaDoc (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 JavaDoc [] ifcs = new String JavaDoc [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 JavaDoc 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 JavaDoc 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 JavaDoc ioe)
483             {
484                 throw new Error JavaDoc (ioe.getMessage ());
485             }
486             catch (NoSuchAlgorithmException JavaDoc nsae)
487             {
488                 throw new SecurityException JavaDoc (nsae.getMessage());
489             }
490         }
491     }
492     
493     
494     public int addCONSTANT_Utf8 (final String JavaDoc 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     
510     public int addStringConstant (final String JavaDoc 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     
518     public int addNameType (final String JavaDoc name, final String JavaDoc 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
527     public int addClassref (final String JavaDoc 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      */

539     public int addField (final String JavaDoc name, final String JavaDoc 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      */

556     public int addField (final String JavaDoc name, final String JavaDoc 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

573     public Method_info newEmptyMethod (final String JavaDoc name, final String JavaDoc 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     
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      */

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      */

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 JavaDoc
638     {
639         // Comparable:
640

641         public final int compareTo (final Object JavaDoc obj)
642         {
643             return m_name.compareTo (((FieldDescriptor) obj).m_name);
644         }
645
646         FieldDescriptor (final String JavaDoc name, final int modifiers, final String JavaDoc descriptor)
647         {
648             m_name = name;
649             m_modifiers = modifiers;
650             m_descriptor = descriptor;
651         }
652
653         
654         final String JavaDoc m_name;
655         final int m_modifiers;
656         final String JavaDoc m_descriptor;
657         
658     } // end of nested class
659

660     
661     private static final class ConstructorDescriptor implements Comparable JavaDoc
662     {
663         // Comparable:
664

665         public final int compareTo (final Object JavaDoc obj)
666         {
667             return m_descriptor.compareTo (((ConstructorDescriptor) obj).m_descriptor);
668         }
669         
670         ConstructorDescriptor (final int modifiers, final String JavaDoc descriptor)
671         {
672             m_modifiers = modifiers;
673             m_descriptor = descriptor;
674         }
675         
676
677         final int m_modifiers;
678         final String JavaDoc m_descriptor;
679         
680     } // end of nested class
681

682     
683     private static final class MethodDescriptor implements Comparable JavaDoc
684     {
685         // Comparable:
686

687         public final int compareTo (final Object JavaDoc 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
698         MethodDescriptor (final String JavaDoc name, final int modifiers, final String JavaDoc descriptor)
699         {
700             m_name = name;
701             m_modifiers = modifiers;
702             m_descriptor = descriptor;
703         }
704
705         
706         final String JavaDoc m_name;
707         final int m_modifiers;
708         final String JavaDoc 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
Popular Tags