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

source code of qtdeclarative/src/qml/compiler/qqmlirbuilder.cpp