1   // Copyright 2012- Bill Campbell, Swami Iyer and Bahar Akbal-Delibas
2   
3   package jminusminus;
4   
5   import java.util.ArrayList;
6   
7   /**
8    * The abstract syntax tree (AST) node representing a compilation unit, and so the root of the AST.
9    * <p>
10   * The AST is produced by the Parser. Once the AST has been built, three successive methods are
11   * invoked:
12   * <ol>
13   *   <li>Method preAnalyze() is invoked for making a first pass at type analysis, recursively
14   *   reaching down to the member headers for declaring types and member interfaces in the
15   *   environment (contexts). preAnalyze() also creates a partial class file (in memory) for
16   *   recording member header information.</li>
17   *
18   *   <li>Method analyze() is invoked for type-checking field initializations and method bodies,
19   *   and determining the types of all expressions. A certain amount of tree surgery is also done
20   *   here. And stack frame offsets are computed for method parameters and local variables.</li>
21   *
22   *   <li>Method codegen() is invoked for generating code for the compilation unit to a class file.
23   *   For each type declaration, it instantiates a CLEmitter object (an abstraction of the class
24   *   file) and then invokes methods on that CLEmitter for generating instructions. At the end of
25   *   each type declaration, a method is invoked on the CLEmitter which writes the class out to
26   *   the file system either as .class file or as a .s (SPIM) file. Of course, codegen() makes
27   *   recursive calls down the tree, to the {@code codegen} methods at each node, for generating
28   *   the appropriate instructions.</li>
29   * </ol>
30   */
31  class JCompilationUnit extends JAST {
32      // Name of the source file.
33      private String fileName;
34  
35      // Package name.
36      private TypeName packageName;
37  
38      // List of imports.
39      private ArrayList<TypeName> imports;
40  
41      // List of type declarations.
42      private ArrayList<JAST> typeDeclarations;
43  
44      // List of CLFile objects corresponding to the type declarations in this compilation unit.
45      private ArrayList<CLFile> clFiles;
46  
47      // For imports and type declarations.
48      private CompilationUnitContext context;
49  
50      // Whether a semantic error has been found.
51      private boolean isInError;
52  
53      /**
54       * Constructs an AST node for a compilation unit.
55       *
56       * @param fileName         the name of the source file.
57       * @param line             line in which the compilation unit occurs in the source file.
58       * @param packageName      package name.
59       * @param imports          a list of imports.
60       * @param typeDeclarations type declarations.
61       */
62      public JCompilationUnit(String fileName, int line, TypeName packageName,
63                              ArrayList<TypeName> imports, ArrayList<JAST> typeDeclarations) {
64          super(line);
65          this.fileName = fileName;
66          this.packageName = packageName;
67          this.imports = imports;
68          this.typeDeclarations = typeDeclarations;
69          clFiles = new ArrayList<CLFile>();
70          compilationUnit = this;
71      }
72  
73      /**
74       * Returns the package in which this compilation unit is defined.
75       *
76       * @return the package in which this compilation unit is defined.
77       */
78      public String packageName() {
79          return packageName == null ? "" : packageName.toString().replace(".", "/");
80      }
81  
82      /**
83       * Returns the list of CLFile objects corresponding to the type declarations in this
84       * compilation unit.
85       *
86       * @return the list of CLFile objects corresponding to the type declarations in this
87       * compilation unit.
88       */
89      public ArrayList<CLFile> clFiles() {
90          return clFiles;
91      }
92  
93      /**
94       * Returns true if a semantic error has occurred up to now, and false otherwise.
95       *
96       * @return true if a semantic error has occurred up to now, and false otherwise..
97       */
98      public boolean errorHasOccurred() {
99          return isInError;
100     }
101 
102     /**
103      * Reports a semantic error.
104      *
105      * @param line      line in which the error occurred in the source file.
106      * @param message   message identifying the error.
107      * @param arguments related values.
108      */
109     public void reportSemanticError(int line, String message, Object... arguments) {
110         isInError = true;
111         System.err.printf("%s:%d: error: ", fileName, line);
112         System.err.printf(message, arguments);
113         System.err.println();
114     }
115 
116     /**
117      * Constructs a context for the compilation unit, initializing it with imported types. Then
118      * pre-analyzes the unit's type declarations, adding their types to the context.
119      */
120     public void preAnalyze() {
121         context = new CompilationUnitContext();
122 
123         // Declare the two implicit types java.lang.Object and java.lang.String.
124         context.addType(0, Type.OBJECT);
125         context.addType(0, Type.STRING);
126 
127         // Declare any imported types.
128         for (TypeName imported : imports) {
129             try {
130                 Class<?> classRep = Class.forName(imported.toString());
131                 context.addType(imported.line(), Type.typeFor(classRep));
132             } catch (Exception e) {
133                 JAST.compilationUnit.reportSemanticError(imported.line(), "Unable to find %s",
134                         imported.toString());
135             }
136         }
137 
138         // Declare the locally declared type(s).
139         CLEmitter.initializeByteClassLoader();
140         for (JAST typeDeclaration : typeDeclarations) {
141             ((JTypeDecl) typeDeclaration).declareThisType(context);
142         }
143 
144         // Pre-analyze the locally declared type(s). Generate (partial) Class instances,
145         // reflecting only the member declaration information.
146         CLEmitter.initializeByteClassLoader();
147         for (JAST typeDeclaration : typeDeclarations) {
148             ((JTypeDecl) typeDeclaration).preAnalyze(context);
149         }
150     }
151 
152     /**
153      * {@inheritDoc}
154      */
155     public JAST analyze(Context context) {
156         for (JAST typeDeclaration : typeDeclarations) {
157             typeDeclaration.analyze(this.context);
158         }
159         return this;
160     }
161 
162     /**
163      * {@inheritDoc}
164      */
165     public void codegen(CLEmitter output) {
166         for (JAST typeDeclaration : typeDeclarations) {
167             typeDeclaration.codegen(output);
168             output.write();
169             clFiles.add(output.clFile());
170         }
171     }
172 
173     /**
174      * {@inheritDoc}
175      */
176     public void toJSON(JSONElement json) {
177         JSONElement e = new JSONElement();
178         json.addChild("JCompilationUnit:" + line, e);
179         e.addAttribute("source", fileName);
180         if (packageName != null) {
181             e.addAttribute("package", packageName());
182         }
183         if (imports != null) {
184             ArrayList<String> value = new ArrayList<String>();
185             for (TypeName imported : imports) {
186                 value.add(String.format("\"%s\"", imported.toString()));
187             }
188             e.addAttribute("imports", value);
189         }
190         if (context != null) {
191             context.toJSON(e);
192         }
193         if (typeDeclarations != null) {
194             for (JAST typeDeclaration : typeDeclarations) {
195                 typeDeclaration.toJSON(e);
196             }
197         }
198     }
199 }
200