diff options
author | Marcelo Lira <marcelo.lira@openbossa.org> | 2011-07-25 22:44:53 -0300 |
---|---|---|
committer | Hugo Parente Lima <hugo.pl@gmail.com> | 2012-03-09 19:10:19 -0300 |
commit | 35ab8b8e722b73a3a3ed9312379f6ab849252e19 (patch) | |
tree | 1411f293a32e125874b4b092bb96788b8f583d85 | |
parent | e7fdca6465740132bd881ffd9d20e61be47472d0 (diff) |
Added improved functionality for the 'conversion-rule' tag.
It works for primitive, container and value types. Object types doesn't
have conversion rules because they can not have implicit conversions,
and the regular conversion is always the same (get C++ object held on
Python wrapper, and finding/creating a Python wrapper to a C++ pointer).
Unit tests were added.
Documentation was updated.
Reviewed by Luciano Wolf <luciano.wolf@openbossa.org>
Reviewed by Renato Araújo <renato.filho@openbossa.org>
-rw-r--r-- | doc/typesystem.rst | 1 | ||||
-rw-r--r-- | doc/typesystem_conversionrule.rst | 113 | ||||
-rw-r--r-- | tests/testconversionruletag.cpp | 177 | ||||
-rw-r--r-- | tests/testconversionruletag.h | 7 | ||||
-rw-r--r-- | typesystem.cpp | 346 | ||||
-rw-r--r-- | typesystem.h | 92 | ||||
-rw-r--r-- | typesystem_p.h | 67 |
7 files changed, 714 insertions, 89 deletions
diff --git a/doc/typesystem.rst b/doc/typesystem.rst index 098a9d983..69dda43a0 100644 --- a/doc/typesystem.rst +++ b/doc/typesystem.rst @@ -23,6 +23,7 @@ can be found in the PySide/<QT_MODULE_NAME> directory of the PySide package. typesystem_arguments typesystem_solving_compilation typesystem_templates + typesystem_conversionrule typesystem_documentation diff --git a/doc/typesystem_conversionrule.rst b/doc/typesystem_conversionrule.rst new file mode 100644 index 000000000..c62d5bbf6 --- /dev/null +++ b/doc/typesystem_conversionrule.rst @@ -0,0 +1,113 @@ +.. _conversion-rule-tag: + +Conversion Rule Tag +------------------- + +.. _conversion-rule: + +conversion-rule +^^^^^^^^^^^^^^^ + + The **conversion-rule** tag specifies how a **primitive-type**, a **container-type**, + or a **value-type** may be converted to and from the native C++ language types to the + target language types. + + .. code-block:: xml + + <value-type> + <conversion-rule> + <native-to-target> + // Code to convert a native value to a target language object. + </native-to-target> + <target-to-native> + <add-conversion type='TARGETTYPEA' check='TARGETTYPEA_CHECK(%in)'> + // Code to convert target language type object of type TARGETTYPEA + // to the C++ native type represented by the value/primitive/container-type. + </add-conversion> + <add-conversion type='TARGETTYPEB' check='TARGETTYPEB_CHECK(%in)'> + // Code to convert target language type object of type TARGETTYPEB + // to the C++ native type represented by the value/primitive/container-type. + </add-conversion> + </target-to-native> + </conversion-rule> + </value-type> + + The example above show the structure of a complete conversion rule. Each of the + child tags comprising the conversion rule are described in their own sections + below. + + +.. _native-to-target: + +native-to-target +^^^^^^^^^^^^^^^^ + + The **native-to-target** tag tells how to convert a native C++ value to its + target language equivalent. The text inside the tag is a C++ code the takes + an input value an does what's needed to convert it to the output value. + ``insert-template`` tags may be used to insert commonly repeating code. + + .. code-block:: xml + + <conversion-rule> + <native-to-target> + // Code to convert a native value to a target language object. + </native-to-target> + </conversion-rule> + + Use the replace node to modify the template code. + Notice that the generator must provide type system variables for the input + and output values and types, namely **%in**, **%out**, **%INTYPE** and + **%OUTTYPE**. In the case of container types, **%INTYPE** refers to the + full container type (e.g. **"list<int>"**) and **%INTYPE_0**, **%INTYPE_1**, + **%INTYPE_#**, should be replaced by the types used in the container template + (e.g. **%INTYPE_0** correspondes to **"int"** for **"list<int>"**). + + +.. _target-to-native: + +target-to-native +^^^^^^^^^^^^^^^^ + + The **target-to-native** tag encloses at least one, but usually many, conversions + from target language values to C++ native values. The *optional* attribute + ``replace`` tells if the target language to C++ conversions will be added to, or if + they will replace the implicit conversions collected by *ApiExtractor*. The default + value for it is *yes*. + + + .. code-block:: xml + + <conversion-rule> + <target-to-native replace='yes|no'>\ + // List of target to native conversions meant to replace or expand + // the already existing implicit conversions. + </target-to-native> + </conversion-rule> + + +.. _add-conversion: + +add-conversion +^^^^^^^^^^^^^^ + + Each **add-conversion** tag adds a rule for conversion of a target language type, + indicated by the ``type`` attribute, to the C++ native type represented by the + **primitive-type**, a **container-type**, or **value-type**, to which the parent + **conversion-rule** belongs. + + .. code-block:: xml + + <target-to-native> + <add-conversion type='TARGETTYPE' check='TARGETTYPECHECK(%in)'> + // Code to convert target language type object of type TARGETTYPE_A + // to the C++ native type represented by the value/primitive/container-type. + </add-conversion> + <target-to-native> + + The ``check`` attribute tells how a target value should be checked to see if it belongs to + the type expected. This attribute is *optional*, for it can be derived from the ``type`` + attribute, but it isn't unusual that some special check is needed. The variables + **%in**, **%out**, **%INTYPE**, **%INTYPE_#**, and **%OUTTYPE**, must be provided by + the generator as in the ``native-to-target`` tag. + diff --git a/tests/testconversionruletag.cpp b/tests/testconversionruletag.cpp index 609a806bd..6228ef287 100644 --- a/tests/testconversionruletag.cpp +++ b/tests/testconversionruletag.cpp @@ -27,7 +27,7 @@ #include <QFile> #include <QTemporaryFile> -void TestConversionRuleTag::testConversionRuleTag() +void TestConversionRuleTag::testConversionRuleTagWithFile() { // temp file used later const char conversionData[] = "Hi! I'm a conversion rule."; @@ -38,7 +38,7 @@ void TestConversionRuleTag::testConversionRuleTag() const char cppCode[] = "struct A {};"; QString xmlCode = "\ - <typesystem package=\"Foo\">\ + <typesystem package='Foo'>\ <value-type name='A'>\ <conversion-rule file='"+ file.fileName() +"' />\ </value-type>\ @@ -52,6 +52,179 @@ void TestConversionRuleTag::testConversionRuleTag() QCOMPARE(typeEntry->conversionRule(), QString(conversionData)); } +void TestConversionRuleTag::testConversionRuleTagReplace() +{ + const char cppCode[] = "\ + struct A {\ + A();\ + A(const char*, int);\ + };\ + struct B {\ + A createA();\ + };\ + "; + const char* xmlCode = "\ + <typesystem package='Foo'>\ + <primitive-type name='int'/>\ + <primitive-type name='char'/>\ + <primitive-type name='A'>\ + <conversion-rule>\ + <native-to-target>\ + DoThis();\ + return ConvertFromCppToPython(%IN);\ + </native-to-target>\ + <target-to-native>\ + <add-conversion type='TargetNone' check='%IN == Target_None'>\ + DoThat();\ + DoSomething();\ + %OUT = A();\ + </add-conversion>\ + <add-conversion type='B' check='CheckIfInputObjectIsB(%IN)'>\ + %OUT = %IN.createA();\ + </add-conversion>\ + <add-conversion type='String' check='String_Check(%IN)'>\ + %OUT = new A(String_AsString(%IN), String_GetSize(%IN));\ + </add-conversion>\ + </target-to-native>\ + </conversion-rule>\ + </primitive-type>\ + <value-type name='B'/>\ + </typesystem>"; + + TestUtil t(cppCode, xmlCode); + TypeDatabase* typeDb = TypeDatabase::instance(); + PrimitiveTypeEntry* typeA = typeDb->findPrimitiveType("A"); + QVERIFY(typeA); + + CustomConversion* conversion = typeA->customConversion(); + QVERIFY(conversion); + + QCOMPARE(typeA, conversion->ownerType()); + QCOMPARE(conversion->nativeToTargetConversion().trimmed(), QString("DoThis(); return ConvertFromCppToPython(%IN);")); + + QVERIFY(conversion->replaceOriginalTargetToNativeConversions()); + QVERIFY(conversion->hasTargetToNativeConversions()); + QCOMPARE(conversion->targetToNativeConversions().size(), 3); + + CustomConversion::TargetToNativeConversion* toNative = conversion->targetToNativeConversions().at(0); + QVERIFY(toNative); + QCOMPARE(toNative->sourceTypeName(), QString("TargetNone")); + QVERIFY(toNative->isCustomType()); + QCOMPARE(toNative->sourceType(), (const TypeEntry*)0); + QCOMPARE(toNative->sourceTypeCheck(), QString("%IN == Target_None")); + QCOMPARE(toNative->conversion().trimmed(), QString("DoThat(); DoSomething(); %OUT = A();")); + + toNative = conversion->targetToNativeConversions().at(1); + QVERIFY(toNative); + QCOMPARE(toNative->sourceTypeName(), QString("B")); + QVERIFY(!toNative->isCustomType()); + TypeEntry* typeB = typeDb->findType("B"); + QVERIFY(typeB); + QCOMPARE(toNative->sourceType(), typeB); + QCOMPARE(toNative->sourceTypeCheck(), QString("CheckIfInputObjectIsB(%IN)")); + QCOMPARE(toNative->conversion().trimmed(), QString("%OUT = %IN.createA();")); + + toNative = conversion->targetToNativeConversions().at(2); + QVERIFY(toNative); + QCOMPARE(toNative->sourceTypeName(), QString("String")); + QVERIFY(toNative->isCustomType()); + QCOMPARE(toNative->sourceType(), (const TypeEntry*)0); + QCOMPARE(toNative->sourceTypeCheck(), QString("String_Check(%IN)")); + QCOMPARE(toNative->conversion().trimmed(), QString("%OUT = new A(String_AsString(%IN), String_GetSize(%IN));")); +} + +void TestConversionRuleTag::testConversionRuleTagAdd() +{ + const char cppCode[] = "\ + struct Date {\ + Date();\ + Date(int, int, int);\ + };\ + "; + const char* xmlCode = "\ + <typesystem package='Foo'>\ + <primitive-type name='int'/>\ + <value-type name='Date'>\ + <conversion-rule>\ + <target-to-native replace='no'>\ + <add-conversion type='TargetDate' check='TargetDate_Check(%IN)'>\ + if (!TargetDateTimeAPI) TargetDateTime_IMPORT;\ + %OUT = new Date(TargetDate_Day(%IN), TargetDate_Month(%IN), TargetDate_Year(%IN));\ + </add-conversion>\ + </target-to-native>\ + </conversion-rule>\ + </value-type>\ + </typesystem>"; + + TestUtil t(cppCode, xmlCode); + AbstractMetaClass* classA = t.builder()->classes().findClass("Date"); + QVERIFY(classA); + + CustomConversion* conversion = classA->typeEntry()->customConversion(); + QVERIFY(conversion); + + QCOMPARE(conversion->nativeToTargetConversion(), QString()); + + QVERIFY(!conversion->replaceOriginalTargetToNativeConversions()); + QVERIFY(conversion->hasTargetToNativeConversions()); + QCOMPARE(conversion->targetToNativeConversions().size(), 1); + + CustomConversion::TargetToNativeConversion* toNative = conversion->targetToNativeConversions().first(); + QVERIFY(toNative); + QCOMPARE(toNative->sourceTypeName(), QString("TargetDate")); + QVERIFY(toNative->isCustomType()); + QCOMPARE(toNative->sourceType(), (const TypeEntry*)0); + QCOMPARE(toNative->sourceTypeCheck(), QString("TargetDate_Check(%IN)")); + QCOMPARE(toNative->conversion().trimmed(), QString("if (!TargetDateTimeAPI) TargetDateTime_IMPORT; %OUT = new Date(TargetDate_Day(%IN), TargetDate_Month(%IN), TargetDate_Year(%IN));")); +} + +void TestConversionRuleTag::testConversionRuleTagWithInsertTemplate() +{ + const char cppCode[] = "struct A {};"; + const char* xmlCode = "\ + <typesystem package='Foo'>\ + <primitive-type name='int'/>\ + <template name='native_to_target'>\ + return ConvertFromCppToPython(%IN);\ + </template>\ + <template name='target_to_native'>\ + %OUT = %IN.createA();\ + </template>\ + <primitive-type name='A'>\ + <conversion-rule>\ + <native-to-target>\ + <insert-template name='native_to_target'/>\ + </native-to-target>\ + <target-to-native>\ + <add-conversion type='TargetType'>\ + <insert-template name='target_to_native'/>\ + </add-conversion>\ + </target-to-native>\ + </conversion-rule>\ + </primitive-type>\ + </typesystem>"; + + TestUtil t(cppCode, xmlCode); + TypeDatabase* typeDb = TypeDatabase::instance(); + PrimitiveTypeEntry* typeA = typeDb->findPrimitiveType("A"); + QVERIFY(typeA); + + CustomConversion* conversion = typeA->customConversion(); + QVERIFY(conversion); + + QCOMPARE(typeA, conversion->ownerType()); + QCOMPARE(conversion->nativeToTargetConversion().trimmed(), + QString("// TEMPLATE - native_to_target - START return ConvertFromCppToPython(%IN); // TEMPLATE - native_to_target - END")); + + QVERIFY(conversion->hasTargetToNativeConversions()); + QCOMPARE(conversion->targetToNativeConversions().size(), 1); + + CustomConversion::TargetToNativeConversion* toNative = conversion->targetToNativeConversions().first(); + QVERIFY(toNative); + QCOMPARE(toNative->conversion().trimmed(), + QString("// TEMPLATE - target_to_native - START %OUT = %IN.createA(); // TEMPLATE - target_to_native - END")); +} + QTEST_APPLESS_MAIN(TestConversionRuleTag) #include "testconversionruletag.moc" diff --git a/tests/testconversionruletag.h b/tests/testconversionruletag.h index afab68889..8ab1481b1 100644 --- a/tests/testconversionruletag.h +++ b/tests/testconversionruletag.h @@ -29,7 +29,10 @@ class TestConversionRuleTag : public QObject { Q_OBJECT private slots: - void testConversionRuleTag(); + void testConversionRuleTagWithFile(); + void testConversionRuleTagReplace(); + void testConversionRuleTagAdd(); + void testConversionRuleTagWithInsertTemplate(); }; -#endif
\ No newline at end of file +#endif diff --git a/typesystem.cpp b/typesystem.cpp index 9976d47f9..daffebaa8 100644 --- a/typesystem.cpp +++ b/typesystem.cpp @@ -33,6 +33,8 @@ static QString strings_char = QLatin1String("char"); static QString strings_jchar = QLatin1String("jchar"); static QString strings_jobject = QLatin1String("jobject"); +static QList<CustomConversion*> customConversionsForReview = QList<CustomConversion*>(); + Handler::Handler(TypeDatabase* database, bool generate) : m_database(database), m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass) { @@ -70,6 +72,9 @@ Handler::Handler(TypeDatabase* database, bool generate) tagNames["reject-enum-value"] = StackElement::RejectEnumValue; tagNames["replace-type"] = StackElement::ReplaceType; tagNames["conversion-rule"] = StackElement::ConversionRule; + tagNames["native-to-target"] = StackElement::NativeToTarget; + tagNames["target-to-native"] = StackElement::TargetToNative; + tagNames["add-conversion"] = StackElement::AddConversion; tagNames["modify-argument"] = StackElement::ModifyArgument; tagNames["remove-argument"] = StackElement::RemoveArgument; tagNames["remove-default-expression"] = StackElement::RemoveDefaultExpression; @@ -155,6 +160,10 @@ bool Handler::endElement(const QString &, const QString &localName, const QStrin if (m_generate == TypeEntry::GenerateAll) { TypeDatabase::instance()->addGlobalUserFunctions(m_contextStack.top()->addedFunctions); TypeDatabase::instance()->addGlobalUserFunctionModifications(m_contextStack.top()->functionMods); + foreach (CustomConversion* customConversion, customConversionsForReview) { + foreach (CustomConversion::TargetToNativeConversion* toNative, customConversion->targetToNativeConversions()) + toNative->setSourceType(m_database->findType(toNative->sourceTypeName())); + } } break; case StackElement::ObjectTypeEntry: @@ -174,6 +183,26 @@ bool Handler::endElement(const QString &, const QString &localName, const QStrin } } break; + case StackElement::NativeToTarget: + case StackElement::AddConversion: { + CustomConversion* customConversion = static_cast<TypeEntry*>(m_current->entry)->customConversion(); + if (!customConversion) { + m_error = "CustomConversion object is missing."; + return false; + } + + QString code = m_contextStack.top()->codeSnips.takeLast().code(); + if (m_current->type == StackElement::AddConversion) { + if (customConversion->targetToNativeConversions().isEmpty()) { + m_error = "CustomConversion's target to native conversions missing."; + return false; + } + customConversion->targetToNativeConversions().last()->setConversion(code); + } else { + customConversion->setNativeToTargetConversion(code); + } + } + break; case StackElement::CustomMetaConstructor: { m_current->entry->setCustomConstructor(*m_current->value.customFunction); delete m_current->value.customFunction; @@ -193,18 +222,28 @@ bool Handler::endElement(const QString &, const QString &localName, const QStrin m_database->addTemplate(m_current->value.templateEntry); break; case StackElement::TemplateInstanceEnum: - if (m_current->parent->type == StackElement::InjectCode) + switch (m_current->parent->type) { + case StackElement::InjectCode: + case StackElement::NativeToTarget: + case StackElement::AddConversion: m_contextStack.top()->codeSnips.last().addTemplateInstance(m_current->value.templateInstance); - else if (m_current->parent->type == StackElement::Template) + break; + case StackElement::Template: m_current->parent->value.templateEntry->addTemplateInstance(m_current->value.templateInstance); - else if (m_current->parent->type == StackElement::CustomMetaConstructor - || m_current->parent->type == StackElement::CustomMetaConstructor) + break; + case StackElement::CustomMetaConstructor: + case StackElement::CustomMetaDestructor: m_current->parent->value.customFunction->addTemplateInstance(m_current->value.templateInstance); - else if (m_current->parent->type == StackElement::ConversionRule) + break; + case StackElement::ConversionRule: m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.last().addTemplateInstance(m_current->value.templateInstance); - else if (m_current->parent->type == StackElement::InjectCodeInFunction) + break; + case StackElement::InjectCodeInFunction: m_contextStack.top()->functionMods.last().snips.last().addTemplateInstance(m_current->value.templateInstance); - + break; + default: + break; // nada + }; break; default: break; @@ -214,7 +253,8 @@ bool Handler::endElement(const QString &, const QString &localName, const QStrin || m_current->type == StackElement::NamespaceTypeEntry || m_current->type == StackElement::InterfaceTypeEntry || m_current->type == StackElement::ObjectTypeEntry - || m_current->type == StackElement::ValueTypeEntry) { + || m_current->type == StackElement::ValueTypeEntry + || m_current->type == StackElement::PrimitiveTypeEntry) { StackElementContext* context = m_contextStack.pop(); delete context; } @@ -247,6 +287,11 @@ bool Handler::characters(const QString &ch) return true; } + if (m_current->type == StackElement::NativeToTarget || m_current->type == StackElement::AddConversion) { + m_contextStack.top()->codeSnips.last().addCode(ch); + return true; + } + if (m_current->parent) { if ((m_current->type & StackElement::CodeSnipMask)) { CodeSnipList snips; @@ -417,11 +462,15 @@ bool Handler::startElement(const QString &, const QString &n, StackElement* element = new StackElement(m_current); element->type = tagNames[tagName]; + if (element->type == StackElement::Root && m_generate == TypeEntry::GenerateAll) + customConversionsForReview.clear(); + if (element->type == StackElement::Root || element->type == StackElement::NamespaceTypeEntry || element->type == StackElement::InterfaceTypeEntry || element->type == StackElement::ObjectTypeEntry - || element->type == StackElement::ValueTypeEntry) { + || element->type == StackElement::ValueTypeEntry + || element->type == StackElement::PrimitiveTypeEntry) { m_contextStack.push(new StackElementContext()); } @@ -953,6 +1002,13 @@ bool Handler::startElement(const QString &, const QString &n, attributes["class"] = QString(); attributes["file"] = QString(); break; + case StackElement::TargetToNative: + attributes["replace"] = QString("yes"); + break; + case StackElement::AddConversion: + attributes["type"] = QString(); + attributes["check"] = QString(); + break; case StackElement::RejectEnumValue: attributes["name"] = ""; break; @@ -1055,11 +1111,10 @@ bool Handler::startElement(const QString &, const QString &n, case StackElement::ConversionRule: { if (topElement.type != StackElement::ModifyArgument && topElement.type != StackElement::ValueTypeEntry - && topElement.type != StackElement::ObjectTypeEntry && topElement.type != StackElement::PrimitiveTypeEntry && topElement.type != StackElement::ContainerTypeEntry) { m_error = "Conversion rules can only be specified for argument modification, " - "value-type, object-type, primitive-type or container-type conversion."; + "value-type, primitive-type or container-type conversion."; return false; } @@ -1082,40 +1137,70 @@ bool Handler::startElement(const QString &, const QString &n, snip.language = lang; m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.append(snip); } else { - if (topElement.entry->hasConversionRule()) { + if (topElement.entry->hasConversionRule() || topElement.entry->hasCustomConversion()) { m_error = "Types can have only one conversion rule"; return false; } + // The old conversion rule tag that uses a file containing the conversion + // will be kept temporarily for compatibility reasons. QString sourceFile = attributes["file"]; - if (sourceFile.isEmpty()) { - m_error = QString("'file' attribute required; the source file containing the" - " containing the conversion functions must be provided"); - return false; - } - - //Handler constructor.... - if (m_generate != TypeEntry::GenerateForSubclass - && m_generate != TypeEntry::GenerateNothing) { - - const char* conversionFlag = NATIVE_CONVERSION_RULE_FLAG; - if (lang == TypeSystem::TargetLangCode) - conversionFlag = TARGET_CONVERSION_RULE_FLAG; - - QFile conversionSource(sourceFile); - if (conversionSource.open(QIODevice::ReadOnly | QIODevice::Text)) { - topElement.entry->setConversionRule(conversionFlag + QString::fromUtf8(conversionSource.readAll())); - } else { - ReportHandler::warning("File containing conversion code for " - + topElement.entry->name() - + " type does not exist or is not readable: " - + sourceFile); + if (!sourceFile.isEmpty()) { + if (m_generate != TypeEntry::GenerateForSubclass + && m_generate != TypeEntry::GenerateNothing) { + + const char* conversionFlag = NATIVE_CONVERSION_RULE_FLAG; + if (lang == TypeSystem::TargetLangCode) + conversionFlag = TARGET_CONVERSION_RULE_FLAG; + + QFile conversionSource(sourceFile); + if (conversionSource.open(QIODevice::ReadOnly | QIODevice::Text)) { + topElement.entry->setConversionRule(conversionFlag + QString::fromUtf8(conversionSource.readAll())); + } else { + ReportHandler::warning("File containing conversion code for " + + topElement.entry->name() + + " type does not exist or is not readable: " + + sourceFile); + } } } + CustomConversion* customConversion = new CustomConversion(static_cast<TypeEntry*>(m_current->entry)); + customConversionsForReview.append(customConversion); } } - + break; + case StackElement::NativeToTarget: { + if (topElement.type != StackElement::ConversionRule) { + m_error = "Native to Target conversion code can only be specified for custom conversion rules."; + return false; + } + m_contextStack.top()->codeSnips << CodeSnip(0); + } + break; + case StackElement::TargetToNative: { + if (topElement.type != StackElement::ConversionRule) { + m_error = "Target to Native conversions can only be specified for custom conversion rules."; + return false; + } + bool replace = attributes["replace"] == "yes"; + static_cast<TypeEntry*>(m_current->entry)->customConversion()->setReplaceOriginalTargetToNativeConversions(replace); + } + break; + case StackElement::AddConversion: { + if (topElement.type != StackElement::TargetToNative) { + m_error = "Target to Native conversions can only be added inside 'target-to-native' tags."; + return false; + } + QString sourceTypeName = attributes["type"]; + if (sourceTypeName.isEmpty()) { + m_error = "Target to Native conversions must specify the input type with the 'type' attribute."; + return false; + } + QString typeCheck = attributes["check"]; + static_cast<TypeEntry*>(m_current->entry)->customConversion()->addTargetToNativeConversion(sourceTypeName, typeCheck); + m_contextStack.top()->codeSnips << CodeSnip(0); + } break; case StackElement::ModifyArgument: { if (topElement.type != StackElement::ModifyFunction @@ -1692,8 +1777,11 @@ bool Handler::startElement(const QString &, const QString &n, (topElement.type != StackElement::Template) && (topElement.type != StackElement::CustomMetaConstructor) && (topElement.type != StackElement::CustomMetaDestructor) && + (topElement.type != StackElement::NativeToTarget) && + (topElement.type != StackElement::AddConversion) && (topElement.type != StackElement::ConversionRule)) { - m_error = "Can only insert templates into code snippets, templates, custom-constructors, custom-destructors or conversion-rule."; + m_error = "Can only insert templates into code snippets, templates, custom-constructors, "\ + "custom-destructors, conversion-rule, native-to-target or add-conversion tags."; return false; } element->value.templateInstance = new TemplateInstance(attributes["name"], since); @@ -2162,3 +2250,189 @@ bool TypeEntry::isCppPrimitive() const return res != &cppTypes[N]; } + +// Again, stuff to avoid ABI breakage. +typedef QHash<const TypeEntry*, CustomConversion*> TypeEntryCustomConversionMap; +Q_GLOBAL_STATIC(TypeEntryCustomConversionMap, typeEntryCustomConversionMap); + +TypeEntry::~TypeEntry() +{ + if (typeEntryCustomConversionMap()->contains(this)) { + CustomConversion* customConversion = typeEntryCustomConversionMap()->value(this); + typeEntryCustomConversionMap()->remove(this); + delete customConversion; + } +} + +bool TypeEntry::hasCustomConversion() const +{ + return typeEntryCustomConversionMap()->contains(this); +} +void TypeEntry::setCustomConversion(CustomConversion* customConversion) +{ + if (customConversion) + typeEntryCustomConversionMap()->insert(this, customConversion); + else if (typeEntryCustomConversionMap()->contains(this)) + typeEntryCustomConversionMap()->remove(this); +} +CustomConversion* TypeEntry::customConversion() const +{ + if (typeEntryCustomConversionMap()->contains(this)) + return typeEntryCustomConversionMap()->value(this); + return 0; +} + +/* +static void injectCode(ComplexTypeEntry *e, + const char *signature, + const QByteArray &code, + const ArgumentMap &args) +{ + CodeSnip snip; + snip.language = TypeSystem::NativeCode; + snip.position = CodeSnip::Beginning; + snip.addCode(QString::fromLatin1(code)); + snip.argumentMap = args; + + FunctionModification mod; + mod.signature = QMetaObject::normalizedSignature(signature); + mod.snips << snip; + mod.modifiers = Modification::CodeInjection; +} +*/ + +struct CustomConversion::CustomConversionPrivate +{ + CustomConversionPrivate(const TypeEntry* ownerType) + : ownerType(ownerType), replaceOriginalTargetToNativeConversions(false) + { + } + const TypeEntry* ownerType; + QString nativeToTargetConversion; + bool replaceOriginalTargetToNativeConversions; + TargetToNativeConversions targetToNativeConversions; +}; + +struct CustomConversion::TargetToNativeConversion::TargetToNativeConversionPrivate +{ + TargetToNativeConversionPrivate() + : sourceType(0) + { + } + const TypeEntry* sourceType; + QString sourceTypeName; + QString sourceTypeCheck; + QString conversion; +}; + +CustomConversion::CustomConversion(TypeEntry* ownerType) +{ + m_d = new CustomConversionPrivate(ownerType); + if (ownerType) + ownerType->setCustomConversion(this); +} + +CustomConversion::~CustomConversion() +{ + foreach (TargetToNativeConversion* targetToNativeConversion, m_d->targetToNativeConversions) + delete targetToNativeConversion; + m_d->targetToNativeConversions.clear(); + delete m_d; +} + +const TypeEntry* CustomConversion::ownerType() const +{ + return m_d->ownerType; +} + +QString CustomConversion::nativeToTargetConversion() const +{ + return m_d->nativeToTargetConversion; +} + +void CustomConversion::setNativeToTargetConversion(const QString& nativeToTargetConversion) +{ + m_d->nativeToTargetConversion = nativeToTargetConversion; +} + +bool CustomConversion::replaceOriginalTargetToNativeConversions() const +{ + return m_d->replaceOriginalTargetToNativeConversions; +} + +void CustomConversion::setReplaceOriginalTargetToNativeConversions(bool replaceOriginalTargetToNativeConversions) +{ + m_d->replaceOriginalTargetToNativeConversions = replaceOriginalTargetToNativeConversions; +} + +bool CustomConversion::hasTargetToNativeConversions() const +{ + return !(m_d->targetToNativeConversions.isEmpty()); +} + +CustomConversion::TargetToNativeConversions& CustomConversion::targetToNativeConversions() +{ + return m_d->targetToNativeConversions; +} + +const CustomConversion::TargetToNativeConversions& CustomConversion::targetToNativeConversions() const +{ + return m_d->targetToNativeConversions; +} + +void CustomConversion::addTargetToNativeConversion(const QString& sourceTypeName, + const QString& sourceTypeCheck, + const QString& conversion) +{ + m_d->targetToNativeConversions.append(new TargetToNativeConversion(sourceTypeName, sourceTypeCheck, conversion)); +} + +CustomConversion::TargetToNativeConversion::TargetToNativeConversion(const QString& sourceTypeName, + const QString& sourceTypeCheck, + const QString& conversion) +{ + m_d = new TargetToNativeConversionPrivate; + m_d->sourceTypeName = sourceTypeName; + m_d->sourceTypeCheck = sourceTypeCheck; + m_d->conversion = conversion; +} + +CustomConversion::TargetToNativeConversion::~TargetToNativeConversion() +{ + delete m_d; +} + +const TypeEntry* CustomConversion::TargetToNativeConversion::sourceType() const +{ + return m_d->sourceType; +} + +void CustomConversion::TargetToNativeConversion::setSourceType(const TypeEntry* sourceType) +{ + m_d->sourceType = sourceType; +} + +bool CustomConversion::TargetToNativeConversion::isCustomType() const +{ + return !(m_d->sourceType); +} + +QString CustomConversion::TargetToNativeConversion::sourceTypeName() const +{ + return m_d->sourceTypeName; +} + +QString CustomConversion::TargetToNativeConversion::sourceTypeCheck() const +{ + return m_d->sourceTypeCheck; +} + +QString CustomConversion::TargetToNativeConversion::conversion() const +{ + return m_d->conversion; +} + +void CustomConversion::TargetToNativeConversion::setConversion(const QString& conversion) +{ + m_d->conversion = conversion; +} diff --git a/typesystem.h b/typesystem.h index abba4455c..c915cd424 100644 --- a/typesystem.h +++ b/typesystem.h @@ -82,7 +82,7 @@ enum Ownership { }; }; -struct ReferenceCount +struct APIEXTRACTOR_API ReferenceCount { ReferenceCount() {} enum Action { // 0x01 - 0xff @@ -102,7 +102,7 @@ struct ReferenceCount QString varName; }; -struct ArgumentOwner +struct APIEXTRACTOR_API ArgumentOwner { enum Action { Invalid = 0x00, @@ -156,7 +156,7 @@ public: QList<CodeSnipFragment> codeList; }; -class CustomFunction : public CodeSnipAbstract +class APIEXTRACTOR_API CustomFunction : public CodeSnipAbstract { public: CustomFunction(const QString &n = QString()) : name(n) { } @@ -165,7 +165,7 @@ public: QString paramName; }; -class TemplateEntry : public CodeSnipAbstract +class APIEXTRACTOR_API TemplateEntry : public CodeSnipAbstract { public: TemplateEntry(const QString &name, double vr) @@ -190,7 +190,7 @@ private: typedef QHash<QString, TemplateEntry *> TemplateEntryHash; -class TemplateInstance +class APIEXTRACTOR_API TemplateInstance { public: TemplateInstance(const QString &name, double vr) @@ -245,7 +245,7 @@ public: }; typedef QList<CodeSnip> CodeSnipList; -struct ArgumentModification +struct APIEXTRACTOR_API ArgumentModification { ArgumentModification(int idx, double vr) : removedDefaultExpression(false), removed(false), @@ -436,7 +436,7 @@ private: }; typedef QList<FunctionModification> FunctionModificationList; -struct FieldModification: public Modification +struct APIEXTRACTOR_API FieldModification: public Modification { bool isReadable() const { @@ -547,7 +547,7 @@ private: }; typedef QList<AddedFunction> AddedFunctionList; -struct ExpensePolicy +struct APIEXTRACTOR_API ExpensePolicy { ExpensePolicy() : limit(-1) {} int limit; @@ -561,7 +561,7 @@ struct ExpensePolicy class InterfaceTypeEntry; class ObjectTypeEntry; -class DocModification +class APIEXTRACTOR_API DocModification { public: enum Mode { @@ -614,6 +614,8 @@ private: typedef QList<DocModification> DocModificationList; +class CustomConversion; + class APIEXTRACTOR_API TypeEntry { public: @@ -662,7 +664,7 @@ public: { }; - virtual ~TypeEntry() { } + virtual ~TypeEntry(); Type type() const { @@ -949,17 +951,23 @@ public: return m_version; } + /// TODO-CONVERTER: mark as deprecated bool hasNativeConversionRule() const { return m_conversionRule.startsWith(NATIVE_CONVERSION_RULE_FLAG); } + /// TODO-CONVERTER: mark as deprecated bool hasTargetConversionRule() const { return m_conversionRule.startsWith(TARGET_CONVERSION_RULE_FLAG); } bool isCppPrimitive() const; + + bool hasCustomConversion() const; + void setCustomConversion(CustomConversion* customConversion); + CustomConversion* customConversion() const; private: QString m_name; Type m_type; @@ -1155,7 +1163,7 @@ private: typedef QList<const PrimitiveTypeEntry*> PrimitiveTypeEntryList; -struct EnumValueRedirection +struct APIEXTRACTOR_API EnumValueRedirection { EnumValueRedirection(const QString &rej, const QString &us) : rejected(rej), @@ -1729,7 +1737,7 @@ public: }; -class ValueTypeEntry : public ComplexTypeEntry +class APIEXTRACTOR_API ValueTypeEntry : public ComplexTypeEntry { public: ValueTypeEntry(const QString &name, double vr) : ComplexTypeEntry(name, BasicValueType, vr) { } @@ -1749,7 +1757,7 @@ protected: }; -class StringTypeEntry : public ValueTypeEntry +class APIEXTRACTOR_API StringTypeEntry : public ValueTypeEntry { public: StringTypeEntry(const QString &name, double vr) @@ -1768,7 +1776,7 @@ public: } }; -class CharTypeEntry : public ValueTypeEntry +class APIEXTRACTOR_API CharTypeEntry : public ValueTypeEntry { public: CharTypeEntry(const QString &name, double vr) : ValueTypeEntry(name, CharType, vr) @@ -1789,7 +1797,7 @@ public: } }; -class VariantTypeEntry: public ValueTypeEntry +class APIEXTRACTOR_API VariantTypeEntry: public ValueTypeEntry { public: VariantTypeEntry(const QString &name, double vr) : ValueTypeEntry(name, VariantType, vr) { } @@ -1889,7 +1897,7 @@ private: InterfaceTypeEntry *m_interface; }; -struct TypeRejection +struct APIEXTRACTOR_API TypeRejection { QString class_name; QString function_name; @@ -1897,6 +1905,56 @@ struct TypeRejection QString enum_name; }; -QString fixCppTypeName(const QString &name); +APIEXTRACTOR_API QString fixCppTypeName(const QString &name); + +class APIEXTRACTOR_API CustomConversion +{ +public: + CustomConversion(TypeEntry* ownerType); + ~CustomConversion(); + + const TypeEntry* ownerType() const; + QString nativeToTargetConversion() const; + void setNativeToTargetConversion(const QString& nativeToTargetConversion); + + class APIEXTRACTOR_API TargetToNativeConversion + { + public: + TargetToNativeConversion(const QString& sourceTypeName, + const QString& sourceTypeCheck, + const QString& conversion = QString()); + ~TargetToNativeConversion(); + + const TypeEntry* sourceType() const; + void setSourceType(const TypeEntry* sourceType); + bool isCustomType() const; + QString sourceTypeName() const; + QString sourceTypeCheck() const; + QString conversion() const; + void setConversion(const QString& conversion); + private: + struct TargetToNativeConversionPrivate; + TargetToNativeConversionPrivate* m_d; + }; + + /** + * Returns true if the target to C++ custom conversions should + * replace the original existing ones, and false if the custom + * conversions should be added to the original. + */ + bool replaceOriginalTargetToNativeConversions() const; + void setReplaceOriginalTargetToNativeConversions(bool replaceOriginalTargetToNativeConversions); + + typedef QList<TargetToNativeConversion*> TargetToNativeConversions; + bool hasTargetToNativeConversions() const; + TargetToNativeConversions& targetToNativeConversions(); + const TargetToNativeConversions& targetToNativeConversions() const; + void addTargetToNativeConversion(const QString& sourceTypeName, + const QString& sourceTypeCheck, + const QString& conversion = QString()); +private: + struct CustomConversionPrivate; + CustomConversionPrivate* m_d; +}; #endif // TYPESYSTEM_H diff --git a/typesystem_p.h b/typesystem_p.h index 0cf575509..371321bc2 100644 --- a/typesystem_p.h +++ b/typesystem_p.h @@ -35,23 +35,23 @@ class StackElement None = 0x0, // Type tags (0x1, ... , 0xff) - ObjectTypeEntry = 0x1, - ValueTypeEntry = 0x2, - InterfaceTypeEntry = 0x3, - NamespaceTypeEntry = 0x4, - ComplexTypeEntryMask = 0x7, + ObjectTypeEntry = 0x1, + ValueTypeEntry = 0x2, + InterfaceTypeEntry = 0x3, + NamespaceTypeEntry = 0x4, + ComplexTypeEntryMask = 0x7, // Non-complex type tags (0x8, 0x9, ... , 0xf) - PrimitiveTypeEntry = 0x8, - EnumTypeEntry = 0x9, - ContainerTypeEntry = 0xa, - FunctionTypeEntry = 0xb, - TypeEntryMask = 0xf, + PrimitiveTypeEntry = 0x8, + EnumTypeEntry = 0x9, + ContainerTypeEntry = 0xa, + FunctionTypeEntry = 0xb, + TypeEntryMask = 0xf, // Documentation tags - InjectDocumentation = 0x10, - ModifyDocumentation = 0x20, - DocumentationMask = 0xf0, + InjectDocumentation = 0x10, + ModifyDocumentation = 0x20, + DocumentationMask = 0xf0, // Simple tags (0x100, 0x200, ... , 0xf00) ExtraIncludes = 0x0100, @@ -70,32 +70,35 @@ class StackElement TemplateInstanceEnum = 0x0e00, Replace = 0x0f00, AddFunction = 0x1000, + NativeToTarget = 0x1100, + TargetToNative = 0x1200, + AddConversion = 0x1300, SimpleMask = 0x3f00, // Code snip tags (0x1000, 0x2000, ... , 0xf000) - InjectCode = 0x4000, - InjectCodeInFunction = 0x8000, - CodeSnipMask = 0xc000, + InjectCode = 0x4000, + InjectCodeInFunction = 0x8000, + CodeSnipMask = 0xc000, // Function modifier tags (0x010000, 0x020000, ... , 0xf00000) - Access = 0x010000, - Removal = 0x020000, - Rename = 0x040000, - ModifyArgument = 0x080000, - Thread = 0x100000, - FunctionModifiers = 0xff0000, + Access = 0x010000, + Removal = 0x020000, + Rename = 0x040000, + ModifyArgument = 0x080000, + Thread = 0x100000, + FunctionModifiers = 0xff0000, // Argument modifier tags (0x01000000 ... 0xf0000000) - ConversionRule = 0x01000000, - ReplaceType = 0x02000000, - ReplaceDefaultExpression = 0x04000000, - RemoveArgument = 0x08000000, - DefineOwnership = 0x10000000, - RemoveDefaultExpression = 0x20000000, - NoNullPointers = 0x40000000, - ReferenceCount = 0x80000000, - ParentOwner = 0x90000000, - ArgumentModifiers = 0xff000000 + ConversionRule = 0x01000000, + ReplaceType = 0x02000000, + ReplaceDefaultExpression = 0x04000000, + RemoveArgument = 0x08000000, + DefineOwnership = 0x10000000, + RemoveDefaultExpression = 0x20000000, + NoNullPointers = 0x40000000, + ReferenceCount = 0x80000000, + ParentOwner = 0x90000000, + ArgumentModifiers = 0xff000000 }; StackElement(StackElement *p) : entry(0), type(None), parent(p) { } |