1
|
1 /****************************************************************************
|
|
2 **
|
|
3 ** Copyright (C) 1992-2008 Nokia. All rights reserved.
|
|
4 **
|
|
5 ** This file is part of Qt Jambi.
|
|
6 **
|
|
7 ** * Commercial Usage
|
|
8 * Licensees holding valid Qt Commercial licenses may use this file in
|
|
9 * accordance with the Qt Commercial License Agreement provided with the
|
|
10 * Software or, alternatively, in accordance with the terms contained in
|
|
11 * a written agreement between you and Nokia.
|
|
12 *
|
|
13 *
|
|
14 * GNU General Public License Usage
|
|
15 * Alternatively, this file may be used under the terms of the GNU
|
|
16 * General Public License versions 2.0 or 3.0 as published by the Free
|
|
17 * Software Foundation and appearing in the file LICENSE.GPL included in
|
|
18 * the packaging of this file. Please review the following information
|
|
19 * to ensure GNU General Public Licensing requirements will be met:
|
|
20 * http://www.fsf.org/licensing/licenses/info/GPLv2.html and
|
|
21 * http://www.gnu.org/copyleft/gpl.html. In addition, as a special
|
|
22 * exception, Nokia gives you certain additional rights. These rights
|
|
23 * are described in the Nokia Qt GPL Exception version 1.2, included in
|
|
24 * the file GPL_EXCEPTION.txt in this package.
|
|
25 *
|
|
26 * Qt for Windows(R) Licensees
|
|
27 * As a special exception, Nokia, as the sole copyright holder for Qt
|
|
28 * Designer, grants users of the Qt/Eclipse Integration plug-in the
|
|
29 * right for the Qt/Eclipse Integration to link to functionality
|
|
30 * provided by Qt Designer and its related libraries.
|
|
31 *
|
|
32 *
|
|
33 * If you are unsure which license is appropriate for your use, please
|
|
34 * contact the sales department at qt-sales@nokia.com.
|
|
35
|
|
36 **
|
|
37 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
|
38 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
39 **
|
|
40 ****************************************************************************/
|
|
41
|
|
42 #include "abstractmetabuilder.h"
|
|
43 #include "reporthandler.h"
|
|
44
|
|
45 #include "ast.h"
|
|
46 #include "binder.h"
|
|
47 #include "control.h"
|
|
48 #include "default_visitor.h"
|
|
49 #include "dumptree.h"
|
|
50 #include "lexer.h"
|
|
51 #include "parser.h"
|
|
52 #include "tokens.h"
|
|
53
|
|
54 #include <QtCore/QDebug>
|
|
55 #include <QtCore/QFile>
|
|
56 #include <QtCore/QFileInfo>
|
|
57 #include <QtCore/QTextCodec>
|
|
58 #include <QtCore/QTextStream>
|
|
59 #include <QtCore/QVariant>
|
|
60
|
|
61 static QString strip_template_args(const QString &name)
|
|
62 {
|
|
63 int pos = name.indexOf('<');
|
|
64 return pos < 0 ? name : name.left(pos);
|
|
65 }
|
|
66
|
|
67 static QHash<QString, QString> *operator_names;
|
|
68 QString rename_operator(const QString &oper)
|
|
69 {
|
|
70 QString op = oper.trimmed();
|
|
71 if (!operator_names) {
|
|
72 operator_names = new QHash<QString, QString>;
|
|
73
|
|
74 operator_names->insert("+", "add");
|
|
75 operator_names->insert("-", "subtract");
|
|
76 operator_names->insert("*", "multiply");
|
|
77 operator_names->insert("/", "divide");
|
|
78 operator_names->insert("%", "modulo");
|
|
79 operator_names->insert("&", "and");
|
|
80 operator_names->insert("|", "or");
|
|
81 operator_names->insert("^", "xor");
|
|
82 operator_names->insert("~", "negate");
|
|
83 operator_names->insert("<<", "shift_left");
|
|
84 operator_names->insert(">>", "shift_right");
|
|
85
|
|
86 // assigments
|
|
87 operator_names->insert("=", "assign");
|
|
88 operator_names->insert("+=", "add_assign");
|
|
89 operator_names->insert("-=", "subtract_assign");
|
|
90 operator_names->insert("*=", "multiply_assign");
|
|
91 operator_names->insert("/=", "divide_assign");
|
|
92 operator_names->insert("%=", "modulo_assign");
|
|
93 operator_names->insert("&=", "and_assign");
|
|
94 operator_names->insert("|=", "or_assign");
|
|
95 operator_names->insert("^=", "xor_assign");
|
|
96 operator_names->insert("<<=", "shift_left_assign");
|
|
97 operator_names->insert(">>=", "shift_right_assign");
|
|
98
|
|
99 // Logical
|
|
100 operator_names->insert("&&", "logical_and");
|
|
101 operator_names->insert("||", "logical_or");
|
|
102 operator_names->insert("!", "not");
|
|
103
|
|
104 // incr/decr
|
|
105 operator_names->insert("++", "increment");
|
|
106 operator_names->insert("--", "decrement");
|
|
107
|
|
108 // compare
|
|
109 operator_names->insert("<", "less");
|
|
110 operator_names->insert(">", "greater");
|
|
111 operator_names->insert("<=", "less_or_equal");
|
|
112 operator_names->insert(">=", "greater_or_equal");
|
|
113 operator_names->insert("!=", "not_equal");
|
|
114 operator_names->insert("==", "equal");
|
|
115
|
|
116 // other
|
|
117 operator_names->insert("[]", "subscript");
|
|
118 operator_names->insert("->", "pointer");
|
|
119 }
|
|
120
|
|
121 if (!operator_names->contains(op)) {
|
|
122 TypeDatabase *tb = TypeDatabase::instance();
|
|
123
|
|
124 TypeParser::Info typeInfo = TypeParser::parse(op);
|
|
125 QString cast_to_name = typeInfo.qualified_name.join("::");
|
|
126 TypeEntry *te = tb->findType(cast_to_name);
|
|
127 if ((te && te->codeGeneration() == TypeEntry::GenerateNothing)
|
|
128 || tb->isClassRejected(cast_to_name)) {
|
|
129 return QString();
|
|
130 } else if (te) {
|
|
131 return "operator_cast_" + typeInfo.qualified_name.join("_");
|
|
132 } else {
|
|
133 ReportHandler::warning(QString("unknown operator '%1'").arg(op));
|
|
134 return "operator " + op;
|
|
135 }
|
|
136 }
|
|
137
|
|
138 return "operator_" + operator_names->value(op);
|
|
139 }
|
|
140
|
|
141 AbstractMetaBuilder::AbstractMetaBuilder()
|
|
142 : m_current_class(0)
|
|
143 {
|
|
144 }
|
|
145
|
|
146 void AbstractMetaBuilder::checkFunctionModifications()
|
|
147 {
|
|
148 TypeDatabase *types = TypeDatabase::instance();
|
|
149 SingleTypeEntryHash entryHash = types->entries();
|
|
150 QList<TypeEntry *> entries = entryHash.values();
|
|
151 foreach (TypeEntry *entry, entries) {
|
|
152 if (entry == 0)
|
|
153 continue;
|
|
154 if (!entry->isComplex() || entry->codeGeneration() == TypeEntry::GenerateNothing)
|
|
155 continue;
|
|
156
|
|
157 ComplexTypeEntry *centry = static_cast<ComplexTypeEntry *>(entry);
|
|
158 FunctionModificationList modifications = centry->functionModifications();
|
|
159
|
|
160 foreach (FunctionModification modification, modifications) {
|
|
161 QString signature = modification.signature;
|
|
162
|
|
163 QString name = signature.trimmed();
|
|
164 name = name.mid(0, signature.indexOf("("));
|
|
165
|
|
166 AbstractMetaClass *clazz = m_meta_classes.findClass(centry->qualifiedCppName());
|
|
167 if (clazz == 0)
|
|
168 continue;
|
|
169
|
|
170 AbstractMetaFunctionList functions = clazz->functions();
|
|
171 bool found = false;
|
|
172 QStringList possibleSignatures;
|
|
173 foreach (AbstractMetaFunction *function, functions) {
|
|
174 if (function->minimalSignature() == signature && function->implementingClass() == clazz) {
|
|
175 found = true;
|
|
176 break;
|
|
177 }
|
|
178
|
|
179 if (function->originalName() == name)
|
|
180 possibleSignatures.append(function->minimalSignature() + " in " + function->implementingClass()->name());
|
|
181 }
|
|
182
|
|
183 if (!found) {
|
|
184 QString warning
|
|
185 = QString("signature '%1' for function modification in '%2' not found. Possible candidates: %3")
|
|
186 .arg(signature)
|
|
187 .arg(clazz->qualifiedCppName())
|
|
188 .arg(possibleSignatures.join(", "));
|
|
189
|
|
190 ReportHandler::warning(warning);
|
|
191 }
|
|
192 }
|
|
193 }
|
|
194 }
|
|
195
|
|
196 AbstractMetaClass *AbstractMetaBuilder::argumentToClass(ArgumentModelItem argument)
|
|
197 {
|
|
198 AbstractMetaClass *returned = 0;
|
|
199 bool ok = false;
|
|
200 AbstractMetaType *type = translateType(argument->type(), &ok);
|
|
201 if (ok && type != 0 && type->typeEntry() != 0 && type->typeEntry()->isComplex()) {
|
|
202 const TypeEntry *entry = type->typeEntry();
|
|
203 returned = m_meta_classes.findClass(entry->name());
|
|
204 }
|
|
205 delete type;
|
|
206 return returned;
|
|
207 }
|
|
208
|
|
209 /**
|
|
210 * Checks the argument of a hash function and flags the type if it is a complex type
|
|
211 */
|
|
212 void AbstractMetaBuilder::registerHashFunction(FunctionModelItem function_item)
|
|
213 {
|
|
214 ArgumentList arguments = function_item->arguments();
|
|
215 if (arguments.size() == 1) {
|
|
216 if (AbstractMetaClass *cls = argumentToClass(arguments.at(0)))
|
|
217 cls->setHasHashFunction(true);
|
|
218 }
|
|
219 }
|
|
220
|
|
221 /**
|
|
222 * Check if a class has a debug stream operator that can be used as toString
|
|
223 */
|
|
224
|
|
225 void AbstractMetaBuilder::registerToStringCapability(FunctionModelItem function_item)
|
|
226 {
|
|
227 ArgumentList arguments = function_item->arguments();
|
|
228 if (arguments.size() == 2) {
|
|
229 if (arguments.at(0)->type().toString() == "QDebug"){
|
|
230 ArgumentModelItem arg = arguments.at(1);
|
|
231 if (AbstractMetaClass *cls = argumentToClass(arg)) {
|
|
232 if (arg->type().indirections() < 2) {
|
|
233 cls->setToStringCapability(function_item);
|
|
234 }
|
|
235 }
|
|
236 }
|
|
237 }
|
|
238 }
|
|
239
|
|
240 void AbstractMetaBuilder::traverseCompareOperator(FunctionModelItem item) {
|
|
241 ArgumentList arguments = item->arguments();
|
|
242 if (arguments.size() == 2 && item->accessPolicy() == CodeModel::Public) {
|
|
243 AbstractMetaClass *comparer_class = argumentToClass(arguments.at(0));
|
|
244 AbstractMetaClass *compared_class = argumentToClass(arguments.at(1));
|
|
245 if (comparer_class != 0 && compared_class != 0) {
|
|
246 AbstractMetaClass *old_current_class = m_current_class;
|
|
247 m_current_class = comparer_class;
|
|
248
|
|
249 AbstractMetaFunction *meta_function = traverseFunction(item);
|
|
250 if (meta_function != 0 && !meta_function->isInvalid()) {
|
|
251 // Strip away first argument, since that is the containing object
|
|
252 AbstractMetaArgumentList arguments = meta_function->arguments();
|
|
253 arguments.pop_front();
|
|
254 meta_function->setArguments(arguments);
|
|
255
|
|
256 meta_function->setFunctionType(AbstractMetaFunction::GlobalScopeFunction);
|
|
257
|
|
258 meta_function->setOriginalAttributes(meta_function->attributes());
|
|
259 setupFunctionDefaults(meta_function, comparer_class);
|
|
260
|
|
261 comparer_class->addFunction(meta_function);
|
|
262 } else if (meta_function != 0) {
|
|
263 delete meta_function;
|
|
264 }
|
|
265
|
|
266 m_current_class = old_current_class;
|
|
267 }
|
|
268 }
|
|
269 }
|
|
270
|
|
271 void AbstractMetaBuilder::traverseStreamOperator(FunctionModelItem item)
|
|
272 {
|
|
273 ArgumentList arguments = item->arguments();
|
|
274 if (arguments.size() == 2 && item->accessPolicy() == CodeModel::Public) {
|
|
275 AbstractMetaClass *streamClass = argumentToClass(arguments.at(0));
|
|
276 AbstractMetaClass *streamedClass = argumentToClass(arguments.at(1));
|
|
277
|
|
278 if (streamClass != 0 && streamedClass != 0
|
|
279 && (streamClass->name() == "QDataStream" || streamClass->name() == "QTextStream")) {
|
|
280 AbstractMetaClass *old_current_class = m_current_class;
|
|
281 m_current_class = streamedClass;
|
|
282 AbstractMetaFunction *streamFunction = traverseFunction(item);
|
|
283
|
|
284 if (streamFunction != 0 && !streamFunction->isInvalid()) {
|
|
285 QString name = item->name();
|
|
286 streamFunction->setFunctionType(AbstractMetaFunction::GlobalScopeFunction);
|
|
287
|
|
288 if (name.endsWith("<<"))
|
|
289 streamFunction->setName("writeTo");
|
|
290 else
|
|
291 streamFunction->setName("readFrom");
|
|
292
|
|
293 // Strip away last argument, since that is the containing object
|
|
294 AbstractMetaArgumentList arguments = streamFunction->arguments();
|
|
295 arguments.pop_back();
|
|
296 streamFunction->setArguments(arguments);
|
|
297
|
|
298 *streamFunction += AbstractMetaAttributes::Final;
|
|
299 *streamFunction += AbstractMetaAttributes::Public;
|
|
300 streamFunction->setOriginalAttributes(streamFunction->attributes());
|
|
301
|
|
302 streamFunction->setType(0);
|
|
303
|
|
304 setupFunctionDefaults(streamFunction, streamedClass);
|
|
305
|
|
306 streamedClass->addFunction(streamFunction);
|
|
307 streamedClass->typeEntry()->addExtraInclude(streamClass->typeEntry()->include());
|
|
308
|
|
309 m_current_class = old_current_class;
|
|
310 }
|
|
311 }
|
|
312 }
|
|
313 }
|
|
314
|
|
315 void AbstractMetaBuilder::fixQObjectForScope(TypeDatabase *types,
|
|
316 NamespaceModelItem scope)
|
|
317 {
|
|
318 foreach (ClassModelItem item, scope->classes()) {
|
|
319 QString qualified_name = item->qualifiedName().join("::");
|
|
320 TypeEntry *entry = types->findType(qualified_name);
|
|
321 if (entry) {
|
|
322 if (isQObject(qualified_name) && entry->isComplex()) {
|
|
323 ((ComplexTypeEntry *) entry)->setQObject(true);
|
|
324 }
|
|
325 }
|
|
326 }
|
|
327
|
|
328 foreach (NamespaceModelItem item, scope->namespaceMap().values()) {
|
|
329 if (scope != item)
|
|
330 fixQObjectForScope(types, item);
|
|
331 }
|
|
332 }
|
|
333
|
|
334 static bool class_less_than(AbstractMetaClass *a, AbstractMetaClass *b)
|
|
335 {
|
|
336 return a->name() < b->name();
|
|
337 }
|
|
338
|
|
339
|
|
340 void AbstractMetaBuilder::sortLists()
|
|
341 {
|
|
342 qSort(m_meta_classes.begin(), m_meta_classes.end(), class_less_than);
|
|
343 foreach (AbstractMetaClass *cls, m_meta_classes) {
|
|
344 cls->sortFunctions();
|
|
345 }
|
|
346 }
|
|
347
|
|
348 bool AbstractMetaBuilder::build()
|
|
349 {
|
|
350 Q_ASSERT(!m_file_name.isEmpty());
|
|
351 ReportHandler::setContext("Parser");
|
|
352
|
|
353 QFile file(m_file_name);
|
|
354
|
|
355 if (!file.open(QFile::ReadOnly))
|
|
356 return false;
|
|
357
|
|
358 QTextStream stream(&file);
|
|
359 stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
|
360 QByteArray contents = stream.readAll().toUtf8();
|
|
361 file.close();
|
|
362
|
|
363 Control control;
|
|
364 Parser p(&control);
|
|
365 pool __pool;
|
|
366
|
|
367 TranslationUnitAST *ast = p.parse(contents, contents.size(), &__pool);
|
|
368
|
|
369 CodeModel model;
|
|
370 Binder binder(&model, p.location());
|
|
371 m_dom = binder.run(ast);
|
|
372
|
|
373
|
|
374 ReportHandler::setContext("MetaJavaBuilder");
|
|
375
|
|
376
|
|
377 pushScope(model_dynamic_cast<ScopeModelItem>(m_dom));
|
|
378
|
|
379 QHash<QString, ClassModelItem> typeMap = m_dom->classMap();
|
|
380
|
|
381
|
|
382 // fix up QObject's in the type system..
|
|
383 TypeDatabase *types = TypeDatabase::instance();
|
|
384 fixQObjectForScope(types, model_dynamic_cast<NamespaceModelItem>(m_dom));
|
|
385
|
|
386
|
|
387 // Start the generation...
|
|
388 foreach (ClassModelItem item, typeMap.values()) {
|
|
389 AbstractMetaClass *cls = traverseClass(item);
|
|
390 addAbstractMetaClass(cls);
|
|
391 }
|
|
392
|
|
393
|
|
394 QHash<QString, NamespaceModelItem> namespaceMap = m_dom->namespaceMap();
|
|
395 foreach (NamespaceModelItem item, namespaceMap.values()) {
|
|
396 AbstractMetaClass *meta_class = traverseNamespace(item);
|
|
397 if (meta_class)
|
|
398 m_meta_classes << meta_class;
|
|
399 }
|
|
400
|
|
401
|
|
402 // Some trickery to support global-namespace enums...
|
|
403 QHash<QString, EnumModelItem> enumMap = m_dom->enumMap();
|
|
404 m_current_class = 0;
|
|
405 foreach (EnumModelItem item, enumMap) {
|
|
406 AbstractMetaEnum *meta_enum = traverseEnum(item, 0, QSet<QString>());
|
|
407
|
|
408 if (meta_enum) {
|
|
409 QString package = meta_enum->typeEntry()->javaPackage();
|
|
410 QString globalName = TypeDatabase::globalNamespaceClassName(meta_enum->typeEntry());
|
|
411
|
|
412 AbstractMetaClass *global = m_meta_classes.findClass(package + "." + globalName);
|
|
413 if (!global) {
|
|
414 ComplexTypeEntry *gte = new ObjectTypeEntry(globalName);
|
|
415 gte->setTargetLangPackage(meta_enum->typeEntry()->javaPackage());
|
|
416 gte->setCodeGeneration(meta_enum->typeEntry()->codeGeneration());
|
|
417 global = createMetaClass();
|
|
418 global->setTypeEntry(gte);
|
|
419 *global += AbstractMetaAttributes::Final;
|
|
420 *global += AbstractMetaAttributes::Public;
|
|
421 *global += AbstractMetaAttributes::Fake;
|
|
422
|
|
423 m_meta_classes << global;
|
|
424 }
|
|
425
|
|
426 global->addEnum(meta_enum);
|
|
427 meta_enum->setEnclosingClass(global);
|
|
428 meta_enum->typeEntry()->setQualifier(globalName);
|
|
429 }
|
|
430
|
|
431
|
|
432 }
|
|
433
|
|
434
|
|
435 // Go through all typedefs to see if we have defined any
|
|
436 // specific typedefs to be used as classes.
|
|
437 TypeAliasList typeAliases = m_dom->typeAliases();
|
|
438 foreach (TypeAliasModelItem typeAlias, typeAliases) {
|
|
439 AbstractMetaClass *cls = traverseTypeAlias(typeAlias);
|
|
440 addAbstractMetaClass(cls);
|
|
441 }
|
|
442
|
|
443
|
|
444
|
|
445
|
|
446 foreach (AbstractMetaClass *cls, m_meta_classes) {
|
|
447 if (!cls->isInterface() && !cls->isNamespace()) {
|
|
448 setupInheritance(cls);
|
|
449 }
|
|
450 }
|
|
451
|
|
452
|
|
453 foreach (AbstractMetaClass *cls, m_meta_classes) {
|
|
454 cls->fixFunctions();
|
|
455
|
|
456 if (cls->typeEntry() == 0) {
|
|
457 ReportHandler::warning(QString("class '%1' does not have an entry in the type system")
|
|
458 .arg(cls->name()));
|
|
459 } else {
|
|
460 if (!cls->hasConstructors() && !cls->isFinalInCpp() && !cls->isInterface() && !cls->isNamespace())
|
|
461 cls->addDefaultConstructor();
|
|
462 }
|
|
463
|
|
464 if (cls->isAbstract() && !cls->isInterface()) {
|
|
465 cls->typeEntry()->setLookupName(cls->typeEntry()->targetLangName() + "_ConcreteWrapper");
|
|
466 }
|
|
467 }
|
|
468
|
|
469 QList<TypeEntry *> entries = TypeDatabase::instance()->entries().values();
|
|
470 foreach (const TypeEntry *entry, entries) {
|
|
471 if (entry->isPrimitive())
|
|
472 continue;
|
|
473
|
|
474 if ((entry->isValue() || entry->isObject())
|
|
475 && !entry->isString()
|
|
476 && !entry->isChar()
|
|
477 && !entry->isContainer()
|
|
478 && !entry->isCustom()
|
|
479 && !entry->isVariant()
|
|
480 && !m_meta_classes.findClass(entry->qualifiedCppName())) {
|
|
481 ReportHandler::warning(QString("type '%1' is specified in typesystem, but not defined. This could potentially lead to compilation errors.")
|
|
482 .arg(entry->qualifiedCppName()));
|
|
483 }
|
|
484
|
|
485 if (entry->isEnum()) {
|
|
486 QString pkg = entry->javaPackage();
|
|
487 QString name = (pkg.isEmpty() ? QString() : pkg + ".")
|
|
488 + ((EnumTypeEntry *) entry)->javaQualifier();
|
|
489 AbstractMetaClass *cls = m_meta_classes.findClass(name);
|
|
490
|
|
491 if (!cls) {
|
|
492 ReportHandler::warning(QString("namespace '%1' for enum '%2' is not declared")
|
|
493 .arg(name).arg(entry->targetLangName()));
|
|
494 } else {
|
|
495 AbstractMetaEnum *e = cls->findEnum(entry->targetLangName());
|
|
496 if (!e)
|
|
497 ReportHandler::warning(QString("enum '%1' is specified in typesystem, "
|
|
498 "but not declared")
|
|
499 .arg(entry->qualifiedCppName()));
|
|
500 }
|
|
501 }
|
|
502 }
|
|
503
|
|
504 {
|
|
505 FunctionList hash_functions = m_dom->findFunctions("qHash");
|
|
506 foreach (FunctionModelItem item, hash_functions) {
|
|
507 registerHashFunction(item);
|
|
508 }
|
|
509 }
|
|
510
|
|
511 {
|
|
512 FunctionList hash_functions = m_dom->findFunctions("operator<<");
|
|
513 foreach (FunctionModelItem item, hash_functions) {
|
|
514 registerToStringCapability(item);
|
|
515 }
|
|
516 }
|
|
517
|
|
518 {
|
|
519 FunctionList compare_operators = m_dom->findFunctions("operator==")
|
|
520 + m_dom->findFunctions("operator<=")
|
|
521 + m_dom->findFunctions("operator>=")
|
|
522 + m_dom->findFunctions("operator<")
|
|
523 + m_dom->findFunctions("operator>");
|
|
524 foreach (FunctionModelItem item, compare_operators) {
|
|
525 traverseCompareOperator(item);
|
|
526 }
|
|
527 }
|
|
528
|
|
529 {
|
|
530 FunctionList stream_operators = m_dom->findFunctions("operator<<") + m_dom->findFunctions("operator>>");
|
|
531 foreach (FunctionModelItem item, stream_operators) {
|
|
532 traverseStreamOperator(item);
|
|
533 }
|
|
534 }
|
|
535
|
|
536 figureOutEnumValues();
|
|
537 figureOutDefaultEnumArguments();
|
|
538 checkFunctionModifications();
|
|
539
|
|
540 foreach (AbstractMetaClass *cls, m_meta_classes) {
|
|
541 setupEquals(cls);
|
|
542 setupComparable(cls);
|
|
543 setupClonable(cls);
|
|
544 }
|
|
545
|
|
546 dumpLog();
|
|
547
|
|
548 sortLists();
|
|
549
|
|
550 return true;
|
|
551 }
|
|
552
|
|
553
|
|
554 void AbstractMetaBuilder::addAbstractMetaClass(AbstractMetaClass *cls)
|
|
555 {
|
|
556 if (!cls)
|
|
557 return;
|
|
558
|
|
559 cls->setOriginalAttributes(cls->attributes());
|
|
560 if (cls->typeEntry()->isContainer()) {
|
|
561 m_templates << cls;
|
|
562 } else {
|
|
563 m_meta_classes << cls;
|
|
564 if (cls->typeEntry()->designatedInterface()) {
|
|
565 AbstractMetaClass *interface = cls->extractInterface();
|
|
566 m_meta_classes << interface;
|
|
567 ReportHandler::debugSparse(QString(" -> interface '%1'").arg(interface->name()));
|
|
568 }
|
|
569 }
|
|
570 }
|
|
571
|
|
572
|
|
573 AbstractMetaClass *AbstractMetaBuilder::traverseNamespace(NamespaceModelItem namespace_item)
|
|
574 {
|
|
575 QString namespace_name = (!m_namespace_prefix.isEmpty() ? m_namespace_prefix + "::" : QString()) + namespace_item->name();
|
|
576 NamespaceTypeEntry *type = TypeDatabase::instance()->findNamespaceType(namespace_name);
|
|
577
|
|
578 if (TypeDatabase::instance()->isClassRejected(namespace_name)) {
|
|
579 m_rejected_classes.insert(namespace_name, GenerationDisabled);
|
|
580 return 0;
|
|
581 }
|
|
582
|
|
583 if (!type) {
|
|
584 ReportHandler::warning(QString("namespace '%1' does not have a type entry")
|
|
585 .arg(namespace_name));
|
|
586 return 0;
|
|
587 }
|
|
588
|
|
589 AbstractMetaClass *meta_class = createMetaClass();
|
|
590 meta_class->setTypeEntry(type);
|
|
591
|
|
592 *meta_class += AbstractMetaAttributes::Public;
|
|
593
|
|
594 m_current_class = meta_class;
|
|
595
|
|
596 ReportHandler::debugSparse(QString("namespace '%1.%2'")
|
|
597 .arg(meta_class->package())
|
|
598 .arg(namespace_item->name()));
|
|
599
|
|
600 traverseEnums(model_dynamic_cast<ScopeModelItem>(namespace_item), meta_class, namespace_item->enumsDeclarations());
|
|
601 traverseFunctions(model_dynamic_cast<ScopeModelItem>(namespace_item), meta_class);
|
|
602 // traverseClasses(model_dynamic_cast<ScopeModelItem>(namespace_item));
|
|
603
|
|
604 pushScope(model_dynamic_cast<ScopeModelItem>(namespace_item));
|
|
605 m_namespace_prefix = currentScope()->qualifiedName().join("::");
|
|
606
|
|
607
|
|
608 ClassList classes = namespace_item->classes();
|
|
609 foreach (ClassModelItem cls, classes) {
|
|
610 AbstractMetaClass *mjc = traverseClass(cls);
|
|
611 addAbstractMetaClass(mjc);
|
|
612 }
|
|
613
|
|
614 // Go through all typedefs to see if we have defined any
|
|
615 // specific typedefs to be used as classes.
|
|
616 TypeAliasList typeAliases = namespace_item->typeAliases();
|
|
617 foreach (TypeAliasModelItem typeAlias, typeAliases) {
|
|
618 AbstractMetaClass *cls = traverseTypeAlias(typeAlias);
|
|
619 addAbstractMetaClass(cls);
|
|
620 }
|
|
621
|
|
622
|
|
623
|
|
624 // Traverse namespaces recursively
|
|
625 QList<NamespaceModelItem> inner_namespaces = namespace_item->namespaceMap().values();
|
|
626 foreach (const NamespaceModelItem &ni, inner_namespaces) {
|
|
627 AbstractMetaClass *mjc = traverseNamespace(ni);
|
|
628 addAbstractMetaClass(mjc);
|
|
629 }
|
|
630
|
|
631 m_current_class = 0;
|
|
632
|
|
633
|
|
634 popScope();
|
|
635 m_namespace_prefix = currentScope()->qualifiedName().join("::");
|
|
636
|
|
637 if (!type->include().isValid()) {
|
|
638 QFileInfo info(namespace_item->fileName());
|
|
639 type->setInclude(Include(Include::IncludePath, info.fileName()));
|
|
640 }
|
|
641
|
|
642 return meta_class;
|
|
643 }
|
|
644
|
|
645 struct Operator
|
|
646 {
|
|
647 enum Type { Plus, ShiftLeft, None };
|
|
648
|
|
649 Operator() : type(None) { }
|
|
650
|
|
651 int calculate(int x) {
|
|
652 switch (type) {
|
|
653 case Plus: return x + value;
|
|
654 case ShiftLeft: return x << value;
|
|
655 case None: return x;
|
|
656 }
|
|
657 return x;
|
|
658 }
|
|
659
|
|
660 Type type;
|
|
661 int value;
|
|
662 };
|
|
663
|
|
664
|
|
665
|
|
666 Operator findOperator(QString *s) {
|
|
667 const char *names[] = {
|
|
668 "+",
|
|
669 "<<"
|
|
670 };
|
|
671
|
|
672 for (int i=0; i<Operator::None; ++i) {
|
|
673 QString name = QLatin1String(names[i]);
|
|
674 QString str = *s;
|
|
675 int splitPoint = str.indexOf(name);
|
|
676 if (splitPoint > 0) {
|
|
677 bool ok;
|
|
678 QString right = str.mid(splitPoint + name.length());
|
|
679 Operator op;
|
|
680 op.value = right.toInt(&ok);
|
|
681 if (ok) {
|
|
682 op.type = Operator::Type(i);
|
|
683 *s = str.left(splitPoint).trimmed();
|
|
684 return op;
|
|
685 }
|
|
686 }
|
|
687 }
|
|
688 return Operator();
|
|
689 }
|
|
690
|
|
691 int AbstractMetaBuilder::figureOutEnumValue(const QString &stringValue,
|
|
692 int oldValuevalue,
|
|
693 AbstractMetaEnum *meta_enum,
|
|
694 AbstractMetaFunction *meta_function)
|
|
695 {
|
|
696 if (stringValue.isEmpty())
|
|
697 return oldValuevalue;
|
|
698
|
|
699 QStringList stringValues = stringValue.split("|");
|
|
700
|
|
701 int returnValue = 0;
|
|
702
|
|
703 bool matched = false;
|
|
704
|
|
705 for (int i=0; i<stringValues.size(); ++i) {
|
|
706 QString s = stringValues.at(i).trimmed();
|
|
707
|
|
708 bool ok;
|
|
709 int v;
|
|
710
|
|
711 Operator op = findOperator(&s);
|
|
712
|
|
713 if (s.length() > 0 && s.at(0) == QLatin1Char('0'))
|
|
714 v = s.toUInt(&ok, 0);
|
|
715 else
|
|
716 v = s.toInt(&ok);
|
|
717
|
|
718 if (ok) {
|
|
719 matched = true;
|
|
720
|
|
721 } else if (m_enum_values.contains(s)) {
|
|
722 v = m_enum_values[s]->value();
|
|
723 matched = true;
|
|
724
|
|
725 } else {
|
|
726 AbstractMetaEnumValue *ev = 0;
|
|
727
|
|
728 if (meta_enum && (ev = meta_enum->values().find(s))) {
|
|
729 v = ev->value();
|
|
730 matched = true;
|
|
731
|
|
732 } else if (meta_enum && (ev = meta_enum->enclosingClass()->findEnumValue(s, meta_enum))) {
|
|
733 v = ev->value();
|
|
734 matched = true;
|
|
735
|
|
736 } else {
|
|
737 if (meta_enum)
|
|
738 ReportHandler::warning("unhandled enum value: " + s + " in "
|
|
739 + meta_enum->enclosingClass()->name() + "::"
|
|
740 + meta_enum->name());
|
|
741 else
|
|
742 ReportHandler::warning("unhandled enum value: Unknown enum");
|
|
743 }
|
|
744 }
|
|
745
|
|
746 if (matched)
|
|
747 returnValue |= op.calculate(v);
|
|
748 }
|
|
749
|
|
750 if (!matched) {
|
|
751 QString warn = QString("unmatched enum %1").arg(stringValue);
|
|
752
|
|
753 if (meta_function != 0) {
|
|
754 warn += QString(" when parsing default value of '%1' in class '%2'")
|
|
755 .arg(meta_function->name())
|
|
756 .arg(meta_function->implementingClass()->name());
|
|
757 }
|
|
758
|
|
759 ReportHandler::warning(warn);
|
|
760 returnValue = oldValuevalue;
|
|
761 }
|
|
762
|
|
763 return returnValue;
|
|
764 }
|
|
765
|
|
766 void AbstractMetaBuilder::figureOutEnumValuesForClass(AbstractMetaClass *meta_class,
|
|
767 QSet<AbstractMetaClass *> *classes)
|
|
768 {
|
|
769 AbstractMetaClass *base = meta_class->baseClass();
|
|
770
|
|
771 if (base != 0 && !classes->contains(base))
|
|
772 figureOutEnumValuesForClass(base, classes);
|
|
773
|
|
774 if (classes->contains(meta_class))
|
|
775 return;
|
|
776
|
|
777 AbstractMetaEnumList enums = meta_class->enums();
|
|
778 foreach (AbstractMetaEnum *e, enums) {
|
|
779 if (!e)
|
|
780 ReportHandler::warning("bad enum in class " + meta_class->name());
|
|
781 AbstractMetaEnumValueList lst = e->values();
|
|
782 int value = 0;
|
|
783 for (int i=0; i<lst.size(); ++i) {
|
|
784 value = figureOutEnumValue(lst.at(i)->stringValue(), value, e);
|
|
785 lst.at(i)->setValue(value);
|
|
786 value++;
|
|
787 }
|
|
788
|
|
789 // Check for duplicate values...
|
|
790 EnumTypeEntry *ete = e->typeEntry();
|
|
791 if (!ete->forceInteger()) {
|
|
792 QHash<int, AbstractMetaEnumValue *> entries;
|
|
793 foreach (AbstractMetaEnumValue *v, lst) {
|
|
794
|
|
795 bool vRejected = ete->isEnumValueRejected(v->name());
|
|
796
|
|
797 AbstractMetaEnumValue *current = entries.value(v->value());
|
|
798 if (current) {
|
|
799 bool currentRejected = ete->isEnumValueRejected(current->name());
|
|
800 if (!currentRejected && !vRejected) {
|
|
801 ReportHandler::warning(
|
|
802 QString("duplicate enum values: %1::%2, %3 and %4 are %5, already rejected: (%6)")
|
|
803 .arg(meta_class->name())
|
|
804 .arg(e->name())
|
|
805 .arg(v->name())
|
|
806 .arg(entries[v->value()]->name())
|
|
807 .arg(v->value())
|
|
808 .arg(ete->enumValueRejections().join(", ")));
|
|
809 continue;
|
|
810 }
|
|
811 }
|
|
812
|
|
813 if (!vRejected)
|
|
814 entries[v->value()] = v;
|
|
815 }
|
|
816
|
|
817 // Entries now contain all the original entries, no
|
|
818 // rejected ones... Use this to generate the enumValueRedirection table.
|
|
819 foreach (AbstractMetaEnumValue *reject, lst) {
|
|
820 if (!ete->isEnumValueRejected(reject->name()))
|
|
821 continue;
|
|
822
|
|
823 AbstractMetaEnumValue *used = entries.value(reject->value());
|
|
824 if (!used) {
|
|
825 ReportHandler::warning(
|
|
826 QString::fromLatin1("Rejected enum has no alternative...: %1::%2\n")
|
|
827 .arg(meta_class->name())
|
|
828 .arg(reject->name()));
|
|
829 continue;
|
|
830 }
|
|
831 ete->addEnumValueRedirection(reject->name(), used->name());
|
|
832 }
|
|
833
|
|
834 }
|
|
835 }
|
|
836
|
|
837
|
|
838
|
|
839 *classes += meta_class;
|
|
840 }
|
|
841
|
|
842
|
|
843 void AbstractMetaBuilder::figureOutEnumValues()
|
|
844 {
|
|
845 // Keep a set of classes that we already traversed. We use this to
|
|
846 // enforce that we traverse base classes prior to subclasses.
|
|
847 QSet<AbstractMetaClass *> classes;
|
|
848 foreach (AbstractMetaClass *c, m_meta_classes) {
|
|
849 figureOutEnumValuesForClass(c, &classes);
|
|
850 }
|
|
851 }
|
|
852
|
|
853 void AbstractMetaBuilder::figureOutDefaultEnumArguments()
|
|
854 {
|
|
855 foreach (AbstractMetaClass *meta_class, m_meta_classes) {
|
|
856 foreach (AbstractMetaFunction *meta_function, meta_class->functions()) {
|
|
857 foreach (AbstractMetaArgument *arg, meta_function->arguments()) {
|
|
858
|
|
859 QString expr = arg->defaultValueExpression();
|
|
860 if (expr.isEmpty())
|
|
861 continue;
|
|
862
|
|
863 if (!meta_function->replacedDefaultExpression(meta_function->implementingClass(),
|
|
864 arg->argumentIndex()+1).isEmpty()) {
|
|
865 continue;
|
|
866 }
|
|
867
|
|
868 QString new_expr = expr;
|
|
869 if (arg->type()->isEnum()) {
|
|
870 QStringList lst = expr.split(QLatin1String("::"));
|
|
871 if (lst.size() == 1) {
|
|
872 QVector<AbstractMetaClass *> classes(1, meta_class);
|
|
873 AbstractMetaEnum *e = 0;
|
|
874 while (!classes.isEmpty() && e == 0) {
|
|
875 if (classes.front() != 0) {
|
|
876 classes << classes.front()->baseClass();
|
|
877
|
|
878 AbstractMetaClassList interfaces = classes.front()->interfaces();
|
|
879 foreach (AbstractMetaClass *interface, interfaces)
|
|
880 classes << interface->primaryInterfaceImplementor();
|
|
881
|
|
882 e = classes.front()->findEnumForValue(expr);
|
|
883 }
|
|
884
|
|
885 classes.pop_front();
|
|
886 }
|
|
887
|
|
888 if (e != 0) {
|
|
889 /* qtd new_expr = QString("%1.%2")
|
|
890 .arg(e->typeEntry()->qualifiedTargetLangName())
|
|
891 .arg(expr);
|
|
892 */
|
|
893 // new_expr = arg->type()->typeEntry()->targetLangName() + "." + expr;
|
|
894 new_expr = arg->type()->typeEntry()->qualifiedTargetLangName() + "." + expr;
|
|
895 } else {
|
|
896 ReportHandler::warning("Cannot find enum constant for value '" + expr + "' in '" + meta_class->name() + "' or any of its super classes");
|
|
897 }
|
|
898 } else if (lst.size() == 2) {
|
|
899 AbstractMetaClass *cl = m_meta_classes.findClass(lst.at(0));
|
|
900 if (!cl) {
|
|
901 ReportHandler::warning("missing required class for enums: " + lst.at(0));
|
|
902 continue;
|
|
903 }
|
|
904 /* qtd new_expr = QString("%1.%2.%3")
|
|
905 .arg(cl->typeEntry()->qualifiedTargetLangName())
|
|
906 .arg(arg->type()->name())
|
|
907 .arg(lst.at(1));
|
|
908 */
|
|
909 // new_expr = arg->type()->typeEntry()->targetLangName() + "." + lst.at(1);
|
|
910 new_expr = arg->type()->typeEntry()->qualifiedTargetLangName() + "." + lst.at(1);
|
|
911 } else {
|
|
912 ReportHandler::warning("bad default value passed to enum " + expr);
|
|
913 }
|
|
914
|
|
915 } else if(arg->type()->isFlags()) {
|
|
916 const FlagsTypeEntry *flagsEntry =
|
|
917 static_cast<const FlagsTypeEntry *>(arg->type()->typeEntry());
|
|
918 EnumTypeEntry *enumEntry = flagsEntry->originator();
|
|
919 AbstractMetaEnum *meta_enum = m_meta_classes.findEnum(enumEntry);
|
|
920 if (!meta_enum) {
|
|
921 ReportHandler::warning("unknown required enum " + enumEntry->qualifiedCppName());
|
|
922 continue;
|
|
923 }
|
|
924
|
|
925 int value = figureOutEnumValue(expr, 0, meta_enum, meta_function);
|
|
926 new_expr = QString::number(value);
|
|
927
|
|
928 } else if (arg->type()->isPrimitive()) {
|
|
929 AbstractMetaEnumValue *value = 0;
|
|
930 if (expr.contains("::"))
|
|
931 value = m_meta_classes.findEnumValue(expr);
|
|
932 if (!value)
|
|
933 value = meta_class->findEnumValue(expr, 0);
|
|
934
|
|
935 if (value) {
|
|
936 new_expr = QString::number(value->value());
|
|
937 } else if (expr.contains(QLatin1Char('+'))) {
|
|
938 new_expr = QString::number(figureOutEnumValue(expr, 0, 0));
|
|
939
|
|
940 }
|
|
941
|
|
942
|
|
943
|
|
944 }
|
|
945
|
|
946 arg->setDefaultValueExpression(new_expr);
|
|
947 }
|
|
948 }
|
|
949 }
|
|
950 }
|
|
951
|
|
952
|
|
953 AbstractMetaEnum *AbstractMetaBuilder::traverseEnum(EnumModelItem enum_item, AbstractMetaClass *enclosing, const QSet<QString> &enumsDeclarations)
|
|
954 {
|
|
955 // Skipping private enums.
|
|
956 if (enum_item->accessPolicy() == CodeModel::Private) {
|
|
957 return 0;
|
|
958 }
|
|
959
|
|
960 QString qualified_name = enum_item->qualifiedName().join("::");
|
|
961
|
|
962 TypeEntry *type_entry = TypeDatabase::instance()->findType(qualified_name);
|
|
963 QString enum_name = enum_item->name();
|
|
964
|
|
965 QString class_name;
|
|
966 if (m_current_class)
|
|
967 class_name = m_current_class->typeEntry()->qualifiedCppName();
|
|
968
|
|
969 if (TypeDatabase::instance()->isEnumRejected(class_name, enum_name)) {
|
|
970 m_rejected_enums.insert(qualified_name, GenerationDisabled);
|
|
971 return 0;
|
|
972 }
|
|
973
|
|
974 if (!type_entry || !type_entry->isEnum()) {
|
|
975 QString context = m_current_class ? m_current_class->name() : QLatin1String("");
|
|
976 ReportHandler::warning(QString("enum '%1' does not have a type entry or is not an enum")
|
|
977 .arg(qualified_name));
|
|
978 m_rejected_enums.insert(qualified_name, NotInTypeSystem);
|
|
979 return 0;
|
|
980 }
|
|
981
|
|
982 AbstractMetaEnum *meta_enum = createMetaEnum();
|
|
983 if ( enumsDeclarations.contains(qualified_name)
|
|
984 || enumsDeclarations.contains(enum_name)) {
|
|
985 meta_enum->setHasQEnumsDeclaration(true);
|
|
986 }
|
|
987
|
|
988 meta_enum->setTypeEntry((EnumTypeEntry *) type_entry);
|
|
989 switch (enum_item->accessPolicy()) {
|
|
990 case CodeModel::Public: *meta_enum += AbstractMetaAttributes::Public; break;
|
|
991 case CodeModel::Protected: *meta_enum += AbstractMetaAttributes::Protected; break;
|
|
992 // case CodeModel::Private: *meta_enum += AbstractMetaAttributes::Private; break;
|
|
993 default: break;
|
|
994 }
|
|
995
|
|
996 ReportHandler::debugMedium(QString(" - traversing enum %1").arg(meta_enum->fullName()));
|
|
997
|
|
998 foreach (EnumeratorModelItem value, enum_item->enumerators()) {
|
|
999
|
|
1000 AbstractMetaEnumValue *meta_enum_value = createMetaEnumValue();
|
|
1001 meta_enum_value->setName(value->name());
|
|
1002 // Deciding the enum value...
|
|
1003
|
|
1004 meta_enum_value->setStringValue(value->value());
|
|
1005 meta_enum->addEnumValue(meta_enum_value);
|
|
1006
|
|
1007 ReportHandler::debugFull(" - " + meta_enum_value->name() + " = "
|
|
1008 + meta_enum_value->value());
|
|
1009
|
|
1010 // Add into global register...
|
|
1011 if (enclosing)
|
|
1012 m_enum_values[enclosing->name() + "::" + meta_enum_value->name()] = meta_enum_value;
|
|
1013 else
|
|
1014 m_enum_values[meta_enum_value->name()] = meta_enum_value;
|
|
1015 }
|
|
1016
|
|
1017 m_enums << meta_enum;
|
|
1018
|
|
1019 return meta_enum;
|
|
1020 }
|
|
1021
|
|
1022 AbstractMetaClass *AbstractMetaBuilder::traverseTypeAlias(TypeAliasModelItem typeAlias)
|
|
1023 {
|
|
1024 QString class_name = strip_template_args(typeAlias->name());
|
|
1025
|
|
1026 QString full_class_name = class_name;
|
|
1027 // we have an inner class
|
|
1028 if (m_current_class) {
|
|
1029 full_class_name = strip_template_args(m_current_class->typeEntry()->qualifiedCppName())
|
|
1030 + "::" + full_class_name;
|
|
1031 }
|
|
1032
|
|
1033 // If we haven't specified anything for the typedef, then we don't care
|
|
1034 ComplexTypeEntry *type = TypeDatabase::instance()->findComplexType(full_class_name);
|
|
1035 if (type == 0)
|
|
1036 return 0;
|
|
1037
|
|
1038 if (type->isObject())
|
|
1039 static_cast<ObjectTypeEntry *>(type)->setQObject(isQObject(strip_template_args(typeAlias->type().qualifiedName().join("::"))));
|
|
1040
|
|
1041 AbstractMetaClass *meta_class = createMetaClass();
|
|
1042 meta_class->setTypeAlias(true);
|
|
1043 meta_class->setTypeEntry(type);
|
|
1044 meta_class->setBaseClassNames(QStringList() << typeAlias->type().qualifiedName().join("::"));
|
|
1045 *meta_class += AbstractMetaAttributes::Public;
|
|
1046
|
|
1047 // Set the default include file name
|
|
1048 if (!type->include().isValid()) {
|
|
1049 QFileInfo info(typeAlias->fileName());
|
|
1050 type->setInclude(Include(Include::IncludePath, info.fileName()));
|
|
1051 }
|
|
1052
|
|
1053 return meta_class;
|
|
1054 }
|
|
1055
|
|
1056 AbstractMetaClass *AbstractMetaBuilder::traverseClass(ClassModelItem class_item)
|
|
1057 {
|
|
1058 QString class_name = strip_template_args(class_item->name());
|
|
1059 QString full_class_name = class_name;
|
|
1060
|
|
1061 // we have inner an class
|
|
1062 if (m_current_class) {
|
|
1063 full_class_name = strip_template_args(m_current_class->typeEntry()->qualifiedCppName())
|
|
1064 + "::" + full_class_name;
|
|
1065 }
|
|
1066
|
|
1067 ComplexTypeEntry *type = TypeDatabase::instance()->findComplexType(full_class_name);
|
|
1068 RejectReason reason = NoReason;
|
|
1069
|
|
1070
|
|
1071 if (TypeDatabase::instance()->isClassRejected(full_class_name)) {
|
|
1072 reason = GenerationDisabled;
|
|
1073 } else if (!type) {
|
|
1074 TypeEntry *te = TypeDatabase::instance()->findType(full_class_name);
|
|
1075 if (te && !te->isComplex())
|
|
1076 reason = RedefinedToNotClass;
|
|
1077 else
|
|
1078 reason = NotInTypeSystem;
|
|
1079 } else if (type->codeGeneration() == TypeEntry::GenerateNothing) {
|
|
1080 reason = GenerationDisabled;
|
|
1081 }
|
|
1082
|
|
1083 if (reason != NoReason) {
|
|
1084 m_rejected_classes.insert(full_class_name, reason);
|
|
1085 return 0;
|
|
1086 }
|
|
1087
|
|
1088 if (type->isObject()) {
|
|
1089 ((ObjectTypeEntry *)type)->setQObject(isQObject(full_class_name));
|
|
1090 }
|
|
1091
|
|
1092 AbstractMetaClass *meta_class = createMetaClass();
|
|
1093 meta_class->setTypeEntry(type);
|
|
1094 meta_class->setBaseClassNames(class_item->baseClasses());
|
|
1095 *meta_class += AbstractMetaAttributes::Public;
|
|
1096
|
|
1097 AbstractMetaClass *old_current_class = m_current_class;
|
|
1098 m_current_class = meta_class;
|
|
1099
|
|
1100 if (type->isContainer()) {
|
|
1101 ReportHandler::debugSparse(QString("container: '%1'").arg(full_class_name));
|
|
1102 } else {
|
|
1103 ReportHandler::debugSparse(QString("class: '%1'").arg(meta_class->fullName()));
|
|
1104 }
|
|
1105
|
|
1106 TemplateParameterList template_parameters = class_item->templateParameters();
|
|
1107 QList<TypeEntry *> template_args;
|
|
1108 template_args.clear();
|
|
1109 for (int i=0; i<template_parameters.size(); ++i) {
|
|
1110 const TemplateParameterModelItem ¶m = template_parameters.at(i);
|
|
1111 TemplateArgumentEntry *param_type = new TemplateArgumentEntry(param->name());
|
|
1112 param_type->setOrdinal(i);
|
|
1113 template_args.append(param_type);
|
|
1114 }
|
|
1115 meta_class->setTemplateArguments(template_args);
|
|
1116
|
|
1117 parseQ_Property(meta_class, class_item->propertyDeclarations());
|
|
1118
|
|
1119 traverseFunctions(model_dynamic_cast<ScopeModelItem>(class_item), meta_class);
|
|
1120 traverseEnums(model_dynamic_cast<ScopeModelItem>(class_item), meta_class, class_item->enumsDeclarations());
|
|
1121 traverseFields(model_dynamic_cast<ScopeModelItem>(class_item), meta_class);
|
|
1122
|
|
1123 // Inner classes
|
|
1124 {
|
|
1125 QList<ClassModelItem> inner_classes = class_item->classMap().values();
|
|
1126 foreach (const ClassModelItem &ci, inner_classes) {
|
|
1127 AbstractMetaClass *cl = traverseClass(ci);
|
|
1128 if (cl) {
|
|
1129 cl->setEnclosingClass(meta_class);
|
|
1130 m_meta_classes << cl;
|
|
1131 }
|
|
1132 }
|
|
1133
|
|
1134 }
|
|
1135
|
|
1136 // Go through all typedefs to see if we have defined any
|
|
1137 // specific typedefs to be used as classes.
|
|
1138 TypeAliasList typeAliases = class_item->typeAliases();
|
|
1139 foreach (TypeAliasModelItem typeAlias, typeAliases) {
|
|
1140 AbstractMetaClass *cls = traverseTypeAlias(typeAlias);
|
|
1141 if (cls != 0) {
|
|
1142 cls->setEnclosingClass(meta_class);
|
|
1143 addAbstractMetaClass(cls);
|
|
1144 }
|
|
1145 }
|
|
1146
|
|
1147
|
|
1148 m_current_class = old_current_class;
|
|
1149
|
|
1150 // Set the default include file name
|
|
1151 if (!type->include().isValid()) {
|
|
1152 QFileInfo info(class_item->fileName());
|
|
1153 type->setInclude(Include(Include::IncludePath, info.fileName()));
|
|
1154 }
|
|
1155
|
|
1156 return meta_class;
|
|
1157 }
|
|
1158
|
|
1159 AbstractMetaField *AbstractMetaBuilder::traverseField(VariableModelItem field, const AbstractMetaClass *cls)
|
|
1160 {
|
|
1161 QString field_name = field->name();
|
|
1162 QString class_name = m_current_class->typeEntry()->qualifiedCppName();
|
|
1163
|
|
1164 // Ignore friend decl.
|
|
1165 if (field->isFriend())
|
|
1166 return 0;
|
|
1167
|
|
1168 if (field->accessPolicy() == CodeModel::Private)
|
|
1169 return 0;
|
|
1170
|
|
1171 if (TypeDatabase::instance()->isFieldRejected(class_name, field_name)) {
|
|
1172 m_rejected_fields.insert(class_name + "::" + field_name, GenerationDisabled);
|
|
1173 return 0;
|
|
1174 }
|
|
1175
|
|
1176
|
|
1177 AbstractMetaField *meta_field = createMetaField();
|
|
1178 meta_field->setName(field_name);
|
|
1179 meta_field->setEnclosingClass(cls);
|
|
1180
|
|
1181 bool ok;
|
|
1182 TypeInfo field_type = field->type();
|
|
1183 AbstractMetaType *meta_type = translateType(field_type, &ok);
|
|
1184
|
|
1185 if (!meta_type || !ok) {
|
|
1186 ReportHandler::warning(QString("skipping field '%1::%2' with unmatched type '%3'")
|
|
1187 .arg(m_current_class->name())
|
|
1188 .arg(field_name)
|
|
1189 .arg(TypeInfo::resolveType(field_type, currentScope()->toItem()).qualifiedName().join("::")));
|
|
1190 delete meta_field;
|
|
1191 return 0;
|
|
1192 }
|
|
1193
|
|
1194 meta_field->setType(meta_type);
|
|
1195
|
|
1196 uint attr = 0;
|
|
1197 if (field->isStatic())
|
|
1198 attr |= AbstractMetaAttributes::Static;
|
|
1199
|
|
1200 CodeModel::AccessPolicy policy = field->accessPolicy();
|
|
1201 if (policy == CodeModel::Public)
|
|
1202 attr |= AbstractMetaAttributes::Public;
|
|
1203 else if (policy == CodeModel::Protected)
|
|
1204 attr |= AbstractMetaAttributes::Protected;
|
|
1205 else
|
|
1206 attr |= AbstractMetaAttributes::Private;
|
|
1207 meta_field->setAttributes(attr);
|
|
1208
|
|
1209 return meta_field;
|
|
1210 }
|
|
1211
|
|
1212 void AbstractMetaBuilder::traverseFields(ScopeModelItem scope_item, AbstractMetaClass *meta_class)
|
|
1213 {
|
|
1214 foreach (VariableModelItem field, scope_item->variables()) {
|
|
1215 AbstractMetaField *meta_field = traverseField(field, meta_class);
|
|
1216
|
|
1217 if (meta_field) {
|
|
1218 meta_field->setOriginalAttributes(meta_field->attributes());
|
|
1219 meta_class->addField(meta_field);
|
|
1220 }
|
|
1221 }
|
|
1222 }
|
|
1223
|
|
1224 void AbstractMetaBuilder::setupFunctionDefaults(AbstractMetaFunction *meta_function, AbstractMetaClass *meta_class)
|
|
1225 {
|
|
1226 // Set the default value of the declaring class. This may be changed
|
|
1227 // in fixFunctions later on
|
|
1228 meta_function->setDeclaringClass(meta_class);
|
|
1229
|
|
1230 // Some of the queries below depend on the implementing class being set
|
|
1231 // to function properly. Such as function modifications
|
|
1232 meta_function->setImplementingClass(meta_class);
|
|
1233
|
|
1234 if (meta_function->name() == "operator_equal")
|
|
1235 meta_class->setHasEqualsOperator(true);
|
|
1236
|
|
1237 if (!meta_function->isFinalInTargetLang()
|
|
1238 && meta_function->isRemovedFrom(meta_class, TypeSystem::TargetLangCode)) {
|
|
1239 *meta_function += AbstractMetaAttributes::FinalInCpp;
|
|
1240 }
|
|
1241 }
|
|
1242
|
|
1243 void AbstractMetaBuilder::traverseFunctions(ScopeModelItem scope_item, AbstractMetaClass *meta_class)
|
|
1244 {
|
|
1245 foreach (FunctionModelItem function, scope_item->functions()) {
|
|
1246 AbstractMetaFunction *meta_function = traverseFunction(function);
|
|
1247
|
|
1248 if (meta_function) {
|
|
1249 meta_function->setOriginalAttributes(meta_function->attributes());
|
|
1250 if (meta_class->isNamespace())
|
|
1251 *meta_function += AbstractMetaAttributes::Static;
|
|
1252
|
|
1253 if (!meta_function->isInvalid()) {
|
|
1254 if (QPropertySpec *read = meta_class->propertySpecForRead(meta_function->name())) {
|
|
1255 if (read->type() == meta_function->type()->typeEntry()) {
|
|
1256 *meta_function += AbstractMetaAttributes::PropertyReader;
|
|
1257 meta_function->setPropertySpec(read);
|
|
1258 // printf("%s is reader for %s\n",
|
|
1259 // qPrintable(meta_function->name()),
|
|
1260 // qPrintable(read->name()));
|
|
1261 }
|
|
1262 } else if (QPropertySpec *write =
|
|
1263 meta_class->propertySpecForWrite(meta_function->name())) {
|
|
1264 if (write->type() == meta_function->arguments().at(0)->type()->typeEntry()) {
|
|
1265 *meta_function += AbstractMetaAttributes::PropertyWriter;
|
|
1266 meta_function->setPropertySpec(write);
|
|
1267 /*
|
|
1268 // qtd D properties syntax, rename setter from setPropertyName to just propertyName
|
|
1269 AbstractMetaFunction *f_copy = new AbstractMetaFunction();
|
|
1270 *f_copy = *meta_function;
|
|
1271
|
|
1272 QString f_name = meta_function->name();
|
|
1273 QString new_name = f_name.midRef(3, 1).toString().toLower() + f_name.right(f_name.length() - 4);
|
|
1274 meta_function->setName(new_name);
|
|
1275 // f_copy->setOwnerClass(meta_function->ownerClass());
|
|
1276 ((AbstractMetaClass*)(meta_function->ownerClass()))->addFunction(f_copy); // hack qtd2
|
|
1277 //
|
|
1278 */
|
|
1279 // printf("%s is writer for %s\n",
|
|
1280 // qPrintable(meta_function->name()),
|
|
1281 // qPrintable(write->name()));
|
|
1282 }
|
|
1283 } else if (QPropertySpec *reset =
|
|
1284 meta_class->propertySpecForReset(meta_function->name())) {
|
|
1285 *meta_function += AbstractMetaAttributes::PropertyResetter;
|
|
1286 meta_function->setPropertySpec(reset);
|
|
1287 // printf("%s is resetter for %s\n",
|
|
1288 // qPrintable(meta_function->name()),
|
|
1289 // qPrintable(reset->name()));
|
|
1290 }
|
|
1291 }
|
|
1292
|
|
1293
|
|
1294 bool isInvalidDestructor = meta_function->isDestructor() && meta_function->isPrivate();
|
|
1295 bool isInvalidConstructor = meta_function->isConstructor()
|
|
1296 && (meta_function->isPrivate() || meta_function->isInvalid());
|
|
1297 if ((isInvalidDestructor || isInvalidConstructor)
|
|
1298 && !meta_class->hasNonPrivateConstructor()) {
|
|
1299 *meta_class += AbstractMetaAttributes::Final;
|
|
1300 } else if (meta_function->isConstructor() && !meta_function->isPrivate()) {
|
|
1301 *meta_class -= AbstractMetaAttributes::Final;
|
|
1302 meta_class->setHasNonPrivateConstructor(true);
|
|
1303 }
|
|
1304
|
|
1305 // Classes with virtual destructors should always have a shell class
|
|
1306 // (since we aren't registering the destructors, we need this extra check)
|
|
1307 if (meta_function->isDestructor() && !meta_function->isFinal())
|
|
1308 meta_class->setForceShellClass(true);
|
|
1309
|
|
1310 if (!meta_function->isDestructor()
|
|
1311 && !meta_function->isInvalid()
|
|
1312 && (!meta_function->isConstructor() || !meta_function->isPrivate())) {
|
|
1313
|
|
1314 if (meta_class->typeEntry()->designatedInterface() && !meta_function->isPublic()
|
|
1315 && !meta_function->isPrivate()) {
|
|
1316 QString warn = QString("non-public function '%1' in interface '%2'")
|
|
1317 .arg(meta_function->name()).arg(meta_class->name());
|
|
1318 ReportHandler::warning(warn);
|
|
1319
|
|
1320 meta_function->setVisibility(AbstractMetaClass::Public);
|
|
1321 }
|
|
1322
|
|
1323 setupFunctionDefaults(meta_function, meta_class);
|
|
1324
|
|
1325 if (meta_function->isSignal() && meta_class->hasSignal(meta_function)) {
|
|
1326 QString warn = QString("signal '%1' in class '%2' is overloaded.")
|
|
1327 .arg(meta_function->name()).arg(meta_class->name());
|
|
1328 ReportHandler::warning(warn);
|
|
1329 }
|
|
1330
|
|
1331 if (meta_function->isSignal() && !meta_class->isQObject()) {
|
|
1332 QString warn = QString("signal '%1' in non-QObject class '%2'")
|
|
1333 .arg(meta_function->name()).arg(meta_class->name());
|
|
1334 ReportHandler::warning(warn);
|
|
1335 }
|
|
1336
|
|
1337 meta_class->addFunction(meta_function);
|
|
1338 } else if (meta_function->isDestructor() && !meta_function->isPublic()) {
|
|
1339 meta_class->setHasPublicDestructor(false);
|
|
1340 }
|
|
1341 }
|
|
1342 }
|
|
1343 }
|
|
1344
|
|
1345 bool AbstractMetaBuilder::setupInheritance(AbstractMetaClass *meta_class)
|
|
1346 {
|
|
1347 Q_ASSERT(!meta_class->isInterface());
|
|
1348
|
|
1349 if (m_setup_inheritance_done.contains(meta_class))
|
|
1350 return true;
|
|
1351 m_setup_inheritance_done.insert(meta_class);
|
|
1352
|
|
1353 QStringList base_classes = meta_class->baseClassNames();
|
|
1354
|
|
1355 TypeDatabase *types = TypeDatabase::instance();
|
|
1356
|
|
1357 // we only support our own containers and ONLY if there is only one baseclass
|
|
1358 if (base_classes.size() == 1 && base_classes.first().count('<') == 1) {
|
|
1359 QStringList scope = meta_class->typeEntry()->qualifiedCppName().split("::");
|
|
1360 scope.removeLast();
|
|
1361 for (int i=scope.size(); i>=0; --i) {
|
|
1362 QString prefix = i > 0 ? QStringList(scope.mid(0, i)).join("::") + "::" : QString();
|
|
1363 QString complete_name = prefix + base_classes.first();
|
|
1364 TypeParser::Info info = TypeParser::parse(complete_name);
|
|
1365 QString base_name = info.qualified_name.join("::");
|
|
1366
|
|
1367 AbstractMetaClass *templ = 0;
|
|
1368 foreach (AbstractMetaClass *c, m_templates) {
|
|
1369 if (c->typeEntry()->name() == base_name) {
|
|
1370 templ = c;
|
|
1371 break;
|
|
1372 }
|
|
1373 }
|
|
1374
|
|
1375 if (templ == 0)
|
|
1376 templ = m_meta_classes.findClass(base_name);
|
|
1377
|
|
1378 if (templ) {
|
|
1379 setupInheritance(templ);
|
|
1380 inheritTemplate(meta_class, templ, info);
|
|
1381 return true;
|
|
1382 }
|
|
1383 }
|
|
1384
|
|
1385 ReportHandler::warning(QString("template baseclass '%1' of '%2' is not known")
|
|
1386 .arg(base_classes.first())
|
|
1387 .arg(meta_class->name()));
|
|
1388 return false;
|
|
1389 }
|
|
1390
|
|
1391 int primary = -1;
|
|
1392 int primaries = 0;
|
|
1393 for (int i=0; i<base_classes.size(); ++i) {
|
|
1394
|
|
1395 if (types->isClassRejected(base_classes.at(i)))
|
|
1396 continue;
|
|
1397
|
|
1398 TypeEntry *base_class_entry = types->findType(base_classes.at(i));
|
|
1399 if (!base_class_entry) {
|
|
1400 ReportHandler::warning(QString("class '%1' inherits from unknown base class '%2'")
|
|
1401 .arg(meta_class->name()).arg(base_classes.at(i)));
|
|
1402 }
|
|
1403
|
|
1404 // true for primary base class
|
|
1405 else if (!base_class_entry->designatedInterface()) {
|
|
1406 if (primaries > 0) {
|
|
1407 ReportHandler::warning(QString("class '%1' has multiple primary base classes"
|
|
1408 " '%2' and '%3'")
|
|
1409 .arg(meta_class->name())
|
|
1410 .arg(base_classes.at(primary))
|
|
1411 .arg(base_class_entry->name()));
|
|
1412 return false;
|
|
1413 }
|
|
1414 primaries++;
|
|
1415 primary = i;
|
|
1416 }
|
|
1417 }
|
|
1418
|
|
1419 if (primary >= 0) {
|
|
1420 AbstractMetaClass *base_class = m_meta_classes.findClass(base_classes.at(primary));
|
|
1421 if (!base_class) {
|
|
1422 ReportHandler::warning(QString("unknown baseclass for '%1': '%2'")
|
|
1423 .arg(meta_class->name())
|
|
1424 .arg(base_classes.at(primary)));
|
|
1425 return false;
|
|
1426 }
|
|
1427 meta_class->setBaseClass(base_class);
|
|
1428
|
|
1429 if (meta_class->typeEntry()->designatedInterface() != 0 && meta_class->isQObject()) {
|
|
1430 ReportHandler::warning(QString("QObject extended by interface type '%1'. This is not supported and the generated Java code will not compile.")
|
|
1431 .arg(meta_class->name()));
|
|
1432 } else if (meta_class->typeEntry()->designatedInterface() != 0 && base_class != 0 && !base_class->isInterface()) {
|
|
1433 ReportHandler::warning(QString("object type '%1' extended by interface type '%2'. The resulting API will be less expressive than the original.")
|
|
1434 .arg(base_class->name())
|
|
1435 .arg(meta_class->name()));
|
|
1436 }
|
|
1437
|
|
1438 }
|
|
1439
|
|
1440 for (int i=0; i<base_classes.size(); ++i) {
|
|
1441 if (types->isClassRejected(base_classes.at(i)))
|
|
1442 continue;
|
|
1443
|
|
1444 if (i != primary) {
|
|
1445 AbstractMetaClass *base_class = m_meta_classes.findClass(base_classes.at(i));
|
|
1446 if (base_class == 0) {
|
|
1447 ReportHandler::warning(QString("class not found for setup inheritance '%1'").arg(base_classes.at(i)));
|
|
1448 return false;
|
|
1449 }
|
|
1450
|
|
1451 setupInheritance(base_class);
|
|
1452
|
|
1453 QString interface_name = InterfaceTypeEntry::interfaceName(base_class->name());
|
|
1454 AbstractMetaClass *iface = m_meta_classes.findClass(interface_name);
|
|
1455 if (!iface) {
|
|
1456 ReportHandler::warning(QString("unknown interface for '%1': '%2'")
|
|
1457 .arg(meta_class->name())
|
|
1458 .arg(interface_name));
|
|
1459 return false;
|
|
1460 }
|
|
1461 meta_class->addInterface(iface);
|
|
1462
|
|
1463 AbstractMetaClassList interfaces = iface->interfaces();
|
|
1464 foreach (AbstractMetaClass *iface, interfaces)
|
|
1465 meta_class->addInterface(iface);
|
|
1466 }
|
|
1467 }
|
|
1468
|
|
1469 return true;
|
|
1470 }
|
|
1471
|
|
1472 void AbstractMetaBuilder::traverseEnums(ScopeModelItem scope_item, AbstractMetaClass *meta_class, const QStringList &enumsDeclarations)
|
|
1473 {
|
|
1474 EnumList enums = scope_item->enums();
|
|
1475 foreach (EnumModelItem enum_item, enums) {
|
|
1476 AbstractMetaEnum *meta_enum = traverseEnum(enum_item, meta_class, QSet<QString>::fromList(enumsDeclarations));
|
|
1477 if (meta_enum) {
|
|
1478 meta_enum->setOriginalAttributes(meta_enum->attributes());
|
|
1479 meta_class->addEnum(meta_enum);
|
|
1480 meta_enum->setEnclosingClass(meta_class);
|
|
1481 }
|
|
1482 }
|
|
1483 }
|
|
1484
|
|
1485 AbstractMetaFunction *AbstractMetaBuilder::traverseFunction(FunctionModelItem function_item)
|
|
1486 {
|
|
1487 QString function_name = function_item->name();
|
|
1488 QString class_name = m_current_class->typeEntry()->qualifiedCppName();
|
|
1489
|
|
1490 if (TypeDatabase::instance()->isFunctionRejected(class_name, function_name)) {
|
|
1491 m_rejected_functions.insert(class_name + "::" + function_name, GenerationDisabled);
|
|
1492 return 0;
|
|
1493 }
|
|
1494
|
|
1495
|
|
1496 Q_ASSERT(function_item->functionType() == CodeModel::Normal
|
|
1497 || function_item->functionType() == CodeModel::Signal
|
|
1498 || function_item->functionType() == CodeModel::Slot);
|
|
1499
|
|
1500 if (function_item->isFriend())
|
|
1501 return 0;
|
|
1502
|
|
1503
|
|
1504 QString cast_type;
|
|
1505
|
|
1506 if (function_name.startsWith("operator")) {
|
|
1507 function_name = rename_operator(function_name.mid(8));
|
|
1508 if (function_name.isEmpty()) {
|
|
1509 m_rejected_functions.insert(class_name + "::" + function_name,
|
|
1510 GenerationDisabled);
|
|
1511 return 0;
|
|
1512 }
|
|
1513 if (function_name.contains("_cast_"))
|
|
1514 cast_type = function_name.mid(14).trimmed();
|
|
1515 }
|
|
1516
|
|
1517 AbstractMetaFunction *meta_function = createMetaFunction();
|
|
1518 meta_function->setConstant(function_item->isConstant());
|
|
1519
|
|
1520 ReportHandler::debugMedium(QString(" - %2()").arg(function_name));
|
|
1521
|
|
1522 meta_function->setName(function_name);
|
|
1523 meta_function->setOriginalName(function_item->name());
|
|
1524
|
|
1525 if (function_item->isAbstract())
|
|
1526 *meta_function += AbstractMetaAttributes::Abstract;
|
|
1527
|
|
1528 if (!meta_function->isAbstract())
|
|
1529 *meta_function += AbstractMetaAttributes::Native;
|
|
1530
|
|
1531 if (!function_item->isVirtual())
|
|
1532 *meta_function += AbstractMetaAttributes::Final;
|
|
1533
|
|
1534 if (function_item->isInvokable())
|
|
1535 *meta_function += AbstractMetaAttributes::Invokable;
|
|
1536
|
|
1537 if (function_item->isStatic()) {
|
|
1538 *meta_function += AbstractMetaAttributes::Static;
|
|
1539 *meta_function += AbstractMetaAttributes::Final;
|
|
1540 }
|
|
1541
|
|
1542 // Access rights
|
|
1543 if (function_item->accessPolicy() == CodeModel::Public)
|
|
1544 *meta_function += AbstractMetaAttributes::Public;
|
|
1545 else if (function_item->accessPolicy() == CodeModel::Private)
|
|
1546 *meta_function += AbstractMetaAttributes::Private;
|
|
1547 else
|
|
1548 *meta_function += AbstractMetaAttributes::Protected;
|
|
1549
|
|
1550
|
|
1551 QString stripped_class_name = class_name;
|
|
1552 int cc_pos = stripped_class_name.lastIndexOf("::");
|
|
1553 if (cc_pos > 0)
|
|
1554 stripped_class_name = stripped_class_name.mid(cc_pos + 2);
|
|
1555
|
|
1556 TypeInfo function_type = function_item->type();
|
|
1557 if (function_name.startsWith('~')) {
|
|
1558 meta_function->setFunctionType(AbstractMetaFunction::DestructorFunction);
|
|
1559 meta_function->setInvalid(true);
|
|
1560 } else if (strip_template_args(function_name) == stripped_class_name) {
|
|
1561 meta_function->setFunctionType(AbstractMetaFunction::ConstructorFunction);
|
|
1562 meta_function->setName(m_current_class->name());
|
|
1563 } else {
|
|
1564 bool ok;
|
|
1565 AbstractMetaType *type = 0;
|
|
1566
|
|
1567 if (!cast_type.isEmpty()) {
|
|
1568 TypeInfo info;
|
|
1569 info.setQualifiedName(QStringList(cast_type));
|
|
1570 type = translateType(info, &ok);
|
|
1571 } else {
|
|
1572 type = translateType(function_type, &ok);
|
|
1573 }
|
|
1574
|
|
1575 if (!ok) {
|
|
1576 ReportHandler::warning(QString("skipping function '%1::%2', unmatched return type '%3'")
|
|
1577 .arg(class_name)
|
|
1578 .arg(function_item->name())
|
|
1579 .arg(function_item->type().toString()));
|
|
1580 m_rejected_functions[class_name + "::" + function_name] =
|
|
1581 UnmatchedReturnType;
|
|
1582 meta_function->setInvalid(true);
|
|
1583 return meta_function;
|
|
1584 }
|
|
1585 meta_function->setType(type);
|
|
1586
|
|
1587 if (function_item->functionType() == CodeModel::Signal)
|
|
1588 meta_function->setFunctionType(AbstractMetaFunction::SignalFunction);
|
|
1589 else if (function_item->functionType() == CodeModel::Slot)
|
|
1590 meta_function->setFunctionType(AbstractMetaFunction::SlotFunction);
|
|
1591 }
|
|
1592
|
|
1593 ArgumentList arguments = function_item->arguments();
|
|
1594 AbstractMetaArgumentList meta_arguments;
|
|
1595
|
|
1596 int first_default_argument = 0;
|
|
1597 for (int i=0; i<arguments.size(); ++i) {
|
|
1598 ArgumentModelItem arg = arguments.at(i);
|
|
1599
|
|
1600 bool ok;
|
|
1601 AbstractMetaType *meta_type = translateType(arg->type(), &ok);
|
|
1602 if (!meta_type || !ok) {
|
|
1603 ReportHandler::warning(QString("skipping function '%1::%2', "
|
|
1604 "unmatched parameter type '%3'")
|
|
1605 .arg(class_name)
|
|
1606 .arg(function_item->name())
|
|
1607 .arg(arg->type().toString()));
|
|
1608 m_rejected_functions[class_name + "::" + function_name] =
|
|
1609 UnmatchedArgumentType;
|
|
1610 meta_function->setInvalid(true);
|
|
1611 return meta_function;
|
|
1612 }
|
|
1613 AbstractMetaArgument *meta_argument = createMetaArgument();
|
|
1614 meta_argument->setType(meta_type);
|
|
1615 meta_argument->setName(arg->name());
|
|
1616 meta_argument->setArgumentIndex(i);
|
|
1617 meta_arguments << meta_argument;
|
|
1618 }
|
|
1619
|
|
1620 meta_function->setArguments(meta_arguments);
|
|
1621
|
|
1622 // Find the correct default values
|
|
1623 for (int i=0; i<arguments.size(); ++i) {
|
|
1624 ArgumentModelItem arg = arguments.at(i);
|
|
1625 AbstractMetaArgument *meta_arg = meta_arguments.at(i);
|
|
1626 if (arg->defaultValue()) {
|
|
1627 QString expr = arg->defaultValueExpression();
|
|
1628 if (!expr.isEmpty())
|
|
1629 meta_arg->setOriginalDefaultValueExpression(expr);
|
|
1630
|
|
1631 expr = translateDefaultValue(arg, meta_arg->type(), meta_function, m_current_class, i);
|
|
1632 if (expr.isEmpty()) {
|
|
1633 first_default_argument = i;
|
|
1634 } else {
|
|
1635 meta_arg->setDefaultValueExpression(expr);
|
|
1636 }
|
|
1637
|
|
1638 if (meta_arg->type()->isEnum() || meta_arg->type()->isFlags()) {
|
|
1639 m_enum_default_arguments
|
|
1640 << QPair<AbstractMetaArgument *, AbstractMetaFunction *>(meta_arg, meta_function);
|
|
1641 }
|
|
1642
|
|
1643 }
|
|
1644 }
|
|
1645
|
|
1646 // If we where not able to translate the default argument make it
|
|
1647 // reset all default arguments before this one too.
|
|
1648 for (int i=0; i<first_default_argument; ++i)
|
|
1649 meta_arguments[i]->setDefaultValueExpression(QString());
|
|
1650
|
|
1651 if (ReportHandler::debugLevel() == ReportHandler::FullDebug)
|
|
1652 foreach(AbstractMetaArgument *arg, meta_arguments)
|
|
1653 ReportHandler::debugFull(" - " + arg->toString());
|
|
1654
|
|
1655 return meta_function;
|
|
1656 }
|
|
1657
|
|
1658
|
|
1659 AbstractMetaType *AbstractMetaBuilder::translateType(const TypeInfo &_typei, bool *ok, bool resolveType, bool resolveScope)
|
|
1660 {
|
|
1661 Q_ASSERT(ok);
|
|
1662 *ok = true;
|
|
1663
|
|
1664 // 1. Test the type info without resolving typedefs in case this is present in the
|
|
1665 // type system
|
|
1666 TypeInfo typei;
|
|
1667 if (resolveType) {
|
|
1668 bool ok;
|
|
1669 AbstractMetaType *t = translateType(_typei, &ok, false, resolveScope);
|
|
1670 if (t != 0 && ok)
|
|
1671 return t;
|
|
1672 }
|
|
1673
|
|
1674 if (!resolveType)
|
|
1675 typei = _typei;
|
|
1676 else {
|
|
1677 // Go through all parts of the current scope (including global namespace)
|
|
1678 // to resolve typedefs. The parser does not properly resolve typedefs in
|
|
1679 // the global scope when they are referenced from inside a namespace.
|
|
1680 // This is a work around to fix this bug since fixing it in resolveType
|
|
1681 // seemed non-trivial
|
|
1682 int i = m_scopes.size() - 1;
|
|
1683 while (i >= 0) {
|
|
1684 typei = TypeInfo::resolveType(_typei, m_scopes.at(i--)->toItem());
|
|
1685 if (typei.qualifiedName().join("::") != _typei.qualifiedName().join("::"))
|
|
1686 break;
|
|
1687 }
|
|
1688
|
|
1689 }
|
|
1690
|
|
1691 if (typei.isFunctionPointer()) {
|
|
1692 *ok = false;
|
|
1693 return 0;
|
|
1694 }
|
|
1695
|
|
1696 TypeParser::Info typeInfo = TypeParser::parse(typei.toString());
|
|
1697 if (typeInfo.is_busted) {
|
|
1698 *ok = false;
|
|
1699 return 0;
|
|
1700 }
|
|
1701
|
|
1702 // 2. Handle pointers specified as arrays with unspecified size
|
|
1703 bool array_of_unspecified_size = false;
|
|
1704 if (typeInfo.arrays.size() > 0) {
|
|
1705 array_of_unspecified_size = true;
|
|
1706 for (int i=0; i<typeInfo.arrays.size(); ++i)
|
|
1707 array_of_unspecified_size = array_of_unspecified_size && typeInfo.arrays.at(i).isEmpty();
|
|
1708
|
|
1709 if (!array_of_unspecified_size) {
|
|
1710 TypeInfo newInfo;
|
|
1711 //newInfo.setArguments(typei.arguments());
|
|
1712 newInfo.setIndirections(typei.indirections());
|
|
1713 newInfo.setConstant(typei.isConstant());
|
|
1714 newInfo.setFunctionPointer(typei.isFunctionPointer());
|
|
1715 newInfo.setQualifiedName(typei.qualifiedName());
|
|
1716 newInfo.setReference(typei.isReference());
|
|
1717 newInfo.setVolatile(typei.isVolatile());
|
|
1718
|
|
1719 AbstractMetaType *elementType = translateType(newInfo, ok);
|
|
1720 if (!ok)
|
|
1721 return 0;
|
|
1722
|
|
1723 for (int i=typeInfo.arrays.size()-1; i>=0; --i) {
|
|
1724 QString s = typeInfo.arrays.at(i);
|
|
1725 bool ok;
|
|
1726
|
|
1727 int elems = s.toInt(&ok);
|
|
1728 if (!ok)
|
|
1729 return 0;
|
|
1730
|
|
1731 AbstractMetaType *arrayType = createMetaType();
|
|
1732 arrayType->setArrayElementCount(elems);
|
|
1733 arrayType->setArrayElementType(elementType);
|
|
1734 arrayType->setTypeEntry(new ArrayTypeEntry(elementType->typeEntry()));
|
|
1735 decideUsagePattern(arrayType);
|
|
1736
|
|
1737 elementType = arrayType;
|
|
1738 }
|
|
1739
|
|
1740 return elementType;
|
|
1741 } else {
|
|
1742 typeInfo.indirections += typeInfo.arrays.size();
|
|
1743 }
|
|
1744 }
|
|
1745
|
|
1746 QStringList qualifier_list = typeInfo.qualified_name;
|
|
1747 if (qualifier_list.isEmpty()) {
|
|
1748 ReportHandler::warning(QString("horribly broken type '%1'").arg(_typei.toString()));
|
|
1749 *ok = false;
|
|
1750 return 0;
|
|
1751 }
|
|
1752
|
|
1753 QString qualified_name = qualifier_list.join("::");
|
|
1754 QString name = qualifier_list.takeLast();
|
|
1755
|
|
1756 // 3. Special case 'void' type
|
|
1757 if (name == "void" && typeInfo.indirections == 0) {
|
|
1758 return 0;
|
|
1759 }
|
|
1760
|
|
1761 // 4. Special case QFlags (include instantiation in name)
|
|
1762 if (qualified_name == "QFlags")
|
|
1763 qualified_name = typeInfo.toString();
|
|
1764
|
|
1765 // 5. Try to find the type
|
|
1766 const TypeEntry *type = TypeDatabase::instance()->findType(qualified_name);
|
|
1767
|
|
1768 // 6. No? Try looking it up as a flags type
|
|
1769 if (!type)
|
|
1770 type = TypeDatabase::instance()->findFlagsType(qualified_name);
|
|
1771
|
|
1772 // 7. No? Try looking it up as a container type
|
|
1773 if (!type)
|
|
1774 type = TypeDatabase::instance()->findContainerType(name);
|
|
1775
|
|
1776 // 8. No? Check if the current class is a template and this type is one
|
|
1777 // of the parameters.
|
|
1778 if (type == 0 && m_current_class != 0) {
|
|
1779 QList<TypeEntry *> template_args = m_current_class->templateArguments();
|
|
1780 foreach (TypeEntry *te, template_args) {
|
|
1781 if (te->name() == qualified_name)
|
|
1782 type = te;
|
|
1783 }
|
|
1784 }
|
|
1785
|
|
1786 // 9. Try finding the type by prefixing it with the current
|
|
1787 // context and all baseclasses of the current context
|
|
1788 if (!type && !TypeDatabase::instance()->isClassRejected(qualified_name) && m_current_class != 0 && resolveScope) {
|
|
1789 QStringList contexts;
|
|
1790 contexts.append(m_current_class->qualifiedCppName());
|
|
1791 contexts.append(currentScope()->qualifiedName().join("::"));
|
|
1792
|
|
1793
|
|
1794 TypeInfo info = typei;
|
|
1795 bool subclasses_done = false;
|
|
1796 while (!contexts.isEmpty() && type == 0) {
|
|
1797 //type = TypeDatabase::instance()->findType(contexts.at(0) + "::" + qualified_name);
|
|
1798
|
|
1799 bool ok;
|
|
1800 info.setQualifiedName(QStringList() << contexts.at(0) << qualified_name);
|
|
1801 AbstractMetaType *t = translateType(info, &ok, true, false);
|
|
1802 if (t != 0 && ok)
|
|
1803 return t;
|
|
1804
|
|
1805 ClassModelItem item = m_dom->findClass(contexts.at(0));
|
|
1806 if (item != 0)
|
|
1807 contexts += item->baseClasses();
|
|
1808 contexts.pop_front();
|
|
1809
|
|
1810 // 10. Last resort: Special cased prefix of Qt namespace since the meta object implicitly inherits this, so
|
|
1811 // enum types from there may be addressed without any scope resolution in properties.
|
|
1812 if (contexts.size() == 0 && !subclasses_done) {
|
|
1813 contexts << "Qt";
|
|
1814 subclasses_done = true;
|
|
1815 }
|
|
1816 }
|
|
1817
|
|
1818 }
|
|
1819
|
|
1820 if (!type) {
|
|
1821 *ok = false;
|
|
1822 return 0;
|
|
1823 }
|
|
1824
|
|
1825 // Used to for diagnostics later...
|
|
1826 m_used_types << type;
|
|
1827
|
|
1828 // These are only implicit and should not appear in code...
|
|
1829 Q_ASSERT(!type->isInterface());
|
|
1830
|
|
1831 AbstractMetaType *meta_type = createMetaType();
|
|
1832 meta_type->setTypeEntry(type);
|
|
1833 meta_type->setIndirections(typeInfo.indirections);
|
|
1834 meta_type->setReference(typeInfo.is_reference);
|
|
1835 meta_type->setConstant(typeInfo.is_constant);
|
|
1836 meta_type->setOriginalTypeDescription(_typei.toString());
|
|
1837 decideUsagePattern(meta_type);
|
|
1838
|
|
1839 if (meta_type->typeEntry()->isContainer()) {
|
|
1840 ContainerTypeEntry::Type container_type = static_cast<const ContainerTypeEntry *>(type)->type();
|
|
1841
|
|
1842 if (container_type == ContainerTypeEntry::StringListContainer) {
|
|
1843 TypeInfo info;
|
|
1844 info.setQualifiedName(QStringList() << "QString");
|
|
1845 AbstractMetaType *targ_type = translateType(info, ok);
|
|
1846
|
|
1847 Q_ASSERT(*ok);
|
|
1848 Q_ASSERT(targ_type);
|
|
1849
|
|
1850 meta_type->addInstantiation(targ_type);
|
|
1851 meta_type->setInstantiationInCpp(false);
|
|
1852
|
|
1853 } else {
|
|
1854 foreach (const TypeParser::Info &ta, typeInfo.template_instantiations) {
|
|
1855 TypeInfo info;
|
|
1856 info.setConstant(ta.is_constant);
|
|
1857 info.setReference(ta.is_reference);
|
|
1858 info.setIndirections(ta.indirections);
|
|
1859
|
|
1860 info.setFunctionPointer(false);
|
|
1861 info.setQualifiedName(ta.instantiationName().split("::"));
|
|
1862
|
|
1863 AbstractMetaType *targ_type = translateType(info, ok);
|
|
1864 if (!(*ok)) {
|
|
1865 delete meta_type;
|
|
1866 return 0;
|
|
1867 }
|
|
1868
|
|
1869 meta_type->addInstantiation(targ_type);
|
|
1870 }
|
|
1871 }
|
|
1872
|
|
1873 if (container_type == ContainerTypeEntry::ListContainer
|
|
1874 || container_type == ContainerTypeEntry::VectorContainer
|
|
1875 || container_type == ContainerTypeEntry::StringListContainer) {
|
|
1876 Q_ASSERT(meta_type->instantiations().size() == 1);
|
|
1877 }
|
|
1878 }
|
|
1879
|
|
1880 return meta_type;
|
|
1881 }
|
|
1882
|
|
1883 void AbstractMetaBuilder::decideUsagePattern(AbstractMetaType *meta_type)
|
|
1884 {
|
|
1885 const TypeEntry *type = meta_type->typeEntry();
|
|
1886
|
|
1887 if (type->isPrimitive() && (meta_type->actualIndirections() == 0
|
|
1888 || (meta_type->isConstant() && meta_type->isReference() && meta_type->indirections() == 0))) {
|
|
1889 meta_type->setTypeUsagePattern(AbstractMetaType::PrimitivePattern);
|
|
1890
|
|
1891 } else if (type->isVoid()) {
|
|
1892 meta_type->setTypeUsagePattern(AbstractMetaType::NativePointerPattern);
|
|
1893
|
|
1894 } else if (type->isString()
|
|
1895 && meta_type->indirections() == 0
|
|
1896 && (meta_type->isConstant() == meta_type->isReference()
|
|
1897 || meta_type->isConstant())) {
|
|
1898 meta_type->setTypeUsagePattern(AbstractMetaType::StringPattern);
|
|
1899
|
|
1900 } else if (type->isChar()
|
|
1901 && meta_type->indirections() == 0
|
|
1902 && meta_type->isConstant() == meta_type->isReference()) {
|
|
1903 meta_type->setTypeUsagePattern(AbstractMetaType::CharPattern);
|
|
1904
|
|
1905 } else if (type->isJObjectWrapper()
|
|
1906 && meta_type->indirections() == 0
|
|
1907 && meta_type->isConstant() == meta_type->isReference()) {
|
|
1908 meta_type->setTypeUsagePattern(AbstractMetaType::JObjectWrapperPattern);
|
|
1909
|
|
1910 } else if (type->isVariant()
|
|
1911 && meta_type->indirections() == 0
|
|
1912 && meta_type->isConstant() == meta_type->isReference()) {
|
|
1913 meta_type->setTypeUsagePattern(AbstractMetaType::VariantPattern);
|
|
1914
|
|
1915 } else if (type->isEnum() && meta_type->actualIndirections() == 0) {
|
|
1916 meta_type->setTypeUsagePattern(AbstractMetaType::EnumPattern);
|
|
1917
|
|
1918 } else if (type->isObject()
|
|
1919 && meta_type->indirections() == 0
|
|
1920 && meta_type->isReference()) {
|
|
1921 if (((ComplexTypeEntry *) type)->isQObject())
|
|
1922 meta_type->setTypeUsagePattern(AbstractMetaType::QObjectPattern);
|
|
1923 else
|
|
1924 meta_type->setTypeUsagePattern(AbstractMetaType::ObjectPattern);
|
|
1925
|
|
1926 } else if (type->isObject()
|
|
1927 && meta_type->indirections() == 1) {
|
|
1928 if (((ComplexTypeEntry *) type)->isQObject())
|
|
1929 meta_type->setTypeUsagePattern(AbstractMetaType::QObjectPattern);
|
|
1930 else
|
|
1931 meta_type->setTypeUsagePattern(AbstractMetaType::ObjectPattern);
|
|
1932
|
|
1933 // const-references to pointers can be passed as pointers
|
|
1934 if (meta_type->isReference() && meta_type->isConstant()) {
|
|
1935 meta_type->setReference(false);
|
|
1936 meta_type->setConstant(false);
|
|
1937 }
|
|
1938
|
|
1939 } else if (type->isContainer() && meta_type->indirections() == 0) {
|
|
1940 meta_type->setTypeUsagePattern(AbstractMetaType::ContainerPattern);
|
|
1941
|
|
1942 } else if (type->isTemplateArgument()) {
|
|
1943
|
|
1944 } else if (type->isFlags()
|
|
1945 && meta_type->indirections() == 0
|
|
1946 && (meta_type->isConstant() == meta_type->isReference())) {
|
|
1947 meta_type->setTypeUsagePattern(AbstractMetaType::FlagsPattern);
|
|
1948
|
|
1949 } else if (type->isArray()) {
|
|
1950 meta_type->setTypeUsagePattern(AbstractMetaType::ArrayPattern);
|
|
1951
|
|
1952 } else if (type->isThread()) {
|
|
1953 Q_ASSERT(meta_type->indirections() == 1);
|
|
1954 meta_type->setTypeUsagePattern(AbstractMetaType::ThreadPattern);
|
|
1955
|
|
1956 } else if (type->isValue()
|
|
1957 && meta_type->indirections() == 0
|
|
1958 && (meta_type->isConstant() == meta_type->isReference()
|
|
1959 || !meta_type->isReference())) {
|
|
1960 meta_type->setTypeUsagePattern(AbstractMetaType::ValuePattern);
|
|
1961
|
|
1962 } else if (type->isObject() && meta_type->actualIndirections() == 0) {
|
|
1963
|
|
1964 ReportHandler::warning(QString("Object type '%1' passed as value. Resulting code will not compile.")
|
|
1965 .arg(meta_type->cppSignature()));
|
|
1966 meta_type->setTypeUsagePattern(AbstractMetaType::NativePointerPattern);
|
|
1967
|
|
1968 } else {
|
|
1969 meta_type->setTypeUsagePattern(AbstractMetaType::NativePointerPattern);
|
|
1970 ReportHandler::debugFull(QString("native pointer pattern for '%1'")
|
|
1971 .arg(meta_type->cppSignature()));
|
|
1972 }
|
|
1973 }
|
|
1974
|
|
1975 QString AbstractMetaBuilder::translateDefaultValue(ArgumentModelItem item, AbstractMetaType *type,
|
|
1976 AbstractMetaFunction *fnc, AbstractMetaClass *implementing_class,
|
|
1977 int argument_index)
|
|
1978 {
|
|
1979 QString function_name = fnc->name();
|
|
1980 QString class_name = implementing_class->name();
|
|
1981
|
|
1982 QString replaced_expression = fnc->replacedDefaultExpression(implementing_class, argument_index + 1);
|
|
1983 if (fnc->removedDefaultExpression(implementing_class, argument_index +1))
|
|
1984 return "";
|
|
1985 if (!replaced_expression.isEmpty())
|
|
1986 return replaced_expression;
|
|
1987
|
|
1988 QString expr = item->defaultValueExpression();
|
|
1989 if (type->isPrimitive()) {
|
|
1990 if (type->name() == "boolean") {
|
|
1991 if (expr == "false" || expr=="true") {
|
|
1992 return expr;
|
|
1993 } else {
|
|
1994 bool ok = false;
|
|
1995 int number = expr.toInt(&ok);
|
|
1996 if (ok && number)
|
|
1997 return "true";
|
|
1998 else
|
|
1999 return "false";
|
|
2000 }
|
|
2001 } else if (expr == "ULONG_MAX") {
|
|
2002 return "Long.MAX_VALUE";
|
|
2003 } else if (expr == "QVariant::Invalid") {
|
|
2004 return QString::number(QVariant::Invalid);
|
|
2005 } else {
|
|
2006 // This can be an enum or flag so I need to delay the
|
|
2007 // translation untill all namespaces are completly
|
|
2008 // processed. This is done in figureOutEnumValues()
|
|
2009 return expr;
|
|
2010 }
|
|
2011 } else if (type != 0 && (type->isFlags() || type->isEnum())) {
|
|
2012 // Same as with enum explanation above...
|
|
2013 return expr;
|
|
2014
|
|
2015 } else {
|
|
2016
|
|
2017 // constructor or functioncall can be a bit tricky...
|
|
2018 if (expr == "QVariant()" /* qtd || expr == "QModelIndex()"*/) {
|
|
2019 return "null";
|
|
2020 } else if (expr == "QString()") {
|
|
2021 return "null";
|
|
2022 } else if (expr.endsWith(")") && expr.contains("::")) {
|
|
2023 TypeEntry *typeEntry = TypeDatabase::instance()->findType(expr.left(expr.indexOf("::")));
|
|
2024 if (typeEntry)
|
|
2025 return typeEntry->qualifiedTargetLangName() + "." + expr.right(expr.length() - expr.indexOf("::") - 2);
|
|
2026 } else if (expr.endsWith(")") && type->isValue()) {
|
|
2027 int pos = expr.indexOf("(");
|
|
2028
|
|
2029 TypeEntry *typeEntry = TypeDatabase::instance()->findType(expr.left(pos));
|
|
2030 if (typeEntry) { // qtd
|
|
2031 if(typeEntry->isStructInD())
|
|
2032 return typeEntry->qualifiedCppName() + expr.right(expr.length() - pos);
|
|
2033 else
|
|
2034 return "new " + typeEntry->qualifiedCppName() + expr.right(expr.length() - pos);
|
|
2035 } else
|
|
2036 return expr;
|
|
2037 } else if (expr == "0") {
|
|
2038 return "null";
|
|
2039 } else if (type->isObject() || type->isValue() || expr.contains("::")) { // like Qt::black passed to a QColor
|
|
2040 TypeEntry *typeEntry = TypeDatabase::instance()->findType(expr.left(expr.indexOf("::")));
|
|
2041
|
|
2042 expr = expr.right(expr.length() - expr.indexOf("::") - 2);
|
|
2043 if (typeEntry) {
|
|
2044 return "new " + type->typeEntry()->qualifiedCppName() +
|
|
2045 "(" + typeEntry->qualifiedCppName() + "." + expr + ")";
|
|
2046 }
|
|
2047 }
|
|
2048 }
|
|
2049
|
|
2050 QString warn = QString("unsupported default value '%3' of argument in function '%1', class '%2'")
|
|
2051 .arg(function_name).arg(class_name).arg(item->defaultValueExpression());
|
|
2052 ReportHandler::warning(warn);
|
|
2053
|
|
2054 return QString();
|
|
2055 }
|
|
2056
|
|
2057
|
|
2058 bool AbstractMetaBuilder::isQObject(const QString &qualified_name)
|
|
2059 {
|
|
2060 if (qualified_name == "QObject")
|
|
2061 return true;
|
|
2062
|
|
2063 ClassModelItem class_item = m_dom->findClass(qualified_name);
|
|
2064
|
|
2065 if (!class_item) {
|
|
2066 QStringList names = qualified_name.split(QLatin1String("::"));
|
|
2067 NamespaceModelItem ns = model_dynamic_cast<NamespaceModelItem>(m_dom);
|
|
2068 for (int i=0; i<names.size() - 1 && ns; ++i)
|
|
2069 ns = ns->namespaceMap().value(names.at(i));
|
|
2070 if (ns && names.size() >= 2)
|
|
2071 class_item = ns->findClass(names.at(names.size() - 1));
|
|
2072 }
|
|
2073
|
|
2074 bool isqobject = class_item && class_item->extendsClass("QObject");
|
|
2075
|
|
2076 if (class_item && !isqobject) {
|
|
2077 QStringList baseClasses = class_item->baseClasses();
|
|
2078 for (int i=0; i<baseClasses.count(); ++i) {
|
|
2079
|
|
2080 isqobject = isQObject(baseClasses.at(i));
|
|
2081 if (isqobject)
|
|
2082 break;
|
|
2083 }
|
|
2084 }
|
|
2085
|
|
2086 return isqobject;
|
|
2087 }
|
|
2088
|
|
2089
|
|
2090 bool AbstractMetaBuilder::isEnum(const QStringList &qualified_name)
|
|
2091 {
|
|
2092 CodeModelItem item = m_dom->model()->findItem(qualified_name, m_dom->toItem());
|
|
2093 return item && item->kind() == _EnumModelItem::__node_kind;
|
|
2094 }
|
|
2095
|
|
2096 AbstractMetaType *AbstractMetaBuilder::inheritTemplateType(const QList<AbstractMetaType *> &template_types,
|
|
2097 AbstractMetaType *meta_type, bool *ok)
|
|
2098 {
|
|
2099 if (ok != 0)
|
|
2100 *ok = true;
|
|
2101 if (!meta_type || (!meta_type->typeEntry()->isTemplateArgument() && !meta_type->hasInstantiations()))
|
|
2102 return meta_type ? meta_type->copy() : 0;
|
|
2103
|
|
2104 AbstractMetaType *returned = meta_type->copy();
|
|
2105 returned->setOriginalTemplateType(meta_type->copy());
|
|
2106
|
|
2107 if (returned->typeEntry()->isTemplateArgument()) {
|
|
2108 const TemplateArgumentEntry *tae = static_cast<const TemplateArgumentEntry *>(returned->typeEntry());
|
|
2109
|
|
2110 // If the template is intantiated with void we special case this as rejecting the functions that use this
|
|
2111 // parameter from the instantiation.
|
|
2112 if (template_types.size() <= tae->ordinal() || template_types.at(tae->ordinal())->typeEntry()->name() == "void") {
|
|
2113 if (ok != 0)
|
|
2114 *ok = false;
|
|
2115 return 0;
|
|
2116 }
|
|
2117
|
|
2118 AbstractMetaType *t = returned->copy();
|
|
2119 t->setTypeEntry(template_types.at(tae->ordinal())->typeEntry());
|
|
2120 t->setIndirections(template_types.at(tae->ordinal())->indirections() + t->indirections()
|
|
2121 ? 1
|
|
2122 : 0);
|
|
2123 decideUsagePattern(t);
|
|
2124
|
|
2125 delete returned;
|
|
2126 returned = inheritTemplateType(template_types, t, ok);
|
|
2127 if (ok != 0 && !(*ok))
|
|
2128 return 0;
|
|
2129 }
|
|
2130
|
|
2131 if (returned->hasInstantiations()) {
|
|
2132 QList<AbstractMetaType *> instantiations = returned->instantiations();
|
|
2133 for (int i=0; i<instantiations.count(); ++i) {
|
|
2134 instantiations[i] = inheritTemplateType(template_types, instantiations.at(i), ok);
|
|
2135 if (ok != 0 && !(*ok))
|
|
2136 return 0;
|
|
2137 }
|
|
2138 returned->setInstantiations(instantiations);
|
|
2139 }
|
|
2140
|
|
2141 return returned;
|
|
2142 }
|
|
2143
|
|
2144 bool AbstractMetaBuilder::inheritTemplate(AbstractMetaClass *subclass,
|
|
2145 const AbstractMetaClass *template_class,
|
|
2146 const TypeParser::Info &info)
|
|
2147 {
|
|
2148 QList<TypeParser::Info> targs = info.template_instantiations;
|
|
2149
|
|
2150 QList<AbstractMetaType *> template_types;
|
|
2151 foreach (const TypeParser::Info &i, targs) {
|
|
2152 TypeEntry *t = TypeDatabase::instance()->findType(i.qualified_name.join("::"));
|
|
2153
|
|
2154 if (t != 0) {
|
|
2155 AbstractMetaType *temporary_type = createMetaType();
|
|
2156 temporary_type->setTypeEntry(t);
|
|
2157 temporary_type->setConstant(i.is_constant);
|
|
2158 temporary_type->setReference(i.is_reference);
|
|
2159 temporary_type->setIndirections(i.indirections);
|
|
2160 template_types << temporary_type;
|
|
2161 }
|
|
2162 }
|
|
2163
|
|
2164 AbstractMetaFunctionList funcs = subclass->functions();
|
|
2165 foreach (const AbstractMetaFunction *function, template_class->functions()) {
|
|
2166
|
|
2167 if (function->isModifiedRemoved(TypeSystem::All))
|
|
2168 continue;
|
|
2169
|
|
2170 AbstractMetaFunction *f = function->copy();
|
|
2171 f->setArguments(AbstractMetaArgumentList());
|
|
2172
|
|
2173 bool ok = true;
|
|
2174 AbstractMetaType *ftype = function->type();
|
|
2175 f->setType(inheritTemplateType(template_types, ftype, &ok));
|
|
2176 if (!ok) {
|
|
2177 delete f;
|
|
2178 continue;
|
|
2179 }
|
|
2180
|
|
2181 foreach (AbstractMetaArgument *argument, function->arguments()) {
|
|
2182 AbstractMetaType *atype = argument->type();
|
|
2183
|
|
2184 AbstractMetaArgument *arg = argument->copy();
|
|
2185 arg->setType(inheritTemplateType(template_types, atype, &ok));
|
|
2186 if (!ok)
|
|
2187 break;
|
|
2188 f->addArgument(arg);
|
|
2189 }
|
|
2190
|
|
2191 if (!ok) {
|
|
2192 delete f;
|
|
2193 continue ;
|
|
2194 }
|
|
2195
|
|
2196 // There is no base class in java to inherit from here, so the
|
|
2197 // template instantiation is the class that implements the function..
|
|
2198 f->setImplementingClass(subclass);
|
|
2199
|
|
2200 // We also set it as the declaring class, since the superclass is
|
|
2201 // supposed to disappear. This allows us to make certain function modifications
|
|
2202 // on the inherited functions.
|
|
2203 f->setDeclaringClass(subclass);
|
|
2204
|
|
2205
|
|
2206 if (f->isConstructor() && subclass->isTypeAlias()) {
|
|
2207 f->setName(subclass->name());
|
|
2208 } else if (f->isConstructor()) {
|
|
2209 delete f;
|
|
2210 continue;
|
|
2211 }
|
|
2212
|
|
2213 // if the instantiation has a function named the same as an existing
|
|
2214 // function we have shadowing so we need to skip it.
|
|
2215 bool found = false;
|
|
2216 for (int i=0; i<funcs.size(); ++i) {
|
|
2217 if (funcs.at(i)->name() == f->name()) {
|
|
2218 found = true;
|
|
2219 continue;
|
|
2220 }
|
|
2221 }
|
|
2222 if (found) {
|
|
2223 delete f;
|
|
2224 continue;
|
|
2225 }
|
|
2226
|
|
2227 ComplexTypeEntry *te = subclass->typeEntry();
|
|
2228 FunctionModificationList mods = function->modifications(template_class);
|
|
2229 for (int i=0; i<mods.size(); ++i) {
|
|
2230 FunctionModification mod = mods.at(i);
|
|
2231 mod.signature = f->minimalSignature();
|
|
2232
|
|
2233 // If we ever need it... Below is the code to do
|
|
2234 // substitution of the template instantation type inside
|
|
2235 // injected code..
|
|
2236 #if 0
|
|
2237 if (mod.modifiers & Modification::CodeInjection) {
|
|
2238 for (int j=0; j<template_types.size(); ++j) {
|
|
2239 CodeSnip &snip = mod.snips.last();
|
|
2240 QString code = snip.code();
|
|
2241 code.replace(QString::fromLatin1("$$QT_TEMPLATE_%1$$").arg(j),
|
|
2242 template_types.at(j)->typeEntry()->qualifiedCppName());
|
|
2243 snip.codeList.clear();
|
|
2244 snip.addCode(code);
|
|
2245 }
|
|
2246 }
|
|
2247 #endif
|
|
2248 te->addFunctionModification(mod);
|
|
2249 }
|
|
2250
|
|
2251 subclass->addFunction(f);
|
|
2252 }
|
|
2253
|
|
2254 // Clean up
|
|
2255 foreach (AbstractMetaType *type, template_types) {
|
|
2256 delete type;
|
|
2257 }
|
|
2258
|
|
2259
|
|
2260 {
|
|
2261 subclass->setTemplateBaseClass(template_class);
|
|
2262
|
|
2263 subclass->setInterfaces(template_class->interfaces());
|
|
2264 subclass->setBaseClass(template_class->baseClass());
|
|
2265 }
|
|
2266
|
|
2267 return true;
|
|
2268 }
|
|
2269
|
|
2270 void AbstractMetaBuilder::parseQ_Property(AbstractMetaClass *meta_class, const QStringList &declarations)
|
|
2271 {
|
|
2272 for (int i=0; i<declarations.size(); ++i) {
|
|
2273 QString p = declarations.at(i);
|
|
2274
|
|
2275 QStringList l = p.split(QLatin1String(" "));
|
|
2276
|
|
2277
|
|
2278 QStringList qualifiedScopeName = currentScope()->qualifiedName();
|
|
2279 bool ok = false;
|
|
2280 AbstractMetaType *type = 0;
|
|
2281 QString scope;
|
|
2282 for (int j=qualifiedScopeName.size(); j>=0; --j) {
|
|
2283 scope = j > 0 ? QStringList(qualifiedScopeName.mid(0, j)).join("::") + "::" : QString();
|
|
2284 TypeInfo info;
|
|
2285 info.setQualifiedName((scope + l.at(0)).split("::"));
|
|
2286
|
|
2287 type = translateType(info, &ok);
|
|
2288 if (type != 0 && ok) {
|
|
2289 break;
|
|
2290 }
|
|
2291 }
|
|
2292
|
|
2293 if (type == 0 || !ok) {
|
|
2294 ReportHandler::warning(QString("Unable to decide type of property: '%1' in class '%2'")
|
|
2295 .arg(l.at(0)).arg(meta_class->name()));
|
|
2296 continue;
|
|
2297 }
|
|
2298
|
|
2299 QString typeName = scope + l.at(0);
|
|
2300
|
|
2301 QPropertySpec *spec = new QPropertySpec(type->typeEntry());
|
|
2302 spec->setName(l.at(1));
|
|
2303 spec->setIndex(i);
|
|
2304
|
|
2305 for (int pos=2; pos+1<l.size(); pos+=2) {
|
|
2306 if (l.at(pos) == QLatin1String("READ"))
|
|
2307 spec->setRead(l.at(pos+1));
|
|
2308 else if (l.at(pos) == QLatin1String("WRITE"))
|
|
2309 spec->setWrite(l.at(pos+1));
|
|
2310 else if (l.at(pos) == QLatin1String("DESIGNABLE"))
|
|
2311 spec->setDesignable(l.at(pos+1));
|
|
2312 else if (l.at(pos) == QLatin1String("RESET"))
|
|
2313 spec->setReset(l.at(pos+1));
|
|
2314 }
|
|
2315
|
|
2316 meta_class->addPropertySpec(spec);
|
|
2317 delete type;
|
|
2318 }
|
|
2319 }
|
|
2320
|
|
2321 static void hide_functions(const AbstractMetaFunctionList &l) {
|
|
2322 foreach (AbstractMetaFunction *f, l) {
|
|
2323 FunctionModification mod;
|
|
2324 mod.signature = f->minimalSignature();
|
|
2325 mod.modifiers = FunctionModification::Private;
|
|
2326 ((ComplexTypeEntry *) f->implementingClass()->typeEntry())->addFunctionModification(mod);
|
|
2327 }
|
|
2328 }
|
|
2329
|
|
2330 static void remove_function(AbstractMetaFunction *f) {
|
|
2331 FunctionModification mod;
|
|
2332 mod.removal = TypeSystem::All;
|
|
2333 mod.signature = f->minimalSignature();
|
|
2334 ((ComplexTypeEntry *) f->implementingClass()->typeEntry())->addFunctionModification(mod);
|
|
2335 }
|
|
2336
|
|
2337 static AbstractMetaFunctionList filter_functions(const AbstractMetaFunctionList &lst, QSet<QString> *signatures)
|
|
2338 {
|
|
2339 AbstractMetaFunctionList functions;
|
|
2340 foreach (AbstractMetaFunction *f, lst) {
|
|
2341 QString signature = f->minimalSignature();
|
|
2342 int start = signature.indexOf(QLatin1Char('(')) + 1;
|
|
2343 int end = signature.lastIndexOf(QLatin1Char(')'));
|
|
2344 signature = signature.mid(start, end - start);
|
|
2345 if (signatures->contains(signature)) {
|
|
2346 remove_function(f);
|
|
2347 continue;
|
|
2348 }
|
|
2349 (*signatures) << signature;
|
|
2350 functions << f;
|
|
2351 }
|
|
2352 return functions;
|
|
2353 }
|
|
2354
|
|
2355 void AbstractMetaBuilder::setupEquals(AbstractMetaClass *cls)
|
|
2356 {
|
|
2357 AbstractMetaFunctionList equals;
|
|
2358 AbstractMetaFunctionList nequals;
|
|
2359
|
|
2360 QString op_equals = QLatin1String("operator_equal");
|
|
2361 QString op_nequals = QLatin1String("operator_not_equal");
|
|
2362
|
|
2363 AbstractMetaFunctionList functions = cls->queryFunctions(AbstractMetaClass::ClassImplements
|
|
2364 | AbstractMetaClass::NotRemovedFromTargetLang);
|
|
2365 foreach (AbstractMetaFunction *f, functions) {
|
|
2366 if (f->name() == op_equals)
|
|
2367 equals << f;
|
|
2368 else if (f->name() == op_nequals)
|
|
2369 nequals << f;
|
|
2370 }
|
|
2371
|
|
2372 if (equals.size() || nequals.size()) {
|
|
2373 if (!cls->hasHashFunction()) {
|
|
2374 ReportHandler::warning(QString::fromLatin1("Class '%1' has equals operators but no qHash() function")
|
|
2375 .arg(cls->name()));
|
|
2376 }
|
|
2377
|
|
2378 hide_functions(equals);
|
|
2379 hide_functions(nequals);
|
|
2380
|
|
2381 // We only need == if we have both == and !=, and one == for
|
|
2382 // each signature type, like QDateTime::==(QDate) and (QTime)
|
|
2383 // if such a thing exists...
|
|
2384 QSet<QString> func_signatures;
|
|
2385 cls->setEqualsFunctions(filter_functions(equals, &func_signatures));
|
|
2386 cls->setNotEqualsFunctions(filter_functions(nequals, &func_signatures));
|
|
2387 }
|
|
2388 }
|
|
2389
|
|
2390 void AbstractMetaBuilder::setupComparable(AbstractMetaClass *cls)
|
|
2391 {
|
|
2392 AbstractMetaFunctionList greater;
|
|
2393 AbstractMetaFunctionList greaterEquals;
|
|
2394 AbstractMetaFunctionList less;
|
|
2395 AbstractMetaFunctionList lessEquals;
|
|
2396
|
|
2397 QString op_greater = QLatin1String("operator_greater");
|
|
2398 QString op_greater_eq = QLatin1String("operator_greater_or_equal");
|
|
2399 QString op_less = QLatin1String("operator_less");
|
|
2400 QString op_less_eq = QLatin1String("operator_less_or_equal");
|
|
2401
|
|
2402 AbstractMetaFunctionList functions = cls->queryFunctions(AbstractMetaClass::ClassImplements
|
|
2403 | AbstractMetaClass::NotRemovedFromTargetLang);
|
|
2404 foreach (AbstractMetaFunction *f, functions) {
|
|
2405 if (f->name() == op_greater)
|
|
2406 greater << f;
|
|
2407 else if (f->name() == op_greater_eq)
|
|
2408 greaterEquals << f;
|
|
2409 else if (f->name() == op_less)
|
|
2410 less << f;
|
|
2411 else if (f->name() == op_less_eq)
|
|
2412 lessEquals << f;
|
|
2413 }
|
|
2414
|
|
2415 bool hasEquals = cls->equalsFunctions().size() || cls->notEqualsFunctions().size();
|
|
2416
|
|
2417 // Conditions for comparable is:
|
|
2418 // >, ==, < - The basic case
|
|
2419 // >, == - Less than becomes else case
|
|
2420 // <, == - Greater than becomes else case
|
|
2421 // >=, <= - if (<= && >=) -> equal
|
|
2422 bool mightBeComparable = greater.size() || greaterEquals.size() || less.size() || lessEquals.size()
|
|
2423 || greaterEquals.size() == 1 || lessEquals.size() == 1;
|
|
2424
|
|
2425 if (mightBeComparable) {
|
|
2426 QSet<QString> signatures;
|
|
2427
|
|
2428 // We only hide the original functions if we are able to make a compareTo() method
|
|
2429 bool wasComparable = false;
|
|
2430
|
|
2431 // The three upper cases, prefer the <, == approach
|
|
2432 if (hasEquals && (greater.size() || less.size())) {
|
|
2433 cls->setLessThanFunctions(filter_functions(less, &signatures));
|
|
2434 cls->setGreaterThanFunctions(filter_functions(greater, &signatures));
|
|
2435 filter_functions(greaterEquals, &signatures);
|
|
2436 filter_functions(lessEquals, &signatures);
|
|
2437 wasComparable = true;
|
|
2438 } else if (hasEquals && (greaterEquals.size() || lessEquals.size())) {
|
|
2439 cls->setLessThanEqFunctions(filter_functions(lessEquals, &signatures));
|
|
2440 cls->setGreaterThanEqFunctions(filter_functions(greaterEquals, &signatures));
|
|
2441 wasComparable = true;
|
|
2442 } else if (greaterEquals.size() == 1 || lessEquals.size() == 1) {
|
|
2443 cls->setGreaterThanEqFunctions(greaterEquals);
|
|
2444 cls->setLessThanEqFunctions(lessEquals);
|
|
2445 filter_functions(less, &signatures);
|
|
2446 filter_functions(greater, &signatures);
|
|
2447 wasComparable = true;
|
|
2448 }
|
|
2449
|
|
2450 if (wasComparable) {
|
|
2451 hide_functions(greater);
|
|
2452 hide_functions(greaterEquals);
|
|
2453 hide_functions(less);
|
|
2454 hide_functions(lessEquals);
|
|
2455 }
|
|
2456 }
|
|
2457
|
|
2458 }
|
|
2459
|
|
2460 void AbstractMetaBuilder::setupClonable(AbstractMetaClass *cls)
|
|
2461 {
|
|
2462 // All value types are required to have a copy constructor,
|
|
2463 // or they will not work as value types (it won't even compile,
|
|
2464 // because of calls to qRegisterMetaType(). Thus all value types
|
|
2465 // should be cloneable.
|
|
2466 if (cls->typeEntry()->isValue()) {
|
|
2467 cls->setHasCloneOperator(true);
|
|
2468 return;
|
|
2469 } else {
|
|
2470 QString op_assign = QLatin1String("operator_assign");
|
|
2471
|
|
2472 AbstractMetaFunctionList functions = cls->queryFunctions(AbstractMetaClass::ClassImplements);
|
|
2473 foreach (AbstractMetaFunction *f, functions) {
|
|
2474 if ((f->name() == op_assign || f->isConstructor()) && f->isPublic()) {
|
|
2475 AbstractMetaArgumentList arguments = f->arguments();
|
|
2476 if (f->actualMinimumArgumentCount() == 1) {
|
|
2477 if (cls->typeEntry()->qualifiedCppName() == arguments.at(0)->type()->typeEntry()->qualifiedCppName()) {
|
|
2478 if (cls->typeEntry()->isValue()) {
|
|
2479 cls->setHasCloneOperator(true);
|
|
2480 return;
|
|
2481 }
|
|
2482 }
|
|
2483 }
|
|
2484 }
|
|
2485 }
|
|
2486 }
|
|
2487 }
|
|
2488
|
|
2489 static void write_reject_log_file(const QString &name,
|
|
2490 const QMap<QString, AbstractMetaBuilder::RejectReason> &rejects)
|
|
2491 {
|
|
2492 QFile f(name);
|
|
2493 if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
2494 ReportHandler::warning(QString("failed to write log file: '%1'")
|
|
2495 .arg(f.fileName()));
|
|
2496 return;
|
|
2497 }
|
|
2498
|
|
2499 QTextStream s(&f);
|
|
2500
|
|
2501
|
|
2502 for (int reason=0; reason<AbstractMetaBuilder::NoReason; ++reason) {
|
|
2503 s << QString(72, '*') << endl;
|
|
2504 switch (reason) {
|
|
2505 case AbstractMetaBuilder::NotInTypeSystem:
|
|
2506 s << "Not in type system";
|
|
2507 break;
|
|
2508 case AbstractMetaBuilder::GenerationDisabled:
|
|
2509 s << "Generation disabled by type system";
|
|
2510 break;
|
|
2511 case AbstractMetaBuilder::RedefinedToNotClass:
|
|
2512 s << "Type redefined to not be a class";
|
|
2513 break;
|
|
2514
|
|
2515 case AbstractMetaBuilder::UnmatchedReturnType:
|
|
2516 s << "Unmatched return type";
|
|
2517 break;
|
|
2518
|
|
2519 case AbstractMetaBuilder::UnmatchedArgumentType:
|
|
2520 s << "Unmatched argument type";
|
|
2521 break;
|
|
2522
|
|
2523 default:
|
|
2524 s << "unknown reason";
|
|
2525 break;
|
|
2526 }
|
|
2527
|
|
2528 s << endl;
|
|
2529
|
|
2530 for (QMap<QString, AbstractMetaBuilder::RejectReason>::const_iterator it = rejects.constBegin();
|
|
2531 it != rejects.constEnd(); ++it) {
|
|
2532 if (it.value() != reason)
|
|
2533 continue;
|
|
2534 s << " - " << it.key() << endl;
|
|
2535 }
|
|
2536
|
|
2537 s << QString(72, '*') << endl << endl;
|
|
2538 }
|
|
2539
|
|
2540 }
|
|
2541
|
|
2542
|
|
2543 void AbstractMetaBuilder::dumpLog()
|
|
2544 {
|
|
2545 write_reject_log_file("mjb_rejected_classes.log", m_rejected_classes);
|
|
2546 write_reject_log_file("mjb_rejected_enums.log", m_rejected_enums);
|
|
2547 write_reject_log_file("mjb_rejected_functions.log", m_rejected_functions);
|
|
2548 write_reject_log_file("mjb_rejected_fields.log", m_rejected_fields);
|
|
2549 }
|
|
2550
|
|
2551 AbstractMetaClassList AbstractMetaBuilder::classesTopologicalSorted() const
|
|
2552 {
|
|
2553 AbstractMetaClassList res;
|
|
2554
|
|
2555 AbstractMetaClassList classes = m_meta_classes;
|
|
2556 qSort(classes);
|
|
2557
|
|
2558 QSet<AbstractMetaClass*> noDependency;
|
|
2559 QHash<AbstractMetaClass*, QSet<AbstractMetaClass* >* > hash;
|
|
2560 foreach (AbstractMetaClass *cls, classes) {
|
|
2561 QSet<AbstractMetaClass* > *depends = new QSet<AbstractMetaClass* >();
|
|
2562
|
|
2563 if (cls->baseClass())
|
|
2564 depends->insert(cls->baseClass());
|
|
2565
|
|
2566 foreach (AbstractMetaClass *interface, cls->interfaces()) {
|
|
2567 depends->insert(interface);
|
|
2568 }
|
|
2569
|
|
2570 if (depends->empty()) {
|
|
2571 noDependency.insert(cls);
|
|
2572 } else {
|
|
2573 hash.insert(cls, depends);
|
|
2574 }
|
|
2575 }
|
|
2576
|
|
2577 while (!noDependency.empty()) {
|
|
2578 foreach (AbstractMetaClass *cls, noDependency.values()) {
|
|
2579 if(!cls->isInterface())
|
|
2580 res.append(cls);
|
|
2581 noDependency.remove(cls);
|
|
2582 QHashIterator<AbstractMetaClass*, QSet<AbstractMetaClass* >* > i(hash);
|
|
2583 while (i.hasNext()) {
|
|
2584 i.next();
|
|
2585 i.value()->remove(cls);
|
|
2586 if (i.value()->empty()) {
|
|
2587 AbstractMetaClass *key = i.key();
|
|
2588 noDependency.insert(key);
|
|
2589 hash.remove(key);
|
|
2590 delete(i.value());
|
|
2591 }
|
|
2592 }
|
|
2593 }
|
|
2594 }
|
|
2595
|
|
2596 if (!noDependency.empty() || !hash.empty()) {
|
|
2597 qWarning("dependency graph was cyclic.");
|
|
2598 }
|
|
2599
|
|
2600 return res;
|
|
2601 }
|