| /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under |
| * the terms of the Common Public License v1.0 which accompanies this distribution, |
| * and is available at http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * $Id: MethodDescriptor.java,v 1.1.1.1.2.1 2004/07/10 03:34:52 vlad_r Exp $ |
| */ |
| package com.vladium.emma.data; |
| |
| import java.io.DataInput; |
| import java.io.DataOutput; |
| import java.io.IOException; |
| import java.io.Serializable; |
| |
| import com.vladium.util.IConstants; |
| import com.vladium.util.IntObjectMap; |
| import com.vladium.util.IntSet; |
| import com.vladium.util.asserts.$assert; |
| |
| // ---------------------------------------------------------------------------- |
| /** |
| * @author Vlad Roubtsov, (C) 2003 |
| */ |
| public |
| final class MethodDescriptor implements IConstants, IMetadataConstants, Serializable |
| { |
| // public: ................................................................ |
| |
| // need a separate 'blockCount' parm because 'blockMap' could be null |
| // and for a class that is never loaded I can't find out the number of |
| // blocks for block coverage reporting |
| |
| public MethodDescriptor (final String name, final String descriptor, final int status, |
| final int [] blockSizes, final int [][] blockMap, final int firstLine) |
| { |
| if (name == null) |
| throw new IllegalArgumentException ("null input: name"); |
| if (descriptor == null) |
| throw new IllegalArgumentException ("null input: descriptor"); |
| |
| if ((status & METHOD_NO_BLOCK_DATA) == 0) |
| { |
| // block metadata is available: blockCount must be positive |
| |
| final int blockCount = blockSizes.length; |
| |
| if ($assert.ENABLED) $assert.ASSERT (blockCount > 0, "blockCount must be positive: " + blockCount); |
| m_blockSizes = blockSizes; |
| |
| if ((status & METHOD_NO_LINE_DATA) == 0) |
| { |
| // line metadata is available: blockMap must not be null or empty |
| |
| if ($assert.ENABLED) $assert.ASSERT (firstLine > 0, "firstLine must be positive: " + firstLine); |
| |
| if ((blockMap == null) || (blockMap.length == 0)) |
| throw new IllegalArgumentException ("null or empty input: blockMap"); |
| |
| if ($assert.ENABLED) |
| { |
| $assert.ASSERT (blockCount == blockMap.length, "blockCount " + blockCount + " != blockMap.length " + blockMap.length); |
| |
| for (int i = 0; i < blockMap.length; ++ i) |
| { |
| $assert.ASSERT (blockMap [i] != null, "blockMap[" + i + "] is null"); |
| // note: it is legal for blockMap [i] to be empty |
| } |
| } |
| |
| m_blockMap = blockMap; |
| m_firstLine = firstLine; |
| } |
| else |
| { |
| m_blockMap = null; |
| m_firstLine = 0; |
| } |
| } |
| else |
| { |
| m_blockSizes = null; |
| m_blockMap = null; |
| m_firstLine = 0; |
| } |
| |
| m_name = name; |
| m_descriptor = descriptor; |
| m_status = status; |
| } |
| |
| |
| public String getName () |
| { |
| return m_name; |
| } |
| |
| public String getDescriptor () |
| { |
| return m_descriptor; |
| } |
| |
| public int getStatus () |
| { |
| return m_status; |
| } |
| |
| public int getBlockCount () |
| { |
| return m_blockSizes.length; |
| } |
| |
| public int [] getBlockSizes () |
| { |
| return m_blockSizes; |
| } |
| |
| public int [][] getBlockMap () |
| { |
| return m_blockMap; |
| } |
| |
| public IntObjectMap /* line no->int[](blockIDs) */ getLineMap () |
| { |
| IntObjectMap lineMap = m_lineMap; |
| if (lineMap != null) |
| return lineMap; |
| else if ((m_status & METHOD_NO_LINE_DATA) == 0) |
| { |
| // construct reverse line->block ID mapping: |
| |
| lineMap = new IntObjectMap (); |
| final int [][] blockMap = m_blockMap; |
| |
| for (int bl = 0, blCount = blockMap.length; bl < blCount; ++ bl) |
| { |
| final int [] lines = blockMap [bl]; |
| if (lines != null) |
| { |
| final int lineCount = lines.length; |
| |
| for (int l = 0; l < lineCount; ++ l) |
| { |
| final int line = lines [l]; |
| IntSet blockIDs = (IntSet) lineMap.get (line); |
| |
| if (blockIDs == null) |
| { |
| blockIDs = new IntSet (); |
| lineMap.put (line, blockIDs); |
| } |
| |
| blockIDs.add (bl); |
| } |
| } |
| } |
| |
| final int [] lines = lineMap.keys (); |
| for (int l = 0, lineCount = lines.length; l < lineCount; ++ l) |
| { |
| final int line = lines [l]; |
| final int [] blockIDs = ((IntSet) lineMap.get (line)).values (); |
| if ($assert.ENABLED) $assert.ASSERT (blockIDs != null && blockIDs.length > 0, "wrong line mapping for line #" + line); |
| |
| lineMap.put (line, blockIDs); // overwrite IntSet as the value |
| } |
| |
| m_lineMap = lineMap; |
| |
| return lineMap; |
| } |
| |
| return null; |
| } |
| |
| public int getFirstLine () |
| { |
| return m_firstLine; |
| } |
| |
| public boolean hasLineNumberInfo () |
| { |
| return (m_status & METHOD_NO_LINE_DATA) == 0; |
| } |
| |
| |
| public String toString () |
| { |
| return toString (""); |
| } |
| |
| public String toString (final String indent) |
| { |
| StringBuffer s = new StringBuffer (indent + "method [" + m_name + "] descriptor:"); |
| |
| if ((m_status & METHOD_NO_LINE_DATA) == 0) |
| { |
| for (int bl = 0; bl < m_blockMap.length; ++ bl) |
| { |
| s.append (EOL); |
| s.append (indent + INDENT_INCREMENT + "block " + bl + " (" + m_blockSizes [bl] + " instrs) : "); |
| |
| final int [] lines = m_blockMap [bl]; |
| for (int l = 0; l < lines.length; ++ l) |
| { |
| if (l != 0) s.append (", "); |
| s.append (lines [l]); |
| } |
| } |
| s.append (EOL); |
| s.append (indent + INDENT_INCREMENT + "---"); |
| |
| final int [] lines = m_lineMap.keys (); |
| for (int l = 0; l < lines.length; ++ l) |
| { |
| s.append (EOL); |
| s.append (indent + INDENT_INCREMENT + "line " + lines [l] + ": "); |
| |
| final int [] blocks = (int []) m_lineMap.get (lines [l]); |
| for (int bl = 0; bl < blocks.length; ++ bl) |
| { |
| if (bl != 0) s.append (", "); |
| s.append (blocks [bl]); |
| } |
| } |
| } |
| else |
| { |
| s.append (" <no line info>"); |
| } |
| |
| return s.toString (); |
| } |
| |
| // protected: ............................................................. |
| |
| // package: ............................................................... |
| |
| |
| static MethodDescriptor readExternal (final DataInput in) |
| throws IOException |
| { |
| final String name = in.readUTF (); |
| final String descriptor = in.readUTF (); |
| |
| final int status = in.readInt (); |
| |
| int [] blockSizes = null; |
| int [][] blockMap = null; |
| int firstLine = 0; |
| |
| if ((status & METHOD_NO_BLOCK_DATA) == 0) |
| { |
| // blockSizes must be set: |
| |
| blockSizes = DataFactory.readIntArray (in); |
| |
| if ((status & METHOD_NO_LINE_DATA) == 0) |
| { |
| // blockMap, lineMap, firstLine must be set: |
| |
| final int length = in.readInt (); |
| blockMap = new int [length][]; |
| |
| for (int i = 0; i < length; ++ i) |
| { |
| blockMap [i] = DataFactory.readIntArray (in); |
| } |
| |
| firstLine = in.readInt (); |
| |
| // [lineMap is transient data] |
| } |
| } |
| |
| return new MethodDescriptor (name, descriptor, status, blockSizes, blockMap, firstLine); |
| } |
| |
| static void writeExternal (final MethodDescriptor method, final DataOutput out) |
| throws IOException |
| { |
| out.writeUTF (method.m_name); |
| out.writeUTF (method.m_descriptor); |
| |
| final int status = method.m_status; |
| out.writeInt (status); |
| |
| if ((status & METHOD_NO_BLOCK_DATA) == 0) |
| { |
| // blockSizes must be set: |
| |
| DataFactory.writeIntArray (method.m_blockSizes, out); |
| |
| if ((status & METHOD_NO_LINE_DATA) == 0) |
| { |
| // blockMap, lineMap, firstLine must be set: |
| |
| final int [][] blockMap = method.m_blockMap; |
| final int length = blockMap.length; |
| out.writeInt (length); |
| |
| for (int i = 0; i < length; ++ i) |
| { |
| DataFactory.writeIntArray (blockMap [i], out); |
| } |
| |
| out.writeInt (method.m_firstLine); |
| |
| // [lineMap is transient data] |
| } |
| } |
| } |
| |
| // private: ............................................................... |
| |
| |
| private final String m_name; // internal JVM name (<init>, <clinit> for initializers, etc) [never null] |
| private final String m_descriptor; // [never null] |
| private final int m_status; // excluded, no debug data, etc |
| private final int [] m_blockSizes; // always of positive length if ((status & METHOD_NO_BLOCK_DATA) == 0) |
| private final int [][] m_blockMap; // [never null or empty if status is ...] |
| private final int m_firstLine; // 0 if not src line info is available |
| |
| private IntObjectMap /* line no->int[](blockIDs) */ m_lineMap; // created lazily [could be empty if status ...] |
| |
| } // end of class |
| // ---------------------------------------------------------------------------- |