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 QString name = callBase.descriptiveName();
664 addError(u"Type %1does not have a property %2 for writing"_s.arg(name, propertyName));
668 if (property.containedType().isNull()) {
669 addError(u"Cannot determine type for property %1 of type %2"_s.arg(
670 propertyName, callBase.descriptiveName()));
674 if (!property.isWritable() && !property.containedType()->isListProperty()) {
675 addError(u"Can't assign to read-only property %1"_s.arg(propertyName));
677 m_logger->log(u"Cannot assign to read-only property %1"_s.arg(propertyName),
678 qmlReadOnlyProperty, currentSourceLocation());
683 if (!canConvertFromTo(m_state.accumulatorIn(), property)) {
684 addError(u"cannot convert from %1 to %2"_s
685 .arg(m_state.accumulatorIn().descriptiveName(), property.descriptiveName()));
698 const QQmlJSScope::ConstPtr varType = m_typeResolver->varType();
699 const QQmlJSRegisterContent readType = m_typeResolver->canHoldUndefined(m_state.accumulatorIn())
700 ? m_typeResolver->convert(property, varType)
701 : std::move(property);
702 addReadAccumulator(readType);
703 addReadRegister(base);
704 m_state.setHasExternalSideEffects();
799void QQmlJSTypePropagator::generate_CallProperty_SCconsole(
800 const QString &name,
int base,
int argc,
int argv)
803 addReadRegister(base, m_typeResolver->voidType());
806 const QQmlJSRegisterContent firstContent = m_state.registers[argv].content;
807 const QQmlJSScope::ConstPtr firstArg = firstContent.containedType();
808 switch (firstArg->accessSemantics()) {
809 case QQmlJSScope::AccessSemantics::Reference:
812 addReadRegister(argv, m_typeResolver->genericType(firstArg));
814 case QQmlJSScope::AccessSemantics::Sequence:
815 addReadRegister(argv);
818 addReadRegister(argv, m_typeResolver->stringType());
823 for (
int i = 1; i < argc; ++i) {
824 const QQmlJSRegisterContent argContent = m_state.registers[argv + i].content;
825 const QQmlJSScope::ConstPtr arg = argContent.containedType();
826 if (arg->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
827 addReadRegister(argv + i);
829 addReadRegister(argv + i, m_typeResolver->stringType());
837 m_state.setHasExternalSideEffects();
839 QQmlJSRegisterContent console = m_state.registers[base].content;
840 QList<QQmlJSMetaMethod> methods = console.containedType()->ownMethods(name);
841 Q_ASSERT(methods.length() == 1);
845 setAccumulator(m_typeResolver->returnType(
846 methods[0], m_typeResolver->voidType(),
847 m_typeResolver->baseType(console.containedType(), console)));
850void QQmlJSTypePropagator::generate_CallProperty(
int nameIndex,
int base,
int argc,
int argv)
852 Q_ASSERT(m_state.registers.contains(base));
853 const auto callBase = m_state.registers[base].content;
854 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
856 if (callBase.contains(m_typeResolver->mathObject())) {
857 generate_CallProperty_SCMath(propertyName, base, argc, argv);
861 if (callBase.contains(m_typeResolver->consoleObject()) && isLoggingMethod(propertyName)) {
862 generate_CallProperty_SCconsole(propertyName, base, argc, argv);
866 const auto baseType = callBase.containedType();
867 const auto member = m_typeResolver->memberType(callBase, propertyName);
869 if (!member.isMethod()) {
870 if (callBase.contains(m_typeResolver->jsValueType())
871 || callBase.contains(m_typeResolver->varType())) {
872 const auto jsValueType = m_typeResolver->jsValueType();
873 addReadRegister(base, jsValueType);
874 for (
int i = 0; i < argc; ++i)
875 addReadRegister(argv + i, jsValueType);
876 m_state.setHasExternalSideEffects();
878 QQmlJSMetaMethod method;
879 method.setIsJavaScriptFunction(
true);
880 method.setMethodName(propertyName);
881 method.setMethodType(QQmlJSMetaMethod::MethodType::Method);
883 setAccumulator(m_typeResolver->returnType(
884 method, m_typeResolver->jsValueType(), callBase));
888 setVarAccumulatorAndError();
889 addError(u"Type %1 does not have a property %2 for calling"_s
890 .arg(callBase.descriptiveName(), propertyName));
892 if (callBase.isType() && isCallingProperty(callBase.type(), propertyName))
895 if (checkForEnumProblems(callBase, propertyName))
898 std::optional<QQmlJSFixSuggestion> fixSuggestion;
900 if (
auto suggestion = QQmlJSUtils::didYouMean(propertyName, baseType->methods().keys(),
901 m_logger->filePath(), currentSourceLocation());
902 suggestion.has_value()) {
903 fixSuggestion = suggestion;
906 if (baseType->isFullyResolved() || baseType->isScript()) {
907 m_logger->log(u"Member \"%1\" not found on type \"%2\""_s.arg(
908 propertyName, callBase.containedTypeName()),
909 qmlMissingProperty, currentSourceLocation(),
true,
true, fixSuggestion);
914 checkDeprecated(baseType, propertyName,
true);
916 addReadRegister(base);
918 if (callBase.contains(m_typeResolver->stringType())) {
919 if (propertyName == u"arg"_s && argc == 1) {
920 propagateStringArgCall(callBase, argv);
925 if (baseType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
926 && member.scope().contains(m_typeResolver->arrayPrototype())
927 && propagateArrayMethod(propertyName, argc, argv, callBase)) {
931 propagateCall(member.method(), argc, argv, member.scope());
934QQmlJSMetaMethod QQmlJSTypePropagator::bestMatchForCall(
const QList<QQmlJSMetaMethod> &methods,
935 int argc,
int argv, QStringList *errors)
937 QQmlJSMetaMethod javascriptFunction;
938 QQmlJSMetaMethod candidate;
939 bool hasMultipleCandidates =
false;
941 for (
const auto &method : methods) {
944 if (method.isJavaScriptFunction() && !javascriptFunction.isValid())
945 javascriptFunction = method;
947 if (method.returnType().isNull() && !method.returnTypeName().isEmpty()) {
948 errors->append(u"return type %1 cannot be resolved"_s
949 .arg(method.returnTypeName()));
953 const auto arguments = method.parameters();
954 if (argc != arguments.size()) {
956 u"Function expects %1 arguments, but %2 were provided"_s.arg(arguments.size())
961 bool fuzzyMatch =
true;
962 bool exactMatch =
true;
963 for (
int i = 0; i < argc; ++i) {
964 const auto argumentType = arguments[i].type();
965 if (argumentType.isNull()) {
967 u"type %1 for argument %2 cannot be resolved"_s.arg(arguments[i].typeName())
974 const auto content = m_state.registers[argv + i].content;
975 if (content.contains(argumentType))
979 if (canConvertFromTo(content, argumentType))
983 if (argumentType->isReferenceType()
984 && m_typeResolver->inherits(
985 argumentType->baseType(), content.containedType())) {
990 u"argument %1 contains %2 but is expected to contain the type %3"_s.arg(i).arg(
991 content.descriptiveName(), arguments[i].typeName()));
998 }
else if (fuzzyMatch) {
999 if (!candidate.isValid())
1002 hasMultipleCandidates =
true;
1006 if (hasMultipleCandidates)
1007 return QQmlJSMetaMethod();
1009 return candidate.isValid() ? candidate : javascriptFunction;
1052void QQmlJSTypePropagator::mergeRegister(
1053 int index,
const VirtualRegister &a,
const VirtualRegister &b)
1055 const VirtualRegister merged = {
1056 (a.content == b.content) ? a.content : m_typeResolver->merge(a.content, b.content),
1057 a.canMove && b.canMove,
1058 a.affectedBySideEffects || b.affectedBySideEffects,
1059 a.isShadowable || b.isShadowable,
1062 Q_ASSERT(merged.content.isValid());
1064 if (!merged.content.isConversion()) {
1066 m_state.annotations[currentInstructionOffset()].typeConversions[index] = merged;
1067 m_state.registers[index] = merged;
1071 auto tryPrevStateConversion = [
this](
int index,
const VirtualRegister &merged) ->
bool {
1072 auto it = m_prevStateAnnotations.find(currentInstructionOffset());
1073 if (it == m_prevStateAnnotations.end())
1076 auto conversion = it->second.typeConversions.find(index);
1077 if (conversion == it->second.typeConversions.end())
1080 const VirtualRegister &lastTry = conversion.value();
1082 Q_ASSERT(lastTry.content.isValid());
1083 if (!lastTry.content.isConversion())
1086 if (lastTry.content.conversionResultType() != merged.content.conversionResultType()
1087 || lastTry.content.conversionOrigins() != merged.content.conversionOrigins()
1088 || lastTry.canMove != merged.canMove
1089 || lastTry.affectedBySideEffects != merged.affectedBySideEffects
1090 || lastTry.isShadowable != merged.isShadowable) {
1095 m_state.annotations[currentInstructionOffset()].typeConversions[index] = lastTry;
1098 Q_ASSERT(!m_state.registers[index].affectedBySideEffects || lastTry.affectedBySideEffects);
1100 m_state.registers[index] = lastTry;
1104 if (!tryPrevStateConversion(index, merged)) {
1106 const VirtualRegister cloned = {
1107 (a == b) ? m_pool->clone(merged.content) : merged.content,
1109 merged.affectedBySideEffects,
1110 merged.isShadowable,
1112 Q_ASSERT(cloned.content.isValid());
1113 m_state.annotations[currentInstructionOffset()].typeConversions[index] = cloned;
1114 m_state.registers[index] = cloned;
1141void QQmlJSTypePropagator::propagateCall(
1142 const QList<QQmlJSMetaMethod> &methods,
int argc,
int argv,
1143 QQmlJSRegisterContent scope)
1146 const QQmlJSMetaMethod match = bestMatchForCall(methods, argc, argv, &errors);
1148 if (!match.isValid()) {
1149 setVarAccumulatorAndError();
1150 if (methods.size() == 1) {
1152 Q_ASSERT(errors.size() == 1);
1153 addError(errors.first());
1154 }
else if (errors.size() < methods.size()) {
1155 addError(u"Multiple matching overrides found. Cannot determine the right one."_s);
1157 addError(u"No matching override found. Candidates:\n"_s + errors.join(u'\n'));
1162 QQmlJSScope::ConstPtr returnType;
1163 if (match.isJavaScriptFunction())
1164 returnType = m_typeResolver->jsValueType();
1165 else if (match.isConstructor())
1166 returnType = scope.containedType();
1168 returnType = match.returnType();
1170 setAccumulator(m_typeResolver->returnType(match, returnType, scope));
1171 if (!m_state.accumulatorOut().isValid())
1172 addError(u"Cannot store return type of method %1()."_s.arg(match.methodName()));
1174 const auto types = match.parameters();
1175 for (
int i = 0; i < argc; ++i) {
1176 if (i < types.size()) {
1177 const QQmlJSScope::ConstPtr type = match.isJavaScriptFunction()
1178 ? m_typeResolver->jsValueType()
1179 : QQmlJSScope::ConstPtr(types.at(i).type());
1180 if (!type.isNull()) {
1181 addReadRegister(argv + i, type);
1185 addReadRegister(argv + i, m_typeResolver->jsValueType());
1187 m_state.setHasExternalSideEffects();
1195bool QQmlJSTypePropagator::propagateTranslationMethod(
1196 const QList<QQmlJSMetaMethod> &methods,
int argc,
int argv)
1198 if (methods.size() != 1)
1201 const QQmlJSMetaMethod method = methods.front();
1202 const QQmlJSScope::ConstPtr intType = m_typeResolver->int32Type();
1203 const QQmlJSScope::ConstPtr stringType = m_typeResolver->stringType();
1205 const QQmlJSRegisterContent returnType = m_typeResolver->returnType(
1206 method, m_typeResolver->stringType(), m_typeResolver->jsGlobalObjectContent());
1208 if (method.methodName() == u"qsTranslate"_s) {
1211 addReadRegister(argv + 3, intType);
1214 addReadRegister(argv + 2, stringType);
1217 addReadRegister(argv + 1, stringType);
1218 addReadRegister(argv, stringType);
1219 setAccumulator(returnType);
1220 propagateTranslationMethod_SAcheck(method.methodName());
1227 if (method.methodName() == u"QT_TRANSLATE_NOOP"_s) {
1230 addReadRegister(argv + 2, stringType);
1233 addReadRegister(argv + 1, stringType);
1234 addReadRegister(argv, stringType);
1235 setAccumulator(returnType);
1236 propagateTranslationMethod_SAcheck(method.methodName());
1243 if (method.methodName() == u"qsTr"_s) {
1246 addReadRegister(argv + 2, intType);
1249 addReadRegister(argv + 1, stringType);
1252 addReadRegister(argv, stringType);
1253 setAccumulator(returnType);
1254 propagateTranslationMethod_SAcheck(method.methodName());
1261 if (method.methodName() == u"QT_TR_NOOP"_s) {
1264 addReadRegister(argv + 1, stringType);
1267 addReadRegister(argv, stringType);
1268 setAccumulator(returnType);
1269 propagateTranslationMethod_SAcheck(method.methodName());
1276 if (method.methodName() == u"qsTrId"_s) {
1279 addReadRegister(argv + 1, intType);
1282 addReadRegister(argv, stringType);
1283 setAccumulator(returnType);
1284 propagateTranslationMethod_SAcheck(method.methodName());
1291 if (method.methodName() == u"QT_TRID_NOOP"_s) {
1294 addReadRegister(argv, stringType);
1295 setAccumulator(returnType);
1296 propagateTranslationMethod_SAcheck(method.methodName());
1341bool QQmlJSTypePropagator::propagateArrayMethod(
1342 const QString &name,
int argc,
int argv, QQmlJSRegisterContent baseType)
1357 const auto intType = m_typeResolver->int32Type();
1358 const auto stringType = m_typeResolver->stringType();
1359 const auto baseContained = baseType.containedType();
1360 const auto elementContained = baseContained->elementType();
1362 const auto setReturnType = [&](
const QQmlJSScope::ConstPtr type) {
1363 QQmlJSMetaMethod method;
1364 method.setIsJavaScriptFunction(
true);
1365 method.setMethodName(name);
1366 setAccumulator(m_typeResolver->returnType(method, type, baseType));
1369 if (name == u"copyWithin" && argc > 0 && argc < 4) {
1370 for (
int i = 0; i < argc; ++i) {
1371 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1375 for (
int i = 0; i < argc; ++i)
1376 addReadRegister(argv + i, intType);
1378 m_state.setHasExternalSideEffects();
1379 setReturnType(baseContained);
1383 if (name == u"fill" && argc > 0 && argc < 4) {
1384 if (!canConvertFromTo(m_state.registers[argv].content, elementContained))
1387 for (
int i = 1; i < argc; ++i) {
1388 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1392 addReadRegister(argv, elementContained);
1394 for (
int i = 1; i < argc; ++i)
1395 addReadRegister(argv + i, intType);
1397 m_state.setHasExternalSideEffects();
1398 setReturnType(baseContained);
1402 if (name == u"includes" && argc > 0 && argc < 3) {
1403 if (!canConvertFromTo(m_state.registers[argv].content, elementContained))
1407 if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
1409 addReadRegister(argv + 1, intType);
1412 addReadRegister(argv, elementContained);
1413 setReturnType(m_typeResolver->boolType());
1417 if (name == u"toString" || (name == u"join" && argc < 2)) {
1419 if (!canConvertFromTo(m_state.registers[argv].content, stringType))
1421 addReadRegister(argv, stringType);
1424 setReturnType(m_typeResolver->stringType());
1428 if ((name == u"pop" || name == u"shift") && argc == 0) {
1429 m_state.setHasExternalSideEffects();
1430 setReturnType(elementContained);
1434 if (name == u"push" || name == u"unshift") {
1435 for (
int i = 0; i < argc; ++i) {
1436 if (!canConvertFromTo(m_state.registers[argv + i].content, elementContained))
1440 for (
int i = 0; i < argc; ++i)
1441 addReadRegister(argv + i, elementContained);
1443 m_state.setHasExternalSideEffects();
1444 setReturnType(m_typeResolver->int32Type());
1448 if (name == u"reverse" && argc == 0) {
1449 m_state.setHasExternalSideEffects();
1450 setReturnType(baseContained);
1454 if (name == u"slice" && argc < 3) {
1455 for (
int i = 0; i < argc; ++i) {
1456 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1460 for (
int i = 0; i < argc; ++i)
1461 addReadRegister(argv + i, intType);
1463 setReturnType(baseType.containedType()->isListProperty()
1464 ? m_typeResolver->qObjectListType()
1469 if (name == u"splice" && argc > 0) {
1470 const int startAndDeleteCount = std::min(argc, 2);
1471 for (
int i = 0; i < startAndDeleteCount; ++i) {
1472 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1476 for (
int i = 2; i < argc; ++i) {
1477 if (!canConvertFromTo(m_state.registers[argv + i].content, elementContained))
1481 for (
int i = 0; i < startAndDeleteCount; ++i)
1482 addReadRegister(argv + i, intType);
1484 for (
int i = 2; i < argc; ++i)
1485 addReadRegister(argv + i, elementContained);
1487 m_state.setHasExternalSideEffects();
1488 setReturnType(baseContained);
1492 if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
1493 if (!canConvertFromTo(m_state.registers[argv].content, elementContained))
1497 if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
1499 addReadRegister(argv + 1, intType);
1502 addReadRegister(argv, elementContained);
1503 setReturnType(m_typeResolver->int32Type());
1630void QQmlJSTypePropagator::generate_Construct(
int func,
int argc,
int argv)
1632 const QQmlJSRegisterContent type = m_state.registers[func].content;
1633 if (type.contains(m_typeResolver->metaObjectType())) {
1634 const QQmlJSRegisterContent valueType = type.scope();
1635 const QQmlJSScope::ConstPtr contained = type.scopeType();
1636 if (contained->isValueType() && contained->isCreatable()) {
1637 const auto extension = contained->extensionType();
1638 if (extension.extensionSpecifier == QQmlJSScope::ExtensionType) {
1640 extension.scope->ownMethods(extension.scope->internalName()),
1641 argc, argv, valueType);
1644 contained->ownMethods(contained->internalName()), argc, argv, valueType);
1650 if (!type.isMethod()) {
1651 m_state.setHasExternalSideEffects();
1652 QQmlJSMetaMethod method;
1653 method.setMethodName(type.containedTypeName());
1654 method.setIsJavaScriptFunction(
true);
1655 method.setIsConstructor(
true);
1656 setAccumulator(m_typeResolver->returnType(method, m_typeResolver->jsValueType(), {}));
1660 if (
const auto methods = type.method();
1661 methods == m_typeResolver->jsGlobalObject()->methods(u"Date"_s)) {
1662 Q_ASSERT(methods.length() == 1);
1663 generate_Construct_SCDate(methods[0], argc, argv);
1667 if (
const auto methods = type.method();
1668 methods == m_typeResolver->jsGlobalObject()->methods(u"Array"_s)) {
1669 Q_ASSERT(methods.length() == 1);
1670 generate_Construct_SCArray(methods[0], argc, argv);
1674 m_state.setHasExternalSideEffects();
1677 QQmlJSMetaMethod match = bestMatchForCall(type.method(), argc, argv, &errors);
1678 if (!match.isValid())
1679 addError(u"Cannot determine matching constructor. Candidates:\n"_s + errors.join(u'\n'));
1680 setAccumulator(m_typeResolver->returnType(match, m_typeResolver->jsValueType(), {}));
2028void QQmlJSTypePropagator::recordEqualsType(
int lhs)
2030 const auto isNumericOrEnum = [
this](QQmlJSRegisterContent content) {
2031 return content.isEnumeration() || m_typeResolver->isNumeric(content);
2034 const auto accumulatorIn = m_state.accumulatorIn();
2035 const auto lhsRegister = m_state.registers[lhs].content;
2038 if (m_typeResolver->isPrimitive(accumulatorIn) || accumulatorIn.isEnumeration()) {
2039 if (accumulatorIn.contains(lhsRegister.containedType())
2040 || (isNumericOrEnum(accumulatorIn) && isNumericOrEnum(lhsRegister))
2041 || m_typeResolver->isPrimitive(lhsRegister)) {
2042 addReadRegister(lhs);
2043 addReadAccumulator();
2048 const auto containedAccumulatorIn = m_typeResolver->isOptionalType(accumulatorIn)
2049 ? m_typeResolver->extractNonVoidFromOptionalType(accumulatorIn).containedType()
2050 : accumulatorIn.containedType();
2052 const auto containedLhs = m_typeResolver->isOptionalType(lhsRegister)
2053 ? m_typeResolver->extractNonVoidFromOptionalType(lhsRegister).containedType()
2054 : lhsRegister.containedType();
2057 if (QQmlJSUtils::canStrictlyCompareWithVar(m_typeResolver, containedLhs, containedAccumulatorIn)
2058 || QQmlJSUtils::canCompareWithQObject(m_typeResolver, containedLhs, containedAccumulatorIn)
2059 || QQmlJSUtils::canCompareWithQUrl(m_typeResolver, containedLhs, containedAccumulatorIn)) {
2060 addReadRegister(lhs);
2061 addReadAccumulator();
2068 const QQmlJSScope::ConstPtr jsval = m_typeResolver->jsValueType();
2069 addReadRegister(lhs, jsval);
2070 addReadAccumulator(jsval);
2201void QQmlJSTypePropagator::generate_As(
int lhs)
2203 const QQmlJSRegisterContent input = checkedInputRegister(lhs);
2204 const QQmlJSScope::ConstPtr inContained = input.containedType();
2206 QQmlJSRegisterContent output;
2208 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
2209 switch (accumulatorIn.variant()) {
2210 case QQmlJSRegisterContent::Attachment:
2211 output = accumulatorIn.scope();
2213 case QQmlJSRegisterContent::MetaType:
2214 output = accumulatorIn.scope();
2215 if (output.containedType()->isComposite())
2216 addReadAccumulator(m_typeResolver->metaObjectType());
2219 output = accumulatorIn;
2223 QQmlJSScope::ConstPtr outContained = output.containedType();
2225 if (outContained->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
2229 if (m_typeResolver->inherits(inContained, outContained))
2230 output = m_pool->clone(input);
2232 output = m_pool->castTo(input, outContained);
2233 }
else if (m_typeResolver->inherits(inContained, outContained)) {
2235 output = m_pool->castTo(input, outContained);
2239 output = m_typeResolver->merge(
2240 m_pool->castTo(input, outContained),
2241 m_pool->castTo(input, m_typeResolver->voidType()));
2244 addReadRegister(lhs);
2245 setAccumulator(output);
2433QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type type)
2435 if (m_state.jumpTargets.contains(currentInstructionOffset())) {
2436 if (m_state.skipInstructionsUntilNextJumpTarget) {
2438 m_state.registers.clear();
2439 m_state.skipInstructionsUntilNextJumpTarget =
false;
2441 }
else if (m_state.skipInstructionsUntilNextJumpTarget
2442 && !instructionManipulatesContext(type)) {
2443 return SkipInstruction;
2446 const int currentOffset = currentInstructionOffset();
2458 for (
auto originRegisterStateIt =
2459 m_jumpOriginRegisterStateByTargetInstructionOffset.constFind(currentOffset);
2460 originRegisterStateIt != m_jumpOriginRegisterStateByTargetInstructionOffset.constEnd()
2461 && originRegisterStateIt.key() == currentOffset;
2462 ++originRegisterStateIt) {
2463 auto stateToMerge = *originRegisterStateIt;
2464 for (
auto registerIt = stateToMerge.registers.constBegin(),
2465 end = stateToMerge.registers.constEnd();
2466 registerIt != end; ++registerIt) {
2467 const int registerIndex = registerIt.key();
2469 const VirtualRegister &newType = registerIt.value();
2470 if (!newType.content.isValid()) {
2471 addError(u"When reached from offset %1, %2 is undefined"_s
2472 .arg(stateToMerge.originatingOffset)
2473 .arg(registerName(registerIndex)));
2474 return SkipInstruction;
2477 auto currentRegister = m_state.registers.find(registerIndex);
2478 if (currentRegister != m_state.registers.end())
2479 mergeRegister(registerIndex, newType, currentRegister.value());
2481 mergeRegister(registerIndex, newType, newType);
2485 return ProcessInstruction;
2488bool QQmlJSTypePropagator::populatesAccumulator(QV4::Moth::Instr::Type instr)
const
2491 case QV4::Moth::Instr::Type::CheckException:
2492 case QV4::Moth::Instr::Type::CloneBlockContext:
2493 case QV4::Moth::Instr::Type::ConvertThisToObject:
2494 case QV4::Moth::Instr::Type::CreateCallContext:
2495 case QV4::Moth::Instr::Type::DeadTemporalZoneCheck:
2496 case QV4::Moth::Instr::Type::Debug:
2497 case QV4::Moth::Instr::Type::DeclareVar:
2498 case QV4::Moth::Instr::Type::IteratorClose:
2499 case QV4::Moth::Instr::Type::IteratorNext:
2500 case QV4::Moth::Instr::Type::IteratorNextForYieldStar:
2501 case QV4::Moth::Instr::Type::Jump:
2502 case QV4::Moth::Instr::Type::JumpFalse:
2503 case QV4::Moth::Instr::Type::JumpNoException:
2504 case QV4::Moth::Instr::Type::JumpNotUndefined:
2505 case QV4::Moth::Instr::Type::JumpTrue:
2506 case QV4::Moth::Instr::Type::MoveConst:
2507 case QV4::Moth::Instr::Type::MoveReg:
2508 case QV4::Moth::Instr::Type::MoveRegExp:
2509 case QV4::Moth::Instr::Type::PopContext:
2510 case QV4::Moth::Instr::Type::PushBlockContext:
2511 case QV4::Moth::Instr::Type::PushCatchContext:
2512 case QV4::Moth::Instr::Type::PushScriptContext:
2513 case QV4::Moth::Instr::Type::Resume:
2514 case QV4::Moth::Instr::Type::Ret:
2515 case QV4::Moth::Instr::Type::SetException:
2516 case QV4::Moth::Instr::Type::SetLookup:
2517 case QV4::Moth::Instr::Type::SetUnwindHandler:
2518 case QV4::Moth::Instr::Type::StoreElement:
2519 case QV4::Moth::Instr::Type::StoreLocal:
2520 case QV4::Moth::Instr::Type::StoreNameSloppy:
2521 case QV4::Moth::Instr::Type::StoreNameStrict:
2522 case QV4::Moth::Instr::Type::StoreProperty:
2523 case QV4::Moth::Instr::Type::StoreReg:
2524 case QV4::Moth::Instr::Type::StoreScopedLocal:
2525 case QV4::Moth::Instr::Type::StoreSuperProperty:
2526 case QV4::Moth::Instr::Type::ThrowException:
2527 case QV4::Moth::Instr::Type::ThrowOnNullOrUndefined:
2528 case QV4::Moth::Instr::Type::UnwindDispatch:
2529 case QV4::Moth::Instr::Type::UnwindToLabel:
2530 case QV4::Moth::Instr::Type::Yield:
2531 case QV4::Moth::Instr::Type::YieldStar:
2533 case QV4::Moth::Instr::Type::Add:
2534 case QV4::Moth::Instr::Type::As:
2535 case QV4::Moth::Instr::Type::BitAnd:
2536 case QV4::Moth::Instr::Type::BitAndConst:
2537 case QV4::Moth::Instr::Type::BitOr:
2538 case QV4::Moth::Instr::Type::BitOrConst:
2539 case QV4::Moth::Instr::Type::BitXor:
2540 case QV4::Moth::Instr::Type::BitXorConst:
2541 case QV4::Moth::Instr::Type::CallGlobalLookup:
2542 case QV4::Moth::Instr::Type::CallName:
2543 case QV4::Moth::Instr::Type::CallPossiblyDirectEval:
2544 case QV4::Moth::Instr::Type::CallProperty:
2545 case QV4::Moth::Instr::Type::CallPropertyLookup:
2546 case QV4::Moth::Instr::Type::CallQmlContextPropertyLookup:
2547 case QV4::Moth::Instr::Type::CallValue:
2548 case QV4::Moth::Instr::Type::CallWithReceiver:
2549 case QV4::Moth::Instr::Type::CallWithSpread:
2550 case QV4::Moth::Instr::Type::CmpEq:
2551 case QV4::Moth::Instr::Type::CmpEqInt:
2552 case QV4::Moth::Instr::Type::CmpEqNull:
2553 case QV4::Moth::Instr::Type::CmpGe:
2554 case QV4::Moth::Instr::Type::CmpGt:
2555 case QV4::Moth::Instr::Type::CmpIn:
2556 case QV4::Moth::Instr::Type::CmpInstanceOf:
2557 case QV4::Moth::Instr::Type::CmpLe:
2558 case QV4::Moth::Instr::Type::CmpLt:
2559 case QV4::Moth::Instr::Type::CmpNe:
2560 case QV4::Moth::Instr::Type::CmpNeInt:
2561 case QV4::Moth::Instr::Type::CmpNeNull:
2562 case QV4::Moth::Instr::Type::CmpStrictEqual:
2563 case QV4::Moth::Instr::Type::CmpStrictNotEqual:
2564 case QV4::Moth::Instr::Type::Construct:
2565 case QV4::Moth::Instr::Type::ConstructWithSpread:
2566 case QV4::Moth::Instr::Type::CreateClass:
2567 case QV4::Moth::Instr::Type::CreateMappedArgumentsObject:
2568 case QV4::Moth::Instr::Type::CreateRestParameter:
2569 case QV4::Moth::Instr::Type::CreateUnmappedArgumentsObject:
2570 case QV4::Moth::Instr::Type::Decrement:
2571 case QV4::Moth::Instr::Type::DefineArray:
2572 case QV4::Moth::Instr::Type::DefineObjectLiteral:
2573 case QV4::Moth::Instr::Type::DeleteName:
2574 case QV4::Moth::Instr::Type::DeleteProperty:
2575 case QV4::Moth::Instr::Type::DestructureRestElement:
2576 case QV4::Moth::Instr::Type::Div:
2577 case QV4::Moth::Instr::Type::Exp:
2578 case QV4::Moth::Instr::Type::GetException:
2579 case QV4::Moth::Instr::Type::GetIterator:
2580 case QV4::Moth::Instr::Type::GetLookup:
2581 case QV4::Moth::Instr::Type::GetOptionalLookup:
2582 case QV4::Moth::Instr::Type::GetTemplateObject:
2583 case QV4::Moth::Instr::Type::Increment:
2584 case QV4::Moth::Instr::Type::InitializeBlockDeadTemporalZone:
2585 case QV4::Moth::Instr::Type::LoadClosure:
2586 case QV4::Moth::Instr::Type::LoadConst:
2587 case QV4::Moth::Instr::Type::LoadElement:
2588 case QV4::Moth::Instr::Type::LoadFalse:
2589 case QV4::Moth::Instr::Type::LoadGlobalLookup:
2590 case QV4::Moth::Instr::Type::LoadImport:
2591 case QV4::Moth::Instr::Type::LoadInt:
2592 case QV4::Moth::Instr::Type::LoadLocal:
2593 case QV4::Moth::Instr::Type::LoadName:
2594 case QV4::Moth::Instr::Type::LoadNull:
2595 case QV4::Moth::Instr::Type::LoadOptionalProperty:
2596 case QV4::Moth::Instr::Type::LoadProperty:
2597 case QV4::Moth::Instr::Type::LoadQmlContextPropertyLookup:
2598 case QV4::Moth::Instr::Type::LoadReg:
2599 case QV4::Moth::Instr::Type::LoadRuntimeString:
2600 case QV4::Moth::Instr::Type::LoadScopedLocal:
2601 case QV4::Moth::Instr::Type::LoadSuperConstructor:
2602 case QV4::Moth::Instr::Type::LoadSuperProperty:
2603 case QV4::Moth::Instr::Type::LoadTrue:
2604 case QV4::Moth::Instr::Type::LoadUndefined:
2605 case QV4::Moth::Instr::Type::LoadZero:
2606 case QV4::Moth::Instr::Type::Mod:
2607 case QV4::Moth::Instr::Type::Mul:
2608 case QV4::Moth::Instr::Type::PushWithContext:
2609 case QV4::Moth::Instr::Type::Shl:
2610 case QV4::Moth::Instr::Type::ShlConst:
2611 case QV4::Moth::Instr::Type::Shr:
2612 case QV4::Moth::Instr::Type::ShrConst:
2613 case QV4::Moth::Instr::Type::Sub:
2614 case QV4::Moth::Instr::Type::TailCall:
2615 case QV4::Moth::Instr::Type::ToObject:
2616 case QV4::Moth::Instr::Type::TypeofName:
2617 case QV4::Moth::Instr::Type::TypeofValue:
2618 case QV4::Moth::Instr::Type::UCompl:
2619 case QV4::Moth::Instr::Type::UMinus:
2620 case QV4::Moth::Instr::Type::UNot:
2621 case QV4::Moth::Instr::Type::UPlus:
2622 case QV4::Moth::Instr::Type::UShr:
2623 case QV4::Moth::Instr::Type::UShrConst:
2626 Q_UNREACHABLE_RETURN(
false);
2641void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
2643 InstructionAnnotation ¤tInstruction = m_state.annotations[currentInstructionOffset()];
2644 currentInstruction.changedRegister = m_state.changedRegister();
2645 currentInstruction.changedRegisterIndex = m_state.changedRegisterIndex();
2646 currentInstruction.readRegisters = m_state.takeReadRegisters();
2647 currentInstruction.hasExternalSideEffects = m_state.hasExternalSideEffects();
2648 currentInstruction.hasInternalSideEffects = m_state.hasInternalSideEffects();
2649 currentInstruction.isRename = m_state.isRename();
2651 bool populates = populatesAccumulator(instr);
2652 int changedIndex = m_state.changedRegisterIndex();
2655 if (instr != QV4::Moth::Instr::Type::InitializeBlockDeadTemporalZone) {
2656 Q_ASSERT((populates && changedIndex == Accumulator && m_state.accumulatorOut().isValid())
2657 || (!populates && changedIndex != Accumulator));
2660 if (!m_logger->currentFunctionHasCompileError() && !isNoop(instr)) {
2663 Q_ASSERT(m_state.hasInternalSideEffects() || changedIndex != InvalidRegister);
2666 if (changedIndex != InvalidRegister) {
2667 Q_ASSERT(m_logger->currentFunctionHasCompileError() || m_state.changedRegister().isValid());
2668 VirtualRegister &r = m_state.registers[changedIndex];
2669 r.content = m_state.changedRegister();
2671 r.affectedBySideEffects = m_state.isRename()
2672 && m_state.isRegisterAffectedBySideEffects(m_state.renameSourceRegisterIndex());
2673 m_state.clearChangedRegister();
2676 m_state.resetSideEffects();
2677 m_state.setIsRename(
false);
2678 m_state.setReadRegisters(VirtualRegisters());
2679 m_state.instructionHasError =
false;