001 package edu.rice.cs.cunit.classFile.code.instructions; 002 003 import edu.rice.cs.cunit.classFile.code.InstructionList; 004 import edu.rice.cs.cunit.classFile.code.Opcode; 005 006 import java.util.Hashtable; 007 import java.util.LinkedList; 008 import java.util.NoSuchElementException; 009 010 /** 011 * A table that relates PC values to line numbers. 012 * 013 * @author Mathias Ricken 014 */ 015 public class LineNumberTable { 016 /** 017 * Hash table to convert from PC to line numbers. 018 */ 019 Hashtable<Integer, Integer> _pcToLineNumber = new Hashtable<Integer, Integer>(); 020 021 /** 022 * Hash table to convert from line numbers to PCs. 023 */ 024 Hashtable<Integer, Integer> _lineNumberToPC = new Hashtable<Integer, Integer>(); 025 026 /** 027 * PC value just after the last instruction. 028 */ 029 int _maxPC; 030 031 /** 032 * Line number value just after the last instruction. 033 */ 034 int _maxLineNo; 035 036 /** 037 * Put this PC-line number pair into the hash tables. 038 * @param pc PC value 039 * @param lineNo line number 040 */ 041 protected void put(int pc, int lineNo) { 042 _pcToLineNumber.put(pc, lineNo); 043 _lineNumberToPC.put(lineNo, pc); 044 } 045 046 /** 047 * Create the line number table from bytecode. 048 * 049 * @param bytecode bytecode 050 */ 051 public LineNumberTable(byte[] bytecode) { 052 int pc = 0; 053 int lineNo = 0; 054 while(pc < bytecode.length) { 055 put(pc, lineNo); 056 pc += Opcode.getInstrSize(bytecode, pc); 057 ++lineNo; 058 } 059 if (pc != bytecode.length) { 060 throw new IllegalArgumentException("Invalid bytecode length"); 061 } 062 put(bytecode.length,lineNo); 063 } 064 065 /** 066 * Create a line number table from a list of instructions. 067 * 068 * @param instrList list of instructions 069 */ 070 public LineNumberTable(LinkedList<AInstruction> instrList) { 071 int pc = 0; 072 int lineNo = 0; 073 for(AInstruction i : instrList) { 074 put(pc, lineNo); 075 pc += i.getBytecodeLength(pc); 076 ++lineNo; 077 } 078 put(pc,instrList.size()); 079 } 080 081 /** 082 * Create a line number table from a list of instructions. 083 * 084 * @param ilist list of instructions 085 */ 086 public LineNumberTable(InstructionList ilist) { 087 int pc = 0; 088 if (ilist.getLength() > 0) { 089 InstructionList copy = new InstructionList(ilist); 090 copy.setIndex(0); 091 int lineNo = 0; 092 do { 093 put(pc, lineNo); 094 pc += copy.getInstr().getBytecodeLength(pc); 095 ++lineNo; 096 } while(copy.advanceIndex()); 097 } 098 put(pc,ilist.getLength()); 099 } 100 101 /** 102 * Get the line number from the PC. 103 * 104 * @param pc program counter 105 * 106 * @return line number 107 */ 108 public int getLineNumber(int pc) throws NoSuchElementException { 109 Integer i = _pcToLineNumber.get(pc); 110 if (i == null) { 111 throw new NoSuchElementException("No line number found for PC=" + pc); 112 } 113 return i; 114 } 115 116 /** 117 * Get the PC from the line number. 118 * 119 * @param lineNo line number 120 * 121 * @return program counter 122 */ 123 public int getPC(int lineNo) throws NoSuchElementException { 124 Integer i = _lineNumberToPC.get(lineNo); 125 if (i == null) { 126 throw new NoSuchElementException("No PC found for line no=" + lineNo); 127 } 128 return i; 129 } 130 }