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