1 /***************************************************************************
2 **
3 ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the utilities of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 #include "legacyspecparser.h"
30 
31 #include <QDebug>
32 #include <QFile>
33 #include <QRegExp>
34 #include <QStringList>
35 #include <QTextStream>
36 
37 #ifdef SPECPARSER_DEBUG
38 #define qLegacySpecParserDebug qDebug
39 #else
40 #define qLegacySpecParserDebug QT_NO_QDEBUG_MACRO
41 #endif
42 
parse()43 bool LegacySpecParser::parse()
44 {
45     // Get the mapping form generic types to specific types suitable for use in C-headers
46     if (!parseTypeMap())
47         return false;
48 
49     // Open up a stream on the actual OpenGL function spec file
50     QFile file(specFileName());
51     if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
52         qWarning() << "Failed to open spec file:" << specFileName() << "Aborting";
53         return false;
54     }
55 
56     QTextStream stream(&file);
57 
58     // Extract the info that we need
59     parseFunctions(stream);
60     return true;
61 }
62 
parseTypeMap()63 bool LegacySpecParser::parseTypeMap()
64 {
65     QFile file(typeMapFileName());
66     if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
67         qWarning() << "Failed to open type file:" << typeMapFileName() << "Aborting";
68         return false;
69     }
70 
71     QTextStream stream(&file);
72 
73     static QRegExp typeMapRegExp("([^,]+)\\W+([^,]+)");
74 
75     while (!stream.atEnd()) {
76         QString line = stream.readLine();
77 
78         if (line.startsWith(QLatin1Char('#')))
79             continue;
80 
81         if (typeMapRegExp.indexIn(line) != -1) {
82             QString key = typeMapRegExp.cap(1).simplified();
83             QString value = typeMapRegExp.cap(2).simplified();
84 
85             // Special case for void
86             if (value == QLatin1String("*"))
87                 value = QStringLiteral("void");
88 
89             m_typeMap.insert(key, value);
90             qLegacySpecParserDebug() << "Found type mapping from" << key << "=>" << value;
91         }
92     }
93 
94     return true;
95 }
96 
parseEnums()97 void LegacySpecParser::parseEnums()
98 {
99 }
100 
parseFunctions(QTextStream & stream)101 void LegacySpecParser::parseFunctions(QTextStream &stream)
102 {
103     static QRegExp functionRegExp("^(\\w+)\\(.*\\)");
104     static QRegExp returnRegExp("^\\treturn\\s+(\\S+)");
105     static QRegExp argumentRegExp("param\\s+(\\S+)\\s+(\\S+) (\\S+) (\\S+)");
106     static QRegExp versionRegExp("^\\tversion\\s+(\\S+)");
107     static QRegExp deprecatedRegExp("^\\tdeprecated\\s+(\\S+)");
108     static QRegExp categoryRegExp("^\\tcategory\\s+(\\S+)");
109     static QRegExp categoryVersionRegExp("VERSION_(\\d)_(\\d)");
110     static QRegExp extToCoreVersionRegExp("passthru:\\s/\\*\\sOpenGL\\s(\\d)\\.(\\d)\\s.*\\sextensions:");
111     static QRegExp extToCoreRegExp("passthru:\\s/\\*\\s(ARB_\\S*)\\s.*\\*/");
112 
113     Function currentFunction;
114     VersionProfile currentVersionProfile;
115     QString currentCategory;
116     bool haveVersionInfo = false;
117     bool acceptCurrentFunctionInCore = false;
118     bool acceptCurrentFunctionInExtension = false;
119 
120     QHash<QString, Version> extensionsNowInCore;
121     Version extToCoreCurrentVersion;
122     int functionCount = 0;
123 
124     QSet<Version> versions;
125 
126     while (!stream.atEnd()) {
127         QString line = stream.readLine();
128         if (line.startsWith("#"))
129             continue;
130 
131         if (functionRegExp.indexIn(line) != -1) {
132 
133             if (!currentFunction.name.isEmpty()) {
134 
135                 // NB - Special handling!
136                 // Versions 4.2 and 4.3 (and probably newer) add functionality by
137                 // subsuming extensions such as ARB_texture_storage. However, some extensions
138                 // also include functions to interact with the EXT_direct_state_access
139                 // extension. These functions should be added to the DSA extension rather
140                 // than the core functionality. The core will already contain non-DSA
141                 // versions of these functions.
142                 if (acceptCurrentFunctionInCore && currentFunction.name.endsWith(QLatin1String("EXT"))) {
143                     acceptCurrentFunctionInCore = false;
144                     acceptCurrentFunctionInExtension = true;
145                     currentCategory = QStringLiteral("EXT_direct_state_access");
146                 }
147 
148                 // Finish off previous function (if any) by inserting it into the core
149                 // functionality or extension functionality (or both)
150                 if (acceptCurrentFunctionInCore) {
151                     m_functions.insert(currentVersionProfile, currentFunction);
152                     versions.insert(currentVersionProfile.version);
153                 }
154 
155                 if (acceptCurrentFunctionInExtension) {
156                     FunctionProfile fp;
157                     fp.profile = currentVersionProfile.profile;
158                     fp.function = currentFunction;
159                     m_extensionFunctions.insert(currentCategory, fp);
160                 }
161             }
162 
163             // Start a new function
164             ++functionCount;
165             haveVersionInfo = false;
166             acceptCurrentFunctionInCore = true;
167             acceptCurrentFunctionInExtension = false;
168             currentCategory = QString();
169             currentFunction = Function();
170 
171             // We assume a core function unless we find a deprecated flag (see below)
172             currentVersionProfile = VersionProfile();
173             currentVersionProfile.profile = VersionProfile::CoreProfile;
174 
175             // Extract the function name
176             QString functionName = functionRegExp.cap(1);
177             currentFunction.name = functionName;
178             qLegacySpecParserDebug() << "Found function:" << functionName;
179 
180         } else if (argumentRegExp.indexIn(line) != -1) {
181             // Extract info about this function argument
182             Argument arg;
183             arg.name = argumentRegExp.cap(1);
184 
185             QString type = argumentRegExp.cap(2); // Lookup in type map
186             arg.type = m_typeMap.value(type);
187 
188             QString direction = argumentRegExp.cap(3);
189             if (direction == QLatin1String("in")) {
190                 arg.direction = Argument::In;
191             } else if (direction == QLatin1String("out")) {
192                 arg.direction = Argument::Out;
193             } else {
194                 qWarning() << "Invalid argument direction found:" << direction;
195                 acceptCurrentFunctionInCore = false;
196             }
197 
198             QString mode = argumentRegExp.cap(4);
199             if (mode == QLatin1String("value")) {
200                 arg.mode = Argument::Value;
201             } else if (mode == QLatin1String("array")) {
202                 arg.mode = Argument::Array;
203             } else if (mode == QLatin1String("reference")) {
204                 arg.mode = Argument::Reference;
205             } else {
206                 qWarning() << "Invalid argument mode found:" << mode;
207                 acceptCurrentFunctionInCore = false;
208             }
209 
210             qLegacySpecParserDebug() << "    argument:" << arg.type << arg.name;
211             currentFunction.arguments.append(arg);
212 
213         } else if (returnRegExp.indexIn(line) != -1) {
214             // Lookup the return type from the typemap
215             QString returnTypeKey = returnRegExp.cap(1).simplified();
216             if (!m_typeMap.contains(returnTypeKey)) {
217                 qWarning() << "Unknown return type found:" << returnTypeKey;
218                 acceptCurrentFunctionInCore = false;
219             }
220             QString returnType = m_typeMap.value(returnTypeKey);
221             qLegacySpecParserDebug() << "    return type:" << returnType;
222             currentFunction.returnType = returnType;
223 
224         } else if (versionRegExp.indexIn(line) != -1 && !haveVersionInfo) { // Only use version line if no other source
225             // Extract the OpenGL version in which this function was introduced
226             QString version = versionRegExp.cap(1);
227             qLegacySpecParserDebug() << "    version:" << version;
228             QStringList parts = version.split(QLatin1Char('.'));
229             if (parts.size() != 2) {
230                 qWarning() << "Found invalid version number";
231                 continue;
232             }
233             int majorVersion = parts.first().toInt();
234             int minorVersion = parts.last().toInt();
235             Version v;
236             v.major = majorVersion;
237             v.minor = minorVersion;
238             currentVersionProfile.version = v;
239 
240         } else if (deprecatedRegExp.indexIn(line) != -1) {
241             // Extract the OpenGL version in which this function was deprecated.
242             // If it is OpenGL 3.1 then it must be a compatibility profile function
243             QString deprecatedVersion = deprecatedRegExp.cap(1).simplified();
244             if (deprecatedVersion == QLatin1String("3.1") && !inDeprecationException(currentFunction.name))
245                 currentVersionProfile.profile = VersionProfile::CompatibilityProfile;
246 
247         } else if (categoryRegExp.indexIn(line) != -1) {
248             // Extract the category for this function
249             QString category = categoryRegExp.cap(1).simplified();
250             qLegacySpecParserDebug() << "    category:" << category;
251 
252             if (categoryVersionRegExp.indexIn(category) != -1) {
253                 // Use the version info in the category in preference to the version
254                 // entry as this is more applicable and consistent
255                 int majorVersion = categoryVersionRegExp.cap(1).toInt();
256                 int minorVersion = categoryVersionRegExp.cap(2).toInt();
257 
258                 Version v;
259                 v.major = majorVersion;
260                 v.minor = minorVersion;
261                 currentVersionProfile.version = v;
262                 haveVersionInfo = true;
263 
264             } else {
265                 // Make a note of the extension name and tag this function as being part of an extension
266                 qLegacySpecParserDebug() << "Found category =" << category;
267                 currentCategory = category;
268                 acceptCurrentFunctionInExtension = true;
269 
270                 // See if this category (extension) is in our set of extensions that
271                 // have now been folded into the core feature set
272                 if (extensionsNowInCore.contains(category)) {
273                     currentVersionProfile.version = extensionsNowInCore.value(category);
274                     haveVersionInfo = true;
275                 } else {
276                     acceptCurrentFunctionInCore = false;
277                 }
278             }
279 
280         } else if (extToCoreVersionRegExp.indexIn(line) != -1) {
281             qLegacySpecParserDebug() << line;
282             int majorVersion = extToCoreVersionRegExp.cap(1).toInt();
283             int minorVersion = extToCoreVersionRegExp.cap(2).toInt();
284             extToCoreCurrentVersion.major = majorVersion;
285             extToCoreCurrentVersion.minor = minorVersion;
286 
287         } else if (extToCoreRegExp.indexIn(line) != -1) {
288             QString extension = extToCoreRegExp.cap(1);
289             extensionsNowInCore.insert(extension, extToCoreCurrentVersion);
290         }
291     }
292 
293     m_versions = versions.toList();
294     std::sort(m_versions.begin(), m_versions.end());
295 }
296 
inDeprecationException(const QString & functionName) const297 bool LegacySpecParser::inDeprecationException(const QString &functionName) const
298 {
299     return functionName == QLatin1String("TexImage3D");
300 }
301