Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-76025 Specify the QML language and write a QML-to-C++ compiler
  3. QTBUG-91956

(qmltc) Support QML language and QQmlEngine related features

    XMLWordPrintable

Details

    • Technical task
    • Resolution: Duplicate
    • P2: Important
    • 6.x
    • QML: Compiler

    Description

      There are multiple language features [1] and runtime (QQmlEngine/QQmlObjectCreator) related features that have to be supported by the type compiler. In particular:

      Legend:
      ☐ - not covered. if versioned, like "☐ 6.4", it would mean that it is important to look at this in the scope of the specific Qt release.
      ☒ - done.

      QML language (object definitions specifically*) [2]:

      • ☒ Import statements are handled by the generic machinery (qmlcompiler library); comments are not interesting at the moment (and they do not map to C++ at all in general)
      • ☒ Types
        • Basic/builtin types [3] (mostly related to properties)
        • JavaScript types (unclear whether there are any special ones worthy of attention)
        • Object types (mostly related to object bindings)
      • ☒ id attribute
      • Properties
        • attributes must map correctly to the C++ model (some require Q_CLASSINFO in C++)
          • ☒ default
          • ☒ required
          • ☒ readonly
        • ☒ special types: `var` (QVariant) and `list` (QQmlListProperty/QQmlListReference)
        • ☒ attached properties: correctly instantiated to be accessible in JavaScript
        • group properties: both references and value types must be handled (differences are visible in bindings)
          • ☒ generalized group properties (QTBUG-105378)
          • ☒ usual group properties
        • ☒ private properties (Q_PRIVATE_PROPERTY): internal-only but must be supported (QtQuick uses them extensively)
        • ☒ special C++ case (no read method): Q_PROPERTY(QByteArray sampler MEMBER sampler)
        • ☒ Properties with only BINDABLE (no READ/WRITE/NOTIFY). Reference: QTBUG-97249
      • Aliases
        • ☒ methods (read, write, notify/bindable) must forward to the corresponding property methods
        • ☒ 6.4 (at least needs testing) attributes must be aligned with the corresponding property attributes (QTBUG-105708)
        • ☒ aliases to private properties
        • ☒ (at least needs extra testing) aliases to group properties (and properties inside group/attached property):
          • broken: Text's font.letterSpacing cannot be aliases at the moment
        • ☒ extension types (like font, etc.) must be preferred over real types
        • ☒ default aliases (fixed along the way in QTBUG-105708)
      • ☒ Enums
        • mapped to C++ enumerations
        • require special code to be accessible in JavaScript (Q_ENUMS should be enough for the meta-object system)
      • ☒ Signals
        • mapped to C++ signals and marked as Q_SIGNALS
      • ☒ Methods/functions
        • call JavaScript code through QQmlEnginePrivate::executeRuntimeFunction()
        • marked as Q_INVOKABLE
        • special attribute: default method (deprecated in I001ddf4d7933871977f84a5e012d020fb043cc6)
      • ☐ Inline components (QTBUG-105946)
        • terra incognita
        • they define a new type (as if having a separate QML file), so must be handled similarly (extra care should be given to how context hierarchy is handled for them: as for document roots?)
        • they use the compilation unit of the QML file (no separate CU)
        • they support cyclic references but not cyclic instantiations (would it actually work for qmltc? might have to use QQmlComponent wrapping just because of this)
          import QtQuick 2.15
          Item {
            component A : Item {
              property B b // NB: if "b: B {}", it should be a recursion error
            }
            component B : Item {
              property A a
            }
            A {
              b: B { Component.onCompleted: console.log(a) }
            }
          }
          
        • could be troublesome in the code resolving names to types (e.g. is Foo.Bar an enum.enumerator or an inline component?)
      • Bindings
        • ☒ Invalid (noop for compiler)
        • Property value assignments
          • ☒ Boolean, Number
          • ☒ String
          • ☒ Null
          • ☐ (in general) support conversions between types; complex cases use QQmlStringConverters (TODO: think whether this is actually in scope of qmltc or in scope of qmlsc/qmlcachegen)
          • ☐ (in general) must follow QQmlObjectCreator::setPropertyValue() logic
          • ☐ (at least requires more tests?) (in general) must be aware of type conversions (e.g. bool -> int, int -> bool, etc.)
        • ☒ Translations (see QTBUG-104637)
          • QV4::CompiledData::Binding::Type_Translation
          • QV4::CompiledData::Binding::Type_TranslationById
          • terra incognita (not addressed at all currently)
        • ☒ Generic script bindings on a property
          • call JavaScript code through QQmlEnginePrivate::executeRuntimeFunction()
          • can be bound to both old style and new style properties (notify and bindable respectively)
        • ☒ Signal handlers (similar to methods). TODO: there are issues e.g. QTBUG-99317
          • call JavaScript code through QQmlEnginePrivate::executeRuntimeFunction()
          • marked as Q_SLOT
          • connected to signals (TODO: we might prefer using
        • ☒ Property change handlers
          • call JavaScript code through QQmlEnginePrivate::executeRuntimeFunction()
          • can be "attached" to both old style and new style properties (notify and bindable respectively)
          • can be "attached" to properties of different kinds (private, group, attached, list( ? ))
        • ☒ Object bindings
          • implement type hierarchy
        • ☒ Bindings on attached properties
          • aggregate bindings for individual attached properties, then recurse to bindings of other types
          • in a nutshell, `this->setBinding` is changed into `this->prop->setBinding`
        • ☒ Bindings on group properties
          • aggregate bindings for individual group properties, then recurse to bindings of other types
          • in a nutshell, `this->setBinding` is changed into `this->prop->setBinding`
          • extra care with value types needed (well-defined semantics has to be specified yet: what happens on write if there's a connection, binding, etc.)
        • ☒ Bindings with regular expressions
          • See QQmlJSImportVisitor::parseLiteralBinding() for literal regular expressions
          • Non-literal ones can probably be handled as script bindings
        • ☐ Intriguing bindings (with more cases in QtDS examples e.g. coffee machine)
              RangeSlider {
                  first.handle.objectName: "rangeslider.first"
                  second.handle.objectName: "rangeslider.second"
              }
          
          import QtQuick 2.15
          import QtQuick.Window 2.15
          
          Window {
              id: window
              width: 600
              height: 600
              visible: true
          
              Image {
                  id: sequence
              }
          
              property alias sequence: sequence
          
              sequence.layer.enabled: false // QTBUG-94983 but the syntax is still valid?
          }
          
          
        • ☒ Bindings on types with extensions (literal ones mostly):
          • We should acquire the extenstion type from the real one and use extenstion type's READ/WRITE/etc (if a method exists). There are cases (`font.letterSpacing: 42`) which would likely fail to work otherwise.
      • ☐ Singleton objects
        • haven't looked at it much, should require special code generation but nothing to redesign in the compiler/codegen (hopefully)
        • could both come from QML (related to Pragma) or C++
      • ☐ Pragmas
        • Singleton: handled via qmldir
        • Strict: unclear if this one needs to be supported
      • ☐ (at least requires testing; mostly relies on qmlcompiler lib?) Namespaces
        • ☒ QML "namespaces" (related mostly to how QML modules are imported)
        • C++ namespaces (a C++ type exposed to QML could be declared inside a namespace; that namespace should be visible and usable by the compiler)
      • Special cases
        • ☒ 6.4 (basic support exists, might need QQmlObjectCreator-aligned model however) Component.onCompleted and Component.onDestruction - https://codereview.qt-project.org/c/qt/qtdeclarative/+/419466
        • ☒ Component-wrapped types (require QQmlComponent wrapping in the generated code)
        • ☐ (at least needs testing) C++ singletons (might require some boilerplate)
        • ☒ "on" assignments (do not seem to semantically differ from normal binding constructions)
        • `parent` property (appear only inside bindings/JS code/etc.)

      QML importing related:

      QQmlEngine/runtime and QtQuick related:

      • ☐ (basic support exists, needs URL resolution tests) QQmlContext hierarchy for created types (influences JavaScript lookups - e.g. id, inherited methods)
      • ☒ Q_INTERFACES (note that these interfaces are inherited, so each subclass' instance should call the interface methods):
        • QQmlParserStatus (QQuickAnimationController)
        • QQmlPropertyValueInterceptor [4] (QQuickBehavior)
        • QQmlPropertyValueSource [5] (QQuickAbstractAnimation)
        • QQmlFinalizerHook
      • ☐ Image providers
        • terra incognita (no additional code is generated at the moment)
      • QQmlObjectCreator::sharedState (has to be available when creating generated types)
        • QQmlObjectCreator::sharedState>finalizeCallbacks (implicitly related to QQmlParserStatus)- – replaced by QQmlFinalizerHook (once available)
        • ☐ (at least needs use case + tests) QQmlObjectCreator::sharedState->allJavaScriptObjects (unclear whether these are needed)
        • ☒ QQmlObjectCreator::sharedState->componentAttached (related to Component.onCompleted/onDestruction; unclear whether needed)
      • Deferred bindings (terra incognita; a.k.a. deferred properties?)
      • ☐ Custom parser types (need to figure whether these should be supported at all - QTBUG-95117)
        • (in general) must be rejected in favor of generalized group properties (which are yet to be supported)
        • QQmlListModel (QML ListModel)
        • QQmlConnections (QML Connections)
        • QQuickPropertyChanges (QML PropertyChanges)
        • SignalTransition
      • QtQuick concepts
        • Visual parents
        • States
        • Transitions
        • Animations (related to Q_INTERFACES(QQmlPropertyValueSource))
        • Animators
        • Animated sprites
      • ☐ (at least needs testing/use case) Resource loading (local, network, etc.)
        • (in general) might require certain network-related code to be generated (e.g. if there's smth handled by QQmlObjectCreator)
        • terra incognita
      • ☐ Extra QML_ macros (at least needs testing):
        • ☒ QML_EXTENDED, QML_EXTENDED_NAMESPACE
        • ☐ QML_FOREIGN_NAMESPACE
        • related to QML language elements, ideally should not require any additional work
      • ☒ (https://codereview.qt-project.org/c/qt/qtdeclarative/+/382086) Object finalization order is as follows:
        • 1) QQmlParserStatus objects are finalized (via componentComplete())
        • 2) QQmlFinalizerHook objects are finalized (via componentFinalized())
        • 3) Component.onComplete attached signals are finalized (in qqmlobjectcreator.cpp via `emit obj->complete()`)
        • (in general) qmltc must ensure this order is preserved. note: within-step order of componentComplete()/componentFinalized()/Component.onCompleted is undefined (so root's componentComplete() might happen before some child's componentComplete()).
        • (in general) simplest solution is to allow componentComplete() to run as part of normal object creation, then generate hardcoded instructions for componentFinalized() [as those are somewhat rare] and Component.onComplete():
          void DocumentRoot::finalize()
          {
            // assume at this point that all componentComplete() are run
          
            // QQmlFinalizerHooks:
            ((QQmlFinalizerHooks *)this->getRandomChild()->getRandomChild())->componentFinalized();
            // ...
          
            // Component.onCompleted:
            // ...
          }
          
      • ☐ Elements with unclear usage / support model:
        • QQmlIncubationController
        • QQmlAbstractUrlInterceptor
        • QQmlInstantiationInterrupt
        • QML State (related to PropertyChanges; might require special code generation?)

      Codegen features (not related to the language/runtime but nice-to-haves for the compilation):

      • ☒ Generate namespace for the classes (to avoid name collisions)
        • Might be optional (with a --namespace argument for qmltc) or "always on"
      • ☐ 6.4 Generate export macro and use that for each generated type (related to QTBUG-96040)


      (Poorly formatted) comments:

      • Interceptors [4] and property value sources [5] (also test how group/attached properties behave with property value sources) – some very basic support added but more work required
      • Proper QQmlEngine context hierarchy – done; fairly extensive tests exist (NB: new hierarchy is not identical to what QQmlComponent generates but this is intentional)
      • Property aliases – partially done; but: need to ensure aliases mimic all Q_PROPERTY attributes of the origins (as much as type inference allows). one missing feature is RESET support.
      • List properties (and list types?) in various shapes and forms (do we need to handle QQmlJSScope::AccessSemantics::Sequence in any interesting ways?) – partially done for list properties (e.g. object bindings) but it's unclear if there's more depth in it
      • Delegates (apparently the parent of a delegate could be a fairly random thing? e.g. contentItem in ListView): must be correctly constructed despite the shenanigans
      • QQmlComponent-based types (usually handled in a special way by loading the component at runtime through QQmlComponent::create()) – delegates are handled but the rest isn't

      [1]: https://doc.qt.io/qt-6/qtqml-syntax-basics.html
      [2]: https://doc.qt.io/qt-6/qtqml-documents-topic.html
      [3]: https://doc.qt.io/qt-6/qmlbasictypes.html and https://doc.qt.io/qt-6/qtqml-typesystem-basictypes.html
      [4]: https://doc.qt.io/qt-6/qtqml-cppintegration-definetypes.html#property-modifier-types
      [5]: https://doc.qt.io/qt-6/qtqml-cppintegration-definetypes.html#property-value-sources


      Other useful links:
      https://doc.qt.io/qt-6/qmlreference.html

      Attachments

        Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

              sami.shalayel Sami Shalayel
              agolubev Andrei Golubev
              Votes:
              2 Vote for this issue
              Watchers:
              8 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: