Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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, BasicBlocks basicBlocks,
32 InstructionAnnotations annotations,
33 QQmlSA::PassManager *passManager)
34 : QQmlJSCompilePass(unitGenerator, typeResolver, logger, basicBlocks, annotations),
35 m_passManager(passManager)
36{
37}
38
41{
43 m_error = error;
44 m_returnType = m_function->returnType;
45
46 do {
47 // Reset the error if we need to do another pass
48 if (m_state.needsMorePasses)
50
51 m_prevStateAnnotations = m_state.annotations;
52 m_state = PassState();
53 m_state.annotations = m_annotations;
54 m_state.State::operator=(initialState(m_function));
55
56 reset();
57 decode(m_function->code.constData(), static_cast<uint>(m_function->code.size()));
58
59 // If we have found unresolved backwards jumps, we need to start over with a fresh state.
60 // Mind that m_jumpOriginRegisterStateByTargetInstructionOffset is retained in that case.
61 // This means that we won't start over for the same reason again.
62 } while (m_state.needsMorePasses);
63
64 return { std::move(m_basicBlocks), std::move(m_state.annotations) };
65}
66
67#define INSTR_PROLOGUE_NOT_IMPLEMENTED() \
68 setError(u"Instruction \"%1\" not implemented"_s.arg(QString::fromUtf8(__func__))); \
69 return;
70
71#define INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE() \
72 m_logger->log(u"Instruction \"%1\" not implemented"_s.arg(QString::fromUtf8(__func__)), \
73 qmlCompiler, QQmlJS::SourceLocation()); \
74 return;
75
76void QQmlJSTypePropagator::generate_ret_SAcheck()
77{
79 return;
85 getCurrentBindingSourceLocation()));
86}
88{
89 if (m_passManager != nullptr)
90 generate_ret_SAcheck();
91
93 // Signal handlers cannot return anything.
95 m_state.accumulatorIn(), m_typeResolver->voidType())) {
96 // You can always return undefined.
97 } else if (!m_returnType.isValid() && m_state.accumulatorIn().isValid()) {
98 setError(u"function without return type annotation returns %1. This may prevent proper "_s
99 u"compilation to Cpp."_s.arg(m_state.accumulatorIn().descriptiveName()));
100
102 // Do not complain if the function didn't have a valid annotation in the first place.
103 m_logger->log(u"Function without return type annotation returns %1"_s.arg(
105 qmlIncompatibleType, getCurrentBindingSourceLocation());
106 }
107 return;
108 } else if (!canConvertFromTo(m_state.accumulatorIn(), m_returnType)) {
109 setError(u"cannot convert from %1 to %2"_s
110 .arg(m_state.accumulatorIn().descriptiveName(),
111 m_returnType.descriptiveName()));
112
113 m_logger->log(u"Cannot assign binding of type %1 to %2"_s.arg(
115 m_typeResolver->containedTypeName(m_returnType, true)),
116 qmlIncompatibleType, getCurrentBindingSourceLocation());
117 return;
118 }
119
120 if (m_returnType.isValid()) {
121 // We need to preserve any possible undefined value as that resets the property.
123 addReadAccumulator(m_state.accumulatorIn());
124 else
125 addReadAccumulator(m_returnType);
126 }
127
128 m_state.setHasSideEffects(true);
129 m_state.skipInstructionsUntilNextJumpTarget = true;
130}
131
136
138{
139 auto encodedConst = m_jsUnitGenerator->constant(index);
140 setAccumulator(m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst)));
141}
142
147
152
157
162
167
172
173void QQmlJSTypePropagator::generate_MoveConst(int constIndex, int destTemp)
174{
175 auto encodedConst = m_jsUnitGenerator->constant(constIndex);
176 setRegister(destTemp, m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst)));
177}
178
180{
181 // Do not re-track the register. We're not manipulating it.
182 m_state.setIsRename(true);
183 const QQmlJSRegisterContent content = checkedInputRegister(reg);
184 m_state.addReadRegister(reg, content);
185 m_state.setRegister(Accumulator, content);
186}
187
189{
190 // Do not re-track the register. We're not manipulating it.
191 m_state.setIsRename(true);
192 m_state.addReadAccumulator(m_state.accumulatorIn());
193 m_state.setRegister(reg, m_state.accumulatorIn());
194}
195
196void QQmlJSTypePropagator::generate_MoveReg(int srcReg, int destReg)
197{
198 Q_ASSERT(destReg != InvalidRegister);
199 // Do not re-track the register. We're not manipulating it.
200 m_state.setIsRename(true);
201 const QQmlJSRegisterContent content = checkedInputRegister(srcReg);
202 m_state.addReadRegister(srcReg, content);
203 m_state.setRegister(destReg, content);
204}
205
211
217
223
230
237
239{
240 Q_UNUSED(stringId)
242 // m_state.accumulatorOut.m_state.value = m_jsUnitGenerator->stringForIndex(stringId);
243}
244
245void QQmlJSTypePropagator::generate_MoveRegExp(int regExpId, int destReg)
246{
247 Q_UNUSED(regExpId)
248 Q_UNUSED(destReg)
250}
251
253{
255 // TODO: Check the function at index and see whether it's a generator to return another type
256 // instead.
258}
259
261{
262 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
264 if (!m_state.accumulatorOut().isValid())
265 setError(u"Cannot find name "_s + name);
266}
267
272
273QQmlJS::SourceLocation QQmlJSTypePropagator::getCurrentSourceLocation() const
274{
276 const auto &entries = m_function->sourceLocations->entries;
277
278 auto item = std::lower_bound(entries.begin(), entries.end(), currentInstructionOffset(),
279 [](auto entry, uint offset) { return entry.offset < offset; });
280 Q_ASSERT(item != entries.end());
281 auto location = item->location;
282
283 return location;
284}
285
286QQmlJS::SourceLocation QQmlJSTypePropagator::getCurrentBindingSourceLocation() const
287{
289 const auto &entries = m_function->sourceLocations->entries;
290
291 Q_ASSERT(!entries.isEmpty());
292 return combine(entries.constFirst().location, entries.constLast().location);
293}
294
295void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name, bool isMethod) const
296{
297 auto location = getCurrentSourceLocation();
298
300 // Only ignore custom parser based elements if it's not Connections.
302 || m_function->qmlScope->baseType()->internalName() != u"QQmlConnections"_s)
303 return;
304 }
305
306 if (isMethod) {
307 if (isCallingProperty(m_function->qmlScope, name))
308 return;
309 } else if (propertyResolution(m_function->qmlScope, name) != PropertyMissing) {
310 return;
311 }
312
313 std::optional<QQmlJSFixSuggestion> suggestion;
314
315 auto childScopes = m_function->qmlScope->childScopes();
316 for (qsizetype i = 0; i < m_function->qmlScope->childScopes().size(); i++) {
317 auto &scope = childScopes[i];
318 if (location.offset > scope->sourceLocation().offset) {
319 if (i + 1 < childScopes.size()
320 && childScopes.at(i + 1)->sourceLocation().offset < location.offset)
321 continue;
322 if (scope->childScopes().size() == 0)
323 continue;
324
325 const auto jsId = scope->childScopes().first()->jsIdentifier(name);
326
327 if (jsId.has_value() && jsId->kind == QQmlJSScope::JavaScriptIdentifier::Injected) {
328 const QQmlJSScope::JavaScriptIdentifier id = jsId.value();
329
330 QQmlJS::SourceLocation fixLocation = id.location;
331 Q_UNUSED(fixLocation)
332 fixLocation.length = 0;
333
334 const auto handler = m_typeResolver->signalHandlers()[id.location];
335
336 QString fixString = handler.isMultiline ? u"function("_s : u"("_s;
337 const auto parameters = handler.signalParameters;
338 for (int numParams = parameters.size(); numParams > 0; --numParams) {
339 fixString += parameters.at(parameters.size() - numParams);
340 if (numParams > 1)
341 fixString += u", "_s;
342 }
343
344 fixString += handler.isMultiline ? u") "_s : u") => "_s;
345
346 suggestion = QQmlJSFixSuggestion {
347 name + u" is accessible in this scope because you are handling a signal"
348 " at %1:%2. Use a function instead.\n"_s
349 .arg(id.location.startLine)
350 .arg(id.location.startColumn),
351 fixLocation,
352 fixString
353 };
354 suggestion->setAutoApplicable();
355 }
356 break;
357 }
358 }
359
360 // Might be a delegate just missing a required property.
361 // This heuristic does not recognize all instances of this occurring but should be sufficient
362 // protection against wrongly suggesting to add an id to the view to access the model that way
363 // which is very misleading
364 if (name == u"model" || name == u"index") {
365 if (QQmlJSScope::ConstPtr parent = m_function->qmlScope->parentScope(); !parent.isNull()) {
366 const auto bindings = parent->ownPropertyBindings(u"delegate"_s);
367
368 for (auto it = bindings.first; it != bindings.second; it++) {
369 if (!it->hasObject())
370 continue;
371 if (it->objectType() == m_function->qmlScope) {
372 suggestion = QQmlJSFixSuggestion {
373 name + " is implicitly injected into this delegate."
374 " Add a required property instead."_L1,
376 };
377 };
378
379 break;
380 }
381 }
382 }
383
384 if (!suggestion.has_value()) {
385 for (QQmlJSScope::ConstPtr scope = m_function->qmlScope; !scope.isNull();
386 scope = scope->parentScope()) {
387 if (scope->hasProperty(name)) {
389
390 QQmlJS::SourceLocation fixLocation = location;
391 fixLocation.length = 0;
392 suggestion = QQmlJSFixSuggestion{
393 name
394 + " is a member of a parent element.\n You can qualify the access "
395 "with its id to avoid this warning.\n"_L1,
396 fixLocation, (id.isEmpty() ? u"<id>."_s : (id + u'.'))
397 };
398
399 if (id.isEmpty())
400 suggestion->setHint("You first have to give the element an id"_L1);
401 else
402 suggestion->setAutoApplicable();
403 }
404 }
405 }
406
407 if (!suggestion.has_value() && !m_function->addressableScopes.componentsAreBound()
409 const QLatin1String replacement = "pragma ComponentBehavior: Bound"_L1;
410 QQmlJSFixSuggestion bindComponents {
411 "Set \"%1\" in order to use IDs from outer components in nested components."_L1
412 .arg(replacement),
413 QQmlJS::SourceLocation(0, 0, 1, 1),
414 replacement + '\n'_L1
415 };
416 bindComponents.setAutoApplicable();
417 suggestion = bindComponents;
418 }
419
420 if (!suggestion.has_value()) {
421 if (auto didYouMean =
424 + m_function->qmlScope->methods().keys(),
425 location);
426 didYouMean.has_value()) {
427 suggestion = didYouMean;
428 }
429 }
430
431 m_logger->log(QLatin1String("Unqualified access"), qmlUnqualified, location, true, true,
432 suggestion);
433}
434
435void QQmlJSTypePropagator::checkDeprecated(QQmlJSScope::ConstPtr scope, const QString &name,
436 bool isMethod) const
437{
438 Q_ASSERT(!scope.isNull());
439 auto qmlScope = QQmlJSScope::findCurrentQMLScope(scope);
440 if (qmlScope.isNull())
441 return;
442
443 QList<QQmlJSAnnotation> annotations;
444
446
447 if (isMethod) {
448 const QVector<QQmlJSMetaMethod> methods = qmlScope->methods(name);
449 if (methods.isEmpty())
450 return;
451 method = methods.constFirst();
452 annotations = method.annotations();
453 } else {
454 QQmlJSMetaProperty property = qmlScope->property(name);
455 if (!property.isValid())
456 return;
457 annotations = property.annotations();
458 }
459
460 auto deprecationAnn = std::find_if(
461 annotations.constBegin(), annotations.constEnd(),
462 [](const QQmlJSAnnotation &annotation) { return annotation.isDeprecation(); });
463
464 if (deprecationAnn == annotations.constEnd())
465 return;
466
467 QQQmlJSDeprecation deprecation = deprecationAnn->deprecation();
468
469 QString descriptor = name;
470 if (isMethod)
471 descriptor += u'(' + method.parameterNames().join(u", "_s) + u')';
472
473 QString message = QStringLiteral("%1 \"%2\" is deprecated")
474 .arg(isMethod ? u"Method"_s : u"Property"_s)
475 .arg(descriptor);
476
477 if (!deprecation.reason.isEmpty())
478 message.append(QStringLiteral(" (Reason: %1)").arg(deprecation.reason));
479
480 m_logger->log(message, qmlDeprecated, getCurrentSourceLocation());
481}
482
483// Only to be called once a lookup has already failed
484QQmlJSTypePropagator::PropertyResolution QQmlJSTypePropagator::propertyResolution(
485 QQmlJSScope::ConstPtr scope, const QString &propertyName) const
486{
487 auto property = scope->property(propertyName);
488 if (!property.isValid())
489 return PropertyMissing;
490
491 QString errorType;
492 if (property.type().isNull())
493 errorType = u"found"_s;
494 else if (!property.type()->isFullyResolved())
495 errorType = u"fully resolved"_s;
496 else
497 return PropertyFullyResolved;
498
499 Q_ASSERT(!errorType.isEmpty());
500
501 m_logger->log(
502 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
503 .arg(property.typeName(), propertyName, errorType),
504 qmlUnresolvedType, getCurrentSourceLocation());
505
506 return PropertyTypeUnresolved;
507}
508
509bool QQmlJSTypePropagator::isCallingProperty(QQmlJSScope::ConstPtr scope, const QString &name) const
510{
511 auto property = scope->property(name);
512 if (!property.isValid())
513 return false;
514
515 QString propertyType = u"Property"_s;
516
517 auto methods = scope->methods(name);
518
519 QString errorType;
520 if (!methods.isEmpty()) {
521 errorType = u"shadowed by a property."_s;
522 switch (methods.first().methodType()) {
523 case QQmlJSMetaMethodType::Signal:
524 propertyType = u"Signal"_s;
525 break;
526 case QQmlJSMetaMethodType::Slot:
527 propertyType = u"Slot"_s;
528 break;
529 case QQmlJSMetaMethodType::Method:
530 propertyType = u"Method"_s;
531 break;
532 default:
533 Q_UNREACHABLE();
534 }
535 } else if (m_typeResolver->equals(property.type(), m_typeResolver->varType())) {
536 errorType =
537 u"a variant property. It may or may not be a method. Use a regular function instead."_s;
538 } else if (m_typeResolver->equals(property.type(), m_typeResolver->jsValueType())) {
539 errorType =
540 u"a QJSValue property. It may or may not be a method. Use a regular Q_INVOKABLE instead."_s;
541 } else {
542 errorType = u"not a method"_s;
543 }
544
545 m_logger->log(u"%1 \"%2\" is %3"_s.arg(propertyType, name, errorType), qmlUseProperFunction,
546 getCurrentSourceLocation(), true, true, {});
547
548 return true;
549}
550
551
552void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup_SAcheck(const QString &name)
553{
554 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
558 getCurrentBindingSourceLocation()));
559}
560
561
563{
564 // LoadQmlContextPropertyLookup does not use accumulatorIn. It always refers to the scope.
565 // Any import namespaces etc. are handled via LoadProperty or GetLookup.
566
567 const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
568 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
569
571
572 if (!m_state.accumulatorOut().isValid() && m_typeResolver->isPrefix(name)) {
574 setAccumulator(QQmlJSRegisterContent::create(
576 m_typeResolver->containedType(inType)));
577 return;
578 }
579
580 checkDeprecated(m_function->qmlScope, name, false);
581
582 if (!m_state.accumulatorOut().isValid()) {
583 setError(u"Cannot access value for name "_s + name);
584 handleUnqualifiedAccess(name, false);
585 return;
586 }
587
588 const QQmlJSScope::ConstPtr outStored
590
591 if (outStored.isNull()) {
592 // It should really be valid.
593 // We get the generic type from aotContext->loadQmlContextPropertyIdLookup().
594 setError(u"Cannot determine generic type for "_s + name);
595 return;
596 }
597
599 && !outStored->isReferenceType()) {
600 setError(u"Cannot retrieve a non-object type by ID: "_s + name);
601 return;
602 }
603
604 if (m_passManager != nullptr)
605 generate_LoadQmlContextPropertyLookup_SAcheck(name);
606
608 m_attachedContext = QQmlJSScope::ConstPtr();
609}
610
611void QQmlJSTypePropagator::generate_StoreNameCommon_SAcheck(const QQmlJSRegisterContent &in, const QString &name)
612{
613 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeWrite(
618 getCurrentBindingSourceLocation()));
619}
620
633{
634 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
636 const QQmlJSRegisterContent in = m_state.accumulatorIn();
637
638 if (!type.isValid()) {
639 handleUnqualifiedAccess(name, false);
640 setError(u"Cannot find name "_s + name);
641 return;
642 }
643
644 if (!type.isProperty()) {
645 QString message = type.isMethod() ? u"Cannot assign to method %1"_s
646 : u"Cannot assign to non-property %1"_s;
647 // The interpreter treats methods as read-only properties in its error messages
648 // and we lack a better fitting category. We might want to revisit this later.
650 getCurrentSourceLocation());
651 setError(u"Cannot assign to non-property "_s + name);
652 return;
653 }
654
655 if (!type.isWritable()) {
656 setError(u"Can't assign to read-only property %1"_s.arg(name));
657
658 m_logger->log(u"Cannot assign to read-only property %1"_s.arg(name), qmlReadOnlyProperty,
659 getCurrentSourceLocation());
660
661 return;
662 }
663
664 if (!canConvertFromTo(in, type)) {
665 setError(u"cannot convert from %1 to %2"_s
666 .arg(in.descriptiveName(), type.descriptiveName()));
667 }
668
669 if (m_passManager != nullptr)
670 generate_StoreNameCommon_SAcheck(in, name);
671
672
675 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->varType()));
676 else
677 addReadAccumulator(in);
678 } else {
679 addReadAccumulator(type);
680 }
681
682 m_state.setHasSideEffects(true);
683}
684
686{
687 return generate_StoreNameCommon(nameIndex);
688}
689
694
696 const QQmlJSRegisterContent &base, const QString &propertyName)
697{
698 if (base.isEnumeration()) {
699 const auto metaEn = base.enumeration();
700 if (!metaEn.hasKey(propertyName)) {
701 auto fixSuggestion = QQmlJSUtils::didYouMean(propertyName, metaEn.keys(),
702 getCurrentSourceLocation());
703 const QString error = u"\"%1\" is not an entry of enum \"%2\"."_s
704 .arg(propertyName, metaEn.name());
706 m_logger->log(
707 error, qmlMissingEnumEntry, getCurrentSourceLocation(), true, true,
708 fixSuggestion);
709 return true;
710 }
711 } else if (base.variant() == QQmlJSRegisterContent::MetaType) {
712 const QQmlJSMetaEnum metaEn = base.scopeType()->enumeration(propertyName);
713 if (metaEn.isValid() && !metaEn.isScoped() && !metaEn.isQml()) {
714 const QString error
715 = u"You cannot access unscoped enum \"%1\" from here."_s.arg(propertyName);
717 m_logger->log(error, qmlRestrictedType, getCurrentSourceLocation());
718 return true;
719 }
720 }
721
722 return false;
723}
724
726{
727 const auto fallback = [&]() {
728 const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType());
729 addReadAccumulator(jsValue);
730 addReadRegister(base, jsValue);
731 setAccumulator(jsValue);
732 };
733
734 const QQmlJSRegisterContent baseRegister = m_state.registers[base].content;
735 if (!baseRegister.isList()
737 fallback();
738 return;
739 }
740 addReadRegister(base, baseRegister);
741
742 if (m_typeResolver->isNumeric(m_state.accumulatorIn())) {
743 const auto contained = m_typeResolver->containedType(m_state.accumulatorIn());
744 if (m_typeResolver->isSignedInteger(contained))
745 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->sizeType()));
746 else if (m_typeResolver->isUnsignedInteger(contained))
747 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->uint32Type()));
748 else
749 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->realType()));
751 m_state.accumulatorIn()))) {
752 addReadAccumulator(m_state.accumulatorIn());
753 } else {
754 fallback();
755 return;
756 }
757
758 // We can end up with undefined.
759 setAccumulator(m_typeResolver->merge(
760 m_typeResolver->valueType(baseRegister),
762}
763
765{
766 const QQmlJSRegisterContent baseRegister = m_state.registers[base].content;
767 const QQmlJSRegisterContent indexRegister = checkedInputRegister(index);
768
769 if (!baseRegister.isList()
770 || !m_typeResolver->isNumeric(indexRegister)) {
771 const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType());
772 addReadAccumulator(jsValue);
773 addReadRegister(base, jsValue);
774 addReadRegister(index, jsValue);
775
776 // Writing to a JS array can have side effects all over the place since it's
777 // passed by reference.
778 m_state.setHasSideEffects(true);
779 return;
780 }
781
782 const auto contained = m_typeResolver->containedType(indexRegister);
783 if (m_typeResolver->isSignedInteger(contained))
785 else if (m_typeResolver->isUnsignedInteger(contained))
787 else
789
790 addReadRegister(base, baseRegister);
791 addReadAccumulator(m_typeResolver->valueType(baseRegister));
792
793 // If we're writing a QQmlListProperty backed by a container somewhere else,
794 // that has side effects.
795 // If we're writing to a list retrieved from a property, that _should_ have side effects,
796 // but currently the QML engine doesn't implement them.
797 // TODO: Figure out the above and accurately set the flag.
798 m_state.setHasSideEffects(true);
799}
800
801void QQmlJSTypePropagator::propagatePropertyLookup_SAcheck(const QString &propertyName)
802{
803 const bool isAttached =
805
806 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
809 propertyName,
810 QQmlJSScope::createQQmlSAElement(isAttached ? m_attachedContext
811 : m_function->qmlScope),
812 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
813 getCurrentBindingSourceLocation()));
814}
815
816void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName, int lookupIndex)
817{
818 setAccumulator(
820 m_state.accumulatorIn(),
823 + u'.' + propertyName
824 : propertyName, lookupIndex));
825
826 if (!m_state.accumulatorOut().isValid()) {
827 if (m_typeResolver->isPrefix(propertyName)) {
828 Q_ASSERT(m_state.accumulatorIn().isValid());
829 addReadAccumulator(m_state.accumulatorIn());
830 setAccumulator(QQmlJSRegisterContent::create(
831 m_state.accumulatorIn().storedType(),
832 m_jsUnitGenerator->getStringId(propertyName),
835 return;
836 }
837 if (m_state.accumulatorIn().isImportNamespace())
838 m_logger->log(u"Type not found in namespace"_s, qmlUnresolvedType,
839 getCurrentSourceLocation());
842 m_logger->log(
843 u"Cannot access singleton as a property of an object. Did you want to access an attached object?"_s,
844 qmlAccessSingleton, getCurrentSourceLocation());
845 setAccumulator(QQmlJSRegisterContent());
846 } else if (m_state.accumulatorOut().isEnumeration()) {
847 switch (m_state.accumulatorIn().variant()) {
856 break; // OK, can look up enums on that thing
857 default:
858 setAccumulator(QQmlJSRegisterContent());
859 }
860 }
861
862 if (!m_state.accumulatorOut().isValid()) {
863 if (checkForEnumProblems(m_state.accumulatorIn(), propertyName))
864 return;
865
866 setError(u"Cannot load property %1 from %2."_s
867 .arg(propertyName, m_state.accumulatorIn().descriptiveName()));
868
870
871 if (typeName == u"QVariant")
872 return;
873 if (m_state.accumulatorIn().isList() && propertyName == u"length")
874 return;
875
876 auto baseType = m_typeResolver->containedType(m_state.accumulatorIn());
877 // Warn separately when a property is only not found because of a missing type
878
879 if (propertyResolution(baseType, propertyName) != PropertyMissing)
880 return;
881
882 if (baseType->isScript())
883 return;
884
885 std::optional<QQmlJSFixSuggestion> fixSuggestion;
886
887 if (auto suggestion = QQmlJSUtils::didYouMean(propertyName, baseType->properties().keys(),
888 getCurrentSourceLocation());
889 suggestion.has_value()) {
890 fixSuggestion = suggestion;
891 }
892
893 if (!fixSuggestion.has_value()
895 QStringList enumKeys;
896 for (const QQmlJSMetaEnum &metaEnum :
897 m_state.accumulatorIn().scopeType()->enumerations())
898 enumKeys << metaEnum.keys();
899
900 if (auto suggestion =
901 QQmlJSUtils::didYouMean(propertyName, enumKeys, getCurrentSourceLocation());
902 suggestion.has_value()) {
903 fixSuggestion = suggestion;
904 }
905 }
906
907 m_logger->log(u"Member \"%1\" not found on type \"%2\""_s.arg(propertyName).arg(typeName),
908 qmlMissingProperty, getCurrentSourceLocation(), true, true, fixSuggestion);
909 return;
910 }
911
912 if (m_state.accumulatorOut().isMethod() && m_state.accumulatorOut().method().size() != 1) {
913 setError(u"Cannot determine overloaded method on loadProperty"_s);
914 return;
915 }
916
917 if (m_state.accumulatorOut().isProperty()) {
918 const QQmlJSScope::ConstPtr mathObject
919 = m_typeResolver->jsGlobalObject()->property(u"Math"_s).type();
920 if (m_typeResolver->registerContains(m_state.accumulatorIn(), mathObject)) {
922 prop.setPropertyName(propertyName);
923 prop.setTypeName(u"double"_s);
925 setAccumulator(
927 m_typeResolver->realType(), prop, m_state.accumulatorIn().resultLookupIndex(), lookupIndex,
929 );
930
931 return;
932 }
933
935 m_state.accumulatorOut(), m_typeResolver->voidType())) {
936 setError(u"Type %1 does not have a property %2 for reading"_s
937 .arg(m_state.accumulatorIn().descriptiveName(), propertyName));
938 return;
939 }
940
941 if (!m_state.accumulatorOut().property().type()) {
942 m_logger->log(
943 QString::fromLatin1("Type of property \"%2\" not found").arg(propertyName),
944 qmlMissingType, getCurrentSourceLocation());
945 }
946 }
947
948 if (m_passManager != nullptr)
949 propagatePropertyLookup_SAcheck(propertyName);
950
952 m_attachedContext = m_typeResolver->containedType(m_state.accumulatorIn());
953
954 switch (m_state.accumulatorOut().variant()) {
958 // For reading enums or singletons, we don't need to access anything, unless it's an
959 // import namespace. Then we need the name.
960 if (m_state.accumulatorIn().isImportNamespace())
961 addReadAccumulator(m_state.accumulatorIn());
962 break;
963 default:
964 addReadAccumulator(m_state.accumulatorIn());
965 break;
966 }
967}
968
970{
971 propagatePropertyLookup(m_jsUnitGenerator->stringForIndex(nameIndex));
972}
973
980
982{
983 propagatePropertyLookup(m_jsUnitGenerator->lookupName(index), index);
984}
985
987{
989 saveRegisterStateForJump(offset);
990 propagatePropertyLookup(m_jsUnitGenerator->lookupName(index), index);
991}
992
993void QQmlJSTypePropagator::generate_StoreProperty_SAcheck(const QString propertyName, const QQmlJSRegisterContent &callBase)
994{
995 const bool isAttached = callBase.variant() == QQmlJSRegisterContent::ObjectAttached;
996
997 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeWrite(
999 propertyName,
1002 QQmlJSScope::createQQmlSAElement(isAttached ? m_attachedContext
1003 : m_function->qmlScope),
1004 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
1005 getCurrentBindingSourceLocation()));
1006}
1007
1009{
1010 auto callBase = m_state.registers[base].content;
1011 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
1012
1013 QQmlJSRegisterContent property = m_typeResolver->memberType(callBase, propertyName);
1014 if (!property.isProperty()) {
1015 setError(u"Type %1 does not have a property %2 for writing"_s
1016 .arg(callBase.descriptiveName(), propertyName));
1017 return;
1018 }
1019
1020 if (property.storedType().isNull()) {
1021 setError(u"Cannot determine type for property %1 of type %2"_s.arg(
1022 propertyName, callBase.descriptiveName()));
1023 return;
1024 }
1025
1026 if (!property.isWritable() && !property.storedType()->isListProperty()) {
1027 setError(u"Can't assign to read-only property %1"_s.arg(propertyName));
1028
1029 m_logger->log(u"Cannot assign to read-only property %1"_s.arg(propertyName),
1030 qmlReadOnlyProperty, getCurrentSourceLocation());
1031
1032 return;
1033 }
1034
1035 if (!canConvertFromTo(m_state.accumulatorIn(), property)) {
1036 setError(u"cannot convert from %1 to %2"_s
1037 .arg(m_state.accumulatorIn().descriptiveName(), property.descriptiveName()));
1038 return;
1039 }
1040
1041 if (m_passManager != nullptr)
1042 generate_StoreProperty_SAcheck(propertyName, callBase);
1043
1044 // If the input can hold undefined we must not coerce it to the property type
1045 // as that might eliminate an undefined value. For example, undefined -> string
1046 // becomes "undefined".
1047 // We need the undefined value for either resetting the property if that is supported
1048 // or generating an exception otherwise. Therefore we explicitly require the value to
1049 // be given as QVariant. This triggers the QVariant fallback path that's also used for
1050 // shadowable properties. QVariant can hold undefined and the lookup functions will
1051 // handle that appropriately.
1052
1053 const QQmlJSScope::ConstPtr varType = m_typeResolver->varType();
1055 ? property.storedIn(varType).castTo(varType)
1056 : std::move(property);
1057 addReadAccumulator(readType);
1058 addReadRegister(base, callBase);
1059 m_state.setHasSideEffects(true);
1060}
1061
1066
1072
1078
1083
1088
1093
1095{
1096 m_state.setHasSideEffects(true);
1097 Q_UNUSED(name)
1098 Q_UNUSED(argc)
1099 Q_UNUSED(argv)
1101}
1102
1103void QQmlJSTypePropagator::generate_CallWithReceiver(int name, int thisObject, int argc, int argv)
1104{
1105 m_state.setHasSideEffects(true);
1106 Q_UNUSED(name)
1107 Q_UNUSED(thisObject)
1108 Q_UNUSED(argc)
1109 Q_UNUSED(argv)
1111}
1112
1113static bool isLoggingMethod(const QString &consoleMethod)
1114{
1115 return consoleMethod == u"log" || consoleMethod == u"debug" || consoleMethod == u"info"
1116 || consoleMethod == u"warn" || consoleMethod == u"error";
1117}
1118
1119void QQmlJSTypePropagator::generate_CallProperty_SCMath(int base, int argc, int argv)
1120{
1121 // If we call a method on the Math object we don't need the actual Math object. We do need
1122 // to transfer the type information to the code generator so that it knows that this is the
1123 // Math object. Read the base register as void. void isn't stored, and the place where it's
1124 // created will be optimized out if there are no other readers. The code generator can
1125 // retrieve the original type and determine that it was the Math object.
1126
1127 addReadRegister(base, m_typeResolver->globalType(m_typeResolver->voidType()));
1128
1132 for (int i = 0; i < argc; ++i)
1133 addReadRegister(argv + i, realType);
1134 setAccumulator(realType);
1135}
1136
1137void QQmlJSTypePropagator::generate_CallProperty_SCconsole(int base, int argc, int argv)
1138{
1139 const QQmlJSRegisterContent voidType
1141
1142 // If we call a method on the console object we don't need the console object.
1143 addReadRegister(base, voidType);
1144
1145 const QQmlJSRegisterContent stringType
1147
1148 if (argc > 0) {
1149 const QQmlJSRegisterContent firstContent = m_state.registers[argv].content;
1150 const QQmlJSScope::ConstPtr firstArg = m_typeResolver->containedType(firstContent);
1151 switch (firstArg->accessSemantics()) {
1152 case QQmlJSScope::AccessSemantics::Reference:
1153 // We cannot know whether this will be a logging category at run time.
1154 // Therefore we always pass any object types as special last argument.
1155 addReadRegister(argv, m_typeResolver->globalType(
1156 m_typeResolver->genericType(firstArg)));
1157 break;
1158 case QQmlJSScope::AccessSemantics::Sequence:
1159 addReadRegister(argv, firstContent);
1160 break;
1161 default:
1162 addReadRegister(argv, stringType);
1163 break;
1164 }
1165 }
1166
1167 for (int i = 1; i < argc; ++i) {
1168 const QQmlJSRegisterContent argContent = m_state.registers[argv + i].content;
1170 addReadRegister(
1171 argv + i,
1172 arg->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1173 ? argContent
1174 : stringType);
1175 }
1176
1177 m_state.setHasSideEffects(true);
1178 setAccumulator(m_typeResolver->returnType(
1181}
1182
1183void QQmlJSTypePropagator::generate_callProperty_SAcheck(const QString propertyName, const QQmlJSScope::ConstPtr &baseType)
1184{
1185 // TODO: Should there be an analyzeCall() in the future? (w. corresponding onCall in Pass)
1186 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
1187 QQmlJSScope::createQQmlSAElement(baseType), propertyName,
1190 getCurrentBindingSourceLocation()));
1191}
1192
1193void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int argc, int argv)
1194{
1195 Q_ASSERT(m_state.registers.contains(base));
1196 const auto callBase = m_state.registers[base].content;
1197 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
1198
1200 generate_CallProperty_SCMath(base, argc, argv);
1201 return;
1202 }
1203
1204 if (m_typeResolver->registerContains(callBase, m_typeResolver->consoleObject()) && isLoggingMethod(propertyName)) {
1205 generate_CallProperty_SCconsole(base, argc, argv);
1206 return;
1207 }
1208
1209 const auto baseType = m_typeResolver->containedType(callBase);
1210 const auto member = m_typeResolver->memberType(callBase, propertyName);
1211
1212 if (!member.isMethod()) {
1215 const auto jsValueType = m_typeResolver->globalType(m_typeResolver->jsValueType());
1216 addReadRegister(base, jsValueType);
1217 for (int i = 0; i < argc; ++i)
1218 addReadRegister(argv + i, jsValueType);
1219 m_state.setHasSideEffects(true);
1220 setAccumulator(m_typeResolver->returnType(
1223 return;
1224 }
1225
1226 setError(u"Type %1 does not have a property %2 for calling"_s
1227 .arg(callBase.descriptiveName(), propertyName));
1228
1229 if (callBase.isType() && isCallingProperty(callBase.type(), propertyName))
1230 return;
1231
1232 if (checkForEnumProblems(callBase, propertyName))
1233 return;
1234
1235 std::optional<QQmlJSFixSuggestion> fixSuggestion;
1236
1237 if (auto suggestion = QQmlJSUtils::didYouMean(propertyName, baseType->methods().keys(),
1238 getCurrentSourceLocation());
1239 suggestion.has_value()) {
1240 fixSuggestion = suggestion;
1241 }
1242
1243 m_logger->log(u"Member \"%1\" not found on type \"%2\""_s.arg(
1244 propertyName, m_typeResolver->containedTypeName(callBase, true)),
1245 qmlMissingProperty, getCurrentSourceLocation(), true, true, fixSuggestion);
1246 return;
1247 }
1248
1249 checkDeprecated(baseType, propertyName, true);
1250
1251 if (m_passManager != nullptr)
1252 generate_callProperty_SAcheck(propertyName, baseType);
1253
1254 addReadRegister(base, callBase);
1255
1257 if (propertyName == u"arg"_s && argc == 1) {
1258 propagateStringArgCall(argv);
1259 return;
1260 }
1261 }
1262
1263 if (baseType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1264 && m_typeResolver->equals(member.scopeType(), m_typeResolver->arrayPrototype())
1265 && propagateArrayMethod(propertyName, argc, argv, callBase)) {
1266 return;
1267 }
1268
1269 propagateCall(member.method(), argc, argv, member.scopeType());
1270}
1271
1272QQmlJSMetaMethod QQmlJSTypePropagator::bestMatchForCall(const QList<QQmlJSMetaMethod> &methods,
1273 int argc, int argv, QStringList *errors)
1274{
1275 QQmlJSMetaMethod javascriptFunction;
1276 QQmlJSMetaMethod candidate;
1277 bool hasMultipleCandidates = false;
1278
1279 for (const auto &method : methods) {
1280
1281 // If we encounter a JavaScript function, use this as a fallback if no other method matches
1282 if (method.isJavaScriptFunction() && !javascriptFunction.isValid())
1283 javascriptFunction = method;
1284
1285 if (method.returnType().isNull() && !method.returnTypeName().isEmpty()) {
1286 errors->append(u"return type %1 cannot be resolved"_s
1287 .arg(method.returnTypeName()));
1288 continue;
1289 }
1290
1291 const auto arguments = method.parameters();
1292 if (argc != arguments.size()) {
1293 errors->append(
1294 u"Function expects %1 arguments, but %2 were provided"_s.arg(arguments.size())
1295 .arg(argc));
1296 continue;
1297 }
1298
1299 bool fuzzyMatch = true;
1300 bool exactMatch = true;
1301 for (int i = 0; i < argc; ++i) {
1302 const auto argumentType = arguments[i].type();
1303 if (argumentType.isNull()) {
1304 errors->append(
1305 u"type %1 for argument %2 cannot be resolved"_s.arg(arguments[i].typeName())
1306 .arg(i));
1307 exactMatch = false;
1308 fuzzyMatch = false;
1309 break;
1310 }
1311
1312 const auto content = m_state.registers[argv + i].content;
1314 continue;
1315
1316 exactMatch = false;
1317 if (canConvertFromTo(content, m_typeResolver->globalType(argumentType)))
1318 continue;
1319
1320 // We can try to call a method that expects a derived type.
1321 if (argumentType->isReferenceType()
1323 argumentType->baseType(), m_typeResolver->containedType(content))) {
1324 continue;
1325 }
1326
1327 errors->append(
1328 u"argument %1 contains %2 but is expected to contain the type %3"_s.arg(i).arg(
1329 content.descriptiveName(), arguments[i].typeName()));
1330 fuzzyMatch = false;
1331 break;
1332 }
1333
1334 if (exactMatch) {
1335 return method;
1336 } else if (fuzzyMatch) {
1337 if (!candidate.isValid())
1338 candidate = method;
1339 else
1340 hasMultipleCandidates = true;
1341 }
1342 }
1343
1344 if (hasMultipleCandidates)
1345 return QQmlJSMetaMethod();
1346
1347 return candidate.isValid() ? candidate : javascriptFunction;
1348}
1349
1350void QQmlJSTypePropagator::setAccumulator(const QQmlJSRegisterContent &content)
1351{
1352 setRegister(Accumulator, content);
1353}
1354
1355void QQmlJSTypePropagator::setRegister(int index, const QQmlJSRegisterContent &content)
1356{
1357 // If we've come to the same conclusion before, let's not track the type again.
1358 auto it = m_prevStateAnnotations.find(currentInstructionOffset());
1359 if (it != m_prevStateAnnotations.end()) {
1360 const QQmlJSRegisterContent &lastTry = it->second.changedRegister;
1362 m_state.setRegister(index, lastTry);
1363 return;
1364 }
1365 }
1366
1367 m_state.setRegister(index, m_typeResolver->tracked(content));
1368}
1369
1370void QQmlJSTypePropagator::mergeRegister(
1372{
1373 auto merged = m_typeResolver->merge(a, b);
1374 Q_ASSERT(merged.isValid());
1375
1376 if (!merged.isConversion()) {
1377 // The registers were the same. We're already tracking them.
1378 m_state.annotations[currentInstructionOffset()].typeConversions[index].content = merged;
1379 m_state.registers[index].content = merged;
1380 return;
1381 }
1382
1383 auto tryPrevStateConversion = [this](int index, const QQmlJSRegisterContent &merged) -> bool {
1384 auto it = m_prevStateAnnotations.find(currentInstructionOffset());
1385 if (it == m_prevStateAnnotations.end())
1386 return false;
1387
1388 auto conversion = it->second.typeConversions.find(index);
1389 if (conversion == it->second.typeConversions.end())
1390 return false;
1391
1392 const VirtualRegister &lastTry = conversion.value();
1393
1394 Q_ASSERT(lastTry.content.isValid());
1395 if (!lastTry.content.isConversion())
1396 return false;
1397
1398 if (!m_typeResolver->equals(lastTry.content.conversionResult(), merged.conversionResult())
1399 || lastTry.content.conversionOrigins() != merged.conversionOrigins()) {
1400 return false;
1401 }
1402
1403 // We don't need to track it again if we've come to the same conclusion before.
1404 m_state.annotations[currentInstructionOffset()].typeConversions[index] = lastTry;
1405 m_state.registers[index] = lastTry;
1406 return true;
1407 };
1408
1409 if (!tryPrevStateConversion(index, merged)) {
1410 merged = m_typeResolver->tracked(merged);
1411 Q_ASSERT(merged.isValid());
1412 m_state.annotations[currentInstructionOffset()].typeConversions[index].content = merged;
1413 m_state.registers[index].content = merged;
1414 }
1415}
1416
1417void QQmlJSTypePropagator::addReadRegister(int index, const QQmlJSRegisterContent &convertTo)
1418{
1420 (m_state.registers[index].content, convertTo));
1421}
1422
1423void QQmlJSTypePropagator::propagateCall(
1424 const QList<QQmlJSMetaMethod> &methods, int argc, int argv,
1425 const QQmlJSScope::ConstPtr &scope)
1426{
1427 QStringList errors;
1428 const QQmlJSMetaMethod match = bestMatchForCall(methods, argc, argv, &errors);
1429
1430 if (!match.isValid()) {
1431 if (methods.size() == 1) {
1432 // Cannot have multiple fuzzy matches if there is only one method
1433 Q_ASSERT(errors.size() == 1);
1434 setError(errors.first());
1435 } else if (errors.size() < methods.size()) {
1436 setError(u"Multiple matching overrides found. Cannot determine the right one."_s);
1437 } else {
1438 setError(u"No matching override found. Candidates:\n"_s + errors.join(u'\n'));
1439 }
1440 return;
1441 }
1442
1443 const auto returnType = match.isJavaScriptFunction()
1445 : QQmlJSScope::ConstPtr(match.returnType());
1446 setAccumulator(m_typeResolver->returnType(
1447 returnType ? QQmlJSScope::ConstPtr(returnType) : m_typeResolver->voidType(),
1448 match.isJavaScriptFunction()
1449 ? QQmlJSRegisterContent::JavaScriptReturnValue
1450 : QQmlJSRegisterContent::MethodReturnValue,
1451 scope));
1452 if (!m_state.accumulatorOut().isValid())
1453 setError(u"Cannot store return type of method %1()."_s.arg(match.methodName()));
1454
1455 const auto types = match.parameters();
1456 for (int i = 0; i < argc; ++i) {
1457 if (i < types.size()) {
1458 const QQmlJSScope::ConstPtr type = match.isJavaScriptFunction()
1460 : QQmlJSScope::ConstPtr(types.at(i).type());
1461 if (!type.isNull()) {
1462 addReadRegister(argv + i, m_typeResolver->globalType(type));
1463 continue;
1464 }
1465 }
1466 addReadRegister(argv + i, m_typeResolver->globalType(m_typeResolver->jsValueType()));
1467 }
1468 m_state.setHasSideEffects(true);
1469}
1470
1471bool QQmlJSTypePropagator::propagateTranslationMethod(
1472 const QList<QQmlJSMetaMethod> &methods, int argc, int argv)
1473{
1474 if (methods.size() != 1)
1475 return false;
1476
1477 const QQmlJSMetaMethod method = methods.front();
1478 const QQmlJSRegisterContent intType
1480 const QQmlJSRegisterContent stringType
1482 const QQmlJSRegisterContent returnType
1486
1487 if (method.methodName() == u"qsTranslate"_s) {
1488 switch (argc) {
1489 case 4:
1490 addReadRegister(argv + 3, intType); // n
1491 Q_FALLTHROUGH();
1492 case 3:
1493 addReadRegister(argv + 2, stringType); // disambiguation
1494 Q_FALLTHROUGH();
1495 case 2:
1496 addReadRegister(argv + 1, stringType); // sourceText
1497 addReadRegister(argv, stringType); // context
1498 setAccumulator(returnType);
1499 return true;
1500 default:
1501 return false;
1502 }
1503 }
1504
1505 if (method.methodName() == u"QT_TRANSLATE_NOOP"_s) {
1506 switch (argc) {
1507 case 3:
1508 addReadRegister(argv + 2, stringType); // disambiguation
1509 Q_FALLTHROUGH();
1510 case 2:
1511 addReadRegister(argv + 1, stringType); // sourceText
1512 addReadRegister(argv, stringType); // context
1513 setAccumulator(returnType);
1514 return true;
1515 default:
1516 return false;
1517 }
1518 }
1519
1520 if (method.methodName() == u"qsTr"_s) {
1521 switch (argc) {
1522 case 3:
1523 addReadRegister(argv + 2, intType); // n
1524 Q_FALLTHROUGH();
1525 case 2:
1526 addReadRegister(argv + 1, stringType); // disambiguation
1527 Q_FALLTHROUGH();
1528 case 1:
1529 addReadRegister(argv, stringType); // sourceText
1530 setAccumulator(returnType);
1531 return true;
1532 default:
1533 return false;
1534 }
1535 }
1536
1537 if (method.methodName() == u"QT_TR_NOOP"_s) {
1538 switch (argc) {
1539 case 2:
1540 addReadRegister(argv + 1, stringType); // disambiguation
1541 Q_FALLTHROUGH();
1542 case 1:
1543 addReadRegister(argv, stringType); // sourceText
1544 setAccumulator(returnType);
1545 return true;
1546 default:
1547 return false;
1548 }
1549 }
1550
1551 if (method.methodName() == u"qsTrId"_s) {
1552 switch (argc) {
1553 case 2:
1554 addReadRegister(argv + 1, intType); // n
1555 Q_FALLTHROUGH();
1556 case 1:
1557 addReadRegister(argv, stringType); // id
1558 setAccumulator(returnType);
1559 return true;
1560 default:
1561 return false;
1562 }
1563 }
1564
1565 if (method.methodName() == u"QT_TRID_NOOP"_s) {
1566 switch (argc) {
1567 case 1:
1568 addReadRegister(argv, stringType); // id
1569 setAccumulator(returnType);
1570 return true;
1571 default:
1572 return false;
1573 }
1574 }
1575
1576 return false;
1577}
1578
1579void QQmlJSTypePropagator::propagateStringArgCall(int argv)
1580{
1581 setAccumulator(m_typeResolver->returnType(
1584 Q_ASSERT(m_state.accumulatorOut().isValid());
1585
1587 m_state.registers[argv].content);
1588
1590 addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->realType()));
1591 return;
1592 }
1593
1595 addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->int32Type()));
1596 return;
1597 }
1598
1600 addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->realType()));
1601 return;
1602 }
1603
1605 addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->boolType()));
1606 return;
1607 }
1608
1609 addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->stringType()));
1610}
1611
1612bool QQmlJSTypePropagator::propagateArrayMethod(
1613 const QString &name, int argc, int argv, const QQmlJSRegisterContent &baseType)
1614{
1615 // TODO:
1616 // * For concat() we need to decide what kind of array to return and what kinds of arguments to
1617 // accept.
1618 // * For entries(), keys(), and values() we need iterators.
1619 // * For find(), findIndex(), sort(), every(), some(), forEach(), map(), filter(), reduce(),
1620 // and reduceRight() we need typed function pointers.
1621
1622 const auto intType = m_typeResolver->globalType(m_typeResolver->int32Type());
1623 const auto stringType = m_typeResolver->globalType(m_typeResolver->stringType());
1624 const auto baseContained = m_typeResolver->containedType(baseType);
1625 const auto valueContained = baseContained->valueType();
1626 const auto valueType = m_typeResolver->globalType(valueContained);
1627
1628 const bool canHaveSideEffects = (baseType.isProperty() && baseType.isWritable())
1629 || baseContained->isListProperty()
1630 || baseType.isConversion();
1631
1632 const auto setReturnType = [&](const QQmlJSScope::ConstPtr type) {
1633 setAccumulator(m_typeResolver->returnType(
1635 };
1636
1637 if (name == u"copyWithin" && argc > 0 && argc < 4) {
1638 for (int i = 0; i < argc; ++i) {
1639 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1640 return false;
1641 }
1642
1643 for (int i = 0; i < argc; ++i)
1644 addReadRegister(argv + i, intType);
1645
1646 m_state.setHasSideEffects(canHaveSideEffects);
1647 setReturnType(baseContained);
1648 return true;
1649 }
1650
1651 if (name == u"fill" && argc > 0 && argc < 4) {
1652 if (!canConvertFromTo(m_state.registers[argv].content, valueType))
1653 return false;
1654
1655 for (int i = 1; i < argc; ++i) {
1656 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1657 return false;
1658 }
1659
1660 addReadRegister(argv, valueType);
1661
1662 for (int i = 1; i < argc; ++i)
1663 addReadRegister(argv + i, intType);
1664
1665 m_state.setHasSideEffects(canHaveSideEffects);
1666 setReturnType(baseContained);
1667 return true;
1668 }
1669
1670 if (name == u"includes" && argc > 0 && argc < 3) {
1671 if (!canConvertFromTo(m_state.registers[argv].content, valueType))
1672 return false;
1673
1674 if (argc == 2) {
1675 if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
1676 return false;
1677 addReadRegister(argv + 1, intType);
1678 }
1679
1680 addReadRegister(argv, valueType);
1681 setReturnType(m_typeResolver->boolType());
1682 return true;
1683 }
1684
1685 if (name == u"toString" || (name == u"join" && argc < 2)) {
1686 if (argc == 1) {
1687 if (!canConvertFromTo(m_state.registers[argv].content, stringType))
1688 return false;
1689 addReadRegister(argv, stringType);
1690 }
1691
1692 setReturnType(m_typeResolver->stringType());
1693 return true;
1694 }
1695
1696 if ((name == u"pop" || name == u"shift") && argc == 0) {
1697 m_state.setHasSideEffects(canHaveSideEffects);
1698 setReturnType(valueContained);
1699 return true;
1700 }
1701
1702 if (name == u"push" || name == u"unshift") {
1703 for (int i = 0; i < argc; ++i) {
1704 if (!canConvertFromTo(m_state.registers[argv + i].content, valueType))
1705 return false;
1706 }
1707
1708 for (int i = 0; i < argc; ++i)
1709 addReadRegister(argv + i, valueType);
1710
1711 m_state.setHasSideEffects(canHaveSideEffects);
1712 setReturnType(m_typeResolver->int32Type());
1713 return true;
1714 }
1715
1716 if (name == u"reverse" && argc == 0) {
1717 m_state.setHasSideEffects(canHaveSideEffects);
1718 setReturnType(baseContained);
1719 return true;
1720 }
1721
1722 if (name == u"slice" && argc < 3) {
1723 for (int i = 0; i < argc; ++i) {
1724 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1725 return false;
1726 }
1727
1728 for (int i = 0; i < argc; ++i)
1729 addReadRegister(argv + i, intType);
1730
1731 setReturnType(baseType.storedType()->isListProperty()
1733 : baseContained);
1734 return true;
1735 }
1736
1737 if (name == u"splice" && argc > 0) {
1738 for (int i = 0; i < 2; ++i) {
1739 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1740 return false;
1741 }
1742
1743 for (int i = 2; i < argc; ++i) {
1744 if (!canConvertFromTo(m_state.registers[argv + i].content, valueType))
1745 return false;
1746 }
1747
1748 for (int i = 0; i < 2; ++i)
1749 addReadRegister(argv + i, intType);
1750
1751 for (int i = 2; i < argc; ++i)
1752 addReadRegister(argv + i, valueType);
1753
1754 m_state.setHasSideEffects(canHaveSideEffects);
1755 setReturnType(baseContained);
1756 return true;
1757 }
1758
1759 if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
1760 if (!canConvertFromTo(m_state.registers[argv].content, valueType))
1761 return false;
1762
1763 if (argc == 2) {
1764 if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
1765 return false;
1766 addReadRegister(argv + 1, intType);
1767 }
1768
1769 addReadRegister(argv, valueType);
1770 setReturnType(m_typeResolver->int32Type());
1771 return true;
1772 }
1773
1774 return false;
1775}
1776
1778 int argv)
1779{
1780 generate_CallProperty(m_jsUnitGenerator->lookupNameIndex(lookupIndex), base, argc, argv);
1781}
1782
1784{
1785 propagateScopeLookupCall(m_jsUnitGenerator->stringForIndex(name), argc, argv);
1786}
1787
1789{
1790 m_state.setHasSideEffects(true);
1791 Q_UNUSED(argc)
1792 Q_UNUSED(argv)
1794}
1795
1796void QQmlJSTypePropagator::propagateScopeLookupCall(const QString &functionName, int argc, int argv)
1797{
1798 const QQmlJSRegisterContent resolvedContent
1799 = m_typeResolver->scopedType(m_function->qmlScope, functionName);
1800 if (resolvedContent.isMethod()) {
1801 const auto methods = resolvedContent.method();
1802 if (resolvedContent.variant() == QQmlJSRegisterContent::JavaScriptGlobal) {
1803 if (propagateTranslationMethod(methods, argc, argv))
1804 return;
1805 }
1806
1807 if (!methods.isEmpty()) {
1808 propagateCall(methods, argc, argv, resolvedContent.scopeType());
1809 return;
1810 }
1811 }
1812
1813 setError(u"method %1 cannot be resolved."_s.arg(functionName));
1815
1816 setError(u"Cannot find function '%1'"_s.arg(functionName));
1817
1818 handleUnqualifiedAccess(functionName, true);
1819}
1820
1822{
1823 propagateScopeLookupCall(m_jsUnitGenerator->lookupName(index), argc, argv);
1824}
1825
1827{
1829 propagateScopeLookupCall(name, argc, argv);
1830 checkDeprecated(m_function->qmlScope, name, true);
1831}
1832
1833void QQmlJSTypePropagator::generate_CallWithSpread(int func, int thisObject, int argc, int argv)
1834{
1835 m_state.setHasSideEffects(true);
1836 Q_UNUSED(func)
1837 Q_UNUSED(thisObject)
1838 Q_UNUSED(argc)
1839 Q_UNUSED(argv)
1841}
1842
1843void QQmlJSTypePropagator::generate_TailCall(int func, int thisObject, int argc, int argv)
1844{
1845 m_state.setHasSideEffects(true);
1846 Q_UNUSED(func)
1847 Q_UNUSED(thisObject)
1848 Q_UNUSED(argc)
1849 Q_UNUSED(argv)
1851}
1852
1853void QQmlJSTypePropagator::generate_Construct_SCDate(int argc, int argv)
1854{
1856
1857 if (argc == 1) {
1858 const QQmlJSRegisterContent argType = m_state.registers[argv].content;
1859 if (m_typeResolver->isNumeric(argType)) {
1860 addReadRegister(
1862 } else if (m_typeResolver->registerContains(argType, m_typeResolver->stringType())) {
1863 addReadRegister(
1868 addReadRegister(
1870 } else {
1871 addReadRegister(
1873 }
1874 } else {
1875 constexpr int maxArgc = 7; // year, month, day, hours, minutes, seconds, milliseconds
1876 for (int i = 0; i < std::min(argc, maxArgc); ++i) {
1877 addReadRegister(
1879 }
1880 }
1881}
1882
1883void QQmlJSTypePropagator::generate_Construct_SCArray(int argc, int argv)
1884{
1885 if (argc == 1) {
1886 if (m_typeResolver->isNumeric(m_state.registers[argv].content)) {
1888 addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->realType()));
1889 } else {
1890 generate_DefineArray(argc, argv);
1891 }
1892 } else {
1893 generate_DefineArray(argc, argv);
1894 }
1895}
1897{
1898 const QQmlJSRegisterContent type = m_state.registers[func].content;
1899 if (!type.isMethod()) {
1900 m_state.setHasSideEffects(true);
1902 return;
1903 }
1904
1905 if (type.method() == m_typeResolver->jsGlobalObject()->methods(u"Date"_s)) {
1906 generate_Construct_SCDate(argc, argv);
1907 return;
1908 }
1909
1910 if (type.method() == m_typeResolver->jsGlobalObject()->methods(u"Array"_s)) {
1911 generate_Construct_SCArray(argc, argv);
1912
1913 return;
1914 }
1915
1916 m_state.setHasSideEffects(true);
1918}
1919
1921{
1922 m_state.setHasSideEffects(true);
1923 Q_UNUSED(func)
1924 Q_UNUSED(argc)
1925 Q_UNUSED(argv)
1927}
1928
1935
1941
1949
1951{
1952 const auto fail = [this, name]() {
1953 setError(u"Cannot statically assert the dead temporal zone check for %1"_s.arg(
1954 name ? m_jsUnitGenerator->stringForIndex(name) : u"the anonymous accumulator"_s));
1955 };
1956
1957 const QQmlJSRegisterContent in = m_state.accumulatorIn();
1958 if (in.isConversion()) {
1959 for (const QQmlJSScope::ConstPtr &origin : in.conversionOrigins()) {
1961 continue;
1962 fail();
1963 break;
1964 }
1966 fail();
1967 }
1968}
1969
1971{
1972 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
1973 m_state.setHasSideEffects(true);
1974 m_state.skipInstructionsUntilNextJumpTarget = true;
1975}
1976
1981
1987
1992
2000
2006
2013
2019
2026
2032
2037
2039{
2040 const QQmlJSRegisterContent listType = m_state.accumulatorIn();
2041 if (!listType.isList()) {
2042 const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType());
2043 addReadAccumulator(jsValue);
2044 setAccumulator(jsValue);
2045 return;
2046 }
2047
2048 addReadAccumulator(listType);
2049 setAccumulator(m_typeResolver->iteratorPointer(
2050 listType, QQmlJS::AST::ForEachType(iterator), currentInstructionOffset()));
2051}
2052
2054{
2055 const QQmlJSRegisterContent iteratorType = m_state.accumulatorIn();
2056 addReadAccumulator(iteratorType);
2057 setRegister(value, m_typeResolver->merge(
2058 m_typeResolver->valueType(iteratorType),
2060 saveRegisterStateForJump(offset);
2061 m_state.setHasSideEffects(true);
2062}
2063
2065{
2066 Q_UNUSED(iterator)
2067 Q_UNUSED(object)
2070}
2071
2073{
2074 // Noop
2075}
2076
2081
2088
2094
2100
2105
2106void QQmlJSTypePropagator::generate_DeclareVar(int varName, int isDeletable)
2107{
2108 Q_UNUSED(varName)
2109 Q_UNUSED(isDeletable)
2111}
2112
2114{
2116
2117 // Track all arguments as the same type.
2118 const QQmlJSRegisterContent elementType
2120 for (int i = 0; i < argc; ++i)
2121 addReadRegister(args + i, elementType);
2122}
2123
2124void QQmlJSTypePropagator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
2125{
2126 const int classSize = m_jsUnitGenerator->jsClassSize(internalClassId);
2127 Q_ASSERT(argc >= classSize);
2128
2129 // Track each element as separate type
2130 for (int i = 0; i < classSize; ++i) {
2131 addReadRegister(
2132 args + i,
2134 }
2135
2136 for (int i = classSize; i < argc; i += 3) {
2137 // layout for remaining members is:
2138 // 0: ObjectLiteralArgument - Value|Method|Getter|Setter
2139 // We cannot do anything useful with this. Any code that would call a getter/setter/method
2140 // could not be compiled to C++. Ignore it.
2141
2142 // 1: name of argument
2143 addReadRegister(
2144 args + i + 1,
2146
2147 // 2: value of argument
2148 addReadRegister(
2149 args + i + 2,
2151 }
2152
2154}
2155
2156void QQmlJSTypePropagator::generate_CreateClass(int classIndex, int heritage, int computedNames)
2157{
2158 Q_UNUSED(classIndex)
2159 Q_UNUSED(heritage)
2160 Q_UNUSED(computedNames)
2162}
2163
2168
2173
2179
2184
2189
2194
2196{
2197 saveRegisterStateForJump(offset);
2198 m_state.skipInstructionsUntilNextJumpTarget = true;
2199 m_state.setHasSideEffects(true);
2200}
2201
2203{
2204 if (!canConvertFromTo(m_state.accumulatorIn(),
2206 setError(u"cannot convert from %1 to boolean"_s
2207 .arg(m_state.accumulatorIn().descriptiveName()));
2208 return;
2209 }
2210 saveRegisterStateForJump(offset);
2211 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
2212 m_state.setHasSideEffects(true);
2213}
2214
2216{
2217 if (!canConvertFromTo(m_state.accumulatorIn(),
2219 setError(u"cannot convert from %1 to boolean"_s
2220 .arg(m_state.accumulatorIn().descriptiveName()));
2221 return;
2222 }
2223 saveRegisterStateForJump(offset);
2224 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
2225 m_state.setHasSideEffects(true);
2226}
2227
2229{
2230 saveRegisterStateForJump(offset);
2231 m_state.setHasSideEffects(true);
2232}
2233
2239
2244
2245void QQmlJSTypePropagator::recordEqualsNullType()
2246{
2247 // TODO: We can specialize this further, for QVariant, QJSValue, int, bool, whatever.
2250 addReadAccumulator(m_state.accumulatorIn());
2251 } else {
2253 }
2254}
2255void QQmlJSTypePropagator::recordEqualsIntType()
2256{
2257 // We have specializations for numeric types and bool.
2260 || m_typeResolver->isNumeric(m_state.accumulatorIn())) {
2261 addReadAccumulator(m_state.accumulatorIn());
2262 } else {
2264 }
2265}
2266void QQmlJSTypePropagator::recordEqualsType(int lhs)
2267{
2268 const auto isNumericOrEnum = [this](const QQmlJSRegisterContent &content) {
2269 return content.isEnumeration() || m_typeResolver->isNumeric(content);
2270 };
2271
2272 const auto accumulatorIn = m_state.accumulatorIn();
2273 const auto lhsRegister = m_state.registers[lhs].content;
2274
2275 // If the types are primitive, we compare directly ...
2276 if (m_typeResolver->isPrimitive(accumulatorIn) || accumulatorIn.isEnumeration()) {
2278 accumulatorIn, m_typeResolver->containedType(lhsRegister))
2279 || (isNumericOrEnum(accumulatorIn) && isNumericOrEnum(lhsRegister))
2280 || m_typeResolver->isPrimitive(lhsRegister)) {
2281 addReadRegister(lhs, lhsRegister);
2282 addReadAccumulator(accumulatorIn);
2283 return;
2284 }
2285 }
2286
2287 const auto containedAccumulatorIn = m_typeResolver->isOptionalType(accumulatorIn)
2289 : m_typeResolver->containedType(accumulatorIn);
2290
2291 const auto containedLhs = m_typeResolver->isOptionalType(lhsRegister)
2293 : m_typeResolver->containedType(lhsRegister);
2294
2295 // We don't modify types if the types are comparable with QObject, QUrl or var types
2296 if (canStrictlyCompareWithVar(m_typeResolver, containedLhs, containedAccumulatorIn)
2297 || canCompareWithQObject(m_typeResolver, containedLhs, containedAccumulatorIn)
2298 || canCompareWithQUrl(m_typeResolver, containedLhs, containedAccumulatorIn)) {
2299 addReadRegister(lhs, lhsRegister);
2300 addReadAccumulator(accumulatorIn);
2301 return;
2302 }
2303
2304 // Otherwise they're both casted to QJSValue.
2305 // TODO: We can add more specializations here: object/null etc
2306
2308 addReadRegister(lhs, jsval);
2309 addReadAccumulator(jsval);
2310}
2311
2312void QQmlJSTypePropagator::recordCompareType(int lhs)
2313{
2314 // If they're both numeric, we can compare them directly.
2315 // They may be casted to double, though.
2318 && m_typeResolver->isNumeric(m_state.registers[lhs].content))
2319 ? m_typeResolver->merge(m_state.accumulatorIn(), m_state.registers[lhs].content)
2320 : m_typeResolver->globalType(m_typeResolver->jsPrimitiveType());
2321 addReadRegister(lhs, read);
2322 addReadAccumulator(read);
2323}
2324
2326{
2327 recordEqualsNullType();
2328 setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
2329}
2330
2332{
2333 recordEqualsNullType();
2334 setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
2335}
2336
2338{
2339 recordEqualsIntType();
2340 Q_UNUSED(lhsConst)
2343 m_state.accumulatorIn())));
2344}
2345
2347{
2348 recordEqualsIntType();
2349 Q_UNUSED(lhsConst)
2352 m_state.accumulatorIn())));
2353}
2354
2356{
2357 recordEqualsType(lhs);
2358 propagateBinaryOperation(QSOperator::Op::Equal, lhs);
2359}
2360
2362{
2363 recordEqualsType(lhs);
2364 propagateBinaryOperation(QSOperator::Op::NotEqual, lhs);
2365}
2366
2368{
2369 recordCompareType(lhs);
2370 propagateBinaryOperation(QSOperator::Op::Gt, lhs);
2371}
2372
2374{
2375 recordCompareType(lhs);
2376 propagateBinaryOperation(QSOperator::Op::Ge, lhs);
2377}
2378
2380{
2381 recordCompareType(lhs);
2382 propagateBinaryOperation(QSOperator::Op::Lt, lhs);
2383}
2384
2386{
2387 recordCompareType(lhs);
2388 propagateBinaryOperation(QSOperator::Op::Le, lhs);
2389}
2390
2392{
2393 recordEqualsType(lhs);
2394 propagateBinaryOperation(QSOperator::Op::StrictEqual, lhs);
2395}
2396
2398{
2399 recordEqualsType(lhs);
2400 propagateBinaryOperation(QSOperator::Op::StrictNotEqual, lhs);
2401}
2402
2404{
2405 // TODO: Most of the time we don't need the object at all, but only its metatype.
2406 // Fix this when we add support for the "in" instruction to the code generator.
2407 // Also, specialize on lhs to avoid conversion to QJSPrimitiveValue.
2408
2409 addReadRegister(lhs, m_typeResolver->globalType(m_typeResolver->jsValueType()));
2410 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
2411
2412 propagateBinaryOperation(QSOperator::Op::In, lhs);
2413}
2414
2420
2422{
2423 const QQmlJSRegisterContent input = checkedInputRegister(lhs);
2425
2426 QQmlJSScope::ConstPtr outContained;
2427
2428 switch (m_state.accumulatorIn().variant()) {
2430 outContained = m_state.accumulatorIn().scopeType();
2431 break;
2433 outContained = m_state.accumulatorIn().scopeType();
2434 if (outContained->isComposite()) // Otherwise we don't need it
2435 addReadAccumulator(m_typeResolver->globalType(m_typeResolver->metaObjectType()));
2436 break;
2437 default:
2438 outContained = m_typeResolver->containedType(m_state.accumulatorIn());
2439 break;
2440 }
2441
2443
2444 if (outContained->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
2445 // A referece type cast can result in either the type or null.
2446 // Reference types can hold null. We don't need to special case that.
2447
2448 if (m_typeResolver->inherits(inContained, outContained))
2449 output = input;
2450 else
2451 output = m_typeResolver->cast(input, outContained);
2452 } else if (!m_typeResolver->canAddressValueTypes()) {
2453 setError(u"invalid cast from %1 to %2. You can only cast object types."_s
2454 .arg(input.descriptiveName(), m_state.accumulatorIn().descriptiveName()));
2455 return;
2456 } else {
2457 if (m_typeResolver->inherits(inContained, outContained)) {
2458 // A "slicing" cannot result in void
2459 output = m_typeResolver->cast(input, outContained);
2460 } else {
2461 // A value type cast can result in either the type or undefined.
2462 // Using convert() retains the variant of the input type.
2464 m_typeResolver->cast(input, outContained),
2466 }
2467 }
2468
2469 addReadRegister(lhs, input);
2470 setAccumulator(output);
2471}
2472
2473void QQmlJSTypePropagator::checkConversion(
2474 const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to)
2475{
2476 if (!canConvertFromTo(from, to)) {
2477 setError(u"cannot convert from %1 to %2"_s
2478 .arg(from.descriptiveName(), to.descriptiveName()));
2479 }
2480}
2481
2482void QQmlJSTypePropagator::generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator op)
2483{
2485 op, m_state.accumulatorIn());
2486 checkConversion(m_state.accumulatorIn(), type);
2487 addReadAccumulator(type);
2488 setAccumulator(type);
2489}
2490
2492{
2493 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Not);
2494}
2495
2497{
2498 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Plus);
2499}
2500
2502{
2503 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Minus);
2504}
2505
2507{
2508 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Complement);
2509}
2510
2512{
2513 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Increment);
2514}
2515
2517{
2518 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Decrement);
2519}
2520
2521void QQmlJSTypePropagator::generateBinaryArithmeticOperation(QSOperator::Op op, int lhs)
2522{
2523 const QQmlJSRegisterContent type = propagateBinaryOperation(op, lhs);
2524
2525 checkConversion(checkedInputRegister(lhs), type);
2526 addReadRegister(lhs, type);
2527
2528 checkConversion(m_state.accumulatorIn(), type);
2529 addReadAccumulator(type);
2530}
2531
2532void QQmlJSTypePropagator::generateBinaryConstArithmeticOperation(QSOperator::Op op)
2533{
2535 op, m_state.accumulatorIn(),
2537
2538 checkConversion(m_state.accumulatorIn(), type);
2539 addReadAccumulator(type);
2540 setAccumulator(type);
2541}
2542
2544{
2545 generateBinaryArithmeticOperation(QSOperator::Op::Add, lhs);
2546}
2547
2549{
2550 generateBinaryArithmeticOperation(QSOperator::Op::BitAnd, lhs);
2551}
2552
2554{
2555 generateBinaryArithmeticOperation(QSOperator::Op::BitOr, lhs);
2556}
2557
2559{
2560 generateBinaryArithmeticOperation(QSOperator::Op::BitXor, lhs);
2561}
2562
2564{
2565 generateBinaryArithmeticOperation(QSOperator::Op::URShift, lhs);
2566}
2567
2569{
2570 generateBinaryArithmeticOperation(QSOperator::Op::RShift, lhs);
2571}
2572
2574{
2575 generateBinaryArithmeticOperation(QSOperator::Op::LShift, lhs);
2576}
2577
2579{
2580 Q_UNUSED(rhsConst)
2581 generateBinaryConstArithmeticOperation(QSOperator::Op::BitAnd);
2582}
2583
2585{
2586 Q_UNUSED(rhsConst)
2587 generateBinaryConstArithmeticOperation(QSOperator::Op::BitOr);
2588}
2589
2591{
2592 Q_UNUSED(rhsConst)
2593 generateBinaryConstArithmeticOperation(QSOperator::Op::BitXor);
2594}
2595
2597{
2598 Q_UNUSED(rhsConst)
2599 generateBinaryConstArithmeticOperation(QSOperator::Op::URShift);
2600}
2601
2603{
2604 Q_UNUSED(rhsConst)
2605 generateBinaryConstArithmeticOperation(QSOperator::Op::RShift);
2606}
2607
2609{
2610 Q_UNUSED(rhsConst)
2611 generateBinaryConstArithmeticOperation(QSOperator::Op::LShift);
2612}
2613
2615{
2616 generateBinaryArithmeticOperation(QSOperator::Op::Exp, lhs);
2617}
2618
2620{
2621 generateBinaryArithmeticOperation(QSOperator::Op::Mul, lhs);
2622}
2623
2625{
2626 generateBinaryArithmeticOperation(QSOperator::Op::Div, lhs);
2627}
2628
2630{
2631 generateBinaryArithmeticOperation(QSOperator::Op::Mod, lhs);
2632}
2633
2635{
2636 generateBinaryArithmeticOperation(QSOperator::Op::Sub, lhs);
2637}
2638
2640{
2641 for (int reg = firstReg, end = firstReg + count; reg < end; ++reg)
2642 setRegister(reg, m_typeResolver->globalType(m_typeResolver->emptyType()));
2643}
2644
2649
2655
2658{
2659 if (m_error->isValid())
2660 return SkipInstruction;
2661
2662 if (m_state.jumpTargets.contains(currentInstructionOffset())) {
2663 if (m_state.skipInstructionsUntilNextJumpTarget) {
2664 // When re-surfacing from dead code, all registers are invalid.
2665 m_state.registers.clear();
2666 m_state.skipInstructionsUntilNextJumpTarget = false;
2667 }
2668 } else if (m_state.skipInstructionsUntilNextJumpTarget
2670 return SkipInstruction;
2671 }
2672
2673 const int currentOffset = currentInstructionOffset();
2674
2675 // If we reach an instruction that is a target of a jump earlier, then we must check that the
2676 // register state at the origin matches the current state. If not, then we may have to inject
2677 // conversion code (communicated to code gen via m_state.typeConversions). For
2678 // example:
2679 //
2680 // function blah(x: number) { return x > 10 ? 10 : x}
2681 //
2682 // translates to a situation where in the "true" case, we load an integer into the accumulator
2683 // and in the else case a number (x). When the control flow is joined, the types don't match and
2684 // we need to make sure that the int is converted to a double just before the jump.
2685 for (auto originRegisterStateIt =
2686 m_jumpOriginRegisterStateByTargetInstructionOffset.constFind(currentOffset);
2687 originRegisterStateIt != m_jumpOriginRegisterStateByTargetInstructionOffset.constEnd()
2688 && originRegisterStateIt.key() == currentOffset;
2689 ++originRegisterStateIt) {
2690 auto stateToMerge = *originRegisterStateIt;
2691 for (auto registerIt = stateToMerge.registers.constBegin(),
2692 end = stateToMerge.registers.constEnd();
2693 registerIt != end; ++registerIt) {
2694 const int registerIndex = registerIt.key();
2695
2696 auto newType = registerIt.value().content;
2697 if (!newType.isValid()) {
2698 setError(u"When reached from offset %1, %2 is undefined"_s
2699 .arg(stateToMerge.originatingOffset)
2700 .arg(registerName(registerIndex)));
2701 return SkipInstruction;
2702 }
2703
2704 auto currentRegister = m_state.registers.find(registerIndex);
2705 if (currentRegister != m_state.registers.end())
2706 mergeRegister(registerIndex, newType, currentRegister.value().content);
2707 else
2708 mergeRegister(registerIndex, newType, newType);
2709 }
2710 }
2711
2712 return ProcessInstruction;
2713}
2714
2716{
2717 InstructionAnnotation &currentInstruction = m_state.annotations[currentInstructionOffset()];
2718 currentInstruction.changedRegister = m_state.changedRegister();
2719 currentInstruction.changedRegisterIndex = m_state.changedRegisterIndex();
2720 currentInstruction.readRegisters = m_state.takeReadRegisters();
2721 currentInstruction.hasSideEffects = m_state.hasSideEffects();
2722 currentInstruction.isRename = m_state.isRename();
2723
2724 switch (instr) {
2725 // the following instructions are not expected to produce output in the accumulator
2726 case QV4::Moth::Instr::Type::Ret:
2727 case QV4::Moth::Instr::Type::Jump:
2728 case QV4::Moth::Instr::Type::JumpFalse:
2729 case QV4::Moth::Instr::Type::JumpTrue:
2730 case QV4::Moth::Instr::Type::StoreReg:
2731 case QV4::Moth::Instr::Type::StoreElement:
2732 case QV4::Moth::Instr::Type::StoreNameSloppy:
2733 case QV4::Moth::Instr::Type::StoreProperty:
2734 case QV4::Moth::Instr::Type::SetLookup:
2735 case QV4::Moth::Instr::Type::MoveConst:
2736 case QV4::Moth::Instr::Type::MoveReg:
2737 case QV4::Moth::Instr::Type::CheckException:
2738 case QV4::Moth::Instr::Type::CreateCallContext:
2739 case QV4::Moth::Instr::Type::PopContext:
2740 case QV4::Moth::Instr::Type::JumpNoException:
2741 case QV4::Moth::Instr::Type::ThrowException:
2742 case QV4::Moth::Instr::Type::SetUnwindHandler:
2743 case QV4::Moth::Instr::Type::PushCatchContext:
2744 case QV4::Moth::Instr::Type::UnwindDispatch:
2745 case QV4::Moth::Instr::Type::InitializeBlockDeadTemporalZone:
2746 case QV4::Moth::Instr::Type::ConvertThisToObject:
2747 case QV4::Moth::Instr::Type::DeadTemporalZoneCheck:
2748 case QV4::Moth::Instr::Type::IteratorNext:
2749 case QV4::Moth::Instr::Type::IteratorNextForYieldStar:
2750 if (m_state.changedRegisterIndex() == Accumulator && !m_error->isValid()) {
2751 setError(u"Instruction is not expected to populate the accumulator"_s);
2752 return;
2753 }
2754 break;
2755 default:
2756 // If the instruction is expected to produce output, save it in the register set
2757 // for the next instruction.
2758 if ((!m_state.changedRegister().isValid() || m_state.changedRegisterIndex() != Accumulator)
2759 && !m_error->isValid()) {
2760 setError(u"Instruction is expected to populate the accumulator"_s);
2761 return;
2762 }
2763 }
2764
2765 if (!(m_error->isValid() && m_error->isError())
2766 && instr != QV4::Moth::Instr::Type::DeadTemporalZoneCheck) {
2767 // An instruction needs to have side effects or write to another register otherwise it's a
2768 // noop. DeadTemporalZoneCheck is not needed by the compiler and is ignored.
2769 Q_ASSERT(m_state.hasSideEffects() || m_state.changedRegisterIndex() != -1);
2770 }
2771
2772 if (m_state.changedRegisterIndex() != InvalidRegister) {
2773 Q_ASSERT(m_error->isValid() || m_state.changedRegister().isValid());
2774 VirtualRegister &r = m_state.registers[m_state.changedRegisterIndex()];
2775 r.content = m_state.changedRegister();
2776 r.canMove = false;
2777 r.affectedBySideEffects = m_state.isRename()
2779 m_state.clearChangedRegister();
2780 }
2781
2782 m_state.setHasSideEffects(false);
2783 m_state.setIsRename(false);
2785}
2786
2787QQmlJSRegisterContent QQmlJSTypePropagator::propagateBinaryOperation(QSOperator::Op op, int lhs)
2788{
2789 auto lhsRegister = checkedInputRegister(lhs);
2790 if (!lhsRegister.isValid())
2791 return QQmlJSRegisterContent();
2792
2794 op, lhsRegister, m_state.accumulatorIn());
2795
2796 setAccumulator(type);
2797
2798 // If we're dealing with QJSPrimitiveType, do not force premature conversion of the arguemnts
2799 // to the target type. Such an operation can lose information.
2800 if (type.storedType() == m_typeResolver->jsPrimitiveType())
2802
2803 return type;
2804}
2805
2806void QQmlJSTypePropagator::saveRegisterStateForJump(int offset)
2807{
2808 auto jumpToOffset = offset + nextInstructionOffset();
2809 ExpectedRegisterState state;
2810 state.registers = m_state.registers;
2811 state.originatingOffset = currentInstructionOffset();
2812 m_state.jumpTargets.insert(jumpToOffset);
2813 if (offset < 0) {
2814 // We're jumping backwards. We won't get to merge the register states in this pass anymore.
2815
2816 const auto registerStates =
2817 m_jumpOriginRegisterStateByTargetInstructionOffset.equal_range(jumpToOffset);
2818 for (auto it = registerStates.first; it != registerStates.second; ++it) {
2819 if (it->registers.keys() == state.registers.keys()
2820 && it->registers.values() == state.registers.values()) {
2821 return; // We've seen the same register state before. No need for merging.
2822 }
2823 }
2824
2825 // The register state at the target offset needs to be resolved in a further pass.
2826 m_state.needsMorePasses = true;
2827 }
2828 m_jumpOriginRegisterStateByTargetInstructionOffset.insert(jumpToOffset, state);
2829}
2830
2831QString QQmlJSTypePropagator::registerName(int registerIndex) const
2832{
2833 if (registerIndex == Accumulator)
2834 return u"accumulator"_s;
2835 if (registerIndex >= FirstArgument
2836 && registerIndex < FirstArgument + m_function->argumentTypes.size()) {
2837 return u"argument %1"_s.arg(registerIndex - FirstArgument);
2838 }
2839
2840 return u"temporary register %1"_s.arg(
2841 registerIndex - FirstArgument - m_function->argumentTypes.size());
2842}
2843
2844QQmlJSRegisterContent QQmlJSTypePropagator::checkedInputRegister(int reg)
2845{
2846 const auto regIt = m_state.registers.find(reg);
2847 if (regIt == m_state.registers.end()) {
2848 if (isArgument(reg))
2849 return argumentType(reg);
2850
2851 setError(u"Type error: could not infer the type of an expression"_s);
2852 return {};
2853 }
2854 return regIt.value().content;
2855}
2856
2857bool QQmlJSTypePropagator::canConvertFromTo(const QQmlJSRegisterContent &from,
2858 const QQmlJSRegisterContent &to)
2859{
2860 return m_typeResolver->canConvertFromTo(from, to);
2861}
2862
static JNINativeMethod methods[]
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
iterator end()
Definition qflatmap_p.h:773
bool contains(const Key &key) const
Definition qflatmap_p.h:625
iterator find(const Key &key)
Definition qflatmap_p.h:816
void clear()
Definition qflatmap_p.h:591
QList< Key > keys() const
Returns a list containing all the keys in the hash, in an arbitrary order.
Definition qhash.h:1086
qsizetype size() const noexcept
Definition qlist.h:397
std::pair< iterator, iterator > equal_range(const Key &key)
Definition qhash.h:2266
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:1925
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:2034
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:2025
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
InstructionAnnotations m_annotations
State initialState(const Function *function)
QFlatMap< int, VirtualRegister > VirtualRegisters
void setError(const QString &message, int instructionOffset)
QQmlJSRegisterContent argumentType(int registerIndex) const
void setHint(const QString &hint)
void setAutoApplicable(bool autoApply=true)
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())
void setPropertyName(const QString &propertyName)
QSharedPointer< const QQmlJSScope > type() const
void setTypeName(const QString &typeName)
void setType(const QSharedPointer< const QQmlJSScope > &type)
QQmlJSMetaProperty property() const
QQmlJSScope::ConstPtr scopeType() const
ContentVariant variant() const
QQmlJSScope::ConstPtr storedType() const
static QQmlJSRegisterContent create(const QQmlJSScope::ConstPtr &storedType, const QQmlJSScope::ConstPtr &type, int resultLookupIndex, ContentVariant variant, const QQmlJSScope::ConstPtr &scope={})
QList< QQmlJSMetaMethod > method() const
QHash< QString, QQmlJSMetaProperty > properties() const
Returns all properties visible from this scope including those of base types and extensions.
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
QQmlJSScope::ConstPtr baseType() const
static QQmlJSScope::ConstPtr findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope)
QQmlJS::SourceLocation sourceLocation() const
QQmlJSScope::ConstPtr valueType() const
bool componentsAreBound() const
bool existsAnywhereInDocument(const QString &id) const
QString id(const QQmlJSScope::ConstPtr &scope, const QQmlJSScope::ConstPtr &referrer, QQmlJSScopesByIdOptions options=Default) 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
QQmlJSScope::ConstPtr dateTimeType() const
QQmlJSScope::ConstPtr stringType() const
const QHash< QQmlJS::SourceLocation, QQmlJSMetaSignalHandler > & signalHandlers() const
QQmlJSRegisterContent cast(const QQmlJSRegisterContent &from, const QQmlJSScope::ConstPtr &to) const
QQmlJSScope::ConstPtr nullType() const
QQmlJSScope::ConstPtr timeType() const
QQmlJSRegisterContent tracked(const QQmlJSRegisterContent &type) const
QQmlJSScope::ConstPtr mathObject() 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 consoleObject() 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 inherits(const QQmlJSScope::ConstPtr &derived, const QQmlJSScope::ConstPtr &base) 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
QQmlJSScope::ConstPtr sizeType() const
QQmlJSRegisterContent valueType(const QQmlJSRegisterContent &list) const
QQmlJSScope::ConstPtr uint32Type() const
QQmlJSScope::ConstPtr extractNonVoidFromOptionalType(const QQmlJSRegisterContent &content) const
QQmlJSScope::ConstPtr int32Type() const
QQmlJSScope::ConstPtr variantListType() const
QQmlJSRegisterContent memberType(const QQmlJSRegisterContent &type, const QString &name, int lookupIndex=QQmlJSRegisterContent::InvalidLookupIndex) const
QQmlJSScope::ConstPtr voidType() const
QQmlJSRegisterContent iteratorPointer(const QQmlJSRegisterContent &listType, QQmlJS::AST::ForEachType type, int lookupIndex) const
QQmlJSScope::ConstPtr metaObjectType() const
QQmlJSScope::ConstPtr realType() const
bool isOptionalType(const QQmlJSRegisterContent &content) const
bool isUnsignedInteger(const QQmlJSScope::ConstPtr &type) const
QQmlJSScope::ConstPtr dateType() 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, int lookupIndex=QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSScopesByIdOptions options=Default) 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
static PassManagerPrivate * get(PassManager *manager)
Definition qqmlsa_p.h:193
\inmodule QtQmlCompiler
Definition qqmlsa.h:303
static QQmlSA::SourceLocation createQQmlSASourceLocation(const QQmlJS::SourceLocation &jsLocation)
QList< T > values() const
Definition qset.h:304
iterator end()
Definition qset.h:140
iterator find(const T &value)
Definition qset.h:159
bool contains(const T &value) const
Definition qset.h:71
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
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 QQmlSA::LoggerWarningId qmlRestrictedType
const QQmlSA::LoggerWarningId qmlReadOnlyProperty
const QQmlSA::LoggerWarningId qmlUseProperFunction
const QQmlSA::LoggerWarningId qmlMissingProperty
const QQmlSA::LoggerWarningId qmlUnqualified
const QQmlSA::LoggerWarningId qmlMissingEnumEntry
const QQmlSA::LoggerWarningId qmlAccessSingleton
const QQmlSA::LoggerWarningId qmlUnresolvedType
const QQmlSA::LoggerWarningId qmlMissingType
const QQmlSA::LoggerWarningId qmlIncompatibleType
const QQmlSA::LoggerWarningId qmlDeprecated
#define INSTR_PROLOGUE_NOT_IMPLEMENTED()
#define INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE()
static bool isLoggingMethod(const QString &consoleMethod)
bool canStrictlyCompareWithVar(const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType, const QQmlJSScope::ConstPtr &rhsType)
bool canCompareWithQObject(const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType, const QQmlJSScope::ConstPtr &rhsType)
bool canCompareWithQUrl(const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType, const QQmlJSScope::ConstPtr &rhsType)
#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:165
unsigned int uint
Definition qtypes.h:34
static const uint base
Definition qurlidna.cpp:20
#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
QQmlJSRegisterContent returnType
const QQmlJSRegisterContent & accumulatorIn() const
The accumulatorIn is the input register of the current instruction.
void addReadAccumulator(const QQmlJSRegisterContent &reg)
VirtualRegisters takeReadRegisters() const
bool isRegisterAffectedBySideEffects(int registerIndex) const
void setReadRegisters(VirtualRegisters readReagisters)
const QQmlJSRegisterContent & changedRegister() const
void setIsRename(bool isRename)
void setRegister(int registerIndex, QQmlJSRegisterContent content)
void addReadRegister(int registerIndex, const QQmlJSRegisterContent &reg)
void setHasSideEffects(bool hasSideEffects)
const QQmlJSRegisterContent & accumulatorOut() const
The accumulatorOut is the output register of the current instruction.
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
bool checkForEnumProblems(const QQmlJSRegisterContent &base, const QString &propertyName)
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_IteratorClose() 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_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
BlocksAndAnnotations run(const Function *m_function, QQmlJS::DiagnosticMessage *error)
void generate_IteratorNextForYieldStar(int iterator, int object, int offset) override
Verdict startInstruction(QV4::Moth::Instr::Type instr) override
void generate_PushCatchContext(int index, int name) override
void generate_CmpGt(int lhs) override
QQmlJSTypePropagator(const QV4::Compiler::JSUnitGenerator *unitGenerator, const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger, BasicBlocks basicBlocks={}, InstructionAnnotations annotations={}, QQmlSA::PassManager *passManager=nullptr)
void generate_IteratorNext(int value, int offset) override
void generate_Add(int lhs) override
void generate_LoadSuperConstructor() override
void generate_CreateMappedArgumentsObject() override
void generate_ThrowOnNullOrUndefined() override
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