001    package sysModel.classFile.code.instructions;
002    
003    import sysModel.classFile.code.InstructionList;
004    import sysModel.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((short)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 (0 < ilist.getLength()) {
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((short)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 (null == i) {
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 (null == i) {
126                throw new NoSuchElementException("No PC found for line no=" + lineNo);
127            }
128            return i;
129        }
130    }