Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qqmljstypepropagator.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qqmljsscope_p.h"
6
7#include "qqmljsutils_p.h"
8#include "qqmlsa_p.h"
9
10#include <private/qv4compilerscanfunctions_p.h>
11
12#include <QtQmlCompiler/private/qqmlsasourcelocation_p.h>
13
15
16using namespace Qt::StringLiterals;
17
30 const QQmlJSTypeResolver *typeResolver,
31 QQmlJSLogger *logger, QQmlSA::PassManager *passManager)
32 : QQmlJSCompilePass(unitGenerator, typeResolver, logger),
33 m_passManager(passManager)
34{
35}
36
39{
41 m_error = error;
43
44 do {
45 // Reset the error if we need to do another pass
46 if (m_state.needsMorePasses)
48
49 m_prevStateAnnotations = m_state.annotations;
50 m_state = PassState();
51 m_state.State::operator=(initialState(m_function));
52
53 reset();
54 decode(m_function->code.constData(), static_cast<uint>(m_function->code.size()));
55
56 // If we have found unresolved backwards jumps, we need to start over with a fresh state.
57 // Mind that m_jumpOriginRegisterStateByTargetInstructionOffset is retained in that case.
58 // This means that we won't start over for the same reason again.
59 } while (m_state.needsMorePasses);
60
61 return m_state.annotations;
62}
63
64#define INSTR_PROLOGUE_NOT_IMPLEMENTED() \
65 setError(u"Instruction \"%1\" not implemented"_s.arg(QString::fromUtf8(__func__))); \
66 return;
67
68#define INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE() \
69 m_logger->log(u"Instruction \"%1\" not implemented"_s.arg(QString::fromUtf8(__func__)), \
70 qmlCompiler, QQmlJS::SourceLocation()); \
71 return;
72
74{
75 if (m_passManager != nullptr && m_function->isProperty) {
76 m_passManager->d_func()->analyzeBinding(
79 m_typeResolver->containedType(m_state.accumulatorIn())),
81 getCurrentBindingSourceLocation()));
82 }
83
85 // Signal handlers cannot return anything.
86 } else if (!m_returnType.isValid() && m_state.accumulatorIn().isValid()
88 m_state.accumulatorIn(), m_typeResolver->voidType())) {
89 setError(u"function without return type annotation returns %1"_s
90 .arg(m_state.accumulatorIn().descriptiveName()));
91
93 // Do not complain if the function didn't have a valid annotation in the first place.
94 m_logger->log(u"Function without return type annotation returns %1"_s.arg(
95 m_typeResolver->containedTypeName(m_state.accumulatorIn(), true)),
96 qmlIncompatibleType, getCurrentBindingSourceLocation());
97 }
98 return;
99 } else if (!canConvertFromTo(m_state.accumulatorIn(), m_returnType)) {
100 setError(u"cannot convert from %1 to %2"_s
101 .arg(m_state.accumulatorIn().descriptiveName(),
102 m_returnType.descriptiveName()));
103
104 m_logger->log(u"Cannot assign binding of type %1 to %2"_s.arg(
105 m_typeResolver->containedTypeName(m_state.accumulatorIn(), true),
106 m_typeResolver->containedTypeName(m_returnType, true)),
107 qmlIncompatibleType, getCurrentBindingSourceLocation());
108 return;
109 }
110
111 if (m_returnType.isValid()) {
112 // We need to preserve any possible undefined value as that resets the property.
113 if (m_typeResolver->canHoldUndefined(m_state.accumulatorIn()))
114 addReadAccumulator(m_state.accumulatorIn());
115 else
116 addReadAccumulator(m_returnType);
117 }
118
119 m_state.setHasSideEffects(true);
120 m_state.skipInstructionsUntilNextJumpTarget = true;
121}
122
124{
126}
127
129{
130 auto encodedConst = m_jsUnitGenerator->constant(index);
131 setAccumulator(m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst)));
132}
133
135{
137}
138
140{
142}
143
145{
147}
148
150{
152}
153
155{
157}
158
160{
162}
163
164void QQmlJSTypePropagator::generate_MoveConst(int constIndex, int destTemp)
165{
166 auto encodedConst = m_jsUnitGenerator->constant(constIndex);
167 setRegister(destTemp, m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst)));
168}
169
171{
172 // Do not re-track the register. We're not manipulating it.
173 m_state.setIsRename(true);
174 const QQmlJSRegisterContent content = checkedInputRegister(reg);
175 m_state.addReadRegister(reg, content);
176 m_state.setRegister(Accumulator, content);
177}
178
180{
181 // Do not re-track the register. We're not manipulating it.
182 m_state.setIsRename(true);
183 m_state.addReadAccumulator(m_state.accumulatorIn());
184 m_state.setRegister(reg, m_state.accumulatorIn());
185}
186
187void QQmlJSTypePropagator::generate_MoveReg(int srcReg, int destReg)
188{
189 Q_ASSERT(destReg != InvalidRegister);
190 // Do not re-track the register. We're not manipulating it.
191 m_state.setIsRename(true);
192 const QQmlJSRegisterContent content = checkedInputRegister(srcReg);
193 m_state.addReadRegister(srcReg, content);
194 m_state.setRegister(destReg, content);
195}
196
198{
201}
202
204{
207}
208
210{
213}
214
216{
217 Q_UNUSED(scope)
220}
221
223{
224 Q_UNUSED(scope)
227}
228
230{
231 Q_UNUSED(stringId)
233 // m_state.accumulatorOut.m_state.value = m_jsUnitGenerator->stringForIndex(stringId);
234}
235
236void QQmlJSTypePropagator::generate_MoveRegExp(int regExpId, int destReg)
237{
238 Q_UNUSED(regExpId)
239 Q_UNUSED(destReg)
241}
242
244{
246 // TODO: Check the function at index and see whether it's a generator to return another type
247 // instead.
249}
250
252{
253 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
255 if (!m_state.accumulatorOut().isValid())
256 setError(u"Cannot find name "_s + name);
257}
258
260{
262}
263
264QQmlJS::SourceLocation QQmlJSTypePropagator::getCurrentSourceLocation() const
265{
267 const auto &entries = m_function->sourceLocations->entries;
268
269 auto item = std::lower_bound(entries.begin(), entries.end(), currentInstructionOffset(),
270 [](auto entry, uint offset) { return entry.offset < offset; });
271 Q_ASSERT(item != entries.end());
272 auto location = item->location;
273
274 return location;
275}
276
277QQmlJS::SourceLocation QQmlJSTypePropagator::getCurrentBindingSourceLocation() const
278{
280 const auto &entries = m_function->sourceLocations->entries;
281
282 Q_ASSERT(!entries.isEmpty());
283 return combine(entries.constFirst().location, entries.constLast().location);
284}
285
286void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name, bool isMethod) const
287{
288 auto location = getCurrentSourceLocation();
289
291 // Only ignore custom parser based elements if it's not Connections.
293 || m_function->qmlScope->baseType()->internalName() != u"QQmlConnections"_s)
294 return;
295 }
296
297 if (isMethod) {
298 if (isCallingProperty(m_function->qmlScope, name))
299 return;
300 } else if (propertyResolution(m_function->qmlScope, name) != PropertyMissing) {
301 return;
302 }
303
304 std::optional<QQmlJSFixSuggestion> suggestion;
305
306 auto childScopes = m_function->qmlScope->childScopes();
307 for (qsizetype i = 0; i < m_function->qmlScope->childScopes().size(); i++) {
308 auto &scope = childScopes[i];
309 if (location.offset > scope->sourceLocation().offset) {
310 if (i + 1 < childScopes.size()
311 && childScopes.at(i + 1)->sourceLocation().offset < location.offset)
312 continue;
313 if (scope->childScopes().size() == 0)
314 continue;
315
316 const auto jsId = scope->childScopes().first()->findJSIdentifier(name);
317
318 if (jsId.has_value() && jsId->kind == QQmlJSScope::JavaScriptIdentifier::Injected) {
319 const QQmlJSScope::JavaScriptIdentifier id = jsId.value();
320
321 QQmlJS::SourceLocation fixLocation = id.location;
322 Q_UNUSED(fixLocation)
323 fixLocation.length = 0;
324
325 const auto handler = m_typeResolver->signalHandlers()[id.location];
326
327 QString fixString = handler.isMultiline ? u"function("_s : u"("_s;
328 const auto parameters = handler.signalParameters;
329 for (int numParams = parameters.size(); numParams > 0; --numParams) {
330 fixString += parameters.at(parameters.size() - numParams);
331 if (numParams > 1)
332 fixString += u", "_s;
333 }
334
335 fixString += handler.isMultiline ? u") "_s : u") => "_s;
336
337 suggestion = QQmlJSFixSuggestion {
338 name + u" is accessible in this scope because you are handling a signal"
339 " at %1:%2. Use a function instead.\n"_s
340 .arg(id.location.startLine)
341 .arg(id.location.startColumn),
342 fixLocation,
343 fixString
344 };
345 suggestion->setAutoApplicable();
346 }
347 break;
348 }
349 }
350
351 // Might be a delegate just missing a required property.
352 // This heuristic does not recognize all instances of this occurring but should be sufficient
353 // protection against wrongly suggesting to add an id to the view to access the model that way
354 // which is very misleading
355 if (name == u"model" || name == u"index") {
357 const auto bindings = parent->ownPropertyBindings(u"delegate"_s);
358
359 for (auto it = bindings.first; it != bindings.second; it++) {
360 if (!it->hasObject())
361 continue;
362 if (it->objectType() == m_function->qmlScope) {
363 suggestion = QQmlJSFixSuggestion {
364 name + " is implicitly injected into this delegate."
365 " Add a required property instead."_L1,
367 };
368 };
369
370 break;
371 }
372 }
373 }
374
375 if (!suggestion.has_value()) {
376 for (QQmlJSScope::ConstPtr scope = m_function->qmlScope; !scope.isNull();
377 scope = scope->parentScope()) {
378 if (scope->hasProperty(name)) {
380
381 QQmlJS::SourceLocation fixLocation = location;
382 fixLocation.length = 0;
383 suggestion = QQmlJSFixSuggestion {
384 name + " is a member of a parent element.\n You can qualify the access "
385 "with its id to avoid this warning:\n"_L1,
386 fixLocation,
387 (id.isEmpty() ? u"<id>."_s : (id + u'.'))
388 };
389
390 if (id.isEmpty())
391 suggestion->setHint("You first have to give the element an id"_L1);
392 else
393 suggestion->setAutoApplicable();
394 }
395 }
396 }
397
398 if (!suggestion.has_value() && !m_function->addressableScopes.componentsAreBound()
400 const QLatin1String replacement = "pragma ComponentBehavior: Bound"_L1;
401 QQmlJSFixSuggestion bindComponents {
402 "Set \"%1\" in order to use IDs from outer components in nested components."_L1
403 .arg(replacement),
404 QQmlJS::SourceLocation(0, 0, 1, 1),
405 replacement + '\n'_L1
406 };
407 bindComponents.setAutoApplicable();
408 suggestion = bindComponents;
409 }
410
411 if (!suggestion.has_value()) {
412 if (auto didYouMean =
415 + m_function->qmlScope->methods().keys(),
416 location);
417 didYouMean.has_value()) {
418 suggestion = didYouMean;
419 }
420 }
421
422 m_logger->log(QLatin1String("Unqualified access"), qmlUnqualified, location, true, true,
423 suggestion);
424}
425
426void QQmlJSTypePropagator::checkDeprecated(QQmlJSScope::ConstPtr scope, const QString &name,
427 bool isMethod) const
428{
429 Q_ASSERT(!scope.isNull());
430 auto qmlScope = QQmlJSScope::findCurrentQMLScope(scope);
431 if (qmlScope.isNull())
432 return;
433
434 QList<QQmlJSAnnotation> annotations;
435
437
438 if (isMethod) {
439 const QVector<QQmlJSMetaMethod> methods = qmlScope->methods(name);
440 if (methods.isEmpty())
441 return;
442 method = methods.constFirst();
443 annotations = method.annotations();
444 } else {
445 QQmlJSMetaProperty property = qmlScope->property(name);
446 if (!property.isValid())
447 return;
448 annotations = property.annotations();
449 }
450
451 auto deprecationAnn = std::find_if(
452 annotations.constBegin(), annotations.constEnd(),
453 [](const QQmlJSAnnotation &annotation) { return annotation.isDeprecation(); });
454
455 if (deprecationAnn == annotations.constEnd())
456 return;
457
458 QQQmlJSDeprecation deprecation = deprecationAnn->deprecation();
459
460 QString descriptor = name;
461 if (isMethod)
462 descriptor += u'(' + method.parameterNames().join(u", "_s) + u')';
463
464 QString message = QStringLiteral("%1 \"%2\" is deprecated")
465 .arg(isMethod ? u"Method"_s : u"Property"_s)
466 .arg(descriptor);
467
468 if (!deprecation.reason.isEmpty())
469 message.append(QStringLiteral(" (Reason: %1)").arg(deprecation.reason));
470
471 m_logger->log(message, qmlDeprecated, getCurrentSourceLocation());
472}
473
474// Only to be called once a lookup has already failed
475QQmlJSTypePropagator::PropertyResolution QQmlJSTypePropagator::propertyResolution(
476 QQmlJSScope::ConstPtr scope, const QString &propertyName) const
477{
478 auto property = scope->property(propertyName);
479 if (!property.isValid())
480 return PropertyMissing;
481
482 QString errorType;
483 if (property.type().isNull())
484 errorType = u"found"_s;
485 else if (!property.type()->isFullyResolved())
486 errorType = u"fully resolved"_s;
487 else
488 return PropertyFullyResolved;
489
490 Q_ASSERT(!errorType.isEmpty());
491
492 m_logger->log(
493 u"Type \"%1\" of property \"%2\" not %3. This is likely due to a missing dependency entry or a type not being exposed declaratively."_s
494 .arg(property.typeName(), propertyName, errorType),
495 qmlUnresolvedType, getCurrentSourceLocation());
496
497 return PropertyTypeUnresolved;
498}
499
500bool QQmlJSTypePropagator::isCallingProperty(QQmlJSScope::ConstPtr scope, const QString &name) const
501{
502 auto property = scope->property(name);
503 if (!property.isValid())
504 return false;
505
506 QString propertyType = u"Property"_s;
507
508 auto methods = scope->methods(name);
509
510 QString errorType;
511 if (!methods.isEmpty()) {
512 errorType = u"shadowed by a property."_s;
513 switch (methods.first().methodType()) {
514 case QQmlJSMetaMethodType::Signal:
515 propertyType = u"Signal"_s;
516 break;
517 case QQmlJSMetaMethodType::Slot:
518 propertyType = u"Slot"_s;
519 break;
520 case QQmlJSMetaMethodType::Method:
521 propertyType = u"Method"_s;
522 break;
523 default:
524 Q_UNREACHABLE();
525 }
526 } else if (m_typeResolver->equals(property.type(), m_typeResolver->varType())) {
527 errorType =
528 u"a variant property. It may or may not be a method. Use a regular function instead."_s;
529 } else if (m_typeResolver->equals(property.type(), m_typeResolver->jsValueType())) {
530 errorType =
531 u"a QJSValue property. It may or may not be a method. Use a regular Q_INVOKABLE instead."_s;
532 } else {
533 errorType = u"not a method"_s;
534 }
535
536 m_logger->log(u"%1 \"%2\" is %3"_s.arg(propertyType, name, errorType), qmlUseProperFunction,
537 getCurrentSourceLocation(), true, true, {});
538
539 return true;
540}
541
543{
544 // LoadQmlContextPropertyLookup does not use accumulatorIn. It always refers to the scope.
545 // Any import namespaces etc. are handled via LoadProperty or GetLookup.
546
547 const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
548 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
549
551
552 if (!m_state.accumulatorOut().isValid() && m_typeResolver->isPrefix(name)) {
554 setAccumulator(QQmlJSRegisterContent::create(
556 m_typeResolver->containedType(inType)));
557 return;
558 }
559
560 checkDeprecated(m_function->qmlScope, name, false);
561
562 if (!m_state.accumulatorOut().isValid()) {
563 setError(u"Cannot access value for name "_s + name);
564 handleUnqualifiedAccess(name, false);
565 return;
566 }
567
568 const QQmlJSScope::ConstPtr outStored
569 = m_typeResolver->genericType(m_state.accumulatorOut().storedType());
570
571 if (outStored.isNull()) {
572 // It should really be valid.
573 // We get the generic type from aotContext->loadQmlContextPropertyIdLookup().
574 setError(u"Cannot determine generic type for "_s + name);
575 return;
576 }
577
578 if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectById
579 && !outStored->isReferenceType()) {
580 setError(u"Cannot retrieve a non-object type by ID: "_s + name);
581 return;
582 }
583
584 if (m_passManager != nullptr) {
585 m_passManager->d_func()->analyzeRead(
589 getCurrentBindingSourceLocation()));
590 }
591
592 if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ScopeAttached)
593 m_attachedContext = QQmlJSScope::ConstPtr();
594}
595
608{
609 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
611 const QQmlJSRegisterContent in = m_state.accumulatorIn();
612
613 if (!type.isValid()) {
614 handleUnqualifiedAccess(name, false);
615 setError(u"Cannot find name "_s + name);
616 return;
617 }
618
619 if (!type.isProperty()) {
620 QString message = type.isMethod() ? u"Cannot assign to method %1"_s
621 : u"Cannot assign to non-property %1"_s;
622 // The interpreter treats methods as read-only properties in its error messages
623 // and we lack a better fitting category. We might want to revisit this later.
625 getCurrentSourceLocation());
626 setError(u"Cannot assign to non-property "_s + name);
627 return;
628 }
629
630 if (!type.isWritable() && !m_function->qmlScope->hasOwnProperty(name)) {
631 setError(u"Can't assign to read-only property %1"_s.arg(name));
632
633 m_logger->log(u"Cannot assign to read-only property %1"_s.arg(name), qmlReadOnlyProperty,
634 getCurrentSourceLocation());
635
636 return;
637 }
638
639 if (!canConvertFromTo(in, type)) {
640 setError(u"cannot convert from %1 to %2"_s
641 .arg(in.descriptiveName(), type.descriptiveName()));
642 }
643
644 if (m_passManager != nullptr) {
645 m_passManager->d_func()->analyzeWrite(
650 getCurrentBindingSourceLocation()));
651 }
652
653 m_state.setHasSideEffects(true);
654
656 if (type.property().reset().isEmpty())
657 setError(u"Cannot assign potential undefined to %1"_s.arg(type.descriptiveName()));
659 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->varType()));
660 else
661 addReadAccumulator(in);
662 } else {
663 addReadAccumulator(type);
664 }
665}
666
668{
669 return generate_StoreNameCommon(nameIndex);
670}
671
673{
675}
676
678 const QQmlJSRegisterContent &base, const QString &propertyName) const
679{
680 if (base.isEnumeration()) {
681 const auto metaEn = base.enumeration();
682 if (!metaEn.isScoped()) {
683 m_logger->log(u"You cannot access unscoped enum \"%1\" from here."_s.arg(propertyName),
684 qmlRestrictedType, getCurrentSourceLocation());
685 return true;
686 } else if (!metaEn.hasKey(propertyName)) {
687 auto fixSuggestion = QQmlJSUtils::didYouMean(propertyName, metaEn.keys(),
688 getCurrentSourceLocation());
689 m_logger->log(u"\"%1\" is not an entry of enum \"%2\"."_s.arg(propertyName)
690 .arg(metaEn.name()),
691 qmlMissingEnumEntry, getCurrentSourceLocation(), true, true,
692 fixSuggestion);
693 return true;
694 }
695 }
696 return false;
697}
698
700{
701 const QQmlJSRegisterContent baseRegister = m_state.registers[base].content;
702
703 if ((!baseRegister.isList()
705 || !m_typeResolver->isNumeric(m_state.accumulatorIn())) {
706 const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType());
707 addReadAccumulator(jsValue);
708 addReadRegister(base, jsValue);
709 setAccumulator(jsValue);
710 return;
711 }
712
713 const auto contained = m_typeResolver->containedType(m_state.accumulatorIn());
714 if (m_typeResolver->isSignedInteger(contained))
715 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->int32Type()));
716 else if (m_typeResolver->isUnsignedInteger(contained))
717 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->uint32Type()));
718 else
719 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->realType()));
720
721 addReadRegister(base, baseRegister);
722 // We can end up with undefined.
723 setAccumulator(m_typeResolver->merge(
724 m_typeResolver->valueType(baseRegister),
726}
727
729{
730 const QQmlJSRegisterContent baseRegister = m_state.registers[base].content;
731 const QQmlJSRegisterContent indexRegister = checkedInputRegister(index);
732
733 if (!baseRegister.isList()
734 || !m_typeResolver->isNumeric(indexRegister)) {
735 const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType());
736 addReadAccumulator(jsValue);
737 addReadRegister(base, jsValue);
738 addReadRegister(index, jsValue);
739
740 // Writing to a JS array can have side effects all over the place since it's
741 // passed by reference.
742 m_state.setHasSideEffects(true);
743 return;
744 }
745
746 const auto contained = m_typeResolver->containedType(indexRegister);
747 if (m_typeResolver->isSignedInteger(contained))
749 else if (m_typeResolver->isUnsignedInteger(contained))
751 else
753
754 addReadRegister(base, baseRegister);
755 addReadAccumulator(m_typeResolver->valueType(baseRegister));
756
757 // If we're writing a QQmlListProperty backed by a container somewhere else,
758 // that has side effects.
759 // If we're writing to a list retrieved from a property, that _should_ have side effects,
760 // but currently the QML engine doesn't implement them.
761 // TODO: Figure out the above and accurately set the flag.
762 m_state.setHasSideEffects(true);
763}
764
765void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
766{
767 setAccumulator(
769 m_state.accumulatorIn(),
770 m_state.accumulatorIn().isImportNamespace()
771 ? m_jsUnitGenerator->stringForIndex(m_state.accumulatorIn().importNamespace())
772 + u'.' + propertyName
773 : propertyName));
774
775 if (!m_state.accumulatorOut().isValid()) {
776 if (m_typeResolver->isPrefix(propertyName)) {
777 Q_ASSERT(m_state.accumulatorIn().isValid());
778 addReadAccumulator(m_state.accumulatorIn());
779 setAccumulator(QQmlJSRegisterContent::create(
780 m_state.accumulatorIn().storedType(),
781 m_jsUnitGenerator->getStringId(propertyName),
783 m_typeResolver->containedType(m_state.accumulatorIn())));
784 return;
785 }
786 if (m_state.accumulatorIn().isImportNamespace())
787 m_logger->log(u"Type not found in namespace"_s, qmlUnresolvedType,
788 getCurrentSourceLocation());
789 } else if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::Singleton
790 && m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ObjectModulePrefix) {
791 m_logger->log(
792 u"Cannot access singleton as a property of an object. Did you want to access an attached object?"_s,
793 qmlAccessSingleton, getCurrentSourceLocation());
794 setAccumulator(QQmlJSRegisterContent());
795 }
796
797 if (checkForEnumProblems(m_state.accumulatorIn(), propertyName))
798 return;
799
800 if (!m_state.accumulatorOut().isValid()) {
801 setError(u"Cannot load property %1 from %2."_s
802 .arg(propertyName, m_state.accumulatorIn().descriptiveName()));
803
804 const QString typeName = m_typeResolver->containedTypeName(m_state.accumulatorIn(), true);
805
806 if (typeName == u"QVariant")
807 return;
808 if (m_state.accumulatorIn().isList() && propertyName == u"length")
809 return;
810
811 auto baseType = m_typeResolver->containedType(m_state.accumulatorIn());
812 // Warn separately when a property is only not found because of a missing type
813
814 if (propertyResolution(baseType, propertyName) != PropertyMissing)
815 return;
816
817 std::optional<QQmlJSFixSuggestion> fixSuggestion;
818
819 if (auto suggestion = QQmlJSUtils::didYouMean(propertyName, baseType->properties().keys(),
820 getCurrentSourceLocation());
821 suggestion.has_value()) {
822 fixSuggestion = suggestion;
823 }
824
825 if (!fixSuggestion.has_value()
826 && m_state.accumulatorIn().variant() == QQmlJSRegisterContent::MetaType) {
827 QStringList enumKeys;
828 for (const QQmlJSMetaEnum &metaEnum :
829 m_state.accumulatorIn().scopeType()->enumerations())
830 enumKeys << metaEnum.keys();
831
832 if (auto suggestion =
833 QQmlJSUtils::didYouMean(propertyName, enumKeys, getCurrentSourceLocation());
834 suggestion.has_value()) {
835 fixSuggestion = suggestion;
836 }
837 }
838
839 m_logger->log(u"Member \"%1\" not found on type \"%2\""_s.arg(propertyName).arg(typeName),
840 qmlMissingProperty, getCurrentSourceLocation(), true, true, fixSuggestion);
841 return;
842 }
843
844 if (m_state.accumulatorOut().isMethod() && m_state.accumulatorOut().method().size() != 1) {
845 setError(u"Cannot determine overloaded method on loadProperty"_s);
846 return;
847 }
848
849 if (m_state.accumulatorOut().isProperty()) {
851 m_state.accumulatorOut(), m_typeResolver->voidType())) {
852 setError(u"Type %1 does not have a property %2 for reading"_s
853 .arg(m_state.accumulatorIn().descriptiveName(), propertyName));
854 return;
855 }
856
857 if (!m_state.accumulatorOut().property().type()) {
858 m_logger->log(
859 QString::fromLatin1("Type of property \"%2\" not found").arg(propertyName),
860 qmlMissingType, getCurrentSourceLocation());
861 }
862 }
863
864 if (m_passManager != nullptr) {
865 const bool isAttached =
866 m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ObjectAttached;
867
868 m_passManager->d_func()->analyzeRead(
870 m_typeResolver->containedType(m_state.accumulatorIn())),
871 propertyName,
872 QQmlJSScope::createQQmlSAElement(isAttached ? m_attachedContext
873 : m_function->qmlScope),
874 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
875 getCurrentBindingSourceLocation()));
876 }
877
878 if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectAttached)
879 m_attachedContext = m_typeResolver->containedType(m_state.accumulatorIn());
880
881 switch (m_state.accumulatorOut().variant()) {
885 // For reading enums or singletons, we don't need to access anything, unless it's an
886 // import namespace. Then we need the name.
887 if (m_state.accumulatorIn().isImportNamespace())
888 addReadAccumulator(m_state.accumulatorIn());
889 break;
890 default:
891 addReadAccumulator(m_state.accumulatorIn());
892 break;
893 }
894}
895
897{
898 propagatePropertyLookup(m_jsUnitGenerator->stringForIndex(nameIndex));
899}
900
902{
903 Q_UNUSED(name);
906}
907
909{
910 propagatePropertyLookup(m_jsUnitGenerator->lookupName(index));
911}
912
914{
918}
919
921{
922 auto callBase = m_state.registers[base].content;
923 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
924
925 QQmlJSRegisterContent property = m_typeResolver->memberType(callBase, propertyName);
926 if (!property.isProperty()) {
927 setError(u"Type %1 does not have a property %2 for writing"_s
928 .arg(callBase.descriptiveName(), propertyName));
929 return;
930 }
931
932 if (!property.isWritable()) {
933 setError(u"Can't assign to read-only property %1"_s.arg(propertyName));
934
935 m_logger->log(u"Cannot assign to read-only property %1"_s.arg(propertyName),
936 qmlReadOnlyProperty, getCurrentSourceLocation());
937
938 return;
939 }
940
941 if (!canConvertFromTo(m_state.accumulatorIn(), property)) {
942 setError(u"cannot convert from %1 to %2"_s
943 .arg(m_state.accumulatorIn().descriptiveName(), property.descriptiveName()));
944 return;
945 }
946
947 if (m_passManager != nullptr) {
948 const bool isAttached = callBase.variant() == QQmlJSRegisterContent::ObjectAttached;
949
950 m_passManager->d_func()->analyzeWrite(
952 propertyName,
954 m_typeResolver->containedType(m_state.accumulatorIn())),
955 QQmlJSScope::createQQmlSAElement(isAttached ? m_attachedContext
958 getCurrentBindingSourceLocation()));
959 }
960
961 m_state.setHasSideEffects(true);
962 addReadAccumulator(property);
963 addReadRegister(base, callBase);
964}
965
967{
969}
970
972{
975}
976
978{
981}
982
984{
986}
987
989{
991}
992
994{
996}
997
999{
1000 m_state.setHasSideEffects(true);
1001 Q_UNUSED(name)
1002 Q_UNUSED(argc)
1003 Q_UNUSED(argv)
1005}
1006
1007void QQmlJSTypePropagator::generate_CallWithReceiver(int name, int thisObject, int argc, int argv)
1008{
1009 m_state.setHasSideEffects(true);
1010 Q_UNUSED(name)
1011 Q_UNUSED(thisObject)
1012 Q_UNUSED(argc)
1013 Q_UNUSED(argv)
1015}
1016
1017static bool isLoggingMethod(const QString &consoleMethod)
1018{
1019 return consoleMethod == u"log" || consoleMethod == u"debug" || consoleMethod == u"info"
1020 || consoleMethod == u"warn" || consoleMethod == u"error";
1021}
1022
1023void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int argc, int argv)
1024{
1025 Q_ASSERT(m_state.registers.contains(base));
1026 const auto callBase = m_state.registers[base].content;
1027 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
1028
1030 callBase, m_typeResolver->jsGlobalObject()->property(u"Math"_s).type())) {
1031
1032 // If we call a method on the Math object we don't need the actual Math object. We do need
1033 // to transfer the type information to the code generator so that it knows that this is the
1034 // Math object. Read the base register as void. void isn't stored, and the place where it's
1035 // created will be optimized out if there are no other readers. The code generator can
1036 // retrieve the original type and determine that it was the Math object.
1037 addReadRegister(base, m_typeResolver->globalType(m_typeResolver->voidType()));
1038
1040 for (int i = 0; i < argc; ++i)
1041 addReadRegister(argv + i, realType);
1042 setAccumulator(realType);
1043 return;
1044 }
1045
1047 callBase, m_typeResolver->jsGlobalObject()->property(u"console"_s).type())
1048 && isLoggingMethod(propertyName)) {
1049
1050 const QQmlJSRegisterContent voidType
1052
1053 // If we call a method on the console object we don't need the console object.
1054 addReadRegister(base, voidType);
1055
1056 const QQmlJSRegisterContent stringType
1058
1059 if (argc > 0) {
1060 const QQmlJSScope::ConstPtr firstArg
1061 = m_typeResolver->containedType(m_state.registers[argv].content);
1062 if (firstArg->isReferenceType()) {
1063 // We cannot know whether this will be a logging category at run time.
1064 // Therefore we always pass any object types as special last argument.
1065 addReadRegister(argv, m_typeResolver->globalType(
1066 m_typeResolver->genericType(firstArg)));
1067 } else {
1068 addReadRegister(argv, stringType);
1069 }
1070 }
1071
1072 for (int i = 1; i < argc; ++i)
1073 addReadRegister(argv + i, stringType);
1074
1075 m_state.setHasSideEffects(true);
1076 setAccumulator(voidType);
1077 return;
1078 }
1079
1082 const auto jsValueType = m_typeResolver->globalType(m_typeResolver->jsValueType());
1083 addReadRegister(base, jsValueType);
1084 for (int i = 0; i < argc; ++i)
1085 addReadRegister(argv + i, jsValueType);
1086 setAccumulator(jsValueType);
1087 m_state.setHasSideEffects(true);
1088 return;
1089 }
1090
1091 const auto baseType = m_typeResolver->containedType(callBase);
1092 const auto member = m_typeResolver->memberType(callBase, propertyName);
1093 if (!member.isMethod()) {
1094 setError(u"Type %1 does not have a property %2 for calling"_s
1095 .arg(callBase.descriptiveName(), propertyName));
1096
1097 if (callBase.isType() && isCallingProperty(callBase.type(), propertyName))
1098 return;
1099
1100 if (checkForEnumProblems(callBase, propertyName))
1101 return;
1102
1103 std::optional<QQmlJSFixSuggestion> fixSuggestion;
1104
1105 if (auto suggestion = QQmlJSUtils::didYouMean(propertyName, baseType->methods().keys(),
1106 getCurrentSourceLocation());
1107 suggestion.has_value()) {
1108 fixSuggestion = suggestion;
1109 }
1110
1111 m_logger->log(u"Member \"%1\" not found on type \"%2\""_s.arg(
1112 propertyName, m_typeResolver->containedTypeName(callBase, true)),
1113 qmlMissingProperty, getCurrentSourceLocation(), true, true, fixSuggestion);
1114 return;
1115 }
1116
1117 checkDeprecated(baseType, propertyName, true);
1118
1119 if (m_passManager != nullptr) {
1120 // TODO: Should there be an analyzeCall() in the future? (w. corresponding onCall in Pass)
1121 m_passManager->d_func()->analyzeRead(
1122 QQmlJSScope::createQQmlSAElement(baseType), propertyName,
1125 getCurrentBindingSourceLocation()));
1126 }
1127
1128 addReadRegister(base, callBase);
1129
1131 if (propertyName == u"arg"_s && argc == 1) {
1132 propagateStringArgCall(argv);
1133 return;
1134 }
1135 }
1136
1137 if (baseType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1138 && m_typeResolver->equals(member.scopeType(), m_typeResolver->arrayPrototype())
1139 && propagateArrayMethod(propertyName, argc, argv, callBase)) {
1140 return;
1141 }
1142
1143 propagateCall(member.method(), argc, argv, member.scopeType());
1144}
1145
1146QQmlJSMetaMethod QQmlJSTypePropagator::bestMatchForCall(const QList<QQmlJSMetaMethod> &methods,
1147 int argc, int argv, QStringList *errors)
1148{
1149 QQmlJSMetaMethod javascriptFunction;
1150 QQmlJSMetaMethod candidate;
1151 for (const auto &method : methods) {
1152
1153 // If we encounter a JavaScript function, use this as a fallback if no other method matches
1154 if (method.isJavaScriptFunction() && !javascriptFunction.isValid())
1155 javascriptFunction = method;
1156
1157 if (method.returnType().isNull() && !method.returnTypeName().isEmpty()) {
1158 errors->append(u"return type %1 cannot be resolved"_s
1159 .arg(method.returnTypeName()));
1160 continue;
1161 }
1162
1163 const auto arguments = method.parameters();
1164 if (argc != arguments.size()) {
1165 errors->append(
1166 u"Function expects %1 arguments, but %2 were provided"_s.arg(arguments.size())
1167 .arg(argc));
1168 continue;
1169 }
1170
1171 bool fuzzyMatch = true;
1172 bool exactMatch = true;
1173 for (int i = 0; i < argc; ++i) {
1174 const auto argumentType = arguments[i].type();
1175 if (argumentType.isNull()) {
1176 errors->append(
1177 u"type %1 for argument %2 cannot be resolved"_s.arg(arguments[i].typeName())
1178 .arg(i));
1179 exactMatch = false;
1180 fuzzyMatch = false;
1181 break;
1182 }
1183
1184 const auto content = m_state.registers[argv + i].content;
1186 continue;
1187
1188 exactMatch = false;
1189 if (canConvertFromTo(content, m_typeResolver->globalType(argumentType)))
1190 continue;
1191
1192 errors->append(
1193 u"argument %1 contains %2 but is expected to contain the type %3"_s.arg(i).arg(
1194 m_state.registers[argv + i].content.descriptiveName(),
1195 arguments[i].typeName()));
1196 fuzzyMatch = false;
1197 break;
1198 }
1199
1200 if (exactMatch)
1201 return method;
1202 else if (fuzzyMatch && !candidate.isValid())
1203 candidate = method;
1204 }
1205
1206 return candidate.isValid() ? candidate : javascriptFunction;
1207}
1208
1209void QQmlJSTypePropagator::setAccumulator(const QQmlJSRegisterContent &content)
1210{
1211 setRegister(Accumulator, content);
1212}
1213
1214void QQmlJSTypePropagator::setRegister(int index, const QQmlJSRegisterContent &content)
1215{
1216 // If we've come to the same conclusion before, let's not track the type again.
1217 auto it = m_prevStateAnnotations.find(currentInstructionOffset());
1218 if (it != m_prevStateAnnotations.end()) {
1219 const QQmlJSRegisterContent &lastTry = it->second.changedRegister;
1221 m_state.setRegister(index, lastTry);
1222 return;
1223 }
1224 }
1225
1226 m_state.setRegister(index, m_typeResolver->tracked(content));
1227}
1228
1229void QQmlJSTypePropagator::mergeRegister(
1231{
1232 auto merged = m_typeResolver->merge(a, b);
1233
1234 Q_ASSERT(merged.isValid());
1235 Q_ASSERT(merged.isConversion());
1236
1237 auto tryPrevStateConversion = [this](int index, const QQmlJSRegisterContent &merged) -> bool {
1238 auto it = m_prevStateAnnotations.find(currentInstructionOffset());
1239 if (it == m_prevStateAnnotations.end())
1240 return false;
1241
1242 auto conversion = it->second.typeConversions.find(index);
1243 if (conversion == it->second.typeConversions.end())
1244 return false;
1245
1246 const VirtualRegister &lastTry = conversion.value();
1247
1248 Q_ASSERT(lastTry.content.isValid());
1249 Q_ASSERT(lastTry.content.isConversion());
1250
1251 if (!m_typeResolver->equals(lastTry.content.conversionResult(), merged.conversionResult())
1252 || lastTry.content.conversionOrigins() != merged.conversionOrigins()) {
1253 return false;
1254 }
1255
1256 // We don't need to track it again if we've come to the same conclusion before.
1257 m_state.annotations[currentInstructionOffset()].typeConversions[index] = lastTry;
1258 m_state.registers[index] = lastTry;
1259 return true;
1260 };
1261
1262 if (!tryPrevStateConversion(index, merged)) {
1263 merged = m_typeResolver->tracked(merged);
1264 Q_ASSERT(merged.isValid());
1265 m_state.annotations[currentInstructionOffset()].typeConversions[index].content = merged;
1266 m_state.registers[index].content = merged;
1267 }
1268}
1269
1270void QQmlJSTypePropagator::addReadRegister(int index, const QQmlJSRegisterContent &convertTo)
1271{
1272 m_state.addReadRegister(index, m_typeResolver->convert
1273 (m_state.registers[index].content, convertTo));
1274}
1275
1276void QQmlJSTypePropagator::propagateCall(
1277 const QList<QQmlJSMetaMethod> &methods, int argc, int argv,
1278 const QQmlJSScope::ConstPtr &scope)
1279{
1280 QStringList errors;
1281 const QQmlJSMetaMethod match = bestMatchForCall(methods, argc, argv, &errors);
1282
1283 if (!match.isValid()) {
1284 Q_ASSERT(errors.size() == methods.size());
1285 if (methods.size() == 1)
1286 setError(errors.first());
1287 else
1288 setError(u"No matching override found. Candidates:\n"_s + errors.join(u'\n'));
1289 return;
1290 }
1291
1292 const auto returnType = match.isJavaScriptFunction()
1294 : QQmlJSScope::ConstPtr(match.returnType());
1295 setAccumulator(m_typeResolver->returnType(
1296 returnType ? QQmlJSScope::ConstPtr(returnType) : m_typeResolver->voidType(),
1297 match.isJavaScriptFunction()
1298 ? QQmlJSRegisterContent::JavaScriptReturnValue
1299 : QQmlJSRegisterContent::MethodReturnValue,
1300 scope));
1301 if (!m_state.accumulatorOut().isValid())
1302 setError(u"Cannot store return type of method %1()."_s.arg(match.methodName()));
1303
1304 m_state.setHasSideEffects(true);
1305 const auto types = match.parameters();
1306 for (int i = 0; i < argc; ++i) {
1307 if (i < types.size()) {
1308 const QQmlJSScope::ConstPtr type = match.isJavaScriptFunction()
1310 : QQmlJSScope::ConstPtr(types.at(i).type());
1311 if (!type.isNull()) {
1312 addReadRegister(argv + i, m_typeResolver->globalType(type));
1313 continue;
1314 }
1315 }
1316 addReadRegister(argv + i, m_typeResolver->globalType(m_typeResolver->jsValueType()));
1317 }
1318}
1319
1320bool QQmlJSTypePropagator::propagateTranslationMethod(
1321 const QList<QQmlJSMetaMethod> &methods, int argc, int argv)
1322{
1323 if (methods.size() != 1)
1324 return false;
1325
1326 const QQmlJSMetaMethod method = methods.front();
1327 const QQmlJSRegisterContent intType
1329 const QQmlJSRegisterContent stringType
1331 const QQmlJSRegisterContent returnType
1335
1336 if (method.methodName() == u"qsTranslate"_s) {
1337 switch (argc) {
1338 case 4:
1339 addReadRegister(argv + 3, intType); // n
1340 Q_FALLTHROUGH();
1341 case 3:
1342 addReadRegister(argv + 2, stringType); // disambiguation
1343 Q_FALLTHROUGH();
1344 case 2:
1345 addReadRegister(argv + 1, stringType); // sourceText
1346 addReadRegister(argv, stringType); // context
1347 setAccumulator(returnType);
1348 return true;
1349 default:
1350 return false;
1351 }
1352 }
1353
1354 if (method.methodName() == u"QT_TRANSLATE_NOOP"_s) {
1355 switch (argc) {
1356 case 3:
1357 addReadRegister(argv + 2, stringType); // disambiguation
1358 Q_FALLTHROUGH();
1359 case 2:
1360 addReadRegister(argv + 1, stringType); // sourceText
1361 addReadRegister(argv, stringType); // context
1362 setAccumulator(returnType);
1363 return true;
1364 default:
1365 return false;
1366 }
1367 }
1368
1369 if (method.methodName() == u"qsTr"_s) {
1370 switch (argc) {
1371 case 3:
1372 addReadRegister(argv + 2, intType); // n
1373 Q_FALLTHROUGH();
1374 case 2:
1375 addReadRegister(argv + 1, stringType); // disambiguation
1376 Q_FALLTHROUGH();
1377 case 1:
1378 addReadRegister(argv, stringType); // sourceText
1379 setAccumulator(returnType);
1380 return true;
1381 default:
1382 return false;
1383 }
1384 }
1385
1386 if (method.methodName() == u"QT_TR_NOOP"_s) {
1387 switch (argc) {
1388 case 2:
1389 addReadRegister(argv + 1, stringType); // disambiguation
1390 Q_FALLTHROUGH();
1391 case 1:
1392 addReadRegister(argv, stringType); // sourceText
1393 setAccumulator(returnType);
1394 return true;
1395 default:
1396 return false;
1397 }
1398 }
1399
1400 if (method.methodName() == u"qsTrId"_s) {
1401 switch (argc) {
1402 case 2:
1403 addReadRegister(argv + 1, intType); // n
1404 Q_FALLTHROUGH();
1405 case 1:
1406 addReadRegister(argv, stringType); // id
1407 setAccumulator(returnType);
1408 return true;
1409 default:
1410 return false;
1411 }
1412 }
1413
1414 if (method.methodName() == u"QT_TRID_NOOP"_s) {
1415 switch (argc) {
1416 case 1:
1417 addReadRegister(argv, stringType); // id
1418 setAccumulator(returnType);
1419 return true;
1420 default:
1421 return false;
1422 }
1423 }
1424
1425 return false;
1426}
1427
1428void QQmlJSTypePropagator::propagateStringArgCall(int argv)
1429{
1430 setAccumulator(m_typeResolver->returnType(
1433 Q_ASSERT(m_state.accumulatorOut().isValid());
1434
1436 m_state.registers[argv].content);
1437
1439 addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->realType()));
1440 return;
1441 }
1442
1444 addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->int32Type()));
1445 return;
1446 }
1447
1449 addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->realType()));
1450 return;
1451 }
1452
1454 addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->boolType()));
1455 return;
1456 }
1457
1458 addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->stringType()));
1459}
1460
1461bool QQmlJSTypePropagator::propagateArrayMethod(
1462 const QString &name, int argc, int argv, const QQmlJSRegisterContent &baseType)
1463{
1464 // TODO:
1465 // * For concat() we need to decide what kind of array to return and what kinds of arguments to
1466 // accept.
1467 // * For entries(), keys(), and values() we need iterators.
1468 // * For find(), findIndex(), sort(), every(), some(), forEach(), map(), filter(), reduce(),
1469 // and reduceRight() we need typed function pointers.
1470
1471 const auto intType = m_typeResolver->globalType(m_typeResolver->int32Type());
1472 const auto boolType = m_typeResolver->globalType(m_typeResolver->boolType());
1473 const auto stringType = m_typeResolver->globalType(m_typeResolver->stringType());
1474 const auto valueType = m_typeResolver->globalType(
1475 m_typeResolver->containedType(baseType)->valueType());
1476
1477 // TODO: We should remember whether a register content can be written back when
1478 // converting and merging. Also, we need a way to detect the "only in same statement"
1479 // write back case. To do this, we should store the statementNumber(s) in
1480 // Property and Conversion RegisterContents.
1481 const bool canHaveSideEffects = (baseType.isProperty() && baseType.isWritable())
1482 || baseType.isConversion();
1483
1484 if (name == u"copyWithin" && argc > 0 && argc < 4) {
1485 for (int i = 0; i < argc; ++i) {
1486 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1487 return false;
1488 }
1489
1490 for (int i = 0; i < argc; ++i)
1491 addReadRegister(argv + i, intType);
1492
1493 setAccumulator(baseType);
1494 m_state.setHasSideEffects(canHaveSideEffects);
1495 return true;
1496 }
1497
1498 if (name == u"fill" && argc > 0 && argc < 4) {
1499 if (!canConvertFromTo(m_state.registers[argv].content, valueType))
1500 return false;
1501
1502 for (int i = 1; i < argc; ++i) {
1503 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1504 return false;
1505 }
1506
1507 addReadRegister(argv, valueType);
1508
1509 for (int i = 1; i < argc; ++i)
1510 addReadRegister(argv + i, intType);
1511
1512 setAccumulator(baseType);
1513 m_state.setHasSideEffects(canHaveSideEffects);
1514 return true;
1515 }
1516
1517 if (name == u"includes" && argc > 0 && argc < 3) {
1518 if (!canConvertFromTo(m_state.registers[argv].content, valueType))
1519 return false;
1520
1521 if (argc == 2) {
1522 if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
1523 return false;
1524 addReadRegister(argv + 1, intType);
1525 }
1526
1527 addReadRegister(argv, valueType);
1528 setAccumulator(boolType);
1529 return true;
1530 }
1531
1532 if (name == u"toString" || (name == u"join" && argc < 2)) {
1533 if (argc == 1) {
1534 if (!canConvertFromTo(m_state.registers[argv].content, stringType))
1535 return false;
1536 addReadRegister(argv, stringType);
1537 }
1538
1539 setAccumulator(stringType);
1540 return true;
1541 }
1542
1543 if ((name == u"pop" || name == u"shift") && argc == 0) {
1544 setAccumulator(valueType);
1545 m_state.setHasSideEffects(canHaveSideEffects);
1546 return true;
1547 }
1548
1549 if (name == u"push" || name == u"unshift") {
1550 for (int i = 0; i < argc; ++i) {
1551 if (!canConvertFromTo(m_state.registers[argv + i].content, valueType))
1552 return false;
1553 }
1554
1555 for (int i = 0; i < argc; ++i)
1556 addReadRegister(argv + i, valueType);
1557
1558 setAccumulator(intType);
1559 m_state.setHasSideEffects(canHaveSideEffects);
1560 return true;
1561 }
1562
1563 if (name == u"reverse" && argc == 0) {
1564 setAccumulator(baseType);
1565 m_state.setHasSideEffects(canHaveSideEffects);
1566 return true;
1567 }
1568
1569 if (name == u"slice" && argc < 3) {
1570 for (int i = 0; i < argc; ++i) {
1571 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1572 return false;
1573 }
1574
1575 for (int i = 0; i < argc; ++i)
1576 addReadRegister(argv + i, intType);
1577
1578 setAccumulator(baseType.storedType()->isListProperty()
1580 : baseType);
1581 return true;
1582 }
1583
1584 if (name == u"splice" && argc > 0) {
1585 for (int i = 0; i < 2; ++i) {
1586 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1587 return false;
1588 }
1589
1590 for (int i = 2; i < argc; ++i) {
1591 if (!canConvertFromTo(m_state.registers[argv + i].content, valueType))
1592 return false;
1593 }
1594
1595 for (int i = 0; i < 2; ++i)
1596 addReadRegister(argv + i, intType);
1597
1598 for (int i = 2; i < argc; ++i)
1599 addReadRegister(argv + i, valueType);
1600
1601 setAccumulator(baseType);
1602 m_state.setHasSideEffects(canHaveSideEffects);
1603 return true;
1604 }
1605
1606 if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
1607 if (!canConvertFromTo(m_state.registers[argv].content, valueType))
1608 return false;
1609
1610 if (argc == 2) {
1611 if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
1612 return false;
1613 addReadRegister(argv + 1, intType);
1614 }
1615
1616 addReadRegister(argv, valueType);
1617 setAccumulator(intType);
1618 return true;
1619 }
1620
1621 return false;
1622}
1623
1625 int argv)
1626{
1627 generate_CallProperty(m_jsUnitGenerator->lookupNameIndex(lookupIndex), base, argc, argv);
1628}
1629
1631{
1632 propagateScopeLookupCall(m_jsUnitGenerator->stringForIndex(name), argc, argv);
1633}
1634
1636{
1637 m_state.setHasSideEffects(true);
1638 Q_UNUSED(argc)
1639 Q_UNUSED(argv)
1641}
1642
1643void QQmlJSTypePropagator::propagateScopeLookupCall(const QString &functionName, int argc, int argv)
1644{
1645 const QQmlJSRegisterContent resolvedContent
1646 = m_typeResolver->scopedType(m_function->qmlScope, functionName);
1647 if (resolvedContent.isMethod()) {
1648 const auto methods = resolvedContent.method();
1649 if (resolvedContent.variant() == QQmlJSRegisterContent::JavaScriptGlobal) {
1650 if (propagateTranslationMethod(methods, argc, argv))
1651 return;
1652 }
1653
1654 if (!methods.isEmpty()) {
1655 propagateCall(methods, argc, argv, resolvedContent.scopeType());
1656 return;
1657 }
1658 }
1659
1660 setError(u"method %1 cannot be resolved."_s.arg(functionName));
1662
1663 setError(u"Cannot find function '%1'"_s.arg(functionName));
1664
1665 handleUnqualifiedAccess(functionName, true);
1666}
1667
1669{
1670 propagateScopeLookupCall(m_jsUnitGenerator->lookupName(index), argc, argv);
1671}
1672
1674{
1676 propagateScopeLookupCall(name, argc, argv);
1677 checkDeprecated(m_function->qmlScope, name, true);
1678}
1679
1680void QQmlJSTypePropagator::generate_CallWithSpread(int func, int thisObject, int argc, int argv)
1681{
1682 m_state.setHasSideEffects(true);
1683 Q_UNUSED(func)
1684 Q_UNUSED(thisObject)
1685 Q_UNUSED(argc)
1686 Q_UNUSED(argv)
1688}
1689
1690void QQmlJSTypePropagator::generate_TailCall(int func, int thisObject, int argc, int argv)
1691{
1692 m_state.setHasSideEffects(true);
1693 Q_UNUSED(func)
1694 Q_UNUSED(thisObject)
1695 Q_UNUSED(argc)
1696 Q_UNUSED(argv)
1698}
1699
1701{
1702 m_state.setHasSideEffects(true);
1703 Q_UNUSED(func)
1704 Q_UNUSED(argv)
1705
1706 Q_UNUSED(argc)
1707
1709}
1710
1712{
1713 m_state.setHasSideEffects(true);
1714 Q_UNUSED(func)
1715 Q_UNUSED(argc)
1716 Q_UNUSED(argv)
1718}
1719
1721{
1722 m_state.setHasSideEffects(true);
1725}
1726
1728{
1729 m_state.setHasSideEffects(true);
1731}
1732
1734{
1735 m_state.setHasSideEffects(true);
1739}
1740
1742{
1743 const auto fail = [this, name]() {
1744 setError(u"Cannot statically assert the dead temporal zone check for %1"_s.arg(
1745 name ? m_jsUnitGenerator->stringForIndex(name) : u"the anonymous accumulator"_s));
1746 };
1747
1748 const QQmlJSRegisterContent in = m_state.accumulatorIn();
1749 if (in.isConversion()) {
1750 for (const QQmlJSScope::ConstPtr &origin : in.conversionOrigins()) {
1752 continue;
1753 fail();
1754 break;
1755 }
1757 fail();
1758 }
1759}
1760
1762{
1763 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
1764 m_state.setHasSideEffects(true);
1765 m_state.skipInstructionsUntilNextJumpTarget = true;
1766}
1767
1769{
1771}
1772
1774{
1775 m_state.setHasSideEffects(true);
1777}
1778
1780{
1781 m_state.setHasSideEffects(true);
1782}
1783
1785{
1786 m_state.setHasSideEffects(true);
1788 Q_UNUSED(name)
1790}
1791
1793{
1794 m_state.setHasSideEffects(true);
1796}
1797
1799{
1800 m_state.setHasSideEffects(true);
1803}
1804
1806{
1807 m_state.setHasSideEffects(true);
1809}
1810
1812{
1813 m_state.setHasSideEffects(true);
1816}
1817
1819{
1820 m_state.setHasSideEffects(true);
1822}
1823
1825{
1826 m_state.setHasSideEffects(true);
1827}
1828
1830{
1831 Q_UNUSED(iterator)
1833}
1834
1836{
1838 Q_UNUSED(done)
1840}
1841
1843{
1844 Q_UNUSED(iterator)
1845 Q_UNUSED(object)
1847}
1848
1850{
1851 Q_UNUSED(done)
1853}
1854
1856{
1858}
1859
1861{
1862 Q_UNUSED(base)
1865}
1866
1868{
1869 Q_UNUSED(name)
1871}
1872
1874{
1875 Q_UNUSED(name);
1877}
1878
1880{
1882}
1883
1884void QQmlJSTypePropagator::generate_DeclareVar(int varName, int isDeletable)
1885{
1886 Q_UNUSED(varName)
1887 Q_UNUSED(isDeletable)
1889}
1890
1892{
1894
1895 // Track all arguments as the same type.
1896 const QQmlJSRegisterContent elementType
1898 for (int i = 0; i < argc; ++i)
1899 addReadRegister(args + i, elementType);
1900}
1901
1902void QQmlJSTypePropagator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
1903{
1904 const int classSize = m_jsUnitGenerator->jsClassSize(internalClassId);
1905 Q_ASSERT(argc >= classSize);
1906
1907 // Track each element as separate type
1908 for (int i = 0; i < classSize; ++i) {
1909 addReadRegister(
1910 args + i,
1912 }
1913
1914 for (int i = classSize; i < argc; i += 3) {
1915 // layout for remaining members is:
1916 // 0: ObjectLiteralArgument - Value|Method|Getter|Setter
1917
1918 // 1: name of argument
1919 addReadRegister(
1920 args + i + 1,
1922
1923 // 2: value of argument
1924 addReadRegister(
1925 args + i + 2,
1927 }
1928
1930}
1931
1932void QQmlJSTypePropagator::generate_CreateClass(int classIndex, int heritage, int computedNames)
1933{
1934 Q_UNUSED(classIndex)
1935 Q_UNUSED(heritage)
1936 Q_UNUSED(computedNames)
1938}
1939
1941{
1943}
1944
1946{
1948}
1949
1951{
1952 Q_UNUSED(argIndex)
1954}
1955
1957{
1959}
1960
1962{
1964}
1965
1967{
1969}
1970
1972{
1973 saveRegisterStateForJump(offset);
1974 m_state.skipInstructionsUntilNextJumpTarget = true;
1975 m_state.setHasSideEffects(true);
1976}
1977
1979{
1980 if (!canConvertFromTo(m_state.accumulatorIn(),
1982 setError(u"cannot convert from %1 to boolean"_s
1983 .arg(m_state.accumulatorIn().descriptiveName()));
1984 return;
1985 }
1986 saveRegisterStateForJump(offset);
1987 m_state.setHasSideEffects(true);
1988 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
1989}
1990
1992{
1993 if (!canConvertFromTo(m_state.accumulatorIn(),
1995 setError(u"cannot convert from %1 to boolean"_s
1996 .arg(m_state.accumulatorIn().descriptiveName()));
1997 return;
1998 }
1999 saveRegisterStateForJump(offset);
2000 m_state.setHasSideEffects(true);
2001 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
2002}
2003
2005{
2006 saveRegisterStateForJump(offset);
2007 m_state.setHasSideEffects(true);
2008}
2009
2011{
2014}
2015
2017{
2018 m_state.setHasSideEffects(true);
2019}
2020
2021void QQmlJSTypePropagator::recordEqualsNullType()
2022{
2023 // TODO: We can specialize this further, for QVariant, QJSValue, int, bool, whatever.
2024 if (m_typeResolver->registerContains(m_state.accumulatorIn(), m_typeResolver->nullType())
2025 || m_typeResolver->containedType(m_state.accumulatorIn())->isReferenceType()) {
2026 addReadAccumulator(m_state.accumulatorIn());
2027 } else {
2029 }
2030}
2031void QQmlJSTypePropagator::recordEqualsIntType()
2032{
2033 // We have specializations for numeric types and bool.
2034 const QQmlJSScope::ConstPtr in = m_typeResolver->containedType(m_state.accumulatorIn());
2035 if (m_typeResolver->registerContains(m_state.accumulatorIn(), m_typeResolver->boolType())
2036 || m_typeResolver->isNumeric(m_state.accumulatorIn())) {
2037 addReadAccumulator(m_state.accumulatorIn());
2038 } else {
2040 }
2041}
2042void QQmlJSTypePropagator::recordEqualsType(int lhs)
2043{
2044 const auto isNumericOrEnum = [this](const QQmlJSRegisterContent &content) {
2045 return content.isEnumeration() || m_typeResolver->isNumeric(content);
2046 };
2047
2048 const auto isIntCompatible = [this](const QQmlJSRegisterContent &content) {
2049 auto contained = m_typeResolver->containedType(content);
2050 if (contained->scopeType() == QQmlSA::ScopeType::EnumScope)
2051 contained = contained->baseType();
2052 return m_typeResolver->isIntegral(contained)
2053 && !m_typeResolver->equals(contained, m_typeResolver->uint32Type());
2054 };
2055
2056 const auto accumulatorIn = m_state.accumulatorIn();
2057 const auto lhsRegister = m_state.registers[lhs].content;
2058
2059 // If the types are primitive, we compare directly ...
2060 if (m_typeResolver->isPrimitive(accumulatorIn) || accumulatorIn.isEnumeration()) {
2062 accumulatorIn, m_typeResolver->containedType(lhsRegister))) {
2063 addReadRegister(lhs, accumulatorIn);
2064 addReadAccumulator(accumulatorIn);
2065 return;
2066 } else if (isNumericOrEnum(accumulatorIn) && isNumericOrEnum(lhsRegister)) {
2067 const auto targetType = isIntCompatible(accumulatorIn) && isIntCompatible(lhsRegister)
2070 addReadRegister(lhs, targetType);
2071 addReadAccumulator(targetType);
2072 return;
2073 } else if (m_typeResolver->isPrimitive(lhsRegister)) {
2076 addReadRegister(lhs, primitive);
2077 addReadAccumulator(primitive);
2078 return;
2079 }
2080 }
2081
2082 // We don't modify types if the types are comparable with QObject, QUrl or var types
2083 if (canStrictlyCompareWithVar(m_typeResolver, lhsRegister, accumulatorIn)
2084 || canCompareWithQObject(m_typeResolver, lhsRegister, accumulatorIn)
2085 || canCompareWithQUrl(m_typeResolver, lhsRegister, accumulatorIn)) {
2086 addReadRegister(lhs, lhsRegister);
2087 addReadAccumulator(accumulatorIn);
2088 return;
2089 }
2090
2091 // Otherwise they're both casted to QJSValue.
2092 // TODO: We can add more specializations here: object/null etc
2093
2095 addReadRegister(lhs, jsval);
2096 addReadAccumulator(jsval);
2097}
2098
2099void QQmlJSTypePropagator::recordCompareType(int lhs)
2100{
2101 // If they're both numeric, we can compare them directly.
2102 // They may be casted to double, though.
2104 = (m_typeResolver->isNumeric(m_state.accumulatorIn())
2105 && m_typeResolver->isNumeric(m_state.registers[lhs].content))
2106 ? m_typeResolver->merge(m_state.accumulatorIn(), m_state.registers[lhs].content)
2107 : m_typeResolver->globalType(m_typeResolver->jsPrimitiveType());
2108 addReadRegister(lhs, read);
2109 addReadAccumulator(read);
2110}
2111
2113{
2114 recordEqualsNullType();
2115 setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
2116}
2117
2119{
2120 recordEqualsNullType();
2121 setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
2122}
2123
2125{
2126 recordEqualsIntType();
2127 Q_UNUSED(lhsConst)
2130 m_state.accumulatorIn())));
2131}
2132
2134{
2135 recordEqualsIntType();
2136 Q_UNUSED(lhsConst)
2139 m_state.accumulatorIn())));
2140}
2141
2143{
2144 recordEqualsType(lhs);
2145 propagateBinaryOperation(QSOperator::Op::Equal, lhs);
2146}
2147
2149{
2150 recordEqualsType(lhs);
2151 propagateBinaryOperation(QSOperator::Op::NotEqual, lhs);
2152}
2153
2155{
2156 recordCompareType(lhs);
2157 propagateBinaryOperation(QSOperator::Op::Gt, lhs);
2158}
2159
2161{
2162 recordCompareType(lhs);
2163 propagateBinaryOperation(QSOperator::Op::Ge, lhs);
2164}
2165
2167{
2168 recordCompareType(lhs);
2169 propagateBinaryOperation(QSOperator::Op::Lt, lhs);
2170}
2171
2173{
2174 recordCompareType(lhs);
2175 propagateBinaryOperation(QSOperator::Op::Le, lhs);
2176}
2177
2179{
2180 recordEqualsType(lhs);
2181 propagateBinaryOperation(QSOperator::Op::StrictEqual, lhs);
2182}
2183
2185{
2186 recordEqualsType(lhs);
2187 propagateBinaryOperation(QSOperator::Op::StrictNotEqual, lhs);
2188}
2189
2191{
2192 // TODO: Most of the time we don't need the object at all, but only its metatype.
2193 // Fix this when we add support for the "in" instruction to the code generator.
2194 // Also, specialize on lhs to avoid conversion to QJSPrimitiveValue.
2195
2196 addReadRegister(lhs, m_typeResolver->globalType(m_typeResolver->jsValueType()));
2197 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
2198
2199 propagateBinaryOperation(QSOperator::Op::In, lhs);
2200}
2201
2203{
2204 Q_UNUSED(lhs)
2206}
2207
2209{
2210 const QQmlJSRegisterContent input = checkedInputRegister(lhs);
2211 QQmlJSScope::ConstPtr contained;
2212
2213 switch (m_state.accumulatorIn().variant()) {
2215 contained = m_state.accumulatorIn().scopeType();
2216 break;
2218 contained = m_state.accumulatorIn().scopeType();
2219 if (contained->isComposite()) // Otherwise we don't need it
2220 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->metaObjectType()));
2221 break;
2222 default:
2223 contained = m_typeResolver->containedType(m_state.accumulatorIn());
2224 break;
2225 }
2226
2228
2229 if (contained->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
2230 // A referece type cast can result in either the type or null.
2231 // Reference tpyes can hold null. We don't need to special case that.
2232 output = m_typeResolver->globalType(contained);
2233 } else if (!m_typeResolver->canAddressValueTypes()) {
2234 setError(u"invalid cast from %1 to %2. You can only cast object types."_s
2235 .arg(input.descriptiveName(), m_state.accumulatorIn().descriptiveName()));
2236 return;
2237 } else {
2238 // A value type cast can result in either the type or undefined.
2240 m_typeResolver->globalType(contained),
2242 }
2243
2244 addReadRegister(lhs, input);
2245 setAccumulator(output);
2246}
2247
2248void QQmlJSTypePropagator::checkConversion(
2249 const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to)
2250{
2251 if (!canConvertFromTo(from, to)) {
2252 setError(u"cannot convert from %1 to %2"_s
2253 .arg(from.descriptiveName(), to.descriptiveName()));
2254 }
2255}
2256
2257void QQmlJSTypePropagator::generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator op)
2258{
2260 op, m_state.accumulatorIn());
2261 checkConversion(m_state.accumulatorIn(), type);
2262 addReadAccumulator(type);
2263 setAccumulator(type);
2264}
2265
2267{
2268 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Not);
2269}
2270
2272{
2273 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Plus);
2274}
2275
2277{
2278 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Minus);
2279}
2280
2282{
2283 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Complement);
2284}
2285
2287{
2288 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Increment);
2289}
2290
2292{
2293 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Decrement);
2294}
2295
2296void QQmlJSTypePropagator::generateBinaryArithmeticOperation(QSOperator::Op op, int lhs)
2297{
2298 const QQmlJSRegisterContent type = propagateBinaryOperation(op, lhs);
2299
2300 checkConversion(checkedInputRegister(lhs), type);
2301 addReadRegister(lhs, type);
2302
2303 checkConversion(m_state.accumulatorIn(), type);
2304 addReadAccumulator(type);
2305}
2306
2307void QQmlJSTypePropagator::generateBinaryConstArithmeticOperation(QSOperator::Op op)
2308{
2310 op, m_state.accumulatorIn(),
2312
2313 checkConversion(m_state.accumulatorIn(), type);
2314 addReadAccumulator(type);
2315 setAccumulator(type);
2316}
2317
2319{
2320 generateBinaryArithmeticOperation(QSOperator::Op::Add, lhs);
2321}
2322
2324{
2325 generateBinaryArithmeticOperation(QSOperator::Op::BitAnd, lhs);
2326}
2327
2329{
2330 generateBinaryArithmeticOperation(QSOperator::Op::BitOr, lhs);
2331}
2332
2334{
2335 generateBinaryArithmeticOperation(QSOperator::Op::BitXor, lhs);
2336}
2337
2339{
2340 generateBinaryArithmeticOperation(QSOperator::Op::URShift, lhs);
2341}
2342
2344{
2345 generateBinaryArithmeticOperation(QSOperator::Op::RShift, lhs);
2346}
2347
2349{
2350 generateBinaryArithmeticOperation(QSOperator::Op::LShift, lhs);
2351}
2352
2354{
2355 Q_UNUSED(rhsConst)
2356 generateBinaryConstArithmeticOperation(QSOperator::Op::BitAnd);
2357}
2358
2360{
2361 Q_UNUSED(rhsConst)
2362 generateBinaryConstArithmeticOperation(QSOperator::Op::BitOr);
2363}
2364
2366{
2367 Q_UNUSED(rhsConst)
2368 generateBinaryConstArithmeticOperation(QSOperator::Op::BitXor);
2369}
2370
2372{
2373 Q_UNUSED(rhsConst)
2374 generateBinaryConstArithmeticOperation(QSOperator::Op::URShift);
2375}
2376
2378{
2379 Q_UNUSED(rhsConst)
2380 generateBinaryConstArithmeticOperation(QSOperator::Op::RShift);
2381}
2382
2384{
2385 Q_UNUSED(rhsConst)
2386 generateBinaryConstArithmeticOperation(QSOperator::Op::LShift);
2387}
2388
2390{
2391 generateBinaryArithmeticOperation(QSOperator::Op::Exp, lhs);
2392}
2393
2395{
2396 generateBinaryArithmeticOperation(QSOperator::Op::Mul, lhs);
2397}
2398
2400{
2401 generateBinaryArithmeticOperation(QSOperator::Op::Div, lhs);
2402}
2403
2405{
2406 generateBinaryArithmeticOperation(QSOperator::Op::Mod, lhs);
2407}
2408
2410{
2411 generateBinaryArithmeticOperation(QSOperator::Op::Sub, lhs);
2412}
2413
2415{
2416 for (int reg = firstReg, end = firstReg + count; reg < end; ++reg)
2417 setRegister(reg, m_typeResolver->globalType(m_typeResolver->emptyType()));
2418}
2419
2421{
2423}
2424
2426{
2429}
2430
2433{
2434 if (m_error->isValid())
2435 return SkipInstruction;
2436
2437 if (m_state.jumpTargets.contains(currentInstructionOffset())) {
2438 if (m_state.skipInstructionsUntilNextJumpTarget) {
2439 // When re-surfacing from dead code, all registers are invalid.
2440 m_state.registers.clear();
2441 m_state.skipInstructionsUntilNextJumpTarget = false;
2442 }
2443 } else if (m_state.skipInstructionsUntilNextJumpTarget
2445 return SkipInstruction;
2446 }
2447
2448 const int currentOffset = currentInstructionOffset();
2449
2450 // If we reach an instruction that is a target of a jump earlier, then we must check that the
2451 // register state at the origin matches the current state. If not, then we may have to inject
2452 // conversion code (communicated to code gen via m_state.typeConversions). For
2453 // example:
2454 //
2455 // function blah(x: number) { return x > 10 ? 10 : x}
2456 //
2457 // translates to a situation where in the "true" case, we load an integer into the accumulator
2458 // and in the else case a number (x). When the control flow is joined, the types don't match and
2459 // we need to make sure that the int is converted to a double just before the jump.
2460 for (auto originRegisterStateIt =
2461 m_jumpOriginRegisterStateByTargetInstructionOffset.constFind(currentOffset);
2462 originRegisterStateIt != m_jumpOriginRegisterStateByTargetInstructionOffset.constEnd()
2463 && originRegisterStateIt.key() == currentOffset;
2464 ++originRegisterStateIt) {
2465 auto stateToMerge = *originRegisterStateIt;
2466 for (auto registerIt = stateToMerge.registers.constBegin(),
2467 end = stateToMerge.registers.constEnd();
2468 registerIt != end; ++registerIt) {
2469 const int registerIndex = registerIt.key();
2470
2471 auto newType = registerIt.value().content;
2472 if (!newType.isValid()) {
2473 setError(u"When reached from offset %1, %2 is undefined"_s
2474 .arg(stateToMerge.originatingOffset)
2475 .arg(registerName(registerIndex)));
2476 return SkipInstruction;
2477 }
2478
2479 auto currentRegister = m_state.registers.find(registerIndex);
2480 if (currentRegister != m_state.registers.end())
2481 mergeRegister(registerIndex, newType, currentRegister.value().content);
2482 else
2483 mergeRegister(registerIndex, newType, newType);
2484 }
2485 }
2486
2487 return ProcessInstruction;
2488}
2489
2491{
2492 InstructionAnnotation &currentInstruction = m_state.annotations[currentInstructionOffset()];
2493 currentInstruction.changedRegister = m_state.changedRegister();
2494 currentInstruction.changedRegisterIndex = m_state.changedRegisterIndex();
2495 currentInstruction.readRegisters = m_state.takeReadRegisters();
2496 currentInstruction.hasSideEffects = m_state.hasSideEffects();
2497 currentInstruction.isRename = m_state.isRename();
2498
2499 switch (instr) {
2500 // the following instructions are not expected to produce output in the accumulator
2501 case QV4::Moth::Instr::Type::Ret:
2502 case QV4::Moth::Instr::Type::Jump:
2503 case QV4::Moth::Instr::Type::JumpFalse:
2504 case QV4::Moth::Instr::Type::JumpTrue:
2505 case QV4::Moth::Instr::Type::StoreReg:
2506 case QV4::Moth::Instr::Type::StoreElement:
2507 case QV4::Moth::Instr::Type::StoreNameSloppy:
2508 case QV4::Moth::Instr::Type::StoreProperty:
2509 case QV4::Moth::Instr::Type::SetLookup:
2510 case QV4::Moth::Instr::Type::MoveConst:
2511 case QV4::Moth::Instr::Type::MoveReg:
2512 case QV4::Moth::Instr::Type::CheckException:
2513 case QV4::Moth::Instr::Type::CreateCallContext:
2514 case QV4::Moth::Instr::Type::PopContext:
2515 case QV4::Moth::Instr::Type::JumpNoException:
2516 case QV4::Moth::Instr::Type::ThrowException:
2517 case QV4::Moth::Instr::Type::SetUnwindHandler:
2518 case QV4::Moth::Instr::Type::PushCatchContext:
2519 case QV4::Moth::Instr::Type::UnwindDispatch:
2520 case QV4::Moth::Instr::Type::InitializeBlockDeadTemporalZone:
2521 case QV4::Moth::Instr::Type::ConvertThisToObject:
2522 case QV4::Moth::Instr::Type::DeadTemporalZoneCheck:
2523 if (m_state.changedRegisterIndex() == Accumulator && !m_error->isValid()) {
2524 setError(u"Instruction is not expected to populate the accumulator"_s);
2525 return;
2526 }
2527 break;
2528 default:
2529 // If the instruction is expected to produce output, save it in the register set
2530 // for the next instruction.
2531 if ((!m_state.changedRegister().isValid() || m_state.changedRegisterIndex() != Accumulator)
2532 && !m_error->isValid()) {
2533 setError(u"Instruction is expected to populate the accumulator"_s);
2534 return;
2535 }
2536 }
2537
2538 if (m_state.changedRegisterIndex() != InvalidRegister) {
2539 Q_ASSERT(m_error->isValid() || m_state.changedRegister().isValid());
2540 VirtualRegister &r = m_state.registers[m_state.changedRegisterIndex()];
2541 r.content = m_state.changedRegister();
2542 r.canMove = false;
2543 r.affectedBySideEffects = m_state.isRename()
2544 && m_state.isRegisterAffectedBySideEffects(m_state.renameSourceRegisterIndex());
2545 m_state.clearChangedRegister();
2546 }
2547
2548 m_state.setHasSideEffects(false);
2549 m_state.setIsRename(false);
2550 m_state.setReadRegisters(VirtualRegisters());
2551}
2552
2553QQmlJSRegisterContent QQmlJSTypePropagator::propagateBinaryOperation(QSOperator::Op op, int lhs)
2554{
2555 auto lhsRegister = checkedInputRegister(lhs);
2556 if (!lhsRegister.isValid())
2557 return QQmlJSRegisterContent();
2558
2560 op, lhsRegister, m_state.accumulatorIn());
2561
2562 setAccumulator(type);
2563
2564 // If we're dealing with QJSPrimitiveType, do not force premature conversion of the arguemnts
2565 // to the target type. Such an operation can lose information.
2566 if (type.storedType() == m_typeResolver->jsPrimitiveType())
2568
2569 return type;
2570}
2571
2572void QQmlJSTypePropagator::saveRegisterStateForJump(int offset)
2573{
2574 auto jumpToOffset = offset + nextInstructionOffset();
2575 ExpectedRegisterState state;
2576 state.registers = m_state.registers;
2577 state.originatingOffset = currentInstructionOffset();
2578 m_state.jumpTargets.insert(jumpToOffset);
2579 if (offset < 0) {
2580 // We're jumping backwards. We won't get to merge the register states in this pass anymore.
2581
2582 const auto registerStates =
2583 m_jumpOriginRegisterStateByTargetInstructionOffset.equal_range(jumpToOffset);
2584 for (auto it = registerStates.first; it != registerStates.second; ++it) {
2585 if (it->registers.keys() == state.registers.keys()
2586 && it->registers.values() == state.registers.values()) {
2587 return; // We've seen the same register state before. No need for merging.
2588 }
2589 }
2590
2591 // The register state at the target offset needs to be resolved in a further pass.
2592 m_state.needsMorePasses = true;
2593 }
2594 m_jumpOriginRegisterStateByTargetInstructionOffset.insert(jumpToOffset, state);
2595}
2596
2597QString QQmlJSTypePropagator::registerName(int registerIndex) const
2598{
2599 if (registerIndex == Accumulator)
2600 return u"accumulator"_s;
2601 if (registerIndex >= FirstArgument
2602 && registerIndex < FirstArgument + m_function->argumentTypes.size()) {
2603 return u"argument %1"_s.arg(registerIndex - FirstArgument);
2604 }
2605
2606 return u"temporary register %1"_s.arg(
2607 registerIndex - FirstArgument - m_function->argumentTypes.size());
2608}
2609
2610QQmlJSRegisterContent QQmlJSTypePropagator::checkedInputRegister(int reg)
2611{
2612 const auto regIt = m_state.registers.find(reg);
2613 if (regIt == m_state.registers.end()) {
2614 if (isArgument(reg))
2615 return argumentType(reg);
2616
2617 setError(u"Type error: could not infer the type of an expression"_s);
2618 return {};
2619 }
2620 return regIt.value().content;
2621}
2622
2623bool QQmlJSTypePropagator::canConvertFromTo(const QQmlJSRegisterContent &from,
2624 const QQmlJSRegisterContent &to)
2625{
2626 return m_typeResolver->canConvertFromTo(from, to);
2627}
2628
static JNINativeMethod methods[]
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
iterator end()
Definition qflatmap_p.h:773
iterator find(const Key &key)
Definition qflatmap_p.h:816
QList< Key > keys() const
Returns a list containing all the keys in the hash, in an arbitrary order.
Definition qhash.h:1076
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
const_iterator constBegin() const noexcept
Definition qlist.h:615
const_iterator constEnd() const noexcept
Definition qlist.h:616
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1833
QPair< iterator, iterator > equal_range(const Key &key)
Definition qhash.h:2128
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1930
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1921
const QV4::Compiler::JSUnitGenerator * m_jsUnitGenerator
bool isArgument(int registerIndex) const
static bool instructionManipulatesContext(QV4::Moth::Instr::Type type)
const Function * m_function
const QQmlJSTypeResolver * m_typeResolver
QQmlJS::DiagnosticMessage * m_error
State initialState(const Function *function)
QFlatMap< int, VirtualRegister > VirtualRegisters
void setError(const QString &message, int instructionOffset)
QQmlJSRegisterContent argumentType(int registerIndex) const
void log(const QString &message, QQmlJS::LoggerWarningId id, const QQmlJS::SourceLocation &srcLocation, bool showContext=true, bool showFileName=true, const std::optional< QQmlJSFixSuggestion > &suggestion={}, const QString overrideFileName=QString())
QSharedPointer< const QQmlJSScope > type() const
QQmlJSScope::ConstPtr scopeType() const
static QQmlJSRegisterContent create(const QQmlJSScope::ConstPtr &storedType, const QQmlJSScope::ConstPtr &type, ContentVariant variant, const QQmlJSScope::ConstPtr &scope={})
ContentVariant variant() const
QQmlJSScope::ConstPtr storedType() const
QList< QQmlJSMetaMethod > method() const
QHash< QString, QQmlJSMetaProperty > properties() const
Returns all properties visible from this scope including those of base types and extensions.
bool isComposite() const
QHash< QString, QQmlJSMetaMethod > methods() const
Returns all methods visible from this scope including those of base types and extensions.
bool isInCustomParserParent() const
QQmlJSScope::Ptr parentScope()
QString internalName() const
bool isReferenceType() const
AccessSemantics accessSemantics() const
static QQmlSA::Element createQQmlSAElement(const ConstPtr &)
QVector< QQmlJSScope::Ptr > childScopes()
bool isListProperty() const
QDeferredSharedPointer< const QQmlJSScope > ConstPtr
QQmlJSMetaProperty property(const QString &name) const
bool hasOwnProperty(const QString &name) const
QQmlJSScope::ConstPtr baseType() const
static QQmlJSScope::ConstPtr findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope)
QQmlJS::SourceLocation sourceLocation() const
QQmlJSScope::ConstPtr valueType() const
QString id(const QQmlJSScope::ConstPtr &scope, const QQmlJSScope::ConstPtr &referrer) const
bool componentsAreBound() const
bool existsAnywhereInDocument(const QString &id) const
bool equals(const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const
QQmlJSScope::ConstPtr functionType() const
QQmlJSRegisterContent merge(const QQmlJSRegisterContent &a, const QQmlJSRegisterContent &b) const
QQmlJSRegisterContent memberType(const QQmlJSRegisterContent &type, const QString &name) const
QQmlJSScope::ConstPtr stringType() const
const QHash< QQmlJS::SourceLocation, QQmlJSMetaSignalHandler > & signalHandlers() const
QQmlJSScope::ConstPtr nullType() const
QQmlJSRegisterContent tracked(const QQmlJSRegisterContent &type) const
QQmlJSScope::ConstPtr genericType(const QQmlJSScope::ConstPtr &type, ComponentIsGeneric allowComponent=ComponentIsGeneric::No) const
QQmlJSScope::ConstPtr emptyType() const
bool registerIsStoredIn(const QQmlJSRegisterContent &reg, const QQmlJSScope::ConstPtr &type) const
QQmlJSScope::ConstPtr containedType(const QQmlJSRegisterContent &container) const
QQmlJSScope::ConstPtr jsPrimitiveType() const
QQmlJSRegisterContent typeForArithmeticUnaryOperation(UnaryOperator op, const QQmlJSRegisterContent &operand) const
bool isNumeric(const QQmlJSRegisterContent &type) const
QQmlJSScope::ConstPtr arrayPrototype() const
QQmlJSScope::ConstPtr boolType() const
QQmlJSScope::ConstPtr qObjectListType() const
QQmlJSScope::ConstPtr jsGlobalObject() const
bool registerContains(const QQmlJSRegisterContent &reg, const QQmlJSScope::ConstPtr &type) const
bool isIntegral(const QQmlJSRegisterContent &type) const
bool isPrimitive(const QQmlJSRegisterContent &type) const
QQmlJSRegisterContent builtinType(const QQmlJSScope::ConstPtr &type) const
QQmlJSRegisterContent returnType(const QQmlJSScope::ConstPtr &type, QQmlJSRegisterContent::ContentVariant variant, const QQmlJSScope::ConstPtr &scope) const
bool canConvertFromTo(const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) const
bool isPrefix(const QString &name) const
QString containedTypeName(const QQmlJSRegisterContent &container, bool useFancyName=false) const
QQmlJSScope::ConstPtr jsValueType() const
QQmlJSRegisterContent globalType(const QQmlJSScope::ConstPtr &type) const
QQmlJSRegisterContent convert(const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to) const
bool canHoldUndefined(const QQmlJSRegisterContent &content) const
QQmlJSRegisterContent valueType(const QQmlJSRegisterContent &list) const
QQmlJSScope::ConstPtr uint32Type() const
QQmlJSScope::ConstPtr int32Type() const
QQmlJSScope::ConstPtr variantListType() const
QQmlJSScope::ConstPtr voidType() const
QQmlJSScope::ConstPtr metaObjectType() const
QQmlJSScope::ConstPtr realType() const
bool isUnsignedInteger(const QQmlJSScope::ConstPtr &type) const
QQmlJSScope::ConstPtr typeForConst(QV4::ReturnedValue rv) const
bool canAddressValueTypes() const
bool isSignedInteger(const QQmlJSScope::ConstPtr &type) const
QQmlJSRegisterContent scopedType(const QQmlJSScope::ConstPtr &scope, const QString &name) const
QQmlJSScope::ConstPtr qObjectType() const
QQmlJSScope::ConstPtr varType() const
QQmlJSRegisterContent typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegisterContent &left, const QQmlJSRegisterContent &right) const
QQmlJSScope::ConstPtr variantMapType() const
\inmodule QtQmlCompiler
Definition qqmlsa.h:309
static QQmlSA::SourceLocation createQQmlSASourceLocation(const QQmlJS::SourceLocation &jsLocation)
QList< T > values() const
Definition qset.h:297
iterator end()
Definition qset.h:140
iterator find(const T &value)
Definition qset.h:159
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QSet< QString >::iterator it
QList< QVariant > arguments
else opt state
[0]
\inmodule QtQmlCompiler
Combined button and popup list for selecting options.
#define Q_FALLTHROUGH()
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
DBusConnection const char DBusError * error
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
const char * typeName
GLint location
GLboolean GLboolean GLboolean b
GLenum GLuint GLint level
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLsizei GLenum GLenum * types
GLenum GLenum GLsizei count
GLenum type
GLuint GLsizei const GLchar * message
GLenum GLuint GLintptr offset
GLuint name
GLenum func
Definition qopenglext.h:663
GLuint entry
GLuint in
GLenum GLenum GLenum input
const QQmlJS::LoggerWarningId qmlUnqualified
const QQmlJS::LoggerWarningId qmlUseProperFunction
const QQmlJS::LoggerWarningId qmlRestrictedType
const QQmlJS::LoggerWarningId qmlMissingEnumEntry
const QQmlJS::LoggerWarningId qmlUnresolvedType
const QQmlJS::LoggerWarningId qmlReadOnlyProperty
const QQmlJS::LoggerWarningId qmlMissingProperty
const QQmlJS::LoggerWarningId qmlDeprecated
const QQmlJS::LoggerWarningId qmlMissingType
const QQmlJS::LoggerWarningId qmlAccessSingleton
const QQmlJS::LoggerWarningId qmlIncompatibleType
#define INSTR_PROLOGUE_NOT_IMPLEMENTED()
#define INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE()
static bool isLoggingMethod(const QString &consoleMethod)
bool canCompareWithQObject(const QQmlJSTypeResolver *typeResolver, const QQmlJSRegisterContent &lhsContent, const QQmlJSRegisterContent &rhsContent)
bool canCompareWithQUrl(const QQmlJSTypeResolver *typeResolver, const QQmlJSRegisterContent &lhsContent, const QQmlJSRegisterContent &rhsContent)
bool canStrictlyCompareWithVar(const QQmlJSTypeResolver *typeResolver, const QQmlJSRegisterContent &lhsContent, const QQmlJSRegisterContent &rhsContent)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int(*) void arg)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define Q_UNUSED(x)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
#define decode(x)
ReturnedValue read(const char *data)
QT_BEGIN_NAMESPACE typedef uchar * output
const char property[13]
Definition qwizard.cpp:101
QStringList keys
QGraphicsItem * item
QJSValueList args
QList< QQmlJSRegisterContent > argumentTypes
QQmlJSScope::ConstPtr qmlScope
const SourceLocationTable * sourceLocations
QQmlJSScope::ConstPtr returnType
void generate_IteratorClose(int done) override
void generate_CmpEqInt(int lhsConst) override
void generate_GetLookup(int index) override
void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) override
void generate_StoreNameStrict(int name) override
void generate_StoreElement(int base, int index) override
void generate_ConvertThisToObject() override
void generate_CloneBlockContext() override
void generate_LoadConst(int index) override
void generate_LoadLocal(int index) override
void generate_CreateUnmappedArgumentsObject() override
void generate_StoreSuperProperty(int property) override
void generate_DeleteName(int name) override
void generate_CreateCallContext() override
void generate_PopScriptContext() override
void generate_MoveRegExp(int regExpId, int destReg) override
void generate_PushWithContext() override
void generate_TypeofName(int name) override
void generate_CallQmlContextPropertyLookup(int index, int argc, int argv) override
void generate_BitXorConst(int rhsConst) override
void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override
void generate_LoadName(int nameIndex) override
void generate_StoreNameCommon(int nameIndex)
void generate_DeclareVar(int varName, int isDeletable) override
void generate_StoreProperty(int name, int base) override
void generate_GetException() override
void generate_LoadReg(int reg) override
void generate_CallGlobalLookup(int index, int argc, int argv) override
void generate_SetLookup(int index, int base) override
void generate_UShrConst(int rhsConst) override
void endInstruction(QV4::Moth::Instr::Type instr) override
void generate_LoadImport(int index) override
void generate_PushBlockContext(int index) override
void generate_CmpGe(int lhs) override
void generate_CmpLt(int lhs) override
void generate_LoadClosure(int value) override
void generate_Mul(int lhs) override
void generate_UnwindToLabel(int level, int offset) override
void generate_StoreScopedLocal(int scope, int index) override
void generate_CmpInstanceOf(int lhs) override
void generate_DestructureRestElement() override
void generate_JumpNoException(int offset) override
void generate_ThrowException() override
void generate_GetOptionalLookup(int index, int offset) override
void generate_Resume(int) override
void generate_BitAndConst(int rhsConst) override
void generate_LoadElement(int base) override
void generate_MoveReg(int srcReg, int destReg) override
void generate_LoadProperty(int nameIndex) override
void generate_CmpNe(int lhs) override
void generate_Construct(int func, int argc, int argv) override
void generate_CallProperty(int name, int base, int argc, int argv) override
void generate_CmpNeInt(int lhs) override
void generate_JumpFalse(int offset) override
void generate_ConstructWithSpread(int func, int argc, int argv) override
void generate_StoreReg(int reg) override
void generate_GetIterator(int iterator) override
void generate_ShlConst(int rhs) override
void generate_BitAnd(int lhs) override
void generate_SetException() override
void generate_CmpStrictNotEqual(int lhs) override
void generate_CmpStrictEqual(int lhs) override
void generate_LoadScopedLocal(int scope, int index) override
void generate_Shl(int lhs) override
void generate_LoadRuntimeString(int stringId) override
void generate_Shr(int lhs) override
void generate_DefineArray(int argc, int args) override
void generate_JumpNotUndefined(int offset) override
void generate_CallWithReceiver(int name, int thisObject, int argc, int argv) override
void generate_CallWithSpread(int func, int thisObject, int argc, int argv) override
void generate_GetTemplateObject(int index) override
void generate_DefineObjectLiteral(int internalClassId, int argc, int args) override
void generate_CreateRestParameter(int argIndex) override
void generate_CheckException() override
void generate_CallValue(int name, int argc, int argv) override
void generate_Mod(int lhs) override
void generate_BitOr(int lhs) override
void generate_CmpLe(int lhs) override
void generate_StoreLocal(int index) override
void generate_SetUnwindHandler(int offset) override
void generate_LoadUndefined() override
void generate_MoveConst(int constIndex, int destTemp) override
void generate_TailCall(int func, int thisObject, int argc, int argv) override
void generate_DeleteProperty(int base, int index) override
void generate_BitOrConst(int rhsConst) override
void generate_StoreNameSloppy(int nameIndex) override
void generate_BitXor(int lhs) override
void generate_Exp(int lhs) override
void generate_Div(int lhs) override
void generate_LoadSuperProperty(int property) override
void generate_CreateClass(int classIndex, int heritage, int computedNames) override
void generate_UShr(int lhs) override
void generate_PushScriptContext(int index) override
void generate_CallName(int name, int argc, int argv) override
void generate_Jump(int offset) override
void generate_CmpEq(int lhs) override
void generate_As(int lhs) override
void generate_LoadOptionalProperty(int name, int offset) override
void generate_Sub(int lhs) override
void generate_LoadGlobalLookup(int index) override
void generate_UnwindDispatch() override
void generate_LoadInt(int value) override
void generate_IteratorNextForYieldStar(int iterator, int object) override
void generate_ShrConst(int rhs) override
void generate_CallPossiblyDirectEval(int argc, int argv) override
void generate_CmpIn(int lhs) override
void generate_JumpTrue(int offset) override
void generate_LoadQmlContextPropertyLookup(int index) override
Verdict startInstruction(QV4::Moth::Instr::Type instr) override
void generate_PushCatchContext(int index, int name) override
void generate_CmpGt(int lhs) override
void generate_IteratorNext(int value, int done) override
QQmlJSTypePropagator(const QV4::Compiler::JSUnitGenerator *unitGenerator, const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger, QQmlSA::PassManager *passManager=nullptr)
bool checkForEnumProblems(const QQmlJSRegisterContent &base, const QString &propertyName) const
void generate_Add(int lhs) override
void generate_LoadSuperConstructor() override
void generate_CreateMappedArgumentsObject() override
void generate_ThrowOnNullOrUndefined() override
InstructionAnnotations run(const Function *m_function, QQmlJS::DiagnosticMessage *error)
void generate_DeadTemporalZoneCheck(int name) override
static std::optional< QQmlJSFixSuggestion > didYouMean(const QString &userInput, QStringList candidates, QQmlJS::SourceLocation location)
QString lookupName(int index) const
ReturnedValue constant(int idx) const
int lookupNameIndex(int index) const
int getStringId(const QString &string) const
QString stringForIndex(int index) const
int jsClassSize(int jsClassId) const
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent