1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
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 tl.append(&tl, sl.at(&sl, i));
404 }
405}
406
407static void cloneProperties(QObject &target, const QObject &source)
408{
409 Q_ASSERT(target.metaObject() == source.metaObject());
410 const auto smo = source.metaObject();
411 auto tmo = target.metaObject();
412 const int propCount = smo->propertyCount();
413 for (int i = 0; i != propCount; ++i) {
414 auto sp = smo->property(index: i);
415 auto tp = tmo->property(index: i);
416 if (sp.typeId() == tp.typeId()) {
417 if (sp.typeId() == TypeInfo<QQuick3DMaterial>::qmlListTypeId())
418 cloneQmlList<QQuick3DMaterial>(so: source, sp, to&: target, tp);
419 else if (sp.typeId() == TypeInfo<QQuick3DEffect>::qmlListTypeId())
420 cloneQmlList<QQuick3DEffect>(so: source, sp, to&: target, tp);
421 else if (sp.typeId() == TypeInfo<QQuick3DShaderUtilsRenderPass>::qmlListTypeId())
422 cloneQmlList<QQuick3DShaderUtilsRenderPass>(so: source, sp, to&: target, tp);
423 else if (sp.typeId() == TypeInfo<QQuick3DShaderUtilsShader>::qmlListTypeId())
424 cloneQmlList<QQuick3DShaderUtilsShader>(so: source, sp, to&: target, tp);
425 else
426 tmo->property(index: i).write(obj: &target, value: smo->property(index: i).read(obj: &source));
427 }
428 }
429
430 // Clone the dynamic properties as well
431 for (const auto &prop : source.dynamicPropertyNames())
432 target.setProperty(name: prop.constData(), value: source.property(name: prop.constData()));
433}
434
435template <typename T>
436inline QVariant fromStringEnumHelper(const QStringView &ref, const QMetaProperty &property)
437{
438 bool ok = false;
439 const int v = property.enumerator().keyToValue(key: ref.toLatin1(), ok: &ok);
440 return ok ? QVariant::fromValue(T(v)) : QVariant();
441}
442
443static QVariant fromString(const QStringView &ref, const Context &ctx)
444{
445 const auto &p = ctx.property;
446 if (!p.target)
447 return QVariant();
448
449 static const auto toBuiltinType = [](int type, const QStringView &ref, const QDir &workingDir) {
450 using namespace BuiltinHelpers;
451 bool ok = false;
452 switch (type) {
453 case QMetaType::Int:
454 {
455 const auto v = ref.toInt(ok: &ok);
456 return (ok ? QVariant::fromValue(value: v) : QVariant());
457 }
458 break;
459 case QMetaType::Bool:
460 {
461 const auto v = ref.toInt(ok: &ok);
462 return (ok ? QVariant::fromValue(value: bool(v)) : QVariant());
463 }
464 case QMetaType::Double:
465 {
466 const auto v = ref.toDouble(ok: &ok);
467 return (ok ? QVariant::fromValue(value: qreal(v)) : QVariant());
468 }
469 break;
470 case QMetaType::QString:
471 return QVariant::fromValue(value: ref);
472 case QMetaType::QUrl:
473 {
474 if (ref.startsWith(c: u':') || ref.startsWith(c: QDir::separator()))
475 return QVariant::fromValue(value: QUrl::fromLocalFile(localfile: ref.toString()));
476 else if (ref.startsWith(c: u'#'))
477 return QVariant::fromValue(value: QUrl(ref.toString()));
478 else
479 return QVariant::fromValue(value: QUrl::fromUserInput(userInput: ref.toString(), workingDirectory: workingDir.canonicalPath()));
480 }
481 case QMetaType::QColor:
482 return QVariant::fromValue(value: QColor(ref));
483 case QMetaType::QTime:
484 return QVariant::fromValue(value: QTime::fromString(string: ref.toString()));
485 case QMetaType::QDate:
486 return QVariant::fromValue(value: QDate::fromString(string: ref.toString()));
487 case QMetaType::QDateTime:
488 return QVariant::fromValue(value: QDateTime::fromString(string: ref.toString()));
489 case QMetaType::QRectF:
490 return QVariant::fromValue(value: toRect(ref));
491 case QMetaType::QPointF:
492 return QVariant::fromValue(value: toPoint(ref));
493 case QMetaType::QSizeF:
494 return QVariant::fromValue(value: toSize(ref));
495 case QMetaType::QVector2D:
496 return QVariant::fromValue(value: toVec<QVector2D>(ref));
497 case QMetaType::QVector3D:
498 return QVariant::fromValue(value: toVec<QVector3D>(ref));
499 case QMetaType::QVector4D:
500 return QVariant::fromValue(value: toVec<QVector4D>(ref));
501 case QMetaType::QQuaternion:
502 return QVariant::fromValue(value: toQuaternion(ref));
503 }
504
505 return QVariant();
506 };
507
508 if (p.type != QMetaType::UnknownType) // Built in Qt types int, vector3d etc
509 return toBuiltinType(p.type, ref, ctx.workingDir);
510
511 // hard mode, detect the property type
512 // We only care about the types that are relevant for us
513 if (p.targetType != QMetaType::UnknownType) {
514 Q_ASSERT(p.target);
515 Q_ASSERT(!p.name.isEmpty());
516 const int idx = p.target->metaObject()->indexOfProperty(name: p.name.toLatin1().constData());
517 const auto property = p.target->metaObject()->property(index: idx);
518 if (property.metaType().id() >= QMetaType::User) {
519 const QMetaType metaType = property.metaType();
520 if (p.targetType == TypeInfo<QQuick3DDefaultMaterial>::typeId() || p.targetType == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
521 // Common for both materials
522 if (metaType.id() == qMetaTypeId<QQuick3DMaterial::CullMode>())
523 return fromStringEnumHelper<QQuick3DMaterial::CullMode>(ref, property);
524 if (metaType.id() == qMetaTypeId<QQuick3DMaterial::TextureChannelMapping>())
525 return fromStringEnumHelper<QQuick3DMaterial::TextureChannelMapping>(ref, property);
526
527 if (p.targetType == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
528 if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::Lighting>())
529 return fromStringEnumHelper<QQuick3DPrincipledMaterial::Lighting>(ref, property);
530 if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::BlendMode>())
531 return fromStringEnumHelper<QQuick3DPrincipledMaterial::BlendMode>(ref, property);
532 if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::AlphaMode>())
533 return fromStringEnumHelper<QQuick3DPrincipledMaterial::AlphaMode>(ref, property);
534 } else if (p.targetType == TypeInfo<QQuick3DDefaultMaterial>::typeId()) {
535 if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::Lighting>())
536 return fromStringEnumHelper<QQuick3DDefaultMaterial::Lighting>(ref, property);
537 if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::BlendMode>())
538 return fromStringEnumHelper<QQuick3DDefaultMaterial::BlendMode>(ref, property);
539 if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::SpecularModel>())
540 return fromStringEnumHelper<QQuick3DDefaultMaterial::SpecularModel>(ref, property);
541 }
542 } else if (p.targetType == TypeInfo<QQuick3DCustomMaterial>::typeId()) {
543 if (metaType.id() == qMetaTypeId<QQuick3DCustomMaterial::ShadingMode>())
544 return fromStringEnumHelper<QQuick3DCustomMaterial::ShadingMode>(ref, property);
545 if (metaType.id() == qMetaTypeId<QQuick3DCustomMaterial::BlendMode>())
546 return fromStringEnumHelper<QQuick3DCustomMaterial::BlendMode>(ref, property);
547 } else if (p.targetType == TypeInfo<QQuick3DSpotLight>::typeId() || p.targetType == TypeInfo<QQuick3DPointLight>::typeId()) {
548 if (metaType.id() == qMetaTypeId<QQuick3DAbstractLight::QSSGShadowMapQuality>())
549 return fromStringEnumHelper<QQuick3DAbstractLight::QSSGShadowMapQuality>(ref, property);
550 } else if (p.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
551 if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes>())
552 return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes>(ref, property);
553 if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues>())
554 return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues>(ref, property);
555 if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues>())
556 return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues>(ref, property);
557 if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentTonemapModes>())
558 return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentTonemapModes>(ref, property);
559 } else if (p.targetType == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) {
560 if (metaType.id() == qMetaTypeId<QQuick3DShaderUtilsShader::Stage>())
561 return fromStringEnumHelper<QQuick3DShaderUtilsShader::Stage>(ref, property);
562 }
563 } else { // Qt type
564 return toBuiltinType(property.metaType().id(), ref, ctx.workingDir);
565 }
566 }
567
568 if (ctx.dbgprint)
569 printf(format: "Unhandled type for property %s\n", ref.toLatin1().constData());
570
571 return QVariant();
572}
573
574static QString getQmlFileExtension() { return QStringLiteral("qml"); }
575
576struct Visitors
577{
578 static void visit(const QQmlJS::AST::UiProgram &program, Context &ctx, int &ret)
579 {
580 using namespace QQmlJS::AST;
581 const bool readHeaders = false;
582 if (readHeaders && program.headers) { // No real need for us to look at the includes
583 using HeaderItem = UiHeaderItemList;
584 using Headers = InvasiveListView<HeaderItem>;
585
586 Headers headers(*program.headers);
587 for (const auto &header : headers)
588 printf("Type: %d\n", header.kind);
589 }
590 if (program.members)
591 visit(*program.members, ctx, ret);
592 }
593 static void visit(const QQmlJS::AST::UiObjectInitializer &objInitializer, Context &ctx, int &ret)
594 {
595 using namespace QQmlJS::AST;
596 if (objInitializer.members)
597 visit(*objInitializer.members, ctx, ret);
598 }
599
600 static void visit(const QQmlJS::AST::UiObjectDefinition &def, Context &ctx, int &ret)
601 {
602 using namespace QQmlJS::AST;
603 if (ctx.dbgprint)
604 printf("Object definition -> %s\n", def.qualifiedTypeNameId->name.toLocal8Bit().constData());
605 if (!(ctx.interceptODFunc && ctx.interceptODFunc(def, ctx, ret))) {
606 if (def.initializer)
607 visit(*static_cast<UiObjectInitializer *>(def.initializer), ctx, ret);
608 }
609 }
610
611 static void visit(const QQmlJS::AST::UiArrayMemberList &memberList, Context &ctx, int &ret)
612 {
613 using namespace QQmlJS::AST;
614 using ArrayMemberItem = UiArrayMemberList;
615 using ArrayMembers = InvasiveListView<ArrayMemberItem>;
616
617 ArrayMembers arrayMembers(memberList);
618 for (auto &object : arrayMembers) {
619 if (object.member->kind == Node::Kind_UiObjectDefinition) {
620 const auto &def = *static_cast<UiObjectDefinition *>(object.member);
621 visit(def, ctx, ret);
622 }
623 }
624 }
625
626 static void visit(const QQmlJS::AST::UiScriptBinding &binding, Context &ctx, int &ret)
627 {
628 using namespace QQmlJS::AST;
629 if (ctx.dbgprint)
630 printf("Script binding -> %s ", binding.qualifiedId->name.toLocal8Bit().constData());
631
632 const auto oldName = ctx.property.name; // reentrancy
633 ctx.property.name = binding.qualifiedId->name;
634
635 if (binding.statement) {
636 if (binding.statement->kind == Node::Kind_ExpressionStatement) {
637 const auto &expressionStatement = static_cast<const ExpressionStatement &>(*binding.statement);
638 visit(expressionStatement, ctx, ret);
639 }
640 }
641 ctx.property.name = oldName;
642 }
643
644 static void visit(const QQmlJS::AST::UiArrayBinding &arrayBinding, Context &ctx, int &ret)
645 {
646 using namespace QQmlJS::AST;
647 if (ctx.dbgprint)
648 printf("Array binding(s) -> %s: [\n", arrayBinding.qualifiedId->name.toLocal8Bit().constData());
649
650 const auto oldName = ctx.property.name; // reentrancy
651 ctx.property.name = arrayBinding.qualifiedId->name;
652
653 if (arrayBinding.members)
654 visit(*arrayBinding.members, ctx, ret);
655
656 if (ctx.dbgprint)
657 printf(format: "]\n");
658
659 ctx.property.name = oldName;
660 }
661
662 static void visit(const QQmlJS::AST::UiObjectBinding &objectBinding, Context &ctx, int &ret)
663 {
664 using namespace QQmlJS::AST;
665
666 if (ctx.dbgprint)
667 printf("Object binding -> %s: %s {\n", objectBinding.qualifiedId->name.toLocal8Bit().constData(), objectBinding.qualifiedTypeNameId->name.toLocal8Bit().constData());
668
669 if (objectBinding.initializer) {
670 if (!(ctx.interceptOBFunc && ctx.interceptOBFunc(objectBinding, ctx, ret)))
671 visit(*objectBinding.initializer, ctx, ret);
672 }
673
674 if (ctx.dbgprint)
675 printf(format: "}\n");
676 }
677
678 static void visit(const QQmlJS::AST::UiPublicMember &member, Context &ctx, int &ret)
679 {
680 using namespace QQmlJS::AST;
681
682 if (ctx.dbgprint)
683 printf("%s member -> %s ", (member.type == UiPublicMember::Signal ? "Signal" : "Property"), member.name.toLocal8Bit().constData());
684
685 auto name = ctx.property.name;
686 ctx.property.name = member.name;
687 const auto typeCpy = ctx.property.type;
688
689 if (!(ctx.interceptPMFunc && ctx.interceptPMFunc(member, ctx, ret))) {
690 if (member.statement) {
691 const auto &statement = member.statement;
692 if (statement->kind == Node::Kind_ExpressionStatement)
693 visit(static_cast<const ExpressionStatement &>(*statement), ctx, ret);
694 else if (ctx.dbgprint)
695 printf("Unhandled statement (%d)\n", statement->kind);
696 } else if (member.binding) {
697 const auto &binding = member.binding;
698 if (binding->kind == Node::Kind_UiObjectBinding)
699 visit(static_cast<const UiObjectBinding &>(*binding), ctx, ret);
700 else if (ctx.dbgprint)
701 printf("Unhandled binding (%d)\n", binding->kind);
702 }
703 }
704
705 qSwap(value1&: ctx.property.name, value2&: name);
706 ctx.property.type = typeCpy;
707 }
708
709 static void visit(const QQmlJS::AST::ExpressionStatement &exprStatement, Context &ctx, int &ret)
710 {
711 using namespace QQmlJS::AST;
712 if (exprStatement.expression) {
713 if (exprStatement.expression->kind == Node::Kind_IdentifierExpression) {
714 const auto &identExpression = static_cast<const IdentifierExpression &>(*exprStatement.expression);
715 visit(identExpression, ctx, ret);
716 } else if (exprStatement.expression->kind == Node::Kind_StringLiteral) {
717 const auto &stringLiteral = static_cast<const StringLiteral &>(*exprStatement.expression);
718 visit(stringLiteral, ctx, ret);
719 } else if (exprStatement.expression->kind == Node::Kind_NumericLiteral) {
720 const auto &numericLiteral = static_cast<const NumericLiteral &>(*exprStatement.expression);
721 visit(numericLiteral, ctx, ret);
722 } else if (exprStatement.expression->kind == Node::Kind_FieldMemberExpression) {
723 const auto &fieldMemberExpression = static_cast<const FieldMemberExpression &>(*exprStatement.expression);
724 visit(fieldMemberExpression, ctx, ret);
725 } else if (exprStatement.expression->kind == Node::Kind_TrueLiteral || exprStatement.expression->kind == Node::Kind_FalseLiteral) {
726 const bool v = (exprStatement.expression->kind == Node::Kind_TrueLiteral);
727 if (ctx.dbgprint)
728 printf(format: "-> TrueLiteral: %s\n", v ? "true" : "false");
729 if (ctx.property.target) {
730 auto target = ctx.property.target;
731 const auto &name = ctx.property.name;
732 target->setProperty(name: name.toLatin1(), value: QVariant::fromValue(value: v));
733 }
734 } else if (exprStatement.expression->kind == Node::Kind_ArrayPattern) {
735 const auto &arrayPattern = static_cast<const ArrayPattern &>(*exprStatement.expression);
736 visit(arrayPattern, ctx, ret);
737 } else if (exprStatement.expression->kind == Node::Kind_CallExpression) {
738 const auto &callExpression = static_cast<const CallExpression &>(*exprStatement.expression);
739 visit(callExpression, ctx, ret);
740 } else if (exprStatement.expression->kind == Node::Kind_UnaryMinusExpression) {
741 const auto &unaryMinusExpr = static_cast<const UnaryMinusExpression &>(*exprStatement.expression);
742 if (unaryMinusExpr.expression && unaryMinusExpr.expression->kind == Node::Kind_NumericLiteral) {
743 auto &numericLiteral = static_cast<NumericLiteral &>(*unaryMinusExpr.expression);
744 const auto value = numericLiteral.value;
745 numericLiteral.value *= -1;
746 visit(numericLiteral, ctx, ret);
747 numericLiteral.value = value;
748 }
749 } else if (exprStatement.expression->kind == Node::Kind_UnaryPlusExpression) {
750 const auto &unaryPlusExpr = static_cast<const UnaryPlusExpression &>(*exprStatement.expression);
751 if (unaryPlusExpr.expression)
752 visit(static_cast<const NumericLiteral &>(*unaryPlusExpr.expression), ctx, ret);
753 } else {
754 if (ctx.dbgprint)
755 printf("<expression: %d>\n", exprStatement.expression->kind);
756 }
757 }
758 }
759
760 static void visit(const QQmlJS::AST::IdentifierExpression &idExpr, Context &ctx, int &ret)
761 {
762 Q_UNUSED(ret);
763 if (ctx.dbgprint)
764 printf("-> Identifier: %s\n", idExpr.name.toLocal8Bit().constData());
765
766 if (ctx.property.target && ctx.type != Context::Type::Component) {
767 const auto foundIt = ctx.identifierMap.constFind(idExpr.name);
768 const auto end = ctx.identifierMap.constEnd();
769 if (foundIt != end) { // If an item was found it means this is a reference
770 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
771 if (QQuick3DMaterial *mat = qobject_cast<QQuick3DMaterial *>(*foundIt)) {
772 auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials();
773 // Since we are initializing this for the first time, make sure we clean out any inherited data!
774 if (ctx.property.memberState == Context::Property::Uninitialized) {
775 if (ctx.dbgprint)
776 printf(format: "Clearing inherited materials\n");
777 materials.clear(&materials);
778 ctx.property.memberState = Context::Property::Initialized;
779 }
780 materials.append(&materials, mat);
781 if (ctx.dbgprint)
782 printf(format: "Appending material to %s\n", ctx.property.name.toLatin1().constData());
783 } else if (QQuick3DInstanceList *instancingList = qobject_cast<QQuick3DInstanceList *>(*foundIt)) {
784 qobject_cast<QQuick3DModel *>(object: ctx.property.target)->setInstancing(instancingList);
785 if (ctx.dbgprint)
786 printf(format: "Setting instance list on model\n");
787 }
788 } else if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
789 if (QQuick3DEffect *effect = qobject_cast<QQuick3DEffect *>(*foundIt)) {
790 auto effects = qobject_cast<QQuick3DSceneEnvironment *>(object: ctx.property.target)->effects();
791 // Since we are initializing this for the first time, make sure we clean out any inherited data!
792 if (ctx.property.memberState == Context::Property::Uninitialized) {
793 if (ctx.dbgprint)
794 printf(format: "Clearing inherited effects\n");
795 effects.clear(&effects);
796 ctx.property.memberState = Context::Property::Initialized;
797 }
798 effects.append(&effects, effect);
799 if (ctx.dbgprint)
800 printf(format: "Appending effect to \'%s\'\n", ctx.property.name.toLatin1().constData());
801 }
802 } else if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
803 if (QQuick3DShaderUtilsShader *shader = qobject_cast<QQuick3DShaderUtilsShader *>(*foundIt)) {
804 auto shaders = qobject_cast<QQuick3DShaderUtilsRenderPass *>(object: ctx.property.target)->shaders();
805 // Since we are initializing this for the first time, make sure we clean out any inherited data!
806 if (ctx.property.memberState == Context::Property::Uninitialized) {
807 if (ctx.dbgprint)
808 printf(format: "Clearing inherited shaders\n");
809 shaders.clear(&shaders);
810 ctx.property.memberState = Context::Property::Initialized;
811 }
812 shaders.append(&shaders, shader);
813 if (ctx.dbgprint)
814 printf(format: "Appending shader to \'%s\'\n", ctx.property.name.toLatin1().constData());
815 }
816 } else if (ctx.property.targetType == TypeInfo<QQuick3DInstanceList>::typeId()) {
817 if (QQuick3DInstanceListEntry *listEntry = qobject_cast<QQuick3DInstanceListEntry *>(*foundIt)) {
818 auto instances = qobject_cast<QQuick3DInstanceList *>(object: ctx.property.target)->instances();
819 // Since we are initializing this for the first time, make sure we clean out any inherited data!
820 if (ctx.property.memberState == Context::Property::Uninitialized) {
821 if (ctx.dbgprint)
822 printf(format: "Clearing inherited instances\n");
823 instances.clear(&instances);
824 ctx.property.memberState = Context::Property::Initialized;
825 }
826 instances.append(&instances, listEntry);
827 if (ctx.dbgprint)
828 printf(format: "Appending instance entry to %s\n", ctx.property.name.toLatin1().constData());
829 } else if (QQuick3DInstanceList *instancingList = qobject_cast<QQuick3DInstanceList *>(*foundIt)) {
830 qobject_cast<QQuick3DModel *>(object: ctx.property.target)->setInstancing(instancingList);
831 if (ctx.dbgprint)
832 printf(format: "Setting instance list on model\n");
833 }
834 } else if (ctx.dbgprint) {
835 printf("Unhandled binding: %s\n", idExpr.name.toLatin1().constData());
836 }
837 } else {
838 // 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).
839 // 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).
840 ctx.identifierMap.insert(idExpr.name, ctx.property.target);
841 }
842 }
843 }
844
845 static void visit(const QQmlJS::AST::StringLiteral &stringLiteral, Context &ctx, int &ret)
846 {
847 Q_UNUSED(ret);
848 if (ctx.dbgprint)
849 printf("-> StringLiteral: \"%s\"\n", stringLiteral.value.toLocal8Bit().constData());
850
851 if (ctx.property.target) {
852 const auto &name = ctx.property.name;
853 const auto v = fromString(stringLiteral.value, ctx);
854 if (v.isValid()) {
855 const bool b = ctx.property.target->setProperty(name.toLatin1(), v);
856 if (b && ctx.dbgprint)
857 printf(format: "Property %s updated!\n", name.toLatin1().constData());
858 }
859 }
860 }
861
862 static void visit(const QQmlJS::AST::NumericLiteral &numericLiteral, Context &ctx, int &ret)
863 {
864 Q_UNUSED(ret);
865 if (ctx.dbgprint)
866 printf("-> NumericLiteral: %f\n", numericLiteral.value);
867
868 if (ctx.property.target) {
869 auto target = ctx.property.target;
870 const auto &name = ctx.property.name;
871 target->setProperty(name.toLatin1(), QVariant::fromValue(numericLiteral.value));
872 }
873 }
874
875 static void visit(const QQmlJS::AST::FieldMemberExpression &fieldMemberExpression, Context &ctx, int &ret)
876 {
877 using namespace QQmlJS::AST;
878
879 Q_UNUSED(ret);
880 if (ctx.dbgprint)
881 printf("-> FieldMemberExpression: %s\n", fieldMemberExpression.name.toLocal8Bit().constData());
882
883 if (ctx.property.target) {
884 const auto &name = ctx.property.name;
885 const auto v = fromString(fieldMemberExpression.name, ctx);
886 if (v.isValid())
887 ctx.property.target->setProperty(name.toLatin1(), v);
888 }
889 }
890
891 static void visit(const QQmlJS::AST::ArrayPattern &arrayPattern, Context &ctx, int &ret)
892 {
893 Q_UNUSED(ret);
894 if (ctx.dbgprint)
895 printf(format: "-> [ ");
896
897 using namespace QQmlJS::AST;
898 using PatternElementItem = PatternElementList;
899 using PatternElementListView = InvasiveListView<PatternElementItem>;
900
901 PatternElementListView elements(*arrayPattern.elements);
902 for (auto &element : elements) {
903 auto patternElement = element.element;
904 if (patternElement->type == PatternElement::Literal) {
905 if (patternElement->initializer && patternElement->initializer->kind == Node::Kind_IdentifierExpression) {
906 const auto &identExpression = static_cast<const IdentifierExpression &>(*patternElement->initializer);
907 visit(identExpression, ctx, ret);
908 }
909 } else if (ctx.dbgprint) {
910 printf("Unahandled(%d), ", patternElement->type);
911 }
912 }
913
914 if (ctx.dbgprint)
915 printf(format: " ]\n");
916
917 }
918
919 static void visit(const QQmlJS::AST::CallExpression &callExpression, Context &ctx, int &ret)
920 {
921 using namespace QQmlJS::AST;
922
923 Q_UNUSED(ret);
924 Q_UNUSED(callExpression);
925 if (ctx.dbgprint)
926 printf("-> Call(%d)\n", callExpression.base->kind);
927
928 (ctx.interceptCallExpr && ctx.interceptCallExpr(callExpression, ctx, ret));
929 }
930
931 static void visit(const QQmlJS::AST::UiObjectMember &member, Context &ctx, int &ret)
932 {
933 using namespace QQmlJS::AST;
934
935 if (member.kind == Node::Kind_UiObjectBinding)
936 visit(static_cast<const UiObjectBinding &>(member), ctx, ret);
937 else if (ctx.dbgprint)
938 printf("Unhandled member (%d)\n", member.kind);
939 }
940
941 static void visit(const QQmlJS::AST::UiObjectMemberList &memberList, Context &ctx, int &ret)
942 {
943 using namespace QQmlJS::AST;
944 using ObjectMemberItem = UiObjectMemberList;
945 using ObjectMembers = InvasiveListView<ObjectMemberItem>;
946
947 const auto oldEvalType = ctx.property.memberState;
948
949 ObjectMembers objectMembers(memberList);
950 for (const auto &member : objectMembers) {
951 if (member.member) {
952 if (member.member->kind == Node::Kind_UiScriptBinding) {
953 ctx.property.memberState = Context::Property::MemberState::Uninitialized;
954 const auto &scriptBinding = static_cast<const UiScriptBinding &>(*member.member);
955 visit(scriptBinding, ctx, ret);
956 } else if (member.member->kind == Node::Kind_UiArrayBinding) {
957 ctx.property.memberState = Context::Property::MemberState::Uninitialized;
958 const auto &arrayBinding = static_cast<const UiArrayBinding &>(*member.member);
959 visit(arrayBinding, ctx, ret);
960 } else if (member.member->kind == Node::Kind_UiObjectDefinition) {
961 const auto &objectDef = static_cast<const UiObjectDefinition &>(*member.member);
962 visit(objectDef, ctx, ret);
963 } else if (member.member->kind == Node::Kind_UiObjectBinding) {
964 ctx.property.memberState = Context::Property::MemberState::Uninitialized;
965 const auto &objBinding = static_cast<const UiObjectBinding &>(*member.member);
966 visit(objBinding, ctx, ret);
967 } else if (member.member->kind == Node::Kind_UiPublicMember) {
968 ctx.property.memberState = Context::Property::MemberState::Uninitialized;
969 const auto &pubMember = static_cast<const UiPublicMember &>(*member.member);
970 visit(pubMember, ctx, ret);
971 } else {
972 if (ctx.dbgprint)
973 printf("<member %d>\n", member.member->kind);
974 }
975 }
976 }
977
978 ctx.property.memberState = oldEvalType;
979 }
980
981private:
982 Visitors() = delete;
983 Q_DISABLE_COPY(Visitors);
984};
985
986template <typename O, typename T>
987T *buildType(const O &obj, Context &ctx, int &ret, const T *base = nullptr)
988{
989 // swap -> reentrancy
990 Context::Property property;
991 qSwap(value1&: property, value2&: ctx.property);
992 Q_ASSERT(ctx.property.target == nullptr);
993
994 T *instance = nullptr;
995
996 if (ctx.dbgprint)
997 printf("Building %s!\n", TypeInfo<T>::qmlTypeName());
998
999 if (obj.initializer) {
1000 instance = new T;
1001 if (base)
1002 cloneProperties(*instance, *base);
1003
1004 if (obj.initializer) {
1005 ctx.property.target = instance;
1006 ctx.property.targetType = TypeInfo<T>::typeId();
1007 Visitors::visit(*obj.initializer, ctx, ret);
1008 }
1009 }
1010
1011 // swap back
1012 qSwap(value1&: property, value2&: ctx.property);
1013
1014 return instance;
1015}
1016
1017static QQuick3DAbstractLight *buildLight(const QQmlJS::AST::UiObjectDefinition &def,
1018 Context &ctx,
1019 int &ret,
1020 int lightType,
1021 const QQuick3DAbstractLight *base = nullptr)
1022{
1023 if (lightType == TypeInfo<QQuick3DDirectionalLight>::typeId())
1024 return buildType(def, ctx, ret, qobject_cast<const QQuick3DDirectionalLight *>(object: base));
1025 if (lightType == TypeInfo<QQuick3DPointLight>::typeId())
1026 return buildType(def, ctx, ret, qobject_cast<const QQuick3DPointLight *>(object: base));
1027 if (lightType == TypeInfo<QQuick3DSpotLight>::typeId())
1028 return buildType(def, ctx, ret, qobject_cast<const QQuick3DSpotLight *>(object: base));
1029 return nullptr;
1030}
1031
1032
1033template <typename T>
1034static void updateProperty(Context &ctx, T type, QStringView propName)
1035{
1036 if (ctx.property.target) {
1037 if (ctx.dbgprint)
1038 printf(format: "Updating property %s\n", propName.toLatin1().constData());
1039 const auto &target = ctx.property.target;
1040 if (ctx.property.memberState == Context::Property::Uninitialized) {
1041 target->setProperty(propName.toLatin1().constData(), QVariant::fromValue(type));
1042 ctx.property.memberState = Context::Property::Initialized;
1043 } else {
1044 const int idx = target->metaObject()->indexOfProperty(name: propName.toLatin1().constData());
1045 if (idx != -1) {
1046 auto prop = target->metaObject()->property(index: idx);
1047 prop.write(target, QVariant::fromValue(type));
1048 }
1049 }
1050 }
1051}
1052
1053static bool interceptObjectBinding(const QQmlJS::AST::UiObjectBinding &objectBinding, Context &ctx, int &ret)
1054{
1055 if (ctx.dbgprint)
1056 printf(format: "Intercepted object binding!\n");
1057
1058 bool handled = false;
1059
1060 const auto &typeName = objectBinding.qualifiedTypeNameId->name.toString();
1061 const auto &propName = objectBinding.qualifiedId->name;
1062
1063 int type = -1;
1064
1065 // Base type?
1066 const auto typeIt = s_typeMap->constFind(typeName);
1067 if (typeIt != s_typeMap->cend())
1068 type = *typeIt;
1069
1070 // Component?
1071 auto &components = ctx.components;
1072 const auto compIt = (type == -1) ? components.constFind(typeName) : components.cend();
1073 QObject *base = nullptr;
1074 if (compIt != components.cend()) {
1075 type = compIt->type;
1076 base = compIt->ptr;
1077 }
1078
1079 if (type != -1) {
1080 if (ctx.dbgprint)
1081 printf("Resolving: \'%s\'\n", qPrintable(typeName));
1082
1083 if (type == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
1084 if (ctx.property.targetType == TypeInfo<QQuick3DViewport>::typeId()) {
1085 if (auto environment = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DSceneEnvironment *>(base))) {
1086 auto viewport = qobject_cast<QQuick3DViewport *>(object: ctx.property.target);
1087 Q_ASSERT(viewport);
1088 viewport->setEnvironment(environment);
1089 handled = true;
1090 }
1091 }
1092 } else if (type == TypeInfo<QQuick3DTexture>::typeId()) {
1093 if (auto tex = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DTexture *>(base))) {
1094 updateProperty(ctx, tex, propName);
1095 ctx.sceneData.textures.append(tex);
1096 }
1097 handled = true;
1098 } else if (type == TypeInfo<QQuick3DShaderUtilsTextureInput>::typeId()) {
1099 auto texInput = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsTextureInput *>(object: base));
1100 if (texInput && texInput->texture()) {
1101 updateProperty(ctx, texInput, propName);
1102 ctx.sceneData.textures.append(texInput->texture());
1103 }
1104 handled = true;
1105 } else if (type == TypeInfo<QQuick3DEffect>::typeId()) {
1106 if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
1107 if (auto effect = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DEffect *>(base))) {
1108 auto sceneEnvironment = qobject_cast<QQuick3DSceneEnvironment *>(object: ctx.property.target);
1109 Q_ASSERT(sceneEnvironment);
1110 auto effects = sceneEnvironment->effects();
1111 effects.append(&effects, effect);
1112 handled = true;
1113 }
1114 }
1115 } else if (type == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
1116 if (ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) {
1117 if (auto pass = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsRenderPass *>(base))) {
1118 auto effect = qobject_cast<QQuick3DEffect *>(object: ctx.property.target);
1119 Q_ASSERT(effect);
1120 auto passes = effect->passes();
1121 passes.append(&passes, pass);
1122 handled = true;
1123 }
1124 }
1125 } else if (type == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) {
1126 if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
1127 if (auto shader = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsShader *>(base))) {
1128 auto pass = qobject_cast<QQuick3DShaderUtilsRenderPass *>(object: ctx.property.target);
1129 Q_ASSERT(pass);
1130 auto shaders = pass->shaders();
1131 shaders.append(&shaders, shader);
1132 handled = true;
1133 }
1134 }
1135 } else if (type == TypeInfo<QQuick3DDefaultMaterial>::typeId()) {
1136 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
1137 if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DDefaultMaterial *>(base))) {
1138 auto model = qobject_cast<QQuick3DModel *>(object: ctx.property.target);
1139 Q_ASSERT(model);
1140 auto materials = model->materials();
1141 materials.append(&materials, mat);
1142 handled = true;
1143 if (ctx.dbgprint)
1144 printf(format: "Appending material to %s\n", ctx.property.name.toLatin1().constData());
1145 }
1146 }
1147 } else if (type == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
1148 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
1149 if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DPrincipledMaterial *>(base))) {
1150 auto model = qobject_cast<QQuick3DModel *>(object: ctx.property.target);
1151 Q_ASSERT(model);
1152 auto materials = model->materials();
1153 materials.append(&materials, mat);
1154 handled = true;
1155 }
1156 }
1157 } else if (type == TypeInfo<QQuick3DCustomMaterial>::typeId()) {
1158 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
1159 if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DCustomMaterial *>(base))) {
1160 auto model = qobject_cast<QQuick3DModel *>(object: ctx.property.target);
1161 Q_ASSERT(model);
1162 auto materials = model->materials();
1163 materials.append(&materials, mat);
1164 handled = true;
1165 }
1166 }
1167 } else if (ctx.dbgprint) {
1168 printf(format: "Unhandled type\n");
1169 }
1170 }
1171
1172 return handled;
1173}
1174
1175static bool interceptObjectDef(const QQmlJS::AST::UiObjectDefinition &def, Context &ctx, int &ret)
1176{
1177 const auto &typeName = def.qualifiedTypeNameId->name.toString();
1178
1179 if (ctx.dbgprint)
1180 printf("Intercepted object definition (\'%s\')!\n", typeName.toLatin1().constData());
1181
1182 QString componentName;
1183 int type = -1;
1184 bool doRegisterComponent = false;
1185
1186 // Base type?
1187 const auto typeIt = s_typeMap->constFind(typeName);
1188 if (typeIt != s_typeMap->cend())
1189 type = *typeIt;
1190
1191 // Component?
1192 auto &components = ctx.components;
1193 const auto compIt = (type == -1) ? components.constFind(typeName) : components.cend();
1194 if (compIt != components.cend())
1195 type = compIt->type;
1196
1197 // If this is a new component register it
1198 if (ctx.type == Context::Type::Component && ctx.property.target == nullptr && type != -1) {
1199 const auto &fileName = ctx.currentFileInfo.fileName();
1200 componentName = fileName.left(n: fileName.size() - 4);
1201 doRegisterComponent = !componentName.isEmpty();
1202 }
1203
1204 const auto registerComponent = [&ctx, &components, &componentName](Context::Component component) {
1205 if (ctx.dbgprint)
1206 printf(format: "Registering component \'%s\'\n", qPrintable(componentName));
1207 components.insert(key: componentName, value: component);
1208 };
1209
1210 if (type == TypeInfo<QQuick3DViewport>::typeId()) {
1211 const QQuick3DViewport *base = (compIt != components.cend()) ? qobject_cast<QQuick3DViewport *>(compIt->ptr) : nullptr;
1212 if (QQuick3DViewport *viewport = buildType(def, ctx, ret, base)) {
1213 // If this is a component we'll store it for lookups later.
1214 if (doRegisterComponent)
1215 registerComponent({ .ptr: viewport, .type: type });
1216 // Only one viewport supported atm (see SceneEnvironment case as well).
1217 if (!ctx.sceneData.viewport)
1218 ctx.sceneData.viewport = viewport;
1219 }
1220 } else if (type == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
1221 const QQuick3DSceneEnvironment *base = (compIt != components.cend()) ? qobject_cast<QQuick3DSceneEnvironment *>(compIt->ptr) : nullptr;
1222 if (QQuick3DSceneEnvironment *sceneEnv = buildType(def, ctx, ret, base)) {
1223 // If this is a component we'll store it for lookups later.
1224 if (doRegisterComponent)
1225 registerComponent({ .ptr: sceneEnv, .type: type });
1226
1227 if (ctx.sceneData.viewport)
1228 ctx.sceneData.viewport->setEnvironment(sceneEnv);
1229 }
1230 } else if (type == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
1231 const QQuick3DPrincipledMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DPrincipledMaterial *>(compIt->ptr) : nullptr;
1232 if (QQuick3DPrincipledMaterial *mat = buildType(def, ctx, ret, base)) {
1233 // If this is a component we'll store it for lookups later.
1234 if (doRegisterComponent)
1235 registerComponent({ .ptr: mat, .type: type });
1236
1237 if (ctx.property.target) {
1238 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
1239 auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials();
1240 if (ctx.property.memberState == Context::Property::Uninitialized) {
1241 if (ctx.dbgprint)
1242 printf(format: "Clearing inherited materials\n");
1243 materials.clear(&materials);
1244 ctx.property.memberState = Context::Property::Initialized;
1245 }
1246 materials.append(&materials, mat);
1247 if (ctx.dbgprint)
1248 printf(format: "Appending material to %s\n", ctx.property.name.toLatin1().constData());
1249 }
1250 }
1251
1252 // At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list
1253 ctx.sceneData.materials.push_back(t: mat);
1254 }
1255 } else if (type == TypeInfo<QQuick3DDefaultMaterial>::typeId()) {
1256 const QQuick3DDefaultMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DDefaultMaterial *>(compIt->ptr) : nullptr;
1257 if (QQuick3DDefaultMaterial *mat = buildType(def, ctx, ret, base)) {
1258 // If this is a component we'll store it for lookups later.
1259 if (doRegisterComponent)
1260 registerComponent({ .ptr: mat, .type: type });
1261
1262 if (ctx.property.target) {
1263 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
1264 auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials();
1265 if (ctx.property.memberState == Context::Property::Uninitialized) {
1266 if (ctx.dbgprint)
1267 printf(format: "Clearing inherited materials\n");
1268 materials.clear(&materials);
1269 ctx.property.memberState = Context::Property::Initialized;
1270 }
1271 materials.append(&materials, mat);
1272 if (ctx.dbgprint)
1273 printf(format: "Appending material to %s\n", ctx.property.name.toLatin1().constData());
1274 }
1275 }
1276
1277 // At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list
1278 ctx.sceneData.materials.push_back(t: mat);
1279 }
1280 } else if (type == TypeInfo<QQuick3DCustomMaterial>::typeId()) {
1281 const QQuick3DCustomMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DCustomMaterial *>(compIt->ptr) : nullptr;
1282 if (QQuick3DCustomMaterial *mat = buildType(def, ctx, ret, base)) {
1283 // If this is a component we'll store it for lookups later.
1284 if (doRegisterComponent)
1285 registerComponent({ .ptr: mat, .type: type });
1286
1287 if (ctx.property.target) {
1288 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
1289 auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials();
1290 if (ctx.property.memberState == Context::Property::Uninitialized) {
1291 if (ctx.dbgprint)
1292 printf(format: "Clearing inherited materials\n");
1293 materials.clear(&materials);
1294 ctx.property.memberState = Context::Property::Initialized;
1295 }
1296 materials.append(&materials, mat);
1297 if (ctx.dbgprint)
1298 printf(format: "Appending material to %s\n", ctx.property.name.toLatin1().constData());
1299 }
1300 }
1301
1302 // At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list
1303 ctx.sceneData.materials.push_back(t: mat);
1304 }
1305 } else if (type == TypeInfo<QQuick3DEffect>::typeId()) {
1306 const QQuick3DEffect *base = (compIt != components.cend()) ? qobject_cast<QQuick3DEffect *>(compIt->ptr) : nullptr;
1307 if (QQuick3DEffect *effect = buildType(def, ctx, ret, base)) {
1308 // If this is a component we'll store it for lookups later.
1309 if (doRegisterComponent)
1310 registerComponent({ .ptr: effect, .type: type });
1311
1312 if (ctx.property.target) {
1313 if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
1314 auto effects = qobject_cast<QQuick3DSceneEnvironment *>(object: ctx.property.target)->effects();
1315 if (ctx.property.memberState == Context::Property::Uninitialized) {
1316 if (ctx.dbgprint)
1317 printf(format: "Clearing inherited effects\n");
1318 effects.clear(&effects);
1319 ctx.property.memberState = Context::Property::Initialized;
1320 }
1321 effects.append(&effects, effect);
1322 if (ctx.dbgprint)
1323 printf(format: "Appending effect to %s\n", ctx.property.name.toLatin1().constData());
1324 }
1325 }
1326
1327 // At this point we don't know if this effect is going to be referenced somewhere else, so keep it in the list
1328 ctx.sceneData.effects.push_back(t: effect);
1329 }
1330 } else if (type == TypeInfo<QQuick3DDirectionalLight>::typeId() || type == TypeInfo<QQuick3DPointLight>::typeId() || type == TypeInfo<QQuick3DSpotLight>::typeId()) {
1331 const QQuick3DAbstractLight *base = (compIt != components.cend()) ? qobject_cast<QQuick3DAbstractLight *>(compIt->ptr) : nullptr;
1332 if (QQuick3DAbstractLight *light = buildLight(def, ctx, ret, type, base)) {
1333 // If this is a component we'll store it for lookups later.
1334 if (doRegisterComponent)
1335 registerComponent({ .ptr: light, .type: type });
1336
1337 ctx.sceneData.lights.push_back(t: light);
1338 }
1339 } else if (type == TypeInfo<QQuick3DTexture>::typeId()) {
1340 const QQuick3DTexture *base = (compIt != components.cend()) ? qobject_cast<QQuick3DTexture *>(compIt->ptr) : nullptr;
1341 if (QQuick3DTexture *tex = buildType(def, ctx, ret, base)) {
1342 // If this is a component we'll store it for lookups later.
1343 if (doRegisterComponent)
1344 registerComponent({ .ptr: tex, .type: type });
1345
1346 ctx.sceneData.textures.push_back(t: tex);
1347 }
1348 } else if (type == TypeInfo<QQuick3DModel>::typeId()) {
1349 const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DModel *>(compIt->ptr) : nullptr;
1350 if (auto *model = buildType(def, ctx, ret, base)) {
1351 // If this is a component we'll store it for lookups later.
1352 if (doRegisterComponent)
1353 registerComponent({ model, type });
1354
1355 ctx.sceneData.models.push_back(model);
1356 }
1357 } else if (type == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) {
1358 const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DShaderUtilsShader *>(compIt->ptr) : nullptr;
1359 if (auto *shader = buildType(def, ctx, ret, base)) {
1360 // If this is a component we'll store it for lookups later.
1361 if (doRegisterComponent)
1362 registerComponent({ shader, type });
1363 if (ctx.property.target) {
1364 if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
1365 auto shaders = qobject_cast<QQuick3DShaderUtilsRenderPass *>(object: ctx.property.target)->shaders();
1366 if (ctx.property.memberState == Context::Property::Uninitialized) {
1367 if (ctx.dbgprint)
1368 printf(format: "Clearing inherited shaders\n");
1369 shaders.clear(&shaders);
1370 ctx.property.memberState = Context::Property::Initialized;
1371 }
1372 shaders.append(&shaders, shader);
1373 if (ctx.dbgprint)
1374 printf(format: "Appending shader to %s\n", ctx.property.name.toLatin1().constData());
1375 }
1376 }
1377
1378 ctx.sceneData.shaders.push_back(shader);
1379 }
1380 } else if (type == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
1381 const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DShaderUtilsRenderPass *>(compIt->ptr) : nullptr;
1382 if (auto *pass = buildType(def, ctx, ret, base)) {
1383 // If this is a component we'll store it for lookups later.
1384 if (doRegisterComponent)
1385 registerComponent({ pass, type });
1386 if (ctx.property.target) {
1387 if (ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) {
1388 auto passes = qobject_cast<QQuick3DEffect *>(object: ctx.property.target)->passes();
1389 if (ctx.property.memberState == Context::Property::Uninitialized) {
1390 if (ctx.dbgprint)
1391 printf(format: "Clearing inherited passes\n");
1392 passes.clear(&passes);
1393 ctx.property.memberState = Context::Property::Initialized;
1394 }
1395 passes.append(&passes, pass);
1396 if (ctx.dbgprint)
1397 printf(format: "Appending pass to %s\n", ctx.property.name.toLatin1().constData());
1398 }
1399 }
1400 }
1401 } else if (type == TypeInfo<QQuick3DInstanceList>::typeId()) {
1402 const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DInstanceList *>(compIt->ptr) : nullptr;
1403 if (auto *instanceList = buildType(def, ctx, ret, base)) {
1404 // If this is a component we'll store it for lookups later.
1405 if (doRegisterComponent)
1406 registerComponent({ instanceList, type });
1407 if (ctx.property.target) {
1408 if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
1409 qobject_cast<QQuick3DModel *>(object: ctx.property.target)->setInstancing(instanceList);
1410 if (ctx.dbgprint)
1411 printf(format: "Setting instance list on %s\n", ctx.property.name.toLatin1().constData());
1412 }
1413 }
1414 }
1415 } else if (type == TypeInfo<QQuick3DInstanceListEntry>::typeId()) {
1416 const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DInstanceListEntry *>(compIt->ptr) : nullptr;
1417 if (auto *instanceListEntry = buildType(def, ctx, ret, base)) {
1418 // If this is a component we'll store it for lookups later.
1419 if (doRegisterComponent)
1420 registerComponent({ instanceListEntry, type });
1421 if (ctx.property.target) {
1422 if (ctx.property.targetType == TypeInfo<QQuick3DInstanceList>::typeId()) {
1423 auto instances = qobject_cast<QQuick3DInstanceList *>(object: ctx.property.target)->instances();
1424 instances.append(&instances, instanceListEntry);
1425 if (ctx.dbgprint)
1426 printf(format: "Appending instance list entry to %s\n", ctx.property.name.toLatin1().constData());
1427 }
1428 }
1429 }
1430 } else {
1431 if (ctx.dbgprint)
1432 printf(format: "Object def for \'%s\' was not handled\n", ctx.property.name.toLatin1().constData());
1433 return false;
1434 }
1435
1436 return true;
1437}
1438
1439static bool interceptPublicMember(const QQmlJS::AST::UiPublicMember &member, Context &ctx, int &ret)
1440{
1441 Q_UNUSED(ret);
1442 using namespace QQmlJS::AST;
1443
1444 if (ctx.dbgprint)
1445 printf(format: "Intercepted public member!\n");
1446
1447 if (member.statement && member.statement->kind == Node::Kind_ExpressionStatement) {
1448 if ((ctx.property.targetType == TypeInfo<QQuick3DCustomMaterial>::typeId() || ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) && member.memberType) {
1449 // For custom materials we have properties that are user provided, so we'll
1450 // need to add these to the objects properties (we add these here to be able
1451 // to piggyback on the existing type matching code)
1452 if (member.memberType->name == u"real") {
1453 ctx.property.type = QMetaType::Double;
1454 } else if (member.memberType->name == u"bool") {
1455 ctx.property.type = QMetaType::Bool;
1456 } else if (member.memberType->name == u"int") {
1457 ctx.property.type = QMetaType::Int;
1458 } else if (member.memberType->name == u"size") {
1459 ctx.property.type = QMetaType::QSizeF;
1460 } else if (member.memberType->name == u"rect") {
1461 ctx.property.type = QMetaType::QRectF;
1462 } else if (member.memberType->name == u"point") {
1463 ctx.property.type = QMetaType::QPointF;
1464 } else if (member.memberType->name == u"color") {
1465 ctx.property.type = QMetaType::QColor;
1466 } else if (member.memberType->name.startsWith(u"vector")) {
1467 if (member.memberType->name.endsWith(u"2d")) {
1468 ctx.property.type = QMetaType::QVector2D;
1469 } else if (member.memberType->name.endsWith(u"3d")) {
1470 ctx.property.type = QMetaType::QVector3D;
1471 } else if (member.memberType->name.endsWith(u"4d")) {
1472 ctx.property.type = QMetaType::QVector4D;
1473 }
1474 } else if (member.memberType->name == u"matrix4x4") {
1475 ctx.property.type = QMetaType::QMatrix4x4;;
1476 } else if (member.memberType->name == u"quaternion") {
1477 ctx.property.type = QMetaType::QQuaternion;
1478 } else if (member.memberType->name == u"var") {
1479 ctx.property.type = QMetaType::QVariant;
1480 }
1481 }
1482 }
1483
1484 return false;
1485}
1486
1487static bool interceptCallExpression(const QQmlJS::AST::CallExpression &callExpression, Context &ctx, int &ret)
1488{
1489 Q_UNUSED(ret);
1490 using namespace QQmlJS::AST;
1491 using namespace BuiltinHelpers;
1492
1493 if (ctx.dbgprint)
1494 printf(format: "Intercepted call expression!\n");
1495
1496 const bool ok = (ctx.property.target && !ctx.property.name.isEmpty());
1497 if (callExpression.base && ok) {
1498 if (callExpression.base->kind == Node::Kind_FieldMemberExpression) {
1499 const auto &fieldMemberExpression = static_cast<const FieldMemberExpression &>(*callExpression.base);
1500 if (fieldMemberExpression.base) {
1501 if (fieldMemberExpression.base->kind == Node::Kind_IdentifierExpression) {
1502 const auto &identExpr = static_cast<const IdentifierExpression &>(*fieldMemberExpression.base);
1503 if (identExpr.name == u"Qt") {
1504 bool ok = false;
1505 QVariant v;
1506 if (fieldMemberExpression.name == u"point") {
1507 const auto point = toPoint(ArgumentListView(*callExpression.arguments), &ok);
1508 if (ctx.dbgprint)
1509 printf("Qt.point(%f, %f)\n", point.x(), point.y());
1510 setProperty(ctx.property, point);
1511 } else if (fieldMemberExpression.name == u"size") {
1512 const auto size = toSize(ArgumentListView(*callExpression.arguments), &ok);
1513 if (ctx.dbgprint)
1514 printf("Qt.size(%f, %f)\n", size.width(), size.height());
1515 setProperty(ctx.property, size);
1516 } else if (fieldMemberExpression.name == u"rect") {
1517 const auto rect = toRect(ArgumentListView(*callExpression.arguments), &ok);
1518 if (ctx.dbgprint)
1519 printf("Qt.rect(%f, %f, %f, %f)\n", rect.x(), rect.y(), rect.width(), rect.height());
1520 setProperty(ctx.property, rect);
1521 } else if (fieldMemberExpression.name.startsWith(u"vector")) {
1522 if (fieldMemberExpression.name.endsWith(u"2d")) {
1523 const auto vec2 = toVec<QVector2D>(ArgumentListView(*callExpression.arguments), &ok);
1524 if (ctx.dbgprint)
1525 printf("Qt.vector2d(%f, %f)\n", vec2.x(), vec2.y());
1526 setProperty(ctx.property, vec2);
1527 } else if (fieldMemberExpression.name.endsWith(u"3d")) {
1528 const auto vec3 = toVec<QVector3D>(ArgumentListView(*callExpression.arguments), &ok);
1529 if (ctx.dbgprint)
1530 printf("Qt.vector3d(%f, %f, %f)\n", vec3.x(), vec3.y(), vec3.z());
1531 setProperty(ctx.property, vec3);
1532 } else if (fieldMemberExpression.name.endsWith(u"4d")) {
1533 const auto vec4 = toVec<QVector4D>(ArgumentListView(*callExpression.arguments), &ok);
1534 if (ctx.dbgprint)
1535 printf("Qt.vector4d(%f, %f, %f, %f)\n", vec4.x(), vec4.y(), vec4.z(), vec4.w());
1536 setProperty(ctx.property, vec4);
1537 }
1538 } else if (fieldMemberExpression.name == u"matrix4x4") {
1539 const auto mat44 = toMat44(ArgumentListView(*callExpression.arguments), &ok);
1540 if (ctx.dbgprint)
1541 printf("Qt.matrix4x4(%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f)\n",
1542 mat44(0, 0), mat44(0, 1), mat44(0, 2), mat44(0, 3),
1543 mat44(1, 0), mat44(1, 1), mat44(1, 2), mat44(1, 3),
1544 mat44(2, 0), mat44(2, 1), mat44(2, 2), mat44(2, 3),
1545 mat44(3, 0), mat44(3, 1), mat44(3, 2), mat44(3, 3));
1546 setProperty(ctx.property, mat44);
1547 } else if (fieldMemberExpression.name == u"quaternion") {
1548 const auto quat = toQuaternion(ArgumentListView(*callExpression.arguments), &ok);
1549 if (ctx.dbgprint)
1550 printf("Qt.quaternion(%f, %f, %f, %f)\n", quat.scalar(), quat.x(), quat.y(), quat.z());
1551 setProperty(ctx.property, quat);
1552 } else if (fieldMemberExpression.name == u"rgba") {
1553 const auto vec4 = toVec<QVector4D>(ArgumentListView(*callExpression.arguments), &ok);
1554 if (ok) {
1555 QColor color = QColor::fromRgbF(r: vec4.x(), g: vec4.y(), b: vec4.z(), a: vec4.w());
1556 if (ctx.dbgprint)
1557 printf(format: "Qt.rgba(%f, %f, %f, %f)\n", color.redF(), color.greenF(), color.blueF(), color.alphaF());
1558 setProperty(property: ctx.property, v: color);
1559 }
1560 }
1561 if (ok && v.isValid() && ctx.property.target)
1562 ctx.property.target->setProperty(name: ctx.property.name.toLatin1().constData(), value: v);
1563 }
1564 }
1565 }
1566 }
1567 }
1568
1569 return false;
1570}
1571
1572static int parseQmlData(const QByteArray &code, Context &ctx)
1573{
1574 Q_ASSERT(ctx.engine && ctx.engine->lexer());
1575 ctx.identifierMap.clear(); // not visible outside the scope of this "code"
1576 if (ctx.dbgprint)
1577 printf(format: "Parsing %s\n", qPrintable(ctx.currentFileInfo.filePath()));
1578 int ret = 0;
1579 ctx.engine->lexer()->setCode(code: QString::fromUtf8(ba: code), lineno: 1, qmlMode: true);
1580 QQmlJS::Parser parser(ctx.engine);
1581 const bool ok = parser.parse();
1582 if (ok) {
1583 const auto program = parser.ast();
1584 if (program)
1585 Visitors::visit(*program, ctx, ret);
1586 } else {
1587 ret = -1;
1588 qWarning("Parsing failed due to %s in %s:%d%d", qPrintable(parser.errorMessage()), qPrintable(ctx.currentFileInfo.fileName()), parser.errorLineNumber(), parser.errorColumnNumber());
1589 }
1590
1591 return ret;
1592}
1593
1594int MaterialParser::parseQmlData(const QByteArray &code, const QString &fileName, MaterialParser::SceneData &sceneData)
1595{
1596 // set initial type map
1597 *s_typeMap = baseTypeMap();
1598
1599 QQmlJS::Engine engine;
1600 QQmlJS::Lexer lexer(&engine);
1601
1602 Context ctx;
1603 ctx.engine = &engine;
1604 ctx.interceptODFunc = &interceptObjectDef;
1605 ctx.interceptOBFunc = &interceptObjectBinding;
1606 ctx.interceptPMFunc = &interceptPublicMember;
1607 ctx.interceptCallExpr = &interceptCallExpression;
1608 ctx.currentFileInfo = QFileInfo(fileName);
1609 ctx.type = Context::Type::Component;
1610
1611 const int ret = ::parseQmlData(code, ctx);
1612 sceneData = std::move(ctx.sceneData);
1613
1614 return ret;
1615}
1616
1617int MaterialParser::parseQmlFiles(const QVector<QString> &filePaths, const QDir &sourceDir, SceneData &sceneData, bool verboseOutput)
1618{
1619 // set initial type map
1620 *s_typeMap = baseTypeMap();
1621
1622 int ret = 0;
1623
1624 if (filePaths.isEmpty()) {
1625 qWarning(msg: "No input files");
1626 return ret;
1627 }
1628
1629 QQmlJS::Engine engine;
1630 QQmlJS::Lexer lexer(&engine);
1631
1632 Context ctx;
1633 ctx.dbgprint = verboseOutput;
1634 ctx.engine = &engine;
1635 ctx.interceptODFunc = &interceptObjectDef;
1636 ctx.interceptOBFunc = &interceptObjectBinding;
1637 ctx.interceptPMFunc = &interceptPublicMember;
1638 ctx.interceptCallExpr = &interceptCallExpression;
1639 ctx.workingDir = sourceDir;
1640
1641 QVector<QString> deferredOther;
1642 QVector<QString> deferredComponets;
1643
1644 const QString sourcePath = sourceDir.canonicalPath() + QDir::separator();
1645
1646 const bool isMultifile = filePaths.size() != 1;
1647
1648 // Go through and find the material components first
1649 for (const auto &v : filePaths) {
1650 QFileInfo &currentFileInfo = ctx.currentFileInfo;
1651 if (!QFileInfo(v).isAbsolute())
1652 currentFileInfo.setFile(sourcePath + v);
1653 else
1654 currentFileInfo.setFile(v);
1655 const bool maybeComponent = currentFileInfo.fileName().at(i: 0).isUpper();
1656 if (currentFileInfo.isFile() && currentFileInfo.suffix() == getQmlFileExtension()) {
1657 const QString filePath = currentFileInfo.canonicalFilePath();
1658 if (isMultifile && maybeComponent) {
1659 QFile f(filePath);
1660 if (!f.open(flags: QFile::ReadOnly)) {
1661 qWarning(msg: "Could not open file %s for reading!", qPrintable(filePath));
1662 return -1;
1663 }
1664
1665 const QByteArray code = f.readAll();
1666 int idx = code.indexOf(ch: '{');
1667 if (idx != -1) {
1668 const QByteArray section = code.mid(index: 0, len: idx);
1669 QVarLengthArray<const char *, 3> componentTypes { TypeInfo<QQuick3DPrincipledMaterial>::qmlTypeName(),
1670 TypeInfo<QQuick3DCustomMaterial>::qmlTypeName(),
1671 TypeInfo<QQuick3DDefaultMaterial>::qmlTypeName()};
1672 for (const auto compType : std::as_const(t&: componentTypes)) {
1673 if ((idx = section.indexOf(bv: compType)) != -1)
1674 break;
1675 }
1676 if (idx != -1) {
1677 ctx.type = Context::Type::Component;
1678 ret = parseQmlData(code, ctx);
1679 if (ret != 0)
1680 break;
1681 } else {
1682 deferredComponets.push_back(t: filePath);
1683 }
1684 } else {
1685 qWarning(msg: "No items found in %s\n", qPrintable(filePath));
1686 }
1687 } else {
1688 deferredOther.push_back(t: filePath);
1689 }
1690 } else {
1691 qWarning(msg: "The file %s is either not a file or has the wrong extension!", qPrintable(v));
1692 }
1693 }
1694
1695 const auto parsePaths = [&ctx, &ret](const QVector<QString> &paths, Context::Type type) {
1696 ctx.type = type;
1697 for (const auto &path : paths) {
1698 QFileInfo &currentFileInfo = ctx.currentFileInfo;
1699 currentFileInfo.setFile(path);
1700 if (currentFileInfo.isFile() && currentFileInfo.suffix() == getQmlFileExtension()) {
1701 const QString filePath = currentFileInfo.canonicalFilePath();
1702 QFile f(filePath);
1703 if (!f.open(flags: QFile::ReadOnly)) {
1704 qWarning(msg: "Could not open file %s for reading!", qPrintable(filePath));
1705 ret = -1;
1706 return;
1707 }
1708
1709 const QByteArray code = f.readAll();
1710 ret = parseQmlData(code, ctx);
1711 if (ret != 0)
1712 break;
1713 }
1714 }
1715 };
1716
1717 // Other components
1718 parsePaths(deferredComponets, Context::Type::Component);
1719
1720 // Now parse the rest
1721 parsePaths(deferredOther, Context::Type::Application);
1722
1723 sceneData = std::move(ctx.sceneData);
1724
1725 return ret;
1726}
1727
1728QT_END_NAMESPACE
1729

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