summaryrefslogtreecommitdiff
path: root/src/fjbg/ch/epfl/lamp/fjbg/JLineNumberTableAttribute.java
blob: af7145939e0f93c789aa777575dee013f2064334 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/* FJBG -- Fast Java Bytecode Generator
 * Copyright 2002-2011 LAMP/EPFL
 * @author  Michel Schinz
 */

package ch.epfl.lamp.fjbg;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

/**
 * Attribute storing correspondance between instructions and source
 * line numbers.
 *
 * @author Michel Schinz
 * @version 1.0
 */

public class JLineNumberTableAttribute extends JAttribute {
    protected final JCode code;

    public JLineNumberTableAttribute(FJBGContext context,
                                     JClass clazz,
                                     JCode owner) {
        super(context, clazz);
        this.code = owner;

        assert owner.getOwner().getOwner() == clazz;
    }

    public JLineNumberTableAttribute(FJBGContext context,
                                     JClass clazz,
                                     Object owner,
                                     String name,
                                     int size,
                                     DataInputStream stream)
        throws IOException {
        super(context, clazz, name);
        this.code = (JCode)owner;

        int[] mapping = new int[code.getSize()];

        int count = stream.readShort();
        for (int i = 0; i < count; ++i) {
            int startPC = stream.readShort();
            int lineNum = stream.readShort();
            mapping[startPC] = lineNum;
        }

        // Avoids duplication of LineNumberTable attribute
        // (see method ensureLineNumberCapacity in class JCode).
        assert code.lineNumbers == null;
        code.lineNumbers = new int[0];

        int lineNum = 0;
        for (int pc = 0; pc < mapping.length; ++pc) {
            if (mapping[pc] != 0) lineNum = mapping[pc];
            if (lineNum != 0) code.setLineNumber(pc, lineNum);
        }

        assert name.equals(getName());
    }

    public String getName() { return "LineNumberTable"; }

    // Follows javap output format for LineNumberTable attribute.
    /*@Override*/ public String toString() {
        StringBuffer buf = new StringBuffer("  LineNumberTable: ");
        int[] encoding = encode();
        for (int i = 0; i < encoding.length/2; ++i) {
            buf.append("\n   line ");
            buf.append(encoding[i * 2 + 1]);
            buf.append(": ");
            buf.append(encoding[i * 2]);
        }
        buf.append("\n");
        return buf.toString();
    }

    protected int[] encoding;
    protected int[] encode() {
        if (encoding == null) {
            int[] lineNumbers = code.getLineNumbers();
            int[] preEncoding = new int[lineNumbers.length * 2];
            int prevLineNum = 0;

            int i = 0;
            for (int pc = 0; pc < lineNumbers.length; ++pc) {
                int lineNum = lineNumbers[pc];
                if (lineNum != 0 & lineNum != prevLineNum) {
                    preEncoding[i++] = pc;
                    preEncoding[i++] = lineNum;
                    prevLineNum = lineNum;
                }
            }
            if (i == preEncoding.length)
                encoding = preEncoding;
            else {
                encoding = new int[i];
                System.arraycopy(preEncoding, 0, encoding, 0, i);
            }
        }
        return encoding;
    }

    protected int getSize() {
        int[] encoding = encode();
        return 2 + encoding.length * 2;
    }

    protected void writeContentsTo(DataOutputStream stream) throws IOException {
        int[] encoding = encode();
        int entries = encoding.length / 2;
        stream.writeShort(entries);
        for (int i = 0; i < entries; ++i) {
            stream.writeShort(encoding[i * 2]);
            stream.writeShort(encoding[i * 2 + 1]);
        }
    }
}