Revision control
++ // Copyright (C) 2020 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
++
++ #include "parser.h"
++
++ #include <QtCore/qdir.h>
++ #include <QtCore/qfileinfo.h>
++ #include <QtCore/qstringview.h>
++
++ #include <QtQml/qqmllist.h>
++
++ // Parsing
++ #include <QtQml/private/qqmljsengine_p.h>
++ #include <QtQml/private/qqmljslexer_p.h>
++ #include <QtQml/private/qqmljsparser_p.h>
++
++ #include <QtQuick3D/private/qquick3dobject_p.h>
++ #include <QtQuick3D/private/qquick3dviewport_p.h>
++ // Scene
++ #include <QtQuick3D/private/qquick3dsceneenvironment_p.h>
++ // Material(s)
++ #include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
++ #include <QtQuick3D/private/qquick3ddefaultmaterial_p.h>
++ #include <QtQuick3D/private/qquick3dcustommaterial_p.h>
++ // Lights
++ #include <QtQuick3D/private/qquick3dspotlight_p.h>
++ #include <QtQuick3D/private/qquick3dpointlight_p.h>
++ #include <QtQuick3D/private/qquick3ddirectionallight_p.h>
++
++ // Instancing
++ #include <QtQuick3D/private/qquick3dinstancing_p.h>
++
++ #include <QtQuick3D/private/qquick3dshaderutils_p.h>
++
++ #include <QtQuick3DUtils/private/qssginvasivelinkedlist_p.h>
++
++ #include <QtGui/qquaternion.h>
++
++ QT_BEGIN_NAMESPACE
++
++ template<typename T, T *T::*N = &T::next>
++ struct InvasiveListView : protected QSSGInvasiveSingleLinkedList<T, N>
++ {
++ using QSSGInvasiveSingleLinkedList<T, N>::begin;
++ using QSSGInvasiveSingleLinkedList<T, N>::end;
++ using QSSGInvasiveSingleLinkedList<T, N>::m_head;
++
++ explicit InvasiveListView(const T &obj) { m_head = &const_cast<T &>(obj); }
++ };
++
++ template <typename T> struct TypeInfo
++ {
++ static constexpr const char *qmlTypeName() { return "Unknown"; }
++ static constexpr int typeId() { return QMetaType::UnknownType; }
++ };
++
++ #define DECLARE_QQ3D_TYPE(CLASS, TYPE_NAME) \
++ template<> struct TypeInfo<CLASS> \
++ { \
++ static constexpr const char *qmlTypeName() { return #TYPE_NAME; } \
++ inline static constexpr int typeId() { return qMetaTypeId<CLASS>(); } \
++ inline static constexpr int qmlListTypeId() { return qMetaTypeId<QQmlListProperty<CLASS>>(); } \
++ }
++
++ DECLARE_QQ3D_TYPE(QQuick3DViewport, View3D);
++ DECLARE_QQ3D_TYPE(QQuick3DSceneEnvironment, SceneEnvironment);
++ DECLARE_QQ3D_TYPE(QQuick3DMaterial, Material);
++ DECLARE_QQ3D_TYPE(QQuick3DPrincipledMaterial, PrincipledMaterial);
++ DECLARE_QQ3D_TYPE(QQuick3DDefaultMaterial, DefaultMaterial);
++ DECLARE_QQ3D_TYPE(QQuick3DCustomMaterial, CustomMaterial);
++ DECLARE_QQ3D_TYPE(QQuick3DDirectionalLight, DirectionalLight);
++ DECLARE_QQ3D_TYPE(QQuick3DPointLight, PointLight);
++ DECLARE_QQ3D_TYPE(QQuick3DSpotLight, SpotLight);
++ DECLARE_QQ3D_TYPE(QQuick3DTexture, Texture);
++ DECLARE_QQ3D_TYPE(QQuick3DShaderUtilsTextureInput, TextureInput);
++ DECLARE_QQ3D_TYPE(QQuick3DModel, Model);
++ DECLARE_QQ3D_TYPE(QQuick3DEffect, Effect);
++ DECLARE_QQ3D_TYPE(QQuick3DShaderUtilsRenderPass, Pass);
++ DECLARE_QQ3D_TYPE(QQuick3DShaderUtilsShader, Shader);
++ DECLARE_QQ3D_TYPE(QQuick3DInstanceList, InstanceList);
++ DECLARE_QQ3D_TYPE(QQuick3DInstanceListEntry, InstanceListEntry);
++
++ using QmlTypeNames = QHash<QString, int>;
++
++ #define QQ3D_TYPE_ENTRY(TYPE) { TypeInfo<TYPE>::qmlTypeName(), TypeInfo<TYPE>::typeId() }
++
++ QmlTypeNames baseTypeMap()
++ {
++ return { QQ3D_TYPE_ENTRY(QQuick3DViewport),
++ QQ3D_TYPE_ENTRY(QQuick3DSceneEnvironment),
++ QQ3D_TYPE_ENTRY(QQuick3DPrincipledMaterial),
++ QQ3D_TYPE_ENTRY(QQuick3DDefaultMaterial),
++ QQ3D_TYPE_ENTRY(QQuick3DCustomMaterial),
++ QQ3D_TYPE_ENTRY(QQuick3DDirectionalLight),
++ QQ3D_TYPE_ENTRY(QQuick3DPointLight),
++ QQ3D_TYPE_ENTRY(QQuick3DSpotLight),
++ QQ3D_TYPE_ENTRY(QQuick3DTexture),
++ QQ3D_TYPE_ENTRY(QQuick3DShaderUtilsTextureInput),
++ QQ3D_TYPE_ENTRY(QQuick3DModel),
++ QQ3D_TYPE_ENTRY(QQuick3DEffect),
++ QQ3D_TYPE_ENTRY(QQuick3DShaderUtilsRenderPass),
++ QQ3D_TYPE_ENTRY(QQuick3DShaderUtilsShader),
++ QQ3D_TYPE_ENTRY(QQuick3DInstanceList),
++ QQ3D_TYPE_ENTRY(QQuick3DInstanceListEntry)
++ };
++ }
++
++ Q_GLOBAL_STATIC(QmlTypeNames, s_typeMap)
++
++ struct Context
++ {
++ enum class Type
++ {
++ Application,
++ Component
++ };
++
++ struct Property {
++ enum MemberState : quint8
++ {
++ Uninitialized,
++ Initialized
++ };
++
++ QObject *target = nullptr;
++ QStringView name;
++ int targetType = QMetaType::UnknownType;
++ QMetaType::Type type = QMetaType::UnknownType;
++ MemberState memberState = Uninitialized;
++ };
++
++ template<typename T> using Vector = QVector<T>;
++ using InterceptObjDefFunc = bool (*)(const QQmlJS::AST::UiObjectDefinition &, Context &, int &);
++ using InterceptObjBinding = bool (*)(const QQmlJS::AST::UiObjectBinding &, Context &, int &);
++ using InterceptPublicMember = bool (*)(const QQmlJS::AST::UiPublicMember &, Context &, int &);
++ using InterceptCallExpression = bool (*)(const QQmlJS::AST::CallExpression &, Context &, int &);
++
++ QQmlJS::Engine *engine = nullptr;
++ QDir workingDir; // aka source directory
++ QFileInfo currentFileInfo;
++
++ MaterialParser::SceneData sceneData;
++ Property property;
++
++ struct Component
++ {
++ QObject *ptr = nullptr;
++ int type = QMetaType::UnknownType;
++ };
++
++ QHash<QStringView, QObject *> identifierMap;
++ QHash<QString, Component> components;
++ InterceptObjDefFunc interceptODFunc = nullptr;
++ InterceptObjBinding interceptOBFunc = nullptr;
++ InterceptPublicMember interceptPMFunc = nullptr;
++ InterceptCallExpression interceptCallExpr = nullptr;
++ Type type = Type::Application;
++ bool dbgprint = false;
++ };
++
++ Q_DECLARE_TYPEINFO(Context::Component, Q_PRIMITIVE_TYPE);
++
++ namespace BuiltinHelpers {
++
++ using ArgumentListView = InvasiveListView<QQmlJS::AST::ArgumentList>;
++
++ template <typename T> Q_REQUIRED_RESULT constexpr quint8 componentCount() { Q_STATIC_ASSERT(true); return 0; }
++ template <> Q_REQUIRED_RESULT constexpr quint8 componentCount<QVector2D>() { return 2; }
++ template <> Q_REQUIRED_RESULT constexpr quint8 componentCount<QVector3D>() { return 3; }
++ template <> Q_REQUIRED_RESULT constexpr quint8 componentCount<QVector4D>() { return 4; }
++
++ static double expressionValue(const QQmlJS::AST::ExpressionNode &expr) {
++ using namespace QQmlJS::AST;
++
++ if (expr.kind == Node::Kind_NumericLiteral) {
++ return static_cast<const NumericLiteral &>(expr).value;
++ } else if (expr.kind == Node::Kind_UnaryMinusExpression) {
++ const auto &minusExpr = static_cast<const UnaryMinusExpression &>(expr);
++ if (minusExpr.expression && minusExpr.expression->kind == Node::Kind_NumericLiteral)
++ return static_cast<const NumericLiteral &>(*minusExpr.expression).value * -1.0;
++ } else if (expr.kind == Node::Kind_UnaryPlusExpression) {
++ const auto &plusExpr = static_cast<const UnaryPlusExpression &>(expr);
++ if (plusExpr.expression && plusExpr.expression->kind == Node::Kind_NumericLiteral)
++ return static_cast<const NumericLiteral &>(*plusExpr.expression).value;
++ } else {
++ printf("Expression type \'%d\' unhandled!\n", expr.kind);
++ }
++
++ return 0.0;
++ }
++
++ template <typename T>
++ static inline bool setProperty(const Context::Property &property, const T &v)
++ {
++ return property.target->setProperty(property.name.toLatin1(), QVariant::fromValue(v));
++ }
++
++ template<typename Vec>
++ static Vec toVec(const ArgumentListView &list, bool *ok = nullptr)
++ {
++ using namespace QQmlJS::AST;
++ int i = 0;
++ const int e = componentCount<Vec>();
++ Vec vec;
++ for (const auto &listItem : list) {
++ if (listItem.expression && i != e)
++ vec[i] = expressionValue(*listItem.expression);
++ ++i;
++ }
++
++ if (ok)
++ *ok = (i == e);
++
++ return vec;
++ }
++
++ static QPointF toPoint(const ArgumentListView &list, bool *ok = nullptr)
++ {
++ using namespace QQmlJS::AST;
++ int i = 0;
++ const int e = 2;
++ qreal args[e];
++ for (const auto &listItem : list) {
++ if (listItem.expression && i != e)
++ args[i] = expressionValue(*listItem.expression);
++ ++i;
++ }
++
++ if (ok)
++ *ok = (i == e);
++
++ return QPointF(args[0], args[1]);
++ }
++
++ static QSizeF toSize(const ArgumentListView &list, bool *ok = nullptr)
++ {
++ using namespace QQmlJS::AST;
++ int i = 0;
++ const int e = 2;
++ qreal args[e];
++ for (const auto &listItem : list) {
++ if (listItem.expression && listItem.expression->kind == Node::Kind_NumericLiteral && i != e)
++ args[i] = expressionValue(*listItem.expression);
++ ++i;
++ }
++
++ if (ok)
++ *ok = (i == e);
++
++ return QSizeF(args[0], args[1]);
++ }
++
++ static QRectF toRect(const ArgumentListView &list, bool *ok = nullptr)
++ {
++ using namespace QQmlJS::AST;
++ int i = 0;
++ const int e = 4;
++ qreal args[e];
++ for (const auto &listItem : list) {
++ if (listItem.expression && i != e)
++ args[i] = expressionValue(*listItem.expression);
++ ++i;
++ }
++
++ if (ok)
++ *ok = (i == e);
++
++ return QRectF(args[0], args[1], args[2], args[3]);
++ }
++
++ static QMatrix4x4 toMat44(const ArgumentListView &list, bool *ok = nullptr)
++ {
++ using namespace QQmlJS::AST;
++ int i = 0;
++ const int e = 16;
++ float args[e];
++ for (const auto &listItem : list) {
++ if (listItem.expression && i != e)
++ args[i] = float(expressionValue(*listItem.expression));
++ ++i;
++ }
++
++ if (ok)
++ *ok = (i == e);
++
++ return QMatrix4x4(args);
++ }
++
++ static QQuaternion toQuaternion(const ArgumentListView &list, bool *ok = nullptr)
++ {
++ using namespace QQmlJS::AST;
++ int i = 0;
++ const int e = 4;
++ float args[e];
++ for (const auto &listItem : list) {
++ if (listItem.expression && listItem.expression->kind == Node::Kind_NumericLiteral && i != e)
++ args[i] = float(expressionValue(*listItem.expression));
++ ++i;
++ }
++
++ if (ok)
++ *ok = (i == e);
++
++ return QQuaternion(args[0], args[1], args[2], args[3]);
++ }
++
++ // String variants
++ // Note: Unlike the call variants we assume arguments are correct (can be converted),
++ // as they should have failed earlier, during parsing, if they were not.
++ template <typename Vec>
++ static Vec toVec(const QStringView &ref)
++ {
++ const auto args = ref.split(u',');
++ Vec vec;
++ bool ok = false;
++ if (args.size() == componentCount<Vec>()) {
++ for (int i = 0; i != componentCount<Vec>(); ++i) {
++ vec[i] = args.at(i).toDouble(&ok);
++ Q_ASSERT(ok);
++ }
++ }
++
++ return vec;
++ }
++
++ static QPointF toPoint(const QStringView &ref)
++ {
++ const auto args = ref.split(u",");
++ if (args.size() == 2) {
++ bool ok = false;
++ const auto arg0 = args.at(0).toDouble(&ok);
++ Q_ASSERT(ok);
++ const auto arg1 = args.at(1).toDouble(&ok);
++ Q_ASSERT(ok);
++ return QPointF(arg0, arg1);
++ }
++ return QPointF();
++ }
++
++ static QSizeF toSize(const QStringView &ref)
++ {
++ const auto args = ref.split(u'x');
++ if (args.size() == 2) {
++ bool ok = false;
++ const auto arg0 = args.at(0).toDouble(&ok);
++ Q_ASSERT(ok);
++ const auto arg1 = args.at(1).toDouble(&ok);
++ Q_ASSERT(ok);
++ return QSizeF(arg0, arg1);
++ }
++ return QSizeF();
++ }
++
++ static QRectF toRect(const QStringView &ref)
++ {
++ auto args = ref.split(u",");
++ if (args.size() == 3) {
++ bool ok = false;
++ const auto arg0 = args.at(0).toDouble(&ok);
++ Q_ASSERT(ok);
++ const auto arg1 = args.at(1).toDouble(&ok);
++ Q_ASSERT(ok);
++ args = args.at(2).split(u'x');
++ if (args.size() == 2) {
++ const auto arg2 = args.at(0).toDouble(&ok);
++ Q_ASSERT(ok);
++ const auto arg3 = args.at(1).toDouble(&ok);
++ Q_ASSERT(ok);
++ return QRectF(arg0, arg1, arg2, arg3);
++ }
++ }
++ return QRectF();
++ }
++
++ static QQuaternion toQuaternion(const QStringView &ref)
++ {
++ const auto args = ref.split(u',');
++ if (args.size() == 4) {
++ bool ok = false;
++ const auto arg0 = args.at(0).toDouble(&ok);
++ Q_ASSERT(ok);
++ const auto arg1 = args.at(1).toDouble(&ok);
++ Q_ASSERT(ok);
++ const auto arg2 = args.at(2).toDouble(&ok);
++ Q_ASSERT(ok);
++ const auto arg3 = args.at(3).toDouble(&ok);
++ Q_ASSERT(ok);
++ return QQuaternion(arg0, arg1, arg2, arg3);
++ }
++ return QQuaternion();
++ }
++
++ } // BuiltinHelpers
++
++ template <typename T>
++ static void cloneQmlList(const QObject &so, QMetaProperty &sp, QObject &to, QMetaProperty &tp) {
++ Q_ASSERT(sp.typeId() == tp.typeId());
++ const auto tv = tp.read(&to);
++ const auto sv = sp.read(&so);
++ if (sv.isValid() && tv.isValid()) {
++ auto tl = tv.value<QQmlListProperty<T>>();
++ auto sl = sv.value<QQmlListProperty<T>>();
++ const auto count = sl.count(&sl);
++ for (int i = 0; count != i; ++i)
++ tl.append(&tl, sl.at(&sl, i));
++ }
++ }
++
++ static void cloneProperties(QObject &target, const QObject &source)
++ {
++ Q_ASSERT(target.metaObject() == source.metaObject());
++ const auto smo = source.metaObject();
++ auto tmo = target.metaObject();
++ const int propCount = smo->propertyCount();
++ for (int i = 0; i != propCount; ++i) {
++ auto sp = smo->property(i);
++ auto tp = tmo->property(i);
++ if (sp.typeId() == tp.typeId()) {
++ if (sp.typeId() == TypeInfo<QQuick3DMaterial>::qmlListTypeId())
++ cloneQmlList<QQuick3DMaterial>(source, sp, target, tp);
++ else if (sp.typeId() == TypeInfo<QQuick3DEffect>::qmlListTypeId())
++ cloneQmlList<QQuick3DEffect>(source, sp, target, tp);
++ else if (sp.typeId() == TypeInfo<QQuick3DShaderUtilsRenderPass>::qmlListTypeId())
++ cloneQmlList<QQuick3DShaderUtilsRenderPass>(source, sp, target, tp);
++ else if (sp.typeId() == TypeInfo<QQuick3DShaderUtilsShader>::qmlListTypeId())
++ cloneQmlList<QQuick3DShaderUtilsShader>(source, sp, target, tp);
++ else
++ tmo->property(i).write(&target, smo->property(i).read(&source));
++ }
++ }
++
++ // Clone the dynamic properties as well
++ for (const auto &prop : source.dynamicPropertyNames())
++ target.setProperty(prop.constData(), source.property(prop.constData()));
++ }
++
++ template <typename T>
++ inline QVariant fromStringEnumHelper(const QStringView &ref, const QMetaProperty &property)
++ {
++ bool ok = false;
++ const int v = property.enumerator().keyToValue(ref.toLatin1(), &ok);
++ return ok ? QVariant::fromValue(T(v)) : QVariant();
++ }
++
++ static QVariant fromString(const QStringView &ref, const Context &ctx)
++ {
++ const auto &p = ctx.property;
++ if (!p.target)
++ return QVariant();
++
++ static const auto toBuiltinType = [](int type, const QStringView &ref, const QDir &workingDir) {
++ using namespace BuiltinHelpers;
++ bool ok = false;
++ switch (type) {
++ case QMetaType::Int:
++ {
++ const auto v = ref.toInt(&ok);
++ return (ok ? QVariant::fromValue(v) : QVariant());
++ }
++ break;
++ case QMetaType::Bool:
++ {
++ const auto v = ref.toInt(&ok);
++ return (ok ? QVariant::fromValue(bool(v)) : QVariant());
++ }
++ case QMetaType::Double:
++ {
++ const auto v = ref.toDouble(&ok);
++ return (ok ? QVariant::fromValue(qreal(v)) : QVariant());
++ }
++ break;
++ case QMetaType::QString:
++ return QVariant::fromValue(ref);
++ case QMetaType::QUrl:
++ {
++ if (ref.startsWith(u':') || ref.startsWith(QDir::separator()))
++ return QVariant::fromValue(QUrl::fromLocalFile(ref.toString()));
++ else if (ref.startsWith(u'#'))
++ return QVariant::fromValue(QUrl(ref.toString()));
++ else
++ return QVariant::fromValue(QUrl::fromUserInput(ref.toString(), workingDir.canonicalPath()));
++ }
++ case QMetaType::QColor:
++ return QVariant::fromValue(QColor(ref));
++ case QMetaType::QTime:
++ return QVariant::fromValue(QTime::fromString(ref.toString()));
++ case QMetaType::QDate:
++ return QVariant::fromValue(QDate::fromString(ref.toString()));
++ case QMetaType::QDateTime:
++ return QVariant::fromValue(QDateTime::fromString(ref.toString()));
++ case QMetaType::QRectF:
++ return QVariant::fromValue(toRect(ref));
++ case QMetaType::QPointF:
++ return QVariant::fromValue(toPoint(ref));
++ case QMetaType::QSizeF:
++ return QVariant::fromValue(toSize(ref));
++ case QMetaType::QVector2D:
++ return QVariant::fromValue(toVec<QVector2D>(ref));
++ case QMetaType::QVector3D:
++ return QVariant::fromValue(toVec<QVector3D>(ref));
++ case QMetaType::QVector4D:
++ return QVariant::fromValue(toVec<QVector4D>(ref));
++ case QMetaType::QQuaternion:
++ return QVariant::fromValue(toQuaternion(ref));
++ }
++
++ return QVariant();
++ };
++
++ if (p.type != QMetaType::UnknownType) // Built in Qt types int, vector3d etc
++ return toBuiltinType(p.type, ref, ctx.workingDir);
++
++ // hard mode, detect the property type
++ // We only care about the types that are relevant for us
++ if (p.targetType != QMetaType::UnknownType) {
++ Q_ASSERT(p.target);
++ Q_ASSERT(!p.name.isEmpty());
++ const int idx = p.target->metaObject()->indexOfProperty(p.name.toLatin1().constData());
++ const auto property = p.target->metaObject()->property(idx);
++ if (property.metaType().id() >= QMetaType::User) {
++ const QMetaType metaType = property.metaType();
++ if (p.targetType == TypeInfo<QQuick3DDefaultMaterial>::typeId() || p.targetType == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
++ // Common for both materials
++ if (metaType.id() == qMetaTypeId<QQuick3DMaterial::CullMode>())
++ return fromStringEnumHelper<QQuick3DMaterial::CullMode>(ref, property);
++ if (metaType.id() == qMetaTypeId<QQuick3DMaterial::TextureChannelMapping>())
++ return fromStringEnumHelper<QQuick3DMaterial::TextureChannelMapping>(ref, property);
++
++ if (p.targetType == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
++ if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::Lighting>())
++ return fromStringEnumHelper<QQuick3DPrincipledMaterial::Lighting>(ref, property);
++ if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::BlendMode>())
++ return fromStringEnumHelper<QQuick3DPrincipledMaterial::BlendMode>(ref, property);
++ if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::AlphaMode>())
++ return fromStringEnumHelper<QQuick3DPrincipledMaterial::AlphaMode>(ref, property);
++ } else if (p.targetType == TypeInfo<QQuick3DDefaultMaterial>::typeId()) {
++ if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::Lighting>())
++ return fromStringEnumHelper<QQuick3DDefaultMaterial::Lighting>(ref, property);
++ if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::BlendMode>())
++ return fromStringEnumHelper<QQuick3DDefaultMaterial::BlendMode>(ref, property);
++ if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::SpecularModel>())
++ return fromStringEnumHelper<QQuick3DDefaultMaterial::SpecularModel>(ref, property);
++ }
++ } else if (p.targetType == TypeInfo<QQuick3DCustomMaterial>::typeId()) {
++ if (metaType.id() == qMetaTypeId<QQuick3DCustomMaterial::ShadingMode>())
++ return fromStringEnumHelper<QQuick3DCustomMaterial::ShadingMode>(ref, property);
++ if (metaType.id() == qMetaTypeId<QQuick3DCustomMaterial::BlendMode>())
++ return fromStringEnumHelper<QQuick3DCustomMaterial::BlendMode>(ref, property);
++ } else if (p.targetType == TypeInfo<QQuick3DSpotLight>::typeId() || p.targetType == TypeInfo<QQuick3DPointLight>::typeId()) {
++ if (metaType.id() == qMetaTypeId<QQuick3DAbstractLight::QSSGShadowMapQuality>())
++ return fromStringEnumHelper<QQuick3DAbstractLight::QSSGShadowMapQuality>(ref, property);
++ } else if (p.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
++ if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes>())
++ return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes>(ref, property);
++ if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues>())
++ return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues>(ref, property);
++ if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues>())
++ return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues>(ref, property);
++ if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentTonemapModes>())
++ return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentTonemapModes>(ref, property);
++ } else if (p.targetType == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) {
++ if (metaType.id() == qMetaTypeId<QQuick3DShaderUtilsShader::Stage>())
++ return fromStringEnumHelper<QQuick3DShaderUtilsShader::Stage>(ref, property);
++ }
++ } else { // Qt type
++ return toBuiltinType(property.metaType().id(), ref, ctx.workingDir);
++ }
++ }
++
++ if (ctx.dbgprint)
++ printf("Unhandled type for property %s\n", ref.toLatin1().constData());
++
++ return QVariant();
++ }
++
++ static QString getQmlFileExtension() { return QStringLiteral("qml"); }
++
++ struct Visitors
++ {
++ static void visit(const QQmlJS::AST::UiProgram &program, Context &ctx, int &ret)
++ {
++ using namespace QQmlJS::AST;
++ const bool readHeaders = false;
++ if (readHeaders && program.headers) { // No real need for us to look at the includes
++ using HeaderItem = UiHeaderItemList;
++ using Headers = InvasiveListView<HeaderItem>;
++
++ Headers headers(*program.headers);
++ for (const auto &header : headers)
++ printf("Type: %d\n", header.kind);
++ }
++ if (program.members)
++ visit(*program.members, ctx, ret);
++ }
++ static void visit(const QQmlJS::AST::UiObjectInitializer &objInitializer, Context &ctx, int &ret)
++ {
++ using namespace QQmlJS::AST;
++ if (objInitializer.members)
++ visit(*objInitializer.members, ctx, ret);
++ }
++
++ static void visit(const QQmlJS::AST::UiObjectDefinition &def, Context &ctx, int &ret)
++ {
++ using namespace QQmlJS::AST;
++ if (ctx.dbgprint)
++ printf("Object definition -> %s\n", def.qualifiedTypeNameId->name.toLocal8Bit().constData());
++ if (!(ctx.interceptODFunc && ctx.interceptODFunc(def, ctx, ret))) {
++ if (def.initializer)
++ visit(*static_cast<UiObjectInitializer *>(def.initializer), ctx, ret);
++ }
++ }
++
++ static void visit(const QQmlJS::AST::UiArrayMemberList &memberList, Context &ctx, int &ret)
++ {
++ using namespace QQmlJS::AST;
++ using ArrayMemberItem = UiArrayMemberList;
++ using ArrayMembers = InvasiveListView<ArrayMemberItem>;
++
++ ArrayMembers arrayMembers(memberList);
++ for (auto &object : arrayMembers) {
++ if (object.member->kind == Node::Kind_UiObjectDefinition) {
++ const auto &def = *static_cast<UiObjectDefinition *>(object.member);
++ visit(def, ctx, ret);
++ }
++ }
++ }
++
++ static void visit(const QQmlJS::AST::UiScriptBinding &binding, Context &ctx, int &ret)
++ {
++ using namespace QQmlJS::AST;
++ if (ctx.dbgprint)
++ printf("Script binding -> %s ", binding.qualifiedId->name.toLocal8Bit().constData());
++
++ const auto oldName = ctx.property.name; // reentrancy
++ ctx.property.name = binding.qualifiedId->name;
++
++ if (binding.statement) {
++ if (binding.statement->kind == Node::Kind_ExpressionStatement) {
++ const auto &expressionStatement = static_cast<const ExpressionStatement &>(*binding.statement);
++ visit(expressionStatement, ctx, ret);
++ }
++ }
++ ctx.property.name = oldName;
++ }
++
++ static void visit(const QQmlJS::AST::UiArrayBinding &arrayBinding, Context &ctx, int &ret)
++ {
++ using namespace QQmlJS::AST;
++ if (ctx.dbgprint)
++ printf("Array binding(s) -> %s: [\n", arrayBinding.qualifiedId->name.toLocal8Bit().constData());
++
++ const auto oldName = ctx.property.name; // reentrancy
++ ctx.property.name = arrayBinding.qualifiedId->name;
++
++ if (arrayBinding.members)
++ visit(*arrayBinding.members, ctx, ret);
++
++ if (ctx.dbgprint)
++ printf("]\n");
++
++ ctx.property.name = oldName;
++ }
++
++ static void visit(const QQmlJS::AST::UiObjectBinding &objectBinding, Context &ctx, int &ret)
++ {
++ using namespace QQmlJS::AST;
++
++ if (ctx.dbgprint)
++ printf("Object binding -> %s: %s {\n", objectBinding.qualifiedId->name.toLocal8Bit().constData(), objectBinding.qualifiedTypeNameId->name.toLocal8Bit().constData());
++
++ if (objectBinding.initializer) {
++ if (!(ctx.interceptOBFunc && ctx.interceptOBFunc(objectBinding, ctx, ret)))
++ visit(*objectBinding.initializer, ctx, ret);
++ }
++
++ if (ctx.dbgprint)
++ printf("}\n");
++ }
++
++ static void visit(const QQmlJS::AST::UiPublicMember &member, Context &ctx, int &ret)
++ {
++ using namespace QQmlJS::AST;
++
++ if (ctx.dbgprint)
++ printf("%s member -> %s ", (member.type == UiPublicMember::Signal ? "Signal" : "Property"), member.name.toLocal8Bit().constData());
++
++ auto name = ctx.property.name;
++ ctx.property.name = member.name;
++ const auto typeCpy = ctx.property.type;
++
++ if (!(ctx.interceptPMFunc && ctx.interceptPMFunc(member, ctx, ret))) {
++ if (member.statement) {
++ const auto &statement = member.statement;
++ if (statement->kind == Node::Kind_ExpressionStatement)
++ visit(static_cast<const ExpressionStatement &>(*statement), ctx, ret);
++ else if (ctx.dbgprint)
++ printf("Unhandled statement (%d)\n", statement->kind);
++ } else if (member.binding) {
++ const auto &binding = member.binding;
++ if (binding->kind == Node::Kind_UiObjectBinding)
++ visit(static_cast<const UiObjectBinding &>(*binding), ctx, ret);
++ else if (ctx.dbgprint)
++ printf("Unhandled binding (%d)\n", binding->kind);
++ }
++ }
++
++ qSwap(ctx.property.name, name);
++ ctx.property.type = typeCpy;
++ }
++
++ static void visit(const QQmlJS::AST::ExpressionStatement &exprStatement, Context &ctx, int &ret)
++ {
++ using namespace QQmlJS::AST;
++ if (exprStatement.expression) {
++ if (exprStatement.expression->kind == Node::Kind_IdentifierExpression) {
++ const auto &identExpression = static_cast<const IdentifierExpression &>(*exprStatement.expression);
++ visit(identExpression, ctx, ret);
++ } else if (exprStatement.expression->kind == Node::Kind_StringLiteral) {
++ const auto &stringLiteral = static_cast<const StringLiteral &>(*exprStatement.expression);
++ visit(stringLiteral, ctx, ret);
++ } else if (exprStatement.expression->kind == Node::Kind_NumericLiteral) {
++ const auto &numericLiteral = static_cast<const NumericLiteral &>(*exprStatement.expression);
++ visit(numericLiteral, ctx, ret);
++ } else if (exprStatement.expression->kind == Node::Kind_FieldMemberExpression) {
++ const auto &fieldMemberExpression = static_cast<const FieldMemberExpression &>(*exprStatement.expression);
++ visit(fieldMemberExpression, ctx, ret);
++ } else if (exprStatement.expression->kind == Node::Kind_TrueLiteral || exprStatement.expression->kind == Node::Kind_FalseLiteral) {
++ const bool v = (exprStatement.expression->kind == Node::Kind_TrueLiteral);
++ if (ctx.dbgprint)
++ printf("-> TrueLiteral: %s\n", v ? "true" : "false");
++ if (ctx.property.target) {
++ auto target = ctx.property.target;
++ const auto &name = ctx.property.name;
++ target->setProperty(name.toLatin1(), QVariant::fromValue(v));
++ }
++ } else if (exprStatement.expression->kind == Node::Kind_ArrayPattern) {
++ const auto &arrayPattern = static_cast<const ArrayPattern &>(*exprStatement.expression);
++ visit(arrayPattern, ctx, ret);
++ } else if (exprStatement.expression->kind == Node::Kind_CallExpression) {
++ const auto &callExpression = static_cast<const CallExpression &>(*exprStatement.expression);
++ visit(callExpression, ctx, ret);
++ } else if (exprStatement.expression->kind == Node::Kind_UnaryMinusExpression) {
++ const auto &unaryMinusExpr = static_cast<const UnaryMinusExpression &>(*exprStatement.expression);
++ if (unaryMinusExpr.expression && unaryMinusExpr.expression->kind == Node::Kind_NumericLiteral) {
++ auto &numericLiteral = static_cast<NumericLiteral &>(*unaryMinusExpr.expression);
++ const auto value = numericLiteral.value;
++ numericLiteral.value *= -1;
++ visit(numericLiteral, ctx, ret);
++ numericLiteral.value = value;
++ }
++ } else if (exprStatement.expression->kind == Node::Kind_UnaryPlusExpression) {
++ const auto &unaryPlusExpr = static_cast<const UnaryPlusExpression &>(*exprStatement.expression);
++ if (unaryPlusExpr.expression)
++ visit(static_cast<const NumericLiteral &>(*unaryPlusExpr.expression), ctx, ret);
++ } else {
++ if (ctx.dbgprint)
++ printf("<expression: %d>\n", exprStatement.expression->kind);
++ }
++ }
++ }
++
++ static void visit(const QQmlJS::AST::IdentifierExpression &idExpr, Context &ctx, int &ret)
++ {
++ Q_UNUSED(ret);
++ if (ctx.dbgprint)
++ printf("-> Identifier: %s\n", idExpr.name.toLocal8Bit().constData());
++
++ if (ctx.property.target && ctx.type != Context::Type::Component) {
++ const auto foundIt = ctx.identifierMap.constFind(idExpr.name);
++ const auto end = ctx.identifierMap.constEnd();
++ if (foundIt != end) { // If an item was found it means this is a reference
++ if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
++ if (QQuick3DMaterial *mat = qobject_cast<QQuick3DMaterial *>(*foundIt)) {
++ auto materials = qobject_cast<QQuick3DModel *>(ctx.property.target)->materials();
++ // Since we are initializing this for the first time, make sure we clean out any inherited data!
++ if (ctx.property.memberState == Context::Property::Uninitialized) {
++ if (ctx.dbgprint)
++ printf("Clearing inherited materials\n");
++ materials.clear(&materials);
++ ctx.property.memberState = Context::Property::Initialized;
++ }
++ materials.append(&materials, mat);
++ if (ctx.dbgprint)
++ printf("Appending material to %s\n", ctx.property.name.toLatin1().constData());
++ } else if (QQuick3DInstanceList *instancingList = qobject_cast<QQuick3DInstanceList *>(*foundIt)) {
++ qobject_cast<QQuick3DModel *>(ctx.property.target)->setInstancing(instancingList);
++ if (ctx.dbgprint)
++ printf("Setting instance list on model\n");
++ }
++ } else if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
++ if (QQuick3DEffect *effect = qobject_cast<QQuick3DEffect *>(*foundIt)) {
++ auto effects = qobject_cast<QQuick3DSceneEnvironment *>(ctx.property.target)->effects();
++ // Since we are initializing this for the first time, make sure we clean out any inherited data!
++ if (ctx.property.memberState == Context::Property::Uninitialized) {
++ if (ctx.dbgprint)
++ printf("Clearing inherited effects\n");
++ effects.clear(&effects);
++ ctx.property.memberState = Context::Property::Initialized;
++ }
++ effects.append(&effects, effect);
++ if (ctx.dbgprint)
++ printf("Appending effect to \'%s\'\n", ctx.property.name.toLatin1().constData());
++ }
++ } else if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
++ if (QQuick3DShaderUtilsShader *shader = qobject_cast<QQuick3DShaderUtilsShader *>(*foundIt)) {
++ auto shaders = qobject_cast<QQuick3DShaderUtilsRenderPass *>(ctx.property.target)->shaders();
++ // Since we are initializing this for the first time, make sure we clean out any inherited data!
++ if (ctx.property.memberState == Context::Property::Uninitialized) {
++ if (ctx.dbgprint)
++ printf("Clearing inherited shaders\n");
++ shaders.clear(&shaders);
++ ctx.property.memberState = Context::Property::Initialized;
++ }
++ shaders.append(&shaders, shader);
++ if (ctx.dbgprint)
++ printf("Appending shader to \'%s\'\n", ctx.property.name.toLatin1().constData());
++ }
++ } else if (ctx.property.targetType == TypeInfo<QQuick3DInstanceList>::typeId()) {
++ if (QQuick3DInstanceListEntry *listEntry = qobject_cast<QQuick3DInstanceListEntry *>(*foundIt)) {
++ auto instances = qobject_cast<QQuick3DInstanceList *>(ctx.property.target)->instances();
++ // Since we are initializing this for the first time, make sure we clean out any inherited data!
++ if (ctx.property.memberState == Context::Property::Uninitialized) {
++ if (ctx.dbgprint)
++ printf("Clearing inherited instances\n");
++ instances.clear(&instances);
++ ctx.property.memberState = Context::Property::Initialized;
++ }
++ instances.append(&instances, listEntry);
++ if (ctx.dbgprint)
++ printf("Appending instance entry to %s\n", ctx.property.name.toLatin1().constData());
++ } else if (QQuick3DInstanceList *instancingList = qobject_cast<QQuick3DInstanceList *>(*foundIt)) {
++ qobject_cast<QQuick3DModel *>(ctx.property.target)->setInstancing(instancingList);
++ if (ctx.dbgprint)
++ printf("Setting instance list on model\n");
++ }
++ } else if (ctx.dbgprint) {
++ printf("Unhandled binding: %s\n", idExpr.name.toLatin1().constData());
++ }
++ } else {
++ // If no item with 'this' id was found, then that id is for 'this' object (if this not the case, then something is broken, e.g., a ref to an unknown item).
++ // NOTE: This can be a problem in the future and we might need to add some more guards, but for now it just won't generate the correct shader(s).
++ ctx.identifierMap.insert(idExpr.name, ctx.property.target);
++ }
++ }
++ }
++
++ static void visit(const QQmlJS::AST::StringLiteral &stringLiteral, Context &ctx, int &ret)
++ {
++ Q_UNUSED(ret);
++ if (ctx.dbgprint)
++ printf("-> StringLiteral: \"%s\"\n", stringLiteral.value.toLocal8Bit().constData());
++
++ if (ctx.property.target) {
++ const auto &name = ctx.property.name;
++ const auto v = fromString(stringLiteral.value, ctx);
++ if (v.isValid()) {
++ const bool b = ctx.property.target->setProperty(name.toLatin1(), v);
++ if (b && ctx.dbgprint)
++ printf("Property %s updated!\n", name.toLatin1().constData());
++ }
++ }
++ }
++
++ static void visit(const QQmlJS::AST::NumericLiteral &numericLiteral, Context &ctx, int &ret)
++ {
++ Q_UNUSED(ret);
++ if (ctx.dbgprint)
++ printf("-> NumericLiteral: %f\n", numericLiteral.value);
++
++ if (ctx.property.target) {
++ auto target = ctx.property.target;
++ const auto &name = ctx.property.name;
++ target->setProperty(name.toLatin1(), QVariant::fromValue(numericLiteral.value));
++ }
++ }
++
++ static void visit(const QQmlJS::AST::FieldMemberExpression &fieldMemberExpression, Context &ctx, int &ret)
++ {
++ using namespace QQmlJS::AST;
++
++ Q_UNUSED(ret);
++ if (ctx.dbgprint)
++ printf("-> FieldMemberExpression: %s\n", fieldMemberExpression.name.toLocal8Bit().constData());
++
++ if (ctx.property.target) {
++ const auto &name = ctx.property.name;
++ const auto v = fromString(fieldMemberExpression.name, ctx);
++ if (v.isValid())
++ ctx.property.target->setProperty(name.toLatin1(), v);
++ }
++ }
++
++ static void visit(const QQmlJS::AST::ArrayPattern &arrayPattern, Context &ctx, int &ret)
++ {
++ Q_UNUSED(ret);
++ if (ctx.dbgprint)
++ printf("-> [ ");
++
++ using namespace QQmlJS::AST;
++ using PatternElementItem = PatternElementList;
++ using PatternElementListView = InvasiveListView<PatternElementItem>;
++
++ PatternElementListView elements(*arrayPattern.elements);
++ for (auto &element : elements) {
++ auto patternElement = element.element;
++ if (patternElement->type == PatternElement::Literal) {
++ if (patternElement->initializer && patternElement->initializer->kind == Node::Kind_IdentifierExpression) {
++ const auto &identExpression = static_cast<const IdentifierExpression &>(*patternElement->initializer);
++ visit(identExpression, ctx, ret);
++ }
++ } else if (ctx.dbgprint) {
++ printf("Unahandled(%d), ", patternElement->type);
++ }
++ }
++
++ if (ctx.dbgprint)
++ printf(" ]\n");
++
++ }
++
++ static void visit(const QQmlJS::AST::CallExpression &callExpression, Context &ctx, int &ret)
++ {
++ using namespace QQmlJS::AST;
++
++ Q_UNUSED(ret);
++ Q_UNUSED(callExpression);
++ if (ctx.dbgprint)
++ printf("-> Call(%d)\n", callExpression.base->kind);
++
++ (ctx.interceptCallExpr && ctx.interceptCallExpr(callExpression, ctx, ret));
++ }
++
++ static void visit(const QQmlJS::AST::UiObjectMember &member, Context &ctx, int &ret)
++ {
++ using namespace QQmlJS::AST;
++
++ if (member.kind == Node::Kind_UiObjectBinding)
++ visit(static_cast<const UiObjectBinding &>(member), ctx, ret);
++ else if (ctx.dbgprint)
++ printf("Unhandled member (%d)\n", member.kind);
++ }
++
++ static void visit(const QQmlJS::AST::UiObjectMemberList &memberList, Context &ctx, int &ret)
++ {
++ using namespace QQmlJS::AST;
++ using ObjectMemberItem = UiObjectMemberList;
++ using ObjectMembers = InvasiveListView<ObjectMemberItem>;
++
++ const auto oldEvalType = ctx.property.memberState;
++
++ ObjectMembers objectMembers(memberList);
++ for (const auto &member : objectMembers) {
++ if (member.member) {
++ if (member.member->kind == Node::Kind_UiScriptBinding) {
++ ctx.property.memberState = Context::Property::MemberState::Uninitialized;
++ const auto &scriptBinding = static_cast<const UiScriptBinding &>(*member.member);
++ visit(scriptBinding, ctx, ret);
++ } else if (member.member->kind == Node::Kind_UiArrayBinding) {
++ ctx.property.memberState = Context::Property::MemberState::Uninitialized;
++ const auto &arrayBinding = static_cast<const UiArrayBinding &>(*member.member);
++ visit(arrayBinding, ctx, ret);
++ } else if (member.member->kind == Node::Kind_UiObjectDefinition) {
++ const auto &objectDef = static_cast<const UiObjectDefinition &>(*member.member);
++ visit(objectDef, ctx, ret);
++ } else if (member.member->kind == Node::Kind_UiObjectBinding) {
++ ctx.property.memberState = Context::Property::MemberState::Uninitialized;
++ const auto &objBinding = static_cast<const UiObjectBinding &>(*member.member);
++ visit(objBinding, ctx, ret);
++ } else if (member.member->kind == Node::Kind_UiPublicMember) {
++ ctx.property.memberState = Context::Property::MemberState::Uninitialized;
++ const auto &pubMember = static_cast<const UiPublicMember &>(*member.member);
++ visit(pubMember, ctx, ret);
++ } else {
++ if (ctx.dbgprint)
++ printf("<member %d>\n", member.member->kind);
++ }
++ }
++ }
++
++ ctx.property.memberState = oldEvalType;
++ }
++
++ private:
++ Visitors() = delete;
++ Q_DISABLE_COPY(Visitors);
++ };
++
++ template <typename O, typename T>
++ T *buildType(const O &obj, Context &ctx, int &ret, const T *base = nullptr)
++ {
++ // swap -> reentrancy
++ Context::Property property;
++ qSwap(property, ctx.property);
++ Q_ASSERT(ctx.property.target == nullptr);
++
++ T *instance = nullptr;
++
++ if (ctx.dbgprint)
++ printf("Building %s!\n", TypeInfo<T>::qmlTypeName());
++
++ if (obj.initializer) {
++ instance = new T;
++ if (base)
++ cloneProperties(*instance, *base);
++
++ if (obj.initializer) {
++ ctx.property.target = instance;
++ ctx.property.targetType = TypeInfo<T>::typeId();
++ Visitors::visit(*obj.initializer, ctx, ret);
++ }
++ }
++
++ // swap back
++ qSwap(property, ctx.property);
++
++ return instance;
++ }
++
++ static QQuick3DAbstractLight *buildLight(const QQmlJS::AST::UiObjectDefinition &def,
++ Context &ctx,
++ int &ret,
++ int lightType,
++ const QQuick3DAbstractLight *base = nullptr)
++ {
++ if (lightType == TypeInfo<QQuick3DDirectionalLight>::typeId())
++ return buildType(def, ctx, ret, qobject_cast<const QQuick3DDirectionalLight *>(base));
++ if (lightType == TypeInfo<QQuick3DPointLight>::typeId())
++ return buildType(def, ctx, ret, qobject_cast<const QQuick3DPointLight *>(base));
++ if (lightType == TypeInfo<QQuick3DSpotLight>::typeId())
++ return buildType(def, ctx, ret, qobject_cast<const QQuick3DSpotLight *>(base));
++ return nullptr;
++ }
++
++
++ template <typename T>
++ static void updateProperty(Context &ctx, T type, QStringView propName)
++ {
++ if (ctx.property.target) {
++ if (ctx.dbgprint)
++ printf("Updating property %s\n", propName.toLatin1().constData());
++ const auto &target = ctx.property.target;
++ if (ctx.property.memberState == Context::Property::Uninitialized) {
++ target->setProperty(propName.toLatin1().constData(), QVariant::fromValue(type));
++ ctx.property.memberState = Context::Property::Initialized;
++ } else {
++ const int idx = target->metaObject()->indexOfProperty(propName.toLatin1().constData());
++ if (idx != -1) {
++ auto prop = target->metaObject()->property(idx);
++ prop.write(target, QVariant::fromValue(type));
++ }
++ }
++ }
++ }
++
++ static bool interceptObjectBinding(const QQmlJS::AST::UiObjectBinding &objectBinding, Context &ctx, int &ret)
++ {
++ if (ctx.dbgprint)
++ printf("Intercepted object binding!\n");
++
++ bool handled = false;
++
++ const auto &typeName = objectBinding.qualifiedTypeNameId->name.toString();
++ const auto &propName = objectBinding.qualifiedId->name;
++
++ int type = -1;
++
++ // Base type?
++ const auto typeIt = s_typeMap->constFind(typeName);
++ if (typeIt != s_typeMap->cend())
++ type = *typeIt;
++
++ // Component?
++ auto &components = ctx.components;
++ const auto compIt = (type == -1) ? components.constFind(typeName) : components.cend();
++ QObject *base = nullptr;
++ if (compIt != components.cend()) {
++ type = compIt->type;
++ base = compIt->ptr;
++ }
++
++ if (type != -1) {
++ if (ctx.dbgprint)
++ printf("Resolving: \'%s\'\n", qPrintable(typeName));
++
++ if (type == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
++ if (ctx.property.targetType == TypeInfo<QQuick3DViewport>::typeId()) {
++ if (auto environment = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DSceneEnvironment *>(base))) {
++ auto viewport = qobject_cast<QQuick3DViewport *>(ctx.property.target);
++ Q_ASSERT(viewport);
++ viewport->setEnvironment(environment);
++ handled = true;
++ }
++ }
++ } else if (type == TypeInfo<QQuick3DTexture>::typeId()) {
++ if (auto tex = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DTexture *>(base))) {
++ updateProperty(ctx, tex, propName);
++ ctx.sceneData.textures.append(tex);
++ }
++ handled = true;
++ } else if (type == TypeInfo<QQuick3DShaderUtilsTextureInput>::typeId()) {
++ auto texInput = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsTextureInput *>(base));
++ if (texInput && texInput->texture()) {
++ updateProperty(ctx, texInput, propName);
++ ctx.sceneData.textures.append(texInput->texture());
++ }
++ handled = true;
++ } else if (type == TypeInfo<QQuick3DEffect>::typeId()) {
++ if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
++ if (auto effect = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DEffect *>(base))) {
++ auto sceneEnvironment = qobject_cast<QQuick3DSceneEnvironment *>(ctx.property.target);
++ Q_ASSERT(sceneEnvironment);
++ auto effects = sceneEnvironment->effects();
++ effects.append(&effects, effect);
++ handled = true;
++ }
++ }
++ } else if (type == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
++ if (ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) {
++ if (auto pass = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsRenderPass *>(base))) {
++ auto effect = qobject_cast<QQuick3DEffect *>(ctx.property.target);
++ Q_ASSERT(effect);
++ auto passes = effect->passes();
++ passes.append(&passes, pass);
++ handled = true;
++ }
++ }
++ } else if (type == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) {
++ if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
++ if (auto shader = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsShader *>(base))) {
++ auto pass = qobject_cast<QQuick3DShaderUtilsRenderPass *>(ctx.property.target);
++ Q_ASSERT(pass);
++ auto shaders = pass->shaders();
++ shaders.append(&shaders, shader);
++ handled = true;
++ }
++ }
++ } else if (type == TypeInfo<QQuick3DDefaultMaterial>::typeId()) {
++ if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
++ if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DDefaultMaterial *>(base))) {
++ auto model = qobject_cast<QQuick3DModel *>(ctx.property.target);
++ Q_ASSERT(model);
++ auto materials = model->materials();
++ materials.append(&materials, mat);
++ handled = true;
++ if (ctx.dbgprint)
++ printf("Appending material to %s\n", ctx.property.name.toLatin1().constData());
++ }
++ }
++ } else if (type == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
++ if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
++ if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DPrincipledMaterial *>(base))) {
++ auto model = qobject_cast<QQuick3DModel *>(ctx.property.target);
++ Q_ASSERT(model);
++ auto materials = model->materials();
++ materials.append(&materials, mat);
++ handled = true;
++ }
++ }
++ } else if (type == TypeInfo<QQuick3DCustomMaterial>::typeId()) {
++ if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
++ if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DCustomMaterial *>(base))) {
++ auto model = qobject_cast<QQuick3DModel *>(ctx.property.target);
++ Q_ASSERT(model);
++ auto materials = model->materials();
++ materials.append(&materials, mat);
++ handled = true;
++ }
++ }
++ } else if (ctx.dbgprint) {
++ printf("Unhandled type\n");
++ }
++ }
++
++ return handled;
++ }
++
++ static bool interceptObjectDef(const QQmlJS::AST::UiObjectDefinition &def, Context &ctx, int &ret)
++ {
++ const auto &typeName = def.qualifiedTypeNameId->name.toString();
++
++ if (ctx.dbgprint)
++ printf("Intercepted object definition (\'%s\')!\n", typeName.toLatin1().constData());
++
++ QString componentName;
++ int type = -1;
++ bool doRegisterComponent = false;
++
++ // Base type?
++ const auto typeIt = s_typeMap->constFind(typeName);
++ if (typeIt != s_typeMap->cend())
++ type = *typeIt;
++
++ // Component?
++ auto &components = ctx.components;
++ const auto compIt = (type == -1) ? components.constFind(typeName) : components.cend();
++ if (compIt != components.cend())
++ type = compIt->type;
++
++ // If this is a new component register it
++ if (ctx.type == Context::Type::Component && ctx.property.target == nullptr && type != -1) {
++ const auto &fileName = ctx.currentFileInfo.fileName();
++ componentName = fileName.left(fileName.size() - 4);
++ doRegisterComponent = !componentName.isEmpty();
++ }
++
++ const auto registerComponent = [&ctx, &components, &componentName](Context::Component component) {
++ if (ctx.dbgprint)
++ printf("Registering component \'%s\'\n", qPrintable(componentName));
++ components.insert(componentName, component);
++ };
++
++ if (type == TypeInfo<QQuick3DViewport>::typeId()) {
++ const QQuick3DViewport *base = (compIt != components.cend()) ? qobject_cast<QQuick3DViewport *>(compIt->ptr) : nullptr;
++ if (QQuick3DViewport *viewport = buildType(def, ctx, ret, base)) {
++ // If this is a component we'll store it for lookups later.
++ if (doRegisterComponent)
++ registerComponent({ viewport, type });
++ // Only one viewport supported atm (see SceneEnvironment case as well).
++ if (!ctx.sceneData.viewport)
++ ctx.sceneData.viewport = viewport;
++ }
++ } else if (type == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
++ const QQuick3DSceneEnvironment *base = (compIt != components.cend()) ? qobject_cast<QQuick3DSceneEnvironment *>(compIt->ptr) : nullptr;
++ if (QQuick3DSceneEnvironment *sceneEnv = buildType(def, ctx, ret, base)) {
++ // If this is a component we'll store it for lookups later.
++ if (doRegisterComponent)
++ registerComponent({ sceneEnv, type });
++
++ if (ctx.sceneData.viewport)
++ ctx.sceneData.viewport->setEnvironment(sceneEnv);
++ }
++ } else if (type == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
++ const QQuick3DPrincipledMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DPrincipledMaterial *>(compIt->ptr) : nullptr;
++ if (QQuick3DPrincipledMaterial *mat = buildType(def, ctx, ret, base)) {
++ // If this is a component we'll store it for lookups later.
++ if (doRegisterComponent)
++ registerComponent({ mat, type });
++
++ if (ctx.property.target) {
++ if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
++ auto materials = qobject_cast<QQuick3DModel *>(ctx.property.target)->materials();
++ if (ctx.property.memberState == Context::Property::Uninitialized) {
++ if (ctx.dbgprint)
++ printf("Clearing inherited materials\n");
++ materials.clear(&materials);
++ ctx.property.memberState = Context::Property::Initialized;
++ }
++ materials.append(&materials, mat);
++ if (ctx.dbgprint)
++ printf("Appending material to %s\n", ctx.property.name.toLatin1().constData());
++ }
++ }
++
++ // At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list
++ ctx.sceneData.materials.push_back(mat);
++ }
++ } else if (type == TypeInfo<QQuick3DDefaultMaterial>::typeId()) {
++ const QQuick3DDefaultMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DDefaultMaterial *>(compIt->ptr) : nullptr;
++ if (QQuick3DDefaultMaterial *mat = buildType(def, ctx, ret, base)) {
++ // If this is a component we'll store it for lookups later.
++ if (doRegisterComponent)
++ registerComponent({ mat, type });
++
++ if (ctx.property.target) {
++ if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
++ auto materials = qobject_cast<QQuick3DModel *>(ctx.property.target)->materials();
++ if (ctx.property.memberState == Context::Property::Uninitialized) {
++ if (ctx.dbgprint)
++ printf("Clearing inherited materials\n");
++ materials.clear(&materials);
++ ctx.property.memberState = Context::Property::Initialized;
++ }
++ materials.append(&materials, mat);
++ if (ctx.dbgprint)
++ printf("Appending material to %s\n", ctx.property.name.toLatin1().constData());
++ }
++ }
++
++ // At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list
++ ctx.sceneData.materials.push_back(mat);
++ }
++ } else if (type == TypeInfo<QQuick3DCustomMaterial>::typeId()) {
++ const QQuick3DCustomMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DCustomMaterial *>(compIt->ptr) : nullptr;
++ if (QQuick3DCustomMaterial *mat = buildType(def, ctx, ret, base)) {
++ // If this is a component we'll store it for lookups later.
++ if (doRegisterComponent)
++ registerComponent({ mat, type });
++
++ if (ctx.property.target) {
++ if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
++ auto materials = qobject_cast<QQuick3DModel *>(ctx.property.target)->materials();
++ if (ctx.property.memberState == Context::Property::Uninitialized) {
++ if (ctx.dbgprint)
++ printf("Clearing inherited materials\n");
++ materials.clear(&materials);
++ ctx.property.memberState = Context::Property::Initialized;
++ }
++ materials.append(&materials, mat);
++ if (ctx.dbgprint)
++ printf("Appending material to %s\n", ctx.property.name.toLatin1().constData());
++ }
++ }
++
++ // At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list
++ ctx.sceneData.materials.push_back(mat);
++ }
++ } else if (type == TypeInfo<QQuick3DEffect>::typeId()) {
++ const QQuick3DEffect *base = (compIt != components.cend()) ? qobject_cast<QQuick3DEffect *>(compIt->ptr) : nullptr;
++ if (QQuick3DEffect *effect = buildType(def, ctx, ret, base)) {
++ // If this is a component we'll store it for lookups later.
++ if (doRegisterComponent)
++ registerComponent({ effect, type });
++
++ if (ctx.property.target) {
++ if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
++ auto effects = qobject_cast<QQuick3DSceneEnvironment *>(ctx.property.target)->effects();
++ if (ctx.property.memberState == Context::Property::Uninitialized) {
++ if (ctx.dbgprint)
++ printf("Clearing inherited effects\n");
++ effects.clear(&effects);
++ ctx.property.memberState = Context::Property::Initialized;
++ }
++ effects.append(&effects, effect);
++ if (ctx.dbgprint)
++ printf("Appending effect to %s\n", ctx.property.name.toLatin1().constData());
++ }
++ }
++
++ // At this point we don't know if this effect is going to be referenced somewhere else, so keep it in the list
++ ctx.sceneData.effects.push_back(effect);
++ }
++ } else if (type == TypeInfo<QQuick3DDirectionalLight>::typeId() || type == TypeInfo<QQuick3DPointLight>::typeId() || type == TypeInfo<QQuick3DSpotLight>::typeId()) {
++ const QQuick3DAbstractLight *base = (compIt != components.cend()) ? qobject_cast<QQuick3DAbstractLight *>(compIt->ptr) : nullptr;
++ if (QQuick3DAbstractLight *light = buildLight(def, ctx, ret, type, base)) {
++ // If this is a component we'll store it for lookups later.
++ if (doRegisterComponent)
++ registerComponent({ light, type });
++
++ ctx.sceneData.lights.push_back(light);
++ }
++ } else if (type == TypeInfo<QQuick3DTexture>::typeId()) {
++ const QQuick3DTexture *base = (compIt != components.cend()) ? qobject_cast<QQuick3DTexture *>(compIt->ptr) : nullptr;
++ if (QQuick3DTexture *tex = buildType(def, ctx, ret, base)) {
++ // If this is a component we'll store it for lookups later.
++ if (doRegisterComponent)
++ registerComponent({ tex, type });
++
++ ctx.sceneData.textures.push_back(tex);
++ }
++ } else if (type == TypeInfo<QQuick3DModel>::typeId()) {
++ const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DModel *>(compIt->ptr) : nullptr;
++ if (auto *model = buildType(def, ctx, ret, base)) {
++ // If this is a component we'll store it for lookups later.
++ if (doRegisterComponent)
++ registerComponent({ model, type });
++
++ ctx.sceneData.models.push_back(model);
++ }
++ } else if (type == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) {
++ const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DShaderUtilsShader *>(compIt->ptr) : nullptr;
++ if (auto *shader = buildType(def, ctx, ret, base)) {
++ // If this is a component we'll store it for lookups later.
++ if (doRegisterComponent)
++ registerComponent({ shader, type });
++ if (ctx.property.target) {
++ if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
++ auto shaders = qobject_cast<QQuick3DShaderUtilsRenderPass *>(ctx.property.target)->shaders();
++ if (ctx.property.memberState == Context::Property::Uninitialized) {
++ if (ctx.dbgprint)
++ printf("Clearing inherited shaders\n");
++ shaders.clear(&shaders);
++ ctx.property.memberState = Context::Property::Initialized;
++ }
++ shaders.append(&shaders, shader);
++ if (ctx.dbgprint)
++ printf("Appending shader to %s\n", ctx.property.name.toLatin1().constData());
++ }
++ }
++
++ ctx.sceneData.shaders.push_back(shader);
++ }
++ } else if (type == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
++ const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DShaderUtilsRenderPass *>(compIt->ptr) : nullptr;
++ if (auto *pass = buildType(def, ctx, ret, base)) {
++ // If this is a component we'll store it for lookups later.
++ if (doRegisterComponent)
++ registerComponent({ pass, type });
++ if (ctx.property.target) {
++ if (ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) {
++ auto passes = qobject_cast<QQuick3DEffect *>(ctx.property.target)->passes();
++ if (ctx.property.memberState == Context::Property::Uninitialized) {
++ if (ctx.dbgprint)
++ printf("Clearing inherited passes\n");
++ passes.clear(&passes);
++ ctx.property.memberState = Context::Property::Initialized;
++ }
++ passes.append(&passes, pass);
++ if (ctx.dbgprint)
++ printf("Appending pass to %s\n", ctx.property.name.toLatin1().constData());
++ }
++ }
++ }
++ } else if (type == TypeInfo<QQuick3DInstanceList>::typeId()) {
++ const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DInstanceList *>(compIt->ptr) : nullptr;
++ if (auto *instanceList = buildType(def, ctx, ret, base)) {
++ // If this is a component we'll store it for lookups later.
++ if (doRegisterComponent)
++ registerComponent({ instanceList, type });
++ if (ctx.property.target) {
++ if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
++ qobject_cast<QQuick3DModel *>(ctx.property.target)->setInstancing(instanceList);
++ if (ctx.dbgprint)
++ printf("Setting instance list on %s\n", ctx.property.name.toLatin1().constData());
++ }
++ }
++ }
++ } else if (type == TypeInfo<QQuick3DInstanceListEntry>::typeId()) {
++ const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DInstanceListEntry *>(compIt->ptr) : nullptr;
++ if (auto *instanceListEntry = buildType(def, ctx, ret, base)) {
++ // If this is a component we'll store it for lookups later.
++ if (doRegisterComponent)
++ registerComponent({ instanceListEntry, type });
++ if (ctx.property.target) {
++ if (ctx.property.targetType == TypeInfo<QQuick3DInstanceList>::typeId()) {
++ auto instances = qobject_cast<QQuick3DInstanceList *>(ctx.property.target)->instances();
++ instances.append(&instances, instanceListEntry);
++ if (ctx.dbgprint)
++ printf("Appending instance list entry to %s\n", ctx.property.name.toLatin1().constData());
++ }
++ }
++ }
++ } else {
++ if (ctx.dbgprint)
++ printf("Object def for \'%s\' was not handled\n", ctx.property.name.toLatin1().constData());
++ return false;
++ }
++
++ return true;
++ }
++
++ static bool interceptPublicMember(const QQmlJS::AST::UiPublicMember &member, Context &ctx, int &ret)
++ {
++ Q_UNUSED(ret);
++ using namespace QQmlJS::AST;
++
++ if (ctx.dbgprint)
++ printf("Intercepted public member!\n");
++
++ if (member.statement && member.statement->kind == Node::Kind_ExpressionStatement) {
++ if ((ctx.property.targetType == TypeInfo<QQuick3DCustomMaterial>::typeId() || ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) && member.memberType) {
++ // For custom materials we have properties that are user provided, so we'll
++ // need to add these to the objects properties (we add these here to be able
++ // to piggyback on the existing type matching code)
++ if (member.memberType->name == u"real") {
++ ctx.property.type = QMetaType::Double;
++ } else if (member.memberType->name == u"bool") {
++ ctx.property.type = QMetaType::Bool;
++ } else if (member.memberType->name == u"int") {
++ ctx.property.type = QMetaType::Int;
++ } else if (member.memberType->name == u"size") {
++ ctx.property.type = QMetaType::QSizeF;
++ } else if (member.memberType->name == u"rect") {
++ ctx.property.type = QMetaType::QRectF;
++ } else if (member.memberType->name == u"point") {
++ ctx.property.type = QMetaType::QPointF;
++ } else if (member.memberType->name == u"color") {
++ ctx.property.type = QMetaType::QColor;
++ } else if (member.memberType->name.startsWith(u"vector")) {
++ if (member.memberType->name.endsWith(u"2d")) {
++ ctx.property.type = QMetaType::QVector2D;
++ } else if (member.memberType->name.endsWith(u"3d")) {
++ ctx.property.type = QMetaType::QVector3D;
++ } else if (member.memberType->name.endsWith(u"4d")) {
++ ctx.property.type = QMetaType::QVector4D;
++ }
++ } else if (member.memberType->name == u"matrix4x4") {
++ ctx.property.type = QMetaType::QMatrix4x4;;
++ } else if (member.memberType->name == u"quaternion") {
++ ctx.property.type = QMetaType::QQuaternion;
++ } else if (member.memberType->name == u"var") {
++ ctx.property.type = QMetaType::QVariant;
++ }
++ }
++ }
++
++ return false;
++ }
++
++ static bool interceptCallExpression(const QQmlJS::AST::CallExpression &callExpression, Context &ctx, int &ret)
++ {
++ Q_UNUSED(ret);
++ using namespace QQmlJS::AST;
++ using namespace BuiltinHelpers;
++
++ if (ctx.dbgprint)
++ printf("Intercepted call expression!\n");
++
++ const bool ok = (ctx.property.target && !ctx.property.name.isEmpty());
++ if (callExpression.base && ok) {
++ if (callExpression.base->kind == Node::Kind_FieldMemberExpression) {
++ const auto &fieldMemberExpression = static_cast<const FieldMemberExpression &>(*callExpression.base);
++ if (fieldMemberExpression.base) {
++ if (fieldMemberExpression.base->kind == Node::Kind_IdentifierExpression) {
++ const auto &identExpr = static_cast<const IdentifierExpression &>(*fieldMemberExpression.base);
++ if (identExpr.name == u"Qt") {
++ bool ok = false;
++ QVariant v;
++ if (fieldMemberExpression.name == u"point") {
++ const auto point = toPoint(ArgumentListView(*callExpression.arguments), &ok);
++ if (ctx.dbgprint)
++ printf("Qt.point(%f, %f)\n", point.x(), point.y());
++ setProperty(ctx.property, point);
++ } else if (fieldMemberExpression.name == u"size") {
++ const auto size = toSize(ArgumentListView(*callExpression.arguments), &ok);
++ if (ctx.dbgprint)
++ printf("Qt.size(%f, %f)\n", size.width(), size.height());
++ setProperty(ctx.property, size);
++ } else if (fieldMemberExpression.name == u"rect") {
++ const auto rect = toRect(ArgumentListView(*callExpression.arguments), &ok);
++ if (ctx.dbgprint)
++ printf("Qt.rect(%f, %f, %f, %f)\n", rect.x(), rect.y(), rect.width(), rect.height());
++ setProperty(ctx.property, rect);
++ } else if (fieldMemberExpression.name.startsWith(u"vector")) {
++ if (fieldMemberExpression.name.endsWith(u"2d")) {
++ const auto vec2 = toVec<QVector2D>(ArgumentListView(*callExpression.arguments), &ok);
++ if (ctx.dbgprint)
++ printf("Qt.vector2d(%f, %f)\n", vec2.x(), vec2.y());
++ setProperty(ctx.property, vec2);
++ } else if (fieldMemberExpression.name.endsWith(u"3d")) {
++ const auto vec3 = toVec<QVector3D>(ArgumentListView(*callExpression.arguments), &ok);
++ if (ctx.dbgprint)
++ printf("Qt.vector3d(%f, %f, %f)\n", vec3.x(), vec3.y(), vec3.z());
++ setProperty(ctx.property, vec3);
++ } else if (fieldMemberExpression.name.endsWith(u"4d")) {
++ const auto vec4 = toVec<QVector4D>(ArgumentListView(*callExpression.arguments), &ok);
++ if (ctx.dbgprint)
++ printf("Qt.vector4d(%f, %f, %f, %f)\n", vec4.x(), vec4.y(), vec4.z(), vec4.w());
++ setProperty(ctx.property, vec4);
++ }
++ } else if (fieldMemberExpression.name == u"matrix4x4") {
++ const auto mat44 = toMat44(ArgumentListView(*callExpression.arguments), &ok);
++ if (ctx.dbgprint)
++ printf("Qt.matrix4x4(%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f)\n",
++ mat44(0, 0), mat44(0, 1), mat44(0, 2), mat44(0, 3),
++ mat44(1, 0), mat44(1, 1), mat44(1, 2), mat44(1, 3),
++ mat44(2, 0), mat44(2, 1), mat44(2, 2), mat44(2, 3),
++ mat44(3, 0), mat44(3, 1), mat44(3, 2), mat44(3, 3));
++ setProperty(ctx.property, mat44);
++ } else if (fieldMemberExpression.name == u"quaternion") {
++ const auto quat = toQuaternion(ArgumentListView(*callExpression.arguments), &ok);
++ if (ctx.dbgprint)
++ printf("Qt.quaternion(%f, %f, %f, %f)\n", quat.scalar(), quat.x(), quat.y(), quat.z());
++ setProperty(ctx.property, quat);
++ } else if (fieldMemberExpression.name == u"rgba") {
++ const auto vec4 = toVec<QVector4D>(ArgumentListView(*callExpression.arguments), &ok);
++ if (ok) {
++ QColor color = QColor::fromRgbF(vec4.x(), vec4.y(), vec4.z(), vec4.w());
++ if (ctx.dbgprint)
++ printf("Qt.rgba(%f, %f, %f, %f)\n", color.redF(), color.greenF(), color.blueF(), color.alphaF());
++ setProperty(ctx.property, color);
++ }
++ }
++ if (ok && v.isValid() && ctx.property.target)
++ ctx.property.target->setProperty(ctx.property.name.toLatin1().constData(), v);
++ }
++ }
++ }
++ }
++ }
++
++ return false;
++ }
++
++ static int parseQmlData(const QByteArray &code, Context &ctx)
++ {
++ Q_ASSERT(ctx.engine && ctx.engine->lexer());
++ ctx.identifierMap.clear(); // not visible outside the scope of this "code"
++ if (ctx.dbgprint)
++ printf("Parsing %s\n", qPrintable(ctx.currentFileInfo.filePath()));
++ int ret = 0;
++ ctx.engine->lexer()->setCode(QString::fromUtf8(code), 1, true);
++ QQmlJS::Parser parser(ctx.engine);
++ const bool ok = parser.parse();
++ if (ok) {
++ const auto program = parser.ast();
++ if (program)
++ Visitors::visit(*program, ctx, ret);
++ } else {
++ ret = -1;
++ qWarning("Parsing failed due to %s in %s:%d%d", qPrintable(parser.errorMessage()), qPrintable(ctx.currentFileInfo.fileName()), parser.errorLineNumber(), parser.errorColumnNumber());
++ }
++
++ return ret;
++ }
++
++ int MaterialParser::parseQmlData(const QByteArray &code, const QString &fileName, MaterialParser::SceneData &sceneData)
++ {
++ // set initial type map
++ *s_typeMap = baseTypeMap();
++
++ QQmlJS::Engine engine;
++ QQmlJS::Lexer lexer(&engine);
++
++ Context ctx;
++ ctx.engine = &engine;
++ ctx.interceptODFunc = &interceptObjectDef;
++ ctx.interceptOBFunc = &interceptObjectBinding;
++ ctx.interceptPMFunc = &interceptPublicMember;
++ ctx.interceptCallExpr = &interceptCallExpression;
++ ctx.currentFileInfo = QFileInfo(fileName);
++ ctx.type = Context::Type::Component;
++
++ const int ret = ::parseQmlData(code, ctx);
++ sceneData = std::move(ctx.sceneData);
++
++ return ret;
++ }
++
++ int MaterialParser::parseQmlFiles(const QVector<QString> &filePaths, const QDir &sourceDir, SceneData &sceneData, bool verboseOutput)
++ {
++ // set initial type map
++ *s_typeMap = baseTypeMap();
++
++ int ret = 0;
++
++ if (filePaths.isEmpty()) {
++ qWarning("No input files");
++ return ret;
++ }
++
++ QQmlJS::Engine engine;
++ QQmlJS::Lexer lexer(&engine);
++
++ Context ctx;
++ ctx.dbgprint = verboseOutput;
++ ctx.engine = &engine;
++ ctx.interceptODFunc = &interceptObjectDef;
++ ctx.interceptOBFunc = &interceptObjectBinding;
++ ctx.interceptPMFunc = &interceptPublicMember;
++ ctx.interceptCallExpr = &interceptCallExpression;
++ ctx.workingDir = sourceDir;
++
++ QVector<QString> deferredOther;
++ QVector<QString> deferredComponets;
++
++ const QString sourcePath = sourceDir.canonicalPath() + QDir::separator();
++
++ const bool isMultifile = filePaths.size() != 1;
++
++ // Go through and find the material components first
++ for (const auto &v : filePaths) {
++ QFileInfo ¤tFileInfo = ctx.currentFileInfo;
++ if (!QFileInfo(v).isAbsolute())
++ currentFileInfo.setFile(sourcePath + v);
++ else
++ currentFileInfo.setFile(v);
++ const bool maybeComponent = currentFileInfo.fileName().at(0).isUpper();
++ if (currentFileInfo.isFile() && currentFileInfo.suffix() == getQmlFileExtension()) {
++ const QString filePath = currentFileInfo.canonicalFilePath();
++ if (isMultifile && maybeComponent) {
++ QFile f(filePath);
++ if (!f.open(QFile::ReadOnly)) {
++ qWarning("Could not open file %s for reading!", qPrintable(filePath));
++ return -1;
++ }
++
++ const QByteArray code = f.readAll();
++ int idx = code.indexOf('{');
++ if (idx != -1) {
++ const QByteArray section = code.mid(0, idx);
++ QVarLengthArray<const char *, 3> componentTypes { TypeInfo<QQuick3DPrincipledMaterial>::qmlTypeName(),
++ TypeInfo<QQuick3DCustomMaterial>::qmlTypeName(),
++ TypeInfo<QQuick3DDefaultMaterial>::qmlTypeName()};
++ for (const auto compType : std::as_const(componentTypes)) {
++ if ((idx = section.indexOf(compType)) != -1)
++ break;
++ }
++ if (idx != -1) {
++ ctx.type = Context::Type::Component;
++ ret = parseQmlData(code, ctx);
++ if (ret != 0)
++ break;
++ } else {
++ deferredComponets.push_back(filePath);
++ }
++ } else {
++ qWarning("No items found in %s\n", qPrintable(filePath));
++ }
++ } else {
++ deferredOther.push_back(filePath);
++ }
++ } else {
++ qWarning("The file %s is either not a file or has the wrong extension!", qPrintable(v));
++ }
++ }
++
++ const auto parsePaths = [&ctx, &ret](const QVector<QString> &paths, Context::Type type) {
++ ctx.type = type;
++ for (const auto &path : paths) {
++ QFileInfo ¤tFileInfo = ctx.currentFileInfo;
++ currentFileInfo.setFile(path);
++ if (currentFileInfo.isFile() && currentFileInfo.suffix() == getQmlFileExtension()) {
++ const QString filePath = currentFileInfo.canonicalFilePath();
++ QFile f(filePath);
++ if (!f.open(QFile::ReadOnly)) {
++ qWarning("Could not open file %s for reading!", qPrintable(filePath));
++ ret = -1;
++ return;
++ }
++
++ const QByteArray code = f.readAll();
++ ret = parseQmlData(code, ctx);
++ if (ret != 0)
++ break;
++ }
++ }
++ };
++
++ // Other components
++ parsePaths(deferredComponets, Context::Type::Component);
++
++ // Now parse the rest
++ parsePaths(deferredOther, Context::Type::Application);
++
++ sceneData = std::move(ctx.sceneData);
++
++ return ret;
++ }
++
++ QT_END_NAMESPACE