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 */ 9 package com.vladium.emma.data; 10 11 import java.io.DataInput; 12 import java.io.DataOutput; 13 import java.io.IOException; 14 import java.io.Serializable; 15 16 import com.vladium.util.IConstants; 17 import com.vladium.util.IntObjectMap; 18 import com.vladium.util.IntSet; 19 import com.vladium.util.asserts.$assert; 20 21 // ---------------------------------------------------------------------------- 22 /** 23 * @author Vlad Roubtsov, (C) 2003 24 */ 25 public 26 final 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 MethodDescriptor(final String name, final String descriptor, final int status, final int [] blockSizes, final int [][] blockMap, final int firstLine)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 getName()93 public String getName () 94 { 95 return m_name; 96 } 97 getDescriptor()98 public String getDescriptor () 99 { 100 return m_descriptor; 101 } 102 getStatus()103 public int getStatus () 104 { 105 return m_status; 106 } 107 getBlockCount()108 public int getBlockCount () 109 { 110 return m_blockSizes.length; 111 } 112 getBlockSizes()113 public int [] getBlockSizes () 114 { 115 return m_blockSizes; 116 } 117 getBlockMap()118 public int [][] getBlockMap () 119 { 120 return m_blockMap; 121 } 122 getLineMap()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 getFirstLine()176 public int getFirstLine () 177 { 178 return m_firstLine; 179 } 180 hasLineNumberInfo()181 public boolean hasLineNumberInfo () 182 { 183 return (m_status & METHOD_NO_LINE_DATA) == 0; 184 } 185 186 toString()187 public String toString () 188 { 189 return toString (""); 190 } 191 toString(final String indent)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 readExternal(final DataInput in)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 writeExternal(final MethodDescriptor method, final DataOutput out)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