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 */
9package com.vladium.jcd.cls;
10
11import java.io.DataOutputStream;
12import java.io.IOException;
13import java.security.MessageDigest;
14import java.security.NoSuchAlgorithmException;
15import java.util.Arrays;
16
17import com.vladium.jcd.cls.attribute.AttributeElementFactory;
18import com.vladium.jcd.cls.attribute.Attribute_info;
19import com.vladium.jcd.cls.attribute.CodeAttribute_info;
20import com.vladium.jcd.cls.attribute.InnerClassesAttribute_info;
21import com.vladium.jcd.cls.constant.CONSTANT_Class_info;
22import com.vladium.jcd.cls.constant.CONSTANT_Fieldref_info;
23import com.vladium.jcd.cls.constant.CONSTANT_NameAndType_info;
24import com.vladium.jcd.cls.constant.CONSTANT_String_info;
25import com.vladium.jcd.cls.constant.CONSTANT_Utf8_info;
26import com.vladium.jcd.compiler.IClassFormatOutput;
27import com.vladium.jcd.lib.Types;
28import com.vladium.jcd.lib.UDataOutputStream;
29import 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 */
44public
45final class ClassDef implements Cloneable, 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 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 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 name)
193    {
194        return m_fields.get (this, name);
195    }
196
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     */
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
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
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 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
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
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
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
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     */
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     */
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
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
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
638    {
639        // Comparable:
640
641        public final int compareTo (final Object obj)
642        {
643            return m_name.compareTo (((FieldDescriptor) obj).m_name);
644        }
645
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
665        public final int compareTo (final Object obj)
666        {
667            return m_descriptor.compareTo (((ConstructorDescriptor) obj).m_descriptor);
668        }
669
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
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
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