286void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(
int index)
291 const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
292 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
294 setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name, index));
296 if (!m_state.accumulatorOut().isValid() && m_typeResolver->isPrefix(name)) {
297 setAccumulator(m_pool->createImportNamespace(
298 nameIndex, m_typeResolver->voidType(), QQmlJSRegisterContent::ModulePrefix,
299 m_function->qmlScope));
303 checkDeprecated(m_function->qmlScope.containedType(), name,
false);
305 const QQmlJSRegisterContent accumulatorOut = m_state.accumulatorOut();
307 if (!accumulatorOut.isValid()) {
308 addError(u"Cannot access value for name "_s + name);
309 handleUnqualifiedAccessAndContextProperties(name,
false);
310 setVarAccumulatorAndError();
314 const QQmlJSScope::ConstPtr retrieved
315 = m_typeResolver->genericType(accumulatorOut.containedType());
317 if (retrieved.isNull()) {
320 addError(u"Cannot determine generic type for "_s + name);
324 if (accumulatorOut.variant() == QQmlJSRegisterContent::ObjectById
325 && !retrieved->isReferenceType()) {
326 addError(u"Cannot retrieve a non-object type by ID: "_s + name);
342void QQmlJSTypePropagator::generate_StoreNameCommon(
int nameIndex)
344 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
345 const QQmlJSRegisterContent type = m_typeResolver->scopedType(m_function->qmlScope, name);
346 const QQmlJSRegisterContent in = m_state.accumulatorIn();
348 if (!type.isValid()) {
349 handleUnqualifiedAccess(name,
false);
350 addError(u"Cannot find name "_s + name);
354 if (!type.isProperty()) {
355 QString message = type.isMethod() ? u"Cannot assign to method %1"_s
356 : u"Cannot assign to non-property %1"_s;
359 m_logger->log(message.arg(name), qmlReadOnlyProperty,
360 currentSourceLocation());
361 addError(u"Cannot assign to non-property "_s + name);
365 if (!type.isWritable() && !type.isList()) {
366 addError(u"Can't assign to read-only property %1"_s.arg(name));
368 m_logger->log(u"Cannot assign to read-only property %1"_s.arg(name), qmlReadOnlyProperty,
369 currentSourceLocation());
374 if (!canConvertFromTo(in, type)) {
375 addError(u"cannot convert from %1 to %2"_s
376 .arg(in.descriptiveName(), type.descriptiveName()));
379 if (m_typeResolver->canHoldUndefined(in) && !m_typeResolver->canHoldUndefined(type)) {
380 if (in.contains(m_typeResolver->voidType()))
381 addReadAccumulator(m_typeResolver->varType());
383 addReadAccumulator();
385 addReadAccumulator(type);
388 m_state.setHasExternalSideEffects();
416void QQmlJSTypePropagator::generate_LoadElement(
int base)
418 const QQmlJSRegisterContent in = m_state.accumulatorIn();
419 const QQmlJSRegisterContent baseRegister = m_state.registers[base].content;
421 const auto fallback = [&]() {
422 const QQmlJSScope::ConstPtr jsValue = m_typeResolver->jsValueType();
424 addReadAccumulator(jsValue);
425 addReadRegister(base, jsValue);
427 QQmlJSMetaProperty property;
428 property.setPropertyName(u"[]"_s);
429 property.setTypeName(jsValue->internalName());
430 property.setType(jsValue);
432 setAccumulator(m_pool->createProperty(
433 property, QQmlJSRegisterContent::InvalidLookupIndex,
434 QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListValue,
435 m_typeResolver->convert(m_typeResolver->elementType(baseRegister), jsValue)));
438 if (baseRegister.isList()) {
439 addReadRegister(base, m_typeResolver->arrayPrototype());
440 }
else if (baseRegister.contains(m_typeResolver->stringType())) {
441 addReadRegister(base, m_typeResolver->stringType());
447 if (m_typeResolver->isNumeric(in)) {
448 const auto contained = in.containedType();
449 if (m_typeResolver->isSignedInteger(contained))
450 addReadAccumulator(m_typeResolver->sizeType());
451 else if (m_typeResolver->isUnsignedInteger(contained))
452 addReadAccumulator(m_typeResolver->uint32Type());
454 addReadAccumulator(m_typeResolver->realType());
455 }
else if (m_typeResolver->isNumeric(m_typeResolver->extractNonVoidFromOptionalType(in))) {
456 addReadAccumulator();
463 setAccumulator(m_typeResolver->merge(
464 m_typeResolver->elementType(baseRegister),
465 m_typeResolver->literalType(m_typeResolver->voidType())));
541void QQmlJSTypePropagator::propagatePropertyLookup(
const QString &propertyName,
int lookupIndex)
543 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
545 m_typeResolver->memberType(
547 accumulatorIn.isImportNamespace()
548 ? m_jsUnitGenerator->stringForIndex(accumulatorIn.importNamespace())
549 + u'.' + propertyName
550 : propertyName, lookupIndex));
552 if (!m_state.accumulatorOut().isValid() && handleImportNamespaceLookup(propertyName))
555 if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::Singleton
556 && accumulatorIn.variant() == QQmlJSRegisterContent::ModulePrefix
557 && !isQmlScopeObject(accumulatorIn.scope())) {
559 u"Cannot access singleton as a property of an object. Did you want to access an attached object?"_s,
560 qmlAccessSingleton, currentSourceLocation());
561 setAccumulator(QQmlJSRegisterContent());
562 }
else if (m_state.accumulatorOut().isEnumeration()) {
563 switch (accumulatorIn.variant()) {
564 case QQmlJSRegisterContent::MetaType:
565 case QQmlJSRegisterContent::Attachment:
566 case QQmlJSRegisterContent::Enum:
567 case QQmlJSRegisterContent::ModulePrefix:
568 case QQmlJSRegisterContent::Singleton:
571 setAccumulator(QQmlJSRegisterContent());
575 if (m_state.instructionHasError || !m_state.accumulatorOut().isValid()) {
576 handleLookupError(propertyName);
580 if (m_state.accumulatorOut().isMethod() && m_state.accumulatorOut().method().size() != 1) {
581 addError(u"Cannot determine overloaded method on loadProperty"_s);
585 if (m_state.accumulatorOut().isProperty()) {
586 const QQmlJSScope::ConstPtr mathObject
587 = m_typeResolver->jsGlobalObject()->property(u"Math"_s).type();
588 if (accumulatorIn.contains(mathObject)) {
589 QQmlJSMetaProperty prop;
590 prop.setPropertyName(propertyName);
591 prop.setTypeName(u"double"_s);
592 prop.setType(m_typeResolver->realType());
594 m_pool->createProperty(
595 prop, accumulatorIn.resultLookupIndex(), lookupIndex,
597 QQmlJSRegisterContent::Property, m_state.accumulatorOut().scope())
603 if (m_state.accumulatorOut().contains(m_typeResolver->voidType())) {
604 addError(u"Type %1 does not have a property %2 for reading"_s
605 .arg(accumulatorIn.descriptiveName(), propertyName));
609 if (!m_state.accumulatorOut().property().type()) {
611 QString::fromLatin1(
"Type of property \"%2\" not found").arg(propertyName),
612 qmlMissingType, currentSourceLocation());
616 switch (m_state.accumulatorOut().variant()) {
617 case QQmlJSRegisterContent::Enum:
618 case QQmlJSRegisterContent::Singleton:
621 if (accumulatorIn.isImportNamespace())
622 addReadAccumulator();
625 addReadAccumulator();
654void QQmlJSTypePropagator::generate_StoreProperty(
int nameIndex,
int base)
656 auto callBase = m_state.registers[base].content;
657 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
659 QQmlJSRegisterContent property = m_typeResolver->memberType(callBase, propertyName);
660 if (!property.isProperty()) {
661 addError(u"Type %1 does not have a property %2 for writing"_s
662 .arg(callBase.descriptiveName(), propertyName));
666 if (property.containedType().isNull()) {
667 addError(u"Cannot determine type for property %1 of type %2"_s.arg(
668 propertyName, callBase.descriptiveName()));
672 if (!property.isWritable() && !property.containedType()->isListProperty()) {
673 addError(u"Can't assign to read-only property %1"_s.arg(propertyName));
675 m_logger->log(u"Cannot assign to read-only property %1"_s.arg(propertyName),
676 qmlReadOnlyProperty, currentSourceLocation());
681 if (!canConvertFromTo(m_state.accumulatorIn(), property)) {
682 addError(u"cannot convert from %1 to %2"_s
683 .arg(m_state.accumulatorIn().descriptiveName(), property.descriptiveName()));
696 const QQmlJSScope::ConstPtr varType = m_typeResolver->varType();
697 const QQmlJSRegisterContent readType = m_typeResolver->canHoldUndefined(m_state.accumulatorIn())
698 ? m_typeResolver->convert(property, varType)
699 : std::move(property);
700 addReadAccumulator(readType);
701 addReadRegister(base);
702 m_state.setHasExternalSideEffects();
797void QQmlJSTypePropagator::generate_CallProperty_SCconsole(
798 const QString &name,
int base,
int argc,
int argv)
801 addReadRegister(base, m_typeResolver->voidType());
804 const QQmlJSRegisterContent firstContent = m_state.registers[argv].content;
805 const QQmlJSScope::ConstPtr firstArg = firstContent.containedType();
806 switch (firstArg->accessSemantics()) {
807 case QQmlJSScope::AccessSemantics::Reference:
810 addReadRegister(argv, m_typeResolver->genericType(firstArg));
812 case QQmlJSScope::AccessSemantics::Sequence:
813 addReadRegister(argv);
816 addReadRegister(argv, m_typeResolver->stringType());
821 for (
int i = 1; i < argc; ++i) {
822 const QQmlJSRegisterContent argContent = m_state.registers[argv + i].content;
823 const QQmlJSScope::ConstPtr arg = argContent.containedType();
824 if (arg->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
825 addReadRegister(argv + i);
827 addReadRegister(argv + i, m_typeResolver->stringType());
835 m_state.setHasExternalSideEffects();
837 QQmlJSRegisterContent console = m_state.registers[base].content;
838 QList<QQmlJSMetaMethod> methods = console.containedType()->ownMethods(name);
839 Q_ASSERT(methods.length() == 1);
843 setAccumulator(m_typeResolver->returnType(
844 methods[0], m_typeResolver->voidType(),
845 m_typeResolver->baseType(console.containedType(), console)));
848void QQmlJSTypePropagator::generate_CallProperty(
int nameIndex,
int base,
int argc,
int argv)
850 Q_ASSERT(m_state.registers.contains(base));
851 const auto callBase = m_state.registers[base].content;
852 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
854 if (callBase.contains(m_typeResolver->mathObject())) {
855 generate_CallProperty_SCMath(propertyName, base, argc, argv);
859 if (callBase.contains(m_typeResolver->consoleObject()) && isLoggingMethod(propertyName)) {
860 generate_CallProperty_SCconsole(propertyName, base, argc, argv);
864 const auto baseType = callBase.containedType();
865 const auto member = m_typeResolver->memberType(callBase, propertyName);
867 if (!member.isMethod()) {
868 if (callBase.contains(m_typeResolver->jsValueType())
869 || callBase.contains(m_typeResolver->varType())) {
870 const auto jsValueType = m_typeResolver->jsValueType();
871 addReadRegister(base, jsValueType);
872 for (
int i = 0; i < argc; ++i)
873 addReadRegister(argv + i, jsValueType);
874 m_state.setHasExternalSideEffects();
876 QQmlJSMetaMethod method;
877 method.setIsJavaScriptFunction(
true);
878 method.setMethodName(propertyName);
879 method.setMethodType(QQmlJSMetaMethod::MethodType::Method);
881 setAccumulator(m_typeResolver->returnType(
882 method, m_typeResolver->jsValueType(), callBase));
886 setVarAccumulatorAndError();
887 addError(u"Type %1 does not have a property %2 for calling"_s
888 .arg(callBase.descriptiveName(), propertyName));
890 if (callBase.isType() && isCallingProperty(callBase.type(), propertyName))
893 if (checkForEnumProblems(callBase, propertyName))
896 std::optional<QQmlJSFixSuggestion> fixSuggestion;
898 if (
auto suggestion = QQmlJSUtils::didYouMean(propertyName, baseType->methods().keys(),
899 m_logger->filePath(), currentSourceLocation());
900 suggestion.has_value()) {
901 fixSuggestion = suggestion;
904 if (baseType->isFullyResolved() || baseType->isScript()) {
905 m_logger->log(u"Member \"%1\" not found on type \"%2\""_s.arg(
906 propertyName, callBase.containedTypeName()),
907 qmlMissingProperty, currentSourceLocation(),
true,
true, fixSuggestion);
912 checkDeprecated(baseType, propertyName,
true);
914 addReadRegister(base);
916 if (callBase.contains(m_typeResolver->stringType())) {
917 if (propertyName == u"arg"_s && argc == 1) {
918 propagateStringArgCall(callBase, argv);
923 if (baseType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
924 && member.scope().contains(m_typeResolver->arrayPrototype())
925 && propagateArrayMethod(propertyName, argc, argv, callBase)) {
929 propagateCall(member.method(), argc, argv, member.scope());
932QQmlJSMetaMethod QQmlJSTypePropagator::bestMatchForCall(
const QList<QQmlJSMetaMethod> &methods,
933 int argc,
int argv, QStringList *errors)
935 QQmlJSMetaMethod javascriptFunction;
936 QQmlJSMetaMethod candidate;
937 bool hasMultipleCandidates =
false;
939 for (
const auto &method : methods) {
942 if (method.isJavaScriptFunction() && !javascriptFunction.isValid())
943 javascriptFunction = method;
945 if (method.returnType().isNull() && !method.returnTypeName().isEmpty()) {
946 errors->append(u"return type %1 cannot be resolved"_s
947 .arg(method.returnTypeName()));
951 const auto arguments = method.parameters();
952 if (argc != arguments.size()) {
954 u"Function expects %1 arguments, but %2 were provided"_s.arg(arguments.size())
959 bool fuzzyMatch =
true;
960 bool exactMatch =
true;
961 for (
int i = 0; i < argc; ++i) {
962 const auto argumentType = arguments[i].type();
963 if (argumentType.isNull()) {
965 u"type %1 for argument %2 cannot be resolved"_s.arg(arguments[i].typeName())
972 const auto content = m_state.registers[argv + i].content;
973 if (content.contains(argumentType))
977 if (canConvertFromTo(content, argumentType))
981 if (argumentType->isReferenceType()
982 && m_typeResolver->inherits(
983 argumentType->baseType(), content.containedType())) {
988 u"argument %1 contains %2 but is expected to contain the type %3"_s.arg(i).arg(
989 content.descriptiveName(), arguments[i].typeName()));
996 }
else if (fuzzyMatch) {
997 if (!candidate.isValid())
1000 hasMultipleCandidates =
true;
1004 if (hasMultipleCandidates)
1005 return QQmlJSMetaMethod();
1007 return candidate.isValid() ? candidate : javascriptFunction;
1050void QQmlJSTypePropagator::mergeRegister(
1051 int index,
const VirtualRegister &a,
const VirtualRegister &b)
1053 const VirtualRegister merged = {
1054 (a.content == b.content) ? a.content : m_typeResolver->merge(a.content, b.content),
1055 a.canMove && b.canMove,
1056 a.affectedBySideEffects || b.affectedBySideEffects,
1057 a.isShadowable || b.isShadowable,
1060 Q_ASSERT(merged.content.isValid());
1062 if (!merged.content.isConversion()) {
1064 m_state.annotations[currentInstructionOffset()].typeConversions[index] = merged;
1065 m_state.registers[index] = merged;
1069 auto tryPrevStateConversion = [
this](
int index,
const VirtualRegister &merged) ->
bool {
1070 auto it = m_prevStateAnnotations.find(currentInstructionOffset());
1071 if (it == m_prevStateAnnotations.end())
1074 auto conversion = it->second.typeConversions.find(index);
1075 if (conversion == it->second.typeConversions.end())
1078 const VirtualRegister &lastTry = conversion.value();
1080 Q_ASSERT(lastTry.content.isValid());
1081 if (!lastTry.content.isConversion())
1084 if (lastTry.content.conversionResultType() != merged.content.conversionResultType()
1085 || lastTry.content.conversionOrigins() != merged.content.conversionOrigins()
1086 || lastTry.canMove != merged.canMove
1087 || lastTry.affectedBySideEffects != merged.affectedBySideEffects
1088 || lastTry.isShadowable != merged.isShadowable) {
1093 m_state.annotations[currentInstructionOffset()].typeConversions[index] = lastTry;
1096 Q_ASSERT(!m_state.registers[index].affectedBySideEffects || lastTry.affectedBySideEffects);
1098 m_state.registers[index] = lastTry;
1102 if (!tryPrevStateConversion(index, merged)) {
1104 const VirtualRegister cloned = {
1105 (a == b) ? m_pool->clone(merged.content) : merged.content,
1107 merged.affectedBySideEffects,
1108 merged.isShadowable,
1110 Q_ASSERT(cloned.content.isValid());
1111 m_state.annotations[currentInstructionOffset()].typeConversions[index] = cloned;
1112 m_state.registers[index] = cloned;
1139void QQmlJSTypePropagator::propagateCall(
1140 const QList<QQmlJSMetaMethod> &methods,
int argc,
int argv,
1141 QQmlJSRegisterContent scope)
1144 const QQmlJSMetaMethod match = bestMatchForCall(methods, argc, argv, &errors);
1146 if (!match.isValid()) {
1147 setVarAccumulatorAndError();
1148 if (methods.size() == 1) {
1150 Q_ASSERT(errors.size() == 1);
1151 addError(errors.first());
1152 }
else if (errors.size() < methods.size()) {
1153 addError(u"Multiple matching overrides found. Cannot determine the right one."_s);
1155 addError(u"No matching override found. Candidates:\n"_s + errors.join(u'\n'));
1160 QQmlJSScope::ConstPtr returnType;
1161 if (match.isJavaScriptFunction())
1162 returnType = m_typeResolver->jsValueType();
1163 else if (match.isConstructor())
1164 returnType = scope.containedType();
1166 returnType = match.returnType();
1168 setAccumulator(m_typeResolver->returnType(match, returnType, scope));
1169 if (!m_state.accumulatorOut().isValid())
1170 addError(u"Cannot store return type of method %1()."_s.arg(match.methodName()));
1172 const auto types = match.parameters();
1173 for (
int i = 0; i < argc; ++i) {
1174 if (i < types.size()) {
1175 const QQmlJSScope::ConstPtr type = match.isJavaScriptFunction()
1176 ? m_typeResolver->jsValueType()
1177 : QQmlJSScope::ConstPtr(types.at(i).type());
1178 if (!type.isNull()) {
1179 addReadRegister(argv + i, type);
1183 addReadRegister(argv + i, m_typeResolver->jsValueType());
1185 m_state.setHasExternalSideEffects();
1193bool QQmlJSTypePropagator::propagateTranslationMethod(
1194 const QList<QQmlJSMetaMethod> &methods,
int argc,
int argv)
1196 if (methods.size() != 1)
1199 const QQmlJSMetaMethod method = methods.front();
1200 const QQmlJSScope::ConstPtr intType = m_typeResolver->int32Type();
1201 const QQmlJSScope::ConstPtr stringType = m_typeResolver->stringType();
1203 const QQmlJSRegisterContent returnType = m_typeResolver->returnType(
1204 method, m_typeResolver->stringType(), m_typeResolver->jsGlobalObjectContent());
1206 if (method.methodName() == u"qsTranslate"_s) {
1209 addReadRegister(argv + 3, intType);
1212 addReadRegister(argv + 2, stringType);
1215 addReadRegister(argv + 1, stringType);
1216 addReadRegister(argv, stringType);
1217 setAccumulator(returnType);
1218 propagateTranslationMethod_SAcheck(method.methodName());
1225 if (method.methodName() == u"QT_TRANSLATE_NOOP"_s) {
1228 addReadRegister(argv + 2, stringType);
1231 addReadRegister(argv + 1, stringType);
1232 addReadRegister(argv, stringType);
1233 setAccumulator(returnType);
1234 propagateTranslationMethod_SAcheck(method.methodName());
1241 if (method.methodName() == u"qsTr"_s) {
1244 addReadRegister(argv + 2, intType);
1247 addReadRegister(argv + 1, stringType);
1250 addReadRegister(argv, stringType);
1251 setAccumulator(returnType);
1252 propagateTranslationMethod_SAcheck(method.methodName());
1259 if (method.methodName() == u"QT_TR_NOOP"_s) {
1262 addReadRegister(argv + 1, stringType);
1265 addReadRegister(argv, stringType);
1266 setAccumulator(returnType);
1267 propagateTranslationMethod_SAcheck(method.methodName());
1274 if (method.methodName() == u"qsTrId"_s) {
1277 addReadRegister(argv + 1, intType);
1280 addReadRegister(argv, stringType);
1281 setAccumulator(returnType);
1282 propagateTranslationMethod_SAcheck(method.methodName());
1289 if (method.methodName() == u"QT_TRID_NOOP"_s) {
1292 addReadRegister(argv, stringType);
1293 setAccumulator(returnType);
1294 propagateTranslationMethod_SAcheck(method.methodName());
1339bool QQmlJSTypePropagator::propagateArrayMethod(
1340 const QString &name,
int argc,
int argv, QQmlJSRegisterContent baseType)
1355 const auto intType = m_typeResolver->int32Type();
1356 const auto stringType = m_typeResolver->stringType();
1357 const auto baseContained = baseType.containedType();
1358 const auto elementContained = baseContained->elementType();
1360 const auto setReturnType = [&](
const QQmlJSScope::ConstPtr type) {
1361 QQmlJSMetaMethod method;
1362 method.setIsJavaScriptFunction(
true);
1363 method.setMethodName(name);
1364 setAccumulator(m_typeResolver->returnType(method, type, baseType));
1367 if (name == u"copyWithin" && argc > 0 && argc < 4) {
1368 for (
int i = 0; i < argc; ++i) {
1369 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1373 for (
int i = 0; i < argc; ++i)
1374 addReadRegister(argv + i, intType);
1376 m_state.setHasExternalSideEffects();
1377 setReturnType(baseContained);
1381 if (name == u"fill" && argc > 0 && argc < 4) {
1382 if (!canConvertFromTo(m_state.registers[argv].content, elementContained))
1385 for (
int i = 1; i < argc; ++i) {
1386 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1390 addReadRegister(argv, elementContained);
1392 for (
int i = 1; i < argc; ++i)
1393 addReadRegister(argv + i, intType);
1395 m_state.setHasExternalSideEffects();
1396 setReturnType(baseContained);
1400 if (name == u"includes" && argc > 0 && argc < 3) {
1401 if (!canConvertFromTo(m_state.registers[argv].content, elementContained))
1405 if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
1407 addReadRegister(argv + 1, intType);
1410 addReadRegister(argv, elementContained);
1411 setReturnType(m_typeResolver->boolType());
1415 if (name == u"toString" || (name == u"join" && argc < 2)) {
1417 if (!canConvertFromTo(m_state.registers[argv].content, stringType))
1419 addReadRegister(argv, stringType);
1422 setReturnType(m_typeResolver->stringType());
1426 if ((name == u"pop" || name == u"shift") && argc == 0) {
1427 m_state.setHasExternalSideEffects();
1428 setReturnType(elementContained);
1432 if (name == u"push" || name == u"unshift") {
1433 for (
int i = 0; i < argc; ++i) {
1434 if (!canConvertFromTo(m_state.registers[argv + i].content, elementContained))
1438 for (
int i = 0; i < argc; ++i)
1439 addReadRegister(argv + i, elementContained);
1441 m_state.setHasExternalSideEffects();
1442 setReturnType(m_typeResolver->int32Type());
1446 if (name == u"reverse" && argc == 0) {
1447 m_state.setHasExternalSideEffects();
1448 setReturnType(baseContained);
1452 if (name == u"slice" && argc < 3) {
1453 for (
int i = 0; i < argc; ++i) {
1454 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1458 for (
int i = 0; i < argc; ++i)
1459 addReadRegister(argv + i, intType);
1461 setReturnType(baseType.containedType()->isListProperty()
1462 ? m_typeResolver->qObjectListType()
1467 if (name == u"splice" && argc > 0) {
1468 const int startAndDeleteCount = std::min(argc, 2);
1469 for (
int i = 0; i < startAndDeleteCount; ++i) {
1470 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1474 for (
int i = 2; i < argc; ++i) {
1475 if (!canConvertFromTo(m_state.registers[argv + i].content, elementContained))
1479 for (
int i = 0; i < startAndDeleteCount; ++i)
1480 addReadRegister(argv + i, intType);
1482 for (
int i = 2; i < argc; ++i)
1483 addReadRegister(argv + i, elementContained);
1485 m_state.setHasExternalSideEffects();
1486 setReturnType(baseContained);
1490 if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
1491 if (!canConvertFromTo(m_state.registers[argv].content, elementContained))
1495 if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
1497 addReadRegister(argv + 1, intType);
1500 addReadRegister(argv, elementContained);
1501 setReturnType(m_typeResolver->int32Type());
1628void QQmlJSTypePropagator::generate_Construct(
int func,
int argc,
int argv)
1630 const QQmlJSRegisterContent type = m_state.registers[func].content;
1631 if (type.contains(m_typeResolver->metaObjectType())) {
1632 const QQmlJSRegisterContent valueType = type.scope();
1633 const QQmlJSScope::ConstPtr contained = type.scopeType();
1634 if (contained->isValueType() && contained->isCreatable()) {
1635 const auto extension = contained->extensionType();
1636 if (extension.extensionSpecifier == QQmlJSScope::ExtensionType) {
1638 extension.scope->ownMethods(extension.scope->internalName()),
1639 argc, argv, valueType);
1642 contained->ownMethods(contained->internalName()), argc, argv, valueType);
1648 if (!type.isMethod()) {
1649 m_state.setHasExternalSideEffects();
1650 QQmlJSMetaMethod method;
1651 method.setMethodName(type.containedTypeName());
1652 method.setIsJavaScriptFunction(
true);
1653 method.setIsConstructor(
true);
1654 setAccumulator(m_typeResolver->returnType(method, m_typeResolver->jsValueType(), {}));
1658 if (
const auto methods = type.method();
1659 methods == m_typeResolver->jsGlobalObject()->methods(u"Date"_s)) {
1660 Q_ASSERT(methods.length() == 1);
1661 generate_Construct_SCDate(methods[0], argc, argv);
1665 if (
const auto methods = type.method();
1666 methods == m_typeResolver->jsGlobalObject()->methods(u"Array"_s)) {
1667 Q_ASSERT(methods.length() == 1);
1668 generate_Construct_SCArray(methods[0], argc, argv);
1672 m_state.setHasExternalSideEffects();
1675 QQmlJSMetaMethod match = bestMatchForCall(type.method(), argc, argv, &errors);
1676 if (!match.isValid())
1677 addError(u"Cannot determine matching constructor. Candidates:\n"_s + errors.join(u'\n'));
1678 setAccumulator(m_typeResolver->returnType(match, m_typeResolver->jsValueType(), {}));
2026void QQmlJSTypePropagator::recordEqualsType(
int lhs)
2028 const auto isNumericOrEnum = [
this](QQmlJSRegisterContent content) {
2029 return content.isEnumeration() || m_typeResolver->isNumeric(content);
2032 const auto accumulatorIn = m_state.accumulatorIn();
2033 const auto lhsRegister = m_state.registers[lhs].content;
2036 if (m_typeResolver->isPrimitive(accumulatorIn) || accumulatorIn.isEnumeration()) {
2037 if (accumulatorIn.contains(lhsRegister.containedType())
2038 || (isNumericOrEnum(accumulatorIn) && isNumericOrEnum(lhsRegister))
2039 || m_typeResolver->isPrimitive(lhsRegister)) {
2040 addReadRegister(lhs);
2041 addReadAccumulator();
2046 const auto containedAccumulatorIn = m_typeResolver->isOptionalType(accumulatorIn)
2047 ? m_typeResolver->extractNonVoidFromOptionalType(accumulatorIn).containedType()
2048 : accumulatorIn.containedType();
2050 const auto containedLhs = m_typeResolver->isOptionalType(lhsRegister)
2051 ? m_typeResolver->extractNonVoidFromOptionalType(lhsRegister).containedType()
2052 : lhsRegister.containedType();
2055 if (QQmlJSUtils::canStrictlyCompareWithVar(m_typeResolver, containedLhs, containedAccumulatorIn)
2056 || QQmlJSUtils::canCompareWithQObject(m_typeResolver, containedLhs, containedAccumulatorIn)
2057 || QQmlJSUtils::canCompareWithQUrl(m_typeResolver, containedLhs, containedAccumulatorIn)) {
2058 addReadRegister(lhs);
2059 addReadAccumulator();
2066 const QQmlJSScope::ConstPtr jsval = m_typeResolver->jsValueType();
2067 addReadRegister(lhs, jsval);
2068 addReadAccumulator(jsval);
2199void QQmlJSTypePropagator::generate_As(
int lhs)
2201 const QQmlJSRegisterContent input = checkedInputRegister(lhs);
2202 const QQmlJSScope::ConstPtr inContained = input.containedType();
2204 QQmlJSRegisterContent output;
2206 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
2207 switch (accumulatorIn.variant()) {
2208 case QQmlJSRegisterContent::Attachment:
2209 output = accumulatorIn.scope();
2211 case QQmlJSRegisterContent::MetaType:
2212 output = accumulatorIn.scope();
2213 if (output.containedType()->isComposite())
2214 addReadAccumulator(m_typeResolver->metaObjectType());
2217 output = accumulatorIn;
2221 QQmlJSScope::ConstPtr outContained = output.containedType();
2223 if (outContained->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
2227 if (m_typeResolver->inherits(inContained, outContained))
2228 output = m_pool->clone(input);
2230 output = m_pool->castTo(input, outContained);
2231 }
else if (m_typeResolver->inherits(inContained, outContained)) {
2233 output = m_pool->castTo(input, outContained);
2237 output = m_typeResolver->merge(
2238 m_pool->castTo(input, outContained),
2239 m_pool->castTo(input, m_typeResolver->voidType()));
2242 addReadRegister(lhs);
2243 setAccumulator(output);
2431QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type type)
2433 if (m_state.jumpTargets.contains(currentInstructionOffset())) {
2434 if (m_state.skipInstructionsUntilNextJumpTarget) {
2436 m_state.registers.clear();
2437 m_state.skipInstructionsUntilNextJumpTarget =
false;
2439 }
else if (m_state.skipInstructionsUntilNextJumpTarget
2440 && !instructionManipulatesContext(type)) {
2441 return SkipInstruction;
2444 const int currentOffset = currentInstructionOffset();
2456 for (
auto originRegisterStateIt =
2457 m_jumpOriginRegisterStateByTargetInstructionOffset.constFind(currentOffset);
2458 originRegisterStateIt != m_jumpOriginRegisterStateByTargetInstructionOffset.constEnd()
2459 && originRegisterStateIt.key() == currentOffset;
2460 ++originRegisterStateIt) {
2461 auto stateToMerge = *originRegisterStateIt;
2462 for (
auto registerIt = stateToMerge.registers.constBegin(),
2463 end = stateToMerge.registers.constEnd();
2464 registerIt != end; ++registerIt) {
2465 const int registerIndex = registerIt.key();
2467 const VirtualRegister &newType = registerIt.value();
2468 if (!newType.content.isValid()) {
2469 addError(u"When reached from offset %1, %2 is undefined"_s
2470 .arg(stateToMerge.originatingOffset)
2471 .arg(registerName(registerIndex)));
2472 return SkipInstruction;
2475 auto currentRegister = m_state.registers.find(registerIndex);
2476 if (currentRegister != m_state.registers.end())
2477 mergeRegister(registerIndex, newType, currentRegister.value());
2479 mergeRegister(registerIndex, newType, newType);
2483 return ProcessInstruction;
2486bool QQmlJSTypePropagator::populatesAccumulator(QV4::Moth::Instr::Type instr)
const
2489 case QV4::Moth::Instr::Type::CheckException:
2490 case QV4::Moth::Instr::Type::CloneBlockContext:
2491 case QV4::Moth::Instr::Type::ConvertThisToObject:
2492 case QV4::Moth::Instr::Type::CreateCallContext:
2493 case QV4::Moth::Instr::Type::DeadTemporalZoneCheck:
2494 case QV4::Moth::Instr::Type::Debug:
2495 case QV4::Moth::Instr::Type::DeclareVar:
2496 case QV4::Moth::Instr::Type::IteratorClose:
2497 case QV4::Moth::Instr::Type::IteratorNext:
2498 case QV4::Moth::Instr::Type::IteratorNextForYieldStar:
2499 case QV4::Moth::Instr::Type::Jump:
2500 case QV4::Moth::Instr::Type::JumpFalse:
2501 case QV4::Moth::Instr::Type::JumpNoException:
2502 case QV4::Moth::Instr::Type::JumpNotUndefined:
2503 case QV4::Moth::Instr::Type::JumpTrue:
2504 case QV4::Moth::Instr::Type::MoveConst:
2505 case QV4::Moth::Instr::Type::MoveReg:
2506 case QV4::Moth::Instr::Type::MoveRegExp:
2507 case QV4::Moth::Instr::Type::PopContext:
2508 case QV4::Moth::Instr::Type::PushBlockContext:
2509 case QV4::Moth::Instr::Type::PushCatchContext:
2510 case QV4::Moth::Instr::Type::PushScriptContext:
2511 case QV4::Moth::Instr::Type::Resume:
2512 case QV4::Moth::Instr::Type::Ret:
2513 case QV4::Moth::Instr::Type::SetException:
2514 case QV4::Moth::Instr::Type::SetLookup:
2515 case QV4::Moth::Instr::Type::SetUnwindHandler:
2516 case QV4::Moth::Instr::Type::StoreElement:
2517 case QV4::Moth::Instr::Type::StoreLocal:
2518 case QV4::Moth::Instr::Type::StoreNameSloppy:
2519 case QV4::Moth::Instr::Type::StoreNameStrict:
2520 case QV4::Moth::Instr::Type::StoreProperty:
2521 case QV4::Moth::Instr::Type::StoreReg:
2522 case QV4::Moth::Instr::Type::StoreScopedLocal:
2523 case QV4::Moth::Instr::Type::StoreSuperProperty:
2524 case QV4::Moth::Instr::Type::ThrowException:
2525 case QV4::Moth::Instr::Type::ThrowOnNullOrUndefined:
2526 case QV4::Moth::Instr::Type::UnwindDispatch:
2527 case QV4::Moth::Instr::Type::UnwindToLabel:
2528 case QV4::Moth::Instr::Type::Yield:
2529 case QV4::Moth::Instr::Type::YieldStar:
2531 case QV4::Moth::Instr::Type::Add:
2532 case QV4::Moth::Instr::Type::As:
2533 case QV4::Moth::Instr::Type::BitAnd:
2534 case QV4::Moth::Instr::Type::BitAndConst:
2535 case QV4::Moth::Instr::Type::BitOr:
2536 case QV4::Moth::Instr::Type::BitOrConst:
2537 case QV4::Moth::Instr::Type::BitXor:
2538 case QV4::Moth::Instr::Type::BitXorConst:
2539 case QV4::Moth::Instr::Type::CallGlobalLookup:
2540 case QV4::Moth::Instr::Type::CallName:
2541 case QV4::Moth::Instr::Type::CallPossiblyDirectEval:
2542 case QV4::Moth::Instr::Type::CallProperty:
2543 case QV4::Moth::Instr::Type::CallPropertyLookup:
2544 case QV4::Moth::Instr::Type::CallQmlContextPropertyLookup:
2545 case QV4::Moth::Instr::Type::CallValue:
2546 case QV4::Moth::Instr::Type::CallWithReceiver:
2547 case QV4::Moth::Instr::Type::CallWithSpread:
2548 case QV4::Moth::Instr::Type::CmpEq:
2549 case QV4::Moth::Instr::Type::CmpEqInt:
2550 case QV4::Moth::Instr::Type::CmpEqNull:
2551 case QV4::Moth::Instr::Type::CmpGe:
2552 case QV4::Moth::Instr::Type::CmpGt:
2553 case QV4::Moth::Instr::Type::CmpIn:
2554 case QV4::Moth::Instr::Type::CmpInstanceOf:
2555 case QV4::Moth::Instr::Type::CmpLe:
2556 case QV4::Moth::Instr::Type::CmpLt:
2557 case QV4::Moth::Instr::Type::CmpNe:
2558 case QV4::Moth::Instr::Type::CmpNeInt:
2559 case QV4::Moth::Instr::Type::CmpNeNull:
2560 case QV4::Moth::Instr::Type::CmpStrictEqual:
2561 case QV4::Moth::Instr::Type::CmpStrictNotEqual:
2562 case QV4::Moth::Instr::Type::Construct:
2563 case QV4::Moth::Instr::Type::ConstructWithSpread:
2564 case QV4::Moth::Instr::Type::CreateClass:
2565 case QV4::Moth::Instr::Type::CreateMappedArgumentsObject:
2566 case QV4::Moth::Instr::Type::CreateRestParameter:
2567 case QV4::Moth::Instr::Type::CreateUnmappedArgumentsObject:
2568 case QV4::Moth::Instr::Type::Decrement:
2569 case QV4::Moth::Instr::Type::DefineArray:
2570 case QV4::Moth::Instr::Type::DefineObjectLiteral:
2571 case QV4::Moth::Instr::Type::DeleteName:
2572 case QV4::Moth::Instr::Type::DeleteProperty:
2573 case QV4::Moth::Instr::Type::DestructureRestElement:
2574 case QV4::Moth::Instr::Type::Div:
2575 case QV4::Moth::Instr::Type::Exp:
2576 case QV4::Moth::Instr::Type::GetException:
2577 case QV4::Moth::Instr::Type::GetIterator:
2578 case QV4::Moth::Instr::Type::GetLookup:
2579 case QV4::Moth::Instr::Type::GetOptionalLookup:
2580 case QV4::Moth::Instr::Type::GetTemplateObject:
2581 case QV4::Moth::Instr::Type::Increment:
2582 case QV4::Moth::Instr::Type::InitializeBlockDeadTemporalZone:
2583 case QV4::Moth::Instr::Type::LoadClosure:
2584 case QV4::Moth::Instr::Type::LoadConst:
2585 case QV4::Moth::Instr::Type::LoadElement:
2586 case QV4::Moth::Instr::Type::LoadFalse:
2587 case QV4::Moth::Instr::Type::LoadGlobalLookup:
2588 case QV4::Moth::Instr::Type::LoadImport:
2589 case QV4::Moth::Instr::Type::LoadInt:
2590 case QV4::Moth::Instr::Type::LoadLocal:
2591 case QV4::Moth::Instr::Type::LoadName:
2592 case QV4::Moth::Instr::Type::LoadNull:
2593 case QV4::Moth::Instr::Type::LoadOptionalProperty:
2594 case QV4::Moth::Instr::Type::LoadProperty:
2595 case QV4::Moth::Instr::Type::LoadQmlContextPropertyLookup:
2596 case QV4::Moth::Instr::Type::LoadReg:
2597 case QV4::Moth::Instr::Type::LoadRuntimeString:
2598 case QV4::Moth::Instr::Type::LoadScopedLocal:
2599 case QV4::Moth::Instr::Type::LoadSuperConstructor:
2600 case QV4::Moth::Instr::Type::LoadSuperProperty:
2601 case QV4::Moth::Instr::Type::LoadTrue:
2602 case QV4::Moth::Instr::Type::LoadUndefined:
2603 case QV4::Moth::Instr::Type::LoadZero:
2604 case QV4::Moth::Instr::Type::Mod:
2605 case QV4::Moth::Instr::Type::Mul:
2606 case QV4::Moth::Instr::Type::PushWithContext:
2607 case QV4::Moth::Instr::Type::Shl:
2608 case QV4::Moth::Instr::Type::ShlConst:
2609 case QV4::Moth::Instr::Type::Shr:
2610 case QV4::Moth::Instr::Type::ShrConst:
2611 case QV4::Moth::Instr::Type::Sub:
2612 case QV4::Moth::Instr::Type::TailCall:
2613 case QV4::Moth::Instr::Type::ToObject:
2614 case QV4::Moth::Instr::Type::TypeofName:
2615 case QV4::Moth::Instr::Type::TypeofValue:
2616 case QV4::Moth::Instr::Type::UCompl:
2617 case QV4::Moth::Instr::Type::UMinus:
2618 case QV4::Moth::Instr::Type::UNot:
2619 case QV4::Moth::Instr::Type::UPlus:
2620 case QV4::Moth::Instr::Type::UShr:
2621 case QV4::Moth::Instr::Type::UShrConst:
2624 Q_UNREACHABLE_RETURN(
false);
2639void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
2641 InstructionAnnotation ¤tInstruction = m_state.annotations[currentInstructionOffset()];
2642 currentInstruction.changedRegister = m_state.changedRegister();
2643 currentInstruction.changedRegisterIndex = m_state.changedRegisterIndex();
2644 currentInstruction.readRegisters = m_state.takeReadRegisters();
2645 currentInstruction.hasExternalSideEffects = m_state.hasExternalSideEffects();
2646 currentInstruction.hasInternalSideEffects = m_state.hasInternalSideEffects();
2647 currentInstruction.isRename = m_state.isRename();
2649 bool populates = populatesAccumulator(instr);
2650 int changedIndex = m_state.changedRegisterIndex();
2653 if (instr != QV4::Moth::Instr::Type::InitializeBlockDeadTemporalZone) {
2654 Q_ASSERT((populates && changedIndex == Accumulator && m_state.accumulatorOut().isValid())
2655 || (!populates && changedIndex != Accumulator));
2658 if (!m_logger->currentFunctionHasCompileError() && !isNoop(instr)) {
2661 Q_ASSERT(m_state.hasInternalSideEffects() || changedIndex != InvalidRegister);
2664 if (changedIndex != InvalidRegister) {
2665 Q_ASSERT(m_logger->currentFunctionHasCompileError() || m_state.changedRegister().isValid());
2666 VirtualRegister &r = m_state.registers[changedIndex];
2667 r.content = m_state.changedRegister();
2669 r.affectedBySideEffects = m_state.isRename()
2670 && m_state.isRegisterAffectedBySideEffects(m_state.renameSourceRegisterIndex());
2671 m_state.clearChangedRegister();
2674 m_state.resetSideEffects();
2675 m_state.setIsRename(
false);
2676 m_state.setReadRegisters(VirtualRegisters());
2677 m_state.instructionHasError =
false;