summaryrefslogtreecommitdiff
path: root/script_api/Specification.cpp
diff options
context:
space:
mode:
authorJean-Luc Brouillet <jeanluc@google.com>2017-01-08 17:35:31 -0800
committerJean-Luc Brouillet <jeanluc@google.com>2017-01-09 11:15:16 -0800
commit2a85b6b9d6f9cb8f1b20d573c1c5ceafe901b011 (patch)
tree038e6e8d5847568e0b560ff12ebee5f85d670542 /script_api/Specification.cpp
parent7f1125d183917d99123367eba3c8393bfde58a20 (diff)
downloadrs-2a85b6b9d6f9cb8f1b20d573c1c5ceafe901b011.tar.gz
Move scriptc to script_api/include.
Part 1 of the directory re-organization. We're renaming the "api" directory to "script_api" directory to distinguish between our control api (java or c++) and our script api. We're also moving the scriptc directory under that newly renamed directory, and change its name to the more appropriate "include". Test: scriptc/generate.sh Test: compiled ImageProcessing_jb Change-Id: I00c3bbf5728b652d1541ebe4123717f6ab639f09
Diffstat (limited to 'script_api/Specification.cpp')
-rw-r--r--script_api/Specification.cpp965
1 files changed, 965 insertions, 0 deletions
diff --git a/script_api/Specification.cpp b/script_api/Specification.cpp
new file mode 100644
index 00000000..c103dc1c
--- /dev/null
+++ b/script_api/Specification.cpp
@@ -0,0 +1,965 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <cctype>
+#include <cstdlib>
+#include <fstream>
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <strings.h>
+
+#include "Generator.h"
+#include "Scanner.h"
+#include "Specification.h"
+#include "Utilities.h"
+
+using namespace std;
+
+// API level when RenderScript was added.
+const unsigned int MIN_API_LEVEL = 9;
+
+const NumericalType TYPES[] = {
+ {"f16", "FLOAT_16", "half", "short", FLOATING_POINT, 11, 5},
+ {"f32", "FLOAT_32", "float", "float", FLOATING_POINT, 24, 8},
+ {"f64", "FLOAT_64", "double", "double", FLOATING_POINT, 53, 11},
+ {"i8", "SIGNED_8", "char", "byte", SIGNED_INTEGER, 7, 0},
+ {"u8", "UNSIGNED_8", "uchar", "byte", UNSIGNED_INTEGER, 8, 0},
+ {"i16", "SIGNED_16", "short", "short", SIGNED_INTEGER, 15, 0},
+ {"u16", "UNSIGNED_16", "ushort", "short", UNSIGNED_INTEGER, 16, 0},
+ {"i32", "SIGNED_32", "int", "int", SIGNED_INTEGER, 31, 0},
+ {"u32", "UNSIGNED_32", "uint", "int", UNSIGNED_INTEGER, 32, 0},
+ {"i64", "SIGNED_64", "long", "long", SIGNED_INTEGER, 63, 0},
+ {"u64", "UNSIGNED_64", "ulong", "long", UNSIGNED_INTEGER, 64, 0},
+};
+
+const int NUM_TYPES = sizeof(TYPES) / sizeof(TYPES[0]);
+
+static const char kTagUnreleased[] = "UNRELEASED";
+
+// Patterns that get substituted with C type or RS Data type names in function
+// names, arguments, return types, and inlines.
+static const string kCTypePatterns[] = {"#1", "#2", "#3", "#4"};
+static const string kRSTypePatterns[] = {"#RST_1", "#RST_2", "#RST_3", "#RST_4"};
+
+// The singleton of the collected information of all the spec files.
+SystemSpecification systemSpecification;
+
+// Returns the index in TYPES for the provided cType
+static int findCType(const string& cType) {
+ for (int i = 0; i < NUM_TYPES; i++) {
+ if (cType == TYPES[i].cType) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/* Converts a string like "u8, u16" to a vector of "ushort", "uint".
+ * For non-numerical types, we don't need to convert the abbreviation.
+ */
+static vector<string> convertToTypeVector(const string& input) {
+ // First convert the string to an array of strings.
+ vector<string> entries;
+ stringstream stream(input);
+ string entry;
+ while (getline(stream, entry, ',')) {
+ trimSpaces(&entry);
+ entries.push_back(entry);
+ }
+
+ /* Second, we look for present numerical types. We do it this way
+ * so the order of numerical types is always the same, no matter
+ * how specified in the spec file.
+ */
+ vector<string> result;
+ for (auto t : TYPES) {
+ for (auto i = entries.begin(); i != entries.end(); ++i) {
+ if (*i == t.specType) {
+ result.push_back(t.cType);
+ entries.erase(i);
+ break;
+ }
+ }
+ }
+
+ // Add the remaining; they are not numerical types.
+ for (auto s : entries) {
+ result.push_back(s);
+ }
+
+ return result;
+}
+
+// Returns true if each entry in typeVector is an RS numerical type
+static bool isRSTValid(const vector<string> &typeVector) {
+ for (auto type: typeVector) {
+ if (findCType(type) == -1)
+ return false;
+ }
+ return true;
+}
+
+void getVectorSizeAndBaseType(const string& type, string& vectorSize, string& baseType) {
+ vectorSize = "1";
+ baseType = type;
+
+ /* If it's a vector type, we need to split the base type from the size.
+ * We know that's it's a vector type if the last character is a digit and
+ * the rest is an actual base type. We used to only verify the first part,
+ * which created a problem with rs_matrix2x2.
+ */
+ const int last = type.size() - 1;
+ const char lastChar = type[last];
+ if (lastChar >= '0' && lastChar <= '9') {
+ const string trimmed = type.substr(0, last);
+ int i = findCType(trimmed);
+ if (i >= 0) {
+ baseType = trimmed;
+ vectorSize = lastChar;
+ }
+ }
+}
+
+void ParameterDefinition::parseParameterDefinition(const string& type, const string& name,
+ const string& testOption, int lineNumber,
+ bool isReturn, Scanner* scanner) {
+ rsType = type;
+ specName = name;
+
+ // Determine if this is an output.
+ isOutParameter = isReturn || charRemoved('*', &rsType);
+
+ getVectorSizeAndBaseType(rsType, mVectorSize, rsBaseType);
+ typeIndex = findCType(rsBaseType);
+
+ if (mVectorSize == "3") {
+ vectorWidth = "4";
+ } else {
+ vectorWidth = mVectorSize;
+ }
+
+ /* Create variable names to be used in the java and .rs files. Because x and
+ * y are reserved in .rs files, we prefix variable names with "in" or "out".
+ */
+ if (isOutParameter) {
+ variableName = "out";
+ if (!specName.empty()) {
+ variableName += capitalize(specName);
+ } else if (!isReturn) {
+ scanner->error(lineNumber) << "Should have a name.\n";
+ }
+ doubleVariableName = variableName + "Double";
+ } else {
+ variableName = "in";
+ if (specName.empty()) {
+ scanner->error(lineNumber) << "Should have a name.\n";
+ }
+ variableName += capitalize(specName);
+ doubleVariableName = variableName + "Double";
+ }
+ rsAllocName = "gAlloc" + capitalize(variableName);
+ javaAllocName = variableName;
+ javaArrayName = "array" + capitalize(javaAllocName);
+
+ // Process the option.
+ undefinedIfOutIsNan = false;
+ compatibleTypeIndex = -1;
+ if (!testOption.empty()) {
+ if (testOption.compare(0, 6, "range(") == 0) {
+ size_t pComma = testOption.find(',');
+ size_t pParen = testOption.find(')');
+ if (pComma == string::npos || pParen == string::npos) {
+ scanner->error(lineNumber) << "Incorrect range " << testOption << "\n";
+ } else {
+ minValue = testOption.substr(6, pComma - 6);
+ maxValue = testOption.substr(pComma + 1, pParen - pComma - 1);
+ }
+ } else if (testOption.compare(0, 6, "above(") == 0) {
+ size_t pParen = testOption.find(')');
+ if (pParen == string::npos) {
+ scanner->error(lineNumber) << "Incorrect testOption " << testOption << "\n";
+ } else {
+ smallerParameter = testOption.substr(6, pParen - 6);
+ }
+ } else if (testOption.compare(0, 11, "compatible(") == 0) {
+ size_t pParen = testOption.find(')');
+ if (pParen == string::npos) {
+ scanner->error(lineNumber) << "Incorrect testOption " << testOption << "\n";
+ } else {
+ compatibleTypeIndex = findCType(testOption.substr(11, pParen - 11));
+ }
+ } else if (testOption.compare(0, 11, "conditional") == 0) {
+ undefinedIfOutIsNan = true;
+ } else {
+ scanner->error(lineNumber) << "Unrecognized testOption " << testOption << "\n";
+ }
+ }
+
+ isFloatType = false;
+ if (typeIndex >= 0) {
+ javaBaseType = TYPES[typeIndex].javaType;
+ specType = TYPES[typeIndex].specType;
+ isFloatType = TYPES[typeIndex].exponentBits > 0;
+ }
+ if (!minValue.empty()) {
+ if (typeIndex < 0 || TYPES[typeIndex].kind != FLOATING_POINT) {
+ scanner->error(lineNumber) << "range(,) is only supported for floating point\n";
+ }
+ }
+}
+
+bool VersionInfo::scan(Scanner* scanner, unsigned int maxApiLevel) {
+ if (scanner->findOptionalTag("version:")) {
+ const string s = scanner->getValue();
+ if (s.compare(0, sizeof(kTagUnreleased), kTagUnreleased) == 0) {
+ // The API is still under development and does not have
+ // an official version number.
+ minVersion = maxVersion = kUnreleasedVersion;
+ } else {
+ sscanf(s.c_str(), "%u %u", &minVersion, &maxVersion);
+ if (minVersion && minVersion < MIN_API_LEVEL) {
+ scanner->error() << "Minimum version must >= 9\n";
+ }
+ if (minVersion == MIN_API_LEVEL) {
+ minVersion = 0;
+ }
+ if (maxVersion && maxVersion < MIN_API_LEVEL) {
+ scanner->error() << "Maximum version must >= 9\n";
+ }
+ }
+ }
+ if (scanner->findOptionalTag("size:")) {
+ sscanf(scanner->getValue().c_str(), "%i", &intSize);
+ }
+
+ if (maxVersion > maxApiLevel) {
+ maxVersion = maxApiLevel;
+ }
+
+ return minVersion == 0 || minVersion <= maxApiLevel;
+}
+
+Definition::Definition(const std::string& name)
+ : mName(name), mDeprecatedApiLevel(0), mHidden(false), mFinalVersion(-1) {
+}
+
+void Definition::updateFinalVersion(const VersionInfo& info) {
+ /* We set it if:
+ * - We have never set mFinalVersion before, or
+ * - The max version is 0, which means we have not expired this API, or
+ * - We have a max that's later than what we currently have.
+ */
+ if (mFinalVersion < 0 || info.maxVersion == 0 ||
+ (mFinalVersion > 0 && info.maxVersion > mFinalVersion)) {
+ mFinalVersion = info.maxVersion;
+ }
+}
+
+void Definition::scanDocumentationTags(Scanner* scanner, bool firstOccurence,
+ const SpecFile* specFile) {
+ if (scanner->findOptionalTag("hidden:")) {
+ scanner->checkNoValue();
+ mHidden = true;
+ }
+ if (scanner->findOptionalTag("deprecated:")) {
+ string value = scanner->getValue();
+ size_t pComma = value.find(", ");
+ if (pComma != string::npos) {
+ mDeprecatedMessage = value.substr(pComma + 2);
+ value.erase(pComma);
+ }
+ sscanf(value.c_str(), "%i", &mDeprecatedApiLevel);
+ if (mDeprecatedApiLevel <= 0) {
+ scanner->error() << "deprecated entries should have a level > 0\n";
+ }
+ }
+ if (firstOccurence) {
+ if (scanner->findTag("summary:")) {
+ mSummary = scanner->getValue();
+ }
+ if (scanner->findTag("description:")) {
+ scanner->checkNoValue();
+ while (scanner->findOptionalTag("")) {
+ mDescription.push_back(scanner->getValue());
+ }
+ }
+ mUrl = specFile->getDetailedDocumentationUrl() + "#android_rs:" + mName;
+ } else if (scanner->findOptionalTag("summary:")) {
+ scanner->error() << "Only the first specification should have a summary.\n";
+ }
+}
+
+Constant::~Constant() {
+ for (auto i : mSpecifications) {
+ delete i;
+ }
+}
+
+Type::~Type() {
+ for (auto i : mSpecifications) {
+ delete i;
+ }
+}
+
+Function::Function(const string& name) : Definition(name) {
+ mCapitalizedName = capitalize(mName);
+}
+
+Function::~Function() {
+ for (auto i : mSpecifications) {
+ delete i;
+ }
+}
+
+bool Function::someParametersAreDocumented() const {
+ for (auto p : mParameters) {
+ if (!p->documentation.empty()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Function::addParameter(ParameterEntry* entry, Scanner* scanner) {
+ for (auto i : mParameters) {
+ if (i->name == entry->name) {
+ // It's a duplicate.
+ if (!entry->documentation.empty()) {
+ scanner->error(entry->lineNumber)
+ << "Only the first occurence of an arg should have the "
+ "documentation.\n";
+ }
+ return;
+ }
+ }
+ mParameters.push_back(entry);
+}
+
+void Function::addReturn(ParameterEntry* entry, Scanner* scanner) {
+ if (entry->documentation.empty()) {
+ return;
+ }
+ if (!mReturnDocumentation.empty()) {
+ scanner->error() << "ret: should be documented only for the first variant\n";
+ }
+ mReturnDocumentation = entry->documentation;
+}
+
+void ConstantSpecification::scanConstantSpecification(Scanner* scanner, SpecFile* specFile,
+ unsigned int maxApiLevel) {
+ string name = scanner->getValue();
+ VersionInfo info;
+ if (!info.scan(scanner, maxApiLevel)) {
+ cout << "Skipping some " << name << " definitions.\n";
+ scanner->skipUntilTag("end:");
+ return;
+ }
+
+ bool created = false;
+ Constant* constant = systemSpecification.findOrCreateConstant(name, &created);
+ ConstantSpecification* spec = new ConstantSpecification(constant);
+ constant->addSpecification(spec);
+ constant->updateFinalVersion(info);
+ specFile->addConstantSpecification(spec, created);
+ spec->mVersionInfo = info;
+
+ if (scanner->findTag("value:")) {
+ spec->mValue = scanner->getValue();
+ }
+ constant->scanDocumentationTags(scanner, created, specFile);
+
+ scanner->findTag("end:");
+}
+
+void TypeSpecification::scanTypeSpecification(Scanner* scanner, SpecFile* specFile,
+ unsigned int maxApiLevel) {
+ string name = scanner->getValue();
+ VersionInfo info;
+ if (!info.scan(scanner, maxApiLevel)) {
+ cout << "Skipping some " << name << " definitions.\n";
+ scanner->skipUntilTag("end:");
+ return;
+ }
+
+ bool created = false;
+ Type* type = systemSpecification.findOrCreateType(name, &created);
+ TypeSpecification* spec = new TypeSpecification(type);
+ type->addSpecification(spec);
+ type->updateFinalVersion(info);
+ specFile->addTypeSpecification(spec, created);
+ spec->mVersionInfo = info;
+
+ if (scanner->findOptionalTag("simple:")) {
+ spec->mKind = SIMPLE;
+ spec->mSimpleType = scanner->getValue();
+ }
+ if (scanner->findOptionalTag("rs_object:")) {
+ spec->mKind = RS_OBJECT;
+ }
+ if (scanner->findOptionalTag("struct:")) {
+ spec->mKind = STRUCT;
+ spec->mStructName = scanner->getValue();
+ while (scanner->findOptionalTag("field:")) {
+ string s = scanner->getValue();
+ string comment;
+ scanner->parseDocumentation(&s, &comment);
+ spec->mFields.push_back(s);
+ spec->mFieldComments.push_back(comment);
+ }
+ }
+ if (scanner->findOptionalTag("enum:")) {
+ spec->mKind = ENUM;
+ spec->mEnumName = scanner->getValue();
+ while (scanner->findOptionalTag("value:")) {
+ string s = scanner->getValue();
+ string comment;
+ scanner->parseDocumentation(&s, &comment);
+ spec->mValues.push_back(s);
+ spec->mValueComments.push_back(comment);
+ }
+ }
+ if (scanner->findOptionalTag("attrib:")) {
+ spec->mAttribute = scanner->getValue();
+ }
+ type->scanDocumentationTags(scanner, created, specFile);
+
+ scanner->findTag("end:");
+}
+
+FunctionSpecification::~FunctionSpecification() {
+ for (auto i : mParameters) {
+ delete i;
+ }
+ delete mReturn;
+ for (auto i : mPermutations) {
+ delete i;
+ }
+}
+
+string FunctionSpecification::expandRSTypeInString(const string &s,
+ const string &pattern,
+ const string &cTypeStr) const {
+ // Find index of numerical type corresponding to cTypeStr. The case where
+ // pattern is found in s but cTypeStr is not a numerical type is checked in
+ // checkRSTPatternValidity.
+ int typeIdx = findCType(cTypeStr);
+ if (typeIdx == -1) {
+ return s;
+ }
+ // If index exists, perform replacement.
+ return stringReplace(s, pattern, TYPES[typeIdx].rsDataType);
+}
+
+string FunctionSpecification::expandString(string s,
+ int replacementIndexes[MAX_REPLACEABLES]) const {
+
+
+ for (unsigned idx = 0; idx < mReplaceables.size(); idx ++) {
+ string toString = mReplaceables[idx][replacementIndexes[idx]];
+
+ // replace #RST_i patterns with RS datatype corresponding to toString
+ s = expandRSTypeInString(s, kRSTypePatterns[idx], toString);
+
+ // replace #i patterns with C type from mReplaceables
+ s = stringReplace(s, kCTypePatterns[idx], toString);
+ }
+
+ return s;
+}
+
+void FunctionSpecification::expandStringVector(const vector<string>& in,
+ int replacementIndexes[MAX_REPLACEABLES],
+ vector<string>* out) const {
+ out->clear();
+ for (vector<string>::const_iterator iter = in.begin(); iter != in.end(); iter++) {
+ out->push_back(expandString(*iter, replacementIndexes));
+ }
+}
+
+void FunctionSpecification::createPermutations(Function* function, Scanner* scanner) {
+ int start[MAX_REPLACEABLES];
+ int end[MAX_REPLACEABLES];
+ for (int i = 0; i < MAX_REPLACEABLES; i++) {
+ if (i < (int)mReplaceables.size()) {
+ start[i] = 0;
+ end[i] = mReplaceables[i].size();
+ } else {
+ start[i] = -1;
+ end[i] = 0;
+ }
+ }
+ int replacementIndexes[MAX_REPLACEABLES];
+ // TODO: These loops assume that MAX_REPLACEABLES is 4.
+ for (replacementIndexes[3] = start[3]; replacementIndexes[3] < end[3];
+ replacementIndexes[3]++) {
+ for (replacementIndexes[2] = start[2]; replacementIndexes[2] < end[2];
+ replacementIndexes[2]++) {
+ for (replacementIndexes[1] = start[1]; replacementIndexes[1] < end[1];
+ replacementIndexes[1]++) {
+ for (replacementIndexes[0] = start[0]; replacementIndexes[0] < end[0];
+ replacementIndexes[0]++) {
+ auto p = new FunctionPermutation(function, this, replacementIndexes, scanner);
+ mPermutations.push_back(p);
+ }
+ }
+ }
+ }
+}
+
+string FunctionSpecification::getName(int replacementIndexes[MAX_REPLACEABLES]) const {
+ return expandString(mUnexpandedName, replacementIndexes);
+}
+
+void FunctionSpecification::getReturn(int replacementIndexes[MAX_REPLACEABLES],
+ std::string* retType, int* lineNumber) const {
+ *retType = expandString(mReturn->type, replacementIndexes);
+ *lineNumber = mReturn->lineNumber;
+}
+
+void FunctionSpecification::getParam(size_t index, int replacementIndexes[MAX_REPLACEABLES],
+ std::string* type, std::string* name, std::string* testOption,
+ int* lineNumber) const {
+ ParameterEntry* p = mParameters[index];
+ *type = expandString(p->type, replacementIndexes);
+ *name = p->name;
+ *testOption = expandString(p->testOption, replacementIndexes);
+ *lineNumber = p->lineNumber;
+}
+
+void FunctionSpecification::getInlines(int replacementIndexes[MAX_REPLACEABLES],
+ std::vector<std::string>* inlines) const {
+ expandStringVector(mInline, replacementIndexes, inlines);
+}
+
+void FunctionSpecification::parseTest(Scanner* scanner) {
+ const string value = scanner->getValue();
+ if (value == "scalar" || value == "vector" || value == "noverify" || value == "custom" ||
+ value == "none") {
+ mTest = value;
+ } else if (value.compare(0, 7, "limited") == 0) {
+ mTest = "limited";
+ if (value.compare(7, 1, "(") == 0) {
+ size_t pParen = value.find(')');
+ if (pParen == string::npos) {
+ scanner->error() << "Incorrect test: \"" << value << "\"\n";
+ } else {
+ mPrecisionLimit = value.substr(8, pParen - 8);
+ }
+ }
+ } else {
+ scanner->error() << "Unrecognized test option: \"" << value << "\"\n";
+ }
+}
+
+bool FunctionSpecification::hasTests(unsigned int versionOfTestFiles) const {
+ if (mVersionInfo.maxVersion != 0 && mVersionInfo.maxVersion < versionOfTestFiles) {
+ return false;
+ }
+ if (mTest == "none") {
+ return false;
+ }
+ return true;
+}
+
+void FunctionSpecification::checkRSTPatternValidity(const string &inlineStr, bool allow,
+ Scanner *scanner) {
+ for (int i = 0; i < MAX_REPLACEABLES; i ++) {
+ bool patternFound = inlineStr.find(kRSTypePatterns[i]) != string::npos;
+
+ if (patternFound) {
+ if (!allow) {
+ scanner->error() << "RST_i pattern not allowed here\n";
+ }
+ else if (mIsRSTAllowed[i] == false) {
+ scanner->error() << "Found pattern \"" << kRSTypePatterns[i]
+ << "\" in spec. But some entry in the corresponding"
+ << " parameter list cannot be translated to an RS type\n";
+ }
+ }
+ }
+}
+
+void FunctionSpecification::scanFunctionSpecification(Scanner* scanner, SpecFile* specFile,
+ unsigned int maxApiLevel) {
+ // Some functions like convert have # part of the name. Truncate at that point.
+ const string& unexpandedName = scanner->getValue();
+ string name = unexpandedName;
+ size_t p = name.find('#');
+ if (p != string::npos) {
+ if (p > 0 && name[p - 1] == '_') {
+ p--;
+ }
+ name.erase(p);
+ }
+ VersionInfo info;
+ if (!info.scan(scanner, maxApiLevel)) {
+ cout << "Skipping some " << name << " definitions.\n";
+ scanner->skipUntilTag("end:");
+ return;
+ }
+
+ bool created = false;
+ Function* function = systemSpecification.findOrCreateFunction(name, &created);
+ FunctionSpecification* spec = new FunctionSpecification(function);
+ function->addSpecification(spec);
+ function->updateFinalVersion(info);
+ specFile->addFunctionSpecification(spec, created);
+
+ spec->mUnexpandedName = unexpandedName;
+ spec->mTest = "scalar"; // default
+ spec->mVersionInfo = info;
+
+ if (scanner->findOptionalTag("internal:")) {
+ spec->mInternal = (scanner->getValue() == "true");
+ }
+ if (scanner->findOptionalTag("intrinsic:")) {
+ spec->mIntrinsic = (scanner->getValue() == "true");
+ }
+ if (scanner->findOptionalTag("attrib:")) {
+ spec->mAttribute = scanner->getValue();
+ }
+ if (scanner->findOptionalTag("w:")) {
+ vector<string> t;
+ if (scanner->getValue().find("1") != string::npos) {
+ t.push_back("");
+ }
+ if (scanner->getValue().find("2") != string::npos) {
+ t.push_back("2");
+ }
+ if (scanner->getValue().find("3") != string::npos) {
+ t.push_back("3");
+ }
+ if (scanner->getValue().find("4") != string::npos) {
+ t.push_back("4");
+ }
+ spec->mReplaceables.push_back(t);
+ // RST_i pattern not applicable for width.
+ spec->mIsRSTAllowed.push_back(false);
+ }
+
+ while (scanner->findOptionalTag("t:")) {
+ spec->mReplaceables.push_back(convertToTypeVector(scanner->getValue()));
+ spec->mIsRSTAllowed.push_back(isRSTValid(spec->mReplaceables.back()));
+ }
+
+ // Disallow RST_* pattern in function name
+ // FIXME the line number for this error would be wrong
+ spec->checkRSTPatternValidity(unexpandedName, false, scanner);
+
+ if (scanner->findTag("ret:")) {
+ ParameterEntry* p = scanner->parseArgString(true);
+ function->addReturn(p, scanner);
+ spec->mReturn = p;
+
+ // Disallow RST_* pattern in return type
+ spec->checkRSTPatternValidity(p->type, false, scanner);
+ }
+ while (scanner->findOptionalTag("arg:")) {
+ ParameterEntry* p = scanner->parseArgString(false);
+ function->addParameter(p, scanner);
+ spec->mParameters.push_back(p);
+
+ // Disallow RST_* pattern in parameter type or testOption
+ spec->checkRSTPatternValidity(p->type, false, scanner);
+ spec->checkRSTPatternValidity(p->testOption, false, scanner);
+ }
+
+ function->scanDocumentationTags(scanner, created, specFile);
+
+ if (scanner->findOptionalTag("inline:")) {
+ scanner->checkNoValue();
+ while (scanner->findOptionalTag("")) {
+ spec->mInline.push_back(scanner->getValue());
+
+ // Allow RST_* pattern in inline definitions
+ spec->checkRSTPatternValidity(spec->mInline.back(), true, scanner);
+ }
+ }
+ if (scanner->findOptionalTag("test:")) {
+ spec->parseTest(scanner);
+ }
+
+ scanner->findTag("end:");
+
+ spec->createPermutations(function, scanner);
+}
+
+FunctionPermutation::FunctionPermutation(Function* func, FunctionSpecification* spec,
+ int replacementIndexes[MAX_REPLACEABLES], Scanner* scanner)
+ : mReturn(nullptr), mInputCount(0), mOutputCount(0) {
+ // We expand the strings now to make capitalization easier. The previous code preserved
+ // the #n
+ // markers just before emitting, which made capitalization difficult.
+ mName = spec->getName(replacementIndexes);
+ mNameTrunk = func->getName();
+ mTest = spec->getTest();
+ mPrecisionLimit = spec->getPrecisionLimit();
+ spec->getInlines(replacementIndexes, &mInline);
+
+ mHasFloatAnswers = false;
+ for (size_t i = 0; i < spec->getNumberOfParams(); i++) {
+ string type, name, testOption;
+ int lineNumber = 0;
+ spec->getParam(i, replacementIndexes, &type, &name, &testOption, &lineNumber);
+ ParameterDefinition* def = new ParameterDefinition();
+ def->parseParameterDefinition(type, name, testOption, lineNumber, false, scanner);
+ if (def->isOutParameter) {
+ mOutputCount++;
+ } else {
+ mInputCount++;
+ }
+
+ if (def->typeIndex < 0 && mTest != "none") {
+ scanner->error(lineNumber)
+ << "Could not find " << def->rsBaseType
+ << " while generating automated tests. Use test: none if not needed.\n";
+ }
+ if (def->isOutParameter && def->isFloatType) {
+ mHasFloatAnswers = true;
+ }
+ mParams.push_back(def);
+ }
+
+ string retType;
+ int lineNumber = 0;
+ spec->getReturn(replacementIndexes, &retType, &lineNumber);
+ if (!retType.empty()) {
+ mReturn = new ParameterDefinition();
+ mReturn->parseParameterDefinition(retType, "", "", lineNumber, true, scanner);
+ if (mReturn->isFloatType) {
+ mHasFloatAnswers = true;
+ }
+ mOutputCount++;
+ }
+}
+
+FunctionPermutation::~FunctionPermutation() {
+ for (auto i : mParams) {
+ delete i;
+ }
+ delete mReturn;
+}
+
+SpecFile::SpecFile(const string& specFileName) : mSpecFileName(specFileName) {
+ string core = mSpecFileName;
+ // Remove .spec
+ size_t l = core.length();
+ const char SPEC[] = ".spec";
+ const int SPEC_SIZE = sizeof(SPEC) - 1;
+ const int start = l - SPEC_SIZE;
+ if (start >= 0 && core.compare(start, SPEC_SIZE, SPEC) == 0) {
+ core.erase(start);
+ }
+
+ // The header file name should have the same base but with a ".rsh" extension.
+ mHeaderFileName = core + ".rsh";
+ mDetailedDocumentationUrl = core + ".html";
+}
+
+void SpecFile::addConstantSpecification(ConstantSpecification* spec, bool hasDocumentation) {
+ mConstantSpecificationsList.push_back(spec);
+ if (hasDocumentation) {
+ Constant* constant = spec->getConstant();
+ mDocumentedConstants.insert(pair<string, Constant*>(constant->getName(), constant));
+ }
+}
+
+void SpecFile::addTypeSpecification(TypeSpecification* spec, bool hasDocumentation) {
+ mTypeSpecificationsList.push_back(spec);
+ if (hasDocumentation) {
+ Type* type = spec->getType();
+ mDocumentedTypes.insert(pair<string, Type*>(type->getName(), type));
+ }
+}
+
+void SpecFile::addFunctionSpecification(FunctionSpecification* spec, bool hasDocumentation) {
+ mFunctionSpecificationsList.push_back(spec);
+ if (hasDocumentation) {
+ Function* function = spec->getFunction();
+ mDocumentedFunctions.insert(pair<string, Function*>(function->getName(), function));
+ }
+}
+
+// Read the specification, adding the definitions to the global functions map.
+bool SpecFile::readSpecFile(unsigned int maxApiLevel) {
+ FILE* specFile = fopen(mSpecFileName.c_str(), "rt");
+ if (!specFile) {
+ cerr << "Error opening input file: " << mSpecFileName << "\n";
+ return false;
+ }
+
+ Scanner scanner(mSpecFileName, specFile);
+
+ // Scan the header that should start the file.
+ scanner.skipBlankEntries();
+ if (scanner.findTag("header:")) {
+ if (scanner.findTag("summary:")) {
+ mBriefDescription = scanner.getValue();
+ }
+ if (scanner.findTag("description:")) {
+ scanner.checkNoValue();
+ while (scanner.findOptionalTag("")) {
+ mFullDescription.push_back(scanner.getValue());
+ }
+ }
+ if (scanner.findOptionalTag("include:")) {
+ scanner.checkNoValue();
+ while (scanner.findOptionalTag("")) {
+ mVerbatimInclude.push_back(scanner.getValue());
+ }
+ }
+ scanner.findTag("end:");
+ }
+
+ while (1) {
+ scanner.skipBlankEntries();
+ if (scanner.atEnd()) {
+ break;
+ }
+ const string tag = scanner.getNextTag();
+ if (tag == "function:") {
+ FunctionSpecification::scanFunctionSpecification(&scanner, this, maxApiLevel);
+ } else if (tag == "type:") {
+ TypeSpecification::scanTypeSpecification(&scanner, this, maxApiLevel);
+ } else if (tag == "constant:") {
+ ConstantSpecification::scanConstantSpecification(&scanner, this, maxApiLevel);
+ } else {
+ scanner.error() << "Expected function:, type:, or constant:. Found: " << tag << "\n";
+ return false;
+ }
+ }
+
+ fclose(specFile);
+ return scanner.getErrorCount() == 0;
+}
+
+SystemSpecification::~SystemSpecification() {
+ for (auto i : mConstants) {
+ delete i.second;
+ }
+ for (auto i : mTypes) {
+ delete i.second;
+ }
+ for (auto i : mFunctions) {
+ delete i.second;
+ }
+ for (auto i : mSpecFiles) {
+ delete i;
+ }
+}
+
+// Returns the named entry in the map. Creates it if it's not there.
+template <class T>
+T* findOrCreate(const string& name, map<string, T*>* map, bool* created) {
+ auto iter = map->find(name);
+ if (iter != map->end()) {
+ *created = false;
+ return iter->second;
+ }
+ *created = true;
+ T* f = new T(name);
+ map->insert(pair<string, T*>(name, f));
+ return f;
+}
+
+Constant* SystemSpecification::findOrCreateConstant(const string& name, bool* created) {
+ return findOrCreate<Constant>(name, &mConstants, created);
+}
+
+Type* SystemSpecification::findOrCreateType(const string& name, bool* created) {
+ return findOrCreate<Type>(name, &mTypes, created);
+}
+
+Function* SystemSpecification::findOrCreateFunction(const string& name, bool* created) {
+ return findOrCreate<Function>(name, &mFunctions, created);
+}
+
+bool SystemSpecification::readSpecFile(const string& fileName, unsigned int maxApiLevel) {
+ SpecFile* spec = new SpecFile(fileName);
+ if (!spec->readSpecFile(maxApiLevel)) {
+ cerr << fileName << ": Failed to parse.\n";
+ return false;
+ }
+ mSpecFiles.push_back(spec);
+ return true;
+}
+
+
+static void updateMaxApiLevel(const VersionInfo& info, unsigned int* maxApiLevel) {
+ if (info.minVersion == VersionInfo::kUnreleasedVersion) {
+ // Ignore development API level in consideration of max API level.
+ return;
+ }
+ *maxApiLevel = max(*maxApiLevel, max(info.minVersion, info.maxVersion));
+}
+
+unsigned int SystemSpecification::getMaximumApiLevel() {
+ unsigned int maxApiLevel = 0;
+ for (auto i : mConstants) {
+ for (auto j: i.second->getSpecifications()) {
+ updateMaxApiLevel(j->getVersionInfo(), &maxApiLevel);
+ }
+ }
+ for (auto i : mTypes) {
+ for (auto j: i.second->getSpecifications()) {
+ updateMaxApiLevel(j->getVersionInfo(), &maxApiLevel);
+ }
+ }
+ for (auto i : mFunctions) {
+ for (auto j: i.second->getSpecifications()) {
+ updateMaxApiLevel(j->getVersionInfo(), &maxApiLevel);
+ }
+ }
+ return maxApiLevel;
+}
+
+bool SystemSpecification::generateFiles(bool forVerification, unsigned int maxApiLevel) const {
+ bool success = generateHeaderFiles("include") &&
+ generateDocumentation("docs", forVerification) &&
+ generateTestFiles("test", maxApiLevel) &&
+ generateStubsWhiteList("slangtest", maxApiLevel);
+ if (success) {
+ cout << "Successfully processed " << mTypes.size() << " types, " << mConstants.size()
+ << " constants, and " << mFunctions.size() << " functions.\n";
+ }
+ return success;
+}
+
+string SystemSpecification::getHtmlAnchor(const string& name) const {
+ Definition* d = nullptr;
+ auto c = mConstants.find(name);
+ if (c != mConstants.end()) {
+ d = c->second;
+ } else {
+ auto t = mTypes.find(name);
+ if (t != mTypes.end()) {
+ d = t->second;
+ } else {
+ auto f = mFunctions.find(name);
+ if (f != mFunctions.end()) {
+ d = f->second;
+ } else {
+ return string();
+ }
+ }
+ }
+ ostringstream stream;
+ stream << "<a href='" << d->getUrl() << "'>" << name << "</a>";
+ return stream.str();
+}