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