summaryrefslogtreecommitdiff
path: root/sources/scalac/framework/History.java
blob: ce0800358d5b544a25458ec6a65502db2df0a8b1 (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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/*     ____ ____  ____ ____  ______                                     *\
**    / __// __ \/ __// __ \/ ____/    SOcos COmpiles Scala             **
**  __\_ \/ /_/ / /__/ /_/ /\_ \       (c) 2002, LAMP/EPFL              **
** /_____/\____/\___/\____/____/                                        **
\*                                                                      */

// $Id$

package scalac.framework;

import scalac.Global;
import scalac.Phase;
import scalac.util.Debug;

/**
 * This class implements a value history. It stores phase dependent
 * values.
 *
 * Implementation: the value "values[n]" is valid from start of phase
 * "starts[n]" to end of phase "(n == 0 ? limit : starts[n-1].prev)"
 * and the promised value "next" is valid from start of phase
 * "limit.next".
 */
public class History {

    //########################################################################
    // Private Constants

    /** An empty array of objects */
    private static final Object[] NO_VALUES = new Object[0];

    /** An empty array of phases */
    private static final Phase[] NO_STARTS = new Phase[0];

    //########################################################################
    // Private Fields

    /** The global environment */
    private final Global global;

    /** The successive values of this history */
    private Object[] values;

    /** The start phases of the successive values */
    private Phase[] starts;

    /** The last known valid phase of the last value */
    private Phase limit;

    /** A promise of the next value (may be null) */
    private Promise next;

    //########################################################################
    // Public Constructors

    /** Initializes this instance with current phase as first one. */
    public History() {
        this(Global.instance);
    }

    /** Initializes this instance with current phase as first one. */
    public History(Global global) {
        this(global, global.currentPhase);
    }

    /** Initializes this instance with given first phase. */
    public History(Phase first) {
        this(first.global, first);
    }

    /** Initializes this instance with given first phase. */
    public History(Global global, Phase first) {
        this.global = global;
        reset(first);
    }

    //########################################################################
    // Public Methods

    /** Returns the first phase of this history. */
    public final Phase getFirstPhase() {
        return starts.length > 0 ? starts[starts.length - 1] : limit.next;
    }

    /** Returns the value at current phase. */
    public final Object getValue(Object owner) {
        return getValueAt(owner, getPhase());
    }

    /** Returns the value at next phase. */
    public final Object getNextValue(Object owner) {
        return getValueAt(owner, getNextPhase());
    }

    /** Returns the value at given phase. */
    public final Object getValueAt(Object owner, Phase phase) {
        assert phase != null: show(owner, phase);
        while (limit.id < phase.id) incrementLimit(owner);
        for (int i = 0; i < starts.length; i++)
            if (starts[i].id <= phase.id) return values[i];
        throw Debug.abort("prehistoric phase", show(owner, phase));
    }

    /** Sets the value at current phase. */
    public final History setValue(Object owner, Object value) {
        return setValueAt(owner, getPhase(), value);
    }

    /** Sets the value at next phase. */
    public final History setNextValue(Object owner, Object value) {
        return setValueAt(owner, getNextPhase(), value);
    }

    /** Sets the value at given phase. */
    public final History setValueAt(Object owner, Phase phase, Object value) {
        assert phase != null: show(owner, phase);
        assert phase == limit.next && next == null: show(owner, phase);
        if (values.length == 0 || values[0] != value) {
            this.values = append(value, values);
            this.starts = append(phase, starts);
        }
        this.limit = phase;
        return this;
    }

    /** Sets a value promise at current phase. */
    public final History setPromise(Object owner, Promise promise) {
        return setPromiseAt(owner, getPhase(), promise);
    }

    /** Sets a value promise at next phase. */
    public final History setNextPromise(Object owner, Promise promise) {
        return setPromiseAt(owner, getNextPhase(), promise);
    }

    /** Sets a value promise at given phase. */
    public final History setPromiseAt(Object owner, Phase phase,
        Promise promise)
    {
        assert phase != null: show(owner, phase);
        assert phase == limit.next && next == null: show(owner, phase);
        this.next = promise;
        return this;
    }

    /** Erases all values. */
    public final void reset() {
        reset(getFirstPhase());
    }

    /** Erases all values and sets first phase to given one. */
    public final void reset(Phase first) {
        assert first != null && first.prev != null: this + " - " + first;
        this.values = NO_VALUES;
        this.starts = NO_STARTS;
        this.limit = first.prev;
        this.next = null;
    }

    /** Returns a string representation of this history. */
    public String toString() {
        StringBuffer buffer = new StringBuffer("[");
        for (int i = 0; i < values.length; i++) {
            buffer.append(starts[i]).append(" -> ");
            buffer.append(Debug.show(values[i])).append(", ");
        }
        buffer.append(limit);
        if (next != null) buffer.append(" - ").append(next);
        return buffer.append("]").toString();
    }

    //########################################################################
    // Protected Methods

    /**
     * Computes the value at phase following given one by transforming
     * the given value (which is the value at given phase). The
     * default implementation changes the current phase to given one
     * and then forwards the call to method "transformValue".
     */
    protected Object transformValueAt(Object owner, Phase phase, Object value){
        Phase current = global.currentPhase;
        global.currentPhase = phase;
        Object result = transformValue(owner, value);
        global.currentPhase = current;
        return result;
    }

    /**
     * Computes the value at next phase by transforming the given
     * value (which is the value at current phase). The default
     * implementation forwards the call to method "computeValueAt".
     */
    protected Object transformValue(Object owner, Object value) {
        return computeValueAt(owner, global.currentPhase.next);
    }

    /**
     * Computes the value at given phase. The default implementation
     * changes the current phase to given one and then forwards the
     * call to method "computeValue".
     */
    protected Object computeValueAt(Object owner, Phase phase) {
        Phase current = global.currentPhase;
        global.currentPhase = phase;
        Object result = computeValue(owner);
        global.currentPhase = current;
        return result;
    }

    /**
     * Computes the value at current phase. The default implementation
     * throws an exception.
     */
    protected Object computeValue(Object owner) {
        throw Debug.abort("undefined value", show(owner, getPhase()));
    }

    //########################################################################
    // Private Methods

    /** Returns the current phase. */
    private Phase getPhase() {
        return global.currentPhase;
    }

    /** Returns the next phase. */
    private Phase getNextPhase() {
        return getPhase().next;
    }

    /** Increments the limit of this history. */
    private void incrementLimit(Object owner) {
        Phase phase = limit;
        Object value;
        if (next != null) {
            value = next.forceAt(owner, global, phase.next);
            if (limit == phase) next = null;
        } else if (values.length > 0) {
            value = transformValueAt(owner, phase, values[0]);
        } else {
            value = computeValueAt(owner, phase.next);
        }
        if (limit == phase) setValueAt(owner, phase.next, value);
    }

    /** Returns a string of this history and given owner. */
    private String show(Object owner) {
        return this + " @ " + Debug.show(owner);
    }

    /** Returns a string of this history and given owner and phase. */
    private String show(Object owner, Phase phase) {
        return show(owner) + " - " + phase;
    }

    //########################################################################
    // Private Functions

    /** Returns the concatenation of given values. */
    private static Object[] append(Object value, Object[] values) {
        Object[] array = new Object[1 + values.length];
        array[0] = value;
        for (int i = 1; i < array.length; i++) array[i] = values[i - 1];
        return array;
    }

    /** Returns the concatenation of given phases. */
    private static Phase[] append(Phase phase, Phase[] phases) {
        Phase[] array = new Phase[1 + phases.length];
        array[0] = phase;
        for (int i = 1; i < array.length; i++) array[i] = phases[i - 1];
        return array;
    }

    //########################################################################
}