• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 << "#define " << constant->getName() << " " << spec.getValue() << "\n\n";
124     writeVersionGuardEnd(file, info);
125 }
126 
writeTypeSpecification(GeneratedFile * file,const TypeSpecification & spec)127 static void writeTypeSpecification(GeneratedFile* file, const TypeSpecification& spec) {
128     const Type* type = spec.getType();
129     const string& typeName = type->getName();
130     const VersionInfo info = spec.getVersionInfo();
131     writeVersionGuardStart(file, info, type->getFinalVersion());
132 
133     const string attribute =
134                 makeAttributeTag(spec.getAttribute(), "", type->getDeprecatedApiLevel(),
135                                  type->getDeprecatedMessage());
136     *file << "typedef ";
137     switch (spec.getKind()) {
138         case SIMPLE:
139             *file << spec.getSimpleType() << attribute;
140             break;
141         case RS_OBJECT:
142             *file << "struct " << typeName << " _RS_OBJECT_DECL" << attribute;
143             break;
144         case ENUM: {
145             *file << "enum" << attribute << " ";
146             const string name = spec.getEnumName();
147             if (!name.empty()) {
148                 *file << name << " ";
149             }
150             *file << "{\n";
151 
152             const vector<string>& values = spec.getValues();
153             const vector<string>& valueComments = spec.getValueComments();
154             const size_t last = values.size() - 1;
155             for (size_t i = 0; i <= last; i++) {
156                 *file << "    " << values[i];
157                 if (i != last) {
158                     *file << ",";
159                 }
160                 if (valueComments.size() > i && !valueComments[i].empty()) {
161                     *file << " // " << valueComments[i];
162                 }
163                 *file << "\n";
164             }
165             *file << "}";
166             break;
167         }
168         case STRUCT: {
169             *file << "struct" << attribute << " ";
170             const string name = spec.getStructName();
171             if (!name.empty()) {
172                 *file << name << " ";
173             }
174             *file << "{\n";
175 
176             const vector<string>& fields = spec.getFields();
177             const vector<string>& fieldComments = spec.getFieldComments();
178             for (size_t i = 0; i < fields.size(); i++) {
179                 *file << "    " << fields[i] << ";";
180                 if (fieldComments.size() > i && !fieldComments[i].empty()) {
181                     *file << " // " << fieldComments[i];
182                 }
183                 *file << "\n";
184             }
185             *file << "}";
186             break;
187         }
188     }
189     *file << " " << typeName << ";\n";
190 
191     writeVersionGuardEnd(file, info);
192     *file << "\n";
193 }
194 
writeTypeComment(GeneratedFile * file,const Type & type)195 static void writeTypeComment(GeneratedFile* file, const Type& type) {
196     const string name = type.getName();
197     writeComment(file, name, type.getSummary(), type.getDescription(), type.deprecated(), true);
198 }
199 
writeFunctionPermutation(GeneratedFile * file,const FunctionSpecification & spec,const FunctionPermutation & permutation)200 static void writeFunctionPermutation(GeneratedFile* file, const FunctionSpecification& spec,
201                                      const FunctionPermutation& permutation) {
202     Function* function = spec.getFunction();
203     writeVersionGuardStart(file, spec.getVersionInfo(), function->getFinalVersion());
204 
205     // Write linkage info.
206     const auto inlineCodeLines = permutation.getInline();
207     if (inlineCodeLines.size() > 0) {
208         *file << "static inline ";
209     } else {
210         *file << "extern ";
211     }
212 
213     // Write the return type.
214     auto ret = permutation.getReturn();
215     if (ret) {
216         *file << ret->rsType;
217     } else {
218         *file << "void";
219     }
220 
221     *file << makeAttributeTag(spec.getAttribute(), spec.isOverloadable() ? "overloadable" : "",
222                               function->getDeprecatedApiLevel(), function->getDeprecatedMessage());
223     *file << "\n";
224 
225     // Write the function name.
226     *file << "    " << permutation.getName() << "(";
227     const int offset = 4 + permutation.getName().size() + 1;  // Size of above
228 
229     // Write the arguments.  We wrap on mulitple lines if a line gets too long.
230     int charsOnLine = offset;
231     bool hasGenerated = false;
232     for (auto p : permutation.getParams()) {
233         if (hasGenerated) {
234             *file << ",";
235             charsOnLine++;
236         }
237         ostringstream ps;
238         ps << p->rsType;
239         if (p->isOutParameter) {
240             ps << "*";
241         }
242         if (!p->specName.empty() && p->rsType != "...") {
243             ps << " " << p->specName;
244         }
245         const string s = ps.str();
246         if (charsOnLine + s.size() >= 100) {
247             *file << "\n" << string(offset, ' ');
248             charsOnLine = offset;
249         } else if (hasGenerated) {
250             *file << " ";
251             charsOnLine++;
252         }
253         *file << s;
254         charsOnLine += s.size();
255         hasGenerated = true;
256     }
257     // In C, if no parameters, we need to output void, e.g. fn(void).
258     if (!hasGenerated) {
259         *file << "void";
260     }
261     *file << ")";
262 
263     // Write the inline code, if any.
264     if (inlineCodeLines.size() > 0) {
265         *file << " {\n";
266         for (size_t ct = 0; ct < inlineCodeLines.size(); ct++) {
267             if (inlineCodeLines[ct].empty()) {
268                 *file << "\n";
269             } else {
270                 *file << "    " << inlineCodeLines[ct] << "\n";
271             }
272         }
273         *file << "}\n";
274     } else {
275         *file << ";\n";
276     }
277 
278     writeVersionGuardEnd(file, spec.getVersionInfo());
279     *file << "\n";
280 }
281 
writeFunctionComment(GeneratedFile * file,const Function & function)282 static void writeFunctionComment(GeneratedFile* file, const Function& function) {
283     // Write the generic documentation.
284     writeComment(file, function.getName(), function.getSummary(), function.getDescription(),
285                  function.deprecated(), false);
286 
287     // Comment the parameters.
288     if (function.someParametersAreDocumented()) {
289         *file << " *\n";
290         *file << " * Parameters:\n";
291         for (auto p : function.getParameters()) {
292             if (!p->documentation.empty()) {
293                 *file << " *   " << p->name << ": " << p->documentation << "\n";
294             }
295         }
296     }
297 
298     // Comment the return type.
299     const string returnDoc = function.getReturnDocumentation();
300     if (!returnDoc.empty()) {
301         *file << " *\n";
302         *file << " * Returns: " << returnDoc << "\n";
303     }
304 
305     *file << " */\n";
306 }
307 
writeFunctionSpecification(GeneratedFile * file,const FunctionSpecification & spec)308 static void writeFunctionSpecification(GeneratedFile* file, const FunctionSpecification& spec) {
309     // Write all the variants.
310     for (auto permutation : spec.getPermutations()) {
311         writeFunctionPermutation(file, spec, *permutation);
312     }
313 }
314 
writeHeaderFile(const string & directory,const SpecFile & specFile)315 static bool writeHeaderFile(const string& directory, const SpecFile& specFile) {
316     const string headerFileName = specFile.getHeaderFileName();
317 
318     // We generate one header file for each spec file.
319     GeneratedFile file;
320     if (!file.start(directory, headerFileName)) {
321         return false;
322     }
323 
324     // Write the comments that start the file.
325     file.writeNotices();
326     writeComment(&file, headerFileName, specFile.getBriefDescription(),
327                  specFile.getFullDescription(), false, true);
328     file << "\n";
329 
330     // Write the ifndef that prevents the file from being included twice.
331     const string guard = makeGuardString(headerFileName);
332     file << "#ifndef " << guard << "\n";
333     file << "#define " << guard << "\n\n";
334 
335     // Add lines that need to be put in "as is".
336     if (specFile.getVerbatimInclude().size() > 0) {
337         for (auto s : specFile.getVerbatimInclude()) {
338             file << s << "\n";
339         }
340         file << "\n";
341     }
342 
343     /* Write the constants, types, and functions in the same order as
344      * encountered in the spec file.
345      */
346     set<Constant*> documentedConstants;
347     for (auto spec : specFile.getConstantSpecifications()) {
348         Constant* constant = spec->getConstant();
349         if (documentedConstants.find(constant) == documentedConstants.end()) {
350             documentedConstants.insert(constant);
351             writeConstantComment(&file, *constant);
352         }
353         writeConstantSpecification(&file, *spec);
354     }
355     set<Type*> documentedTypes;
356     for (auto spec : specFile.getTypeSpecifications()) {
357         Type* type = spec->getType();
358         if (documentedTypes.find(type) == documentedTypes.end()) {
359             documentedTypes.insert(type);
360             writeTypeComment(&file, *type);
361         }
362         writeTypeSpecification(&file, *spec);
363     }
364 
365     set<Function*> documentedFunctions;
366     for (auto spec : specFile.getFunctionSpecifications()) {
367         // Do not include internal APIs in the header files.
368         if (spec->isInternal()) {
369             continue;
370         }
371         Function* function = spec->getFunction();
372         if (documentedFunctions.find(function) == documentedFunctions.end()) {
373             documentedFunctions.insert(function);
374             writeFunctionComment(&file, *function);
375         }
376         writeFunctionSpecification(&file, *spec);
377     }
378 
379     file << "#endif // " << guard << "\n";
380     file.close();
381     return true;
382 }
383 
generateHeaderFiles(const string & directory)384 bool generateHeaderFiles(const string& directory) {
385     bool success = true;
386     for (auto specFile : systemSpecification.getSpecFiles()) {
387         if (!writeHeaderFile(directory, *specFile)) {
388             success = false;
389         }
390     }
391     return success;
392 }
393