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