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