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: ClassDefParser.java,v 1.1.1.1 2004/05/09 16:57:51 vlad_r Exp $
8 */
9package com.vladium.jcd.parser;
10
11import java.io.InputStream;
12import java.io.IOException;
13
14import com.vladium.jcd.cls.*;
15import com.vladium.jcd.cls.attribute.*;
16import com.vladium.jcd.cls.constant.*;
17import com.vladium.jcd.lib.UDataInputStream;
18import com.vladium.util.ByteArrayIStream;
19
20// ----------------------------------------------------------------------------
21/**
22 * This class provides an API for parsing a stream or array of bytecodes into a
23 * {@link ClassDef} AST.
24 *
25 * @author (C) 2001, Vlad Roubtsov
26 */
27public
28abstract class ClassDefParser
29{
30    // public: ................................................................
31
32
33    /**
34     * Parses an array of bytecodes into a {@link ClassDef}.
35     */
36    public static ClassDef parseClass (final byte [] bytes)
37        throws IOException
38    {
39        if (bytes == null) throw new IllegalArgumentException ("null input: bytes");
40
41        classParser parser = new classParser (new UDataInputStream (new ByteArrayIStream (bytes)));
42
43        return parser.class_table ();
44    }
45
46    /**
47     * Parses an array of bytecodes into a {@link ClassDef}.
48     */
49    public static ClassDef parseClass (final byte [] bytes, final int length)
50        throws IOException
51    {
52        if (bytes == null) throw new IllegalArgumentException ("null input: bytes");
53
54        classParser parser = new classParser (new UDataInputStream (new ByteArrayIStream (bytes, length)));
55
56        return parser.class_table ();
57    }
58
59
60    /**
61     * Parses a stream of bytecodes into a {@link ClassDef}.
62     */
63    public static ClassDef parseClass (final InputStream bytes)
64        throws IOException
65    {
66        if (bytes == null) throw new IllegalArgumentException ("null input: bytes");
67
68        classParser parser = new classParser (new UDataInputStream (bytes));
69
70        return parser.class_table ();
71    }
72
73    // protected: .............................................................
74
75    // package: ...............................................................
76
77
78    static final boolean PARSE_SERIAL_VERSION_UID = true;
79
80    static final String SERIAL_VERSION_UID_FIELD_NAME   = "serialVersionUID";
81    static final int SERIAL_VERSION_UID_FIELD_MASK      = IAccessFlags.ACC_STATIC | IAccessFlags.ACC_FINAL;
82
83    // private: ...............................................................
84
85
86    /**
87     * All the parsing work is done by this class and its class_table method. The
88     * work that needs to be done is not complicated, but is rather monotonous -- see
89     * Chapter 4 of VM spec 1.0 for the class file format.
90     */
91    private static final class classParser
92    {
93        classParser (final UDataInputStream bytes)
94        {
95            m_bytes = bytes;
96        }
97
98
99        ClassDef class_table () throws IOException
100        {
101            m_table = new ClassDef ();
102
103
104            magic ();
105            version ();
106
107            if (DEBUG) System.out.println (s_line);
108
109            constant_pool ();
110
111            if (DEBUG) System.out.println (s_line);
112
113            access_flags ();
114            this_class ();
115            super_class ();
116
117            if (DEBUG) System.out.println (s_line);
118
119            interfaces ();
120            if (DEBUG) System.out.println (s_line);
121
122            fields ();
123            if (DEBUG) System.out.println (s_line);
124
125            methods ();
126            if (DEBUG) System.out.println (s_line);
127
128            attributes ();
129            if (DEBUG) System.out.println (s_line);
130
131            return m_table;
132        }
133
134
135        void magic () throws IOException
136        {
137            final long magic = m_bytes.readU4 ();
138            if (DEBUG) System.out.println ("magic: [" + Long.toHexString (magic) + ']');
139
140            m_table.setMagic (magic);
141        }
142
143
144        void version () throws IOException
145        {
146            final int minor_version = m_bytes.readU2 ();
147            final int major_version = m_bytes.readU2 ();
148
149            if (DEBUG)
150            {
151                System.out.println ("major_version: [" + major_version + ']');
152                System.out.println ("minor_version: [" + minor_version + ']');
153            }
154
155            m_table.setVersion (new int [] {major_version, minor_version});
156        }
157
158
159        void constant_pool () throws IOException
160        {
161            final int constant_pool_count = m_bytes.readU2 ();
162            if (DEBUG) System.out.println ("constant_pool_count = " + constant_pool_count + " [actual number of entries = " + (constant_pool_count - 1) + "]");
163
164            final IConstantCollection constants = m_table.getConstants();
165
166            for (int index = 1; index < constant_pool_count; ++ index)
167            {
168                final CONSTANT_info cp_info = CONSTANT_info.new_CONSTANT_info (m_bytes);
169                constants.add (cp_info);
170
171                if (DEBUG) System.out.println ("[" + index + "] constant: " + cp_info);
172
173                if ((cp_info instanceof CONSTANT_Long_info) || (cp_info instanceof CONSTANT_Double_info))
174                    index++;
175            }
176        }
177
178
179        void access_flags () throws IOException
180        {
181            final int _access_flags = m_bytes.readU2 ();
182
183            m_table.setAccessFlags (_access_flags);
184        }
185
186
187        void this_class () throws IOException
188        {
189            final int _class_index = m_bytes.readU2 ();
190            if (DEBUG) System.out.println ("this_class: [" + _class_index + ']');
191
192            m_table.setThisClassIndex (_class_index);
193        }
194
195
196        void super_class () throws IOException
197        {
198            final int _class_index = m_bytes.readU2 ();
199            if (DEBUG) System.out.println ("super_class: [" + _class_index + ']');
200
201            m_table.setSuperClassIndex (_class_index);
202        }
203
204
205        void interfaces () throws IOException
206        {
207            final int _interfaces_count = m_bytes.readU2 ();
208            if (DEBUG) System.out.println ("interfaces_count = " + _interfaces_count);
209
210            for (int i = 0; i < _interfaces_count; i++)
211            {
212                int _interface_index = m_bytes.readU2 ();
213                if (DEBUG) System.out.println ("[" + i + "] interface: " + _interface_index);
214
215                m_table.getInterfaces().add (_interface_index);
216            }
217        }
218
219
220        void fields () throws IOException
221        {
222            final int _fields_count = m_bytes.readU2 ();
223            if (DEBUG) System.out.println ("fields_count = " + _fields_count);
224
225            final IConstantCollection constantPool = m_table.getConstants ();
226
227            for (int i = 0; i < _fields_count; i++)
228            {
229                final Field_info field_info = new Field_info (constantPool, m_bytes);
230                if (DEBUG)
231                {
232                    System.out.println ("[" + i + "] field: " + field_info);
233                    System.out.println ();
234                }
235
236                m_table.getFields().add (field_info);
237
238                if (PARSE_SERIAL_VERSION_UID)
239
240                if (((field_info.getAccessFlags () & SERIAL_VERSION_UID_FIELD_MASK) == SERIAL_VERSION_UID_FIELD_MASK)
241                    && SERIAL_VERSION_UID_FIELD_NAME.equals (field_info.getName (m_table)))
242                {
243                    final IAttributeCollection attributes = field_info.getAttributes ();
244                    for (int a = 0, aLimit = attributes.size (); a < aLimit; ++ a)
245                    {
246                        final Attribute_info attr_info = attributes.get (a);
247
248                        if (attr_info instanceof ConstantValueAttribute_info)
249                        {
250                            final CONSTANT_literal_info constant_value = ((ConstantValueAttribute_info) attr_info).getValue (m_table);
251                            if (constant_value instanceof CONSTANT_Long_info)
252                                m_table.setDeclaredSUID (((CONSTANT_Long_info) constant_value).m_value);
253                        }
254                    }
255                }
256            }
257        }
258
259
260        void methods () throws IOException
261        {
262            final int _methods_count = m_bytes.readU2 ();
263            if (DEBUG) System.out.println ("methods_count = " + _methods_count);
264
265            final IConstantCollection constantPool = m_table.getConstants ();
266
267            for (int i = 0; i < _methods_count; i++)
268            {
269                final Method_info method_info = new Method_info (constantPool, m_bytes);
270                if (DEBUG)
271                {
272                    System.out.println ("[" + i + "] method: " + method_info);
273                    System.out.println ();
274                }
275
276                m_table.getMethods().add (method_info);
277            }
278        }
279
280
281        void attributes () throws IOException
282        {
283            final int _attributes_count = m_bytes.readU2 ();
284            if (DEBUG) System.out.println ("attributes_count = " + _attributes_count);
285
286            IConstantCollection constantPool = m_table.getConstants ();
287
288            for (int i = 0; i < _attributes_count; i++)
289            {
290                Attribute_info attribute_info = Attribute_info.new_Attribute_info (constantPool, m_bytes);
291                if (DEBUG)
292                {
293                    System.out.println ("[" + i + "] attribute: " + attribute_info);
294                    System.out.println ();
295                }
296
297                m_table.getAttributes().add (attribute_info);
298            }
299        }
300
301
302        private final UDataInputStream m_bytes;
303        private ClassDef m_table;
304
305        private static final boolean DEBUG = false;
306        private static final String s_line = "------------------------------------------------------------------------";
307
308    } // end of static class
309
310} // end of class
311// ----------------------------------------------------------------------------
312