1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of Qt for Python.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28
29 #include "testmodifyfunction.h"
30 #include <QtTest/QTest>
31 #include "testutil.h"
32 #include <abstractmetalang.h>
33 #include <typesystem.h>
34
testRenameArgument_data()35 void TestModifyFunction::testRenameArgument_data()
36 {
37 QTest::addColumn<QByteArray>("pattern");
38 QTest::newRow("fixed_string") << QByteArrayLiteral("method(int)");
39 QTest::newRow("regular_expression") << QByteArrayLiteral("^method.*");
40 }
41
testRenameArgument()42 void TestModifyFunction::testRenameArgument()
43 {
44 QFETCH(QByteArray, pattern);
45
46 const char* cppCode ="\
47 struct A {\n\
48 void method(int=0);\n\
49 };\n";
50 const char xmlCode1[] = "\
51 <typesystem package='Foo'>\n\
52 <primitive-type name='int'/>\n\
53 <object-type name='A'>\n\
54 <modify-function signature='";
55 const char xmlCode2[] = "'>\n\
56 <modify-argument index='1'>\n\
57 <rename to='otherArg'/>\n\
58 </modify-argument>\n\
59 </modify-function>\n\
60 </object-type>\n\
61 </typesystem>\n";
62
63 const QByteArray xmlCode = QByteArray(xmlCode1) + pattern + QByteArray(xmlCode2);
64 QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode.constData(), false));
65 QVERIFY(!builder.isNull());
66 AbstractMetaClassList classes = builder->classes();
67 const AbstractMetaClass *classA = AbstractMetaClass::findClass(classes, QLatin1String("A"));
68 const AbstractMetaFunction* func = classA->findFunction(QLatin1String("method"));
69 Q_ASSERT(func);
70
71 QCOMPARE(func->argumentName(1), QLatin1String("otherArg"));
72 }
73
testOwnershipTransfer()74 void TestModifyFunction::testOwnershipTransfer()
75 {
76 const char* cppCode ="\
77 struct A {};\n\
78 struct B {\n\
79 virtual A* method();\n\
80 };\n";
81 const char* xmlCode = "\
82 <typesystem package=\"Foo\">\n\
83 <object-type name='A' />\n\
84 <object-type name='B'>\n\
85 <modify-function signature='method()'>\n\
86 <modify-argument index='return'>\n\
87 <define-ownership owner='c++'/>\n\
88 </modify-argument>\n\
89 </modify-function>\n\
90 </object-type>\n\
91 </typesystem>\n";
92 QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
93 QVERIFY(!builder.isNull());
94 AbstractMetaClassList classes = builder->classes();
95 const AbstractMetaClass *classB = AbstractMetaClass::findClass(classes, QLatin1String("B"));
96 const AbstractMetaFunction* func = classB->findFunction(QLatin1String("method"));
97
98 QCOMPARE(func->ownership(func->ownerClass(), TypeSystem::TargetLangCode, 0), TypeSystem::CppOwnership);
99 }
100
101
invalidateAfterUse()102 void TestModifyFunction::invalidateAfterUse()
103 {
104 const char* cppCode ="\
105 struct A {\n\
106 virtual void call(int *a);\n\
107 };\n\
108 struct B : A {\n\
109 };\n\
110 struct C : B {\n\
111 virtual void call2(int *a);\n\
112 };\n\
113 struct D : C {\n\
114 virtual void call2(int *a);\n\
115 };\n\
116 struct E : D {\n\
117 };\n";
118 const char* xmlCode = "\
119 <typesystem package='Foo'>\n\
120 <primitive-type name='int'/>\n\
121 <object-type name='A'>\n\
122 <modify-function signature='call(int*)'>\n\
123 <modify-argument index='1' invalidate-after-use='true'/>\n\
124 </modify-function>\n\
125 </object-type>\n\
126 <object-type name='B' />\n\
127 <object-type name='C'>\n\
128 <modify-function signature='call2(int*)'>\n\
129 <modify-argument index='1' invalidate-after-use='true'/>\n\
130 </modify-function>\n\
131 </object-type>\n\
132 <object-type name='D'>\n\
133 <modify-function signature='call2(int*)'>\n\
134 <modify-argument index='1' invalidate-after-use='true'/>\n\
135 </modify-function>\n\
136 </object-type>\n\
137 <object-type name='E' />\n\
138 </typesystem>\n";
139 QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode,
140 false, QLatin1String("0.1")));
141 QVERIFY(!builder.isNull());
142 AbstractMetaClassList classes = builder->classes();
143 const AbstractMetaClass *classB = AbstractMetaClass::findClass(classes, QLatin1String("B"));
144 const AbstractMetaFunction* func = classB->findFunction(QLatin1String("call"));
145 QCOMPARE(func->modifications().size(), 1);
146 QCOMPARE(func->modifications().at(0).argument_mods.size(), 1);
147 QVERIFY(func->modifications().at(0).argument_mods.at(0).resetAfterUse);
148
149 const AbstractMetaClass *classC = AbstractMetaClass::findClass(classes, QLatin1String("C"));
150 QVERIFY(classC);
151 func = classC->findFunction(QLatin1String("call"));
152 QCOMPARE(func->modifications().size(), 1);
153 QCOMPARE(func->modifications().at(0).argument_mods.size(), 1);
154 QVERIFY(func->modifications().at(0).argument_mods.at(0).resetAfterUse);
155
156 func = classC->findFunction(QLatin1String("call2"));
157 QCOMPARE(func->modifications().size(), 1);
158 QCOMPARE(func->modifications().at(0).argument_mods.size(), 1);
159 QVERIFY(func->modifications().at(0).argument_mods.at(0).resetAfterUse);
160
161 const AbstractMetaClass *classD = AbstractMetaClass::findClass(classes, QLatin1String("D"));
162 QVERIFY(classD);
163 func = classD->findFunction(QLatin1String("call"));
164 QCOMPARE(func->modifications().size(), 1);
165 QCOMPARE(func->modifications().at(0).argument_mods.size(), 1);
166 QVERIFY(func->modifications().at(0).argument_mods.at(0).resetAfterUse);
167
168 func = classD->findFunction(QLatin1String("call2"));
169 QCOMPARE(func->modifications().size(), 1);
170 QCOMPARE(func->modifications().at(0).argument_mods.size(), 1);
171 QVERIFY(func->modifications().at(0).argument_mods.at(0).resetAfterUse);
172
173 const AbstractMetaClass *classE = AbstractMetaClass::findClass(classes, QLatin1String("E"));
174 QVERIFY(classE);
175 func = classE->findFunction(QLatin1String("call"));
176 QVERIFY(func);
177 QCOMPARE(func->modifications().size(), 1);
178 QCOMPARE(func->modifications().at(0).argument_mods.size(), 1);
179 QVERIFY(func->modifications().at(0).argument_mods.at(0).resetAfterUse);
180
181 func = classE->findFunction(QLatin1String("call2"));
182 QVERIFY(func);
183 QCOMPARE(func->modifications().size(), 1);
184 QCOMPARE(func->modifications().at(0).argument_mods.size(), 1);
185 QVERIFY(func->modifications().at(0).argument_mods.at(0).resetAfterUse);
186 }
187
testWithApiVersion()188 void TestModifyFunction::testWithApiVersion()
189 {
190 const char* cppCode ="\
191 struct A {};\n\
192 struct B {\n\
193 virtual A* method();\n\
194 virtual B* methodB();\n\
195 };\n";
196 const char* xmlCode = "\
197 <typesystem package='Foo'>\n\
198 <object-type name='A' />\n\
199 <object-type name='B'>\n\
200 <modify-function signature='method()' since='0.1'>\n\
201 <modify-argument index='return'>\n\
202 <define-ownership owner='c++'/>\n\
203 </modify-argument>\n\
204 </modify-function>\n\
205 <modify-function signature='methodB()' since='0.2'>\n\
206 <modify-argument index='return'>\n\
207 <define-ownership owner='c++'/>\n\
208 </modify-argument>\n\
209 </modify-function>\n\
210 </object-type>\n\
211 </typesystem>\n";
212 QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode,
213 false, QLatin1String("0.1")));
214 QVERIFY(!builder.isNull());
215 AbstractMetaClassList classes = builder->classes();
216 AbstractMetaClass* classB = AbstractMetaClass::findClass(classes, QLatin1String("B"));
217 const AbstractMetaFunction* func = classB->findFunction(QLatin1String("method"));
218
219 QCOMPARE(func->ownership(func->ownerClass(), TypeSystem::TargetLangCode, 0), TypeSystem::CppOwnership);
220
221 func = classB->findFunction(QLatin1String("methodB"));
222 QVERIFY(func->ownership(func->ownerClass(), TypeSystem::TargetLangCode, 0) != TypeSystem::CppOwnership);
223 }
224
225 // Modifications on class/typesystem level are tested below
226 // in testScopedModifications().
testAllowThread()227 void TestModifyFunction::testAllowThread()
228 {
229 const char cppCode[] =R"CPP(\
230 struct A {
231 void f1();
232 void f2();
233 void f3();
234 int getter1() const;
235 int getter2() const;
236 };
237 )CPP";
238
239 const char xmlCode[] = R"XML(
240 <typesystem package='Foo'>
241 <primitive-type name='int'/>
242 <object-type name='A'>
243 <modify-function signature='f2()' allow-thread='auto'/>
244 <modify-function signature='f3()' allow-thread='no'/>
245 <modify-function signature='getter2()const' allow-thread='yes'/>
246 </object-type>
247 </typesystem>
248 )XML";
249 QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode,
250 false, QLatin1String("0.1")));
251 QVERIFY(!builder.isNull());
252 AbstractMetaClassList classes = builder->classes();
253 const AbstractMetaClass *classA = AbstractMetaClass::findClass(classes, QLatin1String("A"));
254 QVERIFY(classA);
255
256 // Nothing specified, true
257 const AbstractMetaFunction *f1 = classA->findFunction(QLatin1String("f1"));
258 QVERIFY(f1);
259 QVERIFY(!f1->allowThread());
260
261 // 'auto' specified, should be false for nontrivial function
262 const AbstractMetaFunction *f2 = classA->findFunction(QLatin1String("f2"));
263 QVERIFY(f2);
264 QVERIFY(f2->allowThread());
265
266 // 'no' specified, should be false
267 const AbstractMetaFunction *f3 = classA->findFunction(QLatin1String("f3"));
268 QVERIFY(f3);
269 QVERIFY(!f3->allowThread());
270
271 // Nothing specified, should be false for simple getter
272 const AbstractMetaFunction *getter1 = classA->findFunction(QLatin1String("getter1"));
273 QVERIFY(getter1);
274 QVERIFY(!getter1->allowThread());
275
276 // Forced to true simple getter
277 const AbstractMetaFunction *getter2 = classA->findFunction(QLatin1String("getter2"));
278 QVERIFY(getter2);
279 QVERIFY(getter2->allowThread()); // Forced to true simple getter
280 }
281
testGlobalFunctionModification()282 void TestModifyFunction::testGlobalFunctionModification()
283 {
284 const char* cppCode ="\
285 struct A {};\n\
286 void function(A* a = 0);\n";
287 const char* xmlCode = "\
288 <typesystem package='Foo'>\n\
289 <primitive-type name='A'/>\n\
290 <function signature='function(A*)'>\n\
291 <modify-function signature='function(A*)'>\n\
292 <modify-argument index='1'>\n\
293 <replace-type modified-type='A'/>\n\
294 <replace-default-expression with='A()'/>\n\
295 </modify-argument>\n\
296 </modify-function>\n\
297 </function>\n\
298 </typesystem>\n";
299
300 QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
301 QVERIFY(!builder.isNull());
302 QCOMPARE(builder->globalFunctions().size(), 1);
303
304 FunctionModificationList mods = TypeDatabase::instance()->functionModifications(QLatin1String("function(A*)"));
305 QCOMPARE(mods.count(), 1);
306 QVector<ArgumentModification> argMods = mods.constFirst().argument_mods;
307 QCOMPARE(argMods.count(), 1);
308 ArgumentModification argMod = argMods.constFirst();
309 QCOMPARE(argMod.replacedDefaultExpression, QLatin1String("A()"));
310
311 const AbstractMetaFunction *func = builder->globalFunctions().constFirst();
312 QVERIFY(func);
313 QCOMPARE(func->arguments().count(), 1);
314 const AbstractMetaArgument *arg = func->arguments().constFirst();
315 QCOMPARE(arg->type()->cppSignature(), QLatin1String("A *"));
316 QCOMPARE(arg->originalDefaultValueExpression(), QLatin1String("0"));
317 QCOMPARE(arg->defaultValueExpression(), QLatin1String("A()"));
318 }
319
320 // Tests modifications of exception handling and allow-thread
321 // on various levels.
testScopedModifications_data()322 void TestModifyFunction::testScopedModifications_data()
323 {
324 QTest::addColumn<QByteArray>("cppCode");
325 QTest::addColumn<QByteArray>("xmlCode");
326 QTest::addColumn<bool>("expectedGenerateUnspecified");
327 QTest::addColumn<bool>("expectedGenerateNonThrowing");
328 QTest::addColumn<bool>("expectedGenerateThrowing");
329 QTest::addColumn<bool>("expectedAllowThread");
330
331 const QByteArray cppCode = R"CPP(
332 struct Base {
333 };
334
335 struct A : public Base {
336 void unspecified();
337 void nonThrowing() noexcept;
338 void throwing() throw(int);
339 };
340 )CPP";
341
342 // Default: Off
343 QTest::newRow("none")
344 << cppCode
345 << QByteArray(R"XML(
346 <typesystem package= 'Foo'>
347 <primitive-type name='int'/>
348 <object-type name='Base'/>
349 <object-type name='A'/>
350 </typesystem>)XML")
351 << false << false << false // exception
352 << false; // allowthread
353
354 // Modify one function
355 QTest::newRow("modify-function1")
356 << cppCode
357 << QByteArray(R"XML(
358 <typesystem package='Foo'>
359 <primitive-type name='int'/>
360 <object-type name='Base'/>
361 <object-type name='A'>
362 <modify-function signature='throwing()' exception-handling='auto-on'/>
363 </object-type>
364 </typesystem>)XML")
365 << false << false << true // exception
366 << false; // allowthread
367
368 // Flip defaults by modifying functions
369 QTest::newRow("modify-function2")
370 << cppCode
371 << QByteArray(R"XML(
372 <typesystem package='Foo'>
373 <primitive-type name='int'/>
374 <object-type name='Base'/>
375 <object-type name='A'>
376 <modify-function signature='unspecified()' exception-handling='auto-on'/>
377 <modify-function signature='throwing()' exception-handling='off'/>
378 </object-type>
379 </typesystem>)XML")
380 << true << false << false // exception
381 << false; // allowthread
382
383 // Activate on type system level
384 QTest::newRow("typesystem-on")
385 << cppCode
386 << QByteArray(R"XML(
387 <typesystem package='Foo' exception-handling='auto-on' allow-thread='no'>
388 <primitive-type name='int'/>
389 <object-type name='Base'/>
390 <object-type name='A'/>
391 </typesystem>)XML")
392 << true << false << true // exception
393 << false; // allowthread
394
395 // Activate on class level
396 QTest::newRow("class-on")
397 << cppCode
398 << QByteArray(R"XML(
399 <typesystem package='Foo'>
400 <primitive-type name='int'/>
401 <object-type name='Base'/>
402 <object-type name='A' exception-handling='auto-on' allow-thread='no'/>
403 </typesystem>)XML")
404 << true << false << true // exception
405 << false; // allowthread
406
407 // Activate on base class level
408 QTest::newRow("baseclass-on")
409 << cppCode
410 << QByteArray(R"XML(
411 <typesystem package='Foo'>
412 <primitive-type name='int'/>
413 <object-type name='Base' exception-handling='auto-on' allow-thread='no'/>
414 <object-type name='A'/>
415 </typesystem>)XML")
416 << true << false << true // exception
417 << false; // allowthread
418
419 // Override value on class level
420 QTest::newRow("override-class-on")
421 << cppCode
422 << QByteArray(R"XML(
423 <typesystem package='Foo'>
424 <primitive-type name='int'/>
425 <object-type name='Base'/>
426 <object-type name='A' exception-handling='auto-on'>
427 <modify-function signature='throwing()' exception-handling='no'/>
428 </object-type>
429 </typesystem>)XML")
430 << true << false << false // exception
431 << false; // allowthread
432 }
433
testScopedModifications()434 void TestModifyFunction::testScopedModifications()
435 {
436 QFETCH(QByteArray, cppCode);
437 QFETCH(QByteArray, xmlCode);
438 QFETCH(bool, expectedGenerateUnspecified);
439 QFETCH(bool, expectedGenerateNonThrowing);
440 QFETCH(bool, expectedGenerateThrowing);
441 QFETCH(bool, expectedAllowThread);
442
443 QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode.constData(), xmlCode.constData(), false));
444 QVERIFY(!builder.isNull());
445
446 const AbstractMetaClass *classA = AbstractMetaClass::findClass(builder->classes(), QLatin1String("A"));
447 QVERIFY(classA);
448
449 const AbstractMetaFunction *f = classA->findFunction(QStringLiteral("unspecified"));
450 QVERIFY(f);
451 QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Unknown);
452 QCOMPARE(f->generateExceptionHandling(), expectedGenerateUnspecified);
453 QCOMPARE(f->allowThread(), expectedAllowThread);
454
455 f = classA->findFunction(QStringLiteral("nonThrowing"));
456 QVERIFY(f);
457 QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::NoExcept);
458 QCOMPARE(f->generateExceptionHandling(), expectedGenerateNonThrowing);
459
460 f = classA->findFunction(QStringLiteral("throwing"));
461 QVERIFY(f);
462 QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Throws);
463 QCOMPARE(f->generateExceptionHandling(), expectedGenerateThrowing);
464 }
465
466 QTEST_APPLESS_MAIN(TestModifyFunction)
467