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 QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #if !defined(QMETAOBJECT_P_H) && !defined(UTILS_H)
41 # error "Include qmetaobject_p.h (or moc's utils.h) before including this file."
42 #endif
43 
44 //
45 //  W A R N I N G
46 //  -------------
47 //
48 // This file is not part of the Qt API.  It exists purely as an
49 // implementation detail.  This header file may change from version to
50 // version without notice, or even be removed.
51 //
52 // We mean it.
53 //
54 
55 #include <QtCore/private/qglobal_p.h>
56 
57 QT_BEGIN_NAMESPACE
58 
59 // This function is shared with moc.cpp. This file should be included where needed.
60 static QByteArray normalizeTypeInternal(const char *t, const char *e, bool fixScope = false, bool adjustConst = true)
61 {
62     int len = e - t;
63     /*
64       Convert 'char const *' into 'const char *'. Start at index 1,
65       not 0, because 'const char *' is already OK.
66     */
67     QByteArray constbuf;
68     for (int i = 1; i < len; i++) {
69         if ( t[i] == 'c'
70              && strncmp(t + i + 1, "onst", 4) == 0
71              && (i + 5 >= len || !is_ident_char(t[i + 5]))
72              && !is_ident_char(t[i-1])
73              ) {
74             constbuf = QByteArray(t, len);
75             if (is_space(t[i-1]))
76                 constbuf.remove(i-1, 6);
77             else
78                 constbuf.remove(i, 5);
79             constbuf.prepend("const ");
80             t = constbuf.data();
81             e = constbuf.data() + constbuf.length();
82             break;
83         }
84         /*
85           We mustn't convert 'char * const *' into 'const char **'
86           and we must beware of 'Bar<const Bla>'.
87         */
88         if (t[i] == '&' || t[i] == '*' ||t[i] == '<')
89             break;
90     }
91     if (adjustConst && e > t + 6 && strncmp("const ", t, 6) == 0) {
92         if (*(e-1) == '&') { // treat const reference as value
93             t += 6;
94             --e;
95         } else if (is_ident_char(*(e-1)) || *(e-1) == '>') { // treat const value as value
96             t += 6;
97         }
98     }
99     QByteArray result;
100     result.reserve(len);
101 
102 #if 1
103     // consume initial 'const '
104     if (strncmp("const ", t, 6) == 0) {
105         t+= 6;
106         result += "const ";
107     }
108 #endif
109 
110     // some type substitutions for 'unsigned x'
111     if (strncmp("unsigned", t, 8) == 0) {
112         // make sure "unsigned" is an isolated word before making substitutions
113         if (!t[8] || !is_ident_char(t[8])) {
114             if (strncmp(" int", t+8, 4) == 0) {
115                 t += 8+4;
116                 result += "uint";
117             } else if (strncmp(" long", t+8, 5) == 0) {
118                 if ((strlen(t + 8 + 5) < 4 || strncmp(t + 8 + 5, " int", 4) != 0) // preserve '[unsigned] long int'
119                     && (strlen(t + 8 + 5) < 5 || strncmp(t + 8 + 5, " long", 5) != 0) // preserve '[unsigned] long long'
120                    ) {
121                     t += 8+5;
122                     result += "ulong";
123                 }
124             } else if (strncmp(" short", t+8, 6) != 0  // preserve unsigned short
125                 && strncmp(" char", t+8, 5) != 0) {    // preserve unsigned char
126                 //  treat rest (unsigned) as uint
127                 t += 8;
128                 result += "uint";
129             }
130         }
131     } else {
132         // discard 'struct', 'class', and 'enum'; they are optional
133         // and we don't want them in the normalized signature
134         struct {
135             const char *keyword;
136             int len;
137         } optional[] = {
138             { "struct ", 7 },
139             { "class ", 6 },
140             { "enum ", 5 },
141             { nullptr, 0 }
142         };
143         int i = 0;
144         do {
145             if (strncmp(optional[i].keyword, t, optional[i].len) == 0) {
146                 t += optional[i].len;
147                 break;
148             }
149         } while (optional[++i].keyword != nullptr);
150     }
151 
152     bool star = false;
153     while (t != e) {
154         char c = *t++;
155         if (fixScope && c == ':' && *t == ':' ) {
156             ++t;
157             c = *t++;
158             int i = result.size() - 1;
159             while (i >= 0 && is_ident_char(result.at(i)))
160                 --i;
161             result.resize(i + 1);
162         }
163         star = star || c == '*';
164         result += c;
165         if (c == '<') {
166             //template recursion
167             const char* tt = t;
168             int templdepth = 1;
169             int scopeDepth = 0;
170             while (t != e) {
171                 c = *t++;
172                 if (c == '{' || c == '(' || c == '[')
173                     ++scopeDepth;
174                 if (c == '}' || c == ')' || c == ']')
175                     --scopeDepth;
176                 if (scopeDepth == 0) {
177                     if (c == '<')
178                         ++templdepth;
179                     if (c == '>')
180                         --templdepth;
181                     if (templdepth == 0 || (templdepth == 1 && c == ',')) {
182                         result += normalizeTypeInternal(tt, t-1, fixScope, false);
183                         result += c;
184                         if (templdepth == 0) {
185                             if (*t == '>')
186                                 result += ' '; // avoid >>
187                             break;
188                         }
189                         tt = t;
190                     }
191                 }
192             }
193         }
194 
195         // cv qualifers can appear after the type as well
196         if (!is_ident_char(c) && t != e && (e - t >= 5 && strncmp("const", t, 5) == 0)
197             && (e - t == 5 || !is_ident_char(t[5]))) {
198             t += 5;
199             while (t != e && is_space(*t))
200                 ++t;
201             if (adjustConst && t != e && *t == '&') {
202                 // treat const ref as value
203                 ++t;
204             } else if (adjustConst && !star) {
205                 // treat const as value
206             } else if (!star) {
207                 // move const to the front (but not if const comes after a *)
208                 result.prepend("const ");
209             } else {
210                 // keep const after a *
211                 result += "const";
212             }
213         }
214     }
215 
216     return result;
217 }
218 
219 QT_END_NAMESPACE
220