1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <iostream>
18 #include <sstream>
19 
20 #include "Generator.h"
21 #include "Specification.h"
22 #include "Utilities.h"
23 
24 using namespace std;
25 
26 // Convert a file name into a string that can be used to guard the include file with #ifdef...
makeGuardString(const string & filename)27 static string makeGuardString(const string& filename) {
28     string s;
29     s.resize(15 + filename.size());
30     s = "RENDERSCRIPT_";
31     for (char c : filename) {
32         if (c == '.') {
33             s += '_';
34         } else {
35             s += toupper(c);
36         }
37     }
38     return s;
39 }
40 
41 /* Write #ifdef's that ensure that the specified version is present.  If we're at the final version,
42  * add a check on a flag that can be set for internal builds.  This enables us to keep supporting
43  * old APIs in the runtime code.
44  */
writeVersionGuardStart(GeneratedFile * file,VersionInfo info,unsigned int finalVersion)45 static void writeVersionGuardStart(GeneratedFile* file, VersionInfo info, unsigned int finalVersion) {
46     if (info.intSize == 32) {
47         *file << "#ifndef __LP64__\n";
48     } else if (info.intSize == 64) {
49         *file << "#ifdef __LP64__\n";
50     }
51 
52     ostringstream checkMaxVersion;
53     if (info.maxVersion > 0) {
54         checkMaxVersion << "(";
55         if (info.maxVersion == finalVersion) {
56             checkMaxVersion << "defined(RS_DECLARE_EXPIRED_APIS) || ";
57         }
58         checkMaxVersion << "RS_VERSION <= " << info.maxVersion << ")";
59     }
60 
61     if (info.minVersion <= 1) {
62         // No minimum
63         if (info.maxVersion > 0) {
64             *file << "#if !defined(RS_VERSION) || " << checkMaxVersion.str() << "\n";
65         }
66     } else {
67         *file << "#if (defined(RS_VERSION) && (RS_VERSION >= " << info.minVersion << ")";
68         if (info.maxVersion > 0) {
69             *file << " && " << checkMaxVersion.str();
70         }
71         *file << ")\n";
72     }
73 }
74 
writeVersionGuardEnd(GeneratedFile * file,VersionInfo info)75 static void writeVersionGuardEnd(GeneratedFile* file, VersionInfo info) {
76     if (info.minVersion > 1 || info.maxVersion != 0) {
77         *file << "#endif\n";
78     }
79     if (info.intSize != 0) {
80         *file << "#endif\n";
81     }
82 }
83 
writeComment(GeneratedFile * file,const string & name,const string & briefComment,const vector<string> & comment,bool addDeprecatedWarning,bool closeBlock)84 static void writeComment(GeneratedFile* file, const string& name, const string& briefComment,
85                          const vector<string>& comment, bool addDeprecatedWarning,
86                          bool closeBlock) {
87     if (briefComment.empty() && comment.size() == 0) {
88         return;
89     }
90     *file << "/*\n";
91     if (!briefComment.empty()) {
92         *file << " * " << name << ": " << briefComment << "\n";
93         *file << " *\n";
94     }
95     if (addDeprecatedWarning) {
96         *file << " * DEPRECATED.  Do not use.\n";
97         *file << " *\n";
98     }
99     for (size_t ct = 0; ct < comment.size(); ct++) {
100         string s = stripHtml(comment[ct]);
101         s = stringReplace(s, "@", "");
102         if (!s.empty()) {
103             *file << " * " << s << "\n";
104         } else {
105             *file << " *\n";
106         }
107     }
108     if (closeBlock) {
109         *file << " */\n";
110     }
111 }
112 
writeConstantComment(GeneratedFile * file,const Constant & constant)113 static void writeConstantComment(GeneratedFile* file, const Constant& constant) {
114     const string name = constant.getName();
115     writeComment(file, name, constant.getSummary(), constant.getDescription(),
116                  constant.deprecated(), true);
117 }
118 
writeConstantSpecification(GeneratedFile * file,const ConstantSpecification & spec)119 static void writeConstantSpecification(GeneratedFile* file, const ConstantSpecification& spec) {
120     const Constant* constant = spec.getConstant();
121     VersionInfo info = spec.getVersionInfo();
122     writeVersionGuardStart(file, info, constant->getFinalVersion());
123     *file << "static const " << spec.getType() << " " << constant->getName()
124           << " = " << spec.getValue() << ";\n\n";
125     writeVersionGuardEnd(file, info);
126 }
127 
writeTypeSpecification(GeneratedFile * file,const TypeSpecification & spec)128 static void writeTypeSpecification(GeneratedFile* file, const TypeSpecification& spec) {
129     const Type* type = spec.getType();
130     const string& typeName = type->getName();
131     const VersionInfo info = spec.getVersionInfo();
132     writeVersionGuardStart(file, info, type->getFinalVersion());
133 
134     const string attribute =
135                 makeAttributeTag(spec.getAttribute(), "", type->getDeprecatedApiLevel(),
136                                  type->getDeprecatedMessage());
137     *file << "typedef ";
138     switch (spec.getKind()) {
139         case SIMPLE:
140             *file << spec.getSimpleType() << attribute;
141             break;
142         case RS_OBJECT:
143             *file << "struct " << typeName << " _RS_OBJECT_DECL" << attribute;
144             break;
145         case ENUM: {
146             *file << "enum" << attribute << " ";
147             const string name = spec.getEnumName();
148             if (!name.empty()) {
149                 *file << name << " ";
150             }
151             *file << "{\n";
152 
153             const vector<string>& values = spec.getValues();
154             const vector<string>& valueComments = spec.getValueComments();
155             const size_t last = values.size() - 1;
156             for (size_t i = 0; i <= last; i++) {
157                 *file << "    " << values[i];
158                 if (i != last) {
159                     *file << ",";
160                 }
161                 if (valueComments.size() > i && !valueComments[i].empty()) {
162                     *file << " // " << valueComments[i];
163                 }
164                 *file << "\n";
165             }
166             *file << "}";
167             break;
168         }
169         case STRUCT: {
170             *file << "struct" << attribute << " ";
171             const string name = spec.getStructName();
172             if (!name.empty()) {
173                 *file << name << " ";
174             }
175             *file << "{\n";
176 
177             const vector<string>& fields = spec.getFields();
178             const vector<string>& fieldComments = spec.getFieldComments();
179             for (size_t i = 0; i < fields.size(); i++) {
180                 *file << "    " << fields[i] << ";";
181                 if (fieldComments.size() > i && !fieldComments[i].empty()) {
182                     *file << " // " << fieldComments[i];
183                 }
184                 *file << "\n";
185             }
186             *file << "}";
187             break;
188         }
189     }
190     *file << " " << typeName << ";\n";
191 
192     writeVersionGuardEnd(file, info);
193     *file << "\n";
194 }
195 
writeTypeComment(GeneratedFile * file,const Type & type)196 static void writeTypeComment(GeneratedFile* file, const Type& type) {
197     const string name = type.getName();
198     writeComment(file, name, type.getSummary(), type.getDescription(), type.deprecated(), true);
199 }
200 
writeFunctionPermutation(GeneratedFile * file,const FunctionSpecification & spec,const FunctionPermutation & permutation)201 static void writeFunctionPermutation(GeneratedFile* file, const FunctionSpecification& spec,
202                                      const FunctionPermutation& permutation) {
203     Function* function = spec.getFunction();
204     writeVersionGuardStart(file, spec.getVersionInfo(), function->getFinalVersion());
205 
206     // Write linkage info.
207     const auto inlineCodeLines = permutation.getInline();
208     if (inlineCodeLines.size() > 0) {
209         *file << "static inline ";
210     } else {
211         *file << "extern ";
212     }
213 
214     // Write the return type.
215     auto ret = permutation.getReturn();
216     if (ret) {
217         *file << ret->rsType;
218     } else {
219         *file << "void";
220     }
221 
222     *file << makeAttributeTag(spec.getAttribute(), spec.isOverloadable() ? "overloadable" : "",
223                               function->getDeprecatedApiLevel(), function->getDeprecatedMessage());
224     *file << "\n";
225 
226     // Write the function name.
227     *file << "    " << permutation.getName() << "(";
228     const int offset = 4 + permutation.getName().size() + 1;  // Size of above
229 
230     // Write the arguments.  We wrap on mulitple lines if a line gets too long.
231     int charsOnLine = offset;
232     bool hasGenerated = false;
233     for (auto p : permutation.getParams()) {
234         if (hasGenerated) {
235             *file << ",";
236             charsOnLine++;
237         }
238         ostringstream ps;
239         ps << p->rsType;
240         if (p->isOutParameter) {
241             ps << "*";
242         }
243         if (!p->specName.empty() && p->rsType != "...") {
244             ps << " " << p->specName;
245         }
246         const string s = ps.str();
247         if (charsOnLine + s.size() >= 100) {
248             *file << "\n" << string(offset, ' ');
249             charsOnLine = offset;
250         } else if (hasGenerated) {
251             *file << " ";
252             charsOnLine++;
253         }
254         *file << s;
255         charsOnLine += s.size();
256         hasGenerated = true;
257     }
258     // In C, if no parameters, we need to output void, e.g. fn(void).
259     if (!hasGenerated) {
260         *file << "void";
261     }
262     *file << ")";
263 
264     // Write the inline code, if any.
265     if (inlineCodeLines.size() > 0) {
266         *file << " {\n";
267         for (size_t ct = 0; ct < inlineCodeLines.size(); ct++) {
268             if (inlineCodeLines[ct].empty()) {
269                 *file << "\n";
270             } else {
271                 *file << "    " << inlineCodeLines[ct] << "\n";
272             }
273         }
274         *file << "}\n";
275     } else {
276         *file << ";\n";
277     }
278 
279     writeVersionGuardEnd(file, spec.getVersionInfo());
280     *file << "\n";
281 }
282 
writeFunctionComment(GeneratedFile * file,const Function & function)283 static void writeFunctionComment(GeneratedFile* file, const Function& function) {
284     // Write the generic documentation.
285     writeComment(file, function.getName(), function.getSummary(), function.getDescription(),
286                  function.deprecated(), false);
287 
288     // Comment the parameters.
289     if (function.someParametersAreDocumented()) {
290         *file << " *\n";
291         *file << " * Parameters:\n";
292         for (auto p : function.getParameters()) {
293             if (!p->documentation.empty()) {
294                 *file << " *   " << p->name << ": " << p->documentation << "\n";
295             }
296         }
297     }
298 
299     // Comment the return type.
300     const string returnDoc = function.getReturnDocumentation();
301     if (!returnDoc.empty()) {
302         *file << " *\n";
303         *file << " * Returns: " << returnDoc << "\n";
304     }
305 
306     *file << " */\n";
307 }
308 
writeFunctionSpecification(GeneratedFile * file,const FunctionSpecification & spec)309 static void writeFunctionSpecification(GeneratedFile* file, const FunctionSpecification& spec) {
310     // Write all the variants.
311     for (auto permutation : spec.getPermutations()) {
312         writeFunctionPermutation(file, spec, *permutation);
313     }
314 }
315 
writeHeaderFile(const string & directory,const SpecFile & specFile)316 static bool writeHeaderFile(const string& directory, const SpecFile& specFile) {
317     const string headerFileName = specFile.getHeaderFileName();
318 
319     // We generate one header file for each spec file.
320     GeneratedFile file;
321     if (!file.start(directory, headerFileName)) {
322         return false;
323     }
324 
325     // Write the comments that start the file.
326     file.writeNotices();
327     writeComment(&file, headerFileName, specFile.getBriefDescription(),
328                  specFile.getFullDescription(), false, true);
329     file << "\n";
330 
331     // Write the ifndef that prevents the file from being included twice.
332     const string guard = makeGuardString(headerFileName);
333     file << "#ifndef " << guard << "\n";
334     file << "#define " << guard << "\n\n";
335 
336     // Add lines that need to be put in "as is".
337     if (specFile.getVerbatimInclude().size() > 0) {
338         for (auto s : specFile.getVerbatimInclude()) {
339             file << s << "\n";
340         }
341         file << "\n";
342     }
343 
344     /* Write the constants, types, and functions in the same order as
345      * encountered in the spec file.
346      */
347     set<Constant*> documentedConstants;
348     for (auto spec : specFile.getConstantSpecifications()) {
349         Constant* constant = spec->getConstant();
350         if (documentedConstants.find(constant) == documentedConstants.end()) {
351             documentedConstants.insert(constant);
352             writeConstantComment(&file, *constant);
353         }
354         writeConstantSpecification(&file, *spec);
355     }
356     set<Type*> documentedTypes;
357     for (auto spec : specFile.getTypeSpecifications()) {
358         Type* type = spec->getType();
359         if (documentedTypes.find(type) == documentedTypes.end()) {
360             documentedTypes.insert(type);
361             writeTypeComment(&file, *type);
362         }
363         writeTypeSpecification(&file, *spec);
364     }
365 
366     set<Function*> documentedFunctions;
367     for (auto spec : specFile.getFunctionSpecifications()) {
368         // Do not include internal APIs in the header files.
369         if (spec->isInternal()) {
370             continue;
371         }
372         Function* function = spec->getFunction();
373         if (documentedFunctions.find(function) == documentedFunctions.end()) {
374             documentedFunctions.insert(function);
375             writeFunctionComment(&file, *function);
376         }
377         writeFunctionSpecification(&file, *spec);
378     }
379 
380     file << "#endif // " << guard << "\n";
381     file.close();
382     return true;
383 }
384 
generateHeaderFiles(const string & directory)385 bool generateHeaderFiles(const string& directory) {
386     bool success = true;
387     for (auto specFile : systemSpecification.getSpecFiles()) {
388         if (!writeHeaderFile(directory, *specFile)) {
389             success = false;
390         }
391     }
392     return success;
393 }
394