e727f0b7ba51de2d415492f4c292f8e9b8e8aa6e
[cdsspec-compiler.git] / src / edu / uci / eecs / codeGenerator / CodeGenerator.java
1 package edu.uci.eecs.codeGenerator;
2
3 import java.io.BufferedReader;
4 import java.io.File;
5 import java.io.FileNotFoundException;
6 import java.io.FileReader;
7 import java.io.IOException;
8 import java.io.LineNumberReader;
9 import java.util.ArrayList;
10 import java.util.Collections;
11 import java.util.HashMap;
12
13 import edu.uci.eecs.codeGenerator.CodeAdditions.CodeAddition;
14 import edu.uci.eecs.specExtraction.Code;
15 import edu.uci.eecs.specExtraction.Construct;
16 import edu.uci.eecs.specExtraction.DefineConstruct;
17 import edu.uci.eecs.specExtraction.EntryConstruct;
18 import edu.uci.eecs.specExtraction.InterfaceConstruct;
19 import edu.uci.eecs.specExtraction.OPConstruct;
20 import edu.uci.eecs.specExtraction.SpecExtractor;
21 import edu.uci.eecs.specExtraction.SpecNaming;
22 import edu.uci.eecs.specExtraction.WrongAnnotationException;
23 import edu.uci.eecs.utilParser.ParseException;
24
25 /**
26  * <p>
27  * This class represents the engine to generate instrumented code. To construct
28  * an object of this file, users need provide a string that represents the
29  * sub-directory under the benchmarks directory, then the engine will explore
30  * all the C/C++ files that ends with ".cc/.cpp/.c/.h" and extract specification
31  * annotations and generate instrumented code in the generated directory.
32  * </p>
33  * 
34  * @author Peizhao Ou
35  * 
36  */
37 public class CodeGenerator {
38         // Files that we need to process
39         private ArrayList<File> files;
40
41         // Code addition list --- per file
42         private ArrayList<CodeAdditions> allAdditions;
43         // Line change map list --- per file; Each map represents the
44         // line->InterfaceConstruct mapping that will rename the interface
45         // declaration line.
46         private ArrayList<HashMap<Integer, InterfaceConstruct>> renamedLinesMapList;
47
48         // The string that users provide as a sub-directory in the benchmarks
49         // directory: e.g. ms-queue
50         public final String dirName;
51
52         // The original directory --- the benchmarks directory: e.g.
53         // ~/model-checker/benchmarks/
54         public final String originalDir;
55         // The directory for generated files: e.g. ~/model-checker/test-cdsspec/
56         public final String generatedDir;
57
58         // The specification annotation extractor
59         private SpecExtractor extractor;
60
61         public CodeGenerator(String originalDir, String generatedDir, String dirName) {
62                 this.dirName = dirName;
63                 this.originalDir = originalDir + "/" + dirName + "/";
64                 this.generatedDir = generatedDir + "/" + dirName + "/";
65                 
66                 //this.originalDir = Environment.BenchmarksDir + dirName + "/";
67                 //this.generatedDir = Environment.GeneratedFilesDir + dirName + "/";
68                 
69                 try {
70                         files = this.getSrcFiles(this.originalDir);
71                         System.out.println("Original benchmarks directory: " + this.originalDir);
72                         System.out.println("Generated benchmark directory: " + this.generatedDir);
73                         System.out.println("The specific benchmark directory: " + this.dirName);
74                         for (int i = 0; i < files.size(); i++) {
75                                 System.out.println("    Processing: " + files.get(i));
76                         }
77                 } catch (FileNotFoundException e) {
78                         e.printStackTrace();
79                 }
80                 extractor = new SpecExtractor();
81                 try {
82                         extractor.extract(files);
83                 } catch (WrongAnnotationException e) {
84                         e.printStackTrace();
85                 } catch (ParseException e) {
86                         e.printStackTrace();
87                 }
88         }
89
90         /**
91          * <p>
92          * This function initializes the list of code additions and line changes for
93          * all the files. For the code additions of a file, we sort them in an
94          * increasing order by the inserting line number.
95          * </p>
96          * 
97          */
98         private void getAllCodeChanges() {
99                 allAdditions = new ArrayList<CodeAdditions>();
100                 renamedLinesMapList = new ArrayList<HashMap<Integer, InterfaceConstruct>>();
101                 for (int i = 0; i < files.size(); i++) {
102                         File file = files.get(i);
103                         // One CodeAdditions per file
104                         CodeAdditions additions = new CodeAdditions(file);
105                         // Add the additions for this file to the list
106                         allAdditions.add(additions);
107
108                         // One CodeChange per file
109                         HashMap<Integer, InterfaceConstruct> renamedLinesMap = new HashMap<Integer, InterfaceConstruct>();
110                         // Add it the the list
111                         renamedLinesMapList.add(renamedLinesMap);
112
113                         // Extract all the additions
114                         ArrayList<OPConstruct> OPList = extractor.OPListMap.get(file);
115                         EntryConstruct entry = extractor.entryMap.get(file);
116                         ArrayList<DefineConstruct> defineList = extractor.defineListMap
117                                         .get(file);
118                         ArrayList<InterfaceConstruct> interfaceList = extractor.interfaceListMap
119                                         .get(file);
120                         Code code = null;
121                         CodeAddition addition = null;
122                         // For ordering point constructs
123                         if (OPList != null) {
124                                 for (OPConstruct con : OPList) {
125                                         code = CodeGeneratorUtils.Generate4OPConstruct(con);
126                                         addition = new CodeAddition(con.beginLineNum, code);
127                                         additions.addCodeAddition(addition);
128                                 }
129                         }
130                         // For entry constructs
131                         if (entry != null) {
132                                 code = CodeGeneratorUtils.Generate4Entry(entry);
133                                 addition = new CodeAddition(entry.beginLineNum, code);
134                                 additions.addCodeAddition(addition);
135                         }
136                         // For define constructs
137                         if (defineList != null) {
138                                 for (DefineConstruct con : defineList) {
139                                         code = CodeGeneratorUtils.Generate4Define(con);
140                                         addition = new CodeAddition(con.endLineNum, code);
141                                         additions.addCodeAddition(addition);
142                                 }
143                         }
144                         // For interface constructs
145                         if (interfaceList != null) {
146                                 for (InterfaceConstruct con : interfaceList) {
147                                         code = CodeGeneratorUtils.GenerateInterfaceWrapper(con);
148                                         addition = new CodeAddition(con.getEndLineNumFunction(),
149                                                         code);
150                                         additions.addCodeAddition(addition);
151                                         // Record the line to be changed
152                                         renamedLinesMap.put(con.endLineNum + 1, con);
153                                 }
154                         }
155
156                         // Sort additions by line number increasingly
157                         additions.sort();
158                 }
159         }
160
161         /**
162          * <p>
163          * For a specific file, given the code additions and line changes mapping
164          * for that file, this function will generate the new code for that file and
165          * write it back to the destination directory.
166          * </p>
167          * 
168          * @param file
169          *            The file to be processed
170          * @param additions
171          *            The sorted code additions for the file
172          * @param renamedLinesMap
173          *            The line change mapping for the file
174          */
175         private void writeCodeChange(File file, CodeAdditions additions,
176                         HashMap<Integer, InterfaceConstruct> renamedLinesMap) {
177                 Code newCode = new Code();
178                 BufferedReader br = null;
179                 LineNumberReader lineReader = null;
180                 int lineNum = 0;
181                 String curLine = null;
182
183                 String dest = generatedDir + file.getName();
184                 CodeAddition curAddition = null;
185                 int additionIdx = -1;
186                 if (!additions.isEmpty()) {
187                         additionIdx = 0;
188                         curAddition = additions.codeAdditions.get(0);
189                 }
190
191                 // Include the header for C/C++ files (.c/.cc/.cpp)
192                 String name = file.getName();
193                 if (name.endsWith(".c") || name.endsWith(".cc")
194                                 || name.endsWith(".cpp")) {
195                         newCode.addLine(CodeGeneratorUtils.Comment("Add the"
196                                         + SpecNaming.CDSSpecGeneratedHeader + " header file"));
197                         newCode.addLine(CodeGeneratorUtils
198                                         .IncludeHeader(SpecNaming.CDSSpecGeneratedHeader));
199                         newCode.addLine("");
200                 }
201
202                 try {
203                         br = new BufferedReader(new FileReader(file));
204                         lineReader = new LineNumberReader(br);
205                         while ((curLine = lineReader.readLine()) != null) {
206                                 lineNum = lineReader.getLineNumber();
207                                 InterfaceConstruct construct = null;
208                                 if ((construct = renamedLinesMap.get(lineNum)) != null) {
209                                         // Rename line
210                                         String newLine = construct.getFunctionHeader()
211                                                         .getRenamedFuncLine();
212                                         newCode.addLine(newLine);
213                                 } else {
214                                         // First add the current line
215                                         newCode.addLine(curLine);
216                                         // Then check if we need to add code
217                                         if (curAddition != null
218                                                         && lineNum == curAddition.insertingLine) {
219                                                 // Need to insert code
220                                                 newCode.addLines(curAddition.code);
221                                                 // Increase to the next code addition
222                                                 additionIdx++;
223                                                 curAddition = additionIdx == additions.codeAdditions
224                                                                 .size() ? null : additions.codeAdditions
225                                                                 .get(additionIdx);
226                                         }
227                                 }
228                         }
229                         // Write new code change to destination
230                         CodeGeneratorUtils.write2File(dest, newCode.lines);
231                         // System.out.println("/*************** " + file.getName()
232                         // + "  *************/");
233                         // System.out.println(newCode);
234                 } catch (FileNotFoundException e) {
235                         e.printStackTrace();
236                 } catch (IOException e) {
237                         e.printStackTrace();
238                 }
239         }
240
241         /**
242          * <p>
243          * This function is the main interface for the CodeGenerator class. After
244          * constructing a CodeGenerator object, users can call this function to
245          * complete the code generation and file writing process.
246          * </p>
247          */
248         public void generateCode() {
249                 // Extract all the code additions and line change
250                 getAllCodeChanges();
251
252                 // Generate the header file and CPP file
253                 Code generatedHeader = CodeGeneratorUtils
254                                 .GenerateCDSSpecHeaderFile(extractor);
255                 Code generatedCPP = CodeGeneratorUtils
256                                 .GenerateCDSSpecCPPFile(extractor);
257                 CodeGeneratorUtils
258                                 .write2File(generatedDir + SpecNaming.CDSSpecGeneratedName
259                                                 + ".h", generatedHeader.lines);
260                 CodeGeneratorUtils.write2File(generatedDir
261                                 + SpecNaming.CDSSpecGeneratedCPP, generatedCPP.lines);
262
263                 // Iterate over each file
264                 for (int i = 0; i < files.size(); i++) {
265                         File file = files.get(i);
266                         CodeAdditions additions = allAdditions.get(i);
267                         HashMap<Integer, InterfaceConstruct> renamedLinesMap = renamedLinesMapList
268                                         .get(i);
269                         writeCodeChange(file, additions, renamedLinesMap);
270                 }
271         }
272
273         /**
274          * <p>
275          * This is just a testing function that outputs the generated code, but not
276          * actually write them to the disk.
277          * </p>
278          */
279         private void testGenerator() {
280                 // Test code generation
281                 Code generatedHeader = CodeGeneratorUtils
282                                 .GenerateCDSSpecHeaderFile(extractor);
283                 Code generatedCPP = CodeGeneratorUtils
284                                 .GenerateCDSSpecCPPFile(extractor);
285
286                 System.out.println("/***** Generated header file *****/");
287                 System.out.println(generatedHeader);
288                 System.out.println("/***** Generated cpp file *****/");
289                 System.out.println(generatedCPP);
290
291                 for (File file : extractor.OPListMap.keySet()) {
292                         ArrayList<OPConstruct> list = extractor.OPListMap.get(file);
293                         for (OPConstruct con : list) {
294                                 Code code = CodeGeneratorUtils.Generate4OPConstruct(con);
295                                 System.out.println("/*****  *****/");
296                                 System.out.println(con.annotation);
297                                 System.out.println(code);
298                         }
299                 }
300
301                 for (File f : extractor.entryMap.keySet()) {
302                         EntryConstruct con = extractor.entryMap.get(f);
303                         System.out.println("/*****  *****/");
304                         System.out.println(con.annotation);
305                         System.out.println(CodeGeneratorUtils.Generate4Entry(con));
306                 }
307
308                 for (File file : extractor.interfaceListMap.keySet()) {
309                         ArrayList<InterfaceConstruct> list = extractor.interfaceListMap
310                                         .get(file);
311                         for (InterfaceConstruct con : list) {
312                                 Code code = CodeGeneratorUtils.GenerateInterfaceWrapper(con);
313                                 System.out.println("/***** Interface wrapper *****/");
314                                 System.out.println(con.getFunctionHeader().getHeaderLine());
315                                 System.out
316                                                 .println(con.getFunctionHeader().getRenamedFuncLine());
317                                 System.out.println(code);
318                         }
319                 }
320         }
321
322         public ArrayList<File> getSrcFiles(String dirName)
323                         throws FileNotFoundException {
324                 ArrayList<File> res = new ArrayList<File>();
325                 File dir = new File(dirName);
326                 if (dir.isDirectory() && dir.exists()) {
327                         for (String file : dir.list()) {
328                                 if (file.endsWith(".h") || file.endsWith(".c")
329                                                 || file.endsWith(".cc") || file.endsWith(".cpp")) {
330                                         res.add(new File(dir.getAbsolutePath() + "/" + file));
331                                 }
332                         }
333                 } else {
334                         throw new FileNotFoundException(dirName
335                                         + " is not a valid directory!");
336                 }
337                 return res;
338         }
339
340         static public void main(String[] args) {
341                 if (args.length < 3) {
342                         System.out
343                                         .println("Usage: CodeGenerator <Benchmarks-directory> <Directory-for-generated-files> <specific-benchmark-lists...>");
344                         return;
345                 }
346                 
347                 // String[] dirNames = args;
348                 
349 //              String[] dirNames = new String[args.length - 2];
350 //              for (int i = 0; i < args.length - 2; i++) {
351 //                      dirNames[i] = args[i + 2];
352 //              }
353                 String[] dirNames = Environment.Benchmarks;
354
355                 for (int i = 0; i < dirNames.length; i++) {
356                         String dirName = dirNames[i];
357                         System.out.println("/**********   Generating CDSSpec files for "
358                                         + dirName + "    **********/");
359 //                      CodeGenerator generator = new CodeGenerator(Environment.BenchmarksDir, Environment.GeneratedFilesDir, dirName);
360                         CodeGenerator generator = new CodeGenerator(args[0], args[1], dirName);
361                         generator.generateCode();
362                 }
363         }
364 }