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.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                 // Include the header for C/C++ files (.c/.cc/.cpp)
183                 String name = file.getName();
184                 if (name.endsWith(".c") || name.endsWith(".cc") || name.endsWith(".cpp")) {
185                         newCode.addLine(CodeGeneratorUtils.Comment("Add the" + SpecNaming.CDSSpecGeneratedHeader + " header file"));
186                         newCode.addLine(CodeGeneratorUtils.IncludeHeader(SpecNaming.CDSSpecGeneratedHeader));
187                         newCode.addLine("");
188                 }
189
190                 try {
191                         br = new BufferedReader(new FileReader(file));
192                         lineReader = new LineNumberReader(br);
193                         while ((curLine = lineReader.readLine()) != null) {
194                                 lineNum = lineReader.getLineNumber();
195                                 InterfaceConstruct construct = null;
196                                 if ((construct = renamedLinesMap.get(lineNum)) != null) {
197                                         // Rename line
198                                         String newLine = construct.getFunctionHeader()
199                                                         .getRenamedFuncLine();
200                                         newCode.addLine(newLine);
201                                 } else {
202                                         // First add the current line
203                                         newCode.addLine(curLine);
204                                         // Then check if we need to add code
205                                         if (curAddition != null
206                                                         && lineNum == curAddition.insertingLine) {
207                                                 // Need to insert code
208                                                 newCode.addLines(curAddition.code);
209                                                 // Increase to the next code addition
210                                                 additionIdx++;
211                                                 curAddition = additionIdx == additions.codeAdditions
212                                                                 .size() ? null : additions.codeAdditions
213                                                                 .get(additionIdx);
214                                         }
215                                 }
216                         }
217                         // Write new code change to destination
218                         CodeGeneratorUtils.write2File(dest, newCode.lines);
219                         // System.out.println("/*************** " + file.getName()
220                         // + "  *************/");
221                         // System.out.println(newCode);
222                 } catch (FileNotFoundException e) {
223                         e.printStackTrace();
224                 } catch (IOException e) {
225                         e.printStackTrace();
226                 }
227         }
228
229         /**
230          * <p>
231          * This function is the main interface for the CodeGenerator class. After
232          * constructing a CodeGenerator object, users can call this function to
233          * complete the code generation and file writing process.
234          * </p>
235          */
236         public void generateCode() {
237                 // Extract all the code additions and line change
238                 getAllCodeChanges();
239
240                 // Generate the header file and CPP file
241                 Code generatedHeader = CodeGeneratorUtils
242                                 .GenerateCDSSpecHeaderFile(extractor);
243                 Code generatedCPP = CodeGeneratorUtils
244                                 .GenerateCDSSpecCPPFile(extractor);
245                 CodeGeneratorUtils
246                                 .write2File(generatedDir + SpecNaming.CDSSpecGeneratedName
247                                                 + ".h", generatedHeader.lines);
248                 CodeGeneratorUtils.write2File(generatedDir
249                                 + SpecNaming.CDSSpecGeneratedCPP, generatedCPP.lines);
250
251                 // Iterate over each file
252                 for (int i = 0; i < files.size(); i++) {
253                         File file = files.get(i);
254                         CodeAdditions additions = allAdditions.get(i);
255                         HashMap<Integer, InterfaceConstruct> renamedLinesMap = renamedLinesMapList
256                                         .get(i);
257                         writeCodeChange(file, additions, renamedLinesMap);
258                 }
259         }
260
261         /**
262          * <p>
263          * This is just a testing function that outputs the generated code, but not
264          * actually write them to the disk.
265          * </p>
266          */
267         private void testGenerator() {
268                 // Test code generation
269                 Code generatedHeader = CodeGeneratorUtils
270                                 .GenerateCDSSpecHeaderFile(extractor);
271                 Code generatedCPP = CodeGeneratorUtils
272                                 .GenerateCDSSpecCPPFile(extractor);
273
274                 System.out.println("/***** Generated header file *****/");
275                 System.out.println(generatedHeader);
276                 System.out.println("/***** Generated cpp file *****/");
277                 System.out.println(generatedCPP);
278
279                 for (File file : extractor.OPListMap.keySet()) {
280                         ArrayList<OPConstruct> list = extractor.OPListMap.get(file);
281                         for (OPConstruct con : list) {
282                                 Code code = CodeGeneratorUtils.Generate4OPConstruct(con);
283                                 System.out.println("/*****  *****/");
284                                 System.out.println(con.annotation);
285                                 System.out.println(code);
286                         }
287                 }
288
289                 for (File f : extractor.entryMap.keySet()) {
290                         EntryConstruct con = extractor.entryMap.get(f);
291                         System.out.println("/*****  *****/");
292                         System.out.println(con.annotation);
293                         System.out.println(CodeGeneratorUtils.Generate4Entry(con));
294                 }
295
296                 for (File file : extractor.interfaceListMap.keySet()) {
297                         ArrayList<InterfaceConstruct> list = extractor.interfaceListMap
298                                         .get(file);
299                         for (InterfaceConstruct con : list) {
300                                 Code code = CodeGeneratorUtils.GenerateInterfaceWrapper(con);
301                                 System.out.println("/***** Interface wrapper *****/");
302                                 System.out.println(con.getFunctionHeader().getHeaderLine());
303                                 System.out
304                                                 .println(con.getFunctionHeader().getRenamedFuncLine());
305                                 System.out.println(code);
306                         }
307                 }
308         }
309
310         public ArrayList<File> getSrcFiles(String dirName)
311                         throws FileNotFoundException {
312                 ArrayList<File> res = new ArrayList<File>();
313                 File dir = new File(dirName);
314                 if (dir.isDirectory() && dir.exists()) {
315                         for (String file : dir.list()) {
316                                 if (file.endsWith(".h") || file.endsWith(".c")
317                                                 || file.endsWith(".cc") || file.endsWith(".cpp")) {
318                                         res.add(new File(dir.getAbsolutePath() + "/" + file));
319                                 }
320                         }
321                 } else {
322                         throw new FileNotFoundException(dirName
323                                         + " is not a valid directory!");
324                 }
325                 return res;
326         }
327
328         static public void main(String[] args) {
329 //              String[] dirNames = { 
330 //                              Environment.REGISTER, 
331 //                              Environment.MS_QUEUE,
332 //                              Environment.LINUXRWLOCKS,
333 //                              Environment.MCS_LOCK,
334 //                              Environment.DEQUE, 
335 //                              Environment.TREIBER_STACK };
336                 String[] dirNames = args;
337
338                 for (int i = 0; i < dirNames.length; i++) {
339                         String dirName = dirNames[i];
340                         System.out.println("/**********   Generating CDSSpec files for " + dirName
341                                         + "    **********/");
342                         CodeGenerator generator = new CodeGenerator(dirName);
343                         generator.generateCode();
344                 }
345         }
346 }