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 return false;
865 });
866 }
867
868 Q_UNREACHABLE_RETURN(nullptr);
869 }
870
871 static bool isUnique(IRBuilder *builder)
872 {
873 for (const Pragma *prev : builder->_pragmas) {
874 if (prev->type == type())
875 return false;
876 }
877 return true;
878 };
879
880 static QLatin1StringView name()
881 {
882 switch (type()) {
884 return "list property assign behavior"_L1;
886 return "component behavior"_L1;
888 return "function signature behavior"_L1;
890 return "native method behavior"_L1;
892 return "value type behavior"_L1;
893 default:
894 break;
895 }
896 Q_UNREACHABLE_RETURN(QLatin1StringView());
897 }
898};
899
901{
902 Pragma *pragma = New<Pragma>();
903
904 if (!node->name.isNull()) {
905 if (node->name == "Singleton"_L1) {
906 pragma->type = Pragma::Singleton;
907 } else if (node->name == "Strict"_L1) {
908 pragma->type = Pragma::Strict;
909 } else if (node->name == "ComponentBehavior"_L1) {
911 return false;
912 } else if (node->name == "ListPropertyAssignBehavior"_L1) {
914 return false;
915 } else if (node->name == "FunctionSignatureBehavior"_L1) {
917 return false;
918 } else if (node->name == "NativeMethodBehavior"_L1) {
920 return false;
921 } else if (node->name == "ValueTypeBehavior"_L1) {
923 return false;
924 } else if (node->name == "Translator"_L1) {
925 pragma->type = Pragma::Translator;
926 pragma->translationContextIndex = registerString(node->values->value.toString());
927
928 } else {
930 "QQmlParser", "Unknown pragma '%1'").arg(node->name));
931 return false;
932 }
933 } else {
935 "QQmlParser", "Empty pragma found"));
936 return false;
937 }
938
939 pragma->location.set(node->pragmaToken.startLine, node->pragmaToken.startColumn);
940 _pragmas.append(pragma);
941
942 return false;
943}
944
946{
948 QString name =
949 static_cast<QQmlJS::AST::IdentifierExpression *>(node)->name.toString();
950 return QStringList() << name;
953
955 if (rv.isEmpty())
956 return rv;
957 rv.append(expr->name.toString());
958 return rv;
959 }
960 return QStringList();
961}
962
964{
965 Enum *enumeration = New<Enum>();
966 QString enumName = node->name.toString();
967 enumeration->nameIndex = registerString(enumName);
968
969 if (enumName.at(0).isLower())
970 COMPILE_EXCEPTION(node->enumToken, tr("Scoped enum names must begin with an upper case letter"));
971
972 enumeration->location.set(node->enumToken.startLine, node->enumToken.startColumn);
973
974 enumeration->enumValues = New<PoolList<EnumValue>>();
975
977 while (e) {
978 EnumValue *enumValue = New<EnumValue>();
979 QString member = e->member.toString();
980 enumValue->nameIndex = registerString(member);
981 if (member.at(0).isLower())
982 COMPILE_EXCEPTION(e->memberToken, tr("Enum names must begin with an upper case letter"));
983
984 double part;
985 if (std::modf(e->value, &part) != 0.0)
986 COMPILE_EXCEPTION(e->valueToken, tr("Enum value must be an integer"));
987 if (e->value > std::numeric_limits<qint32>::max() || e->value < std::numeric_limits<qint32>::min())
988 COMPILE_EXCEPTION(e->valueToken, tr("Enum value out of range"));
989 enumValue->value = e->value;
990
991 enumValue->location.set(e->memberToken.startLine, e->memberToken.startColumn);
992 enumeration->enumValues->append(enumValue);
993
994 e = e->next;
995 }
996
997 QString error = _object->appendEnum(enumeration);
998 if (!error.isEmpty()) {
1000 return false;
1001 }
1002
1003 return false;
1004}
1005
1006
1008{
1010 Signal *signal = New<Signal>();
1011 const QString signalName = node->name.toString();
1012 signal->nameIndex = registerString(signalName);
1013
1015 signal->location.set(loc.startLine, loc.startColumn);
1016
1017 signal->parameters = New<PoolList<Parameter> >();
1018
1020 while (p) {
1021 if (!p->type) {
1022 recordError(node->typeToken, QCoreApplication::translate("QQmlParser","Expected parameter type"));
1023 return false;
1024 }
1025
1026 Parameter *param = New<Parameter>();
1027 param->nameIndex = registerString(p->name.toString());
1029 &param->type, [this](const QString &str) { return registerString(str); },
1030 p->type)) {
1031 QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: ");
1032 errStr.append(p->type->toString());
1033 recordError(node->typeToken, errStr);
1034 return false;
1035 }
1036 signal->parameters->append(param);
1037 p = p->next;
1038 }
1039
1040 for (const QChar &ch : signalName) {
1041 if (ch.isLower())
1042 break;
1043 if (ch.isUpper()) {
1045 tr("Signal names cannot begin with an upper case letter"));
1046 }
1047 }
1048
1049 if (illegalNames.contains(signalName))
1050 COMPILE_EXCEPTION(node->identifierToken, tr("Illegal signal name"));
1051
1053 if (!error.isEmpty()) {
1055 return false;
1056 }
1057 } else {
1058 QString memberType = asString(node->memberType);
1059 if (memberType == QLatin1String("alias")) {
1060 return appendAlias(node);
1061 } else {
1062 QStringView name = node->name;
1063
1064 Property *property = New<Property>();
1065 property->setIsReadOnly(node->isReadonly());
1066 property->setIsRequired(node->isRequired());
1067
1068 const QV4::CompiledData::CommonType builtinPropertyType
1069 = Parameter::stringToBuiltinType(memberType);
1070 if (builtinPropertyType != QV4::CompiledData::CommonType::Invalid)
1071 property->setCommonType(builtinPropertyType);
1072 else
1073 property->setTypeNameIndex(registerString(memberType));
1074
1075 QStringView typeModifier = node->typeModifier;
1076 if (typeModifier == QLatin1String("list")) {
1077 property->setIsList(true);
1078 } else if (!typeModifier.isEmpty()) {
1079 recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier"));
1080 return false;
1081 }
1082
1083 const QString propName = name.toString();
1084 property->nameIndex = registerString(propName);
1085
1087 property->location.set(loc.startLine, loc.startColumn);
1088
1089 QQmlJS::SourceLocation errorLocation;
1090 QString error;
1091
1092 if (illegalNames.contains(propName))
1093 error = tr("Illegal property name");
1094 else
1095 error = _object->appendProperty(property, propName, node->isDefaultMember(), node->defaultToken(), &errorLocation);
1096
1097 if (!error.isEmpty()) {
1098 if (errorLocation.startLine == 0)
1099 errorLocation = node->identifierToken;
1100
1101 recordError(errorLocation, error);
1102 return false;
1103 }
1104
1106 if (node->binding) {
1107 // process QML-like initializers (e.g. property Object o: Object {})
1109 } else if (node->statement) {
1112 }
1114 }
1115 }
1116
1117 return false;
1118}
1119
1121{
1124 // See Object::appendFunction() for why.
1127 "QQmlParser", "Function declaration inside grouped property"));
1128 return false;
1129 }
1130
1131 CompiledFunctionOrExpression *foe = New<CompiledFunctionOrExpression>();
1132 foe->node = funDecl;
1133 foe->parentNode = funDecl;
1134 foe->nameIndex = registerString(funDecl->name.toString());
1135 const int index = _object->functionsAndExpressions->append(foe);
1136
1137 Function *f = New<Function>();
1138 QQmlJS::SourceLocation loc = funDecl->identifierToken;
1139 f->location.set(loc.startLine, loc.startColumn);
1140 f->index = index;
1141 f->nameIndex = registerString(funDecl->name.toString());
1142
1143 const auto idGenerator = [this](const QString &str) { return registerString(str); };
1144
1146 &f->returnType, idGenerator,
1147 funDecl->typeAnnotation ? funDecl->typeAnnotation->type : nullptr);
1148
1149 const QQmlJS::AST::BoundNames formals = funDecl->formals ? funDecl->formals->formals() : QQmlJS::AST::BoundNames();
1150 int formalsCount = formals.size();
1151 f->formals.allocate(pool, formalsCount);
1152
1153 int i = 0;
1154 for (const auto &arg : formals) {
1155 Parameter *functionParameter = &f->formals[i];
1156 functionParameter->nameIndex = registerString(arg.id);
1158 &functionParameter->type, idGenerator,
1159 arg.typeAnnotation.isNull() ? nullptr : arg.typeAnnotation->type);
1160 ++i;
1161 }
1162
1164 } else {
1165 recordError(node->firstSourceLocation(), QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element"));
1166 }
1167 return false;
1168}
1169
1171{
1172 auto extraData = New<RequiredPropertyExtraData>();
1173 extraData->nameIndex = registerString(ast->name.toString());
1175 return false;
1176}
1177
1179{
1180 QString s;
1181
1182 for (QQmlJS::AST::UiQualifiedId *it = node; it; it = it->next) {
1183 s.append(it->name);
1184
1185 if (it->next)
1186 s.append(QLatin1Char('.'));
1187 }
1188
1189 return s;
1190}
1191
1193{
1194 if (!node)
1195 return QStringView();
1196
1197 return textRefAt(node->firstSourceLocation(), node->lastSourceLocation());
1198}
1199
1201{
1202 if (string.isEmpty())
1203 return QTypeRevision();
1204
1205 const int dot = string.indexOf(QLatin1Char('.'));
1206 return (dot < 0)
1208 : QTypeRevision::fromVersion(string.left(dot).toInt(), string.mid(dot + 1).toInt());
1209}
1210
1212{
1213 return QStringView(sourceCode).mid(first.offset, last.offset + last.length - first.offset);
1214}
1215
1217{
1219 binding->valueLocation.set(loc.startLine, loc.startColumn);
1223
1224 QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement);
1225 if (exprStmt) {
1226 QQmlJS::AST::ExpressionNode * const expr = exprStmt->expression;
1227 if (QQmlJS::AST::StringLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(expr)) {
1229 binding->stringIndex = registerString(lit->value.toString());
1230 } else if (QQmlJS::AST::TemplateLiteral *templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
1231 templateLit && templateLit->hasNoSubstitution) {
1232 // A template literal without substitution is just a string.
1233 // With substitution, it could however be an arbitrarily complex expression
1235 binding->stringIndex = registerString(templateLit->value.toString());
1236 } else if (expr->kind == QQmlJS::AST::Node::Kind_TrueLiteral) {
1238 binding->value.b = true;
1239 } else if (expr->kind == QQmlJS::AST::Node::Kind_FalseLiteral) {
1241 binding->value.b = false;
1242 } else if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expr)) {
1244 binding->value.constantValueIndex = jsGenerator->registerConstant(QV4::Encode(lit->value));
1245 } else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
1246 if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base)) {
1247 tryGeneratingTranslationBinding(base->name, call->arguments, binding);
1248 // If it wasn't a translation binding, a normal script binding will be generated
1249 // below.
1250 }
1251 } else if (QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(expr)) {
1253 } else if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
1254 if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) {
1256 binding->value.constantValueIndex = jsGenerator->registerConstant(QV4::Encode(-lit->value));
1257 }
1258 } else if (QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr)) {
1260 binding->value.nullMarker = 0;
1261 }
1262 }
1263
1264 // Do binding instead
1267
1268 CompiledFunctionOrExpression *expr = New<CompiledFunctionOrExpression>();
1269 expr->node = statement;
1270 expr->parentNode = parentNode;
1271 expr->nameIndex = registerString(QLatin1String("expression for ")
1272 + stringAt(binding->propertyNameIndex));
1273 const int index = bindingsTarget()->functionsAndExpressions->append(expr);
1274 binding->value.compiledScriptIndex = index;
1275 // We don't need to store the binding script as string, except for script strings
1276 // and types with custom parsers. Those will be added later in the compilation phase.
1277 // Except that we cannot recover the string when cachegen runs; we need to therefore retain
1278 // "undefined". Any other "special" strings (for the various literals) are already handled above
1279 QQmlJS::AST::Node *nodeForString = statement;
1280 if (exprStmt)
1281 nodeForString = exprStmt->expression;
1282 if (asStringRef(nodeForString) == u"undefined")
1283 binding->stringIndex = registerString(u"undefined"_s);
1284 else
1285 binding->stringIndex = emptyStringIndex;
1286 }
1287}
1288
1290{
1291 const auto registerString = [&](QStringView string) {
1292 return jsGenerator->registerString(string.toString()) ;
1293 };
1294
1295 const auto finalizeTranslationData = [&](
1297 QV4::CompiledData::TranslationData translationData) {
1298 binding->setType(type);
1301 binding->value.translationDataIndex = jsGenerator->registerTranslation(translationData);
1303 binding->stringIndex = translationData.number;
1304 }
1305 };
1306
1308 base, args,
1309 registerString, registerString, registerString, finalizeTranslationData);
1310}
1311
1313{
1314 const QQmlJS::SourceLocation qualifiedNameLocation = name->identifierToken;
1315 Object *object = nullptr;
1316 if (!resolveQualifiedId(&name, &object))
1317 return;
1318 if (_object == object && name->name == QLatin1String("id")) {
1319 setId(name->identifierToken, value);
1320 return;
1321 }
1322 qSwap(_object, object);
1323 appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), value, parentNode);
1324 qSwap(_object, object);
1325}
1326
1327void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment)
1328{
1329 const QQmlJS::SourceLocation qualifiedNameLocation = name->identifierToken;
1330 Object *object = nullptr;
1331 if (!resolveQualifiedId(&name, &object, isOnAssignment))
1332 return;
1333 qSwap(_object, object);
1334 appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), objectIndex, /*isListItem*/false, isOnAssignment);
1335 qSwap(_object, object);
1336}
1337
1338void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocation, const QQmlJS::SourceLocation &nameLocation, quint32 propertyNameIndex,
1340{
1341 Binding *binding = New<Binding>();
1342 binding->propertyNameIndex = propertyNameIndex;
1343 binding->offset = nameLocation.offset;
1344 binding->location.set(nameLocation.startLine, nameLocation.startColumn);
1345 binding->clearFlags();
1346 setBindingValue(binding, value, parentNode);
1347 QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false);
1348 if (!error.isEmpty()) {
1349 recordError(qualifiedNameLocation, error);
1350 }
1351}
1352
1353void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocation, const QQmlJS::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem, bool isOnAssignment)
1354{
1355 if (stringAt(propertyNameIndex) == QLatin1String("id")) {
1356 recordError(nameLocation, tr("Invalid component id specification"));
1357 return;
1358 }
1359
1360 Binding *binding = New<Binding>();
1361 binding->propertyNameIndex = propertyNameIndex;
1362 binding->offset = nameLocation.offset;
1363 binding->location.set(nameLocation.startLine, nameLocation.startColumn);
1364
1365 const Object *obj = _objects.at(objectIndex);
1366 binding->valueLocation = obj->location;
1367
1368 binding->clearFlags();
1369
1372
1373 // No type name on the initializer means it must be a group property
1374 if (_objects.at(objectIndex)->inheritedTypeNameIndex == emptyStringIndex)
1376 else
1377 binding->setType(Binding::Type_Object);
1378
1379 if (isOnAssignment)
1381 if (isListItem)
1382 binding->setFlag(Binding::IsListItem);
1383
1384 binding->value.objectIndex = objectIndex;
1385 QString error = bindingsTarget()->appendBinding(binding, isListItem);
1386 if (!error.isEmpty()) {
1387 recordError(qualifiedNameLocation, error);
1388 }
1389}
1390
1392{
1393 Alias *alias = New<Alias>();
1394 alias->clearFlags();
1395 if (node->isReadonly())
1397
1398 const QString propName = node->name.toString();
1399 alias->setNameIndex(registerString(propName));
1400
1402 alias->location.set(loc.startLine, loc.startColumn);
1403
1405
1406 if (!node->statement && !node->binding)
1407 COMPILE_EXCEPTION(loc, tr("No property alias location"));
1408
1410 if (node->binding)
1411 rhsLoc = node->binding->firstSourceLocation();
1412 else if (node->statement)
1413 rhsLoc = node->statement->firstSourceLocation();
1414 else
1415 rhsLoc = node->semicolonToken;
1416 alias->referenceLocation.set(rhsLoc.startLine, rhsLoc.startColumn);
1417
1418 QStringList aliasReference;
1419
1420 if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(node->statement)) {
1421 aliasReference = astNodeToStringList(stmt->expression);
1422 if (aliasReference.isEmpty()) {
1423 if (isStatementNodeScript(node->statement)) {
1424 COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
1425 } else {
1426 COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias location"));
1427 }
1428 }
1429 } else {
1430 COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
1431 }
1432
1433 if (aliasReference.size() < 1 || aliasReference.size() > 3)
1434 COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
1435
1436 alias->setIdIndex(registerString(aliasReference.first()));
1437
1438 QString propertyValue = aliasReference.value(1);
1439 if (aliasReference.size() == 3)
1440 propertyValue += QLatin1Char('.') + aliasReference.at(2);
1441 alias->propertyNameIndex = registerString(propertyValue);
1442
1443 QQmlJS::SourceLocation errorLocation;
1444 QString error;
1445
1446 if (illegalNames.contains(propName))
1447 error = tr("Illegal property name");
1448 else
1449 error = _object->appendAlias(alias, propName, node->isDefaultMember(), node->defaultToken(), &errorLocation);
1450
1451 if (!error.isEmpty()) {
1452 if (errorLocation.startLine == 0)
1453 errorLocation = node->identifierToken;
1454
1455 recordError(errorLocation, error);
1456 return false;
1457 }
1458
1459 return false;
1460}
1461
1468
1470{
1471 QQmlJS::SourceLocation loc = value->firstSourceLocation();
1473
1474 QQmlJS::AST::Node *node = value;
1475 if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(node)) {
1476 if (QQmlJS::AST::StringLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(stmt->expression)) {
1477 str = lit->value;
1478 node = nullptr;
1479 } else
1480 node = stmt->expression;
1481 }
1482
1483 if (node && str.isEmpty())
1484 str = asStringRef(node);
1485
1486 if (str.isEmpty())
1487 COMPILE_EXCEPTION(loc, tr( "Invalid empty ID"));
1488
1489 QChar ch = str.at(0);
1490 if (ch.isLetter() && !ch.isLower())
1491 COMPILE_EXCEPTION(loc, tr( "IDs cannot start with an uppercase letter"));
1492
1493 QChar u(QLatin1Char('_'));
1494 if (!ch.isLetter() && ch != u)
1495 COMPILE_EXCEPTION(loc, tr( "IDs must start with a letter or underscore"));
1496
1497 for (int ii = 1; ii < str.size(); ++ii) {
1498 ch = str.at(ii);
1499 if (!ch.isLetterOrNumber() && ch != u)
1500 COMPILE_EXCEPTION(loc, tr( "IDs must contain only letters, numbers, and underscores"));
1501 }
1502
1503 QString idQString(str.toString());
1504 if (illegalNames.contains(idQString))
1505 COMPILE_EXCEPTION(loc, tr( "ID illegally masks global JavaScript property"));
1506
1508 COMPILE_EXCEPTION(idLocation, tr("Property value set multiple times"));
1509
1510 _object->idNameIndex = registerString(idQString);
1511 _object->locationOfIdProperty.set(idLocation.startLine, idLocation.startColumn);
1512
1513 return true;
1514}
1515
1516bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, Object **object, bool onAssignment)
1517{
1518 QQmlJS::AST::UiQualifiedId *qualifiedIdElement = *nameToResolve;
1519
1520 if (qualifiedIdElement->name == QLatin1String("id") && qualifiedIdElement->next)
1521 COMPILE_EXCEPTION(qualifiedIdElement->identifierToken, tr( "Invalid use of id property"));
1522
1523 // If it's a namespace, prepend the qualifier and we'll resolve it later to the correct type.
1524 QString currentName = qualifiedIdElement->name.toString();
1525 if (qualifiedIdElement->next) {
1526 for (const QV4::CompiledData::Import* import : std::as_const(_imports))
1527 if (import->qualifierIndex != emptyStringIndex
1528 && stringAt(import->qualifierIndex) == currentName) {
1529 qualifiedIdElement = qualifiedIdElement->next;
1530 currentName += QLatin1Char('.') + qualifiedIdElement->name;
1531
1532 if (!qualifiedIdElement->name.data()->isUpper())
1533 COMPILE_EXCEPTION(qualifiedIdElement->firstSourceLocation(), tr("Expected type name"));
1534
1535 break;
1536 }
1537 }
1538
1539 *object = _object;
1540 while (qualifiedIdElement->next) {
1541 const quint32 propertyNameIndex = registerString(currentName);
1542 const bool isAttachedProperty = qualifiedIdElement->name.data()->isUpper();
1543
1544 Binding *binding = (*object)->findBinding(propertyNameIndex);
1545 if (binding) {
1546 if (isAttachedProperty) {
1547 if (!binding->isAttachedProperty())
1548 binding = nullptr;
1549 } else if (!binding->isGroupProperty()) {
1550 binding = nullptr;
1551 }
1552 }
1553 if (!binding) {
1554 binding = New<Binding>();
1555 binding->propertyNameIndex = propertyNameIndex;
1556 binding->offset = qualifiedIdElement->identifierToken.offset;
1557 binding->location.set(qualifiedIdElement->identifierToken.startLine,
1558 qualifiedIdElement->identifierToken.startColumn);
1559 binding->valueLocation.set(qualifiedIdElement->next->identifierToken.startLine,
1560 qualifiedIdElement->next->identifierToken.startColumn);
1561 binding->clearFlags();
1562
1563 if (onAssignment)
1565
1566 if (isAttachedProperty)
1568 else
1570
1571 int objIndex = 0;
1572 if (!defineQMLObject(&objIndex, nullptr, binding->location, nullptr, nullptr))
1573 return false;
1574 binding->value.objectIndex = objIndex;
1575
1576 QString error = (*object)->appendBinding(binding, /*isListBinding*/false);
1577 if (!error.isEmpty()) {
1578 recordError(qualifiedIdElement->identifierToken, error);
1579 return false;
1580 }
1581 *object = _objects.at(objIndex);
1582 } else {
1583 Q_ASSERT(binding->isAttachedProperty() || binding->isGroupProperty());
1584 *object = _objects.at(binding->value.objectIndex);
1585 }
1586
1587 qualifiedIdElement = qualifiedIdElement->next;
1588 if (qualifiedIdElement)
1589 currentName = qualifiedIdElement->name.toString();
1590 }
1591 *nameToResolve = qualifiedIdElement;
1592 return true;
1593}
1594
1596{
1598 error.loc = location;
1599 error.message = description;
1600 errors << error;
1601}
1602
1604{
1605 if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement)) {
1606 QQmlJS::AST::ExpressionNode *expr = stmt->expression;
1607 if (QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(expr))
1608 return false;
1609 else if (expr->kind == QQmlJS::AST::Node::Kind_TrueLiteral)
1610 return false;
1612 return false;
1613 else if (QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expr))
1614 return false;
1615 else {
1616
1617 if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
1618 if (QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) {
1619 return false;
1620 }
1621 }
1622 }
1623 }
1624
1625 return true;
1626}
1627
1629{
1630 if (property->isCommonType() || property->isList())
1631 return false;
1632 QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement);
1633 if (!exprStmt)
1634 return false;
1635 QQmlJS::AST::ExpressionNode * const expr = exprStmt->expression;
1636 return QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr);
1637}
1638
1640{
1641 using namespace QV4::CompiledData;
1642
1643 output.jsGenerator.stringTable.registerString(output.jsModule.fileName);
1644 output.jsGenerator.stringTable.registerString(output.jsModule.finalUrl);
1645
1646 Unit *jsUnit = nullptr;
1647
1648 if (!output.javaScriptCompilationUnit)
1649 output.javaScriptCompilationUnit.adopt(new QV4::CompiledData::CompilationUnit);
1650
1651 // We may already have unit data if we're loading an ahead-of-time generated cache file.
1652 if (output.javaScriptCompilationUnit->unitData()) {
1653 jsUnit = const_cast<Unit *>(output.javaScriptCompilationUnit->unitData());
1654 output.javaScriptCompilationUnit->dynamicStrings
1655 = output.jsGenerator.stringTable.allStrings();
1656 } else {
1657 Unit *createdUnit;
1658 jsUnit = createdUnit = output.jsGenerator.generateUnit();
1659
1660 // enable flag if we encountered pragma Singleton
1661 for (Pragma *p : std::as_const(output.pragmas)) {
1662 switch (p->type) {
1663 case Pragma::Singleton:
1664 createdUnit->flags |= Unit::IsSingleton;
1665 break;
1666 case Pragma::Strict:
1667 createdUnit->flags |= Unit::IsStrict;
1668 break;
1670 // ### Qt7: Change the default to Bound by reverting the meaning of the flag.
1671 switch (p->componentBehavior) {
1672 case Pragma::Bound:
1673 createdUnit->flags |= Unit::ComponentsBound;
1674 break;
1675 case Pragma::Unbound:
1676 // this is the default
1677 break;
1678 }
1679 break;
1681 switch (p->listPropertyAssignBehavior) {
1682 case Pragma::Replace:
1683 createdUnit->flags |= Unit::ListPropertyAssignReplace;
1684 break;
1686 createdUnit->flags |= Unit::ListPropertyAssignReplaceIfNotDefault;
1687 break;
1688 case Pragma::Append:
1689 // this is the default
1690 break;
1691 }
1692 break;
1694 switch (p->functionSignatureBehavior) {
1695 case Pragma::Enforced:
1696 break;
1697 case Pragma::Ignored:
1698 createdUnit->flags |= Unit::FunctionSignaturesIgnored;
1699 break;
1700 }
1701 break;
1703 switch (p->nativeMethodBehavior) {
1705 createdUnit->flags |= Unit::NativeMethodsAcceptThisObject;
1706 break;
1708 // this is the default;
1709 break;
1710 }
1711 break;
1713 if (Pragma::ValueTypeBehaviorValues(p->valueTypeBehavior)
1714 .testFlag(Pragma::Copy)) {
1715 createdUnit->flags |= Unit::ValueTypesCopied;
1716 }
1717 if (Pragma::ValueTypeBehaviorValues(p->valueTypeBehavior)
1718 .testFlag(Pragma::Addressable)) {
1719 createdUnit->flags |= Unit::ValueTypesAddressable;
1720 }
1721 break;
1722 case Pragma::Translator:
1723 if (createdUnit->translationTableSize)
1724 if (quint32_le *index = createdUnit->translationContextIndex())
1725 *index = p->translationContextIndex;
1726 break;
1727 }
1728 }
1729
1730 if (dependencyHasher) {
1731 const QByteArray checksum = dependencyHasher();
1732 if (checksum.size() == sizeof(createdUnit->dependencyMD5Checksum)) {
1733 memcpy(createdUnit->dependencyMD5Checksum, checksum.constData(),
1734 sizeof(createdUnit->dependencyMD5Checksum));
1735 }
1736 }
1737
1738 createdUnit->sourceFileIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.fileName);
1739 createdUnit->finalUrlIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.finalUrl);
1740 }
1741
1742 // No more new strings after this point, we're calculating offsets.
1743 output.jsGenerator.stringTable.freeze();
1744
1745 const uint importSize = uint(sizeof(QV4::CompiledData::Import)) * output.imports.size();
1746 const uint objectOffsetTableSize = output.objects.size() * uint(sizeof(quint32));
1747
1748 QHash<const Object*, quint32> objectOffsets;
1749
1750 const unsigned int objectOffset = sizeof(QV4::CompiledData::QmlUnit) + importSize;
1751 uint nextOffset = objectOffset + objectOffsetTableSize;
1752 for (Object *o : std::as_const(output.objects)) {
1753 objectOffsets.insert(o, nextOffset);
1754 nextOffset += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.size(), o->inlineComponentCount(), o->requiredPropertyExtraDataCount());
1755
1756 int signalTableSize = 0;
1757 for (const Signal *s = o->firstSignal(); s; s = s->next)
1758 signalTableSize += QV4::CompiledData::Signal::calculateSize(s->parameters->count);
1759
1760 nextOffset += signalTableSize;
1761
1762 int enumTableSize = 0;
1763 for (const Enum *e = o->firstEnum(); e; e = e->next)
1764 enumTableSize += QV4::CompiledData::Enum::calculateSize(e->enumValues->count);
1765
1766 nextOffset += enumTableSize;
1767 }
1768
1769 const uint totalSize = nextOffset;
1770 char *data = (char*)malloc(totalSize);
1771 memset(data, 0, totalSize);
1772 QV4::CompiledData::QmlUnit *qmlUnit = reinterpret_cast<QV4::CompiledData::QmlUnit *>(data);
1773 qmlUnit->offsetToImports = sizeof(*qmlUnit);
1774 qmlUnit->nImports = output.imports.size();
1775 qmlUnit->offsetToObjects = objectOffset;
1776 qmlUnit->nObjects = output.objects.size();
1777
1778 // write imports
1779 char *importPtr = data + qmlUnit->offsetToImports;
1780 for (const QV4::CompiledData::Import *imp : std::as_const(output.imports)) {
1781 QV4::CompiledData::Import *importToWrite = reinterpret_cast<QV4::CompiledData::Import*>(importPtr);
1782 *importToWrite = *imp;
1783 importPtr += sizeof(QV4::CompiledData::Import);
1784 }
1785
1786 // write objects
1787 quint32_le *objectTable = reinterpret_cast<quint32_le*>(data + qmlUnit->offsetToObjects);
1788 for (int i = 0; i < output.objects.size(); ++i) {
1789 const Object *o = output.objects.at(i);
1790 char * const objectPtr = data + objectOffsets.value(o);
1791 *objectTable++ = objectOffsets.value(o);
1792
1793 QV4::CompiledData::Object *objectToWrite = reinterpret_cast<QV4::CompiledData::Object*>(objectPtr);
1794 objectToWrite->inheritedTypeNameIndex = o->inheritedTypeNameIndex;
1795 objectToWrite->indexOfDefaultPropertyOrAlias = o->indexOfDefaultPropertyOrAlias;
1796 objectToWrite->setHasAliasAsDefaultProperty(o->defaultPropertyIsAlias);
1797 objectToWrite->setFlags(QV4::CompiledData::Object::Flags(o->flags));
1798 objectToWrite->idNameIndex = o->idNameIndex;
1799 objectToWrite->setObjectId(o->id);
1800 objectToWrite->location = o->location;
1801 objectToWrite->locationOfIdProperty = o->locationOfIdProperty;
1802
1803 quint32 nextOffset = sizeof(QV4::CompiledData::Object);
1804
1805 objectToWrite->nFunctions = o->functionCount();
1806 objectToWrite->offsetToFunctions = nextOffset;
1807 nextOffset += objectToWrite->nFunctions * sizeof(quint32);
1808
1809 objectToWrite->nProperties = o->propertyCount();
1810 objectToWrite->offsetToProperties = nextOffset;
1811 nextOffset += objectToWrite->nProperties * sizeof(QV4::CompiledData::Property);
1812
1813 objectToWrite->nAliases = o->aliasCount();
1814 objectToWrite->offsetToAliases = nextOffset;
1815 nextOffset += objectToWrite->nAliases * sizeof(QV4::CompiledData::Alias);
1816
1817 objectToWrite->nEnums = o->enumCount();
1818 objectToWrite->offsetToEnums = nextOffset;
1819 nextOffset += objectToWrite->nEnums * sizeof(quint32);
1820
1821 objectToWrite->nSignals = o->signalCount();
1822 objectToWrite->offsetToSignals = nextOffset;
1823 nextOffset += objectToWrite->nSignals * sizeof(quint32);
1824
1825 objectToWrite->nBindings = o->bindingCount();
1826 objectToWrite->offsetToBindings = nextOffset;
1827 nextOffset += objectToWrite->nBindings * sizeof(QV4::CompiledData::Binding);
1828
1829 objectToWrite->nNamedObjectsInComponent = o->namedObjectsInComponent.size();
1830 objectToWrite->offsetToNamedObjectsInComponent = nextOffset;
1831 nextOffset += objectToWrite->nNamedObjectsInComponent * sizeof(quint32);
1832
1833 objectToWrite->nInlineComponents = o->inlineComponentCount();
1834 objectToWrite->offsetToInlineComponents = nextOffset;
1835 nextOffset += objectToWrite->nInlineComponents * sizeof (QV4::CompiledData::InlineComponent);
1836
1837 objectToWrite->nRequiredPropertyExtraData = o->requiredPropertyExtraDataCount();
1838 objectToWrite->offsetToRequiredPropertyExtraData = nextOffset;
1839 nextOffset += objectToWrite->nRequiredPropertyExtraData * sizeof(QV4::CompiledData::RequiredPropertyExtraData);
1840
1841 quint32_le *functionsTable = reinterpret_cast<quint32_le *>(objectPtr + objectToWrite->offsetToFunctions);
1842 for (const Function *f = o->firstFunction(); f; f = f->next)
1843 *functionsTable++ = o->runtimeFunctionIndices.at(f->index);
1844
1845 char *propertiesPtr = objectPtr + objectToWrite->offsetToProperties;
1846 for (const Property *p = o->firstProperty(); p; p = p->next) {
1847 QV4::CompiledData::Property *propertyToWrite = reinterpret_cast<QV4::CompiledData::Property*>(propertiesPtr);
1848 *propertyToWrite = *p;
1849 propertiesPtr += sizeof(QV4::CompiledData::Property);
1850 }
1851
1852 char *aliasesPtr = objectPtr + objectToWrite->offsetToAliases;
1853 for (const Alias *a = o->firstAlias(); a; a = a->next) {
1854 QV4::CompiledData::Alias *aliasToWrite = reinterpret_cast<QV4::CompiledData::Alias*>(aliasesPtr);
1855 *aliasToWrite = *a;
1856 aliasesPtr += sizeof(QV4::CompiledData::Alias);
1857 }
1858
1859 char *bindingPtr = objectPtr + objectToWrite->offsetToBindings;
1860 bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isValueBindingNoAlias);
1861 bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isSignalHandler);
1862 bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isAttachedProperty);
1863 bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isGroupProperty);
1864 bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isValueBindingToAlias);
1865 Q_ASSERT((bindingPtr - objectToWrite->offsetToBindings - objectPtr) / sizeof(QV4::CompiledData::Binding) == unsigned(o->bindingCount()));
1866
1867 quint32_le *signalOffsetTable = reinterpret_cast<quint32_le *>(objectPtr + objectToWrite->offsetToSignals);
1868 quint32 signalTableSize = 0;
1869 char *signalPtr = objectPtr + nextOffset;
1870 for (const Signal *s = o->firstSignal(); s; s = s->next) {
1871 *signalOffsetTable++ = signalPtr - objectPtr;
1872 QV4::CompiledData::Signal *signalToWrite = reinterpret_cast<QV4::CompiledData::Signal*>(signalPtr);
1873
1874 signalToWrite->nameIndex = s->nameIndex;
1875 signalToWrite->location = s->location;
1876 signalToWrite->nParameters = s->parameters->count;
1877
1878 QV4::CompiledData::Parameter *parameterToWrite = reinterpret_cast<QV4::CompiledData::Parameter*>(signalPtr + sizeof(*signalToWrite));
1879 for (Parameter *param = s->parameters->first; param; param = param->next, ++parameterToWrite)
1880 *parameterToWrite = *param;
1881
1882 int size = QV4::CompiledData::Signal::calculateSize(s->parameters->count);
1883 signalTableSize += size;
1884 signalPtr += size;
1885 }
1886 nextOffset += signalTableSize;
1887
1888 quint32_le *enumOffsetTable = reinterpret_cast<quint32_le*>(objectPtr + objectToWrite->offsetToEnums);
1889 char *enumPtr = objectPtr + nextOffset;
1890 for (const Enum *e = o->firstEnum(); e; e = e->next) {
1891 *enumOffsetTable++ = enumPtr - objectPtr;
1892 QV4::CompiledData::Enum *enumToWrite = reinterpret_cast<QV4::CompiledData::Enum*>(enumPtr);
1893
1894 enumToWrite->nameIndex = e->nameIndex;
1895 enumToWrite->location = e->location;
1896 enumToWrite->nEnumValues = e->enumValues->count;
1897
1898 QV4::CompiledData::EnumValue *enumValueToWrite = reinterpret_cast<QV4::CompiledData::EnumValue*>(enumPtr + sizeof(*enumToWrite));
1899 for (EnumValue *enumValue = e->enumValues->first; enumValue; enumValue = enumValue->next, ++enumValueToWrite)
1900 *enumValueToWrite = *enumValue;
1901
1902 int size = QV4::CompiledData::Enum::calculateSize(e->enumValues->count);
1903 enumPtr += size;
1904 }
1905
1906 quint32_le *namedObjectInComponentPtr = reinterpret_cast<quint32_le *>(objectPtr + objectToWrite->offsetToNamedObjectsInComponent);
1907 for (int i = 0; i < o->namedObjectsInComponent.size(); ++i) {
1908 *namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i);
1909 }
1910
1911 char *inlineComponentPtr = objectPtr + objectToWrite->offsetToInlineComponents;
1912 for (auto it = o->inlineComponentsBegin(); it != o->inlineComponentsEnd(); ++it) {
1913 const InlineComponent *ic = it.ptr;
1914 QV4::CompiledData::InlineComponent *icToWrite = reinterpret_cast<QV4::CompiledData::InlineComponent*>(inlineComponentPtr);
1915 *icToWrite = *ic;
1916 inlineComponentPtr += sizeof(QV4::CompiledData::InlineComponent);
1917 }
1918
1919 char *requiredPropertyExtraDataPtr = objectPtr + objectToWrite->offsetToRequiredPropertyExtraData;
1920 for (auto it = o->requiredPropertyExtraDataBegin(); it != o->requiredPropertyExtraDataEnd(); ++it) {
1921 const RequiredPropertyExtraData *extraData = it.ptr;
1922 QV4::CompiledData::RequiredPropertyExtraData *extraDataToWrite = reinterpret_cast<QV4::CompiledData::RequiredPropertyExtraData*>(requiredPropertyExtraDataPtr);
1923 *extraDataToWrite = *extraData;
1924 requiredPropertyExtraDataPtr += sizeof(QV4::CompiledData::RequiredPropertyExtraData);
1925 }
1926 }
1927
1928 if (!output.javaScriptCompilationUnit->unitData()) {
1929 // Combine the qml data into the general unit data.
1930 jsUnit = static_cast<QV4::CompiledData::Unit *>(realloc(jsUnit, jsUnit->unitSize + totalSize));
1931 jsUnit->offsetToQmlUnit = jsUnit->unitSize;
1932 jsUnit->unitSize += totalSize;
1933 memcpy(jsUnit->qmlUnit(), qmlUnit, totalSize);
1934 free(qmlUnit);
1936 qmlUnit = jsUnit->qmlUnit();
1937 }
1938
1939 static const bool showStats = qEnvironmentVariableIsSet("QML_SHOW_UNIT_STATS");
1940 if (showStats) {
1941 qDebug() << "Generated QML unit that is" << totalSize << "bytes big contains:";
1942 qDebug() << " " << jsUnit->functionTableSize << "functions";
1943 qDebug() << " " << jsUnit->unitSize << "for JS unit";
1944 qDebug() << " " << importSize << "for imports";
1945 qDebug() << " " << nextOffset - objectOffset - objectOffsetTableSize << "for" << qmlUnit->nObjects << "objects";
1946 quint32 totalBindingCount = 0;
1947 for (quint32 i = 0; i < qmlUnit->nObjects; ++i)
1948 totalBindingCount += qmlUnit->objectAt(i)->nBindings;
1949 qDebug() << " " << totalBindingCount << "bindings";
1950 quint32 totalCodeSize = 0;
1951 for (quint32 i = 0; i < jsUnit->functionTableSize; ++i)
1952 totalCodeSize += jsUnit->functionAt(i)->codeSize;
1953 qDebug() << " " << totalCodeSize << "bytes total byte code";
1954 qDebug() << " " << jsUnit->stringTableSize << "strings";
1955 quint32 totalStringSize = 0;
1956 for (quint32 i = 0; i < jsUnit->stringTableSize; ++i)
1957 totalStringSize += QV4::CompiledData::String::calculateSize(jsUnit->stringAtInternal(i));
1958 qDebug() << " " << totalStringSize << "bytes total strings";
1959 }
1960
1961 output.javaScriptCompilationUnit->setUnitData(
1962 jsUnit, qmlUnit, output.jsModule.fileName, output.jsModule.finalUrl);
1963}
1964
1965char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const
1966{
1967 for (const Binding *b = o->firstBinding(); b; b = b->next) {
1968 if (!(b->*(filter))())
1969 continue;
1970 QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr);
1971 *bindingToWrite = *b;
1973 bindingToWrite->value.compiledScriptIndex = o->runtimeFunctionIndices.at(b->value.compiledScriptIndex);
1974 bindingPtr += sizeof(QV4::CompiledData::Binding);
1975 }
1976 return bindingPtr;
1977}
1978
1979JSCodeGen::JSCodeGen(Document *document, const QSet<QString> &globalNames,
1981 bool storeSourceLocations)
1982 : QV4::Compiler::Codegen(&document->jsGenerator, /*strict mode*/ false, iface,
1983 storeSourceLocations),
1984 document(document)
1985{
1986 m_globalNames = globalNames;
1987 _module = &document->jsModule;
1988 _fileNameIsUrl = true;
1989}
1990
1992 const QList<CompiledFunctionOrExpression> &functions)
1993{
1994 auto qmlName = [&](const CompiledFunctionOrExpression &c) {
1995 if (c.nameIndex != 0)
1996 return document->stringAt(c.nameIndex);
1997 else
1998 return QStringLiteral("%qml-expression-entry");
1999 };
2000 QVector<int> runtimeFunctionIndices(functions.size());
2001
2004 for (const CompiledFunctionOrExpression &f : functions) {
2005 Q_ASSERT(f.node != document->program);
2006 Q_ASSERT(f.parentNode && f.parentNode != document->program);
2007 auto function = f.node->asFunctionDefinition();
2008
2009 if (function) {
2010 scan.enterQmlFunction(function);
2011 } else {
2012 Q_ASSERT(f.node != f.parentNode);
2013 scan.enterEnvironment(f.parentNode, QV4::Compiler::ContextType::Binding, qmlName(f));
2014 }
2015
2016 /* We do not want to visit the whole function, as we already called enterQmlFunction
2017 However, there might be a function defined as a default argument of the function.
2018 That needs to be considered, too, so we call handleTopLevelFunctionFormals to
2019 deal with them.
2020 */
2021 scan.handleTopLevelFunctionFormals(function);
2022 scan(function ? function->body : f.node);
2023 scan.leaveEnvironment();
2024 }
2025 scan.leaveEnvironment();
2026
2027 if (hasError())
2028 return QVector<int>();
2029
2030 _context = nullptr;
2031
2032 for (int i = 0; i < functions.size(); ++i) {
2033 const CompiledFunctionOrExpression &qmlFunction = functions.at(i);
2034 QQmlJS::AST::Node *node = qmlFunction.node;
2035 Q_ASSERT(node != document->program);
2036
2038
2039 QString name;
2040 if (function)
2041 name = function->name.toString();
2042 else
2043 name = qmlName(qmlFunction);
2044
2046 if (function) {
2047 body = function->body;
2048 } else {
2049 // Synthesize source elements.
2051
2052 QQmlJS::AST::Statement *stmt = node->statementCast();
2053 if (!stmt) {
2054 Q_ASSERT(node->expressionCast());
2056 stmt = new (pool) QQmlJS::AST::ExpressionStatement(expr);
2057 }
2058 body = new (pool) QQmlJS::AST::StatementList(stmt);
2059 body = body->finish();
2060 }
2061
2062 int idx = defineFunction(name, function ? function : qmlFunction.parentNode,
2063 function ? function->formals : nullptr, body);
2064 runtimeFunctionIndices[i] = idx;
2065 }
2066
2067 return runtimeFunctionIndices;
2068}
2069
2071{
2072 if (object->functionsAndExpressions->count == 0)
2073 return true;
2074
2075 QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
2076 functionsToCompile.reserve(object->functionsAndExpressions->count);
2077 for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe;
2078 foe = foe->next) {
2079 functionsToCompile << *foe;
2080 }
2081
2082 const auto runtimeFunctionIndices = generateJSCodeForFunctionsAndBindings(functionsToCompile);
2083 if (hasError())
2084 return false;
2085
2086 object->runtimeFunctionIndices.allocate(document->jsParserEngine.pool(),
2087 runtimeFunctionIndices);
2088 return true;
2089}
\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:397
bool isEmpty() const noexcept
Definition qlist.h:401
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
void append(parameter_type t)
Definition qlist.h:458
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:71
iterator insert(const T &value)
Definition qset.h:155
\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:5300
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:5506
QString & append(QChar c)
Definition qstring.cpp:3252
\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:164
#define qWarning
Definition qlogging.h:166
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:101
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::@545 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