summaryrefslogtreecommitdiff
path: root/sources/scalac/CompilationLoop.java
blob: 00cf6a576abb3cccff9fcbacc0b4e4ad9905721e (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
/*     ____ ____  ____ ____  ______                                     *\
**    / __// __ \/ __// __ \/ ____/    SOcos COmpiles Scala             **
**  __\_ \/ /_/ / /__/ /_/ /\_ \       (c) 2002, LAMP/EPFL              **
** /_____/\____/\___/\____/____/                                        **
\*                                                                      */

// $Id$

package scalac;

import java.util.ArrayList;

import scalac.util.Debug;

/** This class implements the scalac compilation loop. */
public class CompilationLoop {

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

    /** The global environment */
    private final Global global;
    /** The list of active phases */
    private final Phase[] phases;
    /** The units currently associated to each active phase. */
    private final ArrayList/*<CompilationUnit>*/[] unitss;
    /** The indexes of the first units that are not yet processed. */
    private final int[] indexes;

    //########################################################################
    // Private Constructors

    /** Initializes this instance. */
    public CompilationLoop(Global global) {
        this.global = global;
        int count = 0;
        for (Phase p = global.PHASE.INITIAL.phase(); p != null; p = p.next)
            count++;
        this.phases = new Phase[count];
        this.unitss = new ArrayList[count];
        this.indexes = new int[count];
        Phase phase = global.PHASE.INITIAL.phase();
        for (int i = 0; i < count; i++) {
            phases[i] = phase;
            unitss[i] = new ArrayList();
            phase = phase.next;
        }
    }

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

    /**
     * Compiles the given units and returns list of all compiled units
     * including those loaded automatically.
     */
    public CompilationUnit[] compile(CompilationUnit[] units) {
        for (int i = 0; i < units.length; i++) unitss[0].add(units[i]);
        int limit = phases.length - 1;
        loop(limit, true);
        return global.reporter.errors() != 0
            ? new CompilationUnit[0]
            : (CompilationUnit[])unitss[limit].toArray(
                new CompilationUnit[unitss[limit].size()]);
    }

    /**
     * Inserts the given unit and compiles it up to the current phase
     * excluded.
     */
    public void insert(CompilationUnit unit) {
        Phase backup = global.currentPhase;
        unitss[0].add(unit);
        // !!! This "false" may be dangerous; it may lead to crashes
        // because phases might work on trees/symbol-tables that
        // contain errors (however this occurs only if the newly
        // loaded code may contain errors). On the other hand "true",
        // might starve some phases; if a phase requests a compilation
        // unit, it may never receive it.
        loop(getPhaseIndex(backup), false);
        global.currentPhase = backup;
    }

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

    /** Compiles all units up to phase "phases[limit]" excluded. */
    private void loop(int limit, boolean main) {
        assert limit < phases.length: Debug.show(""+limit, phases);
        for (int current = getFirstNonEmptyIndex(); current < limit; ) {
            // !!! remove NAMER test ?
            if (main && global.reporter.errors() != 0
                && global.currentPhase != global.PHASE.NAMER.phase()) break;
            // Update the current phase pointer.
            Phase phase = global.currentPhase = phases[current];
            // If the phase is not yet started, start it.
            if (indexes[current] == 0) global.start();
            // Apply the phase to all available units. It's important
            // to not cache the result of "size()" as new units may be
            // added during the loop.
            for (int i = indexes[current]; i < unitss[current].size(); i++)
                phase.apply((CompilationUnit)unitss[current].get(i));
            int next = getFirstNonEmptyIndex();
            // If no new units were introduced, stop the phase.
            if (next == current) {
                PhaseDescriptor descriptor = phase.descriptor;
                global.stop(descriptor.taskDescription());
                CompilationUnit[] units =
                    (CompilationUnit[])unitss[current].toArray(
                        new CompilationUnit[unitss[current].size()]);
                if (descriptor.hasPrintFlag()) global.print(units);
                // if (descriptor.hasGraphFlag()); // !!!
                // if (descriptor.hasCheckFlag()); // !!!
                if (phase == global.PHASE.PARSER.phase()) global.fix1(units);
                if (phase == global.PHASE.ANALYZER.phase()) global.fix2(units);
                unitss[current + 1].addAll(unitss[current]);
                unitss[current].clear();
                next++;
            }
            indexes[current] = unitss[current].size();
            current = next;
        }
    }

    /**
     * Returns the first index with a non-empty unit list. If there is
     * no such index, returns "phases.length".
     */
    private int getFirstNonEmptyIndex() {
        int index = 0;
        while (index < phases.length && unitss[index].isEmpty()) index++;
        return index;
    }

    /** Returns the index of the given phase. */
    private int getPhaseIndex(Phase phase) {
        for (int index = 0; index < phases.length; index++)
            if (phases[index] == phase) return index;
        throw Debug.abort("phase not found", Debug.show(phase, phases));
    }

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