Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmlirbuilder.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmlirbuilder_p.h"
5
6#include <private/qv4staticvalue_p.h>
7#include <private/qv4compileddata_p.h>
8#include <private/qqmljsparser_p.h>
9#include <private/qqmljslexer_p.h>
10#include <private/qv4compilerscanfunctions_p.h>
11#include <QCoreApplication>
12#include <QCryptographicHash>
13#include <cmath>
14
16
17using namespace Qt::StringLiterals;
18
19static const quint32 emptyStringIndex = 0;
20using namespace QmlIR;
21using namespace QQmlJS;
22
23#define COMPILE_EXCEPTION(location, desc) \
24 { \
25 recordError(location, desc); \
26 return false; \
27 }
28
29void Object::simplifyRequiredProperties() {
30 // if a property of the current object was marked as required
31 // do not store that information in the ExtraData
32 // but rather mark the property as required
33 QSet<int> required;
34 for (auto it = this->requiredPropertyExtraDataBegin(); it != this->requiredPropertyExtraDataEnd(); ++it)
35 required.insert(it->nameIndex);
36 if (required.isEmpty())
37 return;
38 for (auto it = this->propertiesBegin(); it != this->propertiesEnd(); ++it) {
39 auto requiredIt = required.find(it->nameIndex);
40 if (requiredIt != required.end()) {
41 it->setIsRequired(true);
42 required.erase(requiredIt);
43 }
44 }
46 auto current = this->requiredPropertyExtraDatas->first;
47 while (current) {
48 if (required.contains(current->nameIndex))
49 prev = current;
50 else
51 requiredPropertyExtraDatas->unlink(prev, current);
52 current = current->next;
53 }
54}
55
58 const QString &typeName, int typeNameIndex,
60{
61 auto builtinType = stringToBuiltinType(typeName);
62 if (builtinType == QV4::CompiledData::CommonType::Invalid) {
63 if (typeName.isEmpty()) {
64 paramType->set(listFlag, 0);
65 return false;
66 }
67 Q_ASSERT(quint32(typeNameIndex) < (1u << 31));
68 paramType->set(listFlag, typeNameIndex);
69 } else {
70 Q_ASSERT(quint32(builtinType) < (1u << 31));
72 static_cast<quint32>(builtinType));
73 }
74 return true;
75}
76
78{
79 static const struct TypeNameToType {
80 const char *name;
81 size_t nameLength;
83 } propTypeNameToTypes[] = {
84 { "void", strlen("void"), QV4::CompiledData::CommonType::Void },
85 { "int", strlen("int"), QV4::CompiledData::CommonType::Int },
86 { "bool", strlen("bool"), QV4::CompiledData::CommonType::Bool },
87 { "double", strlen("double"), QV4::CompiledData::CommonType::Real },
88 { "real", strlen("real"), QV4::CompiledData::CommonType::Real },
89 { "string", strlen("string"), QV4::CompiledData::CommonType::String },
90 { "url", strlen("url"), QV4::CompiledData::CommonType::Url },
91 { "date", strlen("date"), QV4::CompiledData::CommonType::DateTime },
92 { "regexp", strlen("regexp"), QV4::CompiledData::CommonType::RegExp },
93 { "rect", strlen("rect"), QV4::CompiledData::CommonType::Rect },
94 { "point", strlen("point"), QV4::CompiledData::CommonType::Point },
95 { "size", strlen("size"), QV4::CompiledData::CommonType::Size },
96 { "variant", strlen("variant"), QV4::CompiledData::CommonType::Var },
97 { "var", strlen("var"), QV4::CompiledData::CommonType::Var }
98 };
99 static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
100 sizeof(propTypeNameToTypes[0]);
101
102 for (int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) {
103 const TypeNameToType *t = propTypeNameToTypes + typeIndex;
104 if (typeName == QLatin1String(t->name, static_cast<int>(t->nameLength))) {
105 return t->type;
106 }
107 }
109}
110
111void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex,
113{
114 Q_ASSERT(loc.line() > 0 && loc.column() > 0);
115 inheritedTypeNameIndex = typeNameIndex;
116 location = loc;
117 idNameIndex = idIndex;
118 id = -1;
122 properties = pool->New<PoolList<Property> >();
123 aliases = pool->New<PoolList<Alias> >();
124 qmlEnums = pool->New<PoolList<Enum>>();
125 qmlSignals = pool->New<PoolList<Signal> >();
126 bindings = pool->New<PoolList<Binding> >();
127 functions = pool->New<PoolList<Function> >();
128 functionsAndExpressions = pool->New<PoolList<CompiledFunctionOrExpression> >();
129 inlineComponents = pool->New<PoolList<InlineComponent>>();
130 requiredPropertyExtraDatas = pool->New<PoolList<RequiredPropertyExtraData>>();
131 declarationsOverride = nullptr;
132}
133
134QString IRBuilder::sanityCheckFunctionNames(Object *obj, const QSet<QString> &illegalNames, QQmlJS::SourceLocation *errorLocation)
135{
136 QSet<int> functionNames;
137 for (auto functionit = obj->functionsBegin(); functionit != obj->functionsEnd(); ++functionit) {
138 Function *f = functionit.ptr;
139 errorLocation->startLine = f->location.line();
140 errorLocation->startColumn = f->location.column();
141 if (functionNames.contains(f->nameIndex))
142 return tr("Duplicate method name");
143 functionNames.insert(f->nameIndex);
144
145 for (auto signalit = obj->signalsBegin(); signalit != obj->signalsEnd(); ++signalit) {
146 QmlIR::Signal *s = signalit.ptr;
147 if (s->nameIndex == f->nameIndex)
148 return tr("Duplicate method name");
149 }
150
151 const QString name = stringAt(f->nameIndex);
152 if (name.at(0).isUpper())
153 return tr("Method names cannot begin with an upper case letter");
155 return tr("Illegal method name");
156 }
157 return QString(); // no error
158}
159
161{
163 if (!target)
164 target = this;
165
166 for (Enum *e = qmlEnums->first; e; e = e->next) {
167 if (e->nameIndex == enumeration->nameIndex)
168 return tr("Duplicate scoped enum name");
169 }
170
171 target->qmlEnums->append(enumeration);
172 return QString(); // no error
173}
174
176{
178 if (!target)
179 target = this;
180
181 for (Signal *s = qmlSignals->first; s; s = s->next) {
182 if (s->nameIndex == signal->nameIndex)
183 return tr("Duplicate signal name");
184 }
185
186 target->qmlSignals->append(signal);
187 return QString(); // no error
188}
189
190QString Object::appendProperty(Property *prop, const QString &propertyName, bool isDefaultProperty, const QQmlJS::SourceLocation &defaultToken, QQmlJS::SourceLocation *errorLocation)
191{
193 if (!target)
194 target = this;
195
196 for (Property *p = target->properties->first; p; p = p->next)
197 if (p->nameIndex == prop->nameIndex)
198 return tr("Duplicate property name");
199
200 for (Alias *a = target->aliases->first; a; a = a->next)
201 if (a->nameIndex() == prop->nameIndex)
202 return tr("Property duplicates alias name");
203
204 if (propertyName.constData()->isUpper())
205 return tr("Property names cannot begin with an upper case letter");
206
207 const int index = target->properties->append(prop);
208 if (isDefaultProperty) {
209 if (target->indexOfDefaultPropertyOrAlias != -1) {
210 *errorLocation = defaultToken;
211 return tr("Duplicate default property");
212 }
213 target->indexOfDefaultPropertyOrAlias = index;
214 }
215 return QString(); // no error
216}
217
218QString Object::appendAlias(Alias *alias, const QString &aliasName, bool isDefaultProperty, const QQmlJS::SourceLocation &defaultToken, QQmlJS::SourceLocation *errorLocation)
219{
221 if (!target)
222 target = this;
223
224 const auto aliasWithSameName = std::find_if(target->aliases->begin(), target->aliases->end(), [&alias](const Alias &targetAlias){
225 return targetAlias.nameIndex() == alias->nameIndex();
226 });
227 if (aliasWithSameName != target->aliases->end())
228 return tr("Duplicate alias name");
229
230 const auto aliasSameAsProperty = std::find_if(target->properties->begin(), target->properties->end(), [&alias](const Property &targetProp){
231 return targetProp.nameIndex == alias->nameIndex();
232 });
233
234 if (aliasSameAsProperty != target->properties->end())
235 return tr("Alias has same name as existing property");
236
237 if (aliasName.constData()->isUpper())
238 return tr("Alias names cannot begin with an upper case letter");
239
240 const int index = target->aliases->append(alias);
241
242 if (isDefaultProperty) {
243 if (target->indexOfDefaultPropertyOrAlias != -1) {
244 *errorLocation = defaultToken;
245 return tr("Duplicate default property");
246 }
247 target->indexOfDefaultPropertyOrAlias = index;
248 target->defaultPropertyIsAlias = true;
249 }
250
251 return QString(); // no error
252}
253
255{
256 // Unlike properties, a function definition inside a grouped property does not go into
257 // the surrounding object. It's been broken since the Qt 5 era, and the semantics
258 // seems super confusing, so it wouldn't make sense to support that.
260 functions->append(f);
261}
262
264{
265 inlineComponents->append(ic);
266}
267
269{
270 requiredPropertyExtraDatas->append(extraData);
271}
272
274{
275 const bool bindingToDefaultProperty = (b->propertyNameIndex == quint32(0));
276 if (!isListBinding
277 && !bindingToDefaultProperty
281 Binding *existing = findBinding(b->propertyNameIndex);
282 if (existing
283 && existing->isValueBinding() == b->isValueBinding()
284 && !existing->hasFlag(QV4::CompiledData::Binding::IsOnAssignment)) {
285 return tr("Property value set multiple times");
286 }
287 }
288 if (bindingToDefaultProperty)
290 else
291 bindings->prepend(b);
292 return QString(); // no error
293}
294
296{
297 for (Binding *b = bindings->first; b; b = b->next)
298 if (b->propertyNameIndex == nameIndex)
299 return b;
300 return nullptr;
301}
302
304{
305 Binding *insertionPoint = bindings->findSortedInsertionPoint<quint32, Binding, &Binding::offset>(b);
306 bindings->insertAfter(insertionPoint, b);
307}
308
309QString Object::bindingAsString(Document *doc, int scriptIndex) const
310{
311 CompiledFunctionOrExpression *foe = functionsAndExpressions->slowAt(scriptIndex);
312 QQmlJS::AST::Node *node = foe->node;
313 if (QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(node))
314 node = exprStmt->expression;
317 return doc->code.mid(start.offset, end.offset + end.length - start.offset);
318}
319
321{
323 result.reserve(parameters->count);
324 for (Parameter *param = parameters->first; param; param = param->next)
325 result << stringPool->stringForIndex(param->nameIndex);
326 return result;
327}
328
329Document::Document(bool debugMode)
330 : jsModule(debugMode)
332 , jsGenerator(&jsModule)
333{
334}
335
337 : document(doc)
338 , engine(&doc->jsParserEngine)
339 , jsGenerator(&doc->jsGenerator)
340{
341}
342
347
348void ScriptDirectivesCollector::importFile(const QString &jsfile, const QString &module, int lineNumber, int column)
349{
350 QV4::CompiledData::Import *import = engine->pool()->New<QV4::CompiledData::Import>();
352 import->uriIndex = jsGenerator->registerString(jsfile);
353 import->qualifierIndex = jsGenerator->registerString(module);
354 import->location.set(lineNumber, column);
355 document->imports << import;
356}
357
358void ScriptDirectivesCollector::importModule(const QString &uri, const QString &version, const QString &module, int lineNumber, int column)
359{
360 QV4::CompiledData::Import *import = engine->pool()->New<QV4::CompiledData::Import>();
362 import->uriIndex = jsGenerator->registerString(uri);
363 import->version = IRBuilder::extractVersion(version);
364 import->qualifierIndex = jsGenerator->registerString(module);
365 import->location.set(lineNumber, column);
366 document->imports << import;
367}
368
369IRBuilder::IRBuilder(const QSet<QString> &illegalNames)
370 : illegalNames(illegalNames)
371 , _object(nullptr)
372 , _propertyDeclaration(nullptr)
373 , pool(nullptr)
374 , jsGenerator(nullptr)
375{
376}
377
379{
381 {
382 QQmlJS::Lexer lexer(&output->jsParserEngine);
383 lexer.setCode(code, /*line = */ 1);
384
385 QQmlJS::Parser parser(&output->jsParserEngine);
386
387 const bool parseResult = parser.parse();
388 const auto diagnosticMessages = parser.diagnosticMessages();
389 if (!parseResult || !diagnosticMessages.isEmpty()) {
390 // Extract errors from the parser
391 for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
392 if (m.isWarning()) {
393 qWarning("%s:%d : %s", qPrintable(url), m.loc.startLine, qPrintable(m.message));
394 continue;
395 }
396
397 errors << m;
398 }
399
400 if (!errors.isEmpty() || !parseResult)
401 return false;
402 }
403 program = parser.ast();
405 }
406
407 output->code = code;
408 output->program = program;
409
410 qSwap(_imports, output->imports);
411 qSwap(_pragmas, output->pragmas);
412 qSwap(_objects, output->objects);
413 this->pool = output->jsParserEngine.pool();
414 this->jsGenerator = &output->jsGenerator;
415
417
418 sourceCode = code;
419
420 accept(program->headers);
421
422 if (program->members->next) {
423 QQmlJS::SourceLocation loc = program->members->next->firstSourceLocation();
424 recordError(loc, QCoreApplication::translate("QQmlParser", "Unexpected object definition"));
425 return false;
426 }
427
428 QQmlJS::AST::UiObjectDefinition *rootObject = QQmlJS::AST::cast<QQmlJS::AST::UiObjectDefinition*>(program->members->member);
429 Q_ASSERT(rootObject);
430 int rootObjectIndex = -1;
431 if (defineQMLObject(&rootObjectIndex, rootObject)) {
432 Q_ASSERT(rootObjectIndex == 0);
433 }
434
435 qSwap(_imports, output->imports);
436 qSwap(_pragmas, output->pragmas);
437 qSwap(_objects, output->objects);
438
439 for (auto object: output->objects)
440 object->simplifyRequiredProperties();
441
442 return errors.isEmpty();
443}
444
446{
447 return QQmlJS::AST::Visitor::visit(ast);
448}
449
451{
452 Q_ASSERT(!"should not happen");
453 return false;
454}
455
457{
458 // The grammar can't distinguish between two different definitions here:
459 // Item { ... }
460 // versus
461 // font { ... }
462 // The former is a new binding with no property name and "Item" as type name,
463 // and the latter is a binding to the font property with no type name but
464 // only initializer.
465
467 while (lastId->next)
468 lastId = lastId->next;
469 bool isType = lastId->name.data()->isUpper();
470 if (isType) {
471 int idx = 0;
472 if (!defineQMLObject(&idx, node))
473 return false;
475 appendBinding(nameLocation, nameLocation, emptyStringIndex, idx);
476 } else {
477 int idx = 0;
479 if (!defineQMLObject(
480 &idx, /*qualfied type name id*/nullptr,
481 { location.startLine, location.startColumn }, node->initializer,
482 /*declarations should go here*/_object)) {
483 return false;
484 }
486 }
487 return false;
488}
489
491{
492 int idx = -1;
494 recordError(ast->firstSourceLocation(), QLatin1String("Nested inline components are not supported"));
495 return false;
496 }
498 recordError(ast->firstSourceLocation(), QLatin1String("Inline component names must be unique per file"));
499 return false;
500 } else {
502 }
503 {
504 QScopedValueRollback<bool> rollBack {insideInlineComponent, true};
505 if (!defineQMLObject(&idx, ast->component))
506 return false;
507 }
508 Q_ASSERT(idx > 0);
509 Object* definedObject = _objects.at(idx);
512 auto inlineComponent = New<InlineComponent>();
513 inlineComponent->nameIndex = registerString(ast->name.toString());
514 inlineComponent->objectIndex = idx;
515 auto location = ast->firstSourceLocation();
516 inlineComponent->location.set(location.startLine, location.startColumn);
517 _object->appendInlineComponent(inlineComponent);
518 return false;
519}
520
522{
523 int idx = 0;
525 if (!defineQMLObject(&idx, node->qualifiedTypeNameId,
526 { location.startLine, location.startColumn }, node->initializer)) {
527 return false;
528 }
529 appendBinding(node->qualifiedId, idx, node->hasOnToken);
530 return false;
531}
532
534{
535 appendBinding(node->qualifiedId, node->statement, node);
536 return false;
537}
538
540{
541 const QQmlJS::SourceLocation qualifiedNameLocation = node->qualifiedId->identifierToken;
542 Object *object = nullptr;
544 if (!resolveQualifiedId(&name, &object))
545 return false;
546
547 qSwap(_object, object);
548
549 const int propertyNameIndex = registerString(name->name.toString());
550
551 if (bindingsTarget()->findBinding(propertyNameIndex) != nullptr) {
552 recordError(name->identifierToken, tr("Property value set multiple times"));
553 return false;
554 }
555
556 QVarLengthArray<QQmlJS::AST::UiArrayMemberList *, 16> memberList;
558 while (member) {
559 memberList.append(member);
560 member = member->next;
561 }
562 for (int i = memberList.size() - 1; i >= 0; --i) {
563 member = memberList.at(i);
564 QQmlJS::AST::UiObjectDefinition *def = QQmlJS::AST::cast<QQmlJS::AST::UiObjectDefinition*>(member->member);
565
566 int idx = 0;
567 if (!defineQMLObject(&idx, def))
568 return false;
569 appendBinding(qualifiedNameLocation, name->identifierToken, propertyNameIndex, idx, /*isListItem*/ true);
570 }
571
572 qSwap(_object, object);
573 return false;
574}
575
577{
578 return QQmlJS::AST::Visitor::visit(list);
579}
580
582{
583 return QQmlJS::AST::Visitor::visit(ast);
584}
585
587{
588 return QQmlJS::AST::Visitor::visit(ast);
589}
590
592{
593 return QQmlJS::AST::Visitor::visit(ast);
594}
595
597{
598 return QQmlJS::AST::Visitor::visit(id);
599}
600
602{
603 QQmlJS::AST::Node::accept(node, this);
604}
605
607 int *objectIndex, QQmlJS::AST::UiQualifiedId *qualifiedTypeNameId,
609 Object *declarationsOverride)
610{
611 if (QQmlJS::AST::UiQualifiedId *lastName = qualifiedTypeNameId) {
612 while (lastName->next)
613 lastName = lastName->next;
614 if (!lastName->name.constData()->isUpper()) {
615 recordError(lastName->identifierToken, tr("Expected type name"));
616 return false;
617 }
618 }
619
620 Object *obj = New<Object>();
621
622 _objects.append(obj);
623 *objectIndex = _objects.size() - 1;
624 qSwap(_object, obj);
625
627 _object->declarationsOverride = declarationsOverride;
630 }
631
632 // A new object is also a boundary for property declarations.
633 Property *declaration = nullptr;
634 qSwap(_propertyDeclaration, declaration);
635
636 accept(initializer);
637
638 qSwap(_propertyDeclaration, declaration);
639
640 qSwap(_object, obj);
641
642 if (!errors.isEmpty())
643 return false;
644
647 if (!error.isEmpty()) {
648 recordError(loc, error);
649 return false;
650 }
651
652 return true;
653}
654
656{
657 QString uri;
658 QV4::CompiledData::Import *import = New<QV4::CompiledData::Import>();
659
660 if (!node->fileName.isNull()) {
661 uri = node->fileName.toString();
662
663 if (uri.endsWith(QLatin1String(".js")) || uri.endsWith(QLatin1String(".mjs"))) {
665 } else {
667 }
668 } else {
670 uri = asString(node->importUri);
671 }
672
673 import->qualifierIndex = emptyStringIndex;
674
675 // Qualifier
676 if (!node->importId.isNull()) {
677 QString qualifier = node->importId.toString();
678 if (!qualifier.at(0).isUpper()) {
679 recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Invalid import qualifier ID"));
680 return false;
681 }
682 if (qualifier == QLatin1String("Qt")) {
683 recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Reserved name \"Qt\" cannot be used as an qualifier"));
684 return false;
685 }
686 import->qualifierIndex = registerString(qualifier);
687
688 // Check for script qualifier clashes
690 for (int ii = 0; ii < _imports.size(); ++ii) {
692 bool otherIsScript = other->type == QV4::CompiledData::Import::ImportScript;
693
694 if ((isScript || otherIsScript) && qualifier == jsGenerator->stringForIndex(other->qualifierIndex)) {
695 recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Script import qualifiers must be unique."));
696 return false;
697 }
698 }
699
700 } else if (import->type == QV4::CompiledData::Import::ImportScript) {
701 recordError(node->fileNameToken, QCoreApplication::translate("QQmlParser","Script import requires a qualifier"));
702 return false;
703 }
704
705 if (node->version) {
706 import->version = node->version->version;
707 } else {
708 // Otherwise initialize the major and minor version to invalid to signal "latest".
709 import->version = QTypeRevision();
710 }
711
712 import->location.set(node->importToken.startLine, node->importToken.startColumn);
713
714 import->uriIndex = registerString(uri);
715
716 _imports.append(import);
717
718 return false;
719}
720
721
722template<typename Argument>
724{
725 static bool run(IRBuilder *builder, QQmlJS::AST::UiPragma *node, Pragma *pragma)
726 {
727 Q_ASSERT(builder);
728 Q_ASSERT(node);
729 Q_ASSERT(pragma);
730
731 if (!isUnique(builder)) {
732 builder->recordError(
734 "QQmlParser", "Multiple %1 pragmas found").arg(name()));
735 return false;
736 }
737
738 pragma->type = type();
739
740 if (QQmlJS::AST::UiPragmaValueList *bad = assign(pragma, node->values)) {
741 builder->recordError(
743 "QQmlParser", "Unknown %1 '%2' in pragma").arg(name(), bad->value));
744 return false;
745 }
746
747 return true;
748 }
749
750private:
751 static constexpr Pragma::PragmaType type()
752 {
753 if constexpr (std::is_same_v<Argument, Pragma::ComponentBehaviorValue>) {
755 } else if constexpr (std::is_same_v<Argument, Pragma::ListPropertyAssignBehaviorValue>) {
757 } else if constexpr (std::is_same_v<Argument, Pragma::FunctionSignatureBehaviorValue>) {
759 } else if constexpr (std::is_same_v<Argument, Pragma::NativeMethodBehaviorValue>) {
761 } else if constexpr (std::is_same_v<Argument, Pragma::ValueTypeBehaviorValue>) {
763 }
764
765 Q_UNREACHABLE_RETURN(Pragma::PragmaType(-1));
766 }
767
768 template<typename F>
769 static QQmlJS::AST::UiPragmaValueList *iterateValues(
771 {
773 if (!process(i->value))
774 return i;
775 }
776 return nullptr;
777 }
778
779 static QQmlJS::AST::UiPragmaValueList *assign(
781 {
782 // We could use QMetaEnum here to make the code more compact,
783 // but it's probably more expensive.
784
785 if constexpr (std::is_same_v<Argument, Pragma::ComponentBehaviorValue>) {
786 return iterateValues(values, [pragma](QStringView value) {
787 if (value == "Unbound"_L1) {
788 pragma->componentBehavior = Pragma::Unbound;
789 return true;
790 }
791 if (value == "Bound"_L1) {
792 pragma->componentBehavior = Pragma::Bound;
793 return true;
794 }
795 return false;
796 });
797 } else if constexpr (std::is_same_v<Argument, Pragma::ListPropertyAssignBehaviorValue>) {
798 return iterateValues(values, [pragma](QStringView value) {
799 if (value == "Append"_L1) {
800 pragma->listPropertyAssignBehavior = Pragma::Append;
801 return true;
802 }
803 if (value == "Replace"_L1) {
804 pragma->listPropertyAssignBehavior = Pragma::Replace;
805 return true;
806 }
807 if (value == "ReplaceIfNotDefault"_L1) {
808 pragma->listPropertyAssignBehavior = Pragma::ReplaceIfNotDefault;
809 return true;
810 }
811 return false;
812 });
813 } else if constexpr (std::is_same_v<Argument, Pragma::FunctionSignatureBehaviorValue>) {
814 return iterateValues(values, [pragma](QStringView value) {
815 if (value == "Ignored"_L1) {
816 pragma->functionSignatureBehavior = Pragma::Ignored;
817 return true;
818 }
819 if (value == "Enforced"_L1) {
820 pragma->functionSignatureBehavior = Pragma::Enforced;
821 return true;
822 }
823 return false;
824 });
825 } else if constexpr (std::is_same_v<Argument, Pragma::NativeMethodBehaviorValue>) {
826 return iterateValues(values, [pragma](QStringView value) {
827 if (value == "AcceptThisObject"_L1) {
828 pragma->nativeMethodBehavior = Pragma::AcceptThisObject;
829 return true;
830 }
831 if (value == "RejectThisObject"_L1) {
832 pragma->nativeMethodBehavior = Pragma::RejectThisObject;
833 return true;
834 }
835 return false;
836 });
837 } else if constexpr (std::is_same_v<Argument, Pragma::ValueTypeBehaviorValue>) {
838 pragma->valueTypeBehavior = Pragma::ValueTypeBehaviorValues().toInt();
839 return iterateValues(values, [pragma](QStringView value) {
840 const auto setFlag = [pragma](Pragma::ValueTypeBehaviorValue flag, bool value) {
841 pragma->valueTypeBehavior
842 = Pragma::ValueTypeBehaviorValues(pragma->valueTypeBehavior)
843 .setFlag(flag, value).toInt();
844 };
845
846 if (value == "Reference"_L1) {
847 setFlag(Pragma::Copy, false);
848 return true;
849 }
850 if (value == "Copy"_L1) {
851 setFlag(Pragma::Copy, true);
852 return true;
853 }
854
855 if (value == "Inaddressable"_L1) {
856 setFlag(Pragma::Addressable, false);
857 return true;
858 }
859 if (value == "Addressable"_L1) {
860 setFlag(Pragma::Addressable, true);
861 return true;
862 }
863
864 if (value == "Inassertable"_L1) {
865 setFlag(Pragma::Assertable, false);
866 return true;
867 }
868 if (value == "Assertable"_L1) {
869 setFlag(Pragma::Assertable, true);
870 return true;
871 }
872
873 return false;
874 });
875 }
876
877 Q_UNREACHABLE_RETURN(nullptr);
878 }
879
880 static bool isUnique(IRBuilder *builder)
881 {
882 for (const Pragma *prev : builder->_pragmas) {
883 if (prev->type == type())
884 return false;
885 }
886 return true;
887 };
888
889 static QLatin1StringView name()
890 {
891 switch (type()) {
893 return "list property assign behavior"_L1;
895 return "component behavior"_L1;
897 return "function signature behavior"_L1;
899 return "native method behavior"_L1;
901 return "value type behavior"_L1;
902 default:
903 break;
904 }
905 Q_UNREACHABLE_RETURN(QLatin1StringView());
906 }
907};
908
910{
911 Pragma *pragma = New<Pragma>();
912
913 if (!node->name.isNull()) {
914 if (node->name == "Singleton"_L1) {
915 pragma->type = Pragma::Singleton;
916 } else if (node->name == "Strict"_L1) {
917 pragma->type = Pragma::Strict;
918 } else if (node->name == "ComponentBehavior"_L1) {
920 return false;
921 } else if (node->name == "ListPropertyAssignBehavior"_L1) {
923 return false;
924 } else if (node->name == "FunctionSignatureBehavior"_L1) {
926 return false;
927 } else if (node->name == "NativeMethodBehavior"_L1) {
929 return false;
930 } else if (node->name == "ValueTypeBehavior"_L1) {
932 return false;
933 } else if (node->name == "Translator"_L1) {
934 pragma->type = Pragma::Translator;
935 pragma->translationContextIndex = registerString(node->values->value.toString());
936
937 } else {
939 "QQmlParser", "Unknown pragma '%1'").arg(node->name));
940 return false;
941 }
942 } else {
944 "QQmlParser", "Empty pragma found"));
945 return false;
946 }
947
948 pragma->location.set(node->pragmaToken.startLine, node->pragmaToken.startColumn);
949 _pragmas.append(pragma);
950
951 return false;
952}
953
955{
957 QString name =
958 static_cast<QQmlJS::AST::IdentifierExpression *>(node)->name.toString();
959 return QStringList() << name;
962
964 if (rv.isEmpty())
965 return rv;
966 rv.append(expr->name.toString());
967 return rv;
968 }
969 return QStringList();
970}
971
973{
974 Enum *enumeration = New<Enum>();
975 QString enumName = node->name.toString();
976 enumeration->nameIndex = registerString(enumName);
977
978 if (enumName.at(0).isLower())
979 COMPILE_EXCEPTION(node->enumToken, tr("Scoped enum names must begin with an upper case letter"));
980
981 enumeration->location.set(node->enumToken.startLine, node->enumToken.startColumn);
982
983 enumeration->enumValues = New<PoolList<EnumValue>>();
984
986 while (e) {
987 EnumValue *enumValue = New<EnumValue>();
988 QString member = e->member.toString();
989 enumValue->nameIndex = registerString(member);
990 if (member.at(0).isLower())
991 COMPILE_EXCEPTION(e->memberToken, tr("Enum names must begin with an upper case letter"));
992
993 double part;
994 if (std::modf(e->value, &part) != 0.0)
995 COMPILE_EXCEPTION(e->valueToken, tr("Enum value must be an integer"));
996 if (e->value > std::numeric_limits<qint32>::max() || e->value < std::numeric_limits<qint32>::min())
997 COMPILE_EXCEPTION(e->valueToken, tr("Enum value out of range"));
998 enumValue->value = e->value;
999
1000 enumValue->location.set(e->memberToken.startLine, e->memberToken.startColumn);
1001 enumeration->enumValues->append(enumValue);
1002
1003 e = e->next;
1004 }
1005
1006 QString error = _object->appendEnum(enumeration);
1007 if (!error.isEmpty()) {
1008 recordError(node->enumToken, error);
1009 return false;
1010 }
1011
1012 return false;
1013}
1014
1015
1017{
1019 Signal *signal = New<Signal>();
1020 const QString signalName = node->name.toString();
1021 signal->nameIndex = registerString(signalName);
1022
1024 signal->location.set(loc.startLine, loc.startColumn);
1025
1026 signal->parameters = New<PoolList<Parameter> >();
1027
1029 while (p) {
1030 if (!p->type) {
1031 recordError(node->typeToken, QCoreApplication::translate("QQmlParser","Expected parameter type"));
1032 return false;
1033 }
1034
1035 Parameter *param = New<Parameter>();
1036 param->nameIndex = registerString(p->name.toString());
1038 &param->type, [this](const QString &str) { return registerString(str); },
1039 p->type)) {
1040 QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: ");
1041 errStr.append(p->type->toString());
1042 recordError(node->typeToken, errStr);
1043 return false;
1044 }
1045 signal->parameters->append(param);
1046 p = p->next;
1047 }
1048
1049 for (const QChar &ch : signalName) {
1050 if (ch.isLower())
1051 break;
1052 if (ch.isUpper()) {
1054 tr("Signal names cannot begin with an upper case letter"));
1055 }
1056 }
1057
1058 if (illegalNames.contains(signalName))
1059 COMPILE_EXCEPTION(node->identifierToken, tr("Illegal signal name"));
1060
1062 if (!error.isEmpty()) {
1064 return false;
1065 }
1066 } else {
1067 QString memberType = asString(node->memberType);
1068 if (memberType == QLatin1String("alias")) {
1069 return appendAlias(node);
1070 } else {
1071 QStringView name = node->name;
1072
1073 Property *property = New<Property>();
1074 property->setIsReadOnly(node->isReadonly());
1075 property->setIsRequired(node->isRequired());
1076
1077 const QV4::CompiledData::CommonType builtinPropertyType
1078 = Parameter::stringToBuiltinType(memberType);
1079 if (builtinPropertyType != QV4::CompiledData::CommonType::Invalid)
1080 property->setCommonType(builtinPropertyType);
1081 else
1082 property->setTypeNameIndex(registerString(memberType));
1083
1084 QStringView typeModifier = node->typeModifier;
1085 if (typeModifier == QLatin1String("list")) {
1086 property->setIsList(true);
1087 } else if (!typeModifier.isEmpty()) {
1088 recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier"));
1089 return false;
1090 }
1091
1092 const QString propName = name.toString();
1093 property->nameIndex = registerString(propName);
1094
1096 property->location.set(loc.startLine, loc.startColumn);
1097
1098 QQmlJS::SourceLocation errorLocation;
1099 QString error;
1100
1101 if (illegalNames.contains(propName))
1102 error = tr("Illegal property name");
1103 else
1104 error = _object->appendProperty(property, propName, node->isDefaultMember(), node->defaultToken(), &errorLocation);
1105
1106 if (!error.isEmpty()) {
1107 if (errorLocation.startLine == 0)
1108 errorLocation = node->identifierToken;
1109
1110 recordError(errorLocation, error);
1111 return false;
1112 }
1113
1115 if (node->binding) {
1116 // process QML-like initializers (e.g. property Object o: Object {})
1118 } else if (node->statement) {
1121 }
1123 }
1124 }
1125
1126 return false;
1127}
1128
1130{
1133 // See Object::appendFunction() for why.
1136 "QQmlParser", "Function declaration inside grouped property"));
1137 return false;
1138 }
1139
1140 CompiledFunctionOrExpression *foe = New<CompiledFunctionOrExpression>();
1141 foe->node = funDecl;
1142 foe->parentNode = funDecl;
1143 foe->nameIndex = registerString(funDecl->name.toString());
1144 const int index = _object->functionsAndExpressions->append(foe);
1145
1146 Function *f = New<Function>();
1147 QQmlJS::SourceLocation loc = funDecl->identifierToken;
1148 f->location.set(loc.startLine, loc.startColumn);
1149 f->index = index;
1150 f->nameIndex = registerString(funDecl->name.toString());
1151
1152 const auto idGenerator = [this](const QString &str) { return registerString(str); };
1153
1155 &f->returnType, idGenerator,
1156 funDecl->typeAnnotation ? funDecl->typeAnnotation->type : nullptr);
1157
1158 const QQmlJS::AST::BoundNames formals = funDecl->formals ? funDecl->formals->formals() : QQmlJS::AST::BoundNames();
1159 int formalsCount = formals.size();
1160 f->formals.allocate(pool, formalsCount);
1161
1162 int i = 0;
1163 for (const auto &arg : formals) {
1164 Parameter *functionParameter = &f->formals[i];
1165 functionParameter->nameIndex = registerString(arg.id);
1167 &functionParameter->type, idGenerator,
1168 arg.typeAnnotation.isNull() ? nullptr : arg.typeAnnotation->type);
1169 ++i;
1170 }
1171
1173 } else {
1174 recordError(node->firstSourceLocation(), QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element"));
1175 }
1176 return false;
1177}
1178
1180{
1181 auto extraData = New<RequiredPropertyExtraData>();
1182 extraData->nameIndex = registerString(ast->name.toString());
1184 return false;
1185}
1186
1188{
1189 QString s;
1190
1191 for (QQmlJS::AST::UiQualifiedId *it = node; it; it = it->next) {
1192 s.append(it->name);
1193
1194 if (it->next)
1195 s.append(QLatin1Char('.'));
1196 }
1197
1198 return s;
1199}
1200
1202{
1203 if (!node)
1204 return QStringView();
1205
1206 return textRefAt(node->firstSourceLocation(), node->lastSourceLocation());
1207}
1208
1210{
1211 if (string.isEmpty())
1212 return QTypeRevision();
1213
1214 const int dot = string.indexOf(QLatin1Char('.'));
1215 return (dot < 0)
1217 : QTypeRevision::fromVersion(string.left(dot).toInt(), string.mid(dot + 1).toInt());
1218}
1219
1221{
1222 return QStringView(sourceCode).mid(first.offset, last.offset + last.length - first.offset);
1223}
1224
1226{
1228 binding->valueLocation.set(loc.startLine, loc.startColumn);
1232
1233 QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement);
1234 if (exprStmt) {
1235 QQmlJS::AST::ExpressionNode * const expr = exprStmt->expression;
1236 if (QQmlJS::AST::StringLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(expr)) {
1238 binding->stringIndex = registerString(lit->value.toString());
1239 } else if (QQmlJS::AST::TemplateLiteral *templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
1240 templateLit && templateLit->hasNoSubstitution) {
1241 // A template literal without substitution is just a string.
1242 // With substitution, it could however be an arbitrarily complex expression
1244 binding->stringIndex = registerString(templateLit->value.toString());
1245 } else if (expr->kind == QQmlJS::AST::Node::Kind_TrueLiteral) {
1247 binding->value.b = true;
1248 } else if (expr->kind == QQmlJS::AST::Node::Kind_FalseLiteral) {
1250 binding->value.b = false;
1251 } else if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expr)) {
1253 binding->value.constantValueIndex = jsGenerator->registerConstant(QV4::Encode(lit->value));
1254 } else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
1255 if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base)) {
1256 tryGeneratingTranslationBinding(base->name, call->arguments, binding);
1257 // If it wasn't a translation binding, a normal script binding will be generated
1258 // below.
1259 }
1260 } else if (QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(expr)) {
1262 } else if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
1263 if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) {
1265 binding->value.constantValueIndex = jsGenerator->registerConstant(QV4::Encode(-lit->value));
1266 }
1267 } else if (QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr)) {
1269 binding->value.nullMarker = 0;
1270 }
1271 }
1272
1273 // Do binding instead
1276
1277 CompiledFunctionOrExpression *expr = New<CompiledFunctionOrExpression>();
1278 expr->node = statement;
1279 expr->parentNode = parentNode;
1280 expr->nameIndex = registerString(QLatin1String("expression for ")
1281 + stringAt(binding->propertyNameIndex));
1282 const int index = bindingsTarget()->functionsAndExpressions->append(expr);
1283 binding->value.compiledScriptIndex = index;
1284 // We don't need to store the binding script as string, except for script strings
1285 // and types with custom parsers. Those will be added later in the compilation phase.
1286 // Except that we cannot recover the string when cachegen runs; we need to therefore retain
1287 // "undefined". Any other "special" strings (for the various literals) are already handled above
1288 QQmlJS::AST::Node *nodeForString = statement;
1289 if (exprStmt)
1290 nodeForString = exprStmt->expression;
1291 if (asStringRef(nodeForString) == u"undefined")
1292 binding->stringIndex = registerString(u"undefined"_s);
1293 else
1294 binding->stringIndex = emptyStringIndex;
1295 }
1296}
1297
1299{
1300 const auto registerString = [&](QStringView string) {
1301 return jsGenerator->registerString(string.toString()) ;
1302 };
1303
1304 const auto finalizeTranslationData = [&](
1306 QV4::CompiledData::TranslationData translationData) {
1307 binding->setType(type);
1310 binding->value.translationDataIndex = jsGenerator->registerTranslation(translationData);
1312 binding->stringIndex = translationData.number;
1313 }
1314 };
1315
1317 base, args,
1318 registerString, registerString, registerString, finalizeTranslationData);
1319}
1320
1322{
1323 const QQmlJS::SourceLocation qualifiedNameLocation = name->identifierToken;
1324 Object *object = nullptr;
1325 if (!resolveQualifiedId(&name, &object))
1326 return;
1327 if (_object == object && name->name == QLatin1String("id")) {
1328 setId(name->identifierToken, value);
1329 return;
1330 }
1331 qSwap(_object, object);
1332 appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), value, parentNode);
1333 qSwap(_object, object);
1334}
1335
1336void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment)
1337{
1338 const QQmlJS::SourceLocation qualifiedNameLocation = name->identifierToken;
1339 Object *object = nullptr;
1340 if (!resolveQualifiedId(&name, &object, isOnAssignment))
1341 return;
1342 qSwap(_object, object);
1343 appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), objectIndex, /*isListItem*/false, isOnAssignment);
1344 qSwap(_object, object);
1345}
1346
1347void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocation, const QQmlJS::SourceLocation &nameLocation, quint32 propertyNameIndex,
1349{
1350 Binding *binding = New<Binding>();
1351 binding->propertyNameIndex = propertyNameIndex;
1352 binding->offset = nameLocation.offset;
1353 binding->location.set(nameLocation.startLine, nameLocation.startColumn);
1354 binding->clearFlags();
1355 setBindingValue(binding, value, parentNode);
1356 QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false);
1357 if (!error.isEmpty()) {
1358 recordError(qualifiedNameLocation, error);
1359 }
1360}
1361
1362void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocation, const QQmlJS::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem, bool isOnAssignment)
1363{
1364 if (stringAt(propertyNameIndex) == QLatin1String("id")) {
1365 recordError(nameLocation, tr("Invalid component id specification"));
1366 return;
1367 }
1368
1369 Binding *binding = New<Binding>();
1370 binding->propertyNameIndex = propertyNameIndex;
1371 binding->offset = nameLocation.offset;
1372 binding->location.set(nameLocation.startLine, nameLocation.startColumn);
1373
1374 const Object *obj = _objects.at(objectIndex);
1375 binding->valueLocation = obj->location;
1376
1377 binding->clearFlags();
1378
1381
1382 // No type name on the initializer means it must be a group property
1383 if (_objects.at(objectIndex)->inheritedTypeNameIndex == emptyStringIndex)
1385 else
1386 binding->setType(Binding::Type_Object);
1387
1388 if (isOnAssignment)
1390 if (isListItem)
1391 binding->setFlag(Binding::IsListItem);
1392
1393 binding->value.objectIndex = objectIndex;
1394 QString error = bindingsTarget()->appendBinding(binding, isListItem);
1395 if (!error.isEmpty()) {
1396 recordError(qualifiedNameLocation, error);
1397 }
1398}
1399
1401{
1402 Alias *alias = New<Alias>();
1403 alias->clearFlags();
1404 if (node->isReadonly())
1406
1407 const QString propName = node->name.toString();
1408 alias->setNameIndex(registerString(propName));
1409
1411 alias->location.set(loc.startLine, loc.startColumn);
1412
1414
1415 if (!node->statement && !node->binding)
1416 COMPILE_EXCEPTION(loc, tr("No property alias location"));
1417
1419 if (node->binding)
1420 rhsLoc = node->binding->firstSourceLocation();
1421 else if (node->statement)
1422 rhsLoc = node->statement->firstSourceLocation();
1423 else
1424 rhsLoc = node->semicolonToken;
1425 alias->referenceLocation.set(rhsLoc.startLine, rhsLoc.startColumn);
1426
1427 QStringList aliasReference;
1428
1429 if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(node->statement)) {
1430 aliasReference = astNodeToStringList(stmt->expression);
1431 if (aliasReference.isEmpty()) {
1432 if (isStatementNodeScript(node->statement)) {
1433 COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
1434 } else {
1435 COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias location"));
1436 }
1437 }
1438 } else {
1439 COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
1440 }
1441
1442 if (aliasReference.size() < 1 || aliasReference.size() > 3)
1443 COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
1444
1445 alias->setIdIndex(registerString(aliasReference.first()));
1446
1447 QString propertyValue = aliasReference.value(1);
1448 if (aliasReference.size() == 3)
1449 propertyValue += QLatin1Char('.') + aliasReference.at(2);
1450 alias->propertyNameIndex = registerString(propertyValue);
1451
1452 QQmlJS::SourceLocation errorLocation;
1453 QString error;
1454
1455 if (illegalNames.contains(propName))
1456 error = tr("Illegal property name");
1457 else
1458 error = _object->appendAlias(alias, propName, node->isDefaultMember(), node->defaultToken(), &errorLocation);
1459
1460 if (!error.isEmpty()) {
1461 if (errorLocation.startLine == 0)
1462 errorLocation = node->identifierToken;
1463
1464 recordError(errorLocation, error);
1465 return false;
1466 }
1467
1468 return false;
1469}
1470
1477
1479{
1480 QQmlJS::SourceLocation loc = value->firstSourceLocation();
1482
1483 QQmlJS::AST::Node *node = value;
1484 if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(node)) {
1485 if (QQmlJS::AST::StringLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(stmt->expression)) {
1486 str = lit->value;
1487 node = nullptr;
1488 } else
1489 node = stmt->expression;
1490 }
1491
1492 if (node && str.isEmpty())
1493 str = asStringRef(node);
1494
1495 if (str.isEmpty())
1496 COMPILE_EXCEPTION(loc, tr( "Invalid empty ID"));
1497
1498 QChar ch = str.at(0);
1499 if (ch.isLetter() && !ch.isLower())
1500 COMPILE_EXCEPTION(loc, tr( "IDs cannot start with an uppercase letter"));
1501
1502 QChar u(QLatin1Char('_'));
1503 if (!ch.isLetter() && ch != u)
1504 COMPILE_EXCEPTION(loc, tr( "IDs must start with a letter or underscore"));
1505
1506 for (int ii = 1; ii < str.size(); ++ii) {
1507 ch = str.at(ii);
1508 if (!ch.isLetterOrNumber() && ch != u)
1509 COMPILE_EXCEPTION(loc, tr( "IDs must contain only letters, numbers, and underscores"));
1510 }
1511
1512 QString idQString(str.toString());
1513 if (illegalNames.contains(idQString))
1514 COMPILE_EXCEPTION(loc, tr( "ID illegally masks global JavaScript property"));
1515
1517 COMPILE_EXCEPTION(idLocation, tr("Property value set multiple times"));
1518
1519 _object->idNameIndex = registerString(idQString);
1520 _object->locationOfIdProperty.set(idLocation.startLine, idLocation.startColumn);
1521
1522 return true;
1523}
1524
1525bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, Object **object, bool onAssignment)
1526{
1527 QQmlJS::AST::UiQualifiedId *qualifiedIdElement = *nameToResolve;
1528
1529 if (qualifiedIdElement->name == QLatin1String("id") && qualifiedIdElement->next)
1530 COMPILE_EXCEPTION(qualifiedIdElement->identifierToken, tr( "Invalid use of id property"));
1531
1532 // If it's a namespace, prepend the qualifier and we'll resolve it later to the correct type.
1533 QString currentName = qualifiedIdElement->name.toString();
1534 if (qualifiedIdElement->next) {
1535 for (const QV4::CompiledData::Import* import : std::as_const(_imports))
1536 if (import->qualifierIndex != emptyStringIndex
1537 && stringAt(import->qualifierIndex) == currentName) {
1538 qualifiedIdElement = qualifiedIdElement->next;
1539 currentName += QLatin1Char('.') + qualifiedIdElement->name;
1540
1541 if (!qualifiedIdElement->name.data()->isUpper())
1542 COMPILE_EXCEPTION(qualifiedIdElement->firstSourceLocation(), tr("Expected type name"));
1543
1544 break;
1545 }
1546 }
1547
1548 *object = _object;
1549 while (qualifiedIdElement->next) {
1550 const quint32 propertyNameIndex = registerString(currentName);
1551 const bool isAttachedProperty = qualifiedIdElement->name.data()->isUpper();
1552
1553 Binding *binding = (*object)->findBinding(propertyNameIndex);
1554 if (binding) {
1555 if (isAttachedProperty) {
1556 if (!binding->isAttachedProperty())
1557 binding = nullptr;
1558 } else if (!binding->isGroupProperty()) {
1559 binding = nullptr;
1560 }
1561 }
1562 if (!binding) {
1563 binding = New<Binding>();
1564 binding->propertyNameIndex = propertyNameIndex;
1565 binding->offset = qualifiedIdElement->identifierToken.offset;
1566 binding->location.set(qualifiedIdElement->identifierToken.startLine,
1567 qualifiedIdElement->identifierToken.startColumn);
1568 binding->valueLocation.set(qualifiedIdElement->next->identifierToken.startLine,
1569 qualifiedIdElement->next->identifierToken.startColumn);
1570 binding->clearFlags();
1571
1572 if (onAssignment)
1574
1575 if (isAttachedProperty)
1577 else
1579
1580 int objIndex = 0;
1581 if (!defineQMLObject(&objIndex, nullptr, binding->location, nullptr, nullptr))
1582 return false;
1583 binding->value.objectIndex = objIndex;
1584
1585 QString error = (*object)->appendBinding(binding, /*isListBinding*/false);
1586 if (!error.isEmpty()) {
1587 recordError(qualifiedIdElement->identifierToken, error);
1588 return false;
1589 }
1590 *object = _objects.at(objIndex);
1591 } else {
1592 Q_ASSERT(binding->isAttachedProperty() || binding->isGroupProperty());
1593 *object = _objects.at(binding->value.objectIndex);
1594 }
1595
1596 qualifiedIdElement = qualifiedIdElement->next;
1597 if (qualifiedIdElement)
1598 currentName = qualifiedIdElement->name.toString();
1599 }
1600 *nameToResolve = qualifiedIdElement;
1601 return true;
1602}
1603
1605{
1607 error.loc = location;
1608 error.message = description;
1609 errors << error;
1610}
1611
1613{
1614 if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement)) {
1615 QQmlJS::AST::ExpressionNode *expr = stmt->expression;
1616 if (QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(expr))
1617 return false;
1618 else if (expr->kind == QQmlJS::AST::Node::Kind_TrueLiteral)
1619 return false;
1621 return false;
1622 else if (QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expr))
1623 return false;
1624 else {
1625
1626 if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
1627 if (QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) {
1628 return false;
1629 }
1630 }
1631 }
1632 }
1633
1634 return true;
1635}
1636
1638{
1639 if (property->isCommonType() || property->isList())
1640 return false;
1641 QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement);
1642 if (!exprStmt)
1643 return false;
1644 QQmlJS::AST::ExpressionNode * const expr = exprStmt->expression;
1645 return QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr);
1646}
1647
1649{
1650 using namespace QV4::CompiledData;
1651
1652 output.jsGenerator.stringTable.registerString(output.jsModule.fileName);
1653 output.jsGenerator.stringTable.registerString(output.jsModule.finalUrl);
1654
1655 Unit *jsUnit = nullptr;
1656
1657 if (!output.javaScriptCompilationUnit)
1658 output.javaScriptCompilationUnit.adopt(new QV4::CompiledData::CompilationUnit);
1659
1660 // We may already have unit data if we're loading an ahead-of-time generated cache file.
1661 if (output.javaScriptCompilationUnit->unitData()) {
1662 jsUnit = const_cast<Unit *>(output.javaScriptCompilationUnit->unitData());
1663 output.javaScriptCompilationUnit->dynamicStrings
1664 = output.jsGenerator.stringTable.allStrings();
1665 } else {
1666 Unit *createdUnit;
1667 jsUnit = createdUnit = output.jsGenerator.generateUnit();
1668
1669 // enable flag if we encountered pragma Singleton
1670 for (Pragma *p : std::as_const(output.pragmas)) {
1671 switch (p->type) {
1672 case Pragma::Singleton:
1673 createdUnit->flags |= Unit::IsSingleton;
1674 break;
1675 case Pragma::Strict:
1676 createdUnit->flags |= Unit::IsStrict;
1677 break;
1679 // ### Qt7: Change the default to Bound by reverting the meaning of the flag.
1680 switch (p->componentBehavior) {
1681 case Pragma::Bound:
1682 createdUnit->flags |= Unit::ComponentsBound;
1683 break;
1684 case Pragma::Unbound:
1685 // this is the default
1686 break;
1687 }
1688 break;
1690 switch (p->listPropertyAssignBehavior) {
1691 case Pragma::Replace:
1692 createdUnit->flags |= Unit::ListPropertyAssignReplace;
1693 break;
1695 createdUnit->flags |= Unit::ListPropertyAssignReplaceIfNotDefault;
1696 break;
1697 case Pragma::Append:
1698 // this is the default
1699 break;
1700 }
1701 break;
1703 switch (p->functionSignatureBehavior) {
1704 case Pragma::Enforced:
1705 break;
1706 case Pragma::Ignored:
1707 createdUnit->flags |= Unit::FunctionSignaturesIgnored;
1708 break;
1709 }
1710 break;
1712 switch (p->nativeMethodBehavior) {
1714 createdUnit->flags |= Unit::NativeMethodsAcceptThisObject;
1715 break;
1717 // this is the default;
1718 break;
1719 }
1720 break;
1722 if (Pragma::ValueTypeBehaviorValues(p->valueTypeBehavior)
1723 .testFlag(Pragma::Copy)) {
1724 createdUnit->flags |= Unit::ValueTypesCopied;
1725 }
1726 if (Pragma::ValueTypeBehaviorValues(p->valueTypeBehavior)
1727 .testFlag(Pragma::Addressable)) {
1728 createdUnit->flags |= Unit::ValueTypesAddressable;
1729 }
1730 if (Pragma::ValueTypeBehaviorValues(p->valueTypeBehavior)
1731 .testFlag(Pragma::Assertable)) {
1732 createdUnit->flags |= Unit::ValueTypesAssertable;
1733 }
1734 break;
1735 case Pragma::Translator:
1736 if (createdUnit->translationTableSize)
1737 if (quint32_le *index = createdUnit->translationContextIndex())
1738 *index = p->translationContextIndex;
1739 break;
1740 }
1741 }
1742
1743 if (dependencyHasher) {
1744 const QByteArray checksum = dependencyHasher();
1745 if (checksum.size() == sizeof(createdUnit->dependencyMD5Checksum)) {
1746 memcpy(createdUnit->dependencyMD5Checksum, checksum.constData(),
1747 sizeof(createdUnit->dependencyMD5Checksum));
1748 }
1749 }
1750
1751 createdUnit->sourceFileIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.fileName);
1752 createdUnit->finalUrlIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.finalUrl);
1753 }
1754
1755 // No more new strings after this point, we're calculating offsets.
1756 output.jsGenerator.stringTable.freeze();
1757
1758 const uint importSize = uint(sizeof(QV4::CompiledData::Import)) * output.imports.size();
1759 const uint objectOffsetTableSize = output.objects.size() * uint(sizeof(quint32));
1760
1761 QHash<const Object*, quint32> objectOffsets;
1762
1763 const unsigned int objectOffset = sizeof(QV4::CompiledData::QmlUnit) + importSize;
1764 uint nextOffset = objectOffset + objectOffsetTableSize;
1765 for (Object *o : std::as_const(output.objects)) {
1766 objectOffsets.insert(o, nextOffset);
1767 nextOffset += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.size(), o->inlineComponentCount(), o->requiredPropertyExtraDataCount());
1768
1769 int signalTableSize = 0;
1770 for (const Signal *s = o->firstSignal(); s; s = s->next)
1771 signalTableSize += QV4::CompiledData::Signal::calculateSize(s->parameters->count);
1772
1773 nextOffset += signalTableSize;
1774
1775 int enumTableSize = 0;
1776 for (const Enum *e = o->firstEnum(); e; e = e->next)
1777 enumTableSize += QV4::CompiledData::Enum::calculateSize(e->enumValues->count);
1778
1779 nextOffset += enumTableSize;
1780 }
1781
1782 const uint totalSize = nextOffset;
1783 char *data = (char*)malloc(totalSize);
1784 memset(data, 0, totalSize);
1785 QV4::CompiledData::QmlUnit *qmlUnit = reinterpret_cast<QV4::CompiledData::QmlUnit *>(data);
1786 qmlUnit->offsetToImports = sizeof(*qmlUnit);
1787 qmlUnit->nImports = output.imports.size();
1788 qmlUnit->offsetToObjects = objectOffset;
1789 qmlUnit->nObjects = output.objects.size();
1790
1791 // write imports
1792 char *importPtr = data + qmlUnit->offsetToImports;
1793 for (const QV4::CompiledData::Import *imp : std::as_const(output.imports)) {
1794 QV4::CompiledData::Import *importToWrite = reinterpret_cast<QV4::CompiledData::Import*>(importPtr);
1795 *importToWrite = *imp;
1796 importPtr += sizeof(QV4::CompiledData::Import);
1797 }
1798
1799 // write objects
1800 quint32_le *objectTable = reinterpret_cast<quint32_le*>(data + qmlUnit->offsetToObjects);
1801 for (int i = 0; i < output.objects.size(); ++i) {
1802 const Object *o = output.objects.at(i);
1803 char * const objectPtr = data + objectOffsets.value(o);
1804 *objectTable++ = objectOffsets.value(o);
1805
1806 QV4::CompiledData::Object *objectToWrite = reinterpret_cast<QV4::CompiledData::Object*>(objectPtr);
1807 objectToWrite->inheritedTypeNameIndex = o->inheritedTypeNameIndex;
1808 objectToWrite->indexOfDefaultPropertyOrAlias = o->indexOfDefaultPropertyOrAlias;
1809 objectToWrite->setHasAliasAsDefaultProperty(o->defaultPropertyIsAlias);
1810 objectToWrite->setFlags(QV4::CompiledData::Object::Flags(o->flags));
1811 objectToWrite->idNameIndex = o->idNameIndex;
1812 objectToWrite->setObjectId(o->id);
1813 objectToWrite->location = o->location;
1814 objectToWrite->locationOfIdProperty = o->locationOfIdProperty;
1815
1816 quint32 nextOffset = sizeof(QV4::CompiledData::Object);
1817
1818 objectToWrite->nFunctions = o->functionCount();
1819 objectToWrite->offsetToFunctions = nextOffset;
1820 nextOffset += objectToWrite->nFunctions * sizeof(quint32);
1821
1822 objectToWrite->nProperties = o->propertyCount();
1823 objectToWrite->offsetToProperties = nextOffset;
1824 nextOffset += objectToWrite->nProperties * sizeof(QV4::CompiledData::Property);
1825
1826 objectToWrite->nAliases = o->aliasCount();
1827 objectToWrite->offsetToAliases = nextOffset;
1828 nextOffset += objectToWrite->nAliases * sizeof(QV4::CompiledData::Alias);
1829
1830 objectToWrite->nEnums = o->enumCount();
1831 objectToWrite->offsetToEnums = nextOffset;
1832 nextOffset += objectToWrite->nEnums * sizeof(quint32);
1833
1834 objectToWrite->nSignals = o->signalCount();
1835 objectToWrite->offsetToSignals = nextOffset;
1836 nextOffset += objectToWrite->nSignals * sizeof(quint32);
1837
1838 objectToWrite->nBindings = o->bindingCount();
1839 objectToWrite->offsetToBindings = nextOffset;
1840 nextOffset += objectToWrite->nBindings * sizeof(QV4::CompiledData::Binding);
1841
1842 objectToWrite->nNamedObjectsInComponent = o->namedObjectsInComponent.size();
1843 objectToWrite->offsetToNamedObjectsInComponent = nextOffset;
1844 nextOffset += objectToWrite->nNamedObjectsInComponent * sizeof(quint32);
1845
1846 objectToWrite->nInlineComponents = o->inlineComponentCount();
1847 objectToWrite->offsetToInlineComponents = nextOffset;
1848 nextOffset += objectToWrite->nInlineComponents * sizeof (QV4::CompiledData::InlineComponent);
1849
1850 objectToWrite->nRequiredPropertyExtraData = o->requiredPropertyExtraDataCount();
1851 objectToWrite->offsetToRequiredPropertyExtraData = nextOffset;
1852 nextOffset += objectToWrite->nRequiredPropertyExtraData * sizeof(QV4::CompiledData::RequiredPropertyExtraData);
1853
1854 quint32_le *functionsTable = reinterpret_cast<quint32_le *>(objectPtr + objectToWrite->offsetToFunctions);
1855 for (const Function *f = o->firstFunction(); f; f = f->next)
1856 *functionsTable++ = o->runtimeFunctionIndices.at(f->index);
1857
1858 char *propertiesPtr = objectPtr + objectToWrite->offsetToProperties;
1859 for (const Property *p = o->firstProperty(); p; p = p->next) {
1860 QV4::CompiledData::Property *propertyToWrite = reinterpret_cast<QV4::CompiledData::Property*>(propertiesPtr);
1861 *propertyToWrite = *p;
1862 propertiesPtr += sizeof(QV4::CompiledData::Property);
1863 }
1864
1865 char *aliasesPtr = objectPtr + objectToWrite->offsetToAliases;
1866 for (const Alias *a = o->firstAlias(); a; a = a->next) {
1867 QV4::CompiledData::Alias *aliasToWrite = reinterpret_cast<QV4::CompiledData::Alias*>(aliasesPtr);
1868 *aliasToWrite = *a;
1869 aliasesPtr += sizeof(QV4::CompiledData::Alias);
1870 }
1871
1872 char *bindingPtr = objectPtr + objectToWrite->offsetToBindings;
1873 bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isValueBindingNoAlias);
1874 bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isSignalHandler);
1875 bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isAttachedProperty);
1876 bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isGroupProperty);
1877 bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isValueBindingToAlias);
1878 Q_ASSERT((bindingPtr - objectToWrite->offsetToBindings - objectPtr) / sizeof(QV4::CompiledData::Binding) == unsigned(o->bindingCount()));
1879
1880 quint32_le *signalOffsetTable = reinterpret_cast<quint32_le *>(objectPtr + objectToWrite->offsetToSignals);
1881 quint32 signalTableSize = 0;
1882 char *signalPtr = objectPtr + nextOffset;
1883 for (const Signal *s = o->firstSignal(); s; s = s->next) {
1884 *signalOffsetTable++ = signalPtr - objectPtr;
1885 QV4::CompiledData::Signal *signalToWrite = reinterpret_cast<QV4::CompiledData::Signal*>(signalPtr);
1886
1887 signalToWrite->nameIndex = s->nameIndex;
1888 signalToWrite->location = s->location;
1889 signalToWrite->nParameters = s->parameters->count;
1890
1891 QV4::CompiledData::Parameter *parameterToWrite = reinterpret_cast<QV4::CompiledData::Parameter*>(signalPtr + sizeof(*signalToWrite));
1892 for (Parameter *param = s->parameters->first; param; param = param->next, ++parameterToWrite)
1893 *parameterToWrite = *param;
1894
1895 int size = QV4::CompiledData::Signal::calculateSize(s->parameters->count);
1896 signalTableSize += size;
1897 signalPtr += size;
1898 }
1899 nextOffset += signalTableSize;
1900
1901 quint32_le *enumOffsetTable = reinterpret_cast<quint32_le*>(objectPtr + objectToWrite->offsetToEnums);
1902 char *enumPtr = objectPtr + nextOffset;
1903 for (const Enum *e = o->firstEnum(); e; e = e->next) {
1904 *enumOffsetTable++ = enumPtr - objectPtr;
1905 QV4::CompiledData::Enum *enumToWrite = reinterpret_cast<QV4::CompiledData::Enum*>(enumPtr);
1906
1907 enumToWrite->nameIndex = e->nameIndex;
1908 enumToWrite->location = e->location;
1909 enumToWrite->nEnumValues = e->enumValues->count;
1910
1911 QV4::CompiledData::EnumValue *enumValueToWrite = reinterpret_cast<QV4::CompiledData::EnumValue*>(enumPtr + sizeof(*enumToWrite));
1912 for (EnumValue *enumValue = e->enumValues->first; enumValue; enumValue = enumValue->next, ++enumValueToWrite)
1913 *enumValueToWrite = *enumValue;
1914
1915 int size = QV4::CompiledData::Enum::calculateSize(e->enumValues->count);
1916 enumPtr += size;
1917 }
1918
1919 quint32_le *namedObjectInComponentPtr = reinterpret_cast<quint32_le *>(objectPtr + objectToWrite->offsetToNamedObjectsInComponent);
1920 for (int i = 0; i < o->namedObjectsInComponent.size(); ++i) {
1921 *namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i);
1922 }
1923
1924 char *inlineComponentPtr = objectPtr + objectToWrite->offsetToInlineComponents;
1925 for (auto it = o->inlineComponentsBegin(); it != o->inlineComponentsEnd(); ++it) {
1926 const InlineComponent *ic = it.ptr;
1927 QV4::CompiledData::InlineComponent *icToWrite = reinterpret_cast<QV4::CompiledData::InlineComponent*>(inlineComponentPtr);
1928 *icToWrite = *ic;
1929 inlineComponentPtr += sizeof(QV4::CompiledData::InlineComponent);
1930 }
1931
1932 char *requiredPropertyExtraDataPtr = objectPtr + objectToWrite->offsetToRequiredPropertyExtraData;
1933 for (auto it = o->requiredPropertyExtraDataBegin(); it != o->requiredPropertyExtraDataEnd(); ++it) {
1934 const RequiredPropertyExtraData *extraData = it.ptr;
1935 QV4::CompiledData::RequiredPropertyExtraData *extraDataToWrite = reinterpret_cast<QV4::CompiledData::RequiredPropertyExtraData*>(requiredPropertyExtraDataPtr);
1936 *extraDataToWrite = *extraData;
1937 requiredPropertyExtraDataPtr += sizeof(QV4::CompiledData::RequiredPropertyExtraData);
1938 }
1939 }
1940
1941 if (!output.javaScriptCompilationUnit->unitData()) {
1942 // Combine the qml data into the general unit data.
1943 jsUnit = static_cast<QV4::CompiledData::Unit *>(realloc(jsUnit, jsUnit->unitSize + totalSize));
1944 jsUnit->offsetToQmlUnit = jsUnit->unitSize;
1945 jsUnit->unitSize += totalSize;
1946 memcpy(jsUnit->qmlUnit(), qmlUnit, totalSize);
1947 free(qmlUnit);
1949 qmlUnit = jsUnit->qmlUnit();
1950 }
1951
1952 static const bool showStats = qEnvironmentVariableIsSet("QML_SHOW_UNIT_STATS");
1953 if (showStats) {
1954 qDebug() << "Generated QML unit that is" << totalSize << "bytes big contains:";
1955 qDebug() << " " << jsUnit->functionTableSize << "functions";
1956 qDebug() << " " << jsUnit->unitSize << "for JS unit";
1957 qDebug() << " " << importSize << "for imports";
1958 qDebug() << " " << nextOffset - objectOffset - objectOffsetTableSize << "for" << qmlUnit->nObjects << "objects";
1959 quint32 totalBindingCount = 0;
1960 for (quint32 i = 0; i < qmlUnit->nObjects; ++i)
1961 totalBindingCount += qmlUnit->objectAt(i)->nBindings;
1962 qDebug() << " " << totalBindingCount << "bindings";
1963 quint32 totalCodeSize = 0;
1964 for (quint32 i = 0; i < jsUnit->functionTableSize; ++i)
1965 totalCodeSize += jsUnit->functionAt(i)->codeSize;
1966 qDebug() << " " << totalCodeSize << "bytes total byte code";
1967 qDebug() << " " << jsUnit->stringTableSize << "strings";
1968 quint32 totalStringSize = 0;
1969 for (quint32 i = 0; i < jsUnit->stringTableSize; ++i)
1970 totalStringSize += QV4::CompiledData::String::calculateSize(jsUnit->stringAtInternal(i));
1971 qDebug() << " " << totalStringSize << "bytes total strings";
1972 }
1973
1974 output.javaScriptCompilationUnit->setUnitData(
1975 jsUnit, qmlUnit, output.jsModule.fileName, output.jsModule.finalUrl);
1976}
1977
1978char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const
1979{
1980 for (const Binding *b = o->firstBinding(); b; b = b->next) {
1981 if (!(b->*(filter))())
1982 continue;
1983 QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr);
1984 *bindingToWrite = *b;
1986 bindingToWrite->value.compiledScriptIndex = o->runtimeFunctionIndices.at(b->value.compiledScriptIndex);
1987 bindingPtr += sizeof(QV4::CompiledData::Binding);
1988 }
1989 return bindingPtr;
1990}
1991
1992JSCodeGen::JSCodeGen(Document *document, const QSet<QString> &globalNames,
1994 bool storeSourceLocations)
1995 : QV4::Compiler::Codegen(&document->jsGenerator, /*strict mode*/ false, iface,
1996 storeSourceLocations),
1997 document(document)
1998{
1999 m_globalNames = globalNames;
2000 _module = &document->jsModule;
2001 _fileNameIsUrl = true;
2002}
2003
2005 const QList<CompiledFunctionOrExpression> &functions)
2006{
2007 auto qmlName = [&](const CompiledFunctionOrExpression &c) {
2008 if (c.nameIndex != 0)
2009 return document->stringAt(c.nameIndex);
2010 else
2011 return QStringLiteral("%qml-expression-entry");
2012 };
2013 QVector<int> runtimeFunctionIndices(functions.size());
2014
2017 for (const CompiledFunctionOrExpression &f : functions) {
2018 Q_ASSERT(f.node != document->program);
2019 Q_ASSERT(f.parentNode && f.parentNode != document->program);
2020 auto function = f.node->asFunctionDefinition();
2021
2022 if (function) {
2023 scan.enterQmlFunction(function);
2024 } else {
2025 Q_ASSERT(f.node != f.parentNode);
2026 scan.enterEnvironment(f.parentNode, QV4::Compiler::ContextType::Binding, qmlName(f));
2027 }
2028
2029 /* We do not want to visit the whole function, as we already called enterQmlFunction
2030 However, there might be a function defined as a default argument of the function.
2031 That needs to be considered, too, so we call handleTopLevelFunctionFormals to
2032 deal with them.
2033 */
2034 scan.handleTopLevelFunctionFormals(function);
2035 scan(function ? function->body : f.node);
2036 scan.leaveEnvironment();
2037 }
2038 scan.leaveEnvironment();
2039
2040 if (hasError())
2041 return QVector<int>();
2042
2043 _context = nullptr;
2044
2045 for (int i = 0; i < functions.size(); ++i) {
2046 const CompiledFunctionOrExpression &qmlFunction = functions.at(i);
2047 QQmlJS::AST::Node *node = qmlFunction.node;
2048 Q_ASSERT(node != document->program);
2049
2051
2052 QString name;
2053 if (function)
2054 name = function->name.toString();
2055 else
2056 name = qmlName(qmlFunction);
2057
2059 if (function) {
2060 body = function->body;
2061 } else {
2062 // Synthesize source elements.
2064
2065 QQmlJS::AST::Statement *stmt = node->statementCast();
2066 if (!stmt) {
2067 Q_ASSERT(node->expressionCast());
2069 stmt = new (pool) QQmlJS::AST::ExpressionStatement(expr);
2070 }
2071 body = new (pool) QQmlJS::AST::StatementList(stmt);
2072 body = body->finish();
2073 }
2074
2075 int idx = defineFunction(name, function ? function : qmlFunction.parentNode,
2076 function ? function->formals : nullptr, body);
2077 runtimeFunctionIndices[i] = idx;
2078 }
2079
2080 return runtimeFunctionIndices;
2081}
2082
2084{
2085 if (object->functionsAndExpressions->count == 0)
2086 return true;
2087
2088 QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
2089 functionsToCompile.reserve(object->functionsAndExpressions->count);
2090 for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe;
2091 foe = foe->next) {
2092 functionsToCompile << *foe;
2093 }
2094
2095 const auto runtimeFunctionIndices = generateJSCodeForFunctionsAndBindings(functionsToCompile);
2096 if (hasError())
2097 return false;
2098
2099 object->runtimeFunctionIndices.allocate(document->jsParserEngine.pool(),
2100 runtimeFunctionIndices);
2101 return true;
2102}
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
static QString translate(const char *context, const char *key, const char *disambiguation=nullptr, int n=-1)
\threadsafe
qsizetype size() const noexcept
Definition qlist.h:398
bool isEmpty() const noexcept
Definition qlist.h:402
const_reference at(qsizetype i) const noexcept
Definition qlist.h:447
void append(parameter_type t)
Definition qlist.h:459
virtual SourceLocation firstSourceLocation() const =0
void accept(BaseVisitor *visitor)
virtual Statement * statementCast()
Definition qqmljsast.cpp:49
virtual SourceLocation lastSourceLocation() const =0
virtual ExpressionNode * expressionCast()
Definition qqmljsast.cpp:39
virtual FunctionExpression * asFunctionDefinition()
Definition qqmljsast.cpp:69
StatementList * finish()
UiArrayMemberList * members
UiVersionSpecifier * version
SourceLocation importIdToken
UiQualifiedId * importUri
SourceLocation fileNameToken
SourceLocation importToken
SourceLocation firstSourceLocation() const override
UiObjectDefinition * component
UiQualifiedId * qualifiedTypeNameId
UiObjectInitializer * initializer
UiObjectInitializer * initializer
SourceLocation firstSourceLocation() const override=0
UiPragmaValueList * values
SourceLocation pragmaToken
UiParameterList * parameters
SourceLocation firstSourceLocation() const override
enum QQmlJS::AST::UiPublicMember::@662 type
SourceLocation defaultToken() const
SourceLocation typeModifierToken
SourceLocation firstSourceLocation() const override
SourceLocation identifierToken
SourceLocation firstSourceLocation() const override
MemoryPool * pool()
void setCode(const QString &code, int lineno, bool qmlMode=true, CodeContinuation codeContinuation=CodeContinuation::Reset)
bool contains(const T &value) const
Definition qset.h:72
iterator insert(const T &value)
Definition qset.h:156
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
constexpr bool isEmpty() const noexcept
Returns whether this string view is empty - that is, whether {size() == 0}.
const_pointer data() const noexcept
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1121
constexpr bool isNull() const noexcept
Returns whether this string view is null - that is, whether {data() == nullptr}.
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5307
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1246
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
QString first(qsizetype n) const &
Definition qstring.h:390
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5516
QString & append(QChar c)
Definition qstring.cpp:3254
\inmodule QtCore
static constexpr QTypeRevision fromMajorVersion(Major majorVersion)
Produces a QTypeRevision from the given majorVersion with an invalid minor version.
static constexpr QTypeRevision fromVersion(Major majorVersion, Minor minorVersion)
Produces a QTypeRevision from the given majorVersion and minorVersion, both of which need to be a val...
QSet< QString > m_globalNames
virtual int defineFunction(const QString &name, QQmlJS::AST::Node *ast, QQmlJS::AST::FormalParameterList *formals, QQmlJS::AST::StatementList *body)
void enterQmlFunction(QQmlJS::AST::FunctionExpression *ast)
void handleTopLevelFunctionFormals(QQmlJS::AST::FunctionExpression *node)
void enterEnvironment(QQmlJS::AST::Node *node, ContextType compilationMode, const QString &name)
void enterGlobalEnvironment(ContextType compilationMode)
void importFile(const QString &jsfile, const QString &module, int lineNumber, int column) override
void importModule(const QString &uri, const QString &version, const QString &module, int lineNumber, int column) override
ScriptDirectivesCollector(QmlIR::Document *doc)
QString str
[2]
QSet< QString >::iterator it
auto signal
std::function< QByteArray()> DependentTypesHasher
void tryGeneratingTranslationBindingBase(QStringView base, QQmlJS::AST::ArgumentList *args, RegisterMainString registerMainString, RegisterCommentString registerCommentString, RegisterContextString registerContextString, FinalizeTranslationData finalizeTranslationData)
QList< QString > QStringList
Constructs a string list that contains the given string, str.
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static quint32 checksum(const QByteArray &table)
static bool isScript(QStringView tag)
Definition qlocale.cpp:557
#define qDebug
[1]
Definition qlogging.h:165
#define qWarning
Definition qlogging.h:167
const char * typeName
GLint location
GLenum GLsizei GLsizei GLint * values
[15]
GLboolean GLboolean GLboolean b
const GLfloat * m
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLuint GLuint end
GLuint object
[3]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLint left
GLenum type
GLenum target
GLbitfield flags
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLuint program
GLuint start
GLenum const GLint * param
GLuint name
GLint first
GLenum GLenum GLsizei void GLsizei void * column
GLhandleARB obj
[2]
GLdouble s
[6]
Definition qopenglext.h:235
const GLubyte * c
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLenum GLenum GLenum input
GLsizei const GLchar *const * string
[0]
Definition qopenglext.h:694
static qreal dot(const QPointF &a, const QPointF &b)
static const quint32 emptyStringIndex
#define COMPILE_EXCEPTION(location, desc)
static QStringList astNodeToStringList(QQmlJS::AST::Node *node)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int void * arg
#define qPrintable(string)
Definition qstring.h:1531
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
QT_BEGIN_NAMESPACE constexpr void qSwap(T &value1, T &value2) noexcept(std::is_nothrow_swappable_v< T >)
Definition qswap.h:20
#define tr(X)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
unsigned int quint32
Definition qtypes.h:50
unsigned int uint
Definition qtypes.h:34
static const uint base
Definition qurlidna.cpp:20
static int toInt(const QChar &qc, int R)
QT_BEGIN_NAMESPACE typedef uchar * output
const char property[13]
Definition qwizard.cpp:100
QList< int > list
[14]
QUrl url("example.com")
[constructor-url-reference]
QObject::connect nullptr
QSharedPointer< T > other(t)
[5]
char * toString(const MyType &t)
[31]
QJSValueList args
QJSEngine engine
[0]
static bool run(IRBuilder *builder, QQmlJS::AST::UiPragma *node, Pragma *pragma)
\inmodule QtCore \reentrant
Definition qchar.h:18
void setNameIndex(quint32 nameIndex)
void setIdIndex(quint32 idIndex)
union QV4::CompiledData::Binding::@548 value
static int calculateSize(int nEnumValues)
void set(quint32 line, quint32 column)
static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent, int nInlineComponents, int nRequiredPropertyExtraData)
void set(Flags flags, quint32 typeNameIndexOrCommonType)
const Object * objectAt(int idx) const
static int calculateSize(int nParameters)
static int calculateSize(const QString &str)
static void generateUnitChecksum(CompiledData::Unit *unit)
int registerTranslation(const CompiledData::TranslationData &translation)
QString stringForIndex(int index) const
int registerConstant(ReturnedValue v)
int registerString(const QString &str)
QString stringForIndex(int index) const
QQmlJS::AST::UiProgram * program
QString stringAt(int index) const
QV4::Compiler::Module jsModule
QV4::Compiler::JSUnitGenerator jsGenerator
QList< const QV4::CompiledData::Import * > imports
Document(bool debugMode)
QQmlJS::Engine jsParserEngine
QV4::CompiledData::Location location
PoolList< EnumValue > * enumValues
QV4::CompiledData::Location location
QVector< Object * > _objects
bool resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, Object **object, bool onAssignment=false)
QList< const QV4::CompiledData::Import * > _imports
QList< Pragma * > _pragmas
bool defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qualifiedTypeNameId, const QV4::CompiledData::Location &location, QQmlJS::AST::UiObjectInitializer *initializer, Object *declarationsOverride=nullptr)
QStringView asStringRef(QQmlJS::AST::Node *node)
static QTypeRevision extractVersion(QStringView string)
Object * bindingsTarget() const
IRBuilder(const QSet< QString > &illegalNames)
bool setId(const QQmlJS::SourceLocation &idLocation, QQmlJS::AST::Statement *value)
static bool isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement)
void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode)
QString stringAt(int index) const
void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, QQmlJS::AST::Node *parentNode)
QQmlJS::MemoryPool * pool
void accept(QQmlJS::AST::Node *node)
bool visit(QQmlJS::AST::UiArrayMemberList *ast) override
Property * _propertyDeclaration
bool generateFromQml(const QString &code, const QString &url, Document *output)
QList< QQmlJS::DiagnosticMessage > errors
static bool isStatementNodeScript(QQmlJS::AST::Statement *statement)
QV4::Compiler::JSUnitGenerator * jsGenerator
bool appendAlias(QQmlJS::AST::UiPublicMember *node)
quint32 registerString(const QString &str) const
QSet< QString > inlineComponentsNames
void tryGeneratingTranslationBinding(QStringView base, QQmlJS::AST::ArgumentList *args, QV4::CompiledData::Binding *binding)
static QString asString(QQmlJS::AST::UiQualifiedId *node)
void recordError(const QQmlJS::SourceLocation &location, const QString &description)
QString sanityCheckFunctionNames(Object *obj, const QSet< QString > &illegalNames, QQmlJS::SourceLocation *errorLocation)
QSet< QString > illegalNames
QStringView textRefAt(const QQmlJS::SourceLocation &loc) const
bool generateRuntimeFunctions(QmlIR::Object *object)
JSCodeGen(Document *document, const QSet< QString > &globalNames, QV4::Compiler::CodegenWarningInterface *iface=QV4::Compiler::defaultCodegenWarningInterface(), bool storeSourceLocations=false)
QVector< int > generateJSCodeForFunctionsAndBindings(const QList< CompiledFunctionOrExpression > &functions)
int indexOfDefaultPropertyOrAlias
QString appendEnum(Enum *enumeration)
void appendRequiredPropertyExtraData(RequiredPropertyExtraData *extraData)
void insertSorted(Binding *b)
QString appendBinding(Binding *b, bool isListBinding)
void appendInlineComponent(InlineComponent *ic)
QString appendSignal(Signal *signal)
QString bindingAsString(Document *doc, int scriptIndex) const
void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QV4::CompiledData::Location &location)
Object * declarationsOverride
QString appendAlias(Alias *prop, const QString &aliasName, bool isDefaultProperty, const QQmlJS::SourceLocation &defaultToken, QQmlJS::SourceLocation *errorLocation)
QString appendProperty(Property *prop, const QString &propertyName, bool isDefaultProperty, const QQmlJS::SourceLocation &defaultToken, QQmlJS::SourceLocation *errorLocation)
PoolList< CompiledFunctionOrExpression > * functionsAndExpressions
void appendFunction(QmlIR::Function *f)
quint32 inheritedTypeNameIndex
QV4::CompiledData::Location locationOfIdProperty
Binding * findBinding(quint32 nameIndex) const
static bool initType(QV4::CompiledData::ParameterType *type, const IdGenerator &idGenerator, const QQmlJS::AST::Type *annotation)
static QV4::CompiledData::CommonType stringToBuiltinType(const QString &typeName)
void generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher=QV4::CompiledData::DependentTypesHasher())
RequiredPropertyExtraData * next
PoolList< Parameter > * parameters
QStringList parameterStringList(const QV4::Compiler::StringTableGenerator *stringPool) const