Home | History | Annotate | Download | only in script_api
      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...
     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  */
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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