283void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(
int index)
288 const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
289 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
291 setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name, index));
293 if (!m_state.accumulatorOut().isValid() && m_typeResolver->isPrefix(name)) {
294 setAccumulator(m_pool->createImportNamespace(
295 nameIndex, m_typeResolver->voidType(), QQmlJSRegisterContent::ModulePrefix,
296 m_function->qmlScope));
300 checkDeprecated(m_function->qmlScope.containedType(), name,
false);
302 const QQmlJSRegisterContent accumulatorOut = m_state.accumulatorOut();
304 if (!accumulatorOut.isValid()) {
305 addError(u"Cannot access value for name "_s + name);
306 handleUnqualifiedAccessAndContextProperties(name,
false);
307 setVarAccumulatorAndError();
311 const QQmlJSScope::ConstPtr retrieved
312 = m_typeResolver->genericType(accumulatorOut.containedType());
314 if (retrieved.isNull()) {
317 addError(u"Cannot determine generic type for "_s + name);
321 if (accumulatorOut.variant() == QQmlJSRegisterContent::ObjectById
322 && !retrieved->isReferenceType()) {
323 addError(u"Cannot retrieve a non-object type by ID: "_s + name);
339void QQmlJSTypePropagator::generate_StoreNameCommon(
int nameIndex)
341 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
342 const QQmlJSRegisterContent type = m_typeResolver->scopedType(m_function->qmlScope, name);
343 const QQmlJSRegisterContent in = m_state.accumulatorIn();
345 if (!type.isValid()) {
346 handleUnqualifiedAccess(name,
false);
347 addError(u"Cannot find name "_s + name);
351 if (!type.isProperty()) {
352 QString message = type.isMethod() ? u"Cannot assign to method %1"_s
353 : u"Cannot assign to non-property %1"_s;
356 m_logger->log(message.arg(name), qmlReadOnlyProperty,
357 currentSourceLocation());
358 addError(u"Cannot assign to non-property "_s + name);
362 if (!type.isWritable() && !type.isList()) {
363 addError(u"Can't assign to read-only property %1"_s.arg(name));
365 m_logger->log(u"Cannot assign to read-only property %1"_s.arg(name), qmlReadOnlyProperty,
366 currentSourceLocation());
371 if (!canConvertFromTo(in, type)) {
372 addError(u"cannot convert from %1 to %2"_s
373 .arg(in.descriptiveName(), type.descriptiveName()));
376 if (m_typeResolver->canHoldUndefined(in) && !m_typeResolver->canHoldUndefined(type)) {
377 if (in.contains(m_typeResolver->voidType()))
378 addReadAccumulator(m_typeResolver->varType());
380 addReadAccumulator();
382 addReadAccumulator(type);
385 m_state.setHasExternalSideEffects();
413void QQmlJSTypePropagator::generate_LoadElement(
int base)
415 const QQmlJSRegisterContent in = m_state.accumulatorIn();
416 const QQmlJSRegisterContent baseRegister = m_state.registers[base].content;
418 const auto fallback = [&]() {
419 const QQmlJSScope::ConstPtr jsValue = m_typeResolver->jsValueType();
421 addReadAccumulator(jsValue);
422 addReadRegister(base, jsValue);
424 QQmlJSMetaProperty property;
425 property.setPropertyName(u"[]"_s);
426 property.setTypeName(jsValue->internalName());
427 property.setType(jsValue);
429 setAccumulator(m_pool->createProperty(
430 property, QQmlJSRegisterContent::InvalidLookupIndex,
431 QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListValue,
432 m_typeResolver->convert(m_typeResolver->elementType(baseRegister), jsValue)));
435 if (baseRegister.isList()) {
436 addReadRegister(base, m_typeResolver->arrayPrototype());
437 }
else if (baseRegister.contains(m_typeResolver->stringType())) {
438 addReadRegister(base, m_typeResolver->stringType());
444 if (m_typeResolver->isNumeric(in)) {
445 const auto contained = in.containedType();
446 if (m_typeResolver->isSignedInteger(contained))
447 addReadAccumulator(m_typeResolver->sizeType());
448 else if (m_typeResolver->isUnsignedInteger(contained))
449 addReadAccumulator(m_typeResolver->uint32Type());
451 addReadAccumulator(m_typeResolver->realType());
452 }
else if (m_typeResolver->isNumeric(m_typeResolver->extractNonVoidFromOptionalType(in))) {
453 addReadAccumulator();
460 setAccumulator(m_typeResolver->merge(
461 m_typeResolver->elementType(baseRegister),
462 m_typeResolver->literalType(m_typeResolver->voidType())));
538void QQmlJSTypePropagator::propagatePropertyLookup(
const QString &propertyName,
int lookupIndex)
540 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
542 m_typeResolver->memberType(
544 accumulatorIn.isImportNamespace()
545 ? m_jsUnitGenerator->stringForIndex(accumulatorIn.importNamespace())
546 + u'.' + propertyName
547 : propertyName, lookupIndex));
549 if (!m_state.accumulatorOut().isValid() && handleImportNamespaceLookup(propertyName))
552 if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::Singleton
553 && accumulatorIn.variant() == QQmlJSRegisterContent::ModulePrefix
554 && !isQmlScopeObject(accumulatorIn.scope())) {
556 u"Cannot access singleton as a property of an object. Did you want to access an attached object?"_s,
557 qmlAccessSingleton, currentSourceLocation());
558 setAccumulator(QQmlJSRegisterContent());
559 }
else if (m_state.accumulatorOut().isEnumeration()) {
560 switch (accumulatorIn.variant()) {
561 case QQmlJSRegisterContent::MetaType:
562 case QQmlJSRegisterContent::Attachment:
563 case QQmlJSRegisterContent::Enum:
564 case QQmlJSRegisterContent::ModulePrefix:
565 case QQmlJSRegisterContent::Singleton:
568 setAccumulator(QQmlJSRegisterContent());
572 if (m_state.instructionHasError || !m_state.accumulatorOut().isValid()) {
573 handleLookupError(propertyName);
577 if (m_state.accumulatorOut().isMethod() && m_state.accumulatorOut().method().size() != 1) {
578 addError(u"Cannot determine overloaded method on loadProperty"_s);
582 if (m_state.accumulatorOut().isProperty()) {
583 const QQmlJSScope::ConstPtr mathObject
584 = m_typeResolver->jsGlobalObject()->property(u"Math"_s).type();
585 if (accumulatorIn.contains(mathObject)) {
586 QQmlJSMetaProperty prop;
587 prop.setPropertyName(propertyName);
588 prop.setTypeName(u"double"_s);
589 prop.setType(m_typeResolver->realType());
591 m_pool->createProperty(
592 prop, accumulatorIn.resultLookupIndex(), lookupIndex,
594 QQmlJSRegisterContent::Property, m_state.accumulatorOut().scope())
600 if (m_state.accumulatorOut().contains(m_typeResolver->voidType())) {
601 addError(u"Type %1 does not have a property %2 for reading"_s
602 .arg(accumulatorIn.descriptiveName(), propertyName));
606 if (!m_state.accumulatorOut().property().type()) {
608 QString::fromLatin1(
"Type of property \"%2\" not found").arg(propertyName),
609 qmlMissingType, currentSourceLocation());
613 switch (m_state.accumulatorOut().variant()) {
614 case QQmlJSRegisterContent::Enum:
615 case QQmlJSRegisterContent::Singleton:
618 if (accumulatorIn.isImportNamespace())
619 addReadAccumulator();
622 addReadAccumulator();
651void QQmlJSTypePropagator::generate_StoreProperty(
int nameIndex,
int base)
653 auto callBase = m_state.registers[base].content;
654 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
656 QQmlJSRegisterContent property = m_typeResolver->memberType(callBase, propertyName);
657 if (!property.isProperty()) {
658 addError(u"Type %1 does not have a property %2 for writing"_s
659 .arg(callBase.descriptiveName(), propertyName));
663 if (property.containedType().isNull()) {
664 addError(u"Cannot determine type for property %1 of type %2"_s.arg(
665 propertyName, callBase.descriptiveName()));
669 if (!property.isWritable() && !property.containedType()->isListProperty()) {
670 addError(u"Can't assign to read-only property %1"_s.arg(propertyName));
672 m_logger->log(u"Cannot assign to read-only property %1"_s.arg(propertyName),
673 qmlReadOnlyProperty, currentSourceLocation());
678 if (!canConvertFromTo(m_state.accumulatorIn(), property)) {
679 addError(u"cannot convert from %1 to %2"_s
680 .arg(m_state.accumulatorIn().descriptiveName(), property.descriptiveName()));
693 const QQmlJSScope::ConstPtr varType = m_typeResolver->varType();
694 const QQmlJSRegisterContent readType = m_typeResolver->canHoldUndefined(m_state.accumulatorIn())
695 ? m_typeResolver->convert(property, varType)
696 : std::move(property);
697 addReadAccumulator(readType);
698 addReadRegister(base);
699 m_state.setHasExternalSideEffects();
794void QQmlJSTypePropagator::generate_CallProperty_SCconsole(
795 const QString &name,
int base,
int argc,
int argv)
798 addReadRegister(base, m_typeResolver->voidType());
801 const QQmlJSRegisterContent firstContent = m_state.registers[argv].content;
802 const QQmlJSScope::ConstPtr firstArg = firstContent.containedType();
803 switch (firstArg->accessSemantics()) {
804 case QQmlJSScope::AccessSemantics::Reference:
807 addReadRegister(argv, m_typeResolver->genericType(firstArg));
809 case QQmlJSScope::AccessSemantics::Sequence:
810 addReadRegister(argv);
813 addReadRegister(argv, m_typeResolver->stringType());
818 for (
int i = 1; i < argc; ++i) {
819 const QQmlJSRegisterContent argContent = m_state.registers[argv + i].content;
820 const QQmlJSScope::ConstPtr arg = argContent.containedType();
821 if (arg->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
822 addReadRegister(argv + i);
824 addReadRegister(argv + i, m_typeResolver->stringType());
832 m_state.setHasExternalSideEffects();
834 QQmlJSRegisterContent console = m_state.registers[base].content;
835 QList<QQmlJSMetaMethod> methods = console.containedType()->ownMethods(name);
836 Q_ASSERT(methods.length() == 1);
840 setAccumulator(m_typeResolver->returnType(
841 methods[0], m_typeResolver->voidType(),
842 m_typeResolver->baseType(console.containedType(), console)));
845void QQmlJSTypePropagator::generate_CallProperty(
int nameIndex,
int base,
int argc,
int argv)
847 Q_ASSERT(m_state.registers.contains(base));
848 const auto callBase = m_state.registers[base].content;
849 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
851 if (callBase.contains(m_typeResolver->mathObject())) {
852 generate_CallProperty_SCMath(propertyName, base, argc, argv);
856 if (callBase.contains(m_typeResolver->consoleObject()) && isLoggingMethod(propertyName)) {
857 generate_CallProperty_SCconsole(propertyName, base, argc, argv);
861 const auto baseType = callBase.containedType();
862 const auto member = m_typeResolver->memberType(callBase, propertyName);
864 if (!member.isMethod()) {
865 if (callBase.contains(m_typeResolver->jsValueType())
866 || callBase.contains(m_typeResolver->varType())) {
867 const auto jsValueType = m_typeResolver->jsValueType();
868 addReadRegister(base, jsValueType);
869 for (
int i = 0; i < argc; ++i)
870 addReadRegister(argv + i, jsValueType);
871 m_state.setHasExternalSideEffects();
873 QQmlJSMetaMethod method;
874 method.setIsJavaScriptFunction(
true);
875 method.setMethodName(propertyName);
876 method.setMethodType(QQmlJSMetaMethod::MethodType::Method);
878 setAccumulator(m_typeResolver->returnType(
879 method, m_typeResolver->jsValueType(), callBase));
883 setVarAccumulatorAndError();
884 addError(u"Type %1 does not have a property %2 for calling"_s
885 .arg(callBase.descriptiveName(), propertyName));
887 if (callBase.isType() && isCallingProperty(callBase.type(), propertyName))
890 if (checkForEnumProblems(callBase, propertyName))
893 std::optional<QQmlJSFixSuggestion> fixSuggestion;
895 if (
auto suggestion = QQmlJSUtils::didYouMean(propertyName, baseType->methods().keys(),
896 m_logger->filePath(), currentSourceLocation());
897 suggestion.has_value()) {
898 fixSuggestion = suggestion;
901 if (baseType->isFullyResolved() || baseType->isScript()) {
902 m_logger->log(u"Member \"%1\" not found on type \"%2\""_s.arg(
903 propertyName, callBase.containedTypeName()),
904 qmlMissingProperty, currentSourceLocation(),
true,
true, fixSuggestion);
909 checkDeprecated(baseType, propertyName,
true);
911 addReadRegister(base);
913 if (callBase.contains(m_typeResolver->stringType())) {
914 if (propertyName == u"arg"_s && argc == 1) {
915 propagateStringArgCall(callBase, argv);
920 if (baseType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
921 && member.scope().contains(m_typeResolver->arrayPrototype())
922 && propagateArrayMethod(propertyName, argc, argv, callBase)) {
926 propagateCall(member.method(), argc, argv, member.scope());
929QQmlJSMetaMethod QQmlJSTypePropagator::bestMatchForCall(
const QList<QQmlJSMetaMethod> &methods,
930 int argc,
int argv, QStringList *errors)
932 QQmlJSMetaMethod javascriptFunction;
933 QQmlJSMetaMethod candidate;
934 bool hasMultipleCandidates =
false;
936 for (
const auto &method : methods) {
939 if (method.isJavaScriptFunction() && !javascriptFunction.isValid())
940 javascriptFunction = method;
942 if (method.returnType().isNull() && !method.returnTypeName().isEmpty()) {
943 errors->append(u"return type %1 cannot be resolved"_s
944 .arg(method.returnTypeName()));
948 const auto arguments = method.parameters();
949 if (argc != arguments.size()) {
951 u"Function expects %1 arguments, but %2 were provided"_s.arg(arguments.size())
956 bool fuzzyMatch =
true;
957 bool exactMatch =
true;
958 for (
int i = 0; i < argc; ++i) {
959 const auto argumentType = arguments[i].type();
960 if (argumentType.isNull()) {
962 u"type %1 for argument %2 cannot be resolved"_s.arg(arguments[i].typeName())
969 const auto content = m_state.registers[argv + i].content;
970 if (content.contains(argumentType))
974 if (canConvertFromTo(content, argumentType))
978 if (argumentType->isReferenceType()
979 && m_typeResolver->inherits(
980 argumentType->baseType(), content.containedType())) {
985 u"argument %1 contains %2 but is expected to contain the type %3"_s.arg(i).arg(
986 content.descriptiveName(), arguments[i].typeName()));
993 }
else if (fuzzyMatch) {
994 if (!candidate.isValid())
997 hasMultipleCandidates =
true;
1001 if (hasMultipleCandidates)
1002 return QQmlJSMetaMethod();
1004 return candidate.isValid() ? candidate : javascriptFunction;
1047void QQmlJSTypePropagator::mergeRegister(
1048 int index,
const VirtualRegister &a,
const VirtualRegister &b)
1050 const VirtualRegister merged = {
1051 (a.content == b.content) ? a.content : m_typeResolver->merge(a.content, b.content),
1052 a.canMove && b.canMove,
1053 a.affectedBySideEffects || b.affectedBySideEffects,
1054 a.isShadowable || b.isShadowable,
1057 Q_ASSERT(merged.content.isValid());
1059 if (!merged.content.isConversion()) {
1061 m_state.annotations[currentInstructionOffset()].typeConversions[index] = merged;
1062 m_state.registers[index] = merged;
1066 auto tryPrevStateConversion = [
this](
int index,
const VirtualRegister &merged) ->
bool {
1067 auto it = m_prevStateAnnotations.find(currentInstructionOffset());
1068 if (it == m_prevStateAnnotations.end())
1071 auto conversion = it->second.typeConversions.find(index);
1072 if (conversion == it->second.typeConversions.end())
1075 const VirtualRegister &lastTry = conversion.value();
1077 Q_ASSERT(lastTry.content.isValid());
1078 if (!lastTry.content.isConversion())
1081 if (lastTry.content.conversionResultType() != merged.content.conversionResultType()
1082 || lastTry.content.conversionOrigins() != merged.content.conversionOrigins()
1083 || lastTry.canMove != merged.canMove
1084 || lastTry.affectedBySideEffects != merged.affectedBySideEffects
1085 || lastTry.isShadowable != merged.isShadowable) {
1090 m_state.annotations[currentInstructionOffset()].typeConversions[index] = lastTry;
1093 Q_ASSERT(!m_state.registers[index].affectedBySideEffects || lastTry.affectedBySideEffects);
1095 m_state.registers[index] = lastTry;
1099 if (!tryPrevStateConversion(index, merged)) {
1101 const VirtualRegister cloned = {
1102 (a == b) ? m_pool->clone(merged.content) : merged.content,
1104 merged.affectedBySideEffects,
1105 merged.isShadowable,
1107 Q_ASSERT(cloned.content.isValid());
1108 m_state.annotations[currentInstructionOffset()].typeConversions[index] = cloned;
1109 m_state.registers[index] = cloned;
1136void QQmlJSTypePropagator::propagateCall(
1137 const QList<QQmlJSMetaMethod> &methods,
int argc,
int argv,
1138 QQmlJSRegisterContent scope)
1141 const QQmlJSMetaMethod match = bestMatchForCall(methods, argc, argv, &errors);
1143 if (!match.isValid()) {
1144 setVarAccumulatorAndError();
1145 if (methods.size() == 1) {
1147 Q_ASSERT(errors.size() == 1);
1148 addError(errors.first());
1149 }
else if (errors.size() < methods.size()) {
1150 addError(u"Multiple matching overrides found. Cannot determine the right one."_s);
1152 addError(u"No matching override found. Candidates:\n"_s + errors.join(u'\n'));
1157 QQmlJSScope::ConstPtr returnType;
1158 if (match.isJavaScriptFunction())
1159 returnType = m_typeResolver->jsValueType();
1160 else if (match.isConstructor())
1161 returnType = scope.containedType();
1163 returnType = match.returnType();
1165 setAccumulator(m_typeResolver->returnType(match, returnType, scope));
1166 if (!m_state.accumulatorOut().isValid())
1167 addError(u"Cannot store return type of method %1()."_s.arg(match.methodName()));
1169 const auto types = match.parameters();
1170 for (
int i = 0; i < argc; ++i) {
1171 if (i < types.size()) {
1172 const QQmlJSScope::ConstPtr type = match.isJavaScriptFunction()
1173 ? m_typeResolver->jsValueType()
1174 : QQmlJSScope::ConstPtr(types.at(i).type());
1175 if (!type.isNull()) {
1176 addReadRegister(argv + i, type);
1180 addReadRegister(argv + i, m_typeResolver->jsValueType());
1182 m_state.setHasExternalSideEffects();
1190bool QQmlJSTypePropagator::propagateTranslationMethod(
1191 const QList<QQmlJSMetaMethod> &methods,
int argc,
int argv)
1193 if (methods.size() != 1)
1196 const QQmlJSMetaMethod method = methods.front();
1197 const QQmlJSScope::ConstPtr intType = m_typeResolver->int32Type();
1198 const QQmlJSScope::ConstPtr stringType = m_typeResolver->stringType();
1200 const QQmlJSRegisterContent returnType = m_typeResolver->returnType(
1201 method, m_typeResolver->stringType(), m_typeResolver->jsGlobalObjectContent());
1203 if (method.methodName() == u"qsTranslate"_s) {
1206 addReadRegister(argv + 3, intType);
1209 addReadRegister(argv + 2, stringType);
1212 addReadRegister(argv + 1, stringType);
1213 addReadRegister(argv, stringType);
1214 setAccumulator(returnType);
1215 propagateTranslationMethod_SAcheck(method.methodName());
1222 if (method.methodName() == u"QT_TRANSLATE_NOOP"_s) {
1225 addReadRegister(argv + 2, stringType);
1228 addReadRegister(argv + 1, stringType);
1229 addReadRegister(argv, stringType);
1230 setAccumulator(returnType);
1231 propagateTranslationMethod_SAcheck(method.methodName());
1238 if (method.methodName() == u"qsTr"_s) {
1241 addReadRegister(argv + 2, intType);
1244 addReadRegister(argv + 1, stringType);
1247 addReadRegister(argv, stringType);
1248 setAccumulator(returnType);
1249 propagateTranslationMethod_SAcheck(method.methodName());
1256 if (method.methodName() == u"QT_TR_NOOP"_s) {
1259 addReadRegister(argv + 1, stringType);
1262 addReadRegister(argv, stringType);
1263 setAccumulator(returnType);
1264 propagateTranslationMethod_SAcheck(method.methodName());
1271 if (method.methodName() == u"qsTrId"_s) {
1274 addReadRegister(argv + 1, intType);
1277 addReadRegister(argv, stringType);
1278 setAccumulator(returnType);
1279 propagateTranslationMethod_SAcheck(method.methodName());
1286 if (method.methodName() == u"QT_TRID_NOOP"_s) {
1289 addReadRegister(argv, stringType);
1290 setAccumulator(returnType);
1291 propagateTranslationMethod_SAcheck(method.methodName());
1336bool QQmlJSTypePropagator::propagateArrayMethod(
1337 const QString &name,
int argc,
int argv, QQmlJSRegisterContent baseType)
1352 const auto intType = m_typeResolver->int32Type();
1353 const auto stringType = m_typeResolver->stringType();
1354 const auto baseContained = baseType.containedType();
1355 const auto elementContained = baseContained->elementType();
1357 const auto setReturnType = [&](
const QQmlJSScope::ConstPtr type) {
1358 QQmlJSMetaMethod method;
1359 method.setIsJavaScriptFunction(
true);
1360 method.setMethodName(name);
1361 setAccumulator(m_typeResolver->returnType(method, type, baseType));
1364 if (name == u"copyWithin" && argc > 0 && argc < 4) {
1365 for (
int i = 0; i < argc; ++i) {
1366 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1370 for (
int i = 0; i < argc; ++i)
1371 addReadRegister(argv + i, intType);
1373 m_state.setHasExternalSideEffects();
1374 setReturnType(baseContained);
1378 if (name == u"fill" && argc > 0 && argc < 4) {
1379 if (!canConvertFromTo(m_state.registers[argv].content, elementContained))
1382 for (
int i = 1; i < argc; ++i) {
1383 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1387 addReadRegister(argv, elementContained);
1389 for (
int i = 1; i < argc; ++i)
1390 addReadRegister(argv + i, intType);
1392 m_state.setHasExternalSideEffects();
1393 setReturnType(baseContained);
1397 if (name == u"includes" && argc > 0 && argc < 3) {
1398 if (!canConvertFromTo(m_state.registers[argv].content, elementContained))
1402 if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
1404 addReadRegister(argv + 1, intType);
1407 addReadRegister(argv, elementContained);
1408 setReturnType(m_typeResolver->boolType());
1412 if (name == u"toString" || (name == u"join" && argc < 2)) {
1414 if (!canConvertFromTo(m_state.registers[argv].content, stringType))
1416 addReadRegister(argv, stringType);
1419 setReturnType(m_typeResolver->stringType());
1423 if ((name == u"pop" || name == u"shift") && argc == 0) {
1424 m_state.setHasExternalSideEffects();
1425 setReturnType(elementContained);
1429 if (name == u"push" || name == u"unshift") {
1430 for (
int i = 0; i < argc; ++i) {
1431 if (!canConvertFromTo(m_state.registers[argv + i].content, elementContained))
1435 for (
int i = 0; i < argc; ++i)
1436 addReadRegister(argv + i, elementContained);
1438 m_state.setHasExternalSideEffects();
1439 setReturnType(m_typeResolver->int32Type());
1443 if (name == u"reverse" && argc == 0) {
1444 m_state.setHasExternalSideEffects();
1445 setReturnType(baseContained);
1449 if (name == u"slice" && argc < 3) {
1450 for (
int i = 0; i < argc; ++i) {
1451 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1455 for (
int i = 0; i < argc; ++i)
1456 addReadRegister(argv + i, intType);
1458 setReturnType(baseType.containedType()->isListProperty()
1459 ? m_typeResolver->qObjectListType()
1464 if (name == u"splice" && argc > 0) {
1465 const int startAndDeleteCount = std::min(argc, 2);
1466 for (
int i = 0; i < startAndDeleteCount; ++i) {
1467 if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
1471 for (
int i = 2; i < argc; ++i) {
1472 if (!canConvertFromTo(m_state.registers[argv + i].content, elementContained))
1476 for (
int i = 0; i < startAndDeleteCount; ++i)
1477 addReadRegister(argv + i, intType);
1479 for (
int i = 2; i < argc; ++i)
1480 addReadRegister(argv + i, elementContained);
1482 m_state.setHasExternalSideEffects();
1483 setReturnType(baseContained);
1487 if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
1488 if (!canConvertFromTo(m_state.registers[argv].content, elementContained))
1492 if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
1494 addReadRegister(argv + 1, intType);
1497 addReadRegister(argv, elementContained);
1498 setReturnType(m_typeResolver->int32Type());
1625void QQmlJSTypePropagator::generate_Construct(
int func,
int argc,
int argv)
1627 const QQmlJSRegisterContent type = m_state.registers[func].content;
1628 if (type.contains(m_typeResolver->metaObjectType())) {
1629 const QQmlJSRegisterContent valueType = type.scope();
1630 const QQmlJSScope::ConstPtr contained = type.scopeType();
1631 if (contained->isValueType() && contained->isCreatable()) {
1632 const auto extension = contained->extensionType();
1633 if (extension.extensionSpecifier == QQmlJSScope::ExtensionType) {
1635 extension.scope->ownMethods(extension.scope->internalName()),
1636 argc, argv, valueType);
1639 contained->ownMethods(contained->internalName()), argc, argv, valueType);
1645 if (!type.isMethod()) {
1646 m_state.setHasExternalSideEffects();
1647 QQmlJSMetaMethod method;
1648 method.setMethodName(type.containedTypeName());
1649 method.setIsJavaScriptFunction(
true);
1650 method.setIsConstructor(
true);
1651 setAccumulator(m_typeResolver->returnType(method, m_typeResolver->jsValueType(), {}));
1655 if (
const auto methods = type.method();
1656 methods == m_typeResolver->jsGlobalObject()->methods(u"Date"_s)) {
1657 Q_ASSERT(methods.length() == 1);
1658 generate_Construct_SCDate(methods[0], argc, argv);
1662 if (
const auto methods = type.method();
1663 methods == m_typeResolver->jsGlobalObject()->methods(u"Array"_s)) {
1664 Q_ASSERT(methods.length() == 1);
1665 generate_Construct_SCArray(methods[0], argc, argv);
1669 m_state.setHasExternalSideEffects();
1672 QQmlJSMetaMethod match = bestMatchForCall(type.method(), argc, argv, &errors);
1673 if (!match.isValid())
1674 addError(u"Cannot determine matching constructor. Candidates:\n"_s + errors.join(u'\n'));
1675 setAccumulator(m_typeResolver->returnType(match, m_typeResolver->jsValueType(), {}));
2023void QQmlJSTypePropagator::recordEqualsType(
int lhs)
2025 const auto isNumericOrEnum = [
this](QQmlJSRegisterContent content) {
2026 return content.isEnumeration() || m_typeResolver->isNumeric(content);
2029 const auto accumulatorIn = m_state.accumulatorIn();
2030 const auto lhsRegister = m_state.registers[lhs].content;
2033 if (m_typeResolver->isPrimitive(accumulatorIn) || accumulatorIn.isEnumeration()) {
2034 if (accumulatorIn.contains(lhsRegister.containedType())
2035 || (isNumericOrEnum(accumulatorIn) && isNumericOrEnum(lhsRegister))
2036 || m_typeResolver->isPrimitive(lhsRegister)) {
2037 addReadRegister(lhs);
2038 addReadAccumulator();
2043 const auto containedAccumulatorIn = m_typeResolver->isOptionalType(accumulatorIn)
2044 ? m_typeResolver->extractNonVoidFromOptionalType(accumulatorIn).containedType()
2045 : accumulatorIn.containedType();
2047 const auto containedLhs = m_typeResolver->isOptionalType(lhsRegister)
2048 ? m_typeResolver->extractNonVoidFromOptionalType(lhsRegister).containedType()
2049 : lhsRegister.containedType();
2052 if (QQmlJSUtils::canStrictlyCompareWithVar(m_typeResolver, containedLhs, containedAccumulatorIn)
2053 || QQmlJSUtils::canCompareWithQObject(m_typeResolver, containedLhs, containedAccumulatorIn)
2054 || QQmlJSUtils::canCompareWithQUrl(m_typeResolver, containedLhs, containedAccumulatorIn)) {
2055 addReadRegister(lhs);
2056 addReadAccumulator();
2063 const QQmlJSScope::ConstPtr jsval = m_typeResolver->jsValueType();
2064 addReadRegister(lhs, jsval);
2065 addReadAccumulator(jsval);
2196void QQmlJSTypePropagator::generate_As(
int lhs)
2198 const QQmlJSRegisterContent input = checkedInputRegister(lhs);
2199 const QQmlJSScope::ConstPtr inContained = input.containedType();
2201 QQmlJSRegisterContent output;
2203 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
2204 switch (accumulatorIn.variant()) {
2205 case QQmlJSRegisterContent::Attachment:
2206 output = accumulatorIn.scope();
2208 case QQmlJSRegisterContent::MetaType:
2209 output = accumulatorIn.scope();
2210 if (output.containedType()->isComposite())
2211 addReadAccumulator(m_typeResolver->metaObjectType());
2214 output = accumulatorIn;
2218 QQmlJSScope::ConstPtr outContained = output.containedType();
2220 if (outContained->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
2224 if (m_typeResolver->inherits(inContained, outContained))
2225 output = m_pool->clone(input);
2227 output = m_pool->castTo(input, outContained);
2228 }
else if (m_typeResolver->inherits(inContained, outContained)) {
2230 output = m_pool->castTo(input, outContained);
2234 output = m_typeResolver->merge(
2235 m_pool->castTo(input, outContained),
2236 m_pool->castTo(input, m_typeResolver->voidType()));
2239 addReadRegister(lhs);
2240 setAccumulator(output);
2428QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type type)
2430 if (m_state.jumpTargets.contains(currentInstructionOffset())) {
2431 if (m_state.skipInstructionsUntilNextJumpTarget) {
2433 m_state.registers.clear();
2434 m_state.skipInstructionsUntilNextJumpTarget =
false;
2436 }
else if (m_state.skipInstructionsUntilNextJumpTarget
2437 && !instructionManipulatesContext(type)) {
2438 return SkipInstruction;
2441 const int currentOffset = currentInstructionOffset();
2453 for (
auto originRegisterStateIt =
2454 m_jumpOriginRegisterStateByTargetInstructionOffset.constFind(currentOffset);
2455 originRegisterStateIt != m_jumpOriginRegisterStateByTargetInstructionOffset.constEnd()
2456 && originRegisterStateIt.key() == currentOffset;
2457 ++originRegisterStateIt) {
2458 auto stateToMerge = *originRegisterStateIt;
2459 for (
auto registerIt = stateToMerge.registers.constBegin(),
2460 end = stateToMerge.registers.constEnd();
2461 registerIt != end; ++registerIt) {
2462 const int registerIndex = registerIt.key();
2464 const VirtualRegister &newType = registerIt.value();
2465 if (!newType.content.isValid()) {
2466 addError(u"When reached from offset %1, %2 is undefined"_s
2467 .arg(stateToMerge.originatingOffset)
2468 .arg(registerName(registerIndex)));
2469 return SkipInstruction;
2472 auto currentRegister = m_state.registers.find(registerIndex);
2473 if (currentRegister != m_state.registers.end())
2474 mergeRegister(registerIndex, newType, currentRegister.value());
2476 mergeRegister(registerIndex, newType, newType);
2480 return ProcessInstruction;
2483bool QQmlJSTypePropagator::populatesAccumulator(QV4::Moth::Instr::Type instr)
const
2486 case QV4::Moth::Instr::Type::CheckException:
2487 case QV4::Moth::Instr::Type::CloneBlockContext:
2488 case QV4::Moth::Instr::Type::ConvertThisToObject:
2489 case QV4::Moth::Instr::Type::CreateCallContext:
2490 case QV4::Moth::Instr::Type::DeadTemporalZoneCheck:
2491 case QV4::Moth::Instr::Type::Debug:
2492 case QV4::Moth::Instr::Type::DeclareVar:
2493 case QV4::Moth::Instr::Type::IteratorClose:
2494 case QV4::Moth::Instr::Type::IteratorNext:
2495 case QV4::Moth::Instr::Type::IteratorNextForYieldStar:
2496 case QV4::Moth::Instr::Type::Jump:
2497 case QV4::Moth::Instr::Type::JumpFalse:
2498 case QV4::Moth::Instr::Type::JumpNoException:
2499 case QV4::Moth::Instr::Type::JumpNotUndefined:
2500 case QV4::Moth::Instr::Type::JumpTrue:
2501 case QV4::Moth::Instr::Type::MoveConst:
2502 case QV4::Moth::Instr::Type::MoveReg:
2503 case QV4::Moth::Instr::Type::MoveRegExp:
2504 case QV4::Moth::Instr::Type::PopContext:
2505 case QV4::Moth::Instr::Type::PushBlockContext:
2506 case QV4::Moth::Instr::Type::PushCatchContext:
2507 case QV4::Moth::Instr::Type::PushScriptContext:
2508 case QV4::Moth::Instr::Type::Resume:
2509 case QV4::Moth::Instr::Type::Ret:
2510 case QV4::Moth::Instr::Type::SetException:
2511 case QV4::Moth::Instr::Type::SetLookup:
2512 case QV4::Moth::Instr::Type::SetUnwindHandler:
2513 case QV4::Moth::Instr::Type::StoreElement:
2514 case QV4::Moth::Instr::Type::StoreLocal:
2515 case QV4::Moth::Instr::Type::StoreNameSloppy:
2516 case QV4::Moth::Instr::Type::StoreNameStrict:
2517 case QV4::Moth::Instr::Type::StoreProperty:
2518 case QV4::Moth::Instr::Type::StoreReg:
2519 case QV4::Moth::Instr::Type::StoreScopedLocal:
2520 case QV4::Moth::Instr::Type::StoreSuperProperty:
2521 case QV4::Moth::Instr::Type::ThrowException:
2522 case QV4::Moth::Instr::Type::ThrowOnNullOrUndefined:
2523 case QV4::Moth::Instr::Type::UnwindDispatch:
2524 case QV4::Moth::Instr::Type::UnwindToLabel:
2525 case QV4::Moth::Instr::Type::Yield:
2526 case QV4::Moth::Instr::Type::YieldStar:
2528 case QV4::Moth::Instr::Type::Add:
2529 case QV4::Moth::Instr::Type::As:
2530 case QV4::Moth::Instr::Type::BitAnd:
2531 case QV4::Moth::Instr::Type::BitAndConst:
2532 case QV4::Moth::Instr::Type::BitOr:
2533 case QV4::Moth::Instr::Type::BitOrConst:
2534 case QV4::Moth::Instr::Type::BitXor:
2535 case QV4::Moth::Instr::Type::BitXorConst:
2536 case QV4::Moth::Instr::Type::CallGlobalLookup:
2537 case QV4::Moth::Instr::Type::CallName:
2538 case QV4::Moth::Instr::Type::CallPossiblyDirectEval:
2539 case QV4::Moth::Instr::Type::CallProperty:
2540 case QV4::Moth::Instr::Type::CallPropertyLookup:
2541 case QV4::Moth::Instr::Type::CallQmlContextPropertyLookup:
2542 case QV4::Moth::Instr::Type::CallValue:
2543 case QV4::Moth::Instr::Type::CallWithReceiver:
2544 case QV4::Moth::Instr::Type::CallWithSpread:
2545 case QV4::Moth::Instr::Type::CmpEq:
2546 case QV4::Moth::Instr::Type::CmpEqInt:
2547 case QV4::Moth::Instr::Type::CmpEqNull:
2548 case QV4::Moth::Instr::Type::CmpGe:
2549 case QV4::Moth::Instr::Type::CmpGt:
2550 case QV4::Moth::Instr::Type::CmpIn:
2551 case QV4::Moth::Instr::Type::CmpInstanceOf:
2552 case QV4::Moth::Instr::Type::CmpLe:
2553 case QV4::Moth::Instr::Type::CmpLt:
2554 case QV4::Moth::Instr::Type::CmpNe:
2555 case QV4::Moth::Instr::Type::CmpNeInt:
2556 case QV4::Moth::Instr::Type::CmpNeNull:
2557 case QV4::Moth::Instr::Type::CmpStrictEqual:
2558 case QV4::Moth::Instr::Type::CmpStrictNotEqual:
2559 case QV4::Moth::Instr::Type::Construct:
2560 case QV4::Moth::Instr::Type::ConstructWithSpread:
2561 case QV4::Moth::Instr::Type::CreateClass:
2562 case QV4::Moth::Instr::Type::CreateMappedArgumentsObject:
2563 case QV4::Moth::Instr::Type::CreateRestParameter:
2564 case QV4::Moth::Instr::Type::CreateUnmappedArgumentsObject:
2565 case QV4::Moth::Instr::Type::Decrement:
2566 case QV4::Moth::Instr::Type::DefineArray:
2567 case QV4::Moth::Instr::Type::DefineObjectLiteral:
2568 case QV4::Moth::Instr::Type::DeleteName:
2569 case QV4::Moth::Instr::Type::DeleteProperty:
2570 case QV4::Moth::Instr::Type::DestructureRestElement:
2571 case QV4::Moth::Instr::Type::Div:
2572 case QV4::Moth::Instr::Type::Exp:
2573 case QV4::Moth::Instr::Type::GetException:
2574 case QV4::Moth::Instr::Type::GetIterator:
2575 case QV4::Moth::Instr::Type::GetLookup:
2576 case QV4::Moth::Instr::Type::GetOptionalLookup:
2577 case QV4::Moth::Instr::Type::GetTemplateObject:
2578 case QV4::Moth::Instr::Type::Increment:
2579 case QV4::Moth::Instr::Type::InitializeBlockDeadTemporalZone:
2580 case QV4::Moth::Instr::Type::LoadClosure:
2581 case QV4::Moth::Instr::Type::LoadConst:
2582 case QV4::Moth::Instr::Type::LoadElement:
2583 case QV4::Moth::Instr::Type::LoadFalse:
2584 case QV4::Moth::Instr::Type::LoadGlobalLookup:
2585 case QV4::Moth::Instr::Type::LoadImport:
2586 case QV4::Moth::Instr::Type::LoadInt:
2587 case QV4::Moth::Instr::Type::LoadLocal:
2588 case QV4::Moth::Instr::Type::LoadName:
2589 case QV4::Moth::Instr::Type::LoadNull:
2590 case QV4::Moth::Instr::Type::LoadOptionalProperty:
2591 case QV4::Moth::Instr::Type::LoadProperty:
2592 case QV4::Moth::Instr::Type::LoadQmlContextPropertyLookup:
2593 case QV4::Moth::Instr::Type::LoadReg:
2594 case QV4::Moth::Instr::Type::LoadRuntimeString:
2595 case QV4::Moth::Instr::Type::LoadScopedLocal:
2596 case QV4::Moth::Instr::Type::LoadSuperConstructor:
2597 case QV4::Moth::Instr::Type::LoadSuperProperty:
2598 case QV4::Moth::Instr::Type::LoadTrue:
2599 case QV4::Moth::Instr::Type::LoadUndefined:
2600 case QV4::Moth::Instr::Type::LoadZero:
2601 case QV4::Moth::Instr::Type::Mod:
2602 case QV4::Moth::Instr::Type::Mul:
2603 case QV4::Moth::Instr::Type::PushWithContext:
2604 case QV4::Moth::Instr::Type::Shl:
2605 case QV4::Moth::Instr::Type::ShlConst:
2606 case QV4::Moth::Instr::Type::Shr:
2607 case QV4::Moth::Instr::Type::ShrConst:
2608 case QV4::Moth::Instr::Type::Sub:
2609 case QV4::Moth::Instr::Type::TailCall:
2610 case QV4::Moth::Instr::Type::ToObject:
2611 case QV4::Moth::Instr::Type::TypeofName:
2612 case QV4::Moth::Instr::Type::TypeofValue:
2613 case QV4::Moth::Instr::Type::UCompl:
2614 case QV4::Moth::Instr::Type::UMinus:
2615 case QV4::Moth::Instr::Type::UNot:
2616 case QV4::Moth::Instr::Type::UPlus:
2617 case QV4::Moth::Instr::Type::UShr:
2618 case QV4::Moth::Instr::Type::UShrConst:
2621 Q_UNREACHABLE_RETURN(
false);
2636void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
2638 InstructionAnnotation ¤tInstruction = m_state.annotations[currentInstructionOffset()];
2639 currentInstruction.changedRegister = m_state.changedRegister();
2640 currentInstruction.changedRegisterIndex = m_state.changedRegisterIndex();
2641 currentInstruction.readRegisters = m_state.takeReadRegisters();
2642 currentInstruction.hasExternalSideEffects = m_state.hasExternalSideEffects();
2643 currentInstruction.hasInternalSideEffects = m_state.hasInternalSideEffects();
2644 currentInstruction.isRename = m_state.isRename();
2646 bool populates = populatesAccumulator(instr);
2647 int changedIndex = m_state.changedRegisterIndex();
2650 if (instr != QV4::Moth::Instr::Type::InitializeBlockDeadTemporalZone) {
2651 Q_ASSERT((populates && changedIndex == Accumulator && m_state.accumulatorOut().isValid())
2652 || (!populates && changedIndex != Accumulator));
2655 if (!m_logger->currentFunctionHasCompileError() && !isNoop(instr)) {
2658 Q_ASSERT(m_state.hasInternalSideEffects() || changedIndex != InvalidRegister);
2661 if (changedIndex != InvalidRegister) {
2662 Q_ASSERT(m_logger->currentFunctionHasCompileError() || m_state.changedRegister().isValid());
2663 VirtualRegister &r = m_state.registers[changedIndex];
2664 r.content = m_state.changedRegister();
2666 r.affectedBySideEffects = m_state.isRename()
2667 && m_state.isRegisterAffectedBySideEffects(m_state.renameSourceRegisterIndex());
2668 m_state.clearChangedRegister();
2671 m_state.resetSideEffects();
2672 m_state.setIsRename(
false);
2673 m_state.setReadRegisters(VirtualRegisters());
2674 m_state.instructionHasError =
false;