1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the qmake application 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 #ifndef QMAKEPARSER_H
30 #define QMAKEPARSER_H
31 
32 #include "qmake_global.h"
33 #include "qmakevfs.h"
34 #include "proitems.h"
35 
36 #include <qhash.h>
37 #include <qstack.h>
38 #ifdef PROPARSER_THREAD_SAFE
39 # include <qmutex.h>
40 # include <qwaitcondition.h>
41 #endif
42 
43 QT_BEGIN_NAMESPACE
44 class QMAKE_EXPORT QMakeParserHandler
45 {
46 public:
47     enum {
48         CategoryMask = 0xf00,
49         InfoMessage = 0x100,
50         WarningMessage = 0x200,
51         ErrorMessage = 0x300,
52 
53         SourceMask = 0xf0,
54         SourceParser = 0,
55 
56         CodeMask = 0xf,
57         WarnLanguage = 0,
58         WarnDeprecated,
59 
60         ParserWarnLanguage = SourceParser | WarningMessage | WarnLanguage,
61         ParserWarnDeprecated = SourceParser | WarningMessage | WarnDeprecated,
62 
63         ParserIoError = ErrorMessage | SourceParser,
64         ParserError
65     };
66     virtual void message(int type, const QString &msg,
67                          const QString &fileName = QString(), int lineNo = 0) = 0;
68 };
69 
70 class ProFileCache;
71 class QMakeVfs;
72 
73 class QMAKE_EXPORT QMakeParser
74 {
75 public:
76     // Call this from a concurrency-free context
77     static void initialize();
78 
79     enum ParseFlag {
80         ParseDefault = 0,
81         ParseUseCache = 1,
82         ParseReportMissing = 4,
83 #ifdef PROEVALUATOR_DUAL_VFS
84         ParseCumulative = 8
85 #else
86         ParseCumulative = 0
87 #endif
88     };
89     Q_DECLARE_FLAGS(ParseFlags, ParseFlag)
90 
91     QMakeParser(ProFileCache *cache, QMakeVfs *vfs, QMakeParserHandler *handler);
92 
93     enum SubGrammar { FullGrammar, TestGrammar, ValueGrammar };
94     // fileName is expected to be absolute and cleanPath()ed.
95     ProFile *parsedProFile(const QString &fileName, ParseFlags flags = ParseDefault);
96     ProFile *parsedProBlock(const QStringRef &contents, int id, const QString &name, int line = 0,
97                             SubGrammar grammar = FullGrammar);
98 
99     void discardFileFromCache(int id);
100 
101 #ifdef PROPARSER_DEBUG
102     static QString formatProBlock(const QString &block);
103 #endif
104 
105 private:
106     enum ScopeNesting {
107         NestNone = 0,
108         NestLoop = 1,
109         NestFunction = 2
110     };
111 
112     struct BlockScope {
BlockScopeBlockScope113         BlockScope() : start(nullptr), braceLevel(0), special(false), inBranch(false), nest(NestNone) {}
114         ushort *start; // Where this block started; store length here
115         int braceLevel; // Nesting of braces in scope
116         bool special; // Single-line conditionals inside loops, etc. cannot have else branches
117         bool inBranch; // The 'else' branch of the previous TokBranch is still open
118         uchar nest; // Into what control structures we are nested
119     };
120 
121     enum ScopeState {
122         StNew,  // Fresh scope
123         StCtrl, // Control statement (for or else) met on current line
124         StCond  // Conditionals met on current line
125     };
126 
127     enum Context { CtxTest, CtxValue, CtxPureValue, CtxArgs };
128     struct ParseCtx {
129         int parens; // Nesting of non-functional parentheses
130         int argc; // Number of arguments in current function call
131         int wordCount; // Number of words in current expression
132         Context context;
133         ushort quote; // Enclosing quote type
134         ushort terminator; // '}' if replace function call is braced, ':' if test function
135     };
136 
137     bool readFile(int id, QMakeParser::ParseFlags flags, QString *contents);
138     void read(ProFile *pro, const QStringRef &content, int line, SubGrammar grammar);
139 
140     ALWAYS_INLINE void putTok(ushort *&tokPtr, ushort tok);
141     ALWAYS_INLINE void putBlockLen(ushort *&tokPtr, uint len);
142     ALWAYS_INLINE void putBlock(ushort *&tokPtr, const ushort *buf, uint len);
143     void putHashStr(ushort *&pTokPtr, const ushort *buf, uint len);
144     void finalizeHashStr(ushort *buf, uint len);
145     void putLineMarker(ushort *&tokPtr);
146     ALWAYS_INLINE bool resolveVariable(ushort *xprPtr, int tlen, int needSep, ushort **ptr,
147                                        ushort **buf, QString *xprBuff,
148                                        ushort **tokPtr, QString *tokBuff,
149                                        const ushort *cur, const QStringRef &in);
150     void finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int wordCount);
151     void finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int argc);
152     void warnOperator(const char *msg);
153     bool failOperator(const char *msg);
154     bool acceptColon(const char *msg);
155     void putOperator(ushort *&tokPtr);
156     void finalizeTest(ushort *&tokPtr);
157     void bogusTest(ushort *&tokPtr, const QString &msg);
158     void enterScope(ushort *&tokPtr, bool special, ScopeState state);
159     void leaveScope(ushort *&tokPtr);
160     void flushCond(ushort *&tokPtr);
161     void flushScopes(ushort *&tokPtr);
162 
163     void message(int type, const QString &msg) const;
parseError(const QString & msg)164     void parseError(const QString &msg) const
165     {
166         message(QMakeParserHandler::ParserError, msg);
167         m_proFile->setOk(false);
168     }
languageWarning(const QString & msg)169     void languageWarning(const QString &msg) const
170             { message(QMakeParserHandler::ParserWarnLanguage, msg); }
deprecationWarning(const QString & msg)171     void deprecationWarning(const QString &msg) const
172             { message(QMakeParserHandler::ParserWarnDeprecated, msg); }
173 
174     // Current location
175     ProFile *m_proFile;
176     int m_lineNo;
177 
178     QStack<BlockScope> m_blockstack;
179     ScopeState m_state;
180     int m_markLine; // Put marker for this line
181     bool m_inError; // Current line had a parsing error; suppress followup error messages
182     bool m_canElse; // Conditionals met on previous line, but no scope was opened
183     int m_invert; // Pending conditional is negated
184     enum { NoOperator, AndOperator, OrOperator } m_operator; // Pending conditional is ORed/ANDed
185 
186     QString m_tmp; // Temporary for efficient toQString
187 
188     ProFileCache *m_cache;
189     QMakeParserHandler *m_handler;
190     QMakeVfs *m_vfs;
191 
192     // This doesn't help gcc 3.3 ...
193     template<typename T> friend class QTypeInfo;
194 
195     friend class ProFileCache;
196 };
197 
Q_DECLARE_OPERATORS_FOR_FLAGS(QMakeParser::ParseFlags)198 Q_DECLARE_OPERATORS_FOR_FLAGS(QMakeParser::ParseFlags)
199 
200 class QMAKE_EXPORT ProFileCache
201 {
202 public:
203     ProFileCache();
204     ~ProFileCache();
205 
206     void discardFile(int id);
207     void discardFile(const QString &fileName, QMakeVfs *vfs);
208     void discardFiles(const QString &prefix, QMakeVfs *vfs);
209 
210 private:
211     struct Entry {
212         ProFile *pro;
213 #ifdef PROPARSER_THREAD_SAFE
214         struct Locker {
215             Locker() : waiters(0), done(false) {}
216             QWaitCondition cond;
217             int waiters;
218             bool done;
219         };
220         Locker *locker;
221 #endif
222     };
223 
224     QHash<int, Entry> parsed_files;
225 #ifdef PROPARSER_THREAD_SAFE
226     QMutex mutex;
227 #endif
228 
229     friend class QMakeParser;
230 };
231 
232 #if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
233 Q_DECLARE_TYPEINFO(QMakeParser::BlockScope, Q_MOVABLE_TYPE);
234 Q_DECLARE_TYPEINFO(QMakeParser::Context, Q_PRIMITIVE_TYPE);
235 #endif
236 
237 QT_END_NAMESPACE
238 
239 #endif // PROFILEPARSER_H
240