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