11#include <private/qv4compilerscanfunctions_p.h>
13#include <QtQmlCompiler/private/qqmlsasourcelocation_p.h>
17using namespace Qt::StringLiterals;
20
21
22
23
24
25
26
27
28
30QQmlJSTypePropagator::QQmlJSTypePropagator(
const QV4::Compiler::JSUnitGenerator *unitGenerator,
31 const QQmlJSTypeResolver *typeResolver,
32 QQmlJSLogger *logger,
const BasicBlocks &basicBlocks,
33 const InstructionAnnotations &annotations,
34 QQmlSA::PassManager *passManager,
35 const ContextPropertyInfo &contextPropertyInfo)
36 : QQmlJSCompilePass(unitGenerator, typeResolver, logger, basicBlocks, annotations),
37 m_passManager(passManager),
38 m_contextPropertyInfo(contextPropertyInfo)
42QQmlJSCompilePass::BlocksAndAnnotations QQmlJSTypePropagator::run(
const Function *function)
44 m_function = function;
45 m_returnType = m_function->returnType;
48 if (m_returnType.containedType() == m_typeResolver->qQmlScriptStringType())
53 if (m_state.needsMorePasses)
56 m_logger->startTransaction();
58 m_prevStateAnnotations = m_state.annotations;
59 m_state = PassState();
60 m_state.annotations = m_annotations;
61 m_state.State::operator=(initialState(m_function));
64 decode(m_function->code.constData(),
static_cast<uint>(m_function->code.size()));
69 }
while (m_state.needsMorePasses);
72 return { std::move(m_basicBlocks), std::move(m_state.annotations) };
75#define INSTR_PROLOGUE_NOT_IMPLEMENTED()
76 addError(u"Instruction \"%1\" not implemented"_s
.arg(QString::fromUtf8(__func__)));
79#define INSTR_PROLOGUE_NOT_IMPLEMENTED_POPULATES_ACC()
80 addError(u"Instruction \"%1\" not implemented"_s
.arg(QString::fromUtf8(__func__)));
81 setVarAccumulatorAndError();
84#define INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE()
85 m_logger->log(u"Instruction \"%1\" not implemented"_s
.arg(QString::fromUtf8(__func__)),
86 qmlCompiler, QQmlJS::SourceLocation());
89void QQmlJSTypePropagator::generate_ret_SAcheck()
91 const QQmlJS::SourceLocation location = m_function->isProperty
92 ? currentFunctionSourceLocation()
93 : currentNonEmptySourceLocation();
94 QQmlSA::PassManagerPrivate::get(m_passManager)
96 QQmlJSScope::createQQmlSAElement(m_function->qmlScope.containedType()),
97 QQmlJSScope::createQQmlSAElement(m_state.accumulatorIn().containedType()),
98 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(location));
100void QQmlJSTypePropagator::generate_Ret()
102 if (m_passManager !=
nullptr)
103 generate_ret_SAcheck();
105 if (m_function->isSignalHandler) {
107 }
else if (m_state.accumulatorIn().contains(m_typeResolver->voidType())) {
109 }
else if (!m_returnType.isValid() && m_state.accumulatorIn().isValid()) {
110 addError(u"function without return type annotation returns %1. This may prevent proper "_s
111 u"compilation to Cpp."_s.arg(m_state.accumulatorIn().descriptiveName()));
113 if (m_function->isFullyTyped) {
115 m_logger->log(u"Function without return type annotation returns %1"_s.arg(
116 m_state.accumulatorIn().containedTypeName()),
117 qmlIncompatibleType, currentFunctionSourceLocation());
120 }
else if (!canConvertFromTo(m_state.accumulatorIn(), m_returnType)) {
121 addError(u"cannot convert from %1 to %2"_s
122 .arg(m_state.accumulatorIn().descriptiveName(),
123 m_returnType.descriptiveName()));
125 m_logger->log(u"Cannot assign binding of type %1 to %2"_s.arg(
126 m_state.accumulatorIn().containedTypeName(),
127 m_returnType.containedTypeName()),
128 qmlIncompatibleType, currentFunctionSourceLocation());
132 if (m_returnType.isValid()) {
134 if (m_typeResolver->canHoldUndefined(m_state.accumulatorIn()))
135 addReadAccumulator();
137 addReadAccumulator(m_returnType);
140 m_state.setHasInternalSideEffects();
141 m_state.skipInstructionsUntilNextJumpTarget =
true;
144void QQmlJSTypePropagator::generate_Debug()
149void QQmlJSTypePropagator::generate_LoadConst(
int index)
151 auto encodedConst = m_jsUnitGenerator->constant(index);
152 setAccumulator(m_typeResolver->literalType(m_typeResolver->typeForConst(encodedConst)));
155void QQmlJSTypePropagator::generate_LoadZero()
157 setAccumulator(m_typeResolver->literalType(m_typeResolver->int32Type()));
160void QQmlJSTypePropagator::generate_LoadTrue()
162 setAccumulator(m_typeResolver->literalType(m_typeResolver->boolType()));
165void QQmlJSTypePropagator::generate_LoadFalse()
167 setAccumulator(m_typeResolver->literalType(m_typeResolver->boolType()));
170void QQmlJSTypePropagator::generate_LoadNull()
172 setAccumulator(m_typeResolver->literalType(m_typeResolver->nullType()));
175void QQmlJSTypePropagator::generate_LoadUndefined()
177 setAccumulator(m_typeResolver->literalType(m_typeResolver->voidType()));
180void QQmlJSTypePropagator::generate_LoadInt(
int)
182 setAccumulator(m_typeResolver->literalType(m_typeResolver->int32Type()));
185void QQmlJSTypePropagator::generate_MoveConst(
int constIndex,
int destTemp)
187 auto encodedConst = m_jsUnitGenerator->constant(constIndex);
188 setRegister(destTemp, m_typeResolver->literalType(m_typeResolver->typeForConst(encodedConst)));
191void QQmlJSTypePropagator::generate_LoadReg(
int reg)
194 m_state.setIsRename(
true);
195 const QQmlJSRegisterContent content = checkedInputRegister(reg);
196 m_state.addReadRegister(reg, content);
197 m_state.setRegister(Accumulator, content);
200void QQmlJSTypePropagator::generate_StoreReg(
int reg)
203 m_state.setIsRename(
true);
204 m_state.addReadAccumulator(m_state.accumulatorIn());
205 m_state.setRegister(reg, m_state.accumulatorIn());
208void QQmlJSTypePropagator::generate_MoveReg(
int srcReg,
int destReg)
210 Q_ASSERT(destReg != InvalidRegister);
212 m_state.setIsRename(
true);
213 const QQmlJSRegisterContent content = checkedInputRegister(srcReg);
214 m_state.addReadRegister(srcReg, content);
215 m_state.setRegister(destReg, content);
218void QQmlJSTypePropagator::generate_LoadImport(
int index)
224void QQmlJSTypePropagator::generate_LoadLocal(
int index)
230 QQmlJSMetaProperty local;
231 local.setType(m_typeResolver->jsValueType());
232 local.setIndex(index);
234 setAccumulator(m_pool->createProperty(
235 local, QQmlJSRegisterContent::InvalidLookupIndex,
236 QQmlJSRegisterContent::InvalidLookupIndex,
237 QQmlJSRegisterContent::Property, QQmlJSRegisterContent()));
240void QQmlJSTypePropagator::generate_StoreLocal(
int index)
246void QQmlJSTypePropagator::generate_LoadScopedLocal(
int scope,
int index)
253void QQmlJSTypePropagator::generate_StoreScopedLocal(
int scope,
int index)
260void QQmlJSTypePropagator::generate_LoadRuntimeString(
int stringId)
263 setAccumulator(m_typeResolver->literalType(m_typeResolver->stringType()));
266void QQmlJSTypePropagator::generate_MoveRegExp(
int regExpId,
int destReg)
269 m_state.setRegister(destReg, m_typeResolver->literalType(m_typeResolver->regexpType()));
272void QQmlJSTypePropagator::generate_LoadClosure(
int value)
277 setAccumulator(m_typeResolver->literalType(m_typeResolver->functionType()));
280void QQmlJSTypePropagator::generate_LoadName(
int nameIndex)
282 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
283 setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name));
284 if (!m_state.accumulatorOut().isValid()) {
285 addError(u"Cannot find name "_s + name);
286 setVarAccumulatorAndError();
290void QQmlJSTypePropagator::generate_LoadGlobalLookup(
int index)
292 generate_LoadName(m_jsUnitGenerator->lookupNameIndex(index));
295void QQmlJSTypePropagator::handleUnqualifiedAccess(
const QString &name,
bool isMethod)
const
297 auto location = currentSourceLocation();
299 const auto qmlScopeContained = m_function->qmlScope.containedType();
300 if (qmlScopeContained->isInCustomParserParent()) {
302 if (qmlScopeContained->baseType().isNull()
303 || qmlScopeContained->baseType()->internalName() != u"QQmlConnections"_s)
308 if (isCallingProperty(qmlScopeContained, name))
310 }
else if (propertyResolution(qmlScopeContained, name) != PropertyMissing) {
314 std::optional<QQmlJSFixSuggestion> suggestion;
316 const auto childScopes = m_function->qmlScope.containedType()->childScopes();
317 for (qsizetype i = 0, end = childScopes.size(); i < end; i++) {
318 auto &scope = childScopes[i];
319 if (location.offset > scope->sourceLocation().offset) {
321 && childScopes.at(i + 1)->sourceLocation().offset < location.offset)
323 if (scope->childScopes().size() == 0)
326 const auto jsId = scope->childScopes().first()->jsIdentifier(name);
328 if (jsId.has_value() && jsId->kind == QQmlJSScope::JavaScriptIdentifier::Injected) {
329 const QQmlJSScope::JavaScriptIdentifier id = jsId.value();
331 QQmlJS::SourceLocation fixLocation = id.location;
332 Q_UNUSED(fixLocation)
333 fixLocation.length = 0;
335 const auto handler = m_typeResolver->signalHandlers()[id.location];
337 QString fixString = handler.isMultiline ? u"function("_s : u"("_s;
338 const auto parameters = handler.signalParameters;
339 for (
int numParams = parameters.size(); numParams > 0; --numParams) {
340 fixString += parameters.at(parameters.size() - numParams);
342 fixString += u", "_s;
345 fixString += handler.isMultiline ? u") "_s : u") => "_s;
347 suggestion = QQmlJSFixSuggestion {
348 name + u" is accessible in this scope because you are handling a signal"
349 " at %1:%2. Use a function instead.\n"_s
350 .arg(id.location.startLine)
351 .arg(id.location.startColumn),
355 suggestion->setAutoApplicable();
365 const auto qmlScope = m_function->qmlScope.containedType();
366 if (name == u"model" || name == u"index") {
367 if (
const QQmlJSScope::ConstPtr parent = qmlScope->parentScope(); !parent.isNull()) {
368 const auto bindings = parent->ownPropertyBindings(u"delegate"_s);
370 for (
auto it = bindings.first; it != bindings.second; it++) {
371 if (!it->hasObject())
373 if (it->objectType() == qmlScope) {
374 suggestion = QQmlJSFixSuggestion {
375 name +
" is implicitly injected into this delegate."
376 " Add a required property instead."_L1,
377 qmlScope->sourceLocation()
386 if (!suggestion.has_value()) {
387 for (QQmlJSScope::ConstPtr scope = qmlScope; !scope.isNull(); scope = scope->parentScope()) {
388 if (scope->hasProperty(name)) {
389 QQmlJSScopesById::MostLikelyCallback<QString> id;
390 m_function->addressableScopes.possibleIds(scope, qmlScope, Default, id);
392 QQmlJS::SourceLocation fixLocation = location;
393 fixLocation.length = 0;
394 suggestion = QQmlJSFixSuggestion{
396 +
" is a member of a parent element.\n You can qualify the access "
397 "with its id to avoid this warning.\n"_L1,
398 fixLocation, (id.result.isEmpty() ? u"<id>."_s : (id.result + u'.'))
401 if (id.result.isEmpty())
402 suggestion->setHint(
"You first have to give the element an id"_L1);
404 suggestion->setAutoApplicable();
409 if (!suggestion.has_value() && !m_function->addressableScopes.componentsAreBound()
410 && m_function->addressableScopes.existsAnywhereInDocument(name)) {
411 const QLatin1String replacement =
"pragma ComponentBehavior: Bound"_L1;
412 QQmlJSFixSuggestion bindComponents {
413 "Set \"%1\" in order to use IDs from outer components in nested components."_L1
415 QQmlJS::SourceLocation(0, 0, 1, 1),
416 replacement +
'\n'_L1
418 bindComponents.setAutoApplicable();
419 suggestion = bindComponents;
422 if (!suggestion.has_value()) {
423 if (
auto didYouMean =
424 QQmlJSUtils::didYouMean(
425 name, qmlScope->properties().keys() + qmlScope->methods().keys(),
427 didYouMean.has_value()) {
428 suggestion = didYouMean;
432 m_logger->log(QLatin1String(
"Unqualified access"), qmlUnqualified, location,
true,
true,
436void QQmlJSTypePropagator::checkDeprecated(QQmlJSScope::ConstPtr scope,
const QString &name,
439 Q_ASSERT(!scope.isNull());
440 auto qmlScope = QQmlJSScope::findCurrentQMLScope(scope);
441 if (qmlScope.isNull())
444 QList<QQmlJSAnnotation> annotations;
446 QQmlJSMetaMethod method;
449 const QVector<QQmlJSMetaMethod> methods = qmlScope->methods(name);
450 if (methods.isEmpty())
452 method = methods.constFirst();
453 annotations = method.annotations();
455 QQmlJSMetaProperty property = qmlScope->property(name);
456 if (!property.isValid())
458 annotations = property.annotations();
461 auto deprecationAnn = std::find_if(
462 annotations.constBegin(), annotations.constEnd(),
463 [](
const QQmlJSAnnotation &annotation) {
return annotation.isDeprecation(); });
465 if (deprecationAnn == annotations.constEnd())
468 QQQmlJSDeprecation deprecation = deprecationAnn->deprecation();
470 QString descriptor = name;
472 descriptor += u'(' + method.parameterNames().join(u", "_s) + u')';
474 QString message = QStringLiteral(
"%1 \"%2\" is deprecated")
475 .arg(isMethod ? u"Method"_s : u"Property"_s)
478 if (!deprecation.reason.isEmpty())
479 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
481 m_logger->log(message, qmlDeprecated, currentSourceLocation());
485QQmlJSTypePropagator::PropertyResolution QQmlJSTypePropagator::propertyResolution(
486 QQmlJSScope::ConstPtr scope,
const QString &propertyName)
const
488 auto property = scope->property(propertyName);
489 if (!property.isValid())
490 return PropertyMissing;
493 if (property.type().isNull())
494 errorType = u"found"_s;
495 else if (!property.type()->isFullyResolved())
496 errorType = u"fully resolved"_s;
498 return PropertyFullyResolved;
500 Q_ASSERT(!errorType.isEmpty());
503 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
504 .arg(property.typeName(), propertyName, errorType),
505 qmlUnresolvedType, currentSourceLocation());
507 return PropertyTypeUnresolved;
510bool QQmlJSTypePropagator::isCallingProperty(QQmlJSScope::ConstPtr scope,
const QString &name)
const
512 auto property = scope->property(name);
513 if (!property.isValid())
516 QString propertyType = u"Property"_s;
518 auto methods = scope->methods(name);
521 if (!methods.isEmpty()) {
522 errorType = u"shadowed by a property."_s;
523 switch (methods.first().methodType()) {
524 case QQmlJSMetaMethodType::Signal:
525 propertyType = u"Signal"_s;
527 case QQmlJSMetaMethodType::Slot:
528 propertyType = u"Slot"_s;
530 case QQmlJSMetaMethodType::Method:
531 propertyType = u"Method"_s;
536 }
else if (property.type() == m_typeResolver->varType()) {
538 u"a var property. It may or may not be a method. Use a regular function instead."_s;
539 }
else if (property.type() == m_typeResolver->jsValueType()) {
541 u"a QJSValue property. It may or may not be a method. Use a regular Q_INVOKABLE instead."_s;
543 errorType = u"not a method"_s;
546 m_logger->log(u"%1 \"%2\" is %3"_s.arg(propertyType, name, errorType), qmlUseProperFunction,
547 currentSourceLocation(),
true,
true, {});
553void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup_SAcheck(
const QString &name)
555 const auto qmlScope = m_function->qmlScope.containedType();
556 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
557 QQmlJSScope::createQQmlSAElement(qmlScope), name,
558 QQmlJSScope::createQQmlSAElement(qmlScope),
559 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
560 currentNonEmptySourceLocation()));
565 if (!qmlScope->isWrappedInImplicitComponent() && !qmlScope->isFileRootComponent()
566 && !qmlScope->isInlineComponent()) {
570 const auto properties = qmlScope->properties();
571 return std::none_of(properties.constBegin(), properties.constEnd(),
572 [&qmlScope](
const QQmlJSMetaProperty &property) {
573 return qmlScope->isPropertyRequired(property.propertyName());
577void QQmlJSTypePropagator::handleUnqualifiedAccessAndContextProperties(
const QString &name,
580 if (m_contextPropertyInfo.userContextProperties.isUnqualifiedAccessDisabled(name))
583 const auto warningMessage = [&name,
this]() {
585 "Potential context property access detected."
586 " Context properties are discouraged in QML: use normal, required, or singleton properties instead."_L1;
588 if (shouldMentionRequiredProperties(m_function->qmlScope.containedType())) {
590 "\nNote: '%1' assumed to be a potential context property because it is not declared as required property."_L1
596 if (m_contextPropertyInfo.userContextProperties.isOnUsageWarned(name)) {
597 m_logger->log(warningMessage(), qmlContextProperties, currentSourceLocation());
602 handleUnqualifiedAccess(name, isMethod);
604 const QList<QQmlJS::HeuristicContextProperty> definitions =
605 m_contextPropertyInfo.heuristicContextProperties.definitionsForName(name);
606 if (definitions.isEmpty())
608 QString warning = warningMessage();
609 for (
const auto &candidate : definitions) {
610 warning.append(
"\nNote: candidate context property declaration '%1' at %2:%3:%4"_L1.arg(
611 name, QDir::cleanPath(candidate.filename),
612 QString::number(candidate.location.startLine),
613 QString::number(candidate.location.startColumn)));
615 m_logger->log(warning, qmlContextProperties, currentSourceLocation());
618void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(
int index)
623 const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
624 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
626 setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name, index));
628 if (!m_state.accumulatorOut().isValid() && m_typeResolver->isPrefix(name)) {
629 setAccumulator(m_pool->createImportNamespace(
630 nameIndex, m_typeResolver->voidType(), QQmlJSRegisterContent::ModulePrefix,
631 m_function->qmlScope));
635 checkDeprecated(m_function->qmlScope.containedType(), name,
false);
637 const QQmlJSRegisterContent accumulatorOut = m_state.accumulatorOut();
639 if (!accumulatorOut.isValid()) {
640 addError(u"Cannot access value for name "_s + name);
641 handleUnqualifiedAccessAndContextProperties(name,
false);
642 setVarAccumulatorAndError();
646 const QQmlJSScope::ConstPtr retrieved
647 = m_typeResolver->genericType(accumulatorOut.containedType());
649 if (retrieved.isNull()) {
652 addError(u"Cannot determine generic type for "_s + name);
656 if (accumulatorOut.variant() == QQmlJSRegisterContent::ObjectById
657 && !retrieved->isReferenceType()) {
658 addError(u"Cannot retrieve a non-object type by ID: "_s + name);
662 if (m_passManager !=
nullptr)
663 generate_LoadQmlContextPropertyLookup_SAcheck(name);
666void QQmlJSTypePropagator::generate_StoreNameCommon_SAcheck(QQmlJSRegisterContent in,
const QString &name)
668 const auto qmlScope = m_function->qmlScope.containedType();
669 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeWrite(
670 QQmlJSScope::createQQmlSAElement(qmlScope), name,
671 QQmlJSScope::createQQmlSAElement(in.containedType()),
672 QQmlJSScope::createQQmlSAElement(qmlScope),
673 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
674 currentNonEmptySourceLocation()));
678
679
680
681
682
683
684
685
686
687
688void QQmlJSTypePropagator::generate_StoreNameCommon(
int nameIndex)
690 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
691 const QQmlJSRegisterContent type = m_typeResolver->scopedType(m_function->qmlScope, name);
692 const QQmlJSRegisterContent in = m_state.accumulatorIn();
694 if (!type.isValid()) {
695 handleUnqualifiedAccess(name,
false);
696 addError(u"Cannot find name "_s + name);
700 if (!type.isProperty()) {
701 QString message = type.isMethod() ? u"Cannot assign to method %1"_s
702 : u"Cannot assign to non-property %1"_s;
705 m_logger->log(message.arg(name), qmlReadOnlyProperty,
706 currentSourceLocation());
707 addError(u"Cannot assign to non-property "_s + name);
711 if (!type.isWritable()) {
712 addError(u"Can't assign to read-only property %1"_s.arg(name));
714 m_logger->log(u"Cannot assign to read-only property %1"_s.arg(name), qmlReadOnlyProperty,
715 currentSourceLocation());
720 if (!canConvertFromTo(in, type)) {
721 addError(u"cannot convert from %1 to %2"_s
722 .arg(in.descriptiveName(), type.descriptiveName()));
725 if (m_passManager !=
nullptr)
726 generate_StoreNameCommon_SAcheck(in, name);
729 if (m_typeResolver->canHoldUndefined(in) && !m_typeResolver->canHoldUndefined(type)) {
730 if (in.contains(m_typeResolver->voidType()))
731 addReadAccumulator(m_typeResolver->varType());
733 addReadAccumulator();
735 addReadAccumulator(type);
738 m_state.setHasExternalSideEffects();
741void QQmlJSTypePropagator::generate_StoreNameSloppy(
int nameIndex)
743 return generate_StoreNameCommon(nameIndex);
746void QQmlJSTypePropagator::generate_StoreNameStrict(
int name)
748 return generate_StoreNameCommon(name);
751bool QQmlJSTypePropagator::checkForEnumProblems(
752 QQmlJSRegisterContent base,
const QString &propertyName)
754 if (base.isEnumeration()) {
755 const auto metaEn = base.enumeration();
756 if (!metaEn.hasKey(propertyName)) {
757 auto fixSuggestion = QQmlJSUtils::didYouMean(propertyName, metaEn.keys(),
758 currentSourceLocation());
759 const QString error = u"\"%1\" is not an entry of enum \"%2\"."_s
760 .arg(propertyName, metaEn.name());
763 error, qmlMissingEnumEntry, currentSourceLocation(),
true,
true,
767 }
else if (base.variant() == QQmlJSRegisterContent::MetaType) {
768 const QQmlJSMetaEnum metaEn = base.scopeType()->enumeration(propertyName);
769 if (metaEn.isValid() && !metaEn.isScoped() && !metaEn.isQml()) {
771 = u"You cannot access unscoped enum \"%1\" from here."_s.arg(propertyName);
773 m_logger->log(error, qmlRestrictedType, currentSourceLocation());
781void QQmlJSTypePropagator::generate_LoadElement(
int base)
783 const QQmlJSRegisterContent in = m_state.accumulatorIn();
784 const QQmlJSRegisterContent baseRegister = m_state.registers[base].content;
786 const auto fallback = [&]() {
787 const QQmlJSScope::ConstPtr jsValue = m_typeResolver->jsValueType();
789 addReadAccumulator(jsValue);
790 addReadRegister(base, jsValue);
792 QQmlJSMetaProperty property;
793 property.setPropertyName(u"[]"_s);
794 property.setTypeName(jsValue->internalName());
795 property.setType(jsValue);
797 setAccumulator(m_pool->createProperty(
798 property, QQmlJSRegisterContent::InvalidLookupIndex,
799 QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListValue,
800 m_typeResolver->convert(m_typeResolver->elementType(baseRegister), jsValue)));
803 if (baseRegister.isList()) {
804 addReadRegister(base, m_typeResolver->arrayPrototype());
805 }
else if (baseRegister.contains(m_typeResolver->stringType())) {
806 addReadRegister(base, m_typeResolver->stringType());
812 if (m_typeResolver->isNumeric(in)) {
813 const auto contained = in.containedType();
814 if (m_typeResolver->isSignedInteger(contained))
815 addReadAccumulator(m_typeResolver->sizeType());
816 else if (m_typeResolver->isUnsignedInteger(contained))
817 addReadAccumulator(m_typeResolver->uint32Type());
819 addReadAccumulator(m_typeResolver->realType());
820 }
else if (m_typeResolver->isNumeric(m_typeResolver->extractNonVoidFromOptionalType(in))) {
821 addReadAccumulator();
828 setAccumulator(m_typeResolver->merge(
829 m_typeResolver->elementType(baseRegister),
830 m_typeResolver->literalType(m_typeResolver->voidType())));
833void QQmlJSTypePropagator::generate_StoreElement(
int base,
int index)
835 const QQmlJSRegisterContent baseRegister = m_state.registers[base].content;
836 const QQmlJSRegisterContent indexRegister = checkedInputRegister(index);
838 if (!baseRegister.isList()
839 || !m_typeResolver->isNumeric(indexRegister)) {
840 const auto jsValue = m_typeResolver->jsValueType();
841 addReadAccumulator(jsValue);
842 addReadRegister(base, jsValue);
843 addReadRegister(index, jsValue);
847 m_state.setHasExternalSideEffects();
851 const auto contained = indexRegister.containedType();
852 if (m_typeResolver->isSignedInteger(contained))
853 addReadRegister(index, m_typeResolver->int32Type());
854 else if (m_typeResolver->isUnsignedInteger(contained))
855 addReadRegister(index, m_typeResolver->uint32Type());
857 addReadRegister(index, m_typeResolver->realType());
859 addReadRegister(base, m_typeResolver->arrayPrototype());
860 addReadAccumulator(m_typeResolver->elementType(baseRegister));
867 m_state.setHasExternalSideEffects();
870void QQmlJSTypePropagator::propagatePropertyLookup_SAcheck(
const QString &propertyName)
872 const QQmlJSRegisterContent in = m_state.accumulatorIn();
873 const bool isAttached = in.variant() == QQmlJSRegisterContent::Attachment;
875 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
876 QQmlJSScope::createQQmlSAElement(
877 m_state.accumulatorIn().containedType()),
879 QQmlJSScope::createQQmlSAElement(isAttached
880 ? in.attachee().containedType()
881 : m_function->qmlScope.containedType()),
882 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
883 currentNonEmptySourceLocation()));
887bool QQmlJSTypePropagator::handleImportNamespaceLookup(
const QString &propertyName)
889 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
891 if (m_typeResolver->isPrefix(propertyName)) {
892 Q_ASSERT(accumulatorIn.isValid());
894 if (!accumulatorIn.containedType()->isReferenceType()) {
895 m_logger->log(u"Cannot use non-QObject type %1 to access prefixed import"_s.arg(
896 accumulatorIn.containedType()->internalName()),
897 qmlPrefixedImportType,
898 currentSourceLocation());
899 setVarAccumulatorAndError();
903 addReadAccumulator();
904 setAccumulator(m_pool->createImportNamespace(
905 m_jsUnitGenerator->getStringId(propertyName),
906 accumulatorIn.containedType(),
907 QQmlJSRegisterContent::ModulePrefix,
912 if (accumulatorIn.isImportNamespace()) {
913 m_logger->log(u"Type not found in namespace"_s, qmlUnresolvedType,
914 currentSourceLocation());
920void QQmlJSTypePropagator::handleLookupError(
const QString &propertyName)
922 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
924 setVarAccumulatorAndError();
925 if (checkForEnumProblems(accumulatorIn, propertyName))
928 addError(u"Cannot load property %1 from %2."_s
929 .arg(propertyName, accumulatorIn.descriptiveName()));
931 const QString typeName = accumulatorIn.containedTypeName();
933 if (typeName == u"QVariant")
935 if (accumulatorIn.isList() && propertyName == u"length")
938 auto baseType = accumulatorIn.containedType();
941 if (propertyResolution(baseType, propertyName) != PropertyMissing)
944 if (baseType->isScript())
947 std::optional<QQmlJSFixSuggestion> fixSuggestion;
949 if (
auto suggestion = QQmlJSUtils::didYouMean(propertyName, baseType->properties().keys(),
950 currentSourceLocation());
951 suggestion.has_value()) {
952 fixSuggestion = suggestion;
955 if (!fixSuggestion.has_value()
956 && accumulatorIn.variant() == QQmlJSRegisterContent::MetaType) {
958 const QQmlJSScope::ConstPtr scopeType = accumulatorIn.scopeType();
959 const auto metaEnums = scopeType->enumerations();
960 const bool enforcesScoped = scopeType->enforcesScopedEnums();
962 QStringList enumKeys;
963 for (
const QQmlJSMetaEnum &metaEnum : metaEnums) {
964 if (!enforcesScoped || !metaEnum.isScoped())
965 enumKeys << metaEnum.keys();
968 if (
auto suggestion = QQmlJSUtils::didYouMean(
969 propertyName, enumKeys, currentSourceLocation());
970 suggestion.has_value()) {
971 fixSuggestion = suggestion;
975 m_logger->log(u"Member \"%1\" not found on type \"%2\""_s.arg(propertyName).arg(typeName),
976 qmlMissingProperty, currentSourceLocation(),
true,
true, fixSuggestion);
979void QQmlJSTypePropagator::propagatePropertyLookup(
const QString &propertyName,
int lookupIndex)
981 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
983 m_typeResolver->memberType(
985 accumulatorIn.isImportNamespace()
986 ? m_jsUnitGenerator->stringForIndex(accumulatorIn.importNamespace())
987 + u'.' + propertyName
988 : propertyName, lookupIndex));
990 if (!m_state.accumulatorOut().isValid() && handleImportNamespaceLookup(propertyName))
993 if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::Singleton
994 && accumulatorIn.variant() == QQmlJSRegisterContent::ModulePrefix
995 && !isQmlScopeObject(accumulatorIn.scope())) {
997 u"Cannot access singleton as a property of an object. Did you want to access an attached object?"_s,
998 qmlAccessSingleton, currentSourceLocation());
999 setAccumulator(QQmlJSRegisterContent());
1000 }
else if (m_state.accumulatorOut().isEnumeration()) {
1001 switch (accumulatorIn.variant()) {
1002 case QQmlJSRegisterContent::MetaType:
1003 case QQmlJSRegisterContent::Attachment:
1004 case QQmlJSRegisterContent::Enum:
1005 case QQmlJSRegisterContent::ModulePrefix:
1006 case QQmlJSRegisterContent::Singleton:
1009 setAccumulator(QQmlJSRegisterContent());
1013 if (m_state.instructionHasError || !m_state.accumulatorOut().isValid()) {
1014 handleLookupError(propertyName);
1018 if (m_state.accumulatorOut().isMethod() && m_state.accumulatorOut().method().size() != 1) {
1019 addError(u"Cannot determine overloaded method on loadProperty"_s);
1023 if (m_state.accumulatorOut().isProperty()) {
1024 const QQmlJSScope::ConstPtr mathObject
1025 = m_typeResolver->jsGlobalObject()->property(u"Math"_s).type();
1026 if (accumulatorIn.contains(mathObject)) {
1027 QQmlJSMetaProperty prop;
1028 prop.setPropertyName(propertyName);
1029 prop.setTypeName(u"double"_s);
1030 prop.setType(m_typeResolver->realType());
1032 m_pool->createProperty(
1033 prop, accumulatorIn.resultLookupIndex(), lookupIndex,
1035 QQmlJSRegisterContent::Property, m_state.accumulatorOut().scope())
1041 if (m_state.accumulatorOut().contains(m_typeResolver->voidType())) {
1042 addError(u"Type %1 does not have a property %2 for reading"_s
1043 .arg(accumulatorIn.descriptiveName(), propertyName));
1047 if (!m_state.accumulatorOut().property().type()) {
1049 QString::fromLatin1(
"Type of property \"%2\" not found").arg(propertyName),
1050 qmlMissingType, currentSourceLocation());
1054 if (m_passManager !=
nullptr)
1055 propagatePropertyLookup_SAcheck(propertyName);
1057 switch (m_state.accumulatorOut().variant()) {
1058 case QQmlJSRegisterContent::Enum:
1059 case QQmlJSRegisterContent::Singleton:
1062 if (accumulatorIn.isImportNamespace())
1063 addReadAccumulator();
1066 addReadAccumulator();
1071void QQmlJSTypePropagator::generate_LoadProperty(
int nameIndex)
1073 propagatePropertyLookup(m_jsUnitGenerator->stringForIndex(nameIndex));
1076void QQmlJSTypePropagator::generate_LoadOptionalProperty(
int name,
int offset)
1083void QQmlJSTypePropagator::generate_GetLookup(
int index)
1085 propagatePropertyLookup(m_jsUnitGenerator->lookupName(index), index);
1088void QQmlJSTypePropagator::generate_GetOptionalLookup(
int index,
int offset)
1091 saveRegisterStateForJump(offset);
1092 propagatePropertyLookup(m_jsUnitGenerator->lookupName(index), index);
1094 generate_GetOptionalLookup_SAcheck();
1097void QQmlJSTypePropagator::generate_GetOptionalLookup_SAcheck()
1099 auto suggMsg =
"Consider using non-optional chaining instead: '?.' -> '.'"_L1;
1100 auto suggestion = std::make_optional(QQmlJSFixSuggestion(suggMsg, currentSourceLocation()));
1101 if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::Enum) {
1102 m_logger->log(
"Redundant optional chaining for enum lookup"_L1, qmlRedundantOptionalChaining,
1103 currentSourceLocation(),
true,
true, suggestion);
1104 }
else if (!m_state.accumulatorIn().containedType()->isReferenceType()
1105 && !m_typeResolver->canHoldUndefined(m_state.accumulatorIn())) {
1106 auto baseType = m_state.accumulatorIn().containedTypeName();
1107 m_logger->log(
"Redundant optional chaining for lookup on non-voidable and non-nullable "_L1
1108 "type %1"_L1.arg(baseType), qmlRedundantOptionalChaining,
1109 currentSourceLocation(),
true,
true, suggestion);
1113void QQmlJSTypePropagator::generate_StoreProperty_SAcheck(
const QString &propertyName,
1114 QQmlJSRegisterContent callBase)
1116 const bool isAttached = callBase.variant() == QQmlJSRegisterContent::Attachment;
1118 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeWrite(
1119 QQmlJSScope::createQQmlSAElement(callBase.containedType()),
1121 QQmlJSScope::createQQmlSAElement(
1122 m_state.accumulatorIn().containedType()),
1123 QQmlJSScope::createQQmlSAElement(isAttached
1124 ? callBase.attachee().containedType()
1125 : m_function->qmlScope.containedType()),
1126 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
1127 currentNonEmptySourceLocation()));
1130void QQmlJSTypePropagator::generate_StoreProperty(
int nameIndex,
int base)
1132 auto callBase = m_state.registers[base].content;
1133 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
1135 QQmlJSRegisterContent property = m_typeResolver->memberType(callBase, propertyName);
1136 if (!property.isProperty()) {
1137 addError(u"Type %1 does not have a property %2 for writing"_s
1138 .arg(callBase.descriptiveName(), propertyName));
1142 if (property.containedType().isNull()) {
1143 addError(u"Cannot determine type for property %1 of type %2"_s.arg(
1144 propertyName, callBase.descriptiveName()));
1148 if (!property.isWritable() && !property.containedType()->isListProperty()) {
1149 addError(u"Can't assign to read-only property %1"_s.arg(propertyName));
1151 m_logger->log(u"Cannot assign to read-only property %1"_s.arg(propertyName),
1152 qmlReadOnlyProperty, currentSourceLocation());
1157 if (!canConvertFromTo(m_state.accumulatorIn(), property)) {
1158 addError(u"cannot convert from %1 to %2"_s
1159 .arg(m_state.accumulatorIn().descriptiveName(), property.descriptiveName()));
1163 if (m_passManager !=
nullptr)
1164 generate_StoreProperty_SAcheck(propertyName, callBase);
1175 const QQmlJSScope::ConstPtr varType = m_typeResolver->varType();
1176 const QQmlJSRegisterContent readType = m_typeResolver->canHoldUndefined(m_state.accumulatorIn())
1177 ? m_typeResolver->convert(property, varType)
1178 : std::move(property);
1179 addReadAccumulator(readType);
1180 addReadRegister(base);
1181 m_state.setHasExternalSideEffects();
1184void QQmlJSTypePropagator::generate_SetLookup(
int index,
int base)
1186 generate_StoreProperty(m_jsUnitGenerator->lookupNameIndex(index), base);
1189void QQmlJSTypePropagator::generate_LoadSuperProperty(
int property)
1195void QQmlJSTypePropagator::generate_StoreSuperProperty(
int property)
1201void QQmlJSTypePropagator::generate_Yield()
1206void QQmlJSTypePropagator::generate_YieldStar()
1211void QQmlJSTypePropagator::generate_Resume(
int)
1216void QQmlJSTypePropagator::generate_CallValue(
int name,
int argc,
int argv)
1218 m_state.setHasExternalSideEffects();
1225void QQmlJSTypePropagator::generate_CallWithReceiver(
int name,
int thisObject,
int argc,
int argv)
1227 m_state.setHasExternalSideEffects();
1229 Q_UNUSED(thisObject)
1237 return consoleMethod == u"log" || consoleMethod == u"debug" || consoleMethod == u"info"
1238 || consoleMethod == u"warn" || consoleMethod == u"error";
1241void QQmlJSTypePropagator::generate_CallProperty_SCMath(
1242 const QString &name,
int base,
int argc,
int argv)
1250 addReadRegister(base, m_typeResolver->voidType());
1252 QQmlJSRegisterContent math = m_state.registers[base].content;
1253 const QList<QQmlJSMetaMethod> methods = math.containedType()->ownMethods(name);
1254 if (methods.isEmpty()) {
1255 setVarAccumulatorAndError();
1256 std::optional<QQmlJSFixSuggestion> fixSuggestion = QQmlJSUtils::didYouMean(
1257 name, math.containedType()->methods().keys(),
1258 currentSourceLocation());
1259 m_logger->log(u"Member \"%1\" not found on Math object"_s.arg(name),
1260 qmlMissingProperty, currentSourceLocation(),
1261 true,
true, std::move(fixSuggestion));
1264 Q_ASSERT(methods.length() == 1);
1268 QQmlJSRegisterContent realType = m_typeResolver->returnType(
1269 methods[0], m_typeResolver->realType(),
1270 m_typeResolver->baseType(math.containedType(), math));
1271 for (
int i = 0; i < argc; ++i)
1272 addReadRegister(argv + i, realType);
1273 setAccumulator(realType);
1276void QQmlJSTypePropagator::generate_CallProperty_SCconsole(
1277 const QString &name,
int base,
int argc,
int argv)
1280 addReadRegister(base, m_typeResolver->voidType());
1283 const QQmlJSRegisterContent firstContent = m_state.registers[argv].content;
1284 const QQmlJSScope::ConstPtr firstArg = firstContent.containedType();
1285 switch (firstArg->accessSemantics()) {
1286 case QQmlJSScope::AccessSemantics::Reference:
1289 addReadRegister(argv, m_typeResolver->genericType(firstArg));
1291 case QQmlJSScope::AccessSemantics::Sequence:
1292 addReadRegister(argv);
1295 addReadRegister(argv, m_typeResolver->stringType());
1300 for (
int i = 1; i < argc; ++i) {
1301 const QQmlJSRegisterContent argContent = m_state.registers[argv + i].content;
1302 const QQmlJSScope::ConstPtr arg = argContent.containedType();
1303 if (arg->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
1304 addReadRegister(argv + i);
1306 addReadRegister(argv + i, m_typeResolver->stringType());
1314 m_state.setHasExternalSideEffects();
1316 QQmlJSRegisterContent console = m_state.registers[base].content;
1317 QList<QQmlJSMetaMethod> methods = console.containedType()->ownMethods(name);
1318 Q_ASSERT(methods.length() == 1);
1322 setAccumulator(m_typeResolver->returnType(
1323 methods[0], m_typeResolver->voidType(),
1324 m_typeResolver->baseType(console.containedType(), console)));
1327void QQmlJSTypePropagator::propagateCall_SAcheck(
const QQmlJSMetaMethod &method,
1328 const QQmlJSScope::ConstPtr &baseType)
1330 Q_ASSERT(m_function);
1332 const QQmlSA::Element saBaseType = QQmlJSScope::createQQmlSAElement(baseType);
1333 const QQmlSA::SourceLocation saLocation{
1334 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(currentSourceLocation())
1336 const QQmlSA::Element saContainedType{ QQmlJSScope::createQQmlSAElement(
1337 m_function->qmlScope.containedType()) };
1339 QQmlSA::PassManagerPrivate::get(m_passManager)
1340 ->analyzeCall(saBaseType, method.methodName(), saContainedType, saLocation);
1343void QQmlJSTypePropagator::generate_callProperty_SAcheck(
const QString &propertyName,
1344 const QQmlJSScope::ConstPtr &baseType)
1346 Q_ASSERT(m_function);
1348 const QQmlSA::Element saBaseType{ QQmlJSScope::createQQmlSAElement(baseType) };
1349 const QQmlSA::Element saContainedType{ QQmlJSScope::createQQmlSAElement(
1350 m_function->qmlScope.containedType()) };
1351 const QQmlSA::SourceLocation saLocation{
1352 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(currentSourceLocation())
1355 QQmlSA::PassManagerPrivate::get(m_passManager)
1356 ->analyzeRead(saBaseType, propertyName, saContainedType, saLocation);
1357 QQmlSA::PassManagerPrivate::get(m_passManager)
1358 ->analyzeCall(saBaseType, propertyName, saContainedType, saLocation);
1361void QQmlJSTypePropagator::generate_CallProperty(
int nameIndex,
int base,
int argc,
int argv)
1363 Q_ASSERT(m_state.registers.contains(base));
1364 const auto callBase = m_state.registers[base].content;
1365 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
1367 if (callBase.contains(m_typeResolver->mathObject())) {
1368 generate_CallProperty_SCMath(propertyName, base, argc, argv);
1369 if (m_passManager !=
nullptr)
1370 generate_callProperty_SAcheck(propertyName, callBase.containedType());
1374 if (callBase.contains(m_typeResolver->consoleObject()) && isLoggingMethod(propertyName)) {
1375 generate_CallProperty_SCconsole(propertyName, base, argc, argv);
1376 if (m_passManager !=
nullptr)
1377 generate_callProperty_SAcheck(propertyName, callBase.containedType());
1381 const auto baseType = callBase.containedType();
1382 const auto member = m_typeResolver->memberType(callBase, propertyName);
1384 if (!member.isMethod()) {
1385 if (callBase.contains(m_typeResolver->jsValueType())
1386 || callBase.contains(m_typeResolver->varType())) {
1387 const auto jsValueType = m_typeResolver->jsValueType();
1388 addReadRegister(base, jsValueType);
1389 for (
int i = 0; i < argc; ++i)
1390 addReadRegister(argv + i, jsValueType);
1391 m_state.setHasExternalSideEffects();
1393 QQmlJSMetaMethod method;
1394 method.setIsJavaScriptFunction(
true);
1395 method.setMethodName(propertyName);
1396 method.setMethodType(QQmlJSMetaMethod::MethodType::Method);
1398 setAccumulator(m_typeResolver->returnType(
1399 method, m_typeResolver->jsValueType(), callBase));
1401 if (m_passManager !=
nullptr)
1402 generate_callProperty_SAcheck(propertyName, callBase.containedType());
1406 setVarAccumulatorAndError();
1407 addError(u"Type %1 does not have a property %2 for calling"_s
1408 .arg(callBase.descriptiveName(), propertyName));
1410 if (callBase.isType() && isCallingProperty(callBase.type(), propertyName))
1413 if (checkForEnumProblems(callBase, propertyName))
1416 std::optional<QQmlJSFixSuggestion> fixSuggestion;
1418 if (
auto suggestion = QQmlJSUtils::didYouMean(propertyName, baseType->methods().keys(),
1419 currentSourceLocation());
1420 suggestion.has_value()) {
1421 fixSuggestion = suggestion;
1424 m_logger->log(u"Member \"%1\" not found on type \"%2\""_s.arg(
1425 propertyName, callBase.containedTypeName()),
1426 qmlMissingProperty, currentSourceLocation(),
true,
true, fixSuggestion);
1430 checkDeprecated(baseType, propertyName,
true);
1432 addReadRegister(base);
1434 if (callBase.contains(m_typeResolver->stringType())) {
1435 if (propertyName == u"arg"_s && argc == 1) {
1436 propagateStringArgCall(callBase, argv);
1441 if (baseType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1442 && member.scope().contains(m_typeResolver->arrayPrototype())
1443 && propagateArrayMethod(propertyName, argc, argv, callBase)) {
1447 propagateCall(member.method(), argc, argv, member.scope());
1450QQmlJSMetaMethod QQmlJSTypePropagator::bestMatchForCall(
const QList<QQmlJSMetaMethod> &methods,
1451 int argc,
int argv, QStringList *errors)
1453 QQmlJSMetaMethod javascriptFunction;
1454 QQmlJSMetaMethod candidate;
1455 bool hasMultipleCandidates =
false;
1457 for (
const auto &method : methods) {
1460 if (method.isJavaScriptFunction() && !javascriptFunction.isValid())
1461 javascriptFunction = method;
1463 if (method.returnType().isNull() && !method.returnTypeName().isEmpty()) {
1464 errors->append(u"return type %1 cannot be resolved"_s
1465 .arg(method.returnTypeName()));
1469 const auto arguments = method.parameters();
1470 if (argc != arguments.size()) {
1472 u"Function expects %1 arguments, but %2 were provided"_s.arg(arguments.size())
1477 bool fuzzyMatch =
true;
1478 bool exactMatch =
true;
1479 for (
int i = 0; i < argc; ++i) {
1480 const auto argumentType = arguments[i].type();
1481 if (argumentType.isNull()) {
1483 u"type %1 for argument %2 cannot be resolved"_s.arg(arguments[i].typeName())
1490 const auto content = m_state.registers[argv + i].content;
1491 if (content.contains(argumentType))
1495 if (canConvertFromTo(content, argumentType))
1499 if (argumentType->isReferenceType()
1500 && m_typeResolver->inherits(
1501 argumentType->baseType(), content.containedType())) {
1506 u"argument %1 contains %2 but is expected to contain the type %3"_s.arg(i).arg(
1507 content.descriptiveName(), arguments[i].typeName()));
1514 }
else if (fuzzyMatch) {
1515 if (!candidate.isValid())
1518 hasMultipleCandidates =
true;
1522 if (hasMultipleCandidates)
1523 return QQmlJSMetaMethod();
1525 return candidate.isValid() ? candidate : javascriptFunction;
1528void QQmlJSTypePropagator::setAccumulator(QQmlJSRegisterContent content)
1530 setRegister(Accumulator, content);
1533void QQmlJSTypePropagator::setRegister(
int index, QQmlJSRegisterContent content)
1536 auto it = m_prevStateAnnotations.find(currentInstructionOffset());
1537 if (it != m_prevStateAnnotations.end()) {
1538 QQmlJSRegisterContent lastTry = it->second.changedRegister;
1539 if (lastTry.contains(content.containedType())) {
1540 m_state.setRegister(index, lastTry);
1545 m_state.setRegister(index, content);
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568void QQmlJSTypePropagator::mergeRegister(
1569 int index,
const VirtualRegister &a,
const VirtualRegister &b)
1571 const VirtualRegister merged = {
1572 (a.content == b.content) ? a.content : m_typeResolver->merge(a.content, b.content),
1573 a.canMove && b.canMove,
1574 a.affectedBySideEffects || b.affectedBySideEffects,
1575 a.isShadowable || b.isShadowable,
1578 Q_ASSERT(merged.content.isValid());
1580 if (!merged.content.isConversion()) {
1582 m_state.annotations[currentInstructionOffset()].typeConversions[index] = merged;
1583 m_state.registers[index] = merged;
1587 auto tryPrevStateConversion = [
this](
int index,
const VirtualRegister &merged) ->
bool {
1588 auto it = m_prevStateAnnotations.find(currentInstructionOffset());
1589 if (it == m_prevStateAnnotations.end())
1592 auto conversion = it->second.typeConversions.find(index);
1593 if (conversion == it->second.typeConversions.end())
1596 const VirtualRegister &lastTry = conversion.value();
1598 Q_ASSERT(lastTry.content.isValid());
1599 if (!lastTry.content.isConversion())
1602 if (lastTry.content.conversionResultType() != merged.content.conversionResultType()
1603 || lastTry.content.conversionOrigins() != merged.content.conversionOrigins()
1604 || lastTry.canMove != merged.canMove
1605 || lastTry.affectedBySideEffects != merged.affectedBySideEffects
1606 || lastTry.isShadowable != merged.isShadowable) {
1611 m_state.annotations[currentInstructionOffset()].typeConversions[index] = lastTry;
1614 Q_ASSERT(!m_state.registers[index].affectedBySideEffects || lastTry.affectedBySideEffects);
1616 m_state.registers[index] = lastTry;
1620 if (!tryPrevStateConversion(index, merged)) {
1622 const VirtualRegister cloned = {
1623 (a == b) ? m_pool->clone(merged.content) : merged.content,
1625 merged.affectedBySideEffects,
1626 merged.isShadowable,
1628 Q_ASSERT(cloned.content.isValid());
1629 m_state.annotations[currentInstructionOffset()].typeConversions[index] = cloned;
1630 m_state.registers[index] = cloned;
1634void QQmlJSTypePropagator::addReadRegister(
int index)
1637 m_state.addReadRegister(index, m_state.registers[index].content);
1640void QQmlJSTypePropagator::addReadRegister(
int index, QQmlJSRegisterContent convertTo)
1642 if (m_state.registers[index].content == convertTo) {
1644 m_state.addReadRegister(index, convertTo);
1646 m_state.addReadRegister(
1647 index, m_typeResolver->convert(m_state.registers[index].content, convertTo));
1651void QQmlJSTypePropagator::addReadRegister(
int index,
const QQmlJSScope::ConstPtr &convertTo)
1653 m_state.addReadRegister(
1654 index, m_typeResolver->convert(m_state.registers[index].content, convertTo));
1657void QQmlJSTypePropagator::propagateCall(
1658 const QList<QQmlJSMetaMethod> &methods,
int argc,
int argv,
1659 QQmlJSRegisterContent scope)
1662 const QQmlJSMetaMethod match = bestMatchForCall(methods, argc, argv, &errors);
1664 if (!match.isValid()) {
1665 setVarAccumulatorAndError();
1666 if (methods.size() == 1) {
1668 Q_ASSERT(errors.size() == 1);
1669 addError(errors.first());
1670 }
else if (errors.size() < methods.size()) {
1671 addError(u"Multiple matching overrides found. Cannot determine the right one."_s);
1673 addError(u"No matching override found. Candidates:\n"_s + errors.join(u'\n'));
1679 propagateCall_SAcheck(match, scope.containedType());
1681 QQmlJSScope::ConstPtr returnType;
1682 if (match.isJavaScriptFunction())
1683 returnType = m_typeResolver->jsValueType();
1684 else if (match.isConstructor())
1685 returnType = scope.containedType();
1687 returnType = match.returnType();
1689 setAccumulator(m_typeResolver->returnType(match, returnType, scope));
1690 if (!m_state.accumulatorOut().isValid())
1691 addError(u"Cannot store return type of method %1()."_s.arg(match.methodName()));
1693 const auto types = match.parameters();
1694 for (
int i = 0; i < argc; ++i) {
1695 if (i < types.size()) {
1696 const QQmlJSScope::ConstPtr type = match.isJavaScriptFunction()
1697 ? m_typeResolver->jsValueType()
1698 : QQmlJSScope::ConstPtr(types.at(i).type());
1699 if (!type.isNull()) {
1700 addReadRegister(argv + i, type);
1704 addReadRegister(argv + i, m_typeResolver->jsValueType());
1706 m_state.setHasExternalSideEffects();
1709void QQmlJSTypePropagator::propagateTranslationMethod_SAcheck(
const QString &methodName)
1711 QQmlSA::PassManagerPrivate::get(m_passManager)
1712 ->analyzeCall(QQmlJSScope::createQQmlSAElement(m_typeResolver->jsGlobalObject()),
1714 QQmlJSScope::createQQmlSAElement(m_function->qmlScope.containedType()),
1715 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
1716 currentNonEmptySourceLocation()));
1719bool QQmlJSTypePropagator::propagateTranslationMethod(
1720 const QList<QQmlJSMetaMethod> &methods,
int argc,
int argv)
1722 if (methods.size() != 1)
1725 const QQmlJSMetaMethod method = methods.front();
1726 const QQmlJSScope::ConstPtr intType = m_typeResolver->int32Type();
1727 const QQmlJSScope::ConstPtr stringType = m_typeResolver->stringType();
1729 const QQmlJSRegisterContent returnType = m_typeResolver->returnType(
1730 method, m_typeResolver->stringType(), m_typeResolver->jsGlobalObjectContent());
1732 if (method.methodName() == u"qsTranslate"_s) {
1735 addReadRegister(argv + 3, intType);
1738 addReadRegister(argv + 2, stringType);
1741 addReadRegister(argv + 1, stringType);
1742 addReadRegister(argv, stringType);
1743 setAccumulator(returnType);
1746 propagateTranslationMethod_SAcheck(method.methodName());
1753 if (method.methodName() == u"QT_TRANSLATE_NOOP"_s) {
1756 addReadRegister(argv + 2, stringType);
1759 addReadRegister(argv + 1, stringType);
1760 addReadRegister(argv, stringType);
1761 setAccumulator(returnType);
1764 propagateTranslationMethod_SAcheck(method.methodName());
1771 if (method.methodName() == u"qsTr"_s) {
1774 addReadRegister(argv + 2, intType);
1777 addReadRegister(argv + 1, stringType);
1780 addReadRegister(argv, stringType);
1781 setAccumulator(returnType);
1784 propagateTranslationMethod_SAcheck(method.methodName());
1791 if (method.methodName() == u"QT_TR_NOOP"_s) {
1794 addReadRegister(argv + 1, stringType);
1797 addReadRegister(argv, stringType);
1798 setAccumulator(returnType);
1801 propagateTranslationMethod_SAcheck(method.methodName());
1808 if (method.methodName() == u"qsTrId"_s) {
1811 addReadRegister(argv + 1, intType);
1814 addReadRegister(argv, stringType);
1815 setAccumulator(returnType);
1818 propagateTranslationMethod_SAcheck(method.methodName());
1825 if (method.methodName() == u"QT_TRID_NOOP"_s) {
1828 addReadRegister(argv, stringType);
1829 setAccumulator(returnType);
1832 propagateTranslationMethod_SAcheck(method.methodName());
1842void QQmlJSTypePropagator::propagateStringArgCall(QQmlJSRegisterContent base,
int argv)
1844 QQmlJSMetaMethod method;
1845 method.setIsJavaScriptFunction(
true);
1846 method.setMethodName(u"arg"_s);
1847 setAccumulator(m_typeResolver->returnType(method, m_typeResolver->stringType(), base));
1848 Q_ASSERT(m_state.accumulatorOut().isValid());
1850 const QQmlJSScope::ConstPtr input = m_state.registers[argv].content.containedType();
1852 if (input == m_typeResolver->uint32Type()
1853 || input == m_typeResolver->int64Type()
1854 || input == m_typeResolver->uint64Type()) {
1855 addReadRegister(argv, m_typeResolver->realType());
1859 if (m_typeResolver->isIntegral(input)) {
1860 addReadRegister(argv, m_typeResolver->int32Type());
1864 if (m_typeResolver->isNumeric(input)) {
1865 addReadRegister(argv, m_typeResolver->realType());
1869 if (input == m_typeResolver->boolType()) {
1870 addReadRegister(argv, m_typeResolver->boolType());
1874 addReadRegister(argv, m_typeResolver->stringType());
1877bool QQmlJSTypePropagator::propagateArrayMethod(
1878 const QString &name,
int argc,
int argv, QQmlJSRegisterContent baseType)
1893 const auto intType = m_typeResolver->int32Type();
1894 const auto stringType = m_typeResolver->stringType();
1895 const auto baseContained = baseType.containedType();
1896 const auto elementContained = baseContained->elementType();
1898 const auto setReturnType = [&](
const QQmlJSScope::ConstPtr type) {
1899 QQmlJSMetaMethod method;
1900 method.setIsJavaScriptFunction(
true);
1901 method.setMethodName(name);
1902 setAccumulator(m_typeResolver->returnType(method, type, baseType));
1905 if (name == u"copyWithin" && argc > 0 && argc < 4) {
1906 for (
int i = 0; i < argc; ++i) {
1907 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1911 for (
int i = 0; i < argc; ++i)
1912 addReadRegister(argv + i, intType);
1914 m_state.setHasExternalSideEffects();
1915 setReturnType(baseContained);
1919 if (name == u"fill" && argc > 0 && argc < 4) {
1920 if (!canConvertFromTo(m_state.registers[argv].content, elementContained))
1923 for (
int i = 1; i < argc; ++i) {
1924 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1928 addReadRegister(argv, elementContained);
1930 for (
int i = 1; i < argc; ++i)
1931 addReadRegister(argv + i, intType);
1933 m_state.setHasExternalSideEffects();
1934 setReturnType(baseContained);
1938 if (name == u"includes" && argc > 0 && argc < 3) {
1939 if (!canConvertFromTo(m_state.registers[argv].content, elementContained))
1943 if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
1945 addReadRegister(argv + 1, intType);
1948 addReadRegister(argv, elementContained);
1949 setReturnType(m_typeResolver->boolType());
1953 if (name == u"toString" || (name == u"join" && argc < 2)) {
1955 if (!canConvertFromTo(m_state.registers[argv].content, stringType))
1957 addReadRegister(argv, stringType);
1960 setReturnType(m_typeResolver->stringType());
1964 if ((name == u"pop" || name == u"shift") && argc == 0) {
1965 m_state.setHasExternalSideEffects();
1966 setReturnType(elementContained);
1970 if (name == u"push" || name == u"unshift") {
1971 for (
int i = 0; i < argc; ++i) {
1972 if (!canConvertFromTo(m_state.registers[argv + i].content, elementContained))
1976 for (
int i = 0; i < argc; ++i)
1977 addReadRegister(argv + i, elementContained);
1979 m_state.setHasExternalSideEffects();
1980 setReturnType(m_typeResolver->int32Type());
1984 if (name == u"reverse" && argc == 0) {
1985 m_state.setHasExternalSideEffects();
1986 setReturnType(baseContained);
1990 if (name == u"slice" && argc < 3) {
1991 for (
int i = 0; i < argc; ++i) {
1992 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1996 for (
int i = 0; i < argc; ++i)
1997 addReadRegister(argv + i, intType);
1999 setReturnType(baseType.containedType()->isListProperty()
2000 ? m_typeResolver->qObjectListType()
2005 if (name == u"splice" && argc > 0) {
2006 for (
int i = 0; i < 2; ++i) {
2007 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
2011 for (
int i = 2; i < argc; ++i) {
2012 if (!canConvertFromTo(m_state.registers[argv + i].content, elementContained))
2016 for (
int i = 0; i < 2; ++i)
2017 addReadRegister(argv + i, intType);
2019 for (
int i = 2; i < argc; ++i)
2020 addReadRegister(argv + i, elementContained);
2022 m_state.setHasExternalSideEffects();
2023 setReturnType(baseContained);
2027 if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
2028 if (!canConvertFromTo(m_state.registers[argv].content, elementContained))
2032 if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
2034 addReadRegister(argv + 1, intType);
2037 addReadRegister(argv, elementContained);
2038 setReturnType(m_typeResolver->int32Type());
2045void QQmlJSTypePropagator::generate_CallPropertyLookup(
int lookupIndex,
int base,
int argc,
2048 generate_CallProperty(m_jsUnitGenerator->lookupNameIndex(lookupIndex), base, argc, argv);
2051void QQmlJSTypePropagator::generate_CallName(
int name,
int argc,
int argv)
2053 propagateScopeLookupCall(m_jsUnitGenerator->stringForIndex(name), argc, argv);
2056void QQmlJSTypePropagator::generate_CallPossiblyDirectEval(
int argc,
int argv)
2058 m_state.setHasExternalSideEffects();
2063 if (m_passManager) {
2064 const QQmlSA::SourceLocation saLocation{
2065 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(currentSourceLocation())
2067 const QQmlSA::Element saBaseType{ QQmlJSScope::createQQmlSAElement(
2068 m_typeResolver->jsGlobalObject()) };
2069 const QQmlSA::Element saContainedType{ QQmlJSScope::createQQmlSAElement(
2070 m_function->qmlScope.containedType()) };
2072 QQmlSA::PassManagerPrivate::get(m_passManager)
2073 ->analyzeCall(saBaseType,
"eval"_L1, saContainedType, saLocation);
2079void QQmlJSTypePropagator::propagateScopeLookupCall(
const QString &functionName,
int argc,
int argv)
2081 const QQmlJSRegisterContent resolvedContent
2082 = m_typeResolver->scopedType(m_function->qmlScope, functionName);
2083 if (resolvedContent.isMethod()) {
2084 const auto methods = resolvedContent.method();
2085 if (resolvedContent.scope().contains(m_typeResolver->jsGlobalObject())) {
2086 if (propagateTranslationMethod(methods, argc, argv))
2090 if (!methods.isEmpty()) {
2091 propagateCall(methods, argc, argv, resolvedContent.scope());
2096 addError(u"method %1 cannot be resolved."_s.arg(functionName));
2097 const auto jsValue = m_typeResolver->jsValueType();
2098 QQmlJSMetaMethod method;
2099 method.setMethodName(functionName);
2100 method.setIsJavaScriptFunction(
true);
2101 setAccumulator(m_typeResolver->returnType(method, jsValue, m_function->qmlScope));
2103 addError(u"Cannot find function '%1'"_s.arg(functionName));
2105 handleUnqualifiedAccessAndContextProperties(functionName,
true);
2108void QQmlJSTypePropagator::generate_CallGlobalLookup(
int index,
int argc,
int argv)
2110 propagateScopeLookupCall(m_jsUnitGenerator->lookupName(index), argc, argv);
2113void QQmlJSTypePropagator::generate_CallQmlContextPropertyLookup(
int index,
int argc,
int argv)
2115 const QString name = m_jsUnitGenerator->lookupName(index);
2116 propagateScopeLookupCall(name, argc, argv);
2117 checkDeprecated(m_function->qmlScope.containedType(), name,
true);
2120void QQmlJSTypePropagator::generate_CallWithSpread(
int func,
int thisObject,
int argc,
int argv)
2122 m_state.setHasExternalSideEffects();
2124 Q_UNUSED(thisObject)
2130void QQmlJSTypePropagator::generate_TailCall(
int func,
int thisObject,
int argc,
int argv)
2132 m_state.setHasExternalSideEffects();
2134 Q_UNUSED(thisObject)
2140void QQmlJSTypePropagator::generate_Construct_SCDate(
2141 const QQmlJSMetaMethod &ctor,
int argc,
int argv)
2143 setAccumulator(m_typeResolver->returnType(ctor, m_typeResolver->dateTimeType(), {}));
2146 const QQmlJSRegisterContent argType = m_state.registers[argv].content;
2147 if (m_typeResolver->isNumeric(argType)) {
2148 addReadRegister(argv, m_typeResolver->realType());
2149 }
else if (argType.contains(m_typeResolver->stringType())) {
2150 addReadRegister(argv, m_typeResolver->stringType());
2151 }
else if (argType.contains(m_typeResolver->dateTimeType())
2152 || argType.contains(m_typeResolver->dateType())
2153 || argType.contains(m_typeResolver->timeType())) {
2154 addReadRegister(argv, m_typeResolver->dateTimeType());
2156 addReadRegister(argv, m_typeResolver->jsPrimitiveType());
2159 constexpr int maxArgc = 7;
2160 for (
int i = 0; i < std::min(argc, maxArgc); ++i)
2161 addReadRegister(argv + i, m_typeResolver->realType());
2165void QQmlJSTypePropagator::generate_Construct_SCArray(
2166 const QQmlJSMetaMethod &ctor,
int argc,
int argv)
2169 if (m_typeResolver->isNumeric(m_state.registers[argv].content)) {
2170 setAccumulator(m_typeResolver->returnType(ctor, m_typeResolver->variantListType(), {}));
2171 addReadRegister(argv, m_typeResolver->realType());
2173 generate_DefineArray(argc, argv);
2176 generate_DefineArray(argc, argv);
2179void QQmlJSTypePropagator::generate_Construct(
int func,
int argc,
int argv)
2181 const QQmlJSRegisterContent type = m_state.registers[func].content;
2182 if (type.contains(m_typeResolver->metaObjectType())) {
2183 const QQmlJSRegisterContent valueType = type.scope();
2184 const QQmlJSScope::ConstPtr contained = type.scopeType();
2185 if (contained->isValueType() && contained->isCreatable()) {
2186 const auto extension = contained->extensionType();
2187 if (extension.extensionSpecifier == QQmlJSScope::ExtensionType) {
2189 extension.scope->ownMethods(extension.scope->internalName()),
2190 argc, argv, valueType);
2193 contained->ownMethods(contained->internalName()), argc, argv, valueType);
2199 if (!type.isMethod()) {
2200 m_state.setHasExternalSideEffects();
2201 QQmlJSMetaMethod method;
2202 method.setMethodName(type.containedTypeName());
2203 method.setIsJavaScriptFunction(
true);
2204 method.setIsConstructor(
true);
2205 setAccumulator(m_typeResolver->returnType(method, m_typeResolver->jsValueType(), {}));
2209 if (
const auto methods = type.method();
2210 methods == m_typeResolver->jsGlobalObject()->methods(u"Date"_s)) {
2211 Q_ASSERT(methods.length() == 1);
2212 generate_Construct_SCDate(methods[0], argc, argv);
2216 if (
const auto methods = type.method();
2217 methods == m_typeResolver->jsGlobalObject()->methods(u"Array"_s)) {
2218 Q_ASSERT(methods.length() == 1);
2219 generate_Construct_SCArray(methods[0], argc, argv);
2223 m_state.setHasExternalSideEffects();
2226 QQmlJSMetaMethod match = bestMatchForCall(type.method(), argc, argv, &errors);
2227 if (!match.isValid())
2228 addError(u"Cannot determine matching constructor. Candidates:\n"_s + errors.join(u'\n'));
2229 setAccumulator(m_typeResolver->returnType(match, m_typeResolver->jsValueType(), {}));
2232void QQmlJSTypePropagator::generate_ConstructWithSpread(
int func,
int argc,
int argv)
2234 m_state.setHasExternalSideEffects();
2241void QQmlJSTypePropagator::generate_SetUnwindHandler(
int offset)
2243 m_state.setHasInternalSideEffects();
2248void QQmlJSTypePropagator::generate_UnwindDispatch()
2250 m_state.setHasInternalSideEffects();
2254void QQmlJSTypePropagator::generate_UnwindToLabel(
int level,
int offset)
2256 m_state.setHasInternalSideEffects();
2262void QQmlJSTypePropagator::generate_DeadTemporalZoneCheck(
int name)
2264 const auto fail = [
this, name]() {
2265 addError(u"Cannot statically assert the dead temporal zone check for %1"_s.arg(
2266 name ? m_jsUnitGenerator->stringForIndex(name) : u"the anonymous accumulator"_s));
2269 const QQmlJSRegisterContent in = m_state.accumulatorIn();
2270 if (in.isConversion()) {
2271 const auto &inConversionOrigins = in.conversionOrigins();
2272 for (QQmlJSRegisterContent origin : inConversionOrigins) {
2273 if (!origin.contains(m_typeResolver->emptyType()))
2278 }
else if (in.contains(m_typeResolver->emptyType())) {
2283void QQmlJSTypePropagator::generate_ThrowException()
2285 addReadAccumulator(m_typeResolver->jsValueType());
2286 m_state.setHasInternalSideEffects();
2287 m_state.skipInstructionsUntilNextJumpTarget =
true;
2290void QQmlJSTypePropagator::generate_GetException()
2295void QQmlJSTypePropagator::generate_SetException()
2297 m_state.setHasInternalSideEffects();
2301void QQmlJSTypePropagator::generate_CreateCallContext()
2303 m_state.setHasInternalSideEffects();
2306void QQmlJSTypePropagator::generate_PushCatchContext(
int index,
int name)
2308 m_state.setHasInternalSideEffects();
2314void QQmlJSTypePropagator::generate_PushWithContext()
2316 m_state.setHasInternalSideEffects();
2320void QQmlJSTypePropagator::generate_PushBlockContext(
int index)
2322 m_state.setHasInternalSideEffects();
2327void QQmlJSTypePropagator::generate_CloneBlockContext()
2329 m_state.setHasInternalSideEffects();
2333void QQmlJSTypePropagator::generate_PushScriptContext(
int index)
2335 m_state.setHasInternalSideEffects();
2340void QQmlJSTypePropagator::generate_PopScriptContext()
2342 m_state.setHasInternalSideEffects();
2346void QQmlJSTypePropagator::generate_PopContext()
2348 m_state.setHasInternalSideEffects();
2351void QQmlJSTypePropagator::generate_GetIterator(
int iterator)
2353 const QQmlJSRegisterContent listType = m_state.accumulatorIn();
2354 if (!listType.isList()) {
2355 const QQmlJSScope::ConstPtr jsValue = m_typeResolver->jsValueType();
2356 addReadAccumulator(jsValue);
2358 QQmlJSMetaProperty prop;
2359 prop.setPropertyName(u"<>"_s);
2360 prop.setTypeName(jsValue->internalName());
2361 prop.setType(jsValue);
2362 setAccumulator(m_pool->createProperty(
2363 prop, currentInstructionOffset(),
2364 QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListIterator,
2369 addReadAccumulator();
2370 setAccumulator(m_typeResolver->iteratorPointer(
2371 listType, QQmlJS::AST::ForEachType(iterator), currentInstructionOffset()));
2374void QQmlJSTypePropagator::generate_IteratorNext(
int value,
int offset)
2376 const QQmlJSRegisterContent iteratorType = m_state.accumulatorIn();
2377 addReadAccumulator();
2378 setRegister(value, m_typeResolver->merge(
2379 m_typeResolver->elementType(iteratorType),
2380 m_typeResolver->literalType(m_typeResolver->voidType())));
2381 saveRegisterStateForJump(offset);
2382 m_state.setHasInternalSideEffects();
2385void QQmlJSTypePropagator::generate_IteratorNextForYieldStar(
int iterator,
int object,
int offset)
2393void QQmlJSTypePropagator::generate_IteratorClose()
2398void QQmlJSTypePropagator::generate_DestructureRestElement()
2403void QQmlJSTypePropagator::generate_DeleteProperty(
int base,
int index)
2410void QQmlJSTypePropagator::generate_DeleteName(
int name)
2416void QQmlJSTypePropagator::generate_TypeofName(
int name)
2419 setAccumulator(m_typeResolver->operationType(m_typeResolver->stringType()));
2422void QQmlJSTypePropagator::generate_TypeofValue()
2424 setAccumulator(m_typeResolver->operationType(m_typeResolver->stringType()));
2427void QQmlJSTypePropagator::generate_DeclareVar(
int varName,
int isDeletable)
2430 Q_UNUSED(isDeletable)
2434void QQmlJSTypePropagator::generate_DefineArray(
int argc,
int args)
2436 setAccumulator(m_typeResolver->operationType(m_typeResolver->variantListType()));
2439 const QQmlJSScope::ConstPtr elementType = m_typeResolver->varType();
2440 for (
int i = 0; i < argc; ++i)
2441 addReadRegister(args + i, elementType);
2444void QQmlJSTypePropagator::generate_DefineObjectLiteral(
int internalClassId,
int argc,
int args)
2446 const int classSize = m_jsUnitGenerator->jsClassSize(internalClassId);
2447 Q_ASSERT(argc >= classSize);
2450 for (
int i = 0; i < classSize; ++i)
2451 addReadRegister(args + i, m_typeResolver->varType());
2453 for (
int i = classSize; i < argc; i += 3) {
2460 addReadRegister(args + i + 1, m_typeResolver->stringType());
2463 addReadRegister(args + i + 2, m_typeResolver->varType());
2466 setAccumulator(m_typeResolver->operationType(m_typeResolver->variantMapType()));
2469void QQmlJSTypePropagator::generate_CreateClass(
int classIndex,
int heritage,
int computedNames)
2471 Q_UNUSED(classIndex)
2473 Q_UNUSED(computedNames)
2477void QQmlJSTypePropagator::generate_CreateMappedArgumentsObject()
2482void QQmlJSTypePropagator::generate_CreateUnmappedArgumentsObject()
2487void QQmlJSTypePropagator::generate_CreateRestParameter(
int argIndex)
2493void QQmlJSTypePropagator::generate_ConvertThisToObject()
2495 setRegister(This, m_pool->clone(m_function->qmlScope));
2498void QQmlJSTypePropagator::generate_LoadSuperConstructor()
2503void QQmlJSTypePropagator::generate_ToObject()
2508void QQmlJSTypePropagator::generate_Jump(
int offset)
2510 saveRegisterStateForJump(offset);
2511 m_state.skipInstructionsUntilNextJumpTarget =
true;
2512 m_state.setHasInternalSideEffects();
2515void QQmlJSTypePropagator::generate_JumpTrue(
int offset)
2517 if (!canConvertFromTo(m_state.accumulatorIn(), m_typeResolver->boolType())) {
2518 addError(u"cannot convert from %1 to boolean"_s
2519 .arg(m_state.accumulatorIn().descriptiveName()));
2522 saveRegisterStateForJump(offset);
2523 addReadAccumulator(m_typeResolver->boolType());
2524 m_state.setHasInternalSideEffects();
2527void QQmlJSTypePropagator::generate_JumpFalse(
int offset)
2529 if (!canConvertFromTo(m_state.accumulatorIn(), m_typeResolver->boolType())) {
2530 addError(u"cannot convert from %1 to boolean"_s
2531 .arg(m_state.accumulatorIn().descriptiveName()));
2534 saveRegisterStateForJump(offset);
2535 addReadAccumulator(m_typeResolver->boolType());
2536 m_state.setHasInternalSideEffects();
2539void QQmlJSTypePropagator::generate_JumpNoException(
int offset)
2541 saveRegisterStateForJump(offset);
2542 m_state.setHasInternalSideEffects();
2545void QQmlJSTypePropagator::generate_JumpNotUndefined(
int offset)
2551void QQmlJSTypePropagator::generate_CheckException()
2553 m_state.setHasInternalSideEffects();
2556void QQmlJSTypePropagator::recordEqualsNullType()
2559 if (m_state.accumulatorIn().contains(m_typeResolver->nullType())
2560 || m_state.accumulatorIn().containedType()->isReferenceType()) {
2561 addReadAccumulator();
2563 addReadAccumulator(m_typeResolver->jsPrimitiveType());
2566void QQmlJSTypePropagator::recordEqualsIntType()
2569 const QQmlJSScope::ConstPtr in = m_state.accumulatorIn().containedType();
2570 if (m_state.accumulatorIn().contains(m_typeResolver->boolType())
2571 || m_typeResolver->isNumeric(m_state.accumulatorIn())) {
2572 addReadAccumulator();
2574 addReadAccumulator(m_typeResolver->jsPrimitiveType());
2577void QQmlJSTypePropagator::recordEqualsType(
int lhs)
2579 const auto isNumericOrEnum = [
this](QQmlJSRegisterContent content) {
2580 return content.isEnumeration() || m_typeResolver->isNumeric(content);
2583 const auto accumulatorIn = m_state.accumulatorIn();
2584 const auto lhsRegister = m_state.registers[lhs].content;
2587 if (m_typeResolver->isPrimitive(accumulatorIn) || accumulatorIn.isEnumeration()) {
2588 if (accumulatorIn.contains(lhsRegister.containedType())
2589 || (isNumericOrEnum(accumulatorIn) && isNumericOrEnum(lhsRegister))
2590 || m_typeResolver->isPrimitive(lhsRegister)) {
2591 addReadRegister(lhs);
2592 addReadAccumulator();
2597 const auto containedAccumulatorIn = m_typeResolver->isOptionalType(accumulatorIn)
2598 ? m_typeResolver->extractNonVoidFromOptionalType(accumulatorIn).containedType()
2599 : accumulatorIn.containedType();
2601 const auto containedLhs = m_typeResolver->isOptionalType(lhsRegister)
2602 ? m_typeResolver->extractNonVoidFromOptionalType(lhsRegister).containedType()
2603 : lhsRegister.containedType();
2606 if (canStrictlyCompareWithVar(m_typeResolver, containedLhs, containedAccumulatorIn)
2607 || canCompareWithQObject(m_typeResolver, containedLhs, containedAccumulatorIn)
2608 || canCompareWithQUrl(m_typeResolver, containedLhs, containedAccumulatorIn)) {
2609 addReadRegister(lhs);
2610 addReadAccumulator();
2617 const QQmlJSScope::ConstPtr jsval = m_typeResolver->jsValueType();
2618 addReadRegister(lhs, jsval);
2619 addReadAccumulator(jsval);
2622void QQmlJSTypePropagator::recordCompareType(
int lhs)
2627 const QQmlJSRegisterContent lhsContent = m_state.registers[lhs].content;
2628 const QQmlJSRegisterContent rhsContent = m_state.accumulatorIn();
2629 if (lhsContent == rhsContent) {
2632 addReadRegister(lhs, lhsContent);
2633 addReadAccumulator(lhsContent);
2634 }
else if (m_typeResolver->isNumeric(lhsContent) && m_typeResolver->isNumeric(rhsContent)) {
2637 const QQmlJSRegisterContent merged = m_typeResolver->merge(lhsContent, rhsContent);
2638 addReadRegister(lhs, merged);
2639 addReadAccumulator(merged);
2641 const QQmlJSScope::ConstPtr primitive = m_typeResolver->jsPrimitiveType();
2642 addReadRegister(lhs, primitive);
2643 addReadAccumulator(primitive);
2648 const QQmlJSTypeResolver *resolver)
2650 return scope == resolver->varType() || scope == resolver->jsValueType()
2651 || scope == resolver->jsPrimitiveType();
2655 const QQmlJSTypeResolver *resolver)
2657 return scope == resolver->boolType() || scope == resolver->stringType()
2658 || resolver->isNumeric(scope);
2662 const QQmlJSTypeResolver *resolver)
2664 return scope == resolver->nullType() || scope == resolver->voidType();
2668 const QQmlJSScope::ConstPtr &rhs,
2669 const QQmlJSTypeResolver *resolver)
2674 if (resolver->isNumeric(lhs) && resolver->isNumeric(rhs))
2677 if (isVoidOrUndefined(lhs, resolver) || isVoidOrUndefined(rhs, resolver))
2680 if (isStringOrNumberOrBoolean(lhs, resolver)
2681 && !mightContainStringOrNumberOrBoolean(rhs, resolver)) {
2685 if (isStringOrNumberOrBoolean(rhs, resolver)
2686 && !mightContainStringOrNumberOrBoolean(lhs, resolver)) {
2693void QQmlJSTypePropagator::warnAboutTypeCoercion(
int lhs)
2695 const QQmlJSScope::ConstPtr lhsType = checkedInputRegister(lhs).containedType();
2696 const QQmlJSScope::ConstPtr rhsType = m_state.accumulatorIn().containedType();
2698 if (!requiresStrictEquality(lhsType, rhsType, m_typeResolver))
2701 m_logger->log(
"== and != may perform type coercion, use === or !== to avoid it."_L1,
2702 qmlEqualityTypeCoercion, currentNonEmptySourceLocation());
2705void QQmlJSTypePropagator::generate_CmpEqNull()
2707 recordEqualsNullType();
2708 setAccumulator(m_typeResolver->operationType(m_typeResolver->boolType()));
2711void QQmlJSTypePropagator::generate_CmpNeNull()
2713 recordEqualsNullType();
2714 setAccumulator(m_typeResolver->operationType(m_typeResolver->boolType()));
2717void QQmlJSTypePropagator::generate_CmpEqInt(
int lhsConst)
2719 recordEqualsIntType();
2721 setAccumulator(m_typeResolver->typeForBinaryOperation(
2722 QSOperator::Op::Equal, m_typeResolver->literalType(m_typeResolver->int32Type()),
2723 m_state.accumulatorIn()));
2726void QQmlJSTypePropagator::generate_CmpNeInt(
int lhsConst)
2728 recordEqualsIntType();
2730 setAccumulator(m_typeResolver->typeForBinaryOperation(
2731 QSOperator::Op::NotEqual, m_typeResolver->literalType(m_typeResolver->int32Type()),
2732 m_state.accumulatorIn()));
2735void QQmlJSTypePropagator::generate_CmpEq(
int lhs)
2737 warnAboutTypeCoercion(lhs);
2738 recordEqualsType(lhs);
2739 propagateBinaryOperation(QSOperator::Op::Equal, lhs);
2742void QQmlJSTypePropagator::generate_CmpNe(
int lhs)
2744 warnAboutTypeCoercion(lhs);
2745 recordEqualsType(lhs);
2746 propagateBinaryOperation(QSOperator::Op::NotEqual, lhs);
2749void QQmlJSTypePropagator::generate_CmpGt(
int lhs)
2751 recordCompareType(lhs);
2752 propagateBinaryOperation(QSOperator::Op::Gt, lhs);
2755void QQmlJSTypePropagator::generate_CmpGe(
int lhs)
2757 recordCompareType(lhs);
2758 propagateBinaryOperation(QSOperator::Op::Ge, lhs);
2761void QQmlJSTypePropagator::generate_CmpLt(
int lhs)
2763 recordCompareType(lhs);
2764 propagateBinaryOperation(QSOperator::Op::Lt, lhs);
2767void QQmlJSTypePropagator::generate_CmpLe(
int lhs)
2769 recordCompareType(lhs);
2770 propagateBinaryOperation(QSOperator::Op::Le, lhs);
2773void QQmlJSTypePropagator::generate_CmpStrictEqual(
int lhs)
2775 recordEqualsType(lhs);
2776 propagateBinaryOperation(QSOperator::Op::StrictEqual, lhs);
2779void QQmlJSTypePropagator::generate_CmpStrictNotEqual(
int lhs)
2781 recordEqualsType(lhs);
2782 propagateBinaryOperation(QSOperator::Op::StrictNotEqual, lhs);
2785void QQmlJSTypePropagator::generate_CmpIn(
int lhs)
2791 addReadRegister(lhs, m_typeResolver->jsValueType());
2792 addReadAccumulator(m_typeResolver->jsValueType());
2794 propagateBinaryOperation(QSOperator::Op::In, lhs);
2797void QQmlJSTypePropagator::generate_CmpInstanceOf(
int lhs)
2803void QQmlJSTypePropagator::generate_As(
int lhs)
2805 const QQmlJSRegisterContent input = checkedInputRegister(lhs);
2806 const QQmlJSScope::ConstPtr inContained = input.containedType();
2808 QQmlJSRegisterContent output;
2810 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
2811 switch (accumulatorIn.variant()) {
2812 case QQmlJSRegisterContent::Attachment:
2813 output = accumulatorIn.scope();
2815 case QQmlJSRegisterContent::MetaType:
2816 output = accumulatorIn.scope();
2817 if (output.containedType()->isComposite())
2818 addReadAccumulator(m_typeResolver->metaObjectType());
2821 output = accumulatorIn;
2825 QQmlJSScope::ConstPtr outContained = output.containedType();
2827 if (outContained->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
2831 if (m_typeResolver->inherits(inContained, outContained))
2832 output = m_pool->clone(input);
2834 output = m_pool->castTo(input, outContained);
2835 }
else if (m_typeResolver->inherits(inContained, outContained)) {
2837 output = m_pool->castTo(input, outContained);
2841 output = m_typeResolver->merge(
2842 m_pool->castTo(input, outContained),
2843 m_pool->castTo(input, m_typeResolver->voidType()));
2846 addReadRegister(lhs);
2847 setAccumulator(output);
2850void QQmlJSTypePropagator::checkConversion(
2851 QQmlJSRegisterContent from, QQmlJSRegisterContent to)
2853 if (!canConvertFromTo(from, to)) {
2854 addError(u"cannot convert from %1 to %2"_s
2855 .arg(from.descriptiveName(), to.descriptiveName()));
2859void QQmlJSTypePropagator::generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator op)
2861 const QQmlJSRegisterContent type = m_typeResolver->typeForArithmeticUnaryOperation(
2862 op, m_state.accumulatorIn());
2863 checkConversion(m_state.accumulatorIn(), type);
2864 addReadAccumulator(type);
2865 setAccumulator(type);
2868void QQmlJSTypePropagator::generate_UNot()
2870 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Not);
2873void QQmlJSTypePropagator::generate_UPlus()
2875 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Plus);
2878void QQmlJSTypePropagator::generate_UMinus()
2880 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Minus);
2883void QQmlJSTypePropagator::generate_UCompl()
2885 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Complement);
2888void QQmlJSTypePropagator::generate_Increment()
2890 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Increment);
2893void QQmlJSTypePropagator::generate_Decrement()
2895 generateUnaryArithmeticOperation(QQmlJSTypeResolver::UnaryOperator::Decrement);
2898void QQmlJSTypePropagator::generateBinaryArithmeticOperation(QSOperator::Op op,
int lhs)
2900 const QQmlJSRegisterContent type = propagateBinaryOperation(op, lhs);
2902 checkConversion(checkedInputRegister(lhs), type);
2903 addReadRegister(lhs, type);
2905 checkConversion(m_state.accumulatorIn(), type);
2906 addReadAccumulator(type);
2909void QQmlJSTypePropagator::generateBinaryConstArithmeticOperation(QSOperator::Op op)
2911 const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
2912 op, m_state.accumulatorIn(),
2913 m_typeResolver->literalType(m_typeResolver->int32Type()));
2915 checkConversion(m_state.accumulatorIn(), type);
2916 addReadAccumulator(type);
2917 setAccumulator(type);
2920void QQmlJSTypePropagator::generate_Add(
int lhs)
2922 generateBinaryArithmeticOperation(QSOperator::Op::Add, lhs);
2925void QQmlJSTypePropagator::generate_BitAnd(
int lhs)
2927 generateBinaryArithmeticOperation(QSOperator::Op::BitAnd, lhs);
2930void QQmlJSTypePropagator::generate_BitOr(
int lhs)
2932 generateBinaryArithmeticOperation(QSOperator::Op::BitOr, lhs);
2935void QQmlJSTypePropagator::generate_BitXor(
int lhs)
2937 generateBinaryArithmeticOperation(QSOperator::Op::BitXor, lhs);
2940void QQmlJSTypePropagator::generate_UShr(
int lhs)
2942 generateBinaryArithmeticOperation(QSOperator::Op::URShift, lhs);
2945void QQmlJSTypePropagator::generate_Shr(
int lhs)
2947 generateBinaryArithmeticOperation(QSOperator::Op::RShift, lhs);
2950void QQmlJSTypePropagator::generate_Shl(
int lhs)
2952 generateBinaryArithmeticOperation(QSOperator::Op::LShift, lhs);
2955void QQmlJSTypePropagator::generate_BitAndConst(
int rhsConst)
2958 generateBinaryConstArithmeticOperation(QSOperator::Op::BitAnd);
2961void QQmlJSTypePropagator::generate_BitOrConst(
int rhsConst)
2964 generateBinaryConstArithmeticOperation(QSOperator::Op::BitOr);
2967void QQmlJSTypePropagator::generate_BitXorConst(
int rhsConst)
2970 generateBinaryConstArithmeticOperation(QSOperator::Op::BitXor);
2973void QQmlJSTypePropagator::generate_UShrConst(
int rhsConst)
2976 generateBinaryConstArithmeticOperation(QSOperator::Op::URShift);
2979void QQmlJSTypePropagator::generate_ShrConst(
int rhsConst)
2982 generateBinaryConstArithmeticOperation(QSOperator::Op::RShift);
2985void QQmlJSTypePropagator::generate_ShlConst(
int rhsConst)
2988 generateBinaryConstArithmeticOperation(QSOperator::Op::LShift);
2991void QQmlJSTypePropagator::generate_Exp(
int lhs)
2993 generateBinaryArithmeticOperation(QSOperator::Op::Exp, lhs);
2996void QQmlJSTypePropagator::generate_Mul(
int lhs)
2998 generateBinaryArithmeticOperation(QSOperator::Op::Mul, lhs);
3001void QQmlJSTypePropagator::generate_Div(
int lhs)
3003 generateBinaryArithmeticOperation(QSOperator::Op::Div, lhs);
3006void QQmlJSTypePropagator::generate_Mod(
int lhs)
3008 generateBinaryArithmeticOperation(QSOperator::Op::Mod, lhs);
3011void QQmlJSTypePropagator::generate_Sub(
int lhs)
3013 generateBinaryArithmeticOperation(QSOperator::Op::Sub, lhs);
3016void QQmlJSTypePropagator::generate_InitializeBlockDeadTemporalZone(
int firstReg,
int count)
3018 setAccumulator(m_typeResolver->literalType(m_typeResolver->emptyType()));
3019 for (
int reg = firstReg, end = firstReg + count; reg < end; ++reg)
3020 setRegister(reg, m_typeResolver->literalType(m_typeResolver->emptyType()));
3023void QQmlJSTypePropagator::generate_ThrowOnNullOrUndefined()
3028void QQmlJSTypePropagator::generate_GetTemplateObject(
int index)
3034QV4::Moth::ByteCodeHandler::Verdict
3035QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type type)
3037 if (m_state.jumpTargets.contains(currentInstructionOffset())) {
3038 if (m_state.skipInstructionsUntilNextJumpTarget) {
3040 m_state.registers.clear();
3041 m_state.skipInstructionsUntilNextJumpTarget =
false;
3043 }
else if (m_state.skipInstructionsUntilNextJumpTarget
3044 && !instructionManipulatesContext(type)) {
3045 return SkipInstruction;
3048 const int currentOffset = currentInstructionOffset();
3060 for (
auto originRegisterStateIt =
3061 m_jumpOriginRegisterStateByTargetInstructionOffset.constFind(currentOffset);
3062 originRegisterStateIt != m_jumpOriginRegisterStateByTargetInstructionOffset.constEnd()
3063 && originRegisterStateIt.key() == currentOffset;
3064 ++originRegisterStateIt) {
3065 auto stateToMerge = *originRegisterStateIt;
3066 for (
auto registerIt = stateToMerge.registers.constBegin(),
3067 end = stateToMerge.registers.constEnd();
3068 registerIt != end; ++registerIt) {
3069 const int registerIndex = registerIt.key();
3071 const VirtualRegister &newType = registerIt.value();
3072 if (!newType.content.isValid()) {
3073 addError(u"When reached from offset %1, %2 is undefined"_s
3074 .arg(stateToMerge.originatingOffset)
3075 .arg(registerName(registerIndex)));
3076 return SkipInstruction;
3079 auto currentRegister = m_state.registers.find(registerIndex);
3080 if (currentRegister != m_state.registers.end())
3081 mergeRegister(registerIndex, newType, currentRegister.value());
3083 mergeRegister(registerIndex, newType, newType);
3087 return ProcessInstruction;
3090bool QQmlJSTypePropagator::populatesAccumulator(QV4::Moth::Instr::Type instr)
const
3093 case QV4::Moth::Instr::Type::CheckException:
3094 case QV4::Moth::Instr::Type::CloneBlockContext:
3095 case QV4::Moth::Instr::Type::ConvertThisToObject:
3096 case QV4::Moth::Instr::Type::CreateCallContext:
3097 case QV4::Moth::Instr::Type::DeadTemporalZoneCheck:
3098 case QV4::Moth::Instr::Type::Debug:
3099 case QV4::Moth::Instr::Type::DeclareVar:
3100 case QV4::Moth::Instr::Type::IteratorClose:
3101 case QV4::Moth::Instr::Type::IteratorNext:
3102 case QV4::Moth::Instr::Type::IteratorNextForYieldStar:
3103 case QV4::Moth::Instr::Type::Jump:
3104 case QV4::Moth::Instr::Type::JumpFalse:
3105 case QV4::Moth::Instr::Type::JumpNoException:
3106 case QV4::Moth::Instr::Type::JumpNotUndefined:
3107 case QV4::Moth::Instr::Type::JumpTrue:
3108 case QV4::Moth::Instr::Type::MoveConst:
3109 case QV4::Moth::Instr::Type::MoveReg:
3110 case QV4::Moth::Instr::Type::MoveRegExp:
3111 case QV4::Moth::Instr::Type::PopContext:
3112 case QV4::Moth::Instr::Type::PushBlockContext:
3113 case QV4::Moth::Instr::Type::PushCatchContext:
3114 case QV4::Moth::Instr::Type::PushScriptContext:
3115 case QV4::Moth::Instr::Type::Resume:
3116 case QV4::Moth::Instr::Type::Ret:
3117 case QV4::Moth::Instr::Type::SetException:
3118 case QV4::Moth::Instr::Type::SetLookup:
3119 case QV4::Moth::Instr::Type::SetUnwindHandler:
3120 case QV4::Moth::Instr::Type::StoreElement:
3121 case QV4::Moth::Instr::Type::StoreLocal:
3122 case QV4::Moth::Instr::Type::StoreNameSloppy:
3123 case QV4::Moth::Instr::Type::StoreNameStrict:
3124 case QV4::Moth::Instr::Type::StoreProperty:
3125 case QV4::Moth::Instr::Type::StoreReg:
3126 case QV4::Moth::Instr::Type::StoreScopedLocal:
3127 case QV4::Moth::Instr::Type::StoreSuperProperty:
3128 case QV4::Moth::Instr::Type::ThrowException:
3129 case QV4::Moth::Instr::Type::ThrowOnNullOrUndefined:
3130 case QV4::Moth::Instr::Type::UnwindDispatch:
3131 case QV4::Moth::Instr::Type::UnwindToLabel:
3132 case QV4::Moth::Instr::Type::Yield:
3133 case QV4::Moth::Instr::Type::YieldStar:
3135 case QV4::Moth::Instr::Type::Add:
3136 case QV4::Moth::Instr::Type::As:
3137 case QV4::Moth::Instr::Type::BitAnd:
3138 case QV4::Moth::Instr::Type::BitAndConst:
3139 case QV4::Moth::Instr::Type::BitOr:
3140 case QV4::Moth::Instr::Type::BitOrConst:
3141 case QV4::Moth::Instr::Type::BitXor:
3142 case QV4::Moth::Instr::Type::BitXorConst:
3143 case QV4::Moth::Instr::Type::CallGlobalLookup:
3144 case QV4::Moth::Instr::Type::CallName:
3145 case QV4::Moth::Instr::Type::CallPossiblyDirectEval:
3146 case QV4::Moth::Instr::Type::CallProperty:
3147 case QV4::Moth::Instr::Type::CallPropertyLookup:
3148 case QV4::Moth::Instr::Type::CallQmlContextPropertyLookup:
3149 case QV4::Moth::Instr::Type::CallValue:
3150 case QV4::Moth::Instr::Type::CallWithReceiver:
3151 case QV4::Moth::Instr::Type::CallWithSpread:
3152 case QV4::Moth::Instr::Type::CmpEq:
3153 case QV4::Moth::Instr::Type::CmpEqInt:
3154 case QV4::Moth::Instr::Type::CmpEqNull:
3155 case QV4::Moth::Instr::Type::CmpGe:
3156 case QV4::Moth::Instr::Type::CmpGt:
3157 case QV4::Moth::Instr::Type::CmpIn:
3158 case QV4::Moth::Instr::Type::CmpInstanceOf:
3159 case QV4::Moth::Instr::Type::CmpLe:
3160 case QV4::Moth::Instr::Type::CmpLt:
3161 case QV4::Moth::Instr::Type::CmpNe:
3162 case QV4::Moth::Instr::Type::CmpNeInt:
3163 case QV4::Moth::Instr::Type::CmpNeNull:
3164 case QV4::Moth::Instr::Type::CmpStrictEqual:
3165 case QV4::Moth::Instr::Type::CmpStrictNotEqual:
3166 case QV4::Moth::Instr::Type::Construct:
3167 case QV4::Moth::Instr::Type::ConstructWithSpread:
3168 case QV4::Moth::Instr::Type::CreateClass:
3169 case QV4::Moth::Instr::Type::CreateMappedArgumentsObject:
3170 case QV4::Moth::Instr::Type::CreateRestParameter:
3171 case QV4::Moth::Instr::Type::CreateUnmappedArgumentsObject:
3172 case QV4::Moth::Instr::Type::Decrement:
3173 case QV4::Moth::Instr::Type::DefineArray:
3174 case QV4::Moth::Instr::Type::DefineObjectLiteral:
3175 case QV4::Moth::Instr::Type::DeleteName:
3176 case QV4::Moth::Instr::Type::DeleteProperty:
3177 case QV4::Moth::Instr::Type::DestructureRestElement:
3178 case QV4::Moth::Instr::Type::Div:
3179 case QV4::Moth::Instr::Type::Exp:
3180 case QV4::Moth::Instr::Type::GetException:
3181 case QV4::Moth::Instr::Type::GetIterator:
3182 case QV4::Moth::Instr::Type::GetLookup:
3183 case QV4::Moth::Instr::Type::GetOptionalLookup:
3184 case QV4::Moth::Instr::Type::GetTemplateObject:
3185 case QV4::Moth::Instr::Type::Increment:
3186 case QV4::Moth::Instr::Type::InitializeBlockDeadTemporalZone:
3187 case QV4::Moth::Instr::Type::LoadClosure:
3188 case QV4::Moth::Instr::Type::LoadConst:
3189 case QV4::Moth::Instr::Type::LoadElement:
3190 case QV4::Moth::Instr::Type::LoadFalse:
3191 case QV4::Moth::Instr::Type::LoadGlobalLookup:
3192 case QV4::Moth::Instr::Type::LoadImport:
3193 case QV4::Moth::Instr::Type::LoadInt:
3194 case QV4::Moth::Instr::Type::LoadLocal:
3195 case QV4::Moth::Instr::Type::LoadName:
3196 case QV4::Moth::Instr::Type::LoadNull:
3197 case QV4::Moth::Instr::Type::LoadOptionalProperty:
3198 case QV4::Moth::Instr::Type::LoadProperty:
3199 case QV4::Moth::Instr::Type::LoadQmlContextPropertyLookup:
3200 case QV4::Moth::Instr::Type::LoadReg:
3201 case QV4::Moth::Instr::Type::LoadRuntimeString:
3202 case QV4::Moth::Instr::Type::LoadScopedLocal:
3203 case QV4::Moth::Instr::Type::LoadSuperConstructor:
3204 case QV4::Moth::Instr::Type::LoadSuperProperty:
3205 case QV4::Moth::Instr::Type::LoadTrue:
3206 case QV4::Moth::Instr::Type::LoadUndefined:
3207 case QV4::Moth::Instr::Type::LoadZero:
3208 case QV4::Moth::Instr::Type::Mod:
3209 case QV4::Moth::Instr::Type::Mul:
3210 case QV4::Moth::Instr::Type::PushWithContext:
3211 case QV4::Moth::Instr::Type::Shl:
3212 case QV4::Moth::Instr::Type::ShlConst:
3213 case QV4::Moth::Instr::Type::Shr:
3214 case QV4::Moth::Instr::Type::ShrConst:
3215 case QV4::Moth::Instr::Type::Sub:
3216 case QV4::Moth::Instr::Type::TailCall:
3217 case QV4::Moth::Instr::Type::ToObject:
3218 case QV4::Moth::Instr::Type::TypeofName:
3219 case QV4::Moth::Instr::Type::TypeofValue:
3220 case QV4::Moth::Instr::Type::UCompl:
3221 case QV4::Moth::Instr::Type::UMinus:
3222 case QV4::Moth::Instr::Type::UNot:
3223 case QV4::Moth::Instr::Type::UPlus:
3224 case QV4::Moth::Instr::Type::UShr:
3225 case QV4::Moth::Instr::Type::UShrConst:
3228 Q_UNREACHABLE_RETURN(
false);
3232bool QQmlJSTypePropagator::isNoop(QV4::Moth::Instr::Type instr)
const
3235 case QV4::Moth::Instr::Type::DeadTemporalZoneCheck:
3236 case QV4::Moth::Instr::Type::IteratorClose:
3243void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
3245 InstructionAnnotation ¤tInstruction = m_state.annotations[currentInstructionOffset()];
3246 currentInstruction.changedRegister = m_state.changedRegister();
3247 currentInstruction.changedRegisterIndex = m_state.changedRegisterIndex();
3248 currentInstruction.readRegisters = m_state.takeReadRegisters();
3249 currentInstruction.hasExternalSideEffects = m_state.hasExternalSideEffects();
3250 currentInstruction.hasInternalSideEffects = m_state.hasInternalSideEffects();
3251 currentInstruction.isRename = m_state.isRename();
3253 bool populates = populatesAccumulator(instr);
3254 int changedIndex = m_state.changedRegisterIndex();
3257 if (instr != QV4::Moth::Instr::Type::InitializeBlockDeadTemporalZone) {
3258 Q_ASSERT((populates && changedIndex == Accumulator && m_state.accumulatorOut().isValid())
3259 || (!populates && changedIndex != Accumulator));
3262 if (!m_logger->currentFunctionHasCompileError() && !isNoop(instr)) {
3265 Q_ASSERT(m_state.hasInternalSideEffects() || changedIndex != InvalidRegister);
3268 if (changedIndex != InvalidRegister) {
3269 Q_ASSERT(m_logger->currentFunctionHasCompileError() || m_state.changedRegister().isValid());
3270 VirtualRegister &r = m_state.registers[changedIndex];
3271 r.content = m_state.changedRegister();
3273 r.affectedBySideEffects = m_state.isRename()
3274 && m_state.isRegisterAffectedBySideEffects(m_state.renameSourceRegisterIndex());
3275 m_state.clearChangedRegister();
3278 m_state.resetSideEffects();
3279 m_state.setIsRename(
false);
3280 m_state.setReadRegisters(VirtualRegisters());
3281 m_state.instructionHasError =
false;
3284QQmlJSRegisterContent QQmlJSTypePropagator::propagateBinaryOperation(QSOperator::Op op,
int lhs)
3286 auto lhsRegister = checkedInputRegister(lhs);
3287 if (!lhsRegister.isValid())
3288 return QQmlJSRegisterContent();
3290 const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
3291 op, lhsRegister, m_state.accumulatorIn());
3293 setAccumulator(type);
3297static bool deepCompare(
const QQmlJSRegisterContent &a,
const QQmlJSRegisterContent &b)
3299 if (!a.isValid() && !b.isValid())
3302 return a.containedType() == b.containedType()
3303 && a.variant() == b.variant()
3304 && deepCompare(a.scope(), b.scope());
3307void QQmlJSTypePropagator::saveRegisterStateForJump(
int offset)
3309 auto jumpToOffset = offset + nextInstructionOffset();
3310 ExpectedRegisterState state;
3311 state.registers = m_state.registers;
3312 state.originatingOffset = currentInstructionOffset();
3313 m_state.jumpTargets.insert(jumpToOffset);
3317 const auto registerStates =
3318 m_jumpOriginRegisterStateByTargetInstructionOffset.equal_range(jumpToOffset);
3319 for (
auto it = registerStates.first; it != registerStates.second; ++it) {
3320 if (it->registers.keys() != state.registers.keys())
3323 const auto valuesIt = it->registers.values();
3324 const auto valuesState = state.registers.values();
3326 bool different =
false;
3327 for (qsizetype i = 0, end = valuesIt.size(); i != end; ++i) {
3328 const auto &valueIt = valuesIt[i];
3329 const auto &valueState = valuesState[i];
3330 if (valueIt.affectedBySideEffects != valueState.affectedBySideEffects
3331 || valueIt.canMove != valueState.canMove
3332 || valueIt.isShadowable != valueState.isShadowable
3333 || !deepCompare(valueIt.content, valueState.content)) {
3344 m_state.needsMorePasses =
true;
3346 m_jumpOriginRegisterStateByTargetInstructionOffset.insert(jumpToOffset, state);
3349QString QQmlJSTypePropagator::registerName(
int registerIndex)
const
3351 switch (registerIndex) {
3352 case InvalidRegister:
3353 return u"invalid"_s;
3354 case CurrentFunction:
3355 return u"function"_s;
3357 return u"context"_s;
3359 return u"accumulator"_s;
3365 return u"newTarget"_s;
3370 if (isArgument(registerIndex))
3371 return u"argument %1"_s.arg(registerIndex - FirstArgument);
3373 return u"temporary register %1"_s.arg(
3374 registerIndex - FirstArgument - m_function->argumentTypes.size());
3377QQmlJSRegisterContent QQmlJSTypePropagator::checkedInputRegister(
int reg)
3379 const auto regIt = m_state.registers.find(reg);
3380 if (regIt != m_state.registers.end())
3381 return regIt.value().content;
3384 case CurrentFunction:
3385 return m_typeResolver->syntheticType(m_typeResolver->functionType());
3387 return m_typeResolver->syntheticType(m_typeResolver->jsValueType());
3389 addError(u"Type error: no value found in accumulator"_s);
3392 return m_function->qmlScope;
3394 return m_typeResolver->syntheticType(m_typeResolver->int32Type());
3397 return m_typeResolver->syntheticType(m_typeResolver->varType());
3402 if (isArgument(reg))
3403 return argumentType(reg);
3405 addError(u"Type error: could not infer the type of an expression"_s);
3409bool QQmlJSTypePropagator::canConvertFromTo(
3410 QQmlJSRegisterContent from, QQmlJSRegisterContent to)
3412 return m_typeResolver->canConvertFromTo(from, to);
3415bool QQmlJSTypePropagator::canConvertFromTo(
3416 QQmlJSRegisterContent from,
const QQmlJSScope::ConstPtr &to)
3418 return m_typeResolver->canConvertFromTo(from.containedType(), to);
static bool isStringOrNumberOrBoolean(const QQmlJSScope::ConstPtr &scope, const QQmlJSTypeResolver *resolver)
#define INSTR_PROLOGUE_NOT_IMPLEMENTED()
static bool deepCompare(const QQmlJSRegisterContent &a, const QQmlJSRegisterContent &b)
static bool shouldMentionRequiredProperties(const QQmlJSScope::ConstPtr &qmlScope)
#define INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE()
#define INSTR_PROLOGUE_NOT_IMPLEMENTED_POPULATES_ACC()
static bool isLoggingMethod(const QString &consoleMethod)
static bool isVoidOrUndefined(const QQmlJSScope::ConstPtr &scope, const QQmlJSTypeResolver *resolver)
static bool mightContainStringOrNumberOrBoolean(const QQmlJSScope::ConstPtr &scope, const QQmlJSTypeResolver *resolver)
static bool requiresStrictEquality(const QQmlJSScope::ConstPtr &lhs, const QQmlJSScope::ConstPtr &rhs, const QQmlJSTypeResolver *resolver)