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