1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "parser.h"
5
6#include <QtCore/qdir.h>
7#include <QtCore/qfileinfo.h>
8#include <QtCore/qstringview.h>
9
10#include <QtQml/qqmllist.h>
11
12// Parsing
13#include <QtQml/private/qqmljsengine_p.h>
14#include <QtQml/private/qqmljslexer_p.h>
15#include <QtQml/private/qqmljsparser_p.h>
16
17#include <QtQuick3D/private/qquick3dobject_p.h>
18#include <QtQuick3D/private/qquick3dviewport_p.h>
19// Scene
20#include <QtQuick3D/private/qquick3dsceneenvironment_p.h>
21// Material(s)
22#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
23#include <QtQuick3D/private/qquick3ddefaultmaterial_p.h>
24#include <QtQuick3D/private/qquick3dcustommaterial_p.h>
25// Lights
26#include <QtQuick3D/private/qquick3dspotlight_p.h>
27#include <QtQuick3D/private/qquick3dpointlight_p.h>
28#include <QtQuick3D/private/qquick3ddirectionallight_p.h>
29
30// Instancing
31#include <QtQuick3D/private/qquick3dinstancing_p.h>
32
33#include <QtQuick3D/private/qquick3dshaderutils_p.h>
34
35#include <QtQuick3DUtils/private/qssginvasivelinkedlist_p.h>
36
37QT_BEGIN_NAMESPACE
38
39template<typename T, T *T::*N = &T::next>
40struct InvasiveListView : protected QSSGInvasiveSingleLinkedList<T, N>
41{
42 using QSSGInvasiveSingleLinkedList<T, N>::begin;
43 using QSSGInvasiveSingleLinkedList<T, N>::end;
44 using QSSGInvasiveSingleLinkedList<T, N>::m_head;
45
46 explicit InvasiveListView(const T &obj) { m_head = &const_cast<T &>(obj); }
47};
48
49template <typename T> struct TypeInfo
50{
51 static constexpr const char *qmlTypeName() { return "Unknown"; }
52 static constexpr int typeId() { return QMetaType::UnknownType; }
53};
54
55#define DECLARE_QQ3D_TYPE(CLASS, TYPE_NAME) \
56template<> struct TypeInfo<CLASS> \
57{ \
58 static constexpr const char *qmlTypeName() { return #TYPE_NAME; } \
59 inline static constexpr int typeId() { return qMetaTypeId<CLASS>(); } \
60 inline static constexpr int qmlListTypeId() { return qMetaTypeId<QQmlListProperty<CLASS>>(); } \
61}
62
63DECLARE_QQ3D_TYPE(QQuick3DViewport, View3D);
64DECLARE_QQ3D_TYPE(QQuick3DSceneEnvironment, SceneEnvironment);
65DECLARE_QQ3D_TYPE(QQuick3DMaterial, Material);
66DECLARE_QQ3D_TYPE(QQuick3DPrincipledMaterial, PrincipledMaterial);
67DECLARE_QQ3D_TYPE(QQuick3DDefaultMaterial, DefaultMaterial);
68DECLARE_QQ3D_TYPE(QQuick3DCustomMaterial, CustomMaterial);
69DECLARE_QQ3D_TYPE(QQuick3DDirectionalLight, DirectionalLight);
70DECLARE_QQ3D_TYPE(QQuick3DPointLight, PointLight);
71DECLARE_QQ3D_TYPE(QQuick3DSpotLight, SpotLight);
72DECLARE_QQ3D_TYPE(QQuick3DTexture, Texture);
73DECLARE_QQ3D_TYPE(QQuick3DShaderUtilsTextureInput, TextureInput);
74DECLARE_QQ3D_TYPE(QQuick3DModel, Model);
75DECLARE_QQ3D_TYPE(QQuick3DEffect, Effect);
76DECLARE_QQ3D_TYPE(QQuick3DShaderUtilsRenderPass, Pass);
77DECLARE_QQ3D_TYPE(QQuick3DShaderUtilsShader, Shader);
78DECLARE_QQ3D_TYPE(QQuick3DInstanceList, InstanceList);
79DECLARE_QQ3D_TYPE(QQuick3DInstanceListEntry, InstanceListEntry);
80
81using QmlTypeNames = QHash<QString, int>;
82
83#define QQ3D_TYPE_ENTRY(TYPE) { TypeInfo<TYPE>::qmlTypeName(), TypeInfo<TYPE>::typeId() }
84
85QmlTypeNames baseTypeMap()
86{
87 return { QQ3D_TYPE_ENTRY(QQuick3DViewport),
88 QQ3D_TYPE_ENTRY(QQuick3DSceneEnvironment),
89 QQ3D_TYPE_ENTRY(QQuick3DPrincipledMaterial),
90 QQ3D_TYPE_ENTRY(QQuick3DDefaultMaterial),
91 QQ3D_TYPE_ENTRY(QQuick3DCustomMaterial),
92 QQ3D_TYPE_ENTRY(QQuick3DDirectionalLight),
93 QQ3D_TYPE_ENTRY(QQuick3DPointLight),
94 QQ3D_TYPE_ENTRY(QQuick3DSpotLight),
95 QQ3D_TYPE_ENTRY(QQuick3DTexture),
96 QQ3D_TYPE_ENTRY(QQuick3DShaderUtilsTextureInput),
97 QQ3D_TYPE_ENTRY(QQuick3DModel),
98 QQ3D_TYPE_ENTRY(QQuick3DEffect),
99 QQ3D_TYPE_ENTRY(QQuick3DShaderUtilsRenderPass),
100 QQ3D_TYPE_ENTRY(QQuick3DShaderUtilsShader),
101 QQ3D_TYPE_ENTRY(QQuick3DInstanceList),
102 QQ3D_TYPE_ENTRY(QQuick3DInstanceListEntry)
103 };
104}
105
106Q_GLOBAL_STATIC(QmlTypeNames, s_typeMap)
107
108struct Context
109{
110 enum class Type
111 {
112 Application,
113 Component
114 };
115
116 struct Property {
117 enum MemberState : quint8
118 {
119 Uninitialized,
120 Initialized
121 };
122
123 QObject *target = nullptr;
124 QStringView name;
125 int targetType = QMetaType::UnknownType;
126 QMetaType::Type type = QMetaType::UnknownType;
127 MemberState memberState = Uninitialized;
128 };
129
130 template<typename T> using Vector = QVector<T>;
131 using InterceptObjDefFunc = bool (*)(const QQmlJS::AST::UiObjectDefinition &, Context &, int &);
132 using InterceptObjBinding = bool (*)(const QQmlJS::AST::UiObjectBinding &, Context &, int &);
133 using InterceptPublicMember = bool (*)(const QQmlJS::AST::UiPublicMember &, Context &, int &);
134 using InterceptCallExpression = bool (*)(const QQmlJS::AST::CallExpression &, Context &, int &);
135
136 QQmlJS::Engine *engine = nullptr;
137 QDir workingDir; // aka source directory
138 QFileInfo currentFileInfo;
139
140 MaterialParser::SceneData sceneData;
141 Property property;
142
143 struct Component
144 {
145 QObject *ptr = nullptr;
146 int type = QMetaType::UnknownType;
147 };
148
149 QHash<QStringView, QObject *> identifierMap;
150 QHash<QString, Component> components;
151 InterceptObjDefFunc interceptODFunc = nullptr;
152 InterceptObjBinding interceptOBFunc = nullptr;
153 InterceptPublicMember interceptPMFunc = nullptr;
154 InterceptCallExpression interceptCallExpr = nullptr;
155 Type type = Type::Application;
156 bool dbgprint = false;
157};
158
159Q_DECLARE_TYPEINFO(Context::Component, Q_PRIMITIVE_TYPE);
160
161namespace BuiltinHelpers {
162
163using ArgumentListView = InvasiveListView<QQmlJS::AST::ArgumentList>;
164
165template <typename T> Q_REQUIRED_RESULT constexpr quint8 componentCount() { Q_STATIC_ASSERT(true); return 0; }
166template <> Q_REQUIRED_RESULT constexpr quint8 componentCount<QVector2D>() { return 2; }
167template <> Q_REQUIRED_RESULT constexpr quint8 componentCount<QVector3D>() { return 3; }
168template <> Q_REQUIRED_RESULT constexpr quint8 componentCount<QVector4D>() { return 4; }
169
170static double expressionValue(const QQmlJS::AST::ExpressionNode &expr) {
171 using namespace QQmlJS::AST;
172
173 if (expr.kind == Node::Kind_NumericLiteral) {
174 return static_cast<const NumericLiteral &>(expr).value;
175 } else if (expr.kind == Node::Kind_UnaryMinusExpression) {
176 const auto &minusExpr = static_cast<const UnaryMinusExpression &>(expr);
177 if (minusExpr.expression && minusExpr.expression->kind == Node::Kind_NumericLiteral)
178 return static_cast<const NumericLiteral &>(*minusExpr.expression).value * -1.0;
179 } else if (expr.kind == Node::Kind_UnaryPlusExpression) {
180 const auto &plusExpr = static_cast<const UnaryPlusExpression &>(expr);
181 if (plusExpr.expression && plusExpr.expression->kind == Node::Kind_NumericLiteral)
182 return static_cast<const NumericLiteral &>(*plusExpr.expression).value;
183 } else {
184 printf("Expression type \'%d\' unhandled!\n", expr.kind);
185 }
186
187 return 0.0;
188}
189
190template <typename T>
191static inline bool setProperty(const Context::Property &property, const T &v)
192{
193 return property.target->setProperty(property.name.toLatin1(), QVariant::fromValue(v));
194}
195
196template<typename Vec>
197static Vec toVec(const ArgumentListView &list, bool *ok = nullptr)
198{
199 using namespace QQmlJS::AST;
200 int i = 0;
201 const int e = componentCount<Vec>();
202 Vec vec;
203 for (const auto &listItem : list) {
204 if (listItem.expression && i != e)
205 vec[i] = expressionValue(*listItem.expression);
206 ++i;
207 }
208
209 if (ok)
210 *ok = (i == e);
211
212 return vec;
213}
214
215static QPointF toPoint(const ArgumentListView &list, bool *ok = nullptr)
216{
217 using namespace QQmlJS::AST;
218 int i = 0;
219 const int e = 2;
220 qreal args[e];
221 for (const auto &listItem : list) {
222 if (listItem.expression && i != e)
223 args[i] = expressionValue(*listItem.expression);
224 ++i;
225 }
226
227 if (ok)
228 *ok = (i == e);
229
230 return QPointF(args[0], args[1]);
231}
232
233static QSizeF toSize(const ArgumentListView &list, bool *ok = nullptr)
234{
235 using namespace QQmlJS::AST;
236 int i = 0;
237 const int e = 2;
238 qreal args[e];
239 for (const auto &listItem : list) {
240 if (listItem.expression && listItem.expression->kind == Node::Kind_NumericLiteral && i != e)
241 args[i] = expressionValue(*listItem.expression);
242 ++i;
243 }
244
245 if (ok)
246 *ok = (i == e);
247
248 return QSizeF(args[0], args[1]);
249}
250
251static QRectF toRect(const ArgumentListView &list, bool *ok = nullptr)
252{
253 using namespace QQmlJS::AST;
254 int i = 0;
255 const int e = 4;
256 qreal args[e];
257 for (const auto &listItem : list) {
258 if (listItem.expression && i != e)
259 args[i] = expressionValue(*listItem.expression);
260 ++i;
261 }
262
263 if (ok)
264 *ok = (i == e);
265
266 return QRectF(args[0], args[1], args[2], args[3]);
267}
268
269static QMatrix4x4 toMat44(const ArgumentListView &list, bool *ok = nullptr)
270{
271 using namespace QQmlJS::AST;
272 int i = 0;
273 const int e = 16;
274 float args[e];
275 for (const auto &listItem : list) {
276 if (listItem.expression && i != e)
277 args[i] = float(expressionValue(*listItem.expression));
278 ++i;
279 }
280
281 if (ok)
282 *ok = (i == e);
283
284 return QMatrix4x4(args);
285}
286
287static QQuaternion toQuaternion(const ArgumentListView &list, bool *ok = nullptr)
288{
289 using namespace QQmlJS::AST;
290 int i = 0;
291 const int e = 4;
292 float args[e];
293 for (const auto &listItem : list) {
294 if (listItem.expression && listItem.expression->kind == Node::Kind_NumericLiteral && i != e)
295 args[i] = float(expressionValue(*listItem.expression));
296 ++i;
297 }
298
299 if (ok)
300 *ok = (i == e);
301
302 return QQuaternion(args[0], args[1], args[2], args[3]);
303}
304
305// String variants
306// Note: Unlike the call variants we assume arguments are correct (can be converted),
307// as they should have failed earlier, during parsing, if they were not.
308template <typename Vec>
309static Vec toVec(const QStringView &ref)
310{
311 const auto args = ref.split(sep: u',');
312 Vec vec;
313 bool ok = false;
314 if (args.size() == componentCount<Vec>()) {
315 for (int i = 0; i != componentCount<Vec>(); ++i) {
316 vec[i] = args.at(i).toDouble(ok: &ok);
317 Q_ASSERT(ok);
318 }
319 }
320
321 return vec;
322}
323
324static QPointF toPoint(const QStringView &ref)
325{
326 const auto args = ref.split(sep: u",");
327 if (args.size() == 2) {
328 bool ok = false;
329 const auto arg0 = args.at(i: 0).toDouble(ok: &ok);
330 Q_ASSERT(ok);
331 const auto arg1 = args.at(i: 1).toDouble(ok: &ok);
332 Q_ASSERT(ok);
333 return QPointF(arg0, arg1);
334 }
335 return QPointF();
336}
337
338static QSizeF toSize(const QStringView &ref)
339{
340 const auto args = ref.split(sep: u'x');
341 if (args.size() == 2) {
342 bool ok = false;
343 const auto arg0 = args.at(i: 0).toDouble(ok: &ok);
344 Q_ASSERT(ok);
345 const auto arg1 = args.at(i: 1).toDouble(ok: &ok);
346 Q_ASSERT(ok);
347 return QSizeF(arg0, arg1);
348 }
349 return QSizeF();
350}
351
352static QRectF toRect(const QStringView &ref)
353{
354 auto args = ref.split(sep: u",");
355 if (args.size() == 3) {
356 bool ok = false;
357 const auto arg0 = args.at(i: 0).toDouble(ok: &ok);
358 Q_ASSERT(ok);
359 const auto arg1 = args.at(i: 1).toDouble(ok: &ok);
360 Q_ASSERT(ok);
361 args = args.at(i: 2).split(sep: u'x');
362 if (args.size() == 2) {
363 const auto arg2 = args.at(i: 0).toDouble(ok: &ok);
364 Q_ASSERT(ok);
365 const auto arg3 = args.at(i: 1).toDouble(ok: &ok);
366 Q_ASSERT(ok);
367 return QRectF(arg0, arg1, arg2, arg3);
368 }
369 }
370 return QRectF();
371}
372
373static QQuaternion toQuaternion(const QStringView &ref)
374{
375 const auto args = ref.split(sep: u',');
376 if (args.size() == 4) {
377 bool ok = false;
378 const auto arg0 = args.at(i: 0).toDouble(ok: &ok);
379 Q_ASSERT(ok);
380 const auto arg1 = args.at(i: 1).toDouble(ok: &ok);
381 Q_ASSERT(ok);
382 const auto arg2 = args.at(i: 2).toDouble(ok: &ok);
383 Q_ASSERT(ok);
384 const auto arg3 = args.at(i: 3).toDouble(ok: &ok);
385 Q_ASSERT(ok);
386 return QQuaternion(arg0, arg1, arg2, arg3);
387 }
388 return QQuaternion();
389}
390
391} // BuiltinHelpers
392
393template <typename T>
394static void cloneQmlList(const QObject &so, QMetaProperty &sp, QObject &to, QMetaProperty &tp) {
395 Q_ASSERT(sp.typeId() == tp.typeId());
396 const auto tv = tp.read(obj: &to);
397 const auto sv = sp.read(obj: &so);
398 if (sv.isValid() && tv.isValid()) {
399 auto tl = tv.value<QQmlListProperty<T>>();
400 auto sl = sv.value<QQmlListProperty<T>>();
401 const auto count = sl.count(&sl);
402 for (int i = 0; count != i; ++i) {
403 const auto &item = sl.at(&sl, i);
404 tl.append(&tl, item);
405 }
406 }
407}
408
409static void cloneProperties(QObject &target, const QObject &source)
410{
411 Q_ASSERT(target.metaObject() == source.metaObject());
412 const auto smo = source.metaObject();
413 auto tmo = target.metaObject();
414 const int propCount = smo->propertyCount();
415 for (int i = 0; i != propCount; ++i) {
416 auto sp = smo->property(index: i);
417 auto tp = tmo->property(index: i);
418 if (sp.typeId() == tp.typeId()) {
419 if (sp.typeId() == TypeInfo<QQuick3DMaterial>::qmlListTypeId())
420 cloneQmlList<QQuick3DMaterial>(so: source, sp, to&: target, tp);
421 else if (sp.typeId() == TypeInfo<QQuick3DEffect>::qmlListTypeId())
422 cloneQmlList<QQuick3DEffect>(so: source, sp, to&: target, tp);
423 else if (sp.typeId() == TypeInfo<QQuick3DShaderUtilsRenderPass>::qmlListTypeId())
424 cloneQmlList<QQuick3DShaderUtilsRenderPass>(so: source, sp, to&: target, tp);
425 else if (sp.typeId() == TypeInfo<QQuick3DShaderUtilsShader>::qmlListTypeId())
426 cloneQmlList<QQuick3DShaderUtilsShader>(so: source, sp, to&: target, tp);
427 else
428 tmo->property(index: i).write(obj: &target, value: smo->property(index: i).read(obj: &source));
429 }
430 }
431
432 // Clone the dynamic properties as well
433 for (const auto &prop : source.dynamicPropertyNames())
434 target.setProperty(name: prop.constData(), value: source.property(name: prop.constData()));
435}
436
437template <typename T>
438inline QVariant fromStringEnumHelper(const QStringView &ref, const QMetaProperty &property)
439{
440 bool ok = false;
441 const int v = property.enumerator().keyToValue(key: ref.toLatin1(), ok: &ok);
442 return ok ? QVariant::fromValue(T(v)) : QVariant();
443}
444
445static QVariant fromString(const QStringView &ref, const Context &ctx)
446{
447 const auto &p = ctx.property;
448 if (!p.target)
449 return QVariant();
450
451 static const auto toBuiltinType = [](int type, const QStringView &ref, const QDir &workingDir) {
452 using namespace BuiltinHelpers;
453 bool ok = false;
454 switch (type) {
455 case QMetaType::Int:
456 {
457 const auto v = ref.toInt(ok: &ok);
458 return (ok ? QVariant::fromValue(value: v) : QVariant());
459 }
460 break;
461 case QMetaType::Bool:
462 {
463 const auto v = ref.toInt(ok: &ok);
464 return (ok ? QVariant::fromValue(value: bool(v)) : QVariant());
465 }
466 case QMetaType::Double:
467 {
468 const auto v = ref.toDouble(ok: &ok);
469 return (ok ? QVariant::fromValue(value: qreal(v)) : QVariant());
470 }
471 break;
472 case QMetaType::QString:
473 return QVariant::fromValue(value: ref);
474 case QMetaType::QUrl:
475 {
476 if (ref.startsWith(c: u':') || ref.startsWith(c: QDir::separator()))
477 return QVariant::fromValue(value: QUrl::fromLocalFile(localfile: ref.toString()));
478 else if (ref.startsWith(c: u'#'))
479 return QVariant::fromValue(value: QUrl(ref.toString()));
480 else
481 return QVariant::fromValue(value: QUrl::fromUserInput(userInput: ref.toString(), workingDirectory: workingDir.canonicalPath()));
482 }
483 case QMetaType::QColor:
484 return QVariant::fromValue(value: QColor(ref));
485 case QMetaType::QTime:
486 return QVariant::fromValue(value: QTime::fromString(string: ref.toString()));
487 case QMetaType::QDate:
488 return QVariant::fromValue(value: QDate::fromString(string: ref.toString()));
489 case QMetaType::QDateTime:
490 return QVariant::fromValue(value: QDateTime::fromString(string: ref.toString()));
491 case QMetaType::QRectF:
492 return QVariant::fromValue(value: toRect(ref));
493 case QMetaType::QPointF:
494 return QVariant::fromValue(value: toPoint(ref));
495 case QMetaType::QSizeF:
496 return QVariant::fromValue(value: toSize(ref));
497 case QMetaType::QVector2D:
498 return QVariant::fromValue(value: toVec<QVector2D>(ref));
499 case QMetaType::QVector3D:
500 return QVariant::fromValue(value: toVec<QVector3D>(ref));
501 case QMetaType::QVector4D:
502 return QVariant::fromValue(value: toVec<QVector4D>(ref));
503 case QMetaType::QQuaternion:
504 return QVariant::fromValue(value: toQuaternion(ref));
505 }
506
507 return QVariant();
508 };
509
510 if (p.type != QMetaType::UnknownType) // Built in Qt types int, vector3d etc
511 return toBuiltinType(p.type, ref, ctx.workingDir);
512
513 // hard mode, detect the property type
514 // We only care about the types that are relevant for us
515 if (p.targetType != QMetaType::UnknownType) {
516 Q_ASSERT(p.target);
517 Q_ASSERT(!p.name.isEmpty());
518 const int idx = p.target->metaObject()->indexOfProperty(name: p.name.toLatin1().constData());
519 const auto property = p.target->metaObject()->property(index: idx);
520 if (property.metaType().id() >= QMetaType::User) {
521 const QMetaType metaType = property.metaType();
522 if (p.targetType == TypeInfo<QQuick3DDefaultMaterial>::typeId() || p.targetType == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
523 // Common for both materials
524 if (metaType.id() == qMetaTypeId<QQuick3DMaterial::CullMode>())
525 return fromStringEnumHelper<QQuick3DMaterial::CullMode>(ref, property);
526 if (metaType.id() == qMetaTypeId<QQuick3DMaterial::TextureChannelMapping>())
527 return fromStringEnumHelper<QQuick3DMaterial::TextureChannelMapping>(ref, property);
528
529 if (p.targetType == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
530 if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::Lighting>())
531 return fromStringEnumHelper<QQuick3DPrincipledMaterial::Lighting>(ref, property);
532 if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::BlendMode>())
533 return fromStringEnumHelper<QQuick3DPrincipledMaterial::BlendMode>(ref, property);
534 if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::AlphaMode>())
535 return fromStringEnumHelper<QQuick3DPrincipledMaterial::AlphaMode>(ref, property);
536 } else if (p.targetType == TypeInfo<QQuick3DDefaultMaterial>::typeId()) {
537 if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::Lighting>())
538 return fromStringEnumHelper<QQuick3DDefaultMaterial::Lighting>(ref, property);
539 if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::BlendMode>())
540 return fromStringEnumHelper<QQuick3DDefaultMaterial::BlendMode>(ref, property);
541 if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::SpecularModel>())
542 return fromStringEnumHelper<QQuick3DDefaultMaterial::SpecularModel>(ref, property);
543 }
544 } else if (p.targetType == TypeInfo<QQuick3DCustomMaterial>::typeId()) {
545 if (metaType.id() == qMetaTypeId<QQuick3DCustomMaterial::ShadingMode>())
546 return fromStringEnumHelper<QQuick3DCustomMaterial::ShadingMode>(ref, property);
547 if (metaType.id() == qMetaTypeId<QQuick3DCustomMaterial::BlendMode>())
548 return fromStringEnumHelper<QQuick3DCustomMaterial::BlendMode>(ref, property);
549 } else if (p.targetType == TypeInfo<QQuick3DSpotLight>::typeId() || p.targetType == TypeInfo<QQuick3DPointLight>::typeId()) {
550 if (metaType.id() == qMetaTypeId<QQuick3DAbstractLight::QSSGShadowMapQuality>())
551 return fromStringEnumHelper<QQuick3DAbstractLight::QSSGShadowMapQuality>(ref, property);
552 } else if (p.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
553 if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes>())
554 return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes>(ref, property);
555 if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues>())
556 return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues>(ref, property);
557 if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues>())
558 return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues>(ref, property);
559 if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentTonemapModes>())
560 return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentTonemapModes>(ref, property);
561 } else if (p.targetType == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) {
562 if (metaType.id() == qMetaTypeId<QQuick3DShaderUtilsShader::Stage>())
563 return fromStringEnumHelper<QQuick3DShaderUtilsShader::Stage>(ref, property);
564 }
565 } else { // Qt type
566 return toBuiltinType(property.metaType().id(), ref, ctx.workingDir);
567 }
568 }
569
570 if (ctx.dbgprint)
571 printf(format: "Unhandled type for property %s\n", ref.toLatin1().constData());
572
573 return QVariant();
574}
575
576static QString getQmlFileExtension() { return QStringLiteral("qml"); }
577
578struct Visitors
579{
580 static void visit(const QQmlJS::AST::UiProgram &program, Context &ctx, int &ret)
581 {
582 using namespace QQmlJS::AST;
583 const bool readHeaders = false;
584 if (readHeaders && program.headers) { // No real need for us to look at the includes
585 using HeaderItem = UiHeaderItemList;
586 using Headers = InvasiveListView<HeaderItem>;
587
588 Headers headers(*program.headers);
589 for (const auto &header : headers)
590 printf("Type: %d\n", header.kind);
591 }
592 if (program.members)
593 visit(*program.members, ctx, ret);
594 }
595 static void visit(const QQmlJS::AST::UiObjectInitializer &objInitializer, Context &ctx, int &ret)
596 {
597 using namespace QQmlJS::AST;
598 if (objInitializer.members)
599 visit(*objInitializer.members, ctx, ret);
600 }
601
602 static void visit(const QQmlJS::AST::UiObjectDefinition &def, Context &ctx, int &ret)
603 {
604 using namespace QQmlJS::AST;
605 if (ctx.dbgprint)
606 printf("Object definition -> %s\n", def.qualifiedTypeNameId->name.toLocal8Bit().constData());
607 if (!(ctx.interceptODFunc && ctx.interceptODFunc(def, ctx, ret))) {
608 if (def.initializer)
609 visit(*static_cast<UiObjectInitializer *>(def.initializer), ctx, ret);
610 }
611 }
612
613 static void visit(const QQmlJS::AST::UiArrayMemberList &memberList, Context &ctx, int &ret)
614 {
615 using namespace QQmlJS::AST;
616 using ArrayMemberItem = UiArrayMemberList;
617 using ArrayMembers = InvasiveListView<ArrayMemberItem>;
618
619 ArrayMembers arrayMembers(memberList);
620 for (auto &object : arrayMembers) {
621 if (object.member->kind == Node::Kind_UiObjectDefinition) {
622 const auto &def = *static_cast<UiObjectDefinition *>(object.member);
623 visit(def, ctx, ret);
624 }
625 }
626 }
627
628 static void visit(const QQmlJS::AST::UiScriptBinding &binding, Context &ctx, int &ret)
629 {
630 using namespace QQmlJS::AST;
631 if (ctx.dbgprint)
632 printf("Script binding -> %s ", binding.qualifiedId->name.toLocal8Bit().constData());
633
634 const auto oldName = ctx.property.name; // reentrancy
635 ctx.property.name = binding.qualifiedId->name;
636
637 if (binding.statement) {
638 if (binding.statement->kind == Node::Kind_ExpressionStatement) {
639 const auto &expressionStatement = static_cast<const ExpressionStatement &>(*binding.statement);
640 visit(expressionStatement, ctx, ret);
641 }
642 }
643 ctx.property.name = oldName;
644 }
645
646 static void visit(const QQmlJS::AST::UiArrayBinding &arrayBinding, Context &ctx, int &ret)
647 {
648 using namespace QQmlJS::AST;
649 if (ctx.dbgprint)
650 printf("Array binding(s) -> %s: [\n", arrayBinding.qualifiedId->name.toLocal8Bit().constData());
651
652 const auto oldName = ctx.property.name; // reentrancy
653 ctx.property.name = arrayBinding.qualifiedId->name;
654
655 if (arrayBinding.members)
656 visit(*arrayBinding.members, ctx, ret);
657
658 if (ctx.dbgprint)
659 printf(format: "]\n");
660
661 ctx.property.name = oldName;
662 }
663
664 static void visit(const QQmlJS::AST::UiObjectBinding &objectBinding, Context &ctx, int &ret)
665 {
666 using namespace QQmlJS::AST;
667
668 if (ctx.dbgprint)
669 printf("Object binding -> %s: %s {\n", objectBinding.qualifiedId->name.toLocal8Bit().constData(), objectBinding.qualifiedTypeNameId->name.toLocal8Bit().constData());
670
671 if (objectBinding.initializer) {
672 if (!(ctx.interceptOBFunc && ctx.interceptOBFunc(objectBinding, ctx, ret)))
673 visit(*objectBinding.initializer, ctx, ret);
674 }
675
676 if (ctx.dbgprint)
677 printf(format: "}\n");
678 }
679
680 static void visit(const QQmlJS::AST::UiPublicMember &member, Context &ctx, int &ret)
681 {
682 using namespace QQmlJS::AST;
683
684 if (ctx.dbgprint)
685 printf("%s member -> %s ", (member.type == UiPublicMember::Signal ? "Signal" : "Property"), member.name.toLocal8Bit().constData());
686
687 auto name = ctx.property.name;
688 ctx.property.name = member.name;
689 const auto typeCpy = ctx.property.type;
690
691 if (!(ctx.interceptPMFunc && ctx.interceptPMFunc(member, ctx, ret))) {
692 if (member.statement) {
693 const auto &statement = member.statement;
694 if (statement->kind == Node::Kind_ExpressionStatement)
695 visit(static_cast<const ExpressionStatement &>(*statement), ctx, ret);
696 else if (ctx.dbgprint)
697 printf("Unhandled statement (%d)\n", statement->kind);
698 } else if (member.binding) {
699 const auto &binding = member.binding;
700 if (binding->kind == Node::Kind_UiObjectBinding)
701 visit(static_cast<const UiObjectBinding &>(*binding), ctx, ret);
702 else if (ctx.dbgprint)
703 printf("Unhandled binding (%d)\n", binding->kind);
704 }
705 }
706
707 qSwap(value1&: ctx.property.name, value2&: name);
708 ctx.property.type = typeCpy;
709 }
710
711 static void visit(const QQmlJS::AST::ExpressionStatement &exprStatement, Context &ctx, int &ret)
712 {
713 using namespace QQmlJS::AST;
714 if (exprStatement.expression) {
715 if (exprStatement.expression->kind == Node::Kind_IdentifierExpression) {
716 const auto &identExpression = static_cast<const IdentifierExpression &>(*exprStatement.expression);
717 visit(identExpression, ctx, ret);
718 } else if (exprStatement.expression->kind == Node::Kind_StringLiteral) {
719 const auto &stringLiteral = static_cast<const StringLiteral &>(*exprStatement.expression);
720 visit(stringLiteral, ctx, ret);
721 } else if (exprStatement.expression->kind == Node::Kind_NumericLiteral) {
722 const auto &numericLiteral = static_cast<const NumericLiteral &>(*exprStatement.expression);
723 visit(numericLiteral, ctx, ret);
724 } else if (exprStatement.expression->kind == Node::Kind_FieldMemberExpression) {
725 const auto &fieldMemberExpression = static_cast<const FieldMemberExpression &>(*exprStatement.expression);
726 visit(fieldMemberExpression, ctx, ret);
727 } else if (exprStatement.expression->kind == Node::Kind_TrueLiteral || exprStatement.expression->kind == Node::Kind_FalseLiteral) {
728 const bool v = (exprStatement.expression->kind == Node::Kind_TrueLiteral);
729 if (ctx.dbgprint)
730 printf(format: "-> TrueLiteral: %s\n", v ? "true" : "false");
731 if (ctx.property.target) {
732 auto target = ctx.property.target;
733 const auto &name = ctx.property.name;
734 target->setProperty(name: name.toLatin1(), value: QVariant::fromValue(value: v));
735 }
736 } else if (exprStatement.expression->kind == Node::Kind_ArrayPattern) {
737 const auto &arrayPattern = static_cast<const ArrayPattern &>(*exprStatement.expression);
738 visit(arrayPattern, ctx, ret);
739 } else if (exprStatement.expression->kind == Node::Kind_CallExpression) {
740 const auto &callExpression = static_cast<const CallExpression &>(*exprStatement.expression);
741 visit(callExpression, ctx, ret);
742 } else if (exprStatement.expression->kind == Node::Kind_UnaryMinusExpression) {
743 const auto &unaryMinusExpr = static_cast<const UnaryMinusExpression &>(*exprStatement.expression);
744 if (unaryMinusExpr.expression && unaryMinusExpr.expression->kind == Node::Kind_NumericLiteral) {
745 auto &numericLiteral = static_cast<NumericLiteral &>(*unaryMinusExpr.expression);
746 const auto value = numericLiteral.value;
747 numericLiteral.value *= -1;
748 visit(numericLiteral, ctx, ret);
749 numericLiteral.value = value;
750 }
751 } else if (exprStatement.expression->kind == Node::Kind_UnaryPlusExpression) {
752 const auto &unaryPlusExpr = static_cast<const UnaryPlusExpression &>(*exprStatement.expression);
753 if (unaryPlusExpr.expression)
754 visit(static_cast<const NumericLiteral &>(*unaryPlusExpr.expression), ctx, ret);
755 } else {
756 if (ctx.dbgprint)
757 printf("<expression: %d>\n", exprStatement.expression->kind);
758 }
759 }
760 }
761
762 static void visit(const QQmlJS::AST::IdentifierExpression &idExpr, Context &ctx, int &ret)
763 {
764 Q_UNUSED(ret);
765 if (ctx.dbgprint)
766 printf("-> Identifier: %s\n", idExpr.name.toLocal8Bit().constData());
767
768 if (ctx.property.target && ctx.type != Context::Type::Component) {
769 const auto foundIt = ctx.identifierMap.constFind(key: idExpr.name);
770 const auto end = ctx.identifierMap.constEnd();
771 if (foundIt != end) { // If an item was found it means this is a reference
772 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
773 if (QQuick3DMaterial *mat = qobject_cast<QQuick3DMaterial *>(*foundIt)) {
774 auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials();
775 // Since we are initializing this for the first time, make sure we clean out any inherited data!
776 if (ctx.property.memberState == Context::Property::Uninitialized) {
777 if (ctx.dbgprint)
778 printf(format: "Clearing inherited materials\n");
779 materials.clear(&materials);
780 ctx.property.memberState = Context::Property::Initialized;
781 }
782 materials.append(&materials, mat);
783 if (ctx.dbgprint)
784 printf(format: "Appending material to %s\n", ctx.property.name.toLatin1().constData());
785 } else if (QQuick3DInstanceList *instancingList = qobject_cast<QQuick3DInstanceList *>(*foundIt)) {
786 qobject_cast<QQuick3DModel *>(object: ctx.property.target)->setInstancing(instancingList);
787 if (ctx.dbgprint)
788 printf(format: "Setting instance list on model\n");
789 }
790 } else if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
791 if (QQuick3DEffect *effect = qobject_cast<QQuick3DEffect *>(*foundIt)) {
792 auto effects = qobject_cast<QQuick3DSceneEnvironment *>(object: ctx.property.target)->effects();
793 // Since we are initializing this for the first time, make sure we clean out any inherited data!
794 if (ctx.property.memberState == Context::Property::Uninitialized) {
795 if (ctx.dbgprint)
796 printf(format: "Clearing inherited effects\n");
797 effects.clear(&effects);
798 ctx.property.memberState = Context::Property::Initialized;
799 }
800 effects.append(&effects, effect);
801 if (ctx.dbgprint)
802 printf(format: "Appending effect to \'%s\'\n", ctx.property.name.toLatin1().constData());
803 }
804 } else if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
805 if (QQuick3DShaderUtilsShader *shader = qobject_cast<QQuick3DShaderUtilsShader *>(*foundIt)) {
806 auto shaders = qobject_cast<QQuick3DShaderUtilsRenderPass *>(object: ctx.property.target)->shaders();
807 // Since we are initializing this for the first time, make sure we clean out any inherited data!
808 if (ctx.property.memberState == Context::Property::Uninitialized) {
809 if (ctx.dbgprint)
810 printf(format: "Clearing inherited shaders\n");
811 shaders.clear(&shaders);
812 ctx.property.memberState = Context::Property::Initialized;
813 }
814 shaders.append(&shaders, shader);
815 if (ctx.dbgprint)
816 printf(format: "Appending shader to \'%s\'\n", ctx.property.name.toLatin1().constData());
817 }
818 } else if (ctx.property.targetType == TypeInfo<QQuick3DInstanceList>::typeId()) {
819 if (QQuick3DInstanceListEntry *listEntry = qobject_cast<QQuick3DInstanceListEntry *>(*foundIt)) {
820 auto instances = qobject_cast<QQuick3DInstanceList *>(object: ctx.property.target)->instances();
821 // Since we are initializing this for the first time, make sure we clean out any inherited data!
822 if (ctx.property.memberState == Context::Property::Uninitialized) {
823 if (ctx.dbgprint)
824 printf(format: "Clearing inherited instances\n");
825 instances.clear(&instances);
826 ctx.property.memberState = Context::Property::Initialized;
827 }
828 instances.append(&instances, listEntry);
829 if (ctx.dbgprint)
830 printf(format: "Appending instance entry to %s\n", ctx.property.name.toLatin1().constData());
831 } else if (QQuick3DInstanceList *instancingList = qobject_cast<QQuick3DInstanceList *>(*foundIt)) {
832 qobject_cast<QQuick3DModel *>(object: ctx.property.target)->setInstancing(instancingList);
833 if (ctx.dbgprint)
834 printf(format: "Setting instance list on model\n");
835 }
836 } else if (ctx.dbgprint) {
837 printf("Unhandled binding: %s\n", idExpr.name.toLatin1().constData());
838 }
839 } else {
840 // 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).
841 // 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).
842 ctx.identifierMap.insert(idExpr.name, ctx.property.target);
843 }
844 }
845 }
846
847 static void visit(const QQmlJS::AST::StringLiteral &stringLiteral, Context &ctx, int &ret)
848 {
849 Q_UNUSED(ret);
850 if (ctx.dbgprint)
851 printf("-> StringLiteral: \"%s\"\n", stringLiteral.value.toLocal8Bit().constData());
852
853 if (ctx.property.target) {
854 const auto &name = ctx.property.name;
855 const auto v = fromString(stringLiteral.value, ctx);
856 if (v.isValid()) {
857 const bool b = ctx.property.target->setProperty(name.toLatin1(), v);
858 if (b && ctx.dbgprint)
859 printf(format: "Property %s updated!\n", name.toLatin1().constData());
860 }
861 }
862 }
863
864 static void visit(const QQmlJS::AST::NumericLiteral &numericLiteral, Context &ctx, int &ret)
865 {
866 Q_UNUSED(ret);
867 if (ctx.dbgprint)
868 printf("-> NumericLiteral: %f\n", numericLiteral.value);
869
870 if (ctx.property.target) {
871 auto target = ctx.property.target;
872 const auto &name = ctx.property.name;
873 target->setProperty(name.toLatin1(), QVariant::fromValue(numericLiteral.value));
874 }
875 }
876
877 static void visit(const QQmlJS::AST::FieldMemberExpression &fieldMemberExpression, Context &ctx, int &ret)
878 {
879 using namespace QQmlJS::AST;
880
881 Q_UNUSED(ret);
882 if (ctx.dbgprint)
883 printf("-> FieldMemberExpression: %s\n", fieldMemberExpression.name.toLocal8Bit().constData());
884
885 if (ctx.property.target) {
886 const auto &name = ctx.property.name;
887 const auto v = fromString(fieldMemberExpression.name, ctx);
888 if (v.isValid())
889 ctx.property.target->setProperty(name.toLatin1(), v);
890 }
891 }
892
893 static void visit(const QQmlJS::AST::ArrayPattern &arrayPattern, Context &ctx, int &ret)
894 {
895 Q_UNUSED(ret);
896 if (ctx.dbgprint)
897 printf(format: "-> [ ");
898
899 using namespace QQmlJS::AST;
900 using PatternElementItem = PatternElementList;
901 using PatternElementListView = InvasiveListView<PatternElementItem>;
902
903 PatternElementListView elements(*arrayPattern.elements);
904 for (auto &element : elements) {
905 auto patternElement = element.element;
906 if (patternElement->type == PatternElement::Literal) {
907 if (patternElement->initializer && patternElement->initializer->kind == Node::Kind_IdentifierExpression) {
908 const auto &identExpression = static_cast<const IdentifierExpression &>(*patternElement->initializer);
909 visit(identExpression, ctx, ret);
910 }
911 } else if (ctx.dbgprint) {
912 printf("Unahandled(%d), ", patternElement->type);
913 }
914 }
915
916 if (ctx.dbgprint)
917 printf(format: " ]\n");
918
919 }
920
921 static void visit(const QQmlJS::AST::CallExpression &callExpression, Context &ctx, int &ret)
922 {
923 using namespace QQmlJS::AST;
924
925 Q_UNUSED(ret);
926 Q_UNUSED(callExpression);
927 if (ctx.dbgprint)
928 printf("-> Call(%d)\n", callExpression.base->kind);
929
930 (ctx.interceptCallExpr && ctx.interceptCallExpr(callExpression, ctx, ret));
931 }
932
933 static void visit(const QQmlJS::AST::UiObjectMember &member, Context &ctx, int &ret)
934 {
935 using namespace QQmlJS::AST;
936
937 if (member.kind == Node::Kind_UiObjectBinding)
938 visit(static_cast<const UiObjectBinding &>(member), ctx, ret);
939 else if (ctx.dbgprint)
940 printf("Unhandled member (%d)\n", member.kind);
941 }
942
943 static void visit(const QQmlJS::AST::UiObjectMemberList &memberList, Context &ctx, int &ret)
944 {
945 using namespace QQmlJS::AST;
946 using ObjectMemberItem = UiObjectMemberList;
947 using ObjectMembers = InvasiveListView<ObjectMemberItem>;
948
949 const auto oldEvalType = ctx.property.memberState;
950
951 ObjectMembers objectMembers(memberList);
952 for (const auto &member : objectMembers) {
953 if (member.member) {
954 if (member.member->kind == Node::Kind_UiScriptBinding) {
955 ctx.property.memberState = Context::Property::MemberState::Uninitialized;
956 const auto &scriptBinding = static_cast<const UiScriptBinding &>(*member.member);
957 visit(scriptBinding, ctx, ret);
958 } else if (member.member->kind == Node::Kind_UiArrayBinding) {
959 ctx.property.memberState = Context::Property::MemberState::Uninitialized;
960 const auto &arrayBinding = static_cast<const UiArrayBinding &>(*member.member);
961 visit(arrayBinding, ctx, ret);
962 } else if (member.member->kind == Node::Kind_UiObjectDefinition) {
963 const auto &objectDef = static_cast<const UiObjectDefinition &>(*member.member);
964 visit(objectDef, ctx, ret);
965 } else if (member.member->kind == Node::Kind_UiObjectBinding) {
966 ctx.property.memberState = Context::Property::MemberState::Uninitialized;
967 const auto &objBinding = static_cast<const UiObjectBinding &>(*member.member);
968 visit(objBinding, ctx, ret);
969 } else if (member.member->kind == Node::Kind_UiPublicMember) {
970 ctx.property.memberState = Context::Property::MemberState::Uninitialized;
971 const auto &pubMember = static_cast<const UiPublicMember &>(*member.member);
972 visit(pubMember, ctx, ret);
973 } else {
974 if (ctx.dbgprint)
975 printf("<member %d>\n", member.member->kind);
976 }
977 }
978 }
979
980 ctx.property.memberState = oldEvalType;
981 }
982
983private:
984 Visitors() = delete;
985 Q_DISABLE_COPY(Visitors);
986};
987
988template <typename O, typename T>
989T *buildType(const O &obj, Context &ctx, int &ret, const T *base = nullptr)
990{
991 // swap -> reentrancy
992 Context::Property property;
993 qSwap(value1&: property, value2&: ctx.property);
994 Q_ASSERT(ctx.property.target == nullptr);
995
996 T *instance = nullptr;
997
998 if (ctx.dbgprint)
999 printf("Building %s!\n", TypeInfo<T>::qmlTypeName());
1000
1001 if (obj.initializer) {
1002 instance = new T;
1003 if (base)
1004 cloneProperties(*instance, *base);
1005
1006 if (obj.initializer) {
1007 ctx.property.target = instance;
1008 ctx.property.targetType = TypeInfo<T>::typeId();
1009 Visitors::visit(*obj.initializer, ctx, ret);
1010 }
1011 }
1012
1013 // swap back
1014 qSwap(value1&: property, value2&: ctx.property);
1015
1016 return instance;
1017}
1018
1019static QQuick3DAbstractLight *buildLight(const QQmlJS::AST::UiObjectDefinition &def,
1020 Context &ctx,
1021 int &ret,
1022 int lightType,
1023 const QQuick3DAbstractLight *base = nullptr)
1024{
1025 if (lightType == TypeInfo<QQuick3DDirectionalLight>::typeId())
1026 return buildType(def, ctx, ret, qobject_cast<const QQuick3DDirectionalLight *>(object: base));
1027 if (lightType == TypeInfo<QQuick3DPointLight>::typeId())
1028 return buildType(def, ctx, ret, qobject_cast<const QQuick3DPointLight *>(object: base));
1029 if (lightType == TypeInfo<QQuick3DSpotLight>::typeId())
1030 return buildType(def, ctx, ret, qobject_cast<const QQuick3DSpotLight *>(object: base));
1031 return nullptr;
1032}
1033
1034
1035template <typename T>
1036static void updateProperty(Context &ctx, T type, QStringView propName)
1037{
1038 if (ctx.property.target) {
1039 if (ctx.dbgprint)
1040 printf(format: "Updating property %s\n", propName.toLatin1().constData());
1041 const auto &target = ctx.property.target;
1042 if (ctx.property.memberState == Context::Property::Uninitialized) {
1043 target->setProperty(propName.toLatin1().constData(), QVariant::fromValue(type));
1044 ctx.property.memberState = Context::Property::Initialized;
1045 } else {
1046 const int idx = target->metaObject()->indexOfProperty(name: propName.toLatin1().constData());
1047 if (idx != -1) {
1048 auto prop = target->metaObject()->property(index: idx);
1049 prop.write(target, QVariant::fromValue(type));
1050 }
1051 }
1052 }
1053}
1054
1055static bool interceptObjectBinding(const QQmlJS::AST::UiObjectBinding &objectBinding, Context &ctx, int &ret)
1056{
1057 if (ctx.dbgprint)
1058 printf(format: "Intercepted object binding!\n");
1059
1060 bool handled = false;
1061
1062 const auto &typeName = objectBinding.qualifiedTypeNameId->name.toString();
1063 const auto &propName = objectBinding.qualifiedId->name;
1064
1065 int type = -1;
1066
1067 // Base type?
1068 const auto typeIt = s_typeMap->constFind(key: typeName);
1069 if (typeIt != s_typeMap->cend())
1070 type = *typeIt;
1071
1072 // Component?
1073 auto &components = ctx.components;
1074 const auto compIt = (type == -1) ? components.constFind(key: typeName) : components.cend();
1075 QObject *base = nullptr;
1076 if (compIt != components.cend()) {
1077 type = compIt->type;
1078 base = compIt->ptr;
1079 }
1080
1081 if (type != -1) {
1082 if (ctx.dbgprint)
1083 printf("Resolving: \'%s\'\n", qPrintable(typeName));
1084
1085 if (type == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
1086 if (ctx.property.targetType == TypeInfo<QQuick3DViewport>::typeId()) {
1087 if (auto environment = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DSceneEnvironment *>(base))) {
1088 auto viewport = qobject_cast<QQuick3DViewport *>(object: ctx.property.target);
1089 Q_ASSERT(viewport);
1090 viewport->setEnvironment(environment);
1091 handled = true;
1092 }
1093 }
1094 } else if (type == TypeInfo<QQuick3DTexture>::typeId()) {
1095 if (auto tex = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DTexture *>(base))) {
1096 updateProperty(ctx, tex, propName);
1097 ctx.sceneData.textures.append(tex);
1098 }
1099 handled = true;
1100 } else if (type == TypeInfo<QQuick3DShaderUtilsTextureInput>::typeId()) {
1101 auto texInput = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsTextureInput *>(object: base));
1102 if (texInput && texInput->texture()) {
1103 updateProperty(ctx, texInput, propName);
1104 ctx.sceneData.textures.append(texInput->texture());
1105 }
1106 handled = true;
1107 } else if (type == TypeInfo<QQuick3DEffect>::typeId()) {
1108 if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
1109 if (auto effect = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DEffect *>(base))) {
1110 auto sceneEnvironment = qobject_cast<QQuick3DSceneEnvironment *>(object: ctx.property.target);
1111 Q_ASSERT(sceneEnvironment);
1112 auto effects = sceneEnvironment->effects();
1113 effects.append(&effects, effect);
1114 handled = true;
1115 }
1116 }
1117 } else if (type == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
1118 if (ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) {
1119 if (auto pass = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsRenderPass *>(base))) {
1120 auto effect = qobject_cast<QQuick3DEffect *>(object: ctx.property.target);
1121 Q_ASSERT(effect);
1122 auto passes = effect->passes();
1123 passes.append(&passes, pass);
1124 handled = true;
1125 }
1126 }
1127 } else if (type == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) {
1128 if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
1129 if (auto shader = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsShader *>(base))) {
1130 auto pass = qobject_cast<QQuick3DShaderUtilsRenderPass *>(object: ctx.property.target);
1131 Q_ASSERT(pass);
1132 auto shaders = pass->shaders();
1133 shaders.append(&shaders, shader);
1134 handled = true;
1135 }
1136 }
1137 } else if (type == TypeInfo<QQuick3DDefaultMaterial>::typeId()) {
1138 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
1139 if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DDefaultMaterial *>(base))) {
1140 auto model = qobject_cast<QQuick3DModel *>(object: ctx.property.target);
1141 Q_ASSERT(model);
1142 auto materials = model->materials();
1143 materials.append(&materials, mat);
1144 handled = true;
1145 if (ctx.dbgprint)
1146 printf(format: "Appending material to %s\n", ctx.property.name.toLatin1().constData());
1147 }
1148 }
1149 } else if (type == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
1150 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
1151 if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DPrincipledMaterial *>(base))) {
1152 auto model = qobject_cast<QQuick3DModel *>(object: ctx.property.target);
1153 Q_ASSERT(model);
1154 auto materials = model->materials();
1155 materials.append(&materials, mat);
1156 handled = true;
1157 }
1158 }
1159 } else if (type == TypeInfo<QQuick3DCustomMaterial>::typeId()) {
1160 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
1161 if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DCustomMaterial *>(base))) {
1162 auto model = qobject_cast<QQuick3DModel *>(object: ctx.property.target);
1163 Q_ASSERT(model);
1164 auto materials = model->materials();
1165 materials.append(&materials, mat);
1166 handled = true;
1167 }
1168 }
1169 } else if (ctx.dbgprint) {
1170 printf(format: "Unhandled type\n");
1171 }
1172 }
1173
1174 return handled;
1175}
1176
1177static bool interceptObjectDef(const QQmlJS::AST::UiObjectDefinition &def, Context &ctx, int &ret)
1178{
1179 const auto &typeName = def.qualifiedTypeNameId->name.toString();
1180
1181 if (ctx.dbgprint)
1182 printf("Intercepted object definition (\'%s\')!\n", typeName.toLatin1().constData());
1183
1184 QString componentName;
1185 int type = -1;
1186 bool doRegisterComponent = false;
1187
1188 // Base type?
1189 const auto typeIt = s_typeMap->constFind(key: typeName);
1190 if (typeIt != s_typeMap->cend())
1191 type = *typeIt;
1192
1193 // Component?
1194 auto &components = ctx.components;
1195 const auto compIt = (type == -1) ? components.constFind(key: typeName) : components.cend();
1196 if (compIt != components.cend())
1197 type = compIt->type;
1198
1199 // If this is a new component register it
1200 if (ctx.type == Context::Type::Component && ctx.property.target == nullptr && type != -1) {
1201 const auto &fileName = ctx.currentFileInfo.fileName();
1202 componentName = fileName.left(n: fileName.size() - 4);
1203 doRegisterComponent = !componentName.isEmpty();
1204 }
1205
1206 const auto registerComponent = [&ctx, &components, &componentName](Context::Component component) {
1207 if (ctx.dbgprint)
1208 printf(format: "Registering component \'%s\'\n", qPrintable(componentName));
1209 components.insert(key: componentName, value: component);
1210 };
1211
1212 if (type == TypeInfo<QQuick3DViewport>::typeId()) {
1213 const QQuick3DViewport *base = (compIt != components.cend()) ? qobject_cast<QQuick3DViewport *>(compIt->ptr) : nullptr;
1214 if (QQuick3DViewport *viewport = buildType(def, ctx, ret, base)) {
1215 // If this is a component we'll store it for lookups later.
1216 if (doRegisterComponent)
1217 registerComponent({ .ptr: viewport, .type: type });
1218 // Only one viewport supported atm (see SceneEnvironment case as well).
1219 if (!ctx.sceneData.viewport)
1220 ctx.sceneData.viewport = viewport;
1221 }
1222 } else if (type == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
1223 const QQuick3DSceneEnvironment *base = (compIt != components.cend()) ? qobject_cast<QQuick3DSceneEnvironment *>(compIt->ptr) : nullptr;
1224 if (QQuick3DSceneEnvironment *sceneEnv = buildType(def, ctx, ret, base)) {
1225 // If this is a component we'll store it for lookups later.
1226 if (doRegisterComponent)
1227 registerComponent({ .ptr: sceneEnv, .type: type });
1228
1229 if (ctx.sceneData.viewport)
1230 ctx.sceneData.viewport->setEnvironment(sceneEnv);
1231 }
1232 } else if (type == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
1233 const QQuick3DPrincipledMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DPrincipledMaterial *>(compIt->ptr) : nullptr;
1234 if (QQuick3DPrincipledMaterial *mat = buildType(def, ctx, ret, base)) {
1235 // If this is a component we'll store it for lookups later.
1236 if (doRegisterComponent)
1237 registerComponent({ .ptr: mat, .type: type });
1238
1239 if (ctx.property.target) {
1240 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
1241 auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials();
1242 if (ctx.property.memberState == Context::Property::Uninitialized) {
1243 if (ctx.dbgprint)
1244 printf(format: "Clearing inherited materials\n");
1245 materials.clear(&materials);
1246 ctx.property.memberState = Context::Property::Initialized;
1247 }
1248 materials.append(&materials, mat);
1249 if (ctx.dbgprint)
1250 printf(format: "Appending material to %s\n", ctx.property.name.toLatin1().constData());
1251 }
1252 }
1253
1254 // At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list
1255 ctx.sceneData.materials.push_back(t: mat);
1256 }
1257 } else if (type == TypeInfo<QQuick3DDefaultMaterial>::typeId()) {
1258 const QQuick3DDefaultMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DDefaultMaterial *>(compIt->ptr) : nullptr;
1259 if (QQuick3DDefaultMaterial *mat = buildType(def, ctx, ret, base)) {
1260 // If this is a component we'll store it for lookups later.
1261 if (doRegisterComponent)
1262 registerComponent({ .ptr: mat, .type: type });
1263
1264 if (ctx.property.target) {
1265 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
1266 auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials();
1267 if (ctx.property.memberState == Context::Property::Uninitialized) {
1268 if (ctx.dbgprint)
1269 printf(format: "Clearing inherited materials\n");
1270 materials.clear(&materials);
1271 ctx.property.memberState = Context::Property::Initialized;
1272 }
1273 materials.append(&materials, mat);
1274 if (ctx.dbgprint)
1275 printf(format: "Appending material to %s\n", ctx.property.name.toLatin1().constData());
1276 }
1277 }
1278
1279 // At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list
1280 ctx.sceneData.materials.push_back(t: mat);
1281 }
1282 } else if (type == TypeInfo<QQuick3DCustomMaterial>::typeId()) {
1283 const QQuick3DCustomMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DCustomMaterial *>(compIt->ptr) : nullptr;
1284 if (QQuick3DCustomMaterial *mat = buildType(def, ctx, ret, base)) {
1285 // If this is a component we'll store it for lookups later.
1286 if (doRegisterComponent)
1287 registerComponent({ .ptr: mat, .type: type });
1288
1289 if (ctx.property.target) {
1290 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
1291 auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials();
1292 if (ctx.property.memberState == Context::Property::Uninitialized) {
1293 if (ctx.dbgprint)
1294 printf(format: "Clearing inherited materials\n");
1295 materials.clear(&materials);
1296 ctx.property.memberState = Context::Property::Initialized;
1297 }
1298 materials.append(&materials, mat);
1299 if (ctx.dbgprint)
1300 printf(format: "Appending material to %s\n", ctx.property.name.toLatin1().constData());
1301 }
1302 }
1303
1304 // At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list
1305 ctx.sceneData.materials.push_back(t: mat);
1306 }
1307 } else if (type == TypeInfo<QQuick3DEffect>::typeId()) {
1308 const QQuick3DEffect *base = (compIt != components.cend()) ? qobject_cast<QQuick3DEffect *>(compIt->ptr) : nullptr;
1309 if (QQuick3DEffect *effect = buildType(def, ctx, ret, base)) {
1310 // If this is a component we'll store it for lookups later.
1311 if (doRegisterComponent)
1312 registerComponent({ .ptr: effect, .type: type });
1313
1314 if (ctx.property.target) {
1315 if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
1316 auto effects = qobject_cast<QQuick3DSceneEnvironment *>(object: ctx.property.target)->effects();
1317 if (ctx.property.memberState == Context::Property::Uninitialized) {
1318 if (ctx.dbgprint)
1319 printf(format: "Clearing inherited effects\n");
1320 effects.clear(&effects);
1321 ctx.property.memberState = Context::Property::Initialized;
1322 }
1323 effects.append(&effects, effect);
1324 if (ctx.dbgprint)
1325 printf(format: "Appending effect to %s\n", ctx.property.name.toLatin1().constData());
1326 }
1327 }
1328
1329 // At this point we don't know if this effect is going to be referenced somewhere else, so keep it in the list
1330 ctx.sceneData.effects.push_back(t: effect);
1331 }
1332 } else if (type == TypeInfo<QQuick3DDirectionalLight>::typeId() || type == TypeInfo<QQuick3DPointLight>::typeId() || type == TypeInfo<QQuick3DSpotLight>::typeId()) {
1333 const QQuick3DAbstractLight *base = (compIt != components.cend()) ? qobject_cast<QQuick3DAbstractLight *>(compIt->ptr) : nullptr;
1334 if (QQuick3DAbstractLight *light = buildLight(def, ctx, ret, type, base)) {
1335 // If this is a component we'll store it for lookups later.
1336 if (doRegisterComponent)
1337 registerComponent({ .ptr: light, .type: type });
1338
1339 ctx.sceneData.lights.push_back(t: light);
1340 }
1341 } else if (type == TypeInfo<QQuick3DTexture>::typeId()) {
1342 const QQuick3DTexture *base = (compIt != components.cend()) ? qobject_cast<QQuick3DTexture *>(compIt->ptr) : nullptr;
1343 if (QQuick3DTexture *tex = buildType(def, ctx, ret, base)) {
1344 // If this is a component we'll store it for lookups later.
1345 if (doRegisterComponent)
1346 registerComponent({ .ptr: tex, .type: type });
1347
1348 ctx.sceneData.textures.push_back(t: tex);
1349 }
1350 } else if (type == TypeInfo<QQuick3DModel>::typeId()) {
1351 const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DModel *>(compIt->ptr) : nullptr;
1352 if (auto *model = buildType(def, ctx, ret, base)) {
1353 // If this is a component we'll store it for lookups later.
1354 if (doRegisterComponent)
1355 registerComponent({ model, type });
1356
1357 ctx.sceneData.models.push_back(model);
1358 }
1359 } else if (type == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) {
1360 const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DShaderUtilsShader *>(compIt->ptr) : nullptr;
1361 if (auto *shader = buildType(def, ctx, ret, base)) {
1362 // If this is a component we'll store it for lookups later.
1363 if (doRegisterComponent)
1364 registerComponent({ shader, type });
1365 if (ctx.property.target) {
1366 if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
1367 auto shaders = qobject_cast<QQuick3DShaderUtilsRenderPass *>(object: ctx.property.target)->shaders();
1368 if (ctx.property.memberState == Context::Property::Uninitialized) {
1369 if (ctx.dbgprint)
1370 printf(format: "Clearing inherited shaders\n");
1371 shaders.clear(&shaders);
1372 ctx.property.memberState = Context::Property::Initialized;
1373 }
1374 shaders.append(&shaders, shader);
1375 if (ctx.dbgprint)
1376 printf(format: "Appending shader to %s\n", ctx.property.name.toLatin1().constData());
1377 }
1378 }
1379
1380 ctx.sceneData.shaders.push_back(shader);
1381 }
1382 } else if (type == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
1383 const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DShaderUtilsRenderPass *>(compIt->ptr) : nullptr;
1384 if (auto *pass = buildType(def, ctx, ret, base)) {
1385 // If this is a component we'll store it for lookups later.
1386 if (doRegisterComponent)
1387 registerComponent({ pass, type });
1388 if (ctx.property.target) {
1389 if (ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) {
1390 auto passes = qobject_cast<QQuick3DEffect *>(object: ctx.property.target)->passes();
1391 if (ctx.property.memberState == Context::Property::Uninitialized) {
1392 if (ctx.dbgprint)
1393 printf(format: "Clearing inherited passes\n");
1394 passes.clear(&passes);
1395 ctx.property.memberState = Context::Property::Initialized;
1396 }
1397 passes.append(&passes, pass);
1398 if (ctx.dbgprint)
1399 printf(format: "Appending pass to %s\n", ctx.property.name.toLatin1().constData());
1400 }
1401 }
1402 }
1403 } else if (type == TypeInfo<QQuick3DInstanceList>::typeId()) {
1404 const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DInstanceList *>(compIt->ptr) : nullptr;
1405 if (auto *instanceList = buildType(def, ctx, ret, base)) {
1406 // If this is a component we'll store it for lookups later.
1407 if (doRegisterComponent)
1408 registerComponent({ instanceList, type });
1409 if (ctx.property.target) {
1410 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
1411 qobject_cast<QQuick3DModel *>(object: ctx.property.target)->setInstancing(instanceList);
1412 if (ctx.dbgprint)
1413 printf(format: "Setting instance list on %s\n", ctx.property.name.toLatin1().constData());
1414 }
1415 }
1416 }
1417 } else if (type == TypeInfo<QQuick3DInstanceListEntry>::typeId()) {
1418 const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DInstanceListEntry *>(compIt->ptr) : nullptr;
1419 if (auto *instanceListEntry = buildType(def, ctx, ret, base)) {
1420 // If this is a component we'll store it for lookups later.
1421 if (doRegisterComponent)
1422 registerComponent({ instanceListEntry, type });
1423 if (ctx.property.target) {
1424 if (ctx.property.targetType == TypeInfo<QQuick3DInstanceList>::typeId()) {
1425 auto instances = qobject_cast<QQuick3DInstanceList *>(object: ctx.property.target)->instances();
1426 instances.append(&instances, instanceListEntry);
1427 if (ctx.dbgprint)
1428 printf(format: "Appending instance list entry to %s\n", ctx.property.name.toLatin1().constData());
1429 }
1430 }
1431 }
1432 } else {
1433 if (ctx.dbgprint)
1434 printf(format: "Object def for \'%s\' was not handled\n", ctx.property.name.toLatin1().constData());
1435 return false;
1436 }
1437
1438 return true;
1439}
1440
1441static bool interceptPublicMember(const QQmlJS::AST::UiPublicMember &member, Context &ctx, int &ret)
1442{
1443 Q_UNUSED(ret);
1444 using namespace QQmlJS::AST;
1445
1446 if (ctx.dbgprint)
1447 printf(format: "Intercepted public member!\n");
1448
1449 if (member.statement && member.statement->kind == Node::Kind_ExpressionStatement) {
1450 if ((ctx.property.targetType == TypeInfo<QQuick3DCustomMaterial>::typeId() || ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) && member.memberType) {
1451 // For custom materials we have properties that are user provided, so we'll
1452 // need to add these to the objects properties (we add these here to be able
1453 // to piggyback on the existing type matching code)
1454 if (member.memberType->name == u"real") {
1455 ctx.property.type = QMetaType::Double;
1456 } else if (member.memberType->name == u"bool") {
1457 ctx.property.type = QMetaType::Bool;
1458 } else if (member.memberType->name == u"int") {
1459 ctx.property.type = QMetaType::Int;
1460 } else if (member.memberType->name == u"size") {
1461 ctx.property.type = QMetaType::QSizeF;
1462 } else if (member.memberType->name == u"rect") {
1463 ctx.property.type = QMetaType::QRectF;
1464 } else if (member.memberType->name == u"point") {
1465 ctx.property.type = QMetaType::QPointF;
1466 } else if (member.memberType->name == u"color") {
1467 ctx.property.type = QMetaType::QColor;
1468 } else if (member.memberType->name.startsWith(u"vector")) {
1469 if (member.memberType->name.endsWith(u"2d")) {
1470 ctx.property.type = QMetaType::QVector2D;
1471 } else if (member.memberType->name.endsWith(u"3d")) {
1472 ctx.property.type = QMetaType::QVector3D;
1473 } else if (member.memberType->name.endsWith(u"4d")) {
1474 ctx.property.type = QMetaType::QVector4D;
1475 }
1476 } else if (member.memberType->name == u"matrix4x4") {
1477 ctx.property.type = QMetaType::QMatrix4x4;;
1478 } else if (member.memberType->name == u"quaternion") {
1479 ctx.property.type = QMetaType::QQuaternion;
1480 } else if (member.memberType->name == u"var") {
1481 ctx.property.type = QMetaType::QVariant;
1482 }
1483 }
1484 }
1485
1486 return false;
1487}
1488
1489static bool interceptCallExpression(const QQmlJS::AST::CallExpression &callExpression, Context &ctx, int &ret)
1490{
1491 Q_UNUSED(ret);
1492 using namespace QQmlJS::AST;
1493 using namespace BuiltinHelpers;
1494
1495 if (ctx.dbgprint)
1496 printf(format: "Intercepted call expression!\n");
1497
1498 const bool ok = (ctx.property.target && !ctx.property.name.isEmpty());
1499 if (callExpression.base && ok) {
1500 if (callExpression.base->kind == Node::Kind_FieldMemberExpression) {
1501 const auto &fieldMemberExpression = static_cast<const FieldMemberExpression &>(*callExpression.base);
1502 if (fieldMemberExpression.base) {
1503 if (fieldMemberExpression.base->kind == Node::Kind_IdentifierExpression) {
1504 const auto &identExpr = static_cast<const IdentifierExpression &>(*fieldMemberExpression.base);
1505 if (identExpr.name == u"Qt") {
1506 bool ok = false;
1507 QVariant v;
1508 if (fieldMemberExpression.name == u"point") {
1509 const auto point = toPoint(ArgumentListView(*callExpression.arguments), &ok);
1510 if (ctx.dbgprint)
1511 printf("Qt.point(%f, %f)\n", point.x(), point.y());
1512 setProperty(ctx.property, point);
1513 } else if (fieldMemberExpression.name == u"size") {
1514 const auto size = toSize(ArgumentListView(*callExpression.arguments), &ok);
1515 if (ctx.dbgprint)
1516 printf("Qt.size(%f, %f)\n", size.width(), size.height());
1517 setProperty(ctx.property, size);
1518 } else if (fieldMemberExpression.name == u"rect") {
1519 const auto rect = toRect(ArgumentListView(*callExpression.arguments), &ok);
1520 if (ctx.dbgprint)
1521 printf("Qt.rect(%f, %f, %f, %f)\n", rect.x(), rect.y(), rect.width(), rect.height());
1522 setProperty(ctx.property, rect);
1523 } else if (fieldMemberExpression.name.startsWith(u"vector")) {
1524 if (fieldMemberExpression.name.endsWith(u"2d")) {
1525 const auto vec2 = toVec<QVector2D>(ArgumentListView(*callExpression.arguments), &ok);
1526 if (ctx.dbgprint)
1527 printf("Qt.vector2d(%f, %f)\n", vec2.x(), vec2.y());
1528 setProperty(ctx.property, vec2);
1529 } else if (fieldMemberExpression.name.endsWith(u"3d")) {
1530 const auto vec3 = toVec<QVector3D>(ArgumentListView(*callExpression.arguments), &ok);
1531 if (ctx.dbgprint)
1532 printf("Qt.vector3d(%f, %f, %f)\n", vec3.x(), vec3.y(), vec3.z());
1533 setProperty(ctx.property, vec3);
1534 } else if (fieldMemberExpression.name.endsWith(u"4d")) {
1535 const auto vec4 = toVec<QVector4D>(ArgumentListView(*callExpression.arguments), &ok);
1536 if (ctx.dbgprint)
1537 printf("Qt.vector4d(%f, %f, %f, %f)\n", vec4.x(), vec4.y(), vec4.z(), vec4.w());
1538 setProperty(ctx.property, vec4);
1539 }
1540 } else if (fieldMemberExpression.name == u"matrix4x4") {
1541 const auto mat44 = toMat44(ArgumentListView(*callExpression.arguments), &ok);
1542 if (ctx.dbgprint)
1543 printf("Qt.matrix4x4(%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f)\n",
1544 mat44(0, 0), mat44(0, 1), mat44(0, 2), mat44(0, 3),
1545 mat44(1, 0), mat44(1, 1), mat44(1, 2), mat44(1, 3),
1546 mat44(2, 0), mat44(2, 1), mat44(2, 2), mat44(2, 3),
1547 mat44(3, 0), mat44(3, 1), mat44(3, 2), mat44(3, 3));
1548 setProperty(ctx.property, mat44);
1549 } else if (fieldMemberExpression.name == u"quaternion") {
1550 const auto quat = toQuaternion(ArgumentListView(*callExpression.arguments), &ok);
1551 if (ctx.dbgprint)
1552 printf("Qt.quaternion(%f, %f, %f, %f)\n", quat.scalar(), quat.x(), quat.y(), quat.z());
1553 setProperty(ctx.property, quat);
1554 } else if (fieldMemberExpression.name == u"rgba") {
1555 const auto vec4 = toVec<QVector4D>(ArgumentListView(*callExpression.arguments), &ok);
1556 if (ok) {
1557 QColor color = QColor::fromRgbF(r: vec4.x(), g: vec4.y(), b: vec4.z(), a: vec4.w());
1558 if (ctx.dbgprint)
1559 printf(format: "Qt.rgba(%f, %f, %f, %f)\n", color.redF(), color.greenF(), color.blueF(), color.alphaF());
1560 setProperty(property: ctx.property, v: color);
1561 }
1562 }
1563 if (ok && v.isValid() && ctx.property.target)
1564 ctx.property.target->setProperty(name: ctx.property.name.toLatin1().constData(), value: v);
1565 }
1566 }
1567 }
1568 }
1569 }
1570
1571 return false;
1572}
1573
1574static int parseQmlData(const QByteArray &code, Context &ctx)
1575{
1576 Q_ASSERT(ctx.engine && ctx.engine->lexer());
1577 ctx.identifierMap.clear(); // not visible outside the scope of this "code"
1578 if (ctx.dbgprint)
1579 printf(format: "Parsing %s\n", qPrintable(ctx.currentFileInfo.filePath()));
1580 int ret = 0;
1581 ctx.engine->lexer()->setCode(code: QString::fromUtf8(ba: code), lineno: 1, qmlMode: true);
1582 QQmlJS::Parser parser(ctx.engine);
1583 const bool ok = parser.parse();
1584 if (ok) {
1585 const auto program = parser.ast();
1586 if (program)
1587 Visitors::visit(*program, ctx, ret);
1588 } else {
1589 ret = -1;
1590 qWarning("Parsing failed due to %s in %s:%d%d", qPrintable(parser.errorMessage()), qPrintable(ctx.currentFileInfo.fileName()), parser.errorLineNumber(), parser.errorColumnNumber());
1591 }
1592
1593 return ret;
1594}
1595
1596int MaterialParser::parseQmlData(const QByteArray &code, const QString &fileName, MaterialParser::SceneData &sceneData)
1597{
1598 // set initial type map
1599 *s_typeMap = baseTypeMap();
1600
1601 QQmlJS::Engine engine;
1602 QQmlJS::Lexer lexer(&engine);
1603
1604 Context ctx;
1605 ctx.engine = &engine;
1606 ctx.interceptODFunc = &interceptObjectDef;
1607 ctx.interceptOBFunc = &interceptObjectBinding;
1608 ctx.interceptPMFunc = &interceptPublicMember;
1609 ctx.interceptCallExpr = &interceptCallExpression;
1610 ctx.currentFileInfo = QFileInfo(fileName);
1611 ctx.type = Context::Type::Component;
1612
1613 const int ret = ::parseQmlData(code, ctx);
1614 sceneData = std::move(ctx.sceneData);
1615
1616 return ret;
1617}
1618
1619int MaterialParser::parseQmlFiles(const QVector<QString> &filePaths, const QDir &sourceDir, SceneData &sceneData, bool verboseOutput)
1620{
1621 // set initial type map
1622 *s_typeMap = baseTypeMap();
1623
1624 int ret = 0;
1625
1626 if (filePaths.isEmpty()) {
1627 qWarning(msg: "No input files");
1628 return ret;
1629 }
1630
1631 QQmlJS::Engine engine;
1632 QQmlJS::Lexer lexer(&engine);
1633
1634 Context ctx;
1635 ctx.dbgprint = verboseOutput;
1636 ctx.engine = &engine;
1637 ctx.interceptODFunc = &interceptObjectDef;
1638 ctx.interceptOBFunc = &interceptObjectBinding;
1639 ctx.interceptPMFunc = &interceptPublicMember;
1640 ctx.interceptCallExpr = &interceptCallExpression;
1641 ctx.workingDir = sourceDir;
1642
1643 QVector<QString> deferredOther;
1644 QVector<QString> deferredComponets;
1645
1646 const QString sourcePath = sourceDir.canonicalPath() + QDir::separator();
1647
1648 const bool isMultifile = filePaths.size() != 1;
1649
1650 // Go through and find the material components first
1651 for (const auto &v : filePaths) {
1652 QFileInfo &currentFileInfo = ctx.currentFileInfo;
1653 if (!QFileInfo(v).isAbsolute())
1654 currentFileInfo.setFile(sourcePath + v);
1655 else
1656 currentFileInfo.setFile(v);
1657 const bool maybeComponent = currentFileInfo.fileName().at(i: 0).isUpper();
1658 if (currentFileInfo.isFile() && currentFileInfo.suffix() == getQmlFileExtension()) {
1659 const QString filePath = currentFileInfo.canonicalFilePath();
1660 if (isMultifile && maybeComponent) {
1661 QFile f(filePath);
1662 if (!f.open(flags: QFile::ReadOnly)) {
1663 qWarning(msg: "Could not open file %s for reading!", qPrintable(filePath));
1664 return -1;
1665 }
1666
1667 const QByteArray code = f.readAll();
1668 int idx = code.indexOf(c: '{');
1669 if (idx != -1) {
1670 const QByteArray section = code.mid(index: 0, len: idx);
1671 QVarLengthArray<const char *, 3> componentTypes { TypeInfo<QQuick3DPrincipledMaterial>::qmlTypeName(),
1672 TypeInfo<QQuick3DCustomMaterial>::qmlTypeName(),
1673 TypeInfo<QQuick3DDefaultMaterial>::qmlTypeName()};
1674 for (const auto compType : std::as_const(t&: componentTypes)) {
1675 if ((idx = section.indexOf(bv: compType)) != -1)
1676 break;
1677 }
1678 if (idx != -1) {
1679 ctx.type = Context::Type::Component;
1680 ret = parseQmlData(code, ctx);
1681 if (ret != 0)
1682 break;
1683 } else {
1684 deferredComponets.push_back(t: filePath);
1685 }
1686 } else {
1687 qWarning(msg: "No items found in %s\n", qPrintable(filePath));
1688 }
1689 } else {
1690 deferredOther.push_back(t: filePath);
1691 }
1692 } else {
1693 qWarning(msg: "The file %s is either not a file or has the wrong extension!", qPrintable(v));
1694 }
1695 }
1696
1697 const auto parsePaths = [&ctx, &ret](const QVector<QString> &paths, Context::Type type) {
1698 ctx.type = type;
1699 for (const auto &path : paths) {
1700 QFileInfo &currentFileInfo = ctx.currentFileInfo;
1701 currentFileInfo.setFile(path);
1702 if (currentFileInfo.isFile() && currentFileInfo.suffix() == getQmlFileExtension()) {
1703 const QString filePath = currentFileInfo.canonicalFilePath();
1704 QFile f(filePath);
1705 if (!f.open(flags: QFile::ReadOnly)) {
1706 qWarning(msg: "Could not open file %s for reading!", qPrintable(filePath));
1707 ret = -1;
1708 return;
1709 }
1710
1711 const QByteArray code = f.readAll();
1712 ret = parseQmlData(code, ctx);
1713 if (ret != 0)
1714 break;
1715 }
1716 }
1717 };
1718
1719 // Other components
1720 parsePaths(deferredComponets, Context::Type::Component);
1721
1722 // Now parse the rest
1723 parsePaths(deferredOther, Context::Type::Application);
1724
1725 sceneData = std::move(ctx.sceneData);
1726
1727 return ret;
1728}
1729
1730QT_END_NAMESPACE
1731

source code of qtquick3d/tools/shadergen/parser.cpp