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