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: MethodDescriptor.java,v 1.1.1.1.2.1 2004/07/10 03:34:52 vlad_r Exp $
8 */
9package com.vladium.emma.data;
10
11import java.io.DataInput;
12import java.io.DataOutput;
13import java.io.IOException;
14import java.io.Serializable;
15
16import com.vladium.util.IConstants;
17import com.vladium.util.IntObjectMap;
18import com.vladium.util.IntSet;
19import com.vladium.util.asserts.$assert;
20
21// ----------------------------------------------------------------------------
22/**
23 * @author Vlad Roubtsov, (C) 2003
24 */
25public
26final class MethodDescriptor implements IConstants, IMetadataConstants, Serializable
27{
28    // public: ................................................................
29
30    // need a separate 'blockCount' parm because 'blockMap' could be null
31    // and for a class that is never loaded I can't find out the number of
32    // blocks for block coverage reporting
33
34    public MethodDescriptor (final String name, final String descriptor, final int status,
35                             final int [] blockSizes, final int [][] blockMap, final int firstLine)
36    {
37        if (name == null)
38            throw new IllegalArgumentException ("null input: name");
39        if (descriptor == null)
40            throw new IllegalArgumentException ("null input: descriptor");
41
42        if ((status & METHOD_NO_BLOCK_DATA) == 0)
43        {
44            // block metadata is available: blockCount must be positive
45
46            final int blockCount = blockSizes.length;
47
48            if ($assert.ENABLED) $assert.ASSERT (blockCount > 0, "blockCount must be positive: " + blockCount);
49            m_blockSizes = blockSizes;
50
51            if ((status & METHOD_NO_LINE_DATA) == 0)
52            {
53                // line metadata is available: blockMap must not be null or empty
54
55                if ($assert.ENABLED) $assert.ASSERT (firstLine > 0, "firstLine must be positive: " + firstLine);
56
57                if ((blockMap == null) || (blockMap.length == 0))
58                    throw new IllegalArgumentException ("null or empty input: blockMap");
59
60                if ($assert.ENABLED)
61                {
62                    $assert.ASSERT (blockCount == blockMap.length, "blockCount " + blockCount + " != blockMap.length " + blockMap.length);
63
64                    for (int i = 0; i < blockMap.length; ++ i)
65                    {
66                        $assert.ASSERT (blockMap [i] != null, "blockMap[" + i + "] is null");
67                        // note: it is legal for blockMap [i] to be empty
68                    }
69                }
70
71                m_blockMap = blockMap;
72                m_firstLine = firstLine;
73            }
74            else
75            {
76                m_blockMap = null;
77                m_firstLine = 0;
78            }
79        }
80        else
81        {
82            m_blockSizes = null;
83            m_blockMap = null;
84            m_firstLine = 0;
85        }
86
87        m_name = name;
88        m_descriptor = descriptor;
89        m_status = status;
90    }
91
92
93    public String getName ()
94    {
95        return m_name;
96    }
97
98    public String getDescriptor ()
99    {
100        return m_descriptor;
101    }
102
103    public int getStatus ()
104    {
105        return m_status;
106    }
107
108    public int getBlockCount ()
109    {
110        return m_blockSizes.length;
111    }
112
113    public int [] getBlockSizes ()
114    {
115        return m_blockSizes;
116    }
117
118    public int [][] getBlockMap ()
119    {
120        return m_blockMap;
121    }
122
123    public IntObjectMap /* line no->int[](blockIDs) */ getLineMap ()
124    {
125        IntObjectMap lineMap = m_lineMap;
126        if (lineMap != null)
127            return lineMap;
128        else if ((m_status & METHOD_NO_LINE_DATA) == 0)
129        {
130            // construct reverse line->block ID mapping:
131
132            lineMap = new IntObjectMap ();
133            final int [][] blockMap = m_blockMap;
134
135            for (int bl = 0, blCount = blockMap.length; bl < blCount; ++ bl)
136            {
137                final int [] lines = blockMap [bl];
138                if (lines != null)
139                {
140                    final int lineCount = lines.length;
141
142                    for (int l = 0; l < lineCount; ++ l)
143                    {
144                        final int line = lines [l];
145                        IntSet blockIDs = (IntSet) lineMap.get (line);
146
147                        if (blockIDs == null)
148                        {
149                            blockIDs = new IntSet ();
150                            lineMap.put (line, blockIDs);
151                        }
152
153                        blockIDs.add (bl);
154                    }
155                }
156            }
157
158            final int [] lines = lineMap.keys ();
159            for (int l = 0, lineCount = lines.length; l < lineCount; ++ l)
160            {
161                final int line = lines [l];
162                final int [] blockIDs = ((IntSet) lineMap.get (line)).values ();
163                if ($assert.ENABLED) $assert.ASSERT (blockIDs != null && blockIDs.length > 0, "wrong line mapping for line #" + line);
164
165                lineMap.put (line, blockIDs); // overwrite IntSet as the value
166            }
167
168            m_lineMap = lineMap;
169
170            return lineMap;
171        }
172
173        return null;
174    }
175
176    public int getFirstLine ()
177    {
178        return m_firstLine;
179    }
180
181    public boolean hasLineNumberInfo ()
182    {
183        return (m_status & METHOD_NO_LINE_DATA) == 0;
184    }
185
186
187    public String toString ()
188    {
189        return toString ("");
190    }
191
192    public String toString (final String indent)
193    {
194        StringBuffer s = new StringBuffer (indent + "method [" + m_name + "] descriptor:");
195
196        if ((m_status & METHOD_NO_LINE_DATA) == 0)
197        {
198            for (int bl = 0; bl < m_blockMap.length; ++ bl)
199            {
200                s.append (EOL);
201                s.append (indent + INDENT_INCREMENT + "block " + bl + " (" + m_blockSizes [bl] + " instrs) : ");
202
203                final int [] lines = m_blockMap [bl];
204                for (int l = 0; l < lines.length; ++ l)
205                {
206                    if (l != 0) s.append (", ");
207                    s.append (lines [l]);
208                }
209            }
210            s.append (EOL);
211            s.append (indent + INDENT_INCREMENT + "---");
212
213            final int [] lines = m_lineMap.keys ();
214            for (int l = 0; l < lines.length; ++ l)
215            {
216                s.append (EOL);
217                s.append (indent + INDENT_INCREMENT + "line " + lines [l] + ": ");
218
219                final int [] blocks = (int []) m_lineMap.get (lines [l]);
220                for (int bl = 0; bl < blocks.length; ++ bl)
221                {
222                    if (bl != 0) s.append (", ");
223                    s.append (blocks [bl]);
224                }
225            }
226        }
227        else
228        {
229            s.append (" <no line info>");
230        }
231
232        return s.toString ();
233    }
234
235    // protected: .............................................................
236
237    // package: ...............................................................
238
239
240    static MethodDescriptor readExternal (final DataInput in)
241        throws IOException
242    {
243        final String name = in.readUTF ();
244        final String descriptor = in.readUTF ();
245
246        final int status = in.readInt ();
247
248        int [] blockSizes = null;
249        int [][] blockMap = null;
250        int firstLine = 0;
251
252        if ((status & METHOD_NO_BLOCK_DATA) == 0)
253        {
254            // blockSizes must be set:
255
256            blockSizes = DataFactory.readIntArray (in);
257
258            if ((status & METHOD_NO_LINE_DATA) == 0)
259            {
260                // blockMap, lineMap, firstLine must be set:
261
262                final int length = in.readInt ();
263                blockMap = new int [length][];
264
265                for (int i = 0; i < length; ++ i)
266                {
267                    blockMap [i] = DataFactory.readIntArray (in);
268                }
269
270                firstLine = in.readInt ();
271
272                // [lineMap is transient data]
273            }
274        }
275
276        return new MethodDescriptor (name, descriptor, status, blockSizes, blockMap, firstLine);
277    }
278
279    static void writeExternal (final MethodDescriptor method, final DataOutput out)
280        throws IOException
281    {
282        out.writeUTF (method.m_name);
283        out.writeUTF (method.m_descriptor);
284
285        final int status = method.m_status;
286        out.writeInt (status);
287
288        if ((status & METHOD_NO_BLOCK_DATA) == 0)
289        {
290            // blockSizes must be set:
291
292            DataFactory.writeIntArray (method.m_blockSizes, out);
293
294            if ((status & METHOD_NO_LINE_DATA) == 0)
295            {
296                // blockMap, lineMap, firstLine must be set:
297
298                final int [][] blockMap = method.m_blockMap;
299                final int length = blockMap.length;
300                out.writeInt (length);
301
302                for (int i = 0; i < length; ++ i)
303                {
304                    DataFactory.writeIntArray (blockMap [i], out);
305                }
306
307                out.writeInt (method.m_firstLine);
308
309                // [lineMap is transient data]
310            }
311        }
312    }
313
314    // private: ...............................................................
315
316
317    private final String m_name; // internal JVM name (<init>, <clinit> for initializers, etc) [never null]
318    private final String m_descriptor; // [never null]
319    private final int m_status; // excluded, no debug data, etc
320    private final int [] m_blockSizes; // always of positive length if ((status & METHOD_NO_BLOCK_DATA) == 0)
321    private final int [][] m_blockMap; // [never null or empty if status is ...]
322    private final int m_firstLine; // 0 if not src line info is available
323
324    private IntObjectMap /* line no->int[](blockIDs) */  m_lineMap; // created lazily [could be empty if status ...]
325
326} // end of class
327// ----------------------------------------------------------------------------
328