309void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(
int index)
314 const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
315 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
317 setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name, index));
319 if (!m_state.accumulatorOut().isValid() && m_typeResolver->isPrefix(name)) {
320 setAccumulator(m_pool->createImportNamespace(
321 nameIndex, m_typeResolver->voidType(), QQmlJSRegisterContent::ModulePrefix,
322 m_function->qmlScope));
326 checkDeprecated(m_function->qmlScope.containedType(), name,
false);
328 const QQmlJSRegisterContent accumulatorOut = m_state.accumulatorOut();
330 if (!accumulatorOut.isValid()) {
331 addError(u"Cannot access value for name "_s + name);
332 handleUnqualifiedAccessAndContextProperties(name,
false);
333 setVarAccumulatorAndError();
337 const QQmlJSScope::ConstPtr retrieved
338 = m_typeResolver->genericType(accumulatorOut.containedType());
340 if (retrieved.isNull()) {
343 addError(u"Cannot determine generic type for "_s + name);
347 if (accumulatorOut.variant() == QQmlJSRegisterContent::ObjectById
348 && !retrieved->isReferenceType()) {
349 addError(u"Cannot retrieve a non-object type by ID: "_s + name);
365void QQmlJSTypePropagator::generate_StoreNameCommon(
int nameIndex)
367 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
368 const QQmlJSRegisterContent type = m_typeResolver->scopedType(m_function->qmlScope, name);
369 const QQmlJSRegisterContent in = m_state.accumulatorIn();
371 if (!type.isValid()) {
372 handleUnqualifiedAccess(name,
false);
373 addError(u"Cannot find name "_s + name);
377 if (!type.isProperty()) {
378 QString message = type.isMethod() ? u"Cannot assign to method %1"_s
379 : u"Cannot assign to non-property %1"_s;
382 m_logger->log(message.arg(name), qmlReadOnlyProperty,
383 currentSourceLocation());
384 addError(u"Cannot assign to non-property "_s + name);
388 if (!type.isWritable()) {
389 addError(u"Can't assign to read-only property %1"_s.arg(name));
391 m_logger->log(u"Cannot assign to read-only property %1"_s.arg(name), qmlReadOnlyProperty,
392 currentSourceLocation());
397 if (!canConvertFromTo(in, type)) {
398 addError(u"cannot convert from %1 to %2"_s
399 .arg(in.descriptiveName(), type.descriptiveName()));
402 if (m_typeResolver->canHoldUndefined(in) && !m_typeResolver->canHoldUndefined(type)) {
403 if (in.contains(m_typeResolver->voidType()))
404 addReadAccumulator(m_typeResolver->varType());
406 addReadAccumulator();
408 addReadAccumulator(type);
411 m_state.setHasExternalSideEffects();
439void QQmlJSTypePropagator::generate_LoadElement(
int base)
441 const QQmlJSRegisterContent in = m_state.accumulatorIn();
442 const QQmlJSRegisterContent baseRegister = m_state.registers[base].content;
444 const auto fallback = [&]() {
445 const QQmlJSScope::ConstPtr jsValue = m_typeResolver->jsValueType();
447 addReadAccumulator(jsValue);
448 addReadRegister(base, jsValue);
450 QQmlJSMetaProperty property;
451 property.setPropertyName(u"[]"_s);
452 property.setTypeName(jsValue->internalName());
453 property.setType(jsValue);
455 setAccumulator(m_pool->createProperty(
456 property, QQmlJSRegisterContent::InvalidLookupIndex,
457 QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListValue,
458 m_typeResolver->convert(m_typeResolver->elementType(baseRegister), jsValue)));
461 if (baseRegister.isList()) {
462 addReadRegister(base, m_typeResolver->arrayPrototype());
463 }
else if (baseRegister.contains(m_typeResolver->stringType())) {
464 addReadRegister(base, m_typeResolver->stringType());
470 if (m_typeResolver->isNumeric(in)) {
471 const auto contained = in.containedType();
472 if (m_typeResolver->isSignedInteger(contained))
473 addReadAccumulator(m_typeResolver->sizeType());
474 else if (m_typeResolver->isUnsignedInteger(contained))
475 addReadAccumulator(m_typeResolver->uint32Type());
477 addReadAccumulator(m_typeResolver->realType());
478 }
else if (m_typeResolver->isNumeric(m_typeResolver->extractNonVoidFromOptionalType(in))) {
479 addReadAccumulator();
486 setAccumulator(m_typeResolver->merge(
487 m_typeResolver->elementType(baseRegister),
488 m_typeResolver->literalType(m_typeResolver->voidType())));
564void QQmlJSTypePropagator::propagatePropertyLookup(
const QString &propertyName,
int lookupIndex)
566 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
568 m_typeResolver->memberType(
570 accumulatorIn.isImportNamespace()
571 ? m_jsUnitGenerator->stringForIndex(accumulatorIn.importNamespace())
572 + u'.' + propertyName
573 : propertyName, lookupIndex));
575 if (!m_state.accumulatorOut().isValid() && handleImportNamespaceLookup(propertyName))
578 if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::Singleton
579 && accumulatorIn.variant() == QQmlJSRegisterContent::ModulePrefix
580 && !isQmlScopeObject(accumulatorIn.scope())) {
582 u"Cannot access singleton as a property of an object. Did you want to access an attached object?"_s,
583 qmlAccessSingleton, currentSourceLocation());
584 setAccumulator(QQmlJSRegisterContent());
585 }
else if (m_state.accumulatorOut().isEnumeration()) {
586 switch (accumulatorIn.variant()) {
587 case QQmlJSRegisterContent::MetaType:
588 case QQmlJSRegisterContent::Attachment:
589 case QQmlJSRegisterContent::Enum:
590 case QQmlJSRegisterContent::ModulePrefix:
591 case QQmlJSRegisterContent::Singleton:
594 setAccumulator(QQmlJSRegisterContent());
598 if (m_state.instructionHasError || !m_state.accumulatorOut().isValid()) {
599 handleLookupError(propertyName);
603 if (m_state.accumulatorOut().isMethod() && m_state.accumulatorOut().method().size() != 1) {
604 addError(u"Cannot determine overloaded method on loadProperty"_s);
608 if (m_state.accumulatorOut().isProperty()) {
609 const QQmlJSScope::ConstPtr mathObject
610 = m_typeResolver->jsGlobalObject()->property(u"Math"_s).type();
611 if (accumulatorIn.contains(mathObject)) {
612 QQmlJSMetaProperty prop;
613 prop.setPropertyName(propertyName);
614 prop.setTypeName(u"double"_s);
615 prop.setType(m_typeResolver->realType());
617 m_pool->createProperty(
618 prop, accumulatorIn.resultLookupIndex(), lookupIndex,
620 QQmlJSRegisterContent::Property, m_state.accumulatorOut().scope())
626 if (m_state.accumulatorOut().contains(m_typeResolver->voidType())) {
627 addError(u"Type %1 does not have a property %2 for reading"_s
628 .arg(accumulatorIn.descriptiveName(), propertyName));
632 if (!m_state.accumulatorOut().property().type()) {
634 QString::fromLatin1(
"Type of property \"%2\" not found").arg(propertyName),
635 qmlMissingType, currentSourceLocation());
639 switch (m_state.accumulatorOut().variant()) {
640 case QQmlJSRegisterContent::Enum:
641 case QQmlJSRegisterContent::Singleton:
644 if (accumulatorIn.isImportNamespace())
645 addReadAccumulator();
648 addReadAccumulator();
677void QQmlJSTypePropagator::generate_StoreProperty(
int nameIndex,
int base)
679 auto callBase = m_state.registers[base].content;
680 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
682 QQmlJSRegisterContent property = m_typeResolver->memberType(callBase, propertyName);
683 if (!property.isProperty()) {
684 addError(u"Type %1 does not have a property %2 for writing"_s
685 .arg(callBase.descriptiveName(), propertyName));
689 if (property.containedType().isNull()) {
690 addError(u"Cannot determine type for property %1 of type %2"_s.arg(
691 propertyName, callBase.descriptiveName()));
695 if (!property.isWritable() && !property.containedType()->isListProperty()) {
696 addError(u"Can't assign to read-only property %1"_s.arg(propertyName));
698 m_logger->log(u"Cannot assign to read-only property %1"_s.arg(propertyName),
699 qmlReadOnlyProperty, currentSourceLocation());
704 if (!canConvertFromTo(m_state.accumulatorIn(), property)) {
705 addError(u"cannot convert from %1 to %2"_s
706 .arg(m_state.accumulatorIn().descriptiveName(), property.descriptiveName()));
719 const QQmlJSScope::ConstPtr varType = m_typeResolver->varType();
720 const QQmlJSRegisterContent readType = m_typeResolver->canHoldUndefined(m_state.accumulatorIn())
721 ? m_typeResolver->convert(property, varType)
722 : std::move(property);
723 addReadAccumulator(readType);
724 addReadRegister(base);
725 m_state.setHasExternalSideEffects();
820void QQmlJSTypePropagator::generate_CallProperty_SCconsole(
821 const QString &name,
int base,
int argc,
int argv)
824 addReadRegister(base, m_typeResolver->voidType());
827 const QQmlJSRegisterContent firstContent = m_state.registers[argv].content;
828 const QQmlJSScope::ConstPtr firstArg = firstContent.containedType();
829 switch (firstArg->accessSemantics()) {
830 case QQmlJSScope::AccessSemantics::Reference:
833 addReadRegister(argv, m_typeResolver->genericType(firstArg));
835 case QQmlJSScope::AccessSemantics::Sequence:
836 addReadRegister(argv);
839 addReadRegister(argv, m_typeResolver->stringType());
844 for (
int i = 1; i < argc; ++i) {
845 const QQmlJSRegisterContent argContent = m_state.registers[argv + i].content;
846 const QQmlJSScope::ConstPtr arg = argContent.containedType();
847 if (arg->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
848 addReadRegister(argv + i);
850 addReadRegister(argv + i, m_typeResolver->stringType());
858 m_state.setHasExternalSideEffects();
860 QQmlJSRegisterContent console = m_state.registers[base].content;
861 QList<QQmlJSMetaMethod> methods = console.containedType()->ownMethods(name);
862 Q_ASSERT(methods.length() == 1);
866 setAccumulator(m_typeResolver->returnType(
867 methods[0], m_typeResolver->voidType(),
868 m_typeResolver->baseType(console.containedType(), console)));
871void QQmlJSTypePropagator::generate_CallProperty(
int nameIndex,
int base,
int argc,
int argv)
873 Q_ASSERT(m_state.registers.contains(base));
874 const auto callBase = m_state.registers[base].content;
875 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
877 if (callBase.contains(m_typeResolver->mathObject())) {
878 generate_CallProperty_SCMath(propertyName, base, argc, argv);
882 if (callBase.contains(m_typeResolver->consoleObject()) && isLoggingMethod(propertyName)) {
883 generate_CallProperty_SCconsole(propertyName, base, argc, argv);
887 const auto baseType = callBase.containedType();
888 const auto member = m_typeResolver->memberType(callBase, propertyName);
890 if (!member.isMethod()) {
891 if (callBase.contains(m_typeResolver->jsValueType())
892 || callBase.contains(m_typeResolver->varType())) {
893 const auto jsValueType = m_typeResolver->jsValueType();
894 addReadRegister(base, jsValueType);
895 for (
int i = 0; i < argc; ++i)
896 addReadRegister(argv + i, jsValueType);
897 m_state.setHasExternalSideEffects();
899 QQmlJSMetaMethod method;
900 method.setIsJavaScriptFunction(
true);
901 method.setMethodName(propertyName);
902 method.setMethodType(QQmlJSMetaMethod::MethodType::Method);
904 setAccumulator(m_typeResolver->returnType(
905 method, m_typeResolver->jsValueType(), callBase));
909 setVarAccumulatorAndError();
910 addError(u"Type %1 does not have a property %2 for calling"_s
911 .arg(callBase.descriptiveName(), propertyName));
913 if (callBase.isType() && isCallingProperty(callBase.type(), propertyName))
916 if (checkForEnumProblems(callBase, propertyName))
919 std::optional<QQmlJSFixSuggestion> fixSuggestion;
921 if (
auto suggestion = QQmlJSUtils::didYouMean(propertyName, baseType->methods().keys(),
922 currentSourceLocation());
923 suggestion.has_value()) {
924 fixSuggestion = suggestion;
927 m_logger->log(u"Member \"%1\" not found on type \"%2\""_s.arg(
928 propertyName, callBase.containedTypeName()),
929 qmlMissingProperty, currentSourceLocation(),
true,
true, fixSuggestion);
933 checkDeprecated(baseType, propertyName,
true);
935 addReadRegister(base);
937 if (callBase.contains(m_typeResolver->stringType())) {
938 if (propertyName == u"arg"_s && argc == 1) {
939 propagateStringArgCall(callBase, argv);
944 if (baseType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
945 && member.scope().contains(m_typeResolver->arrayPrototype())
946 && propagateArrayMethod(propertyName, argc, argv, callBase)) {
950 propagateCall(member.method(), argc, argv, member.scope());
953QQmlJSMetaMethod QQmlJSTypePropagator::bestMatchForCall(
const QList<QQmlJSMetaMethod> &methods,
954 int argc,
int argv, QStringList *errors)
956 QQmlJSMetaMethod javascriptFunction;
957 QQmlJSMetaMethod candidate;
958 bool hasMultipleCandidates =
false;
960 for (
const auto &method : methods) {
963 if (method.isJavaScriptFunction() && !javascriptFunction.isValid())
964 javascriptFunction = method;
966 if (method.returnType().isNull() && !method.returnTypeName().isEmpty()) {
967 errors->append(u"return type %1 cannot be resolved"_s
968 .arg(method.returnTypeName()));
972 const auto arguments = method.parameters();
973 if (argc != arguments.size()) {
975 u"Function expects %1 arguments, but %2 were provided"_s.arg(arguments.size())
980 bool fuzzyMatch =
true;
981 bool exactMatch =
true;
982 for (
int i = 0; i < argc; ++i) {
983 const auto argumentType = arguments[i].type();
984 if (argumentType.isNull()) {
986 u"type %1 for argument %2 cannot be resolved"_s.arg(arguments[i].typeName())
993 const auto content = m_state.registers[argv + i].content;
994 if (content.contains(argumentType))
998 if (canConvertFromTo(content, argumentType))
1002 if (argumentType->isReferenceType()
1003 && m_typeResolver->inherits(
1004 argumentType->baseType(), content.containedType())) {
1009 u"argument %1 contains %2 but is expected to contain the type %3"_s.arg(i).arg(
1010 content.descriptiveName(), arguments[i].typeName()));
1017 }
else if (fuzzyMatch) {
1018 if (!candidate.isValid())
1021 hasMultipleCandidates =
true;
1025 if (hasMultipleCandidates)
1026 return QQmlJSMetaMethod();
1028 return candidate.isValid() ? candidate : javascriptFunction;
1071void QQmlJSTypePropagator::mergeRegister(
1072 int index,
const VirtualRegister &a,
const VirtualRegister &b)
1074 const VirtualRegister merged = {
1075 (a.content == b.content) ? a.content : m_typeResolver->merge(a.content, b.content),
1076 a.canMove && b.canMove,
1077 a.affectedBySideEffects || b.affectedBySideEffects,
1078 a.isShadowable || b.isShadowable,
1081 Q_ASSERT(merged.content.isValid());
1083 if (!merged.content.isConversion()) {
1085 m_state.annotations[currentInstructionOffset()].typeConversions[index] = merged;
1086 m_state.registers[index] = merged;
1090 auto tryPrevStateConversion = [
this](
int index,
const VirtualRegister &merged) ->
bool {
1091 auto it = m_prevStateAnnotations.find(currentInstructionOffset());
1092 if (it == m_prevStateAnnotations.end())
1095 auto conversion = it->second.typeConversions.find(index);
1096 if (conversion == it->second.typeConversions.end())
1099 const VirtualRegister &lastTry = conversion.value();
1101 Q_ASSERT(lastTry.content.isValid());
1102 if (!lastTry.content.isConversion())
1105 if (lastTry.content.conversionResultType() != merged.content.conversionResultType()
1106 || lastTry.content.conversionOrigins() != merged.content.conversionOrigins()
1107 || lastTry.canMove != merged.canMove
1108 || lastTry.affectedBySideEffects != merged.affectedBySideEffects
1109 || lastTry.isShadowable != merged.isShadowable) {
1114 m_state.annotations[currentInstructionOffset()].typeConversions[index] = lastTry;
1117 Q_ASSERT(!m_state.registers[index].affectedBySideEffects || lastTry.affectedBySideEffects);
1119 m_state.registers[index] = lastTry;
1123 if (!tryPrevStateConversion(index, merged)) {
1125 const VirtualRegister cloned = {
1126 (a == b) ? m_pool->clone(merged.content) : merged.content,
1128 merged.affectedBySideEffects,
1129 merged.isShadowable,
1131 Q_ASSERT(cloned.content.isValid());
1132 m_state.annotations[currentInstructionOffset()].typeConversions[index] = cloned;
1133 m_state.registers[index] = cloned;
1160void QQmlJSTypePropagator::propagateCall(
1161 const QList<QQmlJSMetaMethod> &methods,
int argc,
int argv,
1162 QQmlJSRegisterContent scope)
1165 const QQmlJSMetaMethod match = bestMatchForCall(methods, argc, argv, &errors);
1167 if (!match.isValid()) {
1168 setVarAccumulatorAndError();
1169 if (methods.size() == 1) {
1171 Q_ASSERT(errors.size() == 1);
1172 addError(errors.first());
1173 }
else if (errors.size() < methods.size()) {
1174 addError(u"Multiple matching overrides found. Cannot determine the right one."_s);
1176 addError(u"No matching override found. Candidates:\n"_s + errors.join(u'\n'));
1181 QQmlJSScope::ConstPtr returnType;
1182 if (match.isJavaScriptFunction())
1183 returnType = m_typeResolver->jsValueType();
1184 else if (match.isConstructor())
1185 returnType = scope.containedType();
1187 returnType = match.returnType();
1189 setAccumulator(m_typeResolver->returnType(match, returnType, scope));
1190 if (!m_state.accumulatorOut().isValid())
1191 addError(u"Cannot store return type of method %1()."_s.arg(match.methodName()));
1193 const auto types = match.parameters();
1194 for (
int i = 0; i < argc; ++i) {
1195 if (i < types.size()) {
1196 const QQmlJSScope::ConstPtr type = match.isJavaScriptFunction()
1197 ? m_typeResolver->jsValueType()
1198 : QQmlJSScope::ConstPtr(types.at(i).type());
1199 if (!type.isNull()) {
1200 addReadRegister(argv + i, type);
1204 addReadRegister(argv + i, m_typeResolver->jsValueType());
1206 m_state.setHasExternalSideEffects();
1214bool QQmlJSTypePropagator::propagateTranslationMethod(
1215 const QList<QQmlJSMetaMethod> &methods,
int argc,
int argv)
1217 if (methods.size() != 1)
1220 const QQmlJSMetaMethod method = methods.front();
1221 const QQmlJSScope::ConstPtr intType = m_typeResolver->int32Type();
1222 const QQmlJSScope::ConstPtr stringType = m_typeResolver->stringType();
1224 const QQmlJSRegisterContent returnType = m_typeResolver->returnType(
1225 method, m_typeResolver->stringType(), m_typeResolver->jsGlobalObjectContent());
1227 if (method.methodName() == u"qsTranslate"_s) {
1230 addReadRegister(argv + 3, intType);
1233 addReadRegister(argv + 2, stringType);
1236 addReadRegister(argv + 1, stringType);
1237 addReadRegister(argv, stringType);
1238 setAccumulator(returnType);
1239 propagateTranslationMethod_SAcheck(method.methodName());
1246 if (method.methodName() == u"QT_TRANSLATE_NOOP"_s) {
1249 addReadRegister(argv + 2, stringType);
1252 addReadRegister(argv + 1, stringType);
1253 addReadRegister(argv, stringType);
1254 setAccumulator(returnType);
1255 propagateTranslationMethod_SAcheck(method.methodName());
1262 if (method.methodName() == u"qsTr"_s) {
1265 addReadRegister(argv + 2, intType);
1268 addReadRegister(argv + 1, stringType);
1271 addReadRegister(argv, stringType);
1272 setAccumulator(returnType);
1273 propagateTranslationMethod_SAcheck(method.methodName());
1280 if (method.methodName() == u"QT_TR_NOOP"_s) {
1283 addReadRegister(argv + 1, stringType);
1286 addReadRegister(argv, stringType);
1287 setAccumulator(returnType);
1288 propagateTranslationMethod_SAcheck(method.methodName());
1295 if (method.methodName() == u"qsTrId"_s) {
1298 addReadRegister(argv + 1, intType);
1301 addReadRegister(argv, stringType);
1302 setAccumulator(returnType);
1303 propagateTranslationMethod_SAcheck(method.methodName());
1310 if (method.methodName() == u"QT_TRID_NOOP"_s) {
1313 addReadRegister(argv, stringType);
1314 setAccumulator(returnType);
1315 propagateTranslationMethod_SAcheck(method.methodName());
1360bool QQmlJSTypePropagator::propagateArrayMethod(
1361 const QString &name,
int argc,
int argv, QQmlJSRegisterContent baseType)
1376 const auto intType = m_typeResolver->int32Type();
1377 const auto stringType = m_typeResolver->stringType();
1378 const auto baseContained = baseType.containedType();
1379 const auto elementContained = baseContained->elementType();
1381 const auto setReturnType = [&](
const QQmlJSScope::ConstPtr type) {
1382 QQmlJSMetaMethod method;
1383 method.setIsJavaScriptFunction(
true);
1384 method.setMethodName(name);
1385 setAccumulator(m_typeResolver->returnType(method, type, baseType));
1388 if (name == u"copyWithin" && argc > 0 && argc < 4) {
1389 for (
int i = 0; i < argc; ++i) {
1390 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1394 for (
int i = 0; i < argc; ++i)
1395 addReadRegister(argv + i, intType);
1397 m_state.setHasExternalSideEffects();
1398 setReturnType(baseContained);
1402 if (name == u"fill" && argc > 0 && argc < 4) {
1403 if (!canConvertFromTo(m_state.registers[argv].content, elementContained))
1406 for (
int i = 1; i < argc; ++i) {
1407 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1411 addReadRegister(argv, elementContained);
1413 for (
int i = 1; i < argc; ++i)
1414 addReadRegister(argv + i, intType);
1416 m_state.setHasExternalSideEffects();
1417 setReturnType(baseContained);
1421 if (name == u"includes" && argc > 0 && argc < 3) {
1422 if (!canConvertFromTo(m_state.registers[argv].content, elementContained))
1426 if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
1428 addReadRegister(argv + 1, intType);
1431 addReadRegister(argv, elementContained);
1432 setReturnType(m_typeResolver->boolType());
1436 if (name == u"toString" || (name == u"join" && argc < 2)) {
1438 if (!canConvertFromTo(m_state.registers[argv].content, stringType))
1440 addReadRegister(argv, stringType);
1443 setReturnType(m_typeResolver->stringType());
1447 if ((name == u"pop" || name == u"shift") && argc == 0) {
1448 m_state.setHasExternalSideEffects();
1449 setReturnType(elementContained);
1453 if (name == u"push" || name == u"unshift") {
1454 for (
int i = 0; i < argc; ++i) {
1455 if (!canConvertFromTo(m_state.registers[argv + i].content, elementContained))
1459 for (
int i = 0; i < argc; ++i)
1460 addReadRegister(argv + i, elementContained);
1462 m_state.setHasExternalSideEffects();
1463 setReturnType(m_typeResolver->int32Type());
1467 if (name == u"reverse" && argc == 0) {
1468 m_state.setHasExternalSideEffects();
1469 setReturnType(baseContained);
1473 if (name == u"slice" && argc < 3) {
1474 for (
int i = 0; i < argc; ++i) {
1475 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1479 for (
int i = 0; i < argc; ++i)
1480 addReadRegister(argv + i, intType);
1482 setReturnType(baseType.containedType()->isListProperty()
1483 ? m_typeResolver->qObjectListType()
1488 if (name == u"splice" && argc > 0) {
1489 const int startAndDeleteCount = std::min(argc, 2);
1490 for (
int i = 0; i < startAndDeleteCount; ++i) {
1491 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1495 for (
int i = 2; i < argc; ++i) {
1496 if (!canConvertFromTo(m_state.registers[argv + i].content, elementContained))
1500 for (
int i = 0; i < startAndDeleteCount; ++i)
1501 addReadRegister(argv + i, intType);
1503 for (
int i = 2; i < argc; ++i)
1504 addReadRegister(argv + i, elementContained);
1506 m_state.setHasExternalSideEffects();
1507 setReturnType(baseContained);
1511 if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
1512 if (!canConvertFromTo(m_state.registers[argv].content, elementContained))
1516 if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
1518 addReadRegister(argv + 1, intType);
1521 addReadRegister(argv, elementContained);
1522 setReturnType(m_typeResolver->int32Type());
1649void QQmlJSTypePropagator::generate_Construct(
int func,
int argc,
int argv)
1651 const QQmlJSRegisterContent type = m_state.registers[func].content;
1652 if (type.contains(m_typeResolver->metaObjectType())) {
1653 const QQmlJSRegisterContent valueType = type.scope();
1654 const QQmlJSScope::ConstPtr contained = type.scopeType();
1655 if (contained->isValueType() && contained->isCreatable()) {
1656 const auto extension = contained->extensionType();
1657 if (extension.extensionSpecifier == QQmlJSScope::ExtensionType) {
1659 extension.scope->ownMethods(extension.scope->internalName()),
1660 argc, argv, valueType);
1663 contained->ownMethods(contained->internalName()), argc, argv, valueType);
1669 if (!type.isMethod()) {
1670 m_state.setHasExternalSideEffects();
1671 QQmlJSMetaMethod method;
1672 method.setMethodName(type.containedTypeName());
1673 method.setIsJavaScriptFunction(
true);
1674 method.setIsConstructor(
true);
1675 setAccumulator(m_typeResolver->returnType(method, m_typeResolver->jsValueType(), {}));
1679 if (
const auto methods = type.method();
1680 methods == m_typeResolver->jsGlobalObject()->methods(u"Date"_s)) {
1681 Q_ASSERT(methods.length() == 1);
1682 generate_Construct_SCDate(methods[0], argc, argv);
1686 if (
const auto methods = type.method();
1687 methods == m_typeResolver->jsGlobalObject()->methods(u"Array"_s)) {
1688 Q_ASSERT(methods.length() == 1);
1689 generate_Construct_SCArray(methods[0], argc, argv);
1693 m_state.setHasExternalSideEffects();
1696 QQmlJSMetaMethod match = bestMatchForCall(type.method(), argc, argv, &errors);
1697 if (!match.isValid())
1698 addError(u"Cannot determine matching constructor. Candidates:\n"_s + errors.join(u'\n'));
1699 setAccumulator(m_typeResolver->returnType(match, m_typeResolver->jsValueType(), {}));
2047void QQmlJSTypePropagator::recordEqualsType(
int lhs)
2049 const auto isNumericOrEnum = [
this](QQmlJSRegisterContent content) {
2050 return content.isEnumeration() || m_typeResolver->isNumeric(content);
2053 const auto accumulatorIn = m_state.accumulatorIn();
2054 const auto lhsRegister = m_state.registers[lhs].content;
2057 if (m_typeResolver->isPrimitive(accumulatorIn) || accumulatorIn.isEnumeration()) {
2058 if (accumulatorIn.contains(lhsRegister.containedType())
2059 || (isNumericOrEnum(accumulatorIn) && isNumericOrEnum(lhsRegister))
2060 || m_typeResolver->isPrimitive(lhsRegister)) {
2061 addReadRegister(lhs);
2062 addReadAccumulator();
2067 const auto containedAccumulatorIn = m_typeResolver->isOptionalType(accumulatorIn)
2068 ? m_typeResolver->extractNonVoidFromOptionalType(accumulatorIn).containedType()
2069 : accumulatorIn.containedType();
2071 const auto containedLhs = m_typeResolver->isOptionalType(lhsRegister)
2072 ? m_typeResolver->extractNonVoidFromOptionalType(lhsRegister).containedType()
2073 : lhsRegister.containedType();
2076 if (canStrictlyCompareWithVar(m_typeResolver, containedLhs, containedAccumulatorIn)
2077 || canCompareWithQObject(m_typeResolver, containedLhs, containedAccumulatorIn)
2078 || canCompareWithQUrl(m_typeResolver, containedLhs, containedAccumulatorIn)) {
2079 addReadRegister(lhs);
2080 addReadAccumulator();
2087 const QQmlJSScope::ConstPtr jsval = m_typeResolver->jsValueType();
2088 addReadRegister(lhs, jsval);
2089 addReadAccumulator(jsval);
2220void QQmlJSTypePropagator::generate_As(
int lhs)
2222 const QQmlJSRegisterContent input = checkedInputRegister(lhs);
2223 const QQmlJSScope::ConstPtr inContained = input.containedType();
2225 QQmlJSRegisterContent output;
2227 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
2228 switch (accumulatorIn.variant()) {
2229 case QQmlJSRegisterContent::Attachment:
2230 output = accumulatorIn.scope();
2232 case QQmlJSRegisterContent::MetaType:
2233 output = accumulatorIn.scope();
2234 if (output.containedType()->isComposite())
2235 addReadAccumulator(m_typeResolver->metaObjectType());
2238 output = accumulatorIn;
2242 QQmlJSScope::ConstPtr outContained = output.containedType();
2244 if (outContained->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
2248 if (m_typeResolver->inherits(inContained, outContained))
2249 output = m_pool->clone(input);
2251 output = m_pool->castTo(input, outContained);
2252 }
else if (m_typeResolver->inherits(inContained, outContained)) {
2254 output = m_pool->castTo(input, outContained);
2258 output = m_typeResolver->merge(
2259 m_pool->castTo(input, outContained),
2260 m_pool->castTo(input, m_typeResolver->voidType()));
2263 addReadRegister(lhs);
2264 setAccumulator(output);
2452QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type type)
2454 if (m_state.jumpTargets.contains(currentInstructionOffset())) {
2455 if (m_state.skipInstructionsUntilNextJumpTarget) {
2457 m_state.registers.clear();
2458 m_state.skipInstructionsUntilNextJumpTarget =
false;
2460 }
else if (m_state.skipInstructionsUntilNextJumpTarget
2461 && !instructionManipulatesContext(type)) {
2462 return SkipInstruction;
2465 const int currentOffset = currentInstructionOffset();
2477 for (
auto originRegisterStateIt =
2478 m_jumpOriginRegisterStateByTargetInstructionOffset.constFind(currentOffset);
2479 originRegisterStateIt != m_jumpOriginRegisterStateByTargetInstructionOffset.constEnd()
2480 && originRegisterStateIt.key() == currentOffset;
2481 ++originRegisterStateIt) {
2482 auto stateToMerge = *originRegisterStateIt;
2483 for (
auto registerIt = stateToMerge.registers.constBegin(),
2484 end = stateToMerge.registers.constEnd();
2485 registerIt != end; ++registerIt) {
2486 const int registerIndex = registerIt.key();
2488 const VirtualRegister &newType = registerIt.value();
2489 if (!newType.content.isValid()) {
2490 addError(u"When reached from offset %1, %2 is undefined"_s
2491 .arg(stateToMerge.originatingOffset)
2492 .arg(registerName(registerIndex)));
2493 return SkipInstruction;
2496 auto currentRegister = m_state.registers.find(registerIndex);
2497 if (currentRegister != m_state.registers.end())
2498 mergeRegister(registerIndex, newType, currentRegister.value());
2500 mergeRegister(registerIndex, newType, newType);
2504 return ProcessInstruction;
2507bool QQmlJSTypePropagator::populatesAccumulator(QV4::Moth::Instr::Type instr)
const
2510 case QV4::Moth::Instr::Type::CheckException:
2511 case QV4::Moth::Instr::Type::CloneBlockContext:
2512 case QV4::Moth::Instr::Type::ConvertThisToObject:
2513 case QV4::Moth::Instr::Type::CreateCallContext:
2514 case QV4::Moth::Instr::Type::DeadTemporalZoneCheck:
2515 case QV4::Moth::Instr::Type::Debug:
2516 case QV4::Moth::Instr::Type::DeclareVar:
2517 case QV4::Moth::Instr::Type::IteratorClose:
2518 case QV4::Moth::Instr::Type::IteratorNext:
2519 case QV4::Moth::Instr::Type::IteratorNextForYieldStar:
2520 case QV4::Moth::Instr::Type::Jump:
2521 case QV4::Moth::Instr::Type::JumpFalse:
2522 case QV4::Moth::Instr::Type::JumpNoException:
2523 case QV4::Moth::Instr::Type::JumpNotUndefined:
2524 case QV4::Moth::Instr::Type::JumpTrue:
2525 case QV4::Moth::Instr::Type::MoveConst:
2526 case QV4::Moth::Instr::Type::MoveReg:
2527 case QV4::Moth::Instr::Type::MoveRegExp:
2528 case QV4::Moth::Instr::Type::PopContext:
2529 case QV4::Moth::Instr::Type::PushBlockContext:
2530 case QV4::Moth::Instr::Type::PushCatchContext:
2531 case QV4::Moth::Instr::Type::PushScriptContext:
2532 case QV4::Moth::Instr::Type::Resume:
2533 case QV4::Moth::Instr::Type::Ret:
2534 case QV4::Moth::Instr::Type::SetException:
2535 case QV4::Moth::Instr::Type::SetLookup:
2536 case QV4::Moth::Instr::Type::SetUnwindHandler:
2537 case QV4::Moth::Instr::Type::StoreElement:
2538 case QV4::Moth::Instr::Type::StoreLocal:
2539 case QV4::Moth::Instr::Type::StoreNameSloppy:
2540 case QV4::Moth::Instr::Type::StoreNameStrict:
2541 case QV4::Moth::Instr::Type::StoreProperty:
2542 case QV4::Moth::Instr::Type::StoreReg:
2543 case QV4::Moth::Instr::Type::StoreScopedLocal:
2544 case QV4::Moth::Instr::Type::StoreSuperProperty:
2545 case QV4::Moth::Instr::Type::ThrowException:
2546 case QV4::Moth::Instr::Type::ThrowOnNullOrUndefined:
2547 case QV4::Moth::Instr::Type::UnwindDispatch:
2548 case QV4::Moth::Instr::Type::UnwindToLabel:
2549 case QV4::Moth::Instr::Type::Yield:
2550 case QV4::Moth::Instr::Type::YieldStar:
2552 case QV4::Moth::Instr::Type::Add:
2553 case QV4::Moth::Instr::Type::As:
2554 case QV4::Moth::Instr::Type::BitAnd:
2555 case QV4::Moth::Instr::Type::BitAndConst:
2556 case QV4::Moth::Instr::Type::BitOr:
2557 case QV4::Moth::Instr::Type::BitOrConst:
2558 case QV4::Moth::Instr::Type::BitXor:
2559 case QV4::Moth::Instr::Type::BitXorConst:
2560 case QV4::Moth::Instr::Type::CallGlobalLookup:
2561 case QV4::Moth::Instr::Type::CallName:
2562 case QV4::Moth::Instr::Type::CallPossiblyDirectEval:
2563 case QV4::Moth::Instr::Type::CallProperty:
2564 case QV4::Moth::Instr::Type::CallPropertyLookup:
2565 case QV4::Moth::Instr::Type::CallQmlContextPropertyLookup:
2566 case QV4::Moth::Instr::Type::CallValue:
2567 case QV4::Moth::Instr::Type::CallWithReceiver:
2568 case QV4::Moth::Instr::Type::CallWithSpread:
2569 case QV4::Moth::Instr::Type::CmpEq:
2570 case QV4::Moth::Instr::Type::CmpEqInt:
2571 case QV4::Moth::Instr::Type::CmpEqNull:
2572 case QV4::Moth::Instr::Type::CmpGe:
2573 case QV4::Moth::Instr::Type::CmpGt:
2574 case QV4::Moth::Instr::Type::CmpIn:
2575 case QV4::Moth::Instr::Type::CmpInstanceOf:
2576 case QV4::Moth::Instr::Type::CmpLe:
2577 case QV4::Moth::Instr::Type::CmpLt:
2578 case QV4::Moth::Instr::Type::CmpNe:
2579 case QV4::Moth::Instr::Type::CmpNeInt:
2580 case QV4::Moth::Instr::Type::CmpNeNull:
2581 case QV4::Moth::Instr::Type::CmpStrictEqual:
2582 case QV4::Moth::Instr::Type::CmpStrictNotEqual:
2583 case QV4::Moth::Instr::Type::Construct:
2584 case QV4::Moth::Instr::Type::ConstructWithSpread:
2585 case QV4::Moth::Instr::Type::CreateClass:
2586 case QV4::Moth::Instr::Type::CreateMappedArgumentsObject:
2587 case QV4::Moth::Instr::Type::CreateRestParameter:
2588 case QV4::Moth::Instr::Type::CreateUnmappedArgumentsObject:
2589 case QV4::Moth::Instr::Type::Decrement:
2590 case QV4::Moth::Instr::Type::DefineArray:
2591 case QV4::Moth::Instr::Type::DefineObjectLiteral:
2592 case QV4::Moth::Instr::Type::DeleteName:
2593 case QV4::Moth::Instr::Type::DeleteProperty:
2594 case QV4::Moth::Instr::Type::DestructureRestElement:
2595 case QV4::Moth::Instr::Type::Div:
2596 case QV4::Moth::Instr::Type::Exp:
2597 case QV4::Moth::Instr::Type::GetException:
2598 case QV4::Moth::Instr::Type::GetIterator:
2599 case QV4::Moth::Instr::Type::GetLookup:
2600 case QV4::Moth::Instr::Type::GetOptionalLookup:
2601 case QV4::Moth::Instr::Type::GetTemplateObject:
2602 case QV4::Moth::Instr::Type::Increment:
2603 case QV4::Moth::Instr::Type::InitializeBlockDeadTemporalZone:
2604 case QV4::Moth::Instr::Type::LoadClosure:
2605 case QV4::Moth::Instr::Type::LoadConst:
2606 case QV4::Moth::Instr::Type::LoadElement:
2607 case QV4::Moth::Instr::Type::LoadFalse:
2608 case QV4::Moth::Instr::Type::LoadGlobalLookup:
2609 case QV4::Moth::Instr::Type::LoadImport:
2610 case QV4::Moth::Instr::Type::LoadInt:
2611 case QV4::Moth::Instr::Type::LoadLocal:
2612 case QV4::Moth::Instr::Type::LoadName:
2613 case QV4::Moth::Instr::Type::LoadNull:
2614 case QV4::Moth::Instr::Type::LoadOptionalProperty:
2615 case QV4::Moth::Instr::Type::LoadProperty:
2616 case QV4::Moth::Instr::Type::LoadQmlContextPropertyLookup:
2617 case QV4::Moth::Instr::Type::LoadReg:
2618 case QV4::Moth::Instr::Type::LoadRuntimeString:
2619 case QV4::Moth::Instr::Type::LoadScopedLocal:
2620 case QV4::Moth::Instr::Type::LoadSuperConstructor:
2621 case QV4::Moth::Instr::Type::LoadSuperProperty:
2622 case QV4::Moth::Instr::Type::LoadTrue:
2623 case QV4::Moth::Instr::Type::LoadUndefined:
2624 case QV4::Moth::Instr::Type::LoadZero:
2625 case QV4::Moth::Instr::Type::Mod:
2626 case QV4::Moth::Instr::Type::Mul:
2627 case QV4::Moth::Instr::Type::PushWithContext:
2628 case QV4::Moth::Instr::Type::Shl:
2629 case QV4::Moth::Instr::Type::ShlConst:
2630 case QV4::Moth::Instr::Type::Shr:
2631 case QV4::Moth::Instr::Type::ShrConst:
2632 case QV4::Moth::Instr::Type::Sub:
2633 case QV4::Moth::Instr::Type::TailCall:
2634 case QV4::Moth::Instr::Type::ToObject:
2635 case QV4::Moth::Instr::Type::TypeofName:
2636 case QV4::Moth::Instr::Type::TypeofValue:
2637 case QV4::Moth::Instr::Type::UCompl:
2638 case QV4::Moth::Instr::Type::UMinus:
2639 case QV4::Moth::Instr::Type::UNot:
2640 case QV4::Moth::Instr::Type::UPlus:
2641 case QV4::Moth::Instr::Type::UShr:
2642 case QV4::Moth::Instr::Type::UShrConst:
2645 Q_UNREACHABLE_RETURN(
false);
2660void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
2662 InstructionAnnotation ¤tInstruction = m_state.annotations[currentInstructionOffset()];
2663 currentInstruction.changedRegister = m_state.changedRegister();
2664 currentInstruction.changedRegisterIndex = m_state.changedRegisterIndex();
2665 currentInstruction.readRegisters = m_state.takeReadRegisters();
2666 currentInstruction.hasExternalSideEffects = m_state.hasExternalSideEffects();
2667 currentInstruction.hasInternalSideEffects = m_state.hasInternalSideEffects();
2668 currentInstruction.isRename = m_state.isRename();
2670 bool populates = populatesAccumulator(instr);
2671 int changedIndex = m_state.changedRegisterIndex();
2674 if (instr != QV4::Moth::Instr::Type::InitializeBlockDeadTemporalZone) {
2675 Q_ASSERT((populates && changedIndex == Accumulator && m_state.accumulatorOut().isValid())
2676 || (!populates && changedIndex != Accumulator));
2679 if (!m_logger->currentFunctionHasCompileError() && !isNoop(instr)) {
2682 Q_ASSERT(m_state.hasInternalSideEffects() || changedIndex != InvalidRegister);
2685 if (changedIndex != InvalidRegister) {
2686 Q_ASSERT(m_logger->currentFunctionHasCompileError() || m_state.changedRegister().isValid());
2687 VirtualRegister &r = m_state.registers[changedIndex];
2688 r.content = m_state.changedRegister();
2690 r.affectedBySideEffects = m_state.isRename()
2691 && m_state.isRegisterAffectedBySideEffects(m_state.renameSourceRegisterIndex());
2692 m_state.clearChangedRegister();
2695 m_state.resetSideEffects();
2696 m_state.setIsRename(
false);
2697 m_state.setReadRegisters(VirtualRegisters());
2698 m_state.instructionHasError =
false;