Home | History | Annotate | Download | only in script_api
      1 /*
      2  * Copyright (C) 2013 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 <stdio.h>
     18 #include <cctype>
     19 #include <cstdlib>
     20 #include <fstream>
     21 #include <functional>
     22 #include <iostream>
     23 #include <memory>
     24 #include <sstream>
     25 #include <strings.h>
     26 
     27 #include "Generator.h"
     28 #include "Scanner.h"
     29 #include "Specification.h"
     30 #include "Utilities.h"
     31 
     32 using namespace std;
     33 
     34 // API level when RenderScript was added.
     35 const unsigned int MIN_API_LEVEL = 9;
     36 
     37 const NumericalType TYPES[] = {
     38             {"f16", "FLOAT_16", "half", "short", FLOATING_POINT, 11, 5},
     39             {"f32", "FLOAT_32", "float", "float", FLOATING_POINT, 24, 8},
     40             {"f64", "FLOAT_64", "double", "double", FLOATING_POINT, 53, 11},
     41             {"i8", "SIGNED_8", "char", "byte", SIGNED_INTEGER, 7, 0},
     42             {"u8", "UNSIGNED_8", "uchar", "byte", UNSIGNED_INTEGER, 8, 0},
     43             {"i16", "SIGNED_16", "short", "short", SIGNED_INTEGER, 15, 0},
     44             {"u16", "UNSIGNED_16", "ushort", "short", UNSIGNED_INTEGER, 16, 0},
     45             {"i32", "SIGNED_32", "int", "int", SIGNED_INTEGER, 31, 0},
     46             {"u32", "UNSIGNED_32", "uint", "int", UNSIGNED_INTEGER, 32, 0},
     47             {"i64", "SIGNED_64", "long", "long", SIGNED_INTEGER, 63, 0},
     48             {"u64", "UNSIGNED_64", "ulong", "long", UNSIGNED_INTEGER, 64, 0},
     49 };
     50 
     51 const int NUM_TYPES = sizeof(TYPES) / sizeof(TYPES[0]);
     52 
     53 static const char kTagUnreleased[] = "UNRELEASED";
     54 
     55 // Patterns that get substituted with C type or RS Data type names in function
     56 // names, arguments, return types, and inlines.
     57 static const string kCTypePatterns[] = {"#1", "#2", "#3", "#4"};
     58 static const string kRSTypePatterns[] = {"#RST_1", "#RST_2", "#RST_3", "#RST_4"};
     59 
     60 // The singleton of the collected information of all the spec files.
     61 SystemSpecification systemSpecification;
     62 
     63 // Returns the index in TYPES for the provided cType
     64 static int findCType(const string& cType) {
     65     for (int i = 0; i < NUM_TYPES; i++) {
     66         if (cType == TYPES[i].cType) {
     67             return i;
     68         }
     69     }
     70     return -1;
     71 }
     72 
     73 /* Converts a string like "u8, u16" to a vector of "ushort", "uint".
     74  * For non-numerical types, we don't need to convert the abbreviation.
     75  */
     76 static vector<string> convertToTypeVector(const string& input) {
     77     // First convert the string to an array of strings.
     78     vector<string> entries;
     79     stringstream stream(input);
     80     string entry;
     81     while (getline(stream, entry, ',')) {
     82         trimSpaces(&entry);
     83         entries.push_back(entry);
     84     }
     85 
     86     /* Second, we look for present numerical types. We do it this way
     87      * so the order of numerical types is always the same, no matter
     88      * how specified in the spec file.
     89      */
     90     vector<string> result;
     91     for (auto t : TYPES) {
     92         for (auto i = entries.begin(); i != entries.end(); ++i) {
     93             if (*i == t.specType) {
     94                 result.push_back(t.cType);
     95                 entries.erase(i);
     96                 break;
     97             }
     98         }
     99     }
    100 
    101     // Add the remaining; they are not numerical types.
    102     for (auto s : entries) {
    103         result.push_back(s);
    104     }
    105 
    106     return result;
    107 }
    108 
    109 // Returns true if each entry in typeVector is an RS numerical type
    110 static bool isRSTValid(const vector<string> &typeVector) {
    111     for (auto type: typeVector) {
    112         if (findCType(type) == -1)
    113             return false;
    114     }
    115     return true;
    116 }
    117 
    118 void getVectorSizeAndBaseType(const string& type, string& vectorSize, string& baseType) {
    119     vectorSize = "1";
    120     baseType = type;
    121 
    122     /* If it's a vector type, we need to split the base type from the size.
    123      * We know that's it's a vector type if the last character is a digit and
    124      * the rest is an actual base type.   We used to only verify the first part,
    125      * which created a problem with rs_matrix2x2.
    126      */
    127     const int last = type.size() - 1;
    128     const char lastChar = type[last];
    129     if (lastChar >= '0' && lastChar <= '9') {
    130         const string trimmed = type.substr(0, last);
    131         int i = findCType(trimmed);
    132         if (i >= 0) {
    133             baseType = trimmed;
    134             vectorSize = lastChar;
    135         }
    136     }
    137 }
    138 
    139 void ParameterDefinition::parseParameterDefinition(const string& type, const string& name,
    140                                                    const string& testOption, int lineNumber,
    141                                                    bool isReturn, Scanner* scanner) {
    142     rsType = type;
    143     specName = name;
    144 
    145     // Determine if this is an output.
    146     isOutParameter = isReturn || charRemoved('*', &rsType);
    147 
    148     getVectorSizeAndBaseType(rsType, mVectorSize, rsBaseType);
    149     typeIndex = findCType(rsBaseType);
    150 
    151     if (mVectorSize == "3") {
    152         vectorWidth = "4";
    153     } else {
    154         vectorWidth = mVectorSize;
    155     }
    156 
    157     /* Create variable names to be used in the java and .rs files.  Because x and
    158      * y are reserved in .rs files, we prefix variable names with "in" or "out".
    159      */
    160     if (isOutParameter) {
    161         variableName = "out";
    162         if (!specName.empty()) {
    163             variableName += capitalize(specName);
    164         } else if (!isReturn) {
    165             scanner->error(lineNumber) << "Should have a name.\n";
    166         }
    167         doubleVariableName = variableName + "Double";
    168     } else {
    169         variableName = "in";
    170         if (specName.empty()) {
    171             scanner->error(lineNumber) << "Should have a name.\n";
    172         }
    173         variableName += capitalize(specName);
    174         doubleVariableName = variableName + "Double";
    175     }
    176     rsAllocName = "gAlloc" + capitalize(variableName);
    177     javaAllocName = variableName;
    178     javaArrayName = "array" + capitalize(javaAllocName);
    179 
    180     // Process the option.
    181     undefinedIfOutIsNan = false;
    182     compatibleTypeIndex = -1;
    183     if (!testOption.empty()) {
    184         if (testOption.compare(0, 6, "range(") == 0) {
    185             size_t pComma = testOption.find(',');
    186             size_t pParen = testOption.find(')');
    187             if (pComma == string::npos || pParen == string::npos) {
    188                 scanner->error(lineNumber) << "Incorrect range " << testOption << "\n";
    189             } else {
    190                 minValue = testOption.substr(6, pComma - 6);
    191                 maxValue = testOption.substr(pComma + 1, pParen - pComma - 1);
    192             }
    193         } else if (testOption.compare(0, 6, "above(") == 0) {
    194             size_t pParen = testOption.find(')');
    195             if (pParen == string::npos) {
    196                 scanner->error(lineNumber) << "Incorrect testOption " << testOption << "\n";
    197             } else {
    198                 smallerParameter = testOption.substr(6, pParen - 6);
    199             }
    200         } else if (testOption.compare(0, 11, "compatible(") == 0) {
    201             size_t pParen = testOption.find(')');
    202             if (pParen == string::npos) {
    203                 scanner->error(lineNumber) << "Incorrect testOption " << testOption << "\n";
    204             } else {
    205                 compatibleTypeIndex = findCType(testOption.substr(11, pParen - 11));
    206             }
    207         } else if (testOption.compare(0, 11, "conditional") == 0) {
    208             undefinedIfOutIsNan = true;
    209         } else {
    210             scanner->error(lineNumber) << "Unrecognized testOption " << testOption << "\n";
    211         }
    212     }
    213 
    214     isFloatType = false;
    215     if (typeIndex >= 0) {
    216         javaBaseType = TYPES[typeIndex].javaType;
    217         specType = TYPES[typeIndex].specType;
    218         isFloatType = TYPES[typeIndex].exponentBits > 0;
    219     }
    220     if (!minValue.empty()) {
    221         if (typeIndex < 0 || TYPES[typeIndex].kind != FLOATING_POINT) {
    222             scanner->error(lineNumber) << "range(,) is only supported for floating point\n";
    223         }
    224     }
    225 }
    226 
    227 bool VersionInfo::scan(Scanner* scanner, unsigned int maxApiLevel) {
    228     if (scanner->findOptionalTag("version:")) {
    229         const string s = scanner->getValue();
    230         if (s.compare(0, sizeof(kTagUnreleased), kTagUnreleased) == 0) {
    231             // The API is still under development and does not have
    232             // an official version number.
    233             minVersion = maxVersion = kUnreleasedVersion;
    234         } else {
    235             sscanf(s.c_str(), "%u %u", &minVersion, &maxVersion);
    236             if (minVersion && minVersion < MIN_API_LEVEL) {
    237                 scanner->error() << "Minimum version must >= 9\n";
    238             }
    239             if (minVersion == MIN_API_LEVEL) {
    240                 minVersion = 0;
    241             }
    242             if (maxVersion && maxVersion < MIN_API_LEVEL) {
    243                 scanner->error() << "Maximum version must >= 9\n";
    244             }
    245         }
    246     }
    247     if (scanner->findOptionalTag("size:")) {
    248         sscanf(scanner->getValue().c_str(), "%i", &intSize);
    249     }
    250 
    251     if (maxVersion > maxApiLevel) {
    252         maxVersion = maxApiLevel;
    253     }
    254 
    255     return minVersion == 0 || minVersion <= maxApiLevel;
    256 }
    257 
    258 Definition::Definition(const std::string& name)
    259     : mName(name), mDeprecatedApiLevel(0), mHidden(false), mFinalVersion(-1) {
    260 }
    261 
    262 void Definition::updateFinalVersion(const VersionInfo& info) {
    263     /* We set it if:
    264      * - We have never set mFinalVersion before, or
    265      * - The max version is 0, which means we have not expired this API, or
    266      * - We have a max that's later than what we currently have.
    267      */
    268     if (mFinalVersion < 0 || info.maxVersion == 0 ||
    269         (mFinalVersion > 0 &&
    270          static_cast<int>(info.maxVersion) > mFinalVersion)) {
    271         mFinalVersion = info.maxVersion;
    272     }
    273 }
    274 
    275 void Definition::scanDocumentationTags(Scanner* scanner, bool firstOccurence,
    276                                        const SpecFile* specFile) {
    277     if (scanner->findOptionalTag("hidden:")) {
    278         scanner->checkNoValue();
    279         mHidden = true;
    280     }
    281     if (scanner->findOptionalTag("deprecated:")) {
    282         string value = scanner->getValue();
    283         size_t pComma = value.find(", ");
    284         if (pComma != string::npos) {
    285             mDeprecatedMessage = value.substr(pComma + 2);
    286             value.erase(pComma);
    287         }
    288         sscanf(value.c_str(), "%i", &mDeprecatedApiLevel);
    289         if (mDeprecatedApiLevel <= 0) {
    290             scanner->error() << "deprecated entries should have a level > 0\n";
    291         }
    292     }
    293     if (firstOccurence) {
    294         if (scanner->findTag("summary:")) {
    295             mSummary = scanner->getValue();
    296         }
    297         if (scanner->findTag("description:")) {
    298             scanner->checkNoValue();
    299             while (scanner->findOptionalTag("")) {
    300                 mDescription.push_back(scanner->getValue());
    301             }
    302         }
    303         mUrl = specFile->getDetailedDocumentationUrl() + "#android_rs:" + mName;
    304     } else if (scanner->findOptionalTag("summary:")) {
    305         scanner->error() << "Only the first specification should have a summary.\n";
    306     }
    307 }
    308 
    309 Constant::~Constant() {
    310     for (auto i : mSpecifications) {
    311         delete i;
    312     }
    313 }
    314 
    315 Type::~Type() {
    316     for (auto i : mSpecifications) {
    317         delete i;
    318     }
    319 }
    320 
    321 Function::Function(const string& name) : Definition(name) {
    322     mCapitalizedName = capitalize(mName);
    323 }
    324 
    325 Function::~Function() {
    326     for (auto i : mSpecifications) {
    327         delete i;
    328     }
    329 }
    330 
    331 bool Function::someParametersAreDocumented() const {
    332     for (auto p : mParameters) {
    333         if (!p->documentation.empty()) {
    334             return true;
    335         }
    336     }
    337     return false;
    338 }
    339 
    340 void Function::addParameter(ParameterEntry* entry, Scanner* scanner) {
    341     for (auto i : mParameters) {
    342         if (i->name == entry->name) {
    343             // It's a duplicate.
    344             if (!entry->documentation.empty()) {
    345                 scanner->error(entry->lineNumber)
    346                             << "Only the first occurence of an arg should have the "
    347                                "documentation.\n";
    348             }
    349             return;
    350         }
    351     }
    352     mParameters.push_back(entry);
    353 }
    354 
    355 void Function::addReturn(ParameterEntry* entry, Scanner* scanner) {
    356     if (entry->documentation.empty()) {
    357         return;
    358     }
    359     if (!mReturnDocumentation.empty()) {
    360         scanner->error() << "ret: should be documented only for the first variant\n";
    361     }
    362     mReturnDocumentation = entry->documentation;
    363 }
    364 
    365 void ConstantSpecification::scanConstantSpecification(Scanner* scanner, SpecFile* specFile,
    366                                                       unsigned int maxApiLevel) {
    367     string name = scanner->getValue();
    368     VersionInfo info;
    369     if (!info.scan(scanner, maxApiLevel)) {
    370         cout << "Skipping some " << name << " definitions.\n";
    371         scanner->skipUntilTag("end:");
    372         return;
    373     }
    374 
    375     bool created = false;
    376     Constant* constant = systemSpecification.findOrCreateConstant(name, &created);
    377     ConstantSpecification* spec = new ConstantSpecification(constant);
    378     constant->addSpecification(spec);
    379     constant->updateFinalVersion(info);
    380     specFile->addConstantSpecification(spec, created);
    381     spec->mVersionInfo = info;
    382 
    383     if (scanner->findTag("value:")) {
    384         spec->mValue = scanner->getValue();
    385     }
    386     if (scanner->findTag("type:")) {
    387         spec->mType = scanner->getValue();
    388     }
    389     constant->scanDocumentationTags(scanner, created, specFile);
    390 
    391     scanner->findTag("end:");
    392 }
    393 
    394 void TypeSpecification::scanTypeSpecification(Scanner* scanner, SpecFile* specFile,
    395                                               unsigned int maxApiLevel) {
    396     string name = scanner->getValue();
    397     VersionInfo info;
    398     if (!info.scan(scanner, maxApiLevel)) {
    399         cout << "Skipping some " << name << " definitions.\n";
    400         scanner->skipUntilTag("end:");
    401         return;
    402     }
    403 
    404     bool created = false;
    405     Type* type = systemSpecification.findOrCreateType(name, &created);
    406     TypeSpecification* spec = new TypeSpecification(type);
    407     type->addSpecification(spec);
    408     type->updateFinalVersion(info);
    409     specFile->addTypeSpecification(spec, created);
    410     spec->mVersionInfo = info;
    411 
    412     if (scanner->findOptionalTag("simple:")) {
    413         spec->mKind = SIMPLE;
    414         spec->mSimpleType = scanner->getValue();
    415     }
    416     if (scanner->findOptionalTag("rs_object:")) {
    417         spec->mKind = RS_OBJECT;
    418     }
    419     if (scanner->findOptionalTag("struct:")) {
    420         spec->mKind = STRUCT;
    421         spec->mStructName = scanner->getValue();
    422         while (scanner->findOptionalTag("field:")) {
    423             string s = scanner->getValue();
    424             string comment;
    425             scanner->parseDocumentation(&s, &comment);
    426             spec->mFields.push_back(s);
    427             spec->mFieldComments.push_back(comment);
    428         }
    429     }
    430     if (scanner->findOptionalTag("enum:")) {
    431         spec->mKind = ENUM;
    432         spec->mEnumName = scanner->getValue();
    433         while (scanner->findOptionalTag("value:")) {
    434             string s = scanner->getValue();
    435             string comment;
    436             scanner->parseDocumentation(&s, &comment);
    437             spec->mValues.push_back(s);
    438             spec->mValueComments.push_back(comment);
    439         }
    440     }
    441     if (scanner->findOptionalTag("attrib:")) {
    442         spec->mAttribute = scanner->getValue();
    443     }
    444     type->scanDocumentationTags(scanner, created, specFile);
    445 
    446     scanner->findTag("end:");
    447 }
    448 
    449 FunctionSpecification::~FunctionSpecification() {
    450     for (auto i : mParameters) {
    451         delete i;
    452     }
    453     delete mReturn;
    454     for (auto i : mPermutations) {
    455         delete i;
    456     }
    457 }
    458 
    459 string FunctionSpecification::expandRSTypeInString(const string &s,
    460                                                    const string &pattern,
    461                                                    const string &cTypeStr) const {
    462     // Find index of numerical type corresponding to cTypeStr.  The case where
    463     // pattern is found in s but cTypeStr is not a numerical type is checked in
    464     // checkRSTPatternValidity.
    465     int typeIdx = findCType(cTypeStr);
    466     if (typeIdx == -1) {
    467         return s;
    468     }
    469     // If index exists, perform replacement.
    470     return stringReplace(s, pattern, TYPES[typeIdx].rsDataType);
    471 }
    472 
    473 string FunctionSpecification::expandString(string s,
    474                                            int replacementIndexes[MAX_REPLACEABLES]) const {
    475 
    476 
    477     for (unsigned idx = 0; idx < mReplaceables.size(); idx ++) {
    478         string toString = mReplaceables[idx][replacementIndexes[idx]];
    479 
    480         // replace #RST_i patterns with RS datatype corresponding to toString
    481         s = expandRSTypeInString(s, kRSTypePatterns[idx], toString);
    482 
    483         // replace #i patterns with C type from mReplaceables
    484         s = stringReplace(s, kCTypePatterns[idx], toString);
    485     }
    486 
    487     return s;
    488 }
    489 
    490 void FunctionSpecification::expandStringVector(const vector<string>& in,
    491                                                int replacementIndexes[MAX_REPLACEABLES],
    492                                                vector<string>* out) const {
    493     out->clear();
    494     for (vector<string>::const_iterator iter = in.begin(); iter != in.end(); iter++) {
    495         out->push_back(expandString(*iter, replacementIndexes));
    496     }
    497 }
    498 
    499 void FunctionSpecification::createPermutations(Function* function, Scanner* scanner) {
    500     int start[MAX_REPLACEABLES];
    501     int end[MAX_REPLACEABLES];
    502     for (int i = 0; i < MAX_REPLACEABLES; i++) {
    503         if (i < (int)mReplaceables.size()) {
    504             start[i] = 0;
    505             end[i] = mReplaceables[i].size();
    506         } else {
    507             start[i] = -1;
    508             end[i] = 0;
    509         }
    510     }
    511     int replacementIndexes[MAX_REPLACEABLES];
    512     // TODO: These loops assume that MAX_REPLACEABLES is 4.
    513     for (replacementIndexes[3] = start[3]; replacementIndexes[3] < end[3];
    514          replacementIndexes[3]++) {
    515         for (replacementIndexes[2] = start[2]; replacementIndexes[2] < end[2];
    516              replacementIndexes[2]++) {
    517             for (replacementIndexes[1] = start[1]; replacementIndexes[1] < end[1];
    518                  replacementIndexes[1]++) {
    519                 for (replacementIndexes[0] = start[0]; replacementIndexes[0] < end[0];
    520                      replacementIndexes[0]++) {
    521                     auto p = new FunctionPermutation(function, this, replacementIndexes, scanner);
    522                     mPermutations.push_back(p);
    523                 }
    524             }
    525         }
    526     }
    527 }
    528 
    529 string FunctionSpecification::getName(int replacementIndexes[MAX_REPLACEABLES]) const {
    530     return expandString(mUnexpandedName, replacementIndexes);
    531 }
    532 
    533 void FunctionSpecification::getReturn(int replacementIndexes[MAX_REPLACEABLES],
    534                                       std::string* retType, int* lineNumber) const {
    535     *retType = expandString(mReturn->type, replacementIndexes);
    536     *lineNumber = mReturn->lineNumber;
    537 }
    538 
    539 void FunctionSpecification::getParam(size_t index, int replacementIndexes[MAX_REPLACEABLES],
    540                                      std::string* type, std::string* name, std::string* testOption,
    541                                      int* lineNumber) const {
    542     ParameterEntry* p = mParameters[index];
    543     *type = expandString(p->type, replacementIndexes);
    544     *name = p->name;
    545     *testOption = expandString(p->testOption, replacementIndexes);
    546     *lineNumber = p->lineNumber;
    547 }
    548 
    549 void FunctionSpecification::getInlines(int replacementIndexes[MAX_REPLACEABLES],
    550                                        std::vector<std::string>* inlines) const {
    551     expandStringVector(mInline, replacementIndexes, inlines);
    552 }
    553 
    554 void FunctionSpecification::parseTest(Scanner* scanner) {
    555     const string value = scanner->getValue();
    556     if (value == "scalar" || value == "vector" || value == "noverify" || value == "custom" ||
    557         value == "none") {
    558         mTest = value;
    559     } else if (value.compare(0, 7, "limited") == 0) {
    560         mTest = "limited";
    561         if (value.compare(7, 1, "(") == 0) {
    562             size_t pParen = value.find(')');
    563             if (pParen == string::npos) {
    564                 scanner->error() << "Incorrect test: \"" << value << "\"\n";
    565             } else {
    566                 mPrecisionLimit = value.substr(8, pParen - 8);
    567             }
    568         }
    569     } else {
    570         scanner->error() << "Unrecognized test option: \"" << value << "\"\n";
    571     }
    572 }
    573 
    574 bool FunctionSpecification::hasTests(unsigned int versionOfTestFiles) const {
    575     if (mVersionInfo.maxVersion != 0 && mVersionInfo.maxVersion < versionOfTestFiles) {
    576         return false;
    577     }
    578     if (mTest == "none") {
    579         return false;
    580     }
    581     return true;
    582 }
    583 
    584 void FunctionSpecification::checkRSTPatternValidity(const string &inlineStr,  bool allow,
    585                                                     Scanner *scanner) {
    586     for (int i = 0; i < MAX_REPLACEABLES; i ++) {
    587         bool patternFound = inlineStr.find(kRSTypePatterns[i]) != string::npos;
    588 
    589         if (patternFound) {
    590             if (!allow) {
    591                 scanner->error() << "RST_i pattern not allowed here\n";
    592             }
    593             else if (mIsRSTAllowed[i] == false) {
    594                 scanner->error() << "Found pattern \"" << kRSTypePatterns[i]
    595                     << "\" in spec.  But some entry in the corresponding"
    596                     << " parameter list cannot be translated to an RS type\n";
    597             }
    598         }
    599     }
    600 }
    601 
    602 void FunctionSpecification::scanFunctionSpecification(Scanner* scanner, SpecFile* specFile,
    603                                                       unsigned int maxApiLevel) {
    604     // Some functions like convert have # part of the name.  Truncate at that point.
    605     const string& unexpandedName = scanner->getValue();
    606     string name = unexpandedName;
    607     size_t p = name.find('#');
    608     if (p != string::npos) {
    609         if (p > 0 && name[p - 1] == '_') {
    610             p--;
    611         }
    612         name.erase(p);
    613     }
    614     VersionInfo info;
    615     if (!info.scan(scanner, maxApiLevel)) {
    616         cout << "Skipping some " << name << " definitions.\n";
    617         scanner->skipUntilTag("end:");
    618         return;
    619     }
    620 
    621     bool created = false;
    622     Function* function = systemSpecification.findOrCreateFunction(name, &created);
    623     FunctionSpecification* spec = new FunctionSpecification(function);
    624     function->addSpecification(spec);
    625     function->updateFinalVersion(info);
    626     specFile->addFunctionSpecification(spec, created);
    627 
    628     spec->mUnexpandedName = unexpandedName;
    629     spec->mTest = "scalar";  // default
    630     spec->mVersionInfo = info;
    631 
    632     if (scanner->findOptionalTag("internal:")) {
    633         spec->mInternal = (scanner->getValue() == "true");
    634     }
    635     if (scanner->findOptionalTag("intrinsic:")) {
    636         spec->mIntrinsic = (scanner->getValue() == "true");
    637     }
    638     if (scanner->findOptionalTag("attrib:")) {
    639         spec->mAttribute = scanner->getValue();
    640     }
    641     if (scanner->findOptionalTag("w:")) {
    642         vector<string> t;
    643         if (scanner->getValue().find("1") != string::npos) {
    644             t.push_back("");
    645         }
    646         if (scanner->getValue().find("2") != string::npos) {
    647             t.push_back("2");
    648         }
    649         if (scanner->getValue().find("3") != string::npos) {
    650             t.push_back("3");
    651         }
    652         if (scanner->getValue().find("4") != string::npos) {
    653             t.push_back("4");
    654         }
    655         spec->mReplaceables.push_back(t);
    656         // RST_i pattern not applicable for width.
    657         spec->mIsRSTAllowed.push_back(false);
    658     }
    659 
    660     while (scanner->findOptionalTag("t:")) {
    661         spec->mReplaceables.push_back(convertToTypeVector(scanner->getValue()));
    662         spec->mIsRSTAllowed.push_back(isRSTValid(spec->mReplaceables.back()));
    663     }
    664 
    665     // Disallow RST_* pattern in function name
    666     // FIXME the line number for this error would be wrong
    667     spec->checkRSTPatternValidity(unexpandedName, false, scanner);
    668 
    669     if (scanner->findTag("ret:")) {
    670         ParameterEntry* p = scanner->parseArgString(true);
    671         function->addReturn(p, scanner);
    672         spec->mReturn = p;
    673 
    674         // Disallow RST_* pattern in return type
    675         spec->checkRSTPatternValidity(p->type, false, scanner);
    676     }
    677     while (scanner->findOptionalTag("arg:")) {
    678         ParameterEntry* p = scanner->parseArgString(false);
    679         function->addParameter(p, scanner);
    680         spec->mParameters.push_back(p);
    681 
    682         // Disallow RST_* pattern in parameter type or testOption
    683         spec->checkRSTPatternValidity(p->type, false, scanner);
    684         spec->checkRSTPatternValidity(p->testOption, false, scanner);
    685     }
    686 
    687     function->scanDocumentationTags(scanner, created, specFile);
    688 
    689     if (scanner->findOptionalTag("inline:")) {
    690         scanner->checkNoValue();
    691         while (scanner->findOptionalTag("")) {
    692             spec->mInline.push_back(scanner->getValue());
    693 
    694             // Allow RST_* pattern in inline definitions
    695             spec->checkRSTPatternValidity(spec->mInline.back(), true, scanner);
    696         }
    697     }
    698     if (scanner->findOptionalTag("test:")) {
    699         spec->parseTest(scanner);
    700     }
    701 
    702     scanner->findTag("end:");
    703 
    704     spec->createPermutations(function, scanner);
    705 }
    706 
    707 FunctionPermutation::FunctionPermutation(Function* func, FunctionSpecification* spec,
    708                                          int replacementIndexes[MAX_REPLACEABLES], Scanner* scanner)
    709     : mReturn(nullptr), mInputCount(0), mOutputCount(0) {
    710     // We expand the strings now to make capitalization easier.  The previous code preserved
    711     // the #n
    712     // markers just before emitting, which made capitalization difficult.
    713     mName = spec->getName(replacementIndexes);
    714     mNameTrunk = func->getName();
    715     mTest = spec->getTest();
    716     mPrecisionLimit = spec->getPrecisionLimit();
    717     spec->getInlines(replacementIndexes, &mInline);
    718 
    719     mHasFloatAnswers = false;
    720     for (size_t i = 0; i < spec->getNumberOfParams(); i++) {
    721         string type, name, testOption;
    722         int lineNumber = 0;
    723         spec->getParam(i, replacementIndexes, &type, &name, &testOption, &lineNumber);
    724         ParameterDefinition* def = new ParameterDefinition();
    725         def->parseParameterDefinition(type, name, testOption, lineNumber, false, scanner);
    726         if (def->isOutParameter) {
    727             mOutputCount++;
    728         } else {
    729             mInputCount++;
    730         }
    731 
    732         if (def->typeIndex < 0 && mTest != "none") {
    733             scanner->error(lineNumber)
    734                         << "Could not find " << def->rsBaseType
    735                         << " while generating automated tests.  Use test: none if not needed.\n";
    736         }
    737         if (def->isOutParameter && def->isFloatType) {
    738             mHasFloatAnswers = true;
    739         }
    740         mParams.push_back(def);
    741     }
    742 
    743     string retType;
    744     int lineNumber = 0;
    745     spec->getReturn(replacementIndexes, &retType, &lineNumber);
    746     if (!retType.empty()) {
    747         mReturn = new ParameterDefinition();
    748         mReturn->parseParameterDefinition(retType, "", "", lineNumber, true, scanner);
    749         if (mReturn->isFloatType) {
    750             mHasFloatAnswers = true;
    751         }
    752         mOutputCount++;
    753     }
    754 }
    755 
    756 FunctionPermutation::~FunctionPermutation() {
    757     for (auto i : mParams) {
    758         delete i;
    759     }
    760     delete mReturn;
    761 }
    762 
    763 SpecFile::SpecFile(const string& specFileName) : mSpecFileName(specFileName) {
    764     string core = mSpecFileName;
    765     // Remove .spec
    766     size_t l = core.length();
    767     const char SPEC[] = ".spec";
    768     const int SPEC_SIZE = sizeof(SPEC) - 1;
    769     const int start = l - SPEC_SIZE;
    770     if (start >= 0 && core.compare(start, SPEC_SIZE, SPEC) == 0) {
    771         core.erase(start);
    772     }
    773 
    774     // The header file name should have the same base but with a ".rsh" extension.
    775     mHeaderFileName = core + ".rsh";
    776     mDetailedDocumentationUrl = core + ".html";
    777 }
    778 
    779 void SpecFile::addConstantSpecification(ConstantSpecification* spec, bool hasDocumentation) {
    780     mConstantSpecificationsList.push_back(spec);
    781     if (hasDocumentation) {
    782         Constant* constant = spec->getConstant();
    783         mDocumentedConstants.insert(pair<string, Constant*>(constant->getName(), constant));
    784     }
    785 }
    786 
    787 void SpecFile::addTypeSpecification(TypeSpecification* spec, bool hasDocumentation) {
    788     mTypeSpecificationsList.push_back(spec);
    789     if (hasDocumentation) {
    790         Type* type = spec->getType();
    791         mDocumentedTypes.insert(pair<string, Type*>(type->getName(), type));
    792     }
    793 }
    794 
    795 void SpecFile::addFunctionSpecification(FunctionSpecification* spec, bool hasDocumentation) {
    796     mFunctionSpecificationsList.push_back(spec);
    797     if (hasDocumentation) {
    798         Function* function = spec->getFunction();
    799         mDocumentedFunctions.insert(pair<string, Function*>(function->getName(), function));
    800     }
    801 }
    802 
    803 // Read the specification, adding the definitions to the global functions map.
    804 bool SpecFile::readSpecFile(unsigned int maxApiLevel) {
    805     FILE* specFile = fopen(mSpecFileName.c_str(), "rt");
    806     if (!specFile) {
    807         cerr << "Error opening input file: " << mSpecFileName << "\n";
    808         return false;
    809     }
    810 
    811     Scanner scanner(mSpecFileName, specFile);
    812 
    813     // Scan the header that should start the file.
    814     scanner.skipBlankEntries();
    815     if (scanner.findTag("header:")) {
    816         if (scanner.findTag("summary:")) {
    817             mBriefDescription = scanner.getValue();
    818         }
    819         if (scanner.findTag("description:")) {
    820             scanner.checkNoValue();
    821             while (scanner.findOptionalTag("")) {
    822                 mFullDescription.push_back(scanner.getValue());
    823             }
    824         }
    825         if (scanner.findOptionalTag("include:")) {
    826             scanner.checkNoValue();
    827             while (scanner.findOptionalTag("")) {
    828                 mVerbatimInclude.push_back(scanner.getValue());
    829             }
    830         }
    831         scanner.findTag("end:");
    832     }
    833 
    834     while (1) {
    835         scanner.skipBlankEntries();
    836         if (scanner.atEnd()) {
    837             break;
    838         }
    839         const string tag = scanner.getNextTag();
    840         if (tag == "function:") {
    841             FunctionSpecification::scanFunctionSpecification(&scanner, this, maxApiLevel);
    842         } else if (tag == "type:") {
    843             TypeSpecification::scanTypeSpecification(&scanner, this, maxApiLevel);
    844         } else if (tag == "constant:") {
    845             ConstantSpecification::scanConstantSpecification(&scanner, this, maxApiLevel);
    846         } else {
    847             scanner.error() << "Expected function:, type:, or constant:.  Found: " << tag << "\n";
    848             return false;
    849         }
    850     }
    851 
    852     fclose(specFile);
    853     return scanner.getErrorCount() == 0;
    854 }
    855 
    856 SystemSpecification::~SystemSpecification() {
    857     for (auto i : mConstants) {
    858         delete i.second;
    859     }
    860     for (auto i : mTypes) {
    861         delete i.second;
    862     }
    863     for (auto i : mFunctions) {
    864         delete i.second;
    865     }
    866     for (auto i : mSpecFiles) {
    867         delete i;
    868     }
    869 }
    870 
    871 // Returns the named entry in the map.  Creates it if it's not there.
    872 template <class T>
    873 T* findOrCreate(const string& name, map<string, T*>* map, bool* created) {
    874     auto iter = map->find(name);
    875     if (iter != map->end()) {
    876         *created = false;
    877         return iter->second;
    878     }
    879     *created = true;
    880     T* f = new T(name);
    881     map->insert(pair<string, T*>(name, f));
    882     return f;
    883 }
    884 
    885 Constant* SystemSpecification::findOrCreateConstant(const string& name, bool* created) {
    886     return findOrCreate<Constant>(name, &mConstants, created);
    887 }
    888 
    889 Type* SystemSpecification::findOrCreateType(const string& name, bool* created) {
    890     return findOrCreate<Type>(name, &mTypes, created);
    891 }
    892 
    893 Function* SystemSpecification::findOrCreateFunction(const string& name, bool* created) {
    894     return findOrCreate<Function>(name, &mFunctions, created);
    895 }
    896 
    897 bool SystemSpecification::readSpecFile(const string& fileName, unsigned int maxApiLevel) {
    898     SpecFile* spec = new SpecFile(fileName);
    899     if (!spec->readSpecFile(maxApiLevel)) {
    900         cerr << fileName << ": Failed to parse.\n";
    901         return false;
    902     }
    903     mSpecFiles.push_back(spec);
    904     return true;
    905 }
    906 
    907 
    908 static void updateMaxApiLevel(const VersionInfo& info, unsigned int* maxApiLevel) {
    909     if (info.minVersion == VersionInfo::kUnreleasedVersion) {
    910         // Ignore development API level in consideration of max API level.
    911         return;
    912     }
    913     *maxApiLevel = max(*maxApiLevel, max(info.minVersion, info.maxVersion));
    914 }
    915 
    916 unsigned int SystemSpecification::getMaximumApiLevel() {
    917     unsigned int maxApiLevel = 0;
    918     for (auto i : mConstants) {
    919         for (auto j: i.second->getSpecifications()) {
    920             updateMaxApiLevel(j->getVersionInfo(), &maxApiLevel);
    921         }
    922     }
    923     for (auto i : mTypes) {
    924         for (auto j: i.second->getSpecifications()) {
    925             updateMaxApiLevel(j->getVersionInfo(), &maxApiLevel);
    926         }
    927     }
    928     for (auto i : mFunctions) {
    929         for (auto j: i.second->getSpecifications()) {
    930             updateMaxApiLevel(j->getVersionInfo(), &maxApiLevel);
    931         }
    932     }
    933     return maxApiLevel;
    934 }
    935 
    936 bool SystemSpecification::generateFiles(unsigned int maxApiLevel) const {
    937     bool success = generateHeaderFiles("include") &&
    938                    generateDocumentation("docs") &&
    939                    generateTestFiles("test", maxApiLevel) &&
    940                    generateStubsWhiteList("slangtest", maxApiLevel);
    941     if (success) {
    942         cout << "Successfully processed " << mTypes.size() << " types, " << mConstants.size()
    943              << " constants, and " << mFunctions.size() << " functions.\n";
    944     }
    945     return success;
    946 }
    947 
    948 string SystemSpecification::getHtmlAnchor(const string& name) const {
    949     Definition* d = nullptr;
    950     auto c = mConstants.find(name);
    951     if (c != mConstants.end()) {
    952         d = c->second;
    953     } else {
    954         auto t = mTypes.find(name);
    955         if (t != mTypes.end()) {
    956             d = t->second;
    957         } else {
    958             auto f = mFunctions.find(name);
    959             if (f != mFunctions.end()) {
    960                 d = f->second;
    961             } else {
    962                 return string();
    963             }
    964         }
    965     }
    966     ostringstream stream;
    967     stream << "<a href='" << d->getUrl() << "'>" << name << "</a>";
    968     return stream.str();
    969 }
    970