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